Explorar o código

Merge pull request #297 from chamilo/chamilo19

Chamilo19
Julio Montoya %!s(int64=11) %!d(string=hai) anos
pai
achega
e5ec22dcd6
Modificáronse 100 ficheiros con 7567 adicións e 3134 borrados
  1. 17 19
      main/document/create_draw.php
  2. 144 122
      main/inc/lib/main_api.lib.php
  3. 0 22
      main/inc/lib/svg-edit/AUTHORS
  4. 0 19
      main/inc/lib/svg-edit/LICENSE
  5. 0 21
      main/inc/lib/svg-edit/README
  6. 10 6
      main/inc/lib/svg-edit/browser-not-supported.html
  7. 75 71
      main/inc/lib/svg-edit/browser.js
  8. 248 186
      main/inc/lib/svg-edit/canvg/canvg.js
  9. 0 6
      main/inc/lib/svg-edit/canvg/index.html
  10. 31 24
      main/inc/lib/svg-edit/canvg/rgbcolor.js
  11. 145 0
      main/inc/lib/svg-edit/config-sample.js
  12. 17 0
      main/inc/lib/svg-edit/config.js
  13. 17 18
      main/inc/lib/svg-edit/contextmenu.js
  14. 0 6
      main/inc/lib/svg-edit/contextmenu/index.html
  15. 2 3
      main/inc/lib/svg-edit/contextmenu/jquery.contextMenu.js
  16. 0 22
      main/inc/lib/svg-edit/contextmenu/jquery.contextMenu.min.js
  17. 316 0
      main/inc/lib/svg-edit/coords.js
  18. 6 0
      main/inc/lib/svg-edit/custom.css
  19. 188 179
      main/inc/lib/svg-edit/draw.js
  20. 59 0
      main/inc/lib/svg-edit/embedapi.html
  21. 0 56
      main/inc/lib/svg-edit/embedapi.html_disabled
  22. 101 148
      main/inc/lib/svg-edit/embedapi.js
  23. 11 0
      main/inc/lib/svg-edit/extensions/allowedMimeTypes.php
  24. 9 0
      main/inc/lib/svg-edit/extensions/closepath_icons.svg
  25. 260 265
      main/inc/lib/svg-edit/extensions/ext-arrows.js
  26. 71 72
      main/inc/lib/svg-edit/extensions/ext-closepath.js
  27. 155 149
      main/inc/lib/svg-edit/extensions/ext-connector.js
  28. 67 0
      main/inc/lib/svg-edit/extensions/ext-executablebuilder.js
  29. 91 88
      main/inc/lib/svg-edit/extensions/ext-eyedropper.js
  30. 229 238
      main/inc/lib/svg-edit/extensions/ext-foreignobject.js
  31. 101 130
      main/inc/lib/svg-edit/extensions/ext-grid.js
  32. 6 4
      main/inc/lib/svg-edit/extensions/ext-helloworld.js
  33. 134 120
      main/inc/lib/svg-edit/extensions/ext-imagelib.js
  34. 147 140
      main/inc/lib/svg-edit/extensions/ext-imagelib.xml
  35. 273 0
      main/inc/lib/svg-edit/extensions/ext-mathjax.js
  36. 142 0
      main/inc/lib/svg-edit/extensions/ext-overview_window.js
  37. 46 0
      main/inc/lib/svg-edit/extensions/ext-panning.js
  38. 7 0
      main/inc/lib/svg-edit/extensions/ext-panning.xml
  39. 23 0
      main/inc/lib/svg-edit/extensions/ext-php_savefile.js
  40. 34 0
      main/inc/lib/svg-edit/extensions/ext-php_savefile_chamilo.js
  41. 295 0
      main/inc/lib/svg-edit/extensions/ext-polygon.js
  42. 23 19
      main/inc/lib/svg-edit/extensions/ext-server_moinsave.js
  43. 80 51
      main/inc/lib/svg-edit/extensions/ext-server_opensave.js
  44. 357 387
      main/inc/lib/svg-edit/extensions/ext-shapes.js
  45. 236 0
      main/inc/lib/svg-edit/extensions/ext-shapes.xml
  46. 230 0
      main/inc/lib/svg-edit/extensions/ext-storage - Copy.js
  47. 293 0
      main/inc/lib/svg-edit/extensions/ext-storage.js
  48. 67 0
      main/inc/lib/svg-edit/extensions/ext-webappfind.js
  49. 42 0
      main/inc/lib/svg-edit/extensions/ext-xdomain-messaging.js
  50. 30 50
      main/inc/lib/svg-edit/extensions/eyedropper-icon.xml
  51. 34 127
      main/inc/lib/svg-edit/extensions/filesave.php
  52. 27 0
      main/inc/lib/svg-edit/extensions/foreignobject-icons.xml
  53. 70 0
      main/inc/lib/svg-edit/extensions/grid-icon.xml
  54. 0 6
      main/inc/lib/svg-edit/extensions/imagelib/smiley.svg
  55. 11 0
      main/inc/lib/svg-edit/extensions/markers-icons.xml
  56. 14 0
      main/inc/lib/svg-edit/extensions/polygon-icons.svg
  57. 5 0
      main/inc/lib/svg-edit/extensions/saved.svg
  58. 16 0
      main/inc/lib/svg-edit/extensions/savefile.php
  59. 166 0
      main/inc/lib/svg-edit/extensions/savefile_config.php
  60. 0 6
      main/inc/lib/svg-edit/extensions/shapelib/animal.json
  61. 0 1
      main/inc/lib/svg-edit/extensions/shapelib/math.json
  62. 0 0
      main/inc/lib/svg-edit/extensions/shapelib/raphael.txt
  63. 14 0
      main/inc/lib/svg-edit/extensions/shapelib/raphael_2.json
  64. 23 0
      main/inc/lib/svg-edit/extensions/webappfind-icon.svg
  65. 92 80
      main/inc/lib/svg-edit/history.js
  66. 1 1
      main/inc/lib/svg-edit/images/README.txt
  67. 0 6
      main/inc/lib/svg-edit/images/align-bottom.png
  68. BIN=BIN
      main/inc/lib/svg-edit/images/italic.png
  69. 0 0
      main/inc/lib/svg-edit/images/move_bottom.png
  70. 1 3
      main/inc/lib/svg-edit/images/shape_ungroup.png
  71. 0 6
      main/inc/lib/svg-edit/images/svg_edit_icons.svgz
  72. 1 1
      main/inc/lib/svg-edit/jgraduate/css/jPicker.css
  73. 0 6
      main/inc/lib/svg-edit/jgraduate/images/AlphaBar.png
  74. 0 6
      main/inc/lib/svg-edit/jgraduate/images/map-opacity.png
  75. 1673 0
      main/inc/lib/svg-edit/jgraduate/jpicker.js
  76. 4 4
      main/inc/lib/svg-edit/jgraduate/jpicker.min.js
  77. 74 0
      main/inc/lib/svg-edit/jgraduate/jquery.jgraduate.min.js
  78. 0 6
      main/inc/lib/svg-edit/jquery-ui/index.html
  79. 17 0
      main/inc/lib/svg-edit/jquery-ui/jquery-ui-1.8.17.custom.min.js
  80. 0 6
      main/inc/lib/svg-edit/jquery.js
  81. 0 6
      main/inc/lib/svg-edit/jquerybbq/jquery.bbq.min.js
  82. 1 0
      main/inc/lib/svg-edit/js-hotkeys/jquery.hotkeys.min.js
  83. 0 6
      main/inc/lib/svg-edit/locale/index.html
  84. 19 3
      main/inc/lib/svg-edit/locale/lang.af.js
  85. 19 3
      main/inc/lib/svg-edit/locale/lang.ar.js
  86. 19 3
      main/inc/lib/svg-edit/locale/lang.az.js
  87. 19 3
      main/inc/lib/svg-edit/locale/lang.be.js
  88. 19 3
      main/inc/lib/svg-edit/locale/lang.bg.js
  89. 19 3
      main/inc/lib/svg-edit/locale/lang.ca.js
  90. 19 3
      main/inc/lib/svg-edit/locale/lang.cs.js
  91. 19 3
      main/inc/lib/svg-edit/locale/lang.cy.js
  92. 19 3
      main/inc/lib/svg-edit/locale/lang.da.js
  93. 56 40
      main/inc/lib/svg-edit/locale/lang.de.js
  94. 19 3
      main/inc/lib/svg-edit/locale/lang.el.js
  95. 19 3
      main/inc/lib/svg-edit/locale/lang.en.js
  96. 83 67
      main/inc/lib/svg-edit/locale/lang.es.js
  97. 19 3
      main/inc/lib/svg-edit/locale/lang.et.js
  98. 19 3
      main/inc/lib/svg-edit/locale/lang.fa.js
  99. 19 3
      main/inc/lib/svg-edit/locale/lang.fi.js
  100. 83 67
      main/inc/lib/svg-edit/locale/lang.fr.js

+ 17 - 19
main/document/create_draw.php

@@ -34,7 +34,7 @@ api_block_anonymous_users();
 $document_data = DocumentManager::get_document_data_by_id($_GET['id'], api_get_course_id(), true);
 if (empty($document_data)) {
     if (api_is_in_group()) {
-        $group_properties   = GroupManager::get_group_properties(api_get_group_id());        
+        $group_properties   = GroupManager::get_group_properties(api_get_group_id());
         $document_id        = DocumentManager::get_document_id(api_get_course_info(), $group_properties['directory']);
         $document_data      = DocumentManager::get_document_data_by_id($document_id, api_get_course_id());
     }
@@ -117,7 +117,7 @@ if (isset ($group)) {
 	// Copied from document.php
 	$dir_array = explode('/', $dir);
 	$array_len = count($dir_array);
-	
+
 	/*
 	TODO:check and delete this code
 	if (!$is_certificate_mode) {
@@ -143,37 +143,35 @@ echo '<div class="actions">';
 		echo '<a href="document.php?id='.$document_id.'">'.Display::return_icon('back.png',get_lang('BackTo').' '.get_lang('DocumentsOverview'),'',ICON_SIZE_MEDIUM).'</a>';
 echo '</div>';
 
-if (api_browser_support('svg')){
-	
+if (api_browser_support('svg')) {
+
 	//automatic loading the course language
 	$svgedit_code_translation_table = array('' => 'en', 'pt' => 'pt-Pt', 'sr' => 'sr_latn');
 	$langsvgedit  = api_get_language_isocode();
 	$langsvgedit = isset($svgedit_code_translation_table[$langsvgedit]) ? $svgedit_code_translation_table[$langsvgedit] : $langsvgedit;
 	$langsvgedit = file_exists(api_get_path(LIBRARY_PATH).'svg-edit/locale/lang.'.$langsvgedit.'.js') ? $langsvgedit : 'en';
-	$svg_url= api_get_path(WEB_LIBRARY_PATH).'svg-edit/svg-editor.php?lang='.$langsvgedit ;
+	$svg_url= api_get_path(WEB_LIBRARY_PATH).'svg-edit/svg-editor.php?lang='.$langsvgedit;
 	?>
-    
-	<script type="text/javascript">
-	
+	<script>
 		document.write ('<iframe id="frame" frameborder="0" scrolling="no" src="<?php echo  $svg_url; ?>" width="100%" height="100%"><noframes><p>Sorry, your browser does not handle frames</p></noframes></iframe>');
-	function resizeIframe() {
-    	var height = window.innerHeight -50;
-		//max lower size
-		if (height<550) {
-			height=550;
-		}
-    	document.getElementById('frame').style.height = height +"px";
-	};
+        function resizeIframe() {
+            var height = window.innerHeight -50;
+            //max lower size
+            if (height<550) {
+                height=550;
+            }
+            document.getElementById('frame').style.height = height +"px";
+        }
 	document.getElementById('frame').onload = resizeIframe;
 	window.onresize = resizeIframe;
-	
+
 	</script>
-    
+
     <?php
     echo '<noscript>';
 	echo '<iframe style="height: 550px; width: 100%;" scrolling="no" frameborder="0" src="'.$svg_url.'"><noframes><p>Sorry, your browser does not handle frames</p></noframes></iframe>';
 	echo '</noscript>';
-} else {	
+} else {
 	Display::display_error_message(get_lang('BrowserDontSupportsSVG'));
 }
 

+ 144 - 122
main/inc/lib/main_api.lib.php

@@ -5875,7 +5875,7 @@ function api_protect_global_admin_script() {
 }
 
 /**
- * Get actived template
+ * Get active template
  * @param string    theme type (optional: default)
  * @param string    path absolute(abs) or relative(rel) (optional:rel)
  * @return string   actived template path
@@ -5900,136 +5900,158 @@ function api_get_template($path_type = 'rel') {
 
 /**
  * Check browser support for type files
- ** This function check if the users browser support a file format or return the current browser and major ver when $format=check_browser
+ * This function check if the users browser support a file format or
+ * return the current browser and major ver when $format=check_browser
  * @param string $format
  *
  * @return bool, or return text array if $format=check_browser
  * @author Juan Carlos Raña Trabado
  */
 
-function api_browser_support($format="") {
-    require_once api_get_path(LIBRARY_PATH).'browser/Browser.php';
+function api_browser_support($format = "")
+{
+    require_once api_get_path(LIBRARY_PATH) . 'browser/Browser.php';
     $browser = new Browser();
     $current_browser = $browser->getBrowser();
     $a_versiontemp = explode('.', $browser->getVersion());
-    $current_majorver= $a_versiontemp[0];
-
-	//native svg support
-	if ($format=='svg'){
-		if (($current_browser == 'Internet Explorer' && $current_majorver >= 9) || ($current_browser == 'Firefox' && $current_majorver > 1) || ($current_browser == 'Safari' && $current_majorver >= 4) || ($current_browser == 'Chrome' && $current_majorver >= 1) || ($current_browser == 'Opera' && $current_majorver >= 9)) {
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='pdf') {
-		//native pdf support
-		if($current_browser == 'Chrome' && $current_majorver >= 6){
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='tif' || $format=='tiff'){
-		//native tif support
-		if($current_browser == 'Safari' && $current_majorver >= 5){
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='ogg' || $format=='ogx'|| $format=='ogv' || $format=='oga'){
-	//native ogg, ogv,oga support
-		if (($current_browser == 'Firefox' && $current_majorver >= 3)  || ($current_browser == 'Chrome' && $current_majorver >= 3) || ($current_browser == 'Opera' && $current_majorver >= 9)) {
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='mpg' || $format=='mpeg'){
-		//native mpg support
-		if(($current_browser == 'Safari' && $current_majorver >= 5)){
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='mp4') {
-		//native mp4 support (TODO: Android, iPhone)
-		if($current_browser == 'Android' || $current_browser == 'iPhone') {
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='mov') {
-		//native mov support( TODO:check iPhone)
-		if($current_browser == 'Safari' && $current_majorver >= 5 || $current_browser == 'iPhone'){
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='avi') {
-		//native avi support
-		if($current_browser == 'Safari' && $current_majorver >= 5){
-			return true;
-		}
-		else{
-			return false;
-		}
-	} elseif($format=='wmv') {
-		//native wmv support
-		if ($current_browser == 'Firefox' && $current_majorver >= 4){
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='webm') {
-		//native webm support (TODO:check IE9, Chrome9, Android)
-		if(($current_browser == 'Firefox' && $current_majorver >= 4) || ($current_browser == 'Opera' && $current_majorver >= 9) || ($current_browser == 'Internet Explorer' && $current_majorver >= 9)|| ($current_browser == 'Chrome' && $current_majorver >=9)|| $current_browser == 'Android'){
-			return true;
-		}
-		else{
-			return false;
-		}
-	} elseif($format=='wav') {
-		//native wav support (only some codecs !)
-		if (($current_browser == 'Firefox' && $current_majorver >= 4) || ($current_browser == 'Safari' && $current_majorver >= 5) || ($current_browser == 'Opera' && $current_majorver >= 9) || ($current_browser == 'Internet Explorer' && $current_majorver >= 9)|| ($current_browser == 'Chrome' && $current_majorver > 9)|| $current_browser == 'Android' || $current_browser == 'iPhone'){
-			return true;
-		}
-		else{
-			return false;
-		}
-	} elseif($format=='mid' || $format=='kar') {
-		//native midi support (TODO:check Android)
-		if($current_browser == 'Opera'&& $current_majorver >= 9 || $current_browser == 'Android'){
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=='wma') {
-		//native wma support
-		if($current_browser == 'Firefox' && $current_majorver >= 4){
-			return true;
-		}
-		else{
-			return false;
-		}
-	} elseif($format=='au') {
-		//native au support
-		if($current_browser == 'Safari' && $current_majorver >= 5){
-			return true;
-		}
-		else{
-			return false;
-		}
-	} elseif($format=='mp3') {
-		//native mp3 support (TODO:check Android, iPhone)
-		if(($current_browser == 'Safari' && $current_majorver >= 5) || ($current_browser == 'Chrome' && $current_majorver >=6)|| ($current_browser == 'Internet Explorer' && $current_majorver >= 9)|| $current_browser == 'Android' || $current_browser == 'iPhone'){
-			return true;
-		} else {
-			return false;
-		}
-	} elseif($format=="check_browser") {
-		$array_check_browser=array($current_browser, $current_majorver);
-		return $array_check_browser;
-	} else {
-		return false;
-	}
+    $current_majorver = $a_versiontemp[0];
+
+    // Native svg support
+    if ($format == 'svg') {
+
+        if (($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
+            ($current_browser == 'Firefox' && $current_majorver > 1) ||
+            ($current_browser == 'Safari' && $current_majorver >= 4) ||
+            ($current_browser == 'Chrome' && $current_majorver >= 1) ||
+            ($current_browser == 'Opera' && $current_majorver >= 9)
+        ) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'pdf') {
+        //native pdf support
+        if ($current_browser == 'Chrome' && $current_majorver >= 6) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'tif' || $format == 'tiff') {
+        //native tif support
+        if ($current_browser == 'Safari' && $current_majorver >= 5) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'ogg' || $format == 'ogx' || $format == 'ogv' || $format == 'oga') {
+        //native ogg, ogv,oga support
+        if (($current_browser == 'Firefox' && $current_majorver >= 3) ||
+            ($current_browser == 'Chrome' && $current_majorver >= 3) ||
+            ($current_browser == 'Opera' && $current_majorver >= 9)) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'mpg' || $format == 'mpeg') {
+        //native mpg support
+        if (($current_browser == 'Safari' && $current_majorver >= 5)) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'mp4') {
+        //native mp4 support (TODO: Android, iPhone)
+        if ($current_browser == 'Android' || $current_browser == 'iPhone') {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'mov') {
+        //native mov support( TODO:check iPhone)
+        if ($current_browser == 'Safari' && $current_majorver >= 5 || $current_browser == 'iPhone') {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'avi') {
+        //native avi support
+        if ($current_browser == 'Safari' && $current_majorver >= 5) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'wmv') {
+        //native wmv support
+        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'webm') {
+        //native webm support (TODO:check IE9, Chrome9, Android)
+        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
+            ($current_browser == 'Opera' && $current_majorver >= 9) ||
+            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
+            ($current_browser == 'Chrome' && $current_majorver >= 9) ||
+            $current_browser == 'Android'
+        ) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'wav') {
+        //native wav support (only some codecs !)
+        if (($current_browser == 'Firefox' && $current_majorver >= 4) ||
+            ($current_browser == 'Safari' && $current_majorver >= 5) ||
+            ($current_browser == 'Opera' && $current_majorver >= 9) ||
+            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
+            ($current_browser == 'Chrome' && $current_majorver > 9) ||
+            $current_browser == 'Android' ||
+            $current_browser == 'iPhone'
+        ) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'mid' || $format == 'kar') {
+        //native midi support (TODO:check Android)
+        if ($current_browser == 'Opera' && $current_majorver >= 9 || $current_browser == 'Android') {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'wma') {
+        //native wma support
+        if ($current_browser == 'Firefox' && $current_majorver >= 4) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'au') {
+        //native au support
+        if ($current_browser == 'Safari' && $current_majorver >= 5) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == 'mp3') {
+        //native mp3 support (TODO:check Android, iPhone)
+        if (($current_browser == 'Safari' && $current_majorver >= 5) ||
+            ($current_browser == 'Chrome' && $current_majorver >= 6) ||
+            ($current_browser == 'Internet Explorer' && $current_majorver >= 9) ||
+            $current_browser == 'Android' ||
+            $current_browser == 'iPhone'
+        ) {
+            return true;
+        } else {
+            return false;
+        }
+    } elseif ($format == "check_browser") {
+        $array_check_browser = array($current_browser, $current_majorver);
+        return $array_check_browser;
+    } else {
+        return false;
+    }
 }
 
 /**

+ 0 - 22
main/inc/lib/svg-edit/AUTHORS

@@ -1,22 +0,0 @@
-Narendra Sisodiya <narendra.sisodiya@gmail.com>
-Pavol Rusnak <rusnakp@gmail.com>
-Jeff Schiller <codedread@gmail.com>
-Vidar Hokstad <vidar.hokstad@gmail.com>
-Alexis Deveria <adeveria@gmail.com>
-
-Translation credits:
-
-ar: Tarik Belaam <dubraise@gmail.com> (العربية)
-cs: Jan Ptacek <jan.ptacek@gmail.com> (Čeština)
-de: Reimar Bauer <rb.proj@googlemail.com> (Deutsch)
-es: Alicia Puerto <alicia.puerto.g@gmail.com> (Español)
-fa: Payman Delshad <payman@opera.com> (فارسی)
-fr: wormsxulla <wormsxulla@yahoo.com> (Français)
-fy: Wander Nauta <info@wandernauta.nl> (Frysk)
-hi: Tavish Naruka <tavishnaruka@gmail.com> (हिन्दी)
-ja: Dong <iwadon@yarhalla.jpn.org> (日本語)
-nl: Jaap Blom <jaap.blom@gmail.com> (Nederlands)
-ro: Christian Tzurcanu <christian.tzurcanu@gmail.com> (Româneşte)
-ru: Laurent Dufloux <laurent.dufloux@etu.upmc.fr> (Русский)
-sk: Pavol Rusnak <rusnakp@gmail.com> (Slovenčina)
-zh-TW: 黃瀚生 (Han Sheng Huang) <zenixls2@gmail.com> (台灣正體) 

+ 0 - 19
main/inc/lib/svg-edit/LICENSE

@@ -1,19 +0,0 @@
-Copyright (c) 2009-2011 by SVG-edit authors (see AUTHORS file)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

+ 0 - 21
main/inc/lib/svg-edit/README

@@ -1,21 +0,0 @@
-SVG-edit, a web based SVG editor
-
-http://code.google.com/p/svg-edit/
-
-see AUTHORS file for authors
-
------
-
-SVG-edit contains code from these projects:
-
-jQuery JavaScript Library v1.3.2
-http://jquery.com/
-Copyright (c) 2009 John Resig
-
-jQuery js-Hotkeys
-http://code.google.com/p/js-hotkeys/
-Copyright (c) 2008 Tzury Bar Yochay
-
-jPicker
-http://www.digitalmagicpro.com/jPicker/
-Copyright (c) 2009 Christopher T. Tillman

+ 10 - 6
main/inc/lib/svg-edit/browser-not-supported.html

@@ -5,7 +5,7 @@
 <meta http-equiv="X-UA-Compatible" content="chrome=1"/>
 <link rel="icon" type="image/png" href="images/logo.png"/>
 <link rel="stylesheet" href="svg-editor.css" type="text/css"/>
-<script type="text/javascript" src="jquery.js"></script>
+<script src="jquery.js"></script>
 <title>Browser does not support SVG | SVG-edit</title>
 </head>
 <body>
@@ -16,12 +16,16 @@
 <p>Try the latest version of <a href="http://www.getfirefox.com">Firefox</a>, <a href="http://www.google.com/chrome/">Google Chrome</a>, <a href="http://www.apple.com/safari/download/">Safari</a>, <a href="http://www.opera.com/download/">Opera</a> or <a href="http://windows.microsoft.com/ie9">Internet Explorer</a>.</p>
 <p>If you are unable to install one of these and must use an old version of Internet Explorer, you can install the <a href="http://code.google.com/chrome/chromeframe/">Google Chrome Frame plugin</a>.</p>
 
-<script type="text/javascript">
-var viewportHeight =(window.innerHeight ? window.innerHeight : $(window).height()) - 140;
-document.write('<iframe width="100%" height="'+viewportHeight+'" src="http://caniuse.com/#cats=SVG"></iframe>');
-</script>
-
 </div>
 
+<script>
+/*globals $*/
+var viewportHeight = window.innerHeight || ($(window).height() - 140);
+var iframe = document.createElement('iframe');
+iframe.style.width = '100%';
+iframe.style.height = viewportHeight + 'px';
+iframe.src = 'http://caniuse.com/#cats=SVG';
+document.body.appendChild(iframe);
+</script>
 </body>
 </html>

+ 75 - 71
main/inc/lib/svg-edit/browser.js

@@ -1,7 +1,9 @@
+/*globals $, svgedit*/
+/*jslint vars: true, eqeq: true*/
 /**
  * Package: svgedit.browser
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Jeff Schiller
  * Copyright(c) 2010 Alexis Deveria
@@ -10,46 +12,50 @@
 // Dependencies:
 // 1) jQuery (for $.alert())
 
-var svgedit = svgedit || {};
-
-(function() {
+(function() {'use strict';
 
 if (!svgedit.browser) {
 	svgedit.browser = {};
 }
+
+// alias
+var NS = svgedit.NS;
+
 var supportsSvg_ = (function() {
-        return !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
-})();
-svgedit.browser.supportsSvg = function() { return supportsSvg_; }
+	return !!document.createElementNS && !!document.createElementNS(NS.SVG, 'svg').createSVGRect;
+}());
+
+svgedit.browser.supportsSvg = function() { return supportsSvg_; };
 if(!svgedit.browser.supportsSvg()) {
-	window.location = "browser-not-supported.html";
+	window.location = 'browser-not-supported.html';
+	return;
 }
-else{
 
-var svgns = 'http://www.w3.org/2000/svg';
 var userAgent = navigator.userAgent;
-var svg = document.createElementNS(svgns, 'svg');
+var svg = document.createElementNS(NS.SVG, 'svg');
 
 // Note: Browser sniffing should only be used if no other detection method is possible
 var isOpera_ = !!window.opera;
-var isWebkit_ = userAgent.indexOf("AppleWebKit") >= 0;
+var isWebkit_ = userAgent.indexOf('AppleWebKit') >= 0;
 var isGecko_ = userAgent.indexOf('Gecko/') >= 0;
 var isIE_ = userAgent.indexOf('MSIE') >= 0;
 var isChrome_ = userAgent.indexOf('Chrome/') >= 0;
 var isWindows_ = userAgent.indexOf('Windows') >= 0;
+var isMac_ = userAgent.indexOf('Macintosh') >= 0;
+var isTouch_ = 'ontouchstart' in window;
 
 var supportsSelectors_ = (function() {
 	return !!svg.querySelector;
-})();
+}());
 
 var supportsXpath_ = (function() {
 	return !!document.evaluate;
-})();
+}());
 
 // segList functions (for FF1.5 and 2.0)
 var supportsPathReplaceItem_ = (function() {
-	var path = document.createElementNS(svgns, 'path');
-	path.setAttribute('d','M0,0 10,10');
+	var path = document.createElementNS(NS.SVG, 'path');
+	path.setAttribute('d', 'M0,0 10,10');
 	var seglist = path.pathSegList;
 	var seg = path.createSVGPathSegLinetoAbs(5,5);
 	try {
@@ -57,11 +63,11 @@ var supportsPathReplaceItem_ = (function() {
 		return true;
 	} catch(err) {}
 	return false;
-})();
+}());
 
 var supportsPathInsertItemBefore_ = (function() {
-	var path = document.createElementNS(svgns,'path');
-	path.setAttribute('d','M0,0 10,10');
+	var path = document.createElementNS(NS.SVG, 'path');
+	path.setAttribute('d', 'M0,0 10,10');
 	var seglist = path.pathSegList;
 	var seg = path.createSVGPathSegLinetoAbs(5,5);
 	try {
@@ -69,44 +75,43 @@ var supportsPathInsertItemBefore_ = (function() {
 		return true;
 	} catch(err) {}
 	return false;
-})();
+}());
 
 // text character positioning (for IE9)
 var supportsGoodTextCharPos_ = (function() {
-	var retValue = false;
-	var svgroot = document.createElementNS(svgns, 'svg');
-	var svgcontent = document.createElementNS(svgns, 'svg');
+	var svgroot = document.createElementNS(NS.SVG, 'svg');
+	var svgcontent = document.createElementNS(NS.SVG, 'svg');
 	document.documentElement.appendChild(svgroot);
 	svgcontent.setAttribute('x', 5);
 	svgroot.appendChild(svgcontent);
-	var text = document.createElementNS(svgns,'text');
+	var text = document.createElementNS(NS.SVG, 'text');
 	text.textContent = 'a';
 	svgcontent.appendChild(text);
 	var pos = text.getStartPositionOfChar(0).x;
 	document.documentElement.removeChild(svgroot);
 	return (pos === 0);
-})();
+}());
 
 var supportsPathBBox_ = (function() {
-	var svgcontent = document.createElementNS(svgns, 'svg');
+	var svgcontent = document.createElementNS(NS.SVG, 'svg');
 	document.documentElement.appendChild(svgcontent);
-	var path = document.createElementNS(svgns, 'path');
-	path.setAttribute('d','M0,0 C0,0 10,10 10,0');
+	var path = document.createElementNS(NS.SVG, 'path');
+	path.setAttribute('d', 'M0,0 C0,0 10,10 10,0');
 	svgcontent.appendChild(path);
 	var bbox = path.getBBox();
 	document.documentElement.removeChild(svgcontent);
 	return (bbox.height > 4 && bbox.height < 5);
-})();
+}());
 
 // Support for correct bbox sizing on groups with horizontal/vertical lines
 var supportsHVLineContainerBBox_ = (function() {
-	var svgcontent = document.createElementNS(svgns, 'svg');
+	var svgcontent = document.createElementNS(NS.SVG, 'svg');
 	document.documentElement.appendChild(svgcontent);
-	var path = document.createElementNS(svgns, 'path');
-	path.setAttribute('d','M0,0 10,0');
-	var path2 = document.createElementNS(svgns, 'path');
-	path2.setAttribute('d','M5,0 15,0');
-	var g = document.createElementNS(svgns, 'g');
+	var path = document.createElementNS(NS.SVG, 'path');
+	path.setAttribute('d', 'M0,0 10,0');
+	var path2 = document.createElementNS(NS.SVG, 'path');
+	path2.setAttribute('d', 'M5,0 15,0');
+	var g = document.createElementNS(NS.SVG, 'g');
 	g.appendChild(path);
 	g.appendChild(path2);
 	svgcontent.appendChild(g);
@@ -114,63 +119,62 @@ var supportsHVLineContainerBBox_ = (function() {
 	document.documentElement.removeChild(svgcontent);
 	// Webkit gives 0, FF gives 10, Opera (correctly) gives 15
 	return (bbox.width == 15);
-})();
+}());
 
 var supportsEditableText_ = (function() {
 	// TODO: Find better way to check support for this
 	return isOpera_;
-})();
+}());
 
 var supportsGoodDecimals_ = (function() {
 	// Correct decimals on clone attributes (Opera < 10.5/win/non-en)
-	var rect = document.createElementNS(svgns, 'rect');
-	rect.setAttribute('x',.1);
+	var rect = document.createElementNS(NS.SVG, 'rect');
+	rect.setAttribute('x', 0.1);
 	var crect = rect.cloneNode(false);
 	var retValue = (crect.getAttribute('x').indexOf(',') == -1);
 	if(!retValue) {
-		$.alert("NOTE: This version of Opera is known to contain bugs in SVG-edit.\n\
-		Please upgrade to the <a href='http://opera.com'>latest version</a> in which the problems have been fixed.");
+		$.alert('NOTE: This version of Opera is known to contain bugs in SVG-edit.\n'+
+		'Please upgrade to the <a href="http://opera.com">latest version</a> in which the problems have been fixed.');
 	}
 	return retValue;
-})();
+}());
 
 var supportsNonScalingStroke_ = (function() {
-	var rect = document.createElementNS(svgns, 'rect');
-	rect.setAttribute('style','vector-effect:non-scaling-stroke');
+	var rect = document.createElementNS(NS.SVG, 'rect');
+	rect.setAttribute('style', 'vector-effect:non-scaling-stroke');
 	return rect.style.vectorEffect === 'non-scaling-stroke';
-})();
+}());
 
 var supportsNativeSVGTransformLists_ = (function() {
-	var rect = document.createElementNS(svgns, 'rect');
+	var rect = document.createElementNS(NS.SVG, 'rect');
 	var rxform = rect.transform.baseVal;
-	
 	var t1 = svg.createSVGTransform();
 	rxform.appendItem(t1);
 	return rxform.getItem(0) == t1;
-})();
+}());
 
 // Public API
 
-svgedit.browser.isOpera = function() { return isOpera_; }
-svgedit.browser.isWebkit = function() { return isWebkit_; }
-svgedit.browser.isGecko = function() { return isGecko_; }
-svgedit.browser.isIE = function() { return isIE_; }
-svgedit.browser.isChrome = function() { return isChrome_; }
-svgedit.browser.isWindows = function() { return isWindows_; }
-
-svgedit.browser.supportsSelectors = function() { return supportsSelectors_; }
-svgedit.browser.supportsXpath = function() { return supportsXpath_; }
-
-svgedit.browser.supportsPathReplaceItem = function() { return supportsPathReplaceItem_; }
-svgedit.browser.supportsPathInsertItemBefore = function() { return supportsPathInsertItemBefore_; }
-svgedit.browser.supportsPathBBox = function() { return supportsPathBBox_; }
-svgedit.browser.supportsHVLineContainerBBox = function() { return supportsHVLineContainerBBox_; }
-svgedit.browser.supportsGoodTextCharPos = function() { return supportsGoodTextCharPos_; }
-svgedit.browser.supportsEditableText = function() { return supportsEditableText_; }
-svgedit.browser.supportsGoodDecimals = function() { return supportsGoodDecimals_; }
-svgedit.browser.supportsNonScalingStroke = function() { return supportsNonScalingStroke_; }
-svgedit.browser.supportsNativeTransformLists = function() { return supportsNativeSVGTransformLists_; }
-
-}
-
-})();
+svgedit.browser.isOpera = function() { return isOpera_; };
+svgedit.browser.isWebkit = function() { return isWebkit_; };
+svgedit.browser.isGecko = function() { return isGecko_; };
+svgedit.browser.isIE = function() { return isIE_; };
+svgedit.browser.isChrome = function() { return isChrome_; };
+svgedit.browser.isWindows = function() { return isWindows_; };
+svgedit.browser.isMac = function() { return isMac_; };
+svgedit.browser.isTouch = function() { return isTouch_; };
+
+svgedit.browser.supportsSelectors = function() { return supportsSelectors_; };
+svgedit.browser.supportsXpath = function() { return supportsXpath_; };
+
+svgedit.browser.supportsPathReplaceItem = function() { return supportsPathReplaceItem_; };
+svgedit.browser.supportsPathInsertItemBefore = function() { return supportsPathInsertItemBefore_; };
+svgedit.browser.supportsPathBBox = function() { return supportsPathBBox_; };
+svgedit.browser.supportsHVLineContainerBBox = function() { return supportsHVLineContainerBBox_; };
+svgedit.browser.supportsGoodTextCharPos = function() { return supportsGoodTextCharPos_; };
+svgedit.browser.supportsEditableText = function() { return supportsEditableText_; };
+svgedit.browser.supportsGoodDecimals = function() { return supportsGoodDecimals_; };
+svgedit.browser.supportsNonScalingStroke = function() { return supportsNonScalingStroke_; };
+svgedit.browser.supportsNativeTransformLists = function() { return supportsNativeSVGTransformLists_; };
+
+}());

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 248 - 186
main/inc/lib/svg-edit/canvg/canvg.js


+ 0 - 6
main/inc/lib/svg-edit/canvg/index.html

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 31 - 24
main/inc/lib/svg-edit/canvg/rgbcolor.js

@@ -1,15 +1,15 @@
+/*jslint vars: true*/
 /**
  * A class to parse color values
  * @author Stoyan Stefanov <sstoo@gmail.com>
  * @link   http://www.phpied.com/rgb-color-parser-in-javascript/
  * @license Use it if you like it
  */
-function RGBColor(color_string)
-{
+function RGBColor(color_string) { 'use strict';
     this.ok = false;
 
     // strip any leading #
-    if (color_string.charAt(0) == '#') { // remove # if any
+    if (color_string.charAt(0) === '#') { // remove # if any
         color_string = color_string.substr(1,6);
     }
 
@@ -163,9 +163,12 @@ function RGBColor(color_string)
         yellow: 'ffff00',
         yellowgreen: '9acd32'
     };
-    for (var key in simple_colors) {
-        if (color_string == key) {
-            color_string = simple_colors[key];
+    var key;
+    for (key in simple_colors) {
+        if (simple_colors.hasOwnProperty(key)) {
+            if (color_string == key) {
+                color_string = simple_colors[key];
+            }
         }
     }
     // emd of simple type-in colors
@@ -177,9 +180,9 @@ function RGBColor(color_string)
             example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
             process: function (bits){
                 return [
-                    parseInt(bits[1]),
-                    parseInt(bits[2]),
-                    parseInt(bits[3])
+                    parseInt(bits[1], 10),
+                    parseInt(bits[2], 10),
+                    parseInt(bits[3], 10)
                 ];
             }
         },
@@ -207,13 +210,14 @@ function RGBColor(color_string)
         }
     ];
 
+    var i;
     // search through the definitions to find a match
-    for (var i = 0; i < color_defs.length; i++) {
+    for (i = 0; i < color_defs.length; i++) {
         var re = color_defs[i].re;
         var processor = color_defs[i].process;
         var bits = re.exec(color_string);
         if (bits) {
-            channels = processor(bits);
+            var channels = processor(bits);
             this.r = channels[0];
             this.g = channels[1];
             this.b = channels[2];
@@ -230,36 +234,39 @@ function RGBColor(color_string)
     // some getters
     this.toRGB = function () {
         return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
-    }
+    };
     this.toHex = function () {
         var r = this.r.toString(16);
         var g = this.g.toString(16);
         var b = this.b.toString(16);
-        if (r.length == 1) r = '0' + r;
-        if (g.length == 1) g = '0' + g;
-        if (b.length == 1) b = '0' + b;
+        if (r.length === 1) {r = '0' + r;}
+        if (g.length === 1) {g = '0' + g;}
+        if (b.length === 1) {b = '0' + b;}
         return '#' + r + g + b;
-    }
+    };
 
     // help
     this.getHelpXML = function () {
-
-        var examples = new Array();
+        var i, j;
+        var examples = [];
         // add regexps
-        for (var i = 0; i < color_defs.length; i++) {
+        for (i = 0; i < color_defs.length; i++) {
             var example = color_defs[i].example;
-            for (var j = 0; j < example.length; j++) {
+            for (j = 0; j < example.length; j++) {
                 examples[examples.length] = example[j];
             }
         }
         // add type-in colors
-        for (var sc in simple_colors) {
-            examples[examples.length] = sc;
+        var sc;
+        for (sc in simple_colors) {
+            if (simple_colors.hasOwnProperty(sc)) {
+                examples[examples.length] = sc;
+            }
         }
 
         var xml = document.createElement('ul');
         xml.setAttribute('id', 'rgbcolor-examples');
-        for (var i = 0; i < examples.length; i++) {
+        for (i = 0; i < examples.length; i++) {
             try {
                 var list_item = document.createElement('li');
                 var list_color = new RGBColor(examples[i]);
@@ -282,6 +289,6 @@ function RGBColor(color_string)
         }
         return xml;
 
-    }
+    };
 
 }

+ 145 - 0
main/inc/lib/svg-edit/config-sample.js

@@ -0,0 +1,145 @@
+// DO NOT EDIT THIS FILE!
+// THIS FILE IS JUST A SAMPLE; TO APPLY, YOU MUST
+//   CREATE A NEW FILE config.js AND ADD CONTENTS
+//   SUCH AS SHOWN BELOW INTO THAT FILE.
+
+/*globals svgEditor*/
+/*
+The config.js file is intended for the setting of configuration or
+  preferences which must run early on; if this is not needed, it is
+  recommended that you create an extension instead (for greater
+  reusability and modularity).
+*/
+
+// CONFIG AND EXTENSION SETTING
+/*
+See defaultConfig and defaultExtensions in svg-editor.js for a list
+  of possible configuration settings.
+
+See svg-editor.js for documentation on using setConfig().
+*/
+
+// URL OVERRIDE CONFIG
+svgEditor.setConfig({
+	/**
+	To override the ability for URLs to set URL-based SVG content,
+	    uncomment the following:
+	*/
+	// preventURLContentLoading: true,
+	/**
+	To override the ability for URLs to set other configuration (including
+	    extensions), uncomment the following:
+	*/
+	// preventAllURLConfig: true,
+	/**
+	To override the ability for URLs to set their own extensions,
+	  uncomment the following (note that if setConfig() is used in
+	  extension code, it will still be additive to extensions,
+	  however):
+	*/
+	// lockExtensions: true,
+});
+
+svgEditor.setConfig({
+	/*
+	Provide default values here which differ from that of the editor but
+		which the URL can override
+	*/
+}, {allowInitialUserOverride: true});
+
+// EXTENSION CONFIG
+svgEditor.setConfig({
+	extensions: [
+		// 'ext-overview_window.js', 'ext-markers.js', 'ext-connector.js', 'ext-eyedropper.js', 'ext-shapes.js', 'ext-imagelib.js', 'ext-grid.js', 'ext-polygon.js', 'ext-star.js', 'ext-panning.js', 'ext-storage.js'
+	]
+	// , noDefaultExtensions: false, // noDefaultExtensions can only be meaningfully used in config.js or in the URL
+});
+
+// OTHER CONFIG
+svgEditor.setConfig({	
+	// canvasName: 'default',
+	// canvas_expansion: 3,
+	// initFill: {
+		// color: 'FF0000', // solid red
+		// opacity: 1
+	// },
+	// initStroke: {
+		// width: 5,
+		// color: '000000', // solid black
+		// opacity: 1
+	// },
+	// initOpacity: 1,
+	// colorPickerCSS: null,
+	// initTool: 'select',
+	// wireframe: false,
+	// showlayers: false,
+	// no_save_warning: false,
+	// PATH CONFIGURATION
+	// imgPath: 'images/',
+	// langPath: 'locale/',
+	// extPath: 'extensions/',
+	// jGraduatePath: 'jgraduate/images/',
+	/*
+	Uncomment the following to allow at least same domain (embedded) access,
+	including file:// access.
+	Setting as `['*']` would allow any domain to access but would be unsafe to
+	data privacy and integrity.
+	*/
+	// allowedOrigins: [window.location.origin || 'null'], // May be 'null' (as a string) when used as a file:// URL
+	// DOCUMENT PROPERTIES
+	// dimensions: [640, 480],
+	// EDITOR OPTIONS
+	// gridSnapping: false,
+	// gridColor: '#000',
+	// baseUnit: 'px',
+	// snappingStep: 10,
+	// showRulers: true,
+	// EXTENSION-RELATED (GRID)
+	// showGrid: false, // Set by ext-grid.js
+	// EXTENSION-RELATED (STORAGE)
+	// noStorageOnLoad: false, // Some interaction with ext-storage.js; prevent even the loading of previously saved local storage
+	// forceStorage: false, // Some interaction with ext-storage.js; strongly discouraged from modification as it bypasses user privacy by preventing them from choosing whether to keep local storage or not
+	// emptyStorageOnDecline: true, // Used by ext-storage.js; empty any prior storage if the user declines to store
+});
+
+// PREF CHANGES
+/**
+setConfig() can also be used to set preferences in addition to
+  configuration (see defaultPrefs in svg-editor.js for a list of
+  possible settings), but at least if you are using ext-storage.js
+  to store preferences, it will probably be better to let your
+  users control these.
+As with configuration, one may use allowInitialUserOverride, but
+  in the case of preferences, any previously stored preferences
+  will also thereby be enabled to override this setting (and at a
+  higher priority than any URL preference setting overrides).
+  Failing to use allowInitialUserOverride will ensure preferences
+  are hard-coded here regardless of URL or prior user storage setting.
+*/
+svgEditor.setConfig(
+	{
+		// lang: '', // Set dynamically within locale.js if not previously set
+		// iconsize: '', // Will default to 's' if the window height is smaller than the minimum height and 'm' otherwise
+		/**
+		* When showing the preferences dialog, svg-editor.js currently relies
+		* on curPrefs instead of $.pref, so allowing an override for bkgd_color
+		* means that this value won't have priority over block auto-detection as
+		* far as determining which color shows initially in the preferences
+		* dialog (though it can be changed and saved).
+		*/
+		// bkgd_color: '#FFF',
+		// bkgd_url: '',
+		// img_save: 'embed',
+		// Only shows in UI as far as alert notices
+		// save_notice_done: false,
+		// export_notice_done: false
+	}
+);
+svgEditor.setConfig(
+	{
+		// Indicate pref settings here if you wish to allow user storage or URL settings
+		//   to be able to override your default preferences (unless other config options
+		//   have already explicitly prevented one or the other)
+	},
+	{allowInitialUserOverride: true}
+);

+ 17 - 0
main/inc/lib/svg-edit/config.js

@@ -0,0 +1,17 @@
+/*globals svgEditor*/
+/* see https://code.google.com/p/svg-edit/wiki/ConfigOptions */
+svgEditor.setConfig({
+extensions: [
+    'ext-php_savefile_chamilo.js',
+	'ext-eyedropper.js',
+	'ext-shapes.js',
+	'ext-polygon.js',
+	'ext-star.js'
+],
+    noStorageOnLoad: 'true',
+    selectNew: true,
+    no_save_warning: true,
+    emptyStorageOnDecline: true,
+    iconsize: 'm',
+	allowedOrigins: [window.location.origin] // May be 'null' (as a string) when used as a file:// URL
+});

+ 17 - 18
main/inc/lib/svg-edit/contextmenu.js

@@ -1,3 +1,5 @@
+/*globals $, svgEditor*/
+/*jslint vars: true, eqeq: true*/
 /**
  * Package: svgedit.contextmenu
  * 
@@ -13,55 +15,52 @@ var svgedit = svgedit || {};
 	if (!svgedit.contextmenu) {
 		svgedit.contextmenu = {};
 	}
-	self.contextMenuExtensions = {}
+	self.contextMenuExtensions = {};
+	var menuItemIsValid = function(menuItem) {
+		return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action == 'function';
+	};
 	var addContextMenuItem = function(menuItem) {
 		// menuItem: {id, label, shortcut, action}
 		if (!menuItemIsValid(menuItem)) {
-			console
-					.error("Menu items must be defined and have at least properties: id, label, action, where action must be a function");
+			console.error("Menu items must be defined and have at least properties: id, label, action, where action must be a function");
 			return;
 		}
 		if (menuItem.id in self.contextMenuExtensions) {
-			console.error('Cannot add extension "' + menuItem.id
-					+ '", an extension by that name already exists"');
+			console.error('Cannot add extension "' + menuItem.id + '", an extension by that name already exists"');
 			return;
 		}
 		// Register menuItem action, see below for deferred menu dom injection
 		console.log("Registed contextmenu item: {id:"+ menuItem.id+", label:"+menuItem.label+"}");
 		self.contextMenuExtensions[menuItem.id] = menuItem;
 		//TODO: Need to consider how to handle custom enable/disable behavior
-	}
+	};
 	var hasCustomHandler = function(handlerKey) {
 		return self.contextMenuExtensions[handlerKey] && true;
-	}
+	};
 	var getCustomHandler = function(handlerKey) {
 		return self.contextMenuExtensions[handlerKey].action;
-	}
+	};
 	var injectExtendedContextMenuItemIntoDom = function(menuItem) {
-		if (Object.keys(self.contextMenuExtensions).length == 0) {
+		if (Object.keys(self.contextMenuExtensions).length === 0) {
 			// all menuItems appear at the bottom of the menu in their own container.
 			// if this is the first extension menu we need to add the separator.
 			$("#cmenu_canvas").append("<li class='separator'>");
 		}
 		var shortcut = menuItem.shortcut || "";
-		$("#cmenu_canvas").append("<li class='disabled'><a href='#" + menuItem.id + "'>"                   
+		$("#cmenu_canvas").append("<li class='disabled'><a href='#" + menuItem.id + "'>"
 									+ menuItem.label + "<span class='shortcut'>"
 									+ shortcut + "</span></a></li>");
-	}
-
-	var menuItemIsValid = function(menuItem) {
-		return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action == 'function';
-	}
-	
+	};
 	// Defer injection to wait out initial menu processing. This probably goes away once all context
 	// menu behavior is brought here.
 	svgEditor.ready(function() {
+		var menuItem;
 		for (menuItem in contextMenuExtensions) {
 			injectExtendedContextMenuItemIntoDom(contextMenuExtensions[menuItem]);
 		}
 	});
-	svgedit.contextmenu.resetCustomMenus = function(){self.contextMenuExtensions = {}}
+	svgedit.contextmenu.resetCustomMenus = function(){self.contextMenuExtensions = {};};
 	svgedit.contextmenu.add = addContextMenuItem;
 	svgedit.contextmenu.hasCustomHandler = hasCustomHandler;
 	svgedit.contextmenu.getCustomHandler = getCustomHandler;
-})();
+}());

+ 0 - 6
main/inc/lib/svg-edit/contextmenu/index.html

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 2 - 3
main/inc/lib/svg-edit/contextmenu/jquery.contextMenu.js

@@ -36,14 +36,13 @@ if(jQuery)( function() {
 
 				// Add contextMenu class
 				menu.addClass('contextMenu');
-				
 				// Simulate a true right click
-				$(this).mousedown( function(e) {
+				$(this).bind( "mousedown", function(e) {
 					var evt = e;
 					$(this).mouseup( function(e) {
 						var srcElement = $(this);
 						srcElement.unbind('mouseup');
-						if( evt.button === 2 || o.allowLeft) {
+						if( evt.button === 2 || o.allowLeft || (evt.ctrlKey && svgedit.browser.isMac()) ) {
 							e.stopPropagation();
 							// Hide context menus that may be showing
 							$(".contextMenu").hide();

+ 0 - 22
main/inc/lib/svg-edit/contextmenu/jquery.contextMenu.min.js

@@ -1,22 +0,0 @@
-// jQuery Context Menu Plugin
-//
-// Version 1.01
-//
-// Cory S.N. LaViska
-// A Beautiful Site (http://abeautifulsite.net/)
-//
-// Modifications by Alexis Deveria
-//
-// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/
-//
-// Terms of Use
-//
-// This plugin is dual-licensed under the GNU General Public License
-//   and the MIT License and is copyright A Beautiful Site, LLC.
-//
-jQuery&&function(){var i=$(window),e=$(document);$.extend($.fn,{contextMenu:function(a,d){if(a.menu==undefined)return false;if(a.inSpeed==undefined)a.inSpeed=150;if(a.outSpeed==undefined)a.outSpeed=75;if(a.inSpeed==0)a.inSpeed=-1;if(a.outSpeed==0)a.outSpeed=-1;$(this).each(function(){var c=$(this),j=$(c).offset(),b=$("#"+a.menu);b.addClass("contextMenu");$(this).mousedown(function(m){$(this).mouseup(function(f){var k=$(this);k.unbind("mouseup");if(m.button===2||a.allowLeft){f.stopPropagation();$(".contextMenu").hide();
-if(c.hasClass("disabled"))return false;var g=f.pageX,h=f.pageY;f=i.width()-b.width();var l=i.height()-b.height();if(g>f-15)g=f-15;if(h>l-30)h=l-30;e.unbind("click");b.css({top:h,left:g}).fadeIn(a.inSpeed);b.find("A").mouseover(function(){b.find("LI.hover").removeClass("hover");$(this).parent().addClass("hover")}).mouseout(function(){b.find("LI.hover").removeClass("hover")});e.keypress(function(n){switch(n.keyCode){case 38:if(b.find("LI.hover").length){b.find("LI.hover").removeClass("hover").prevAll("LI:not(.disabled)").eq(0).addClass("hover");
-b.find("LI.hover").length||b.find("LI:last").addClass("hover")}else b.find("LI:last").addClass("hover");break;case 40:if(b.find("LI.hover").length==0)b.find("LI:first").addClass("hover");else{b.find("LI.hover").removeClass("hover").nextAll("LI:not(.disabled)").eq(0).addClass("hover");b.find("LI.hover").length||b.find("LI:first").addClass("hover")}break;case 13:b.find("LI.hover A").trigger("click");break;case 27:e.trigger("click")}});b.find("A").unbind("mouseup");b.find("LI:not(.disabled) A").mouseup(function(){e.unbind("click").unbind("keypress");
-$(".contextMenu").hide();d&&d($(this).attr("href").substr(1),$(k),{x:g-j.left,y:h-j.top,docX:g,docY:h});return false});setTimeout(function(){e.click(function(){e.unbind("click").unbind("keypress");b.fadeOut(a.outSpeed);return false})},0)}})});if($.browser.mozilla)$("#"+a.menu).each(function(){$(this).css({MozUserSelect:"none"})});else $.browser.msie?$("#"+a.menu).each(function(){$(this).bind("selectstart.disableTextSelect",function(){return false})}):$("#"+a.menu).each(function(){$(this).bind("mousedown.disableTextSelect",
-function(){return false})});$(c).add($("UL.contextMenu")).bind("contextmenu",function(){return false})});return $(this)},disableContextMenuItems:function(a){if(a==undefined){$(this).find("LI").addClass("disabled");return $(this)}$(this).each(function(){if(a!=undefined)for(var d=a.split(","),c=0;c<d.length;c++)$(this).find('A[href="'+d[c]+'"]').parent().addClass("disabled")});return $(this)},enableContextMenuItems:function(a){if(a==undefined){$(this).find("LI.disabled").removeClass("disabled");return $(this)}$(this).each(function(){if(a!=
-undefined)for(var d=a.split(","),c=0;c<d.length;c++)$(this).find('A[href="'+d[c]+'"]').parent().removeClass("disabled")});return $(this)},disableContextMenu:function(){$(this).each(function(){$(this).addClass("disabled")});return $(this)},enableContextMenu:function(){$(this).each(function(){$(this).removeClass("disabled")});return $(this)},destroyContextMenu:function(){$(this).each(function(){$(this).unbind("mousedown").unbind("mouseup")});return $(this)}})}(jQuery);

+ 316 - 0
main/inc/lib/svg-edit/coords.js

@@ -0,0 +1,316 @@
+/*globals $, svgroot */
+/*jslint vars: true, eqeq: true, forin: true*/
+/**
+ * Coords.
+ *
+ * Licensed under the MIT License
+ *
+ */
+
+// Dependencies:
+// 1) jquery.js
+// 2) math.js
+// 3) browser.js
+// 4) svgutils.js
+// 5) units.js
+// 6) svgtransformlist.js
+
+var svgedit = svgedit || {};
+
+(function() {'use strict';
+
+if (!svgedit.coords) {
+  svgedit.coords = {};
+}
+
+// this is how we map paths to our preferred relative segment types
+var pathMap = [0, 'z', 'M', 'm', 'L', 'l', 'C', 'c', 'Q', 'q', 'A', 'a', 
+    'H', 'h', 'V', 'v', 'S', 's', 'T', 't'];
+
+/**
+ * @typedef editorContext
+ * @type {?object}
+ * @property {function} getGridSnapping
+ * @property {function} getDrawing
+*/
+var editorContext_ = null;
+
+/**
+* @param {editorContext} editorContext
+*/
+svgedit.coords.init = function(editorContext) {
+  editorContext_ = editorContext;
+};
+
+/**
+ * Applies coordinate changes to an element based on the given matrix
+ * @param {Element} selected - DOM element to be changed
+ * @param {object} changes - Object with changes to be remapped
+ * @param {SVGMatrix} m - Matrix object to use for remapping coordinates
+*/
+svgedit.coords.remapElement = function(selected, changes, m) {
+  var i, type,
+    remap = function(x, y) { return svgedit.math.transformPoint(x, y, m); },
+    scalew = function(w) { return m.a * w; },
+    scaleh = function(h) { return m.d * h; },
+    doSnapping = editorContext_.getGridSnapping() && selected.parentNode.parentNode.localName === 'svg',
+    finishUp = function() {
+      var o;
+      if (doSnapping) {
+        for (o in changes) {
+          changes[o] = svgedit.utilities.snapToGrid(changes[o]);
+        }
+      }
+      svgedit.utilities.assignAttributes(selected, changes, 1000, true);
+    },
+    box = svgedit.utilities.getBBox(selected);
+
+  for (i = 0; i < 2; i++) {
+    type = i === 0 ? 'fill' : 'stroke';
+    var attrVal = selected.getAttribute(type);
+    if (attrVal && attrVal.indexOf('url(') === 0) {
+      if (m.a < 0 || m.d < 0) {
+        var grad = svgedit.utilities.getRefElem(attrVal);
+        var newgrad = grad.cloneNode(true);
+        if (m.a < 0) {
+          // flip x
+          var x1 = newgrad.getAttribute('x1');
+          var x2 = newgrad.getAttribute('x2');
+          newgrad.setAttribute('x1', -(x1 - 1));
+          newgrad.setAttribute('x2', -(x2 - 1));
+        } 
+
+        if (m.d < 0) {
+          // flip y
+          var y1 = newgrad.getAttribute('y1');
+          var y2 = newgrad.getAttribute('y2');
+          newgrad.setAttribute('y1', -(y1 - 1));
+          newgrad.setAttribute('y2', -(y2 - 1));
+        }
+        newgrad.id = editorContext_.getDrawing().getNextId();
+        svgedit.utilities.findDefs().appendChild(newgrad);
+        selected.setAttribute(type, 'url(#' + newgrad.id + ')');
+      }
+
+      // Not really working :(
+//      if (selected.tagName === 'path') {
+//        reorientGrads(selected, m);
+//      }
+    }
+  }
+
+  var elName = selected.tagName;
+  var chlist, mt;
+  if (elName === 'g' || elName === 'text' || elName == 'tspan' || elName === 'use') {
+    // if it was a translate, then just update x,y
+    if (m.a == 1 && m.b == 0 && m.c == 0 && m.d == 1 && (m.e != 0 || m.f != 0) ) {
+      // [T][M] = [M][T']
+      // therefore [T'] = [M_inv][T][M]
+      var existing = svgedit.math.transformListToTransform(selected).matrix,
+          t_new = svgedit.math.matrixMultiply(existing.inverse(), m, existing);
+      changes.x = parseFloat(changes.x) + t_new.e;
+      changes.y = parseFloat(changes.y) + t_new.f;
+    } else {
+      // we just absorb all matrices into the element and don't do any remapping
+      chlist = svgedit.transformlist.getTransformList(selected);
+      mt = svgroot.createSVGTransform();
+      mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m));
+      chlist.clear();
+      chlist.appendItem(mt);
+    }
+  }
+  var c, pt, pt1, pt2, len;
+  // now we have a set of changes and an applied reduced transform list
+  // we apply the changes directly to the DOM
+  switch (elName) {
+    case 'foreignObject':
+    case 'rect':
+    case 'image':
+      // Allow images to be inverted (give them matrix when flipped)
+      if (elName === 'image' && (m.a < 0 || m.d < 0)) {
+        // Convert to matrix
+        chlist = svgedit.transformlist.getTransformList(selected);
+        mt = svgroot.createSVGTransform();
+        mt.setMatrix(svgedit.math.matrixMultiply(svgedit.math.transformListToTransform(chlist).matrix, m));
+        chlist.clear();
+        chlist.appendItem(mt);
+      } else {
+        pt1 = remap(changes.x, changes.y);
+        changes.width = scalew(changes.width);
+        changes.height = scaleh(changes.height);
+        changes.x = pt1.x + Math.min(0, changes.width);
+        changes.y = pt1.y + Math.min(0, changes.height);
+        changes.width = Math.abs(changes.width);
+        changes.height = Math.abs(changes.height);
+      }
+      finishUp();
+      break;
+    case 'ellipse':
+      c = remap(changes.cx, changes.cy);
+      changes.cx = c.x;
+      changes.cy = c.y;
+      changes.rx = scalew(changes.rx);
+      changes.ry = scaleh(changes.ry);
+      changes.rx = Math.abs(changes.rx);
+      changes.ry = Math.abs(changes.ry);
+      finishUp();
+      break;
+    case 'circle':
+      c = remap(changes.cx,changes.cy);
+      changes.cx = c.x;
+      changes.cy = c.y;
+      // take the minimum of the new selected box's dimensions for the new circle radius
+      var tbox = svgedit.math.transformBox(box.x, box.y, box.width, box.height, m);
+      var w = tbox.tr.x - tbox.tl.x, h = tbox.bl.y - tbox.tl.y;
+      changes.r = Math.min(w/2, h/2);
+
+      if (changes.r) {changes.r = Math.abs(changes.r);}
+      finishUp();
+      break;
+    case 'line':
+      pt1 = remap(changes.x1, changes.y1);
+      pt2 = remap(changes.x2, changes.y2);
+      changes.x1 = pt1.x;
+      changes.y1 = pt1.y;
+      changes.x2 = pt2.x;
+      changes.y2 = pt2.y;
+      // deliberately fall through here
+    case 'text':
+    case 'tspan':
+    case 'use':
+      finishUp();
+      break;
+    case 'g':
+      var gsvg = $(selected).data('gsvg');
+      if (gsvg) {
+          svgedit.utilities.assignAttributes(gsvg, changes, 1000, true);
+      }
+      break;
+    case 'polyline':
+    case 'polygon':
+      len = changes.points.length;
+      for (i = 0; i < len; ++i) {
+        pt = changes.points[i];
+        pt = remap(pt.x, pt.y);
+        changes.points[i].x = pt.x;
+        changes.points[i].y = pt.y;
+      }
+
+      len = changes.points.length;
+      var pstr = '';
+      for (i = 0; i < len; ++i) {
+        pt = changes.points[i];
+        pstr += pt.x + ',' + pt.y + ' ';
+      }
+      selected.setAttribute('points', pstr);
+      break;
+    case 'path':
+      var seg;
+      var segList = selected.pathSegList;
+      len = segList.numberOfItems;
+      changes.d = [];
+      for (i = 0; i < len; ++i) {
+          seg = segList.getItem(i);
+          changes.d[i] = {
+              type: seg.pathSegType,
+              x: seg.x,
+              y: seg.y,
+              x1: seg.x1,
+              y1: seg.y1,
+              x2: seg.x2,
+              y2: seg.y2,
+              r1: seg.r1,
+              r2: seg.r2,
+              angle: seg.angle,
+              largeArcFlag: seg.largeArcFlag,
+              sweepFlag: seg.sweepFlag
+          };
+      }
+
+      len = changes.d.length;
+      var firstseg = changes.d[0],
+          currentpt = remap(firstseg.x, firstseg.y);
+      changes.d[0].x = currentpt.x;
+      changes.d[0].y = currentpt.y;
+      for (i = 1; i < len; ++i) {
+        seg = changes.d[i];
+        type = seg.type;
+        // if absolute or first segment, we want to remap x, y, x1, y1, x2, y2
+        // if relative, we want to scalew, scaleh
+        if (type % 2 == 0) { // absolute
+          var thisx = (seg.x != undefined) ? seg.x : currentpt.x, // for V commands
+              thisy = (seg.y != undefined) ? seg.y : currentpt.y; // for H commands
+          pt = remap(thisx,thisy);
+          pt1 = remap(seg.x1, seg.y1);
+          pt2 = remap(seg.x2, seg.y2);
+          seg.x = pt.x;
+          seg.y = pt.y;
+          seg.x1 = pt1.x;
+          seg.y1 = pt1.y;
+          seg.x2 = pt2.x;
+          seg.y2 = pt2.y;
+          seg.r1 = scalew(seg.r1);
+          seg.r2 = scaleh(seg.r2);
+        }
+        else { // relative
+          seg.x = scalew(seg.x);
+          seg.y = scaleh(seg.y);
+          seg.x1 = scalew(seg.x1);
+          seg.y1 = scaleh(seg.y1);
+          seg.x2 = scalew(seg.x2);
+          seg.y2 = scaleh(seg.y2);
+          seg.r1 = scalew(seg.r1);
+          seg.r2 = scaleh(seg.r2);
+        }
+      } // for each segment
+
+      var dstr = '';
+      len = changes.d.length;
+      for (i = 0; i < len; ++i) {
+        seg = changes.d[i];
+        type = seg.type;
+        dstr += pathMap[type];
+        switch (type) {
+            case 13: // relative horizontal line (h)
+            case 12: // absolute horizontal line (H)
+                dstr += seg.x + ' ';
+                break;
+            case 15: // relative vertical line (v)
+            case 14: // absolute vertical line (V)
+                dstr += seg.y + ' ';
+                break;
+            case 3: // relative move (m)
+            case 5: // relative line (l)
+            case 19: // relative smooth quad (t)
+            case 2: // absolute move (M)
+            case 4: // absolute line (L)
+            case 18: // absolute smooth quad (T)
+                dstr += seg.x + ',' + seg.y + ' ';
+                break;
+            case 7: // relative cubic (c)
+            case 6: // absolute cubic (C)
+                dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x2 + ',' + seg.y2 + ' ' +
+                     seg.x + ',' + seg.y + ' ';
+                break;
+            case 9: // relative quad (q) 
+            case 8: // absolute quad (Q)
+                dstr += seg.x1 + ',' + seg.y1 + ' ' + seg.x + ',' + seg.y + ' ';
+                break;
+            case 11: // relative elliptical arc (a)
+            case 10: // absolute elliptical arc (A)
+                dstr += seg.r1 + ',' + seg.r2 + ' ' + seg.angle + ' ' + (+seg.largeArcFlag) +
+                    ' ' + (+seg.sweepFlag) + ' ' + seg.x + ',' + seg.y + ' ';
+                break;
+            case 17: // relative smooth cubic (s)
+            case 16: // absolute smooth cubic (S)
+                dstr += seg.x2 + ',' + seg.y2 + ' ' + seg.x + ',' + seg.y + ' ';
+                break;
+          }
+      }
+
+      selected.setAttribute('d', dstr);
+      break;
+    }
+};
+
+}());

+ 6 - 0
main/inc/lib/svg-edit/custom.css

@@ -0,0 +1,6 @@
+#main_icon {
+    width:38px !important;
+}
+#tools_top {
+    left: 55px !important;
+}

+ 188 - 179
main/inc/lib/svg-edit/draw.js

@@ -1,7 +1,9 @@
+/*globals $, svgedit*/
+/*jslint vars: true, eqeq: true, todo: true*/
 /**
  * Package: svgedit.draw
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2011 Jeff Schiller
  */
@@ -11,20 +13,15 @@
 // 2) browser.js
 // 3) svgutils.js
 
-var svgedit = svgedit || {};
-
-(function() {
+(function() {'use strict';
 
 if (!svgedit.draw) {
 	svgedit.draw = {};
 }
+// alias
+var NS = svgedit.NS;
 
-var svg_ns = "http://www.w3.org/2000/svg";
-var se_ns = "http://svg-edit.googlecode.com";
-var xmlns_ns = "http://www.w3.org/2000/xmlns/";
-
-var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use';
-var visElems_arr = visElems.split(',');
+var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use'.split(',');
 
 var RandomizeModes = {
 	LET_DOCUMENT_DECIDE: 0,
@@ -35,52 +32,57 @@ var randomize_ids = RandomizeModes.LET_DOCUMENT_DECIDE;
 
 /**
  * This class encapsulates the concept of a layer in the drawing
- * @param name {String} Layer name
- * @param child {SVGGElement} Layer SVG group.
+ * @param {String} name - Layer name
+ * @param {SVGGElement} child - Layer SVG group.
  */
 svgedit.draw.Layer = function(name, group) {
 	this.name_ = name;
 	this.group_ = group;
 };
 
+/**
+ * @returns {string} The layer name
+ */
 svgedit.draw.Layer.prototype.getName = function() {
 	return this.name_;
 };
 
+/**
+ * @returns {SVGGElement} The layer SVG group
+ */
 svgedit.draw.Layer.prototype.getGroup = function() {
 	return this.group_;
 };
 
 
-// Called to ensure that drawings will or will not have randomized ids.
-// The current_drawing will have its nonce set if it doesn't already.
-// 
-// Params:
-// enableRandomization - flag indicating if documents should have randomized ids
-svgedit.draw.randomizeIds = function(enableRandomization, current_drawing) {
-	randomize_ids = enableRandomization == false ?
+/**
+ * Called to ensure that drawings will or will not have randomized ids.
+ * The currentDrawing will have its nonce set if it doesn't already.
+ * @param {boolean} enableRandomization - flag indicating if documents should have randomized ids
+ * @param {svgedit.draw.Drawing} currentDrawing
+ */
+svgedit.draw.randomizeIds = function(enableRandomization, currentDrawing) {
+	randomize_ids = enableRandomization === false ?
 		RandomizeModes.NEVER_RANDOMIZE :
 		RandomizeModes.ALWAYS_RANDOMIZE;
 
-	if (randomize_ids == RandomizeModes.ALWAYS_RANDOMIZE && !current_drawing.getNonce()) {
-		current_drawing.setNonce(Math.floor(Math.random() * 100001));
-	} else if (randomize_ids == RandomizeModes.NEVER_RANDOMIZE && current_drawing.getNonce()) {
-		current_drawing.clearNonce();
+	if (randomize_ids == RandomizeModes.ALWAYS_RANDOMIZE && !currentDrawing.getNonce()) {
+		currentDrawing.setNonce(Math.floor(Math.random() * 100001));
+	} else if (randomize_ids == RandomizeModes.NEVER_RANDOMIZE && currentDrawing.getNonce()) {
+		currentDrawing.clearNonce();
 	}
 };
 
 /**
  * This class encapsulates the concept of a SVG-edit drawing
- *
- * @param svgElem {SVGSVGElement} The SVG DOM Element that this JS object
+ * @param {SVGSVGElement} svgElem - The SVG DOM Element that this JS object
  *     encapsulates.  If the svgElem has a se:nonce attribute on it, then
  *     IDs will use the nonce as they are generated.
- * @param opt_idPrefix {String} The ID prefix to use.  Defaults to "svg_"
- *     if not specified.
+ * @param {String=svg_} [opt_idPrefix] - The ID prefix to use.
  */
 svgedit.draw.Drawing = function(svgElem, opt_idPrefix) {
 	if (!svgElem || !svgElem.tagName || !svgElem.namespaceURI ||
-		svgElem.tagName != 'svg' || svgElem.namespaceURI != svg_ns) {
+		svgElem.tagName != 'svg' || svgElem.namespaceURI != NS.SVG) {
 		throw "Error: svgedit.draw.Drawing instance initialized without a <svg> element";
 	}
 
@@ -127,8 +129,8 @@ svgedit.draw.Drawing = function(svgElem, opt_idPrefix) {
 	 * The nonce to use to uniquely identify elements across drawings.
 	 * @type {!String}
 	 */
-	this.nonce_ = "";
-	var n = this.svgElem_.getAttributeNS(se_ns, 'nonce');
+	this.nonce_ = '';
+	var n = this.svgElem_.getAttributeNS(NS.SE, 'nonce');
 	// If already set in the DOM, use the nonce throughout the document
 	// else, if randomizeIds(true) has been called, create and set the nonce.
 	if (!!n && randomize_ids != RandomizeModes.NEVER_RANDOMIZE) {
@@ -138,51 +140,66 @@ svgedit.draw.Drawing = function(svgElem, opt_idPrefix) {
 	}
 };
 
-svgedit.draw.Drawing.prototype.getElem_ = function(id) {
-	if(this.svgElem_.querySelector) {
+/**
+ * @param {string} id Element ID to retrieve
+ * @returns {Element} SVG element within the root SVGSVGElement
+*/
+svgedit.draw.Drawing.prototype.getElem_ = function (id) {
+	if (this.svgElem_.querySelector) {
 		// querySelector lookup
-		return this.svgElem_.querySelector('#'+id);
-	} else {
-		// jQuery lookup: twice as slow as xpath in FF
-		return $(this.svgElem_).find('[id=' + id + ']')[0];
+		return this.svgElem_.querySelector('#' + id);
 	}
+	// jQuery lookup: twice as slow as xpath in FF
+	return $(this.svgElem_).find('[id=' + id + ']')[0];
 };
 
-svgedit.draw.Drawing.prototype.getSvgElem = function() {
+/**
+ * @returns {SVGSVGElement}
+ */
+svgedit.draw.Drawing.prototype.getSvgElem = function () {
 	return this.svgElem_;
 };
 
+/**
+ * @returns {!string|number} The previously set nonce
+ */
 svgedit.draw.Drawing.prototype.getNonce = function() {
 	return this.nonce_;
 };
 
+/**
+ * @param {!string|number} n The nonce to set
+ */
 svgedit.draw.Drawing.prototype.setNonce = function(n) {
-	this.svgElem_.setAttributeNS(xmlns_ns, 'xmlns:se', se_ns);
-	this.svgElem_.setAttributeNS(se_ns, 'se:nonce', n);
+	this.svgElem_.setAttributeNS(NS.XMLNS, 'xmlns:se', NS.SE);
+	this.svgElem_.setAttributeNS(NS.SE, 'se:nonce', n);
 	this.nonce_ = n;
 };
 
-svgedit.draw.Drawing.prototype.clearNonce = function() {
+/**
+ * Clears any previously set nonce
+ */
+svgedit.draw.Drawing.prototype.clearNonce = function () {
 	// We deliberately leave any se:nonce attributes alone,
 	// we just don't use it to randomize ids.
-	this.nonce_ = "";
+	this.nonce_ = '';
 };
 
 /**
  * Returns the latest object id as a string.
  * @return {String} The latest object Id.
  */
-svgedit.draw.Drawing.prototype.getId = function() {
+svgedit.draw.Drawing.prototype.getId = function () {
 	return this.nonce_ ?
-		this.idPrefix + this.nonce_ +'_' + this.obj_num :
- 		this.idPrefix + this.obj_num;
+		this.idPrefix + this.nonce_ + '_' + this.obj_num :
+		this.idPrefix + this.obj_num;
 };
 
 /**
  * Returns the next object Id as a string.
  * @return {String} The next object Id to use.
  */
-svgedit.draw.Drawing.prototype.getNextId = function() {
+svgedit.draw.Drawing.prototype.getNextId = function () {
 	var oldObjNum = this.obj_num;
 	var restoreOldObjNum = false;
 
@@ -214,28 +231,25 @@ svgedit.draw.Drawing.prototype.getNextId = function() {
 	return id;
 };
 
-// Function: svgedit.draw.Drawing.releaseId
-// Releases the object Id, letting it be used as the next id in getNextId().
-// This method DOES NOT remove any elements from the DOM, it is expected
-// that client code will do this.
-//
-// Parameters:
-// id - The id to release.
-//
-// Returns:
-// True if the id was valid to be released, false otherwise.
-svgedit.draw.Drawing.prototype.releaseId = function(id) {
+/**
+ * Releases the object Id, letting it be used as the next id in getNextId().
+ * This method DOES NOT remove any elements from the DOM, it is expected
+ * that client code will do this.
+ * @param {string} id - The id to release.
+ * @returns {boolean} True if the id was valid to be released, false otherwise.
+*/
+svgedit.draw.Drawing.prototype.releaseId = function (id) {
 	// confirm if this is a valid id for this Document, else return false
-	var front = this.idPrefix + (this.nonce_ ? this.nonce_ +'_' : '');
-	if (typeof id != typeof '' || id.indexOf(front) != 0) {
+	var front = this.idPrefix + (this.nonce_ ? this.nonce_ + '_' : '');
+	if (typeof id !== 'string' || id.indexOf(front) !== 0) {
 		return false;
 	}
 	// extract the obj_num of this id
-	var num = parseInt(id.substr(front.length));
+	var num = parseInt(id.substr(front.length), 10);
 
 	// if we didn't get a positive number or we already released this number
 	// then return false.
-	if (typeof num != typeof 1 || num <= 0 || this.releasedNums.indexOf(num) != -1) {
+	if (typeof num !== 'number' || num <= 0 || this.releasedNums.indexOf(num) != -1) {
 		return false;
 	}
 	
@@ -245,73 +259,71 @@ svgedit.draw.Drawing.prototype.releaseId = function(id) {
 	return true;
 };
 
-// Function: svgedit.draw.Drawing.getNumLayers
-// Returns the number of layers in the current drawing.
-// 
-// Returns:
-// The number of layers in the current drawing.
+/**
+ * Returns the number of layers in the current drawing.
+ * @returns {integer} The number of layers in the current drawing.
+*/
 svgedit.draw.Drawing.prototype.getNumLayers = function() {
 	return this.all_layers.length;
 };
 
-// Function: svgedit.draw.Drawing.hasLayer
-// Check if layer with given name already exists
-svgedit.draw.Drawing.prototype.hasLayer = function(name) {
-	for(var i = 0; i < this.getNumLayers(); i++) {
-		if(this.all_layers[i][0] == name) return true;
+/**
+ * Check if layer with given name already exists
+ * @param {string} name - The layer name to check
+*/
+svgedit.draw.Drawing.prototype.hasLayer = function (name) {
+	var i;
+	for (i = 0; i < this.getNumLayers(); i++) {
+		if(this.all_layers[i][0] == name) {return true;}
 	}
 	return false;
 };
 
 
-// Function: svgedit.draw.Drawing.getLayerName
-// Returns the name of the ith layer. If the index is out of range, an empty string is returned.
-//
-// Parameters:
-// i - the zero-based index of the layer you are querying.
-// 
-// Returns:
-// The name of the ith layer
-svgedit.draw.Drawing.prototype.getLayerName = function(i) {
+/**
+ * Returns the name of the ith layer. If the index is out of range, an empty string is returned.
+ * @param {integer} i - The zero-based index of the layer you are querying.
+ * @returns {string} The name of the ith layer (or the empty string if none found)
+*/
+svgedit.draw.Drawing.prototype.getLayerName = function (i) {
 	if (i >= 0 && i < this.getNumLayers()) {
 		return this.all_layers[i][0];
 	}
-	return "";
+	return '';
 };
 
-// Function: svgedit.draw.Drawing.getCurrentLayer
-// Returns:
-// The SVGGElement representing the current layer.
+/**
+ * @returns {SVGGElement} The SVGGElement representing the current layer.
+ */
 svgedit.draw.Drawing.prototype.getCurrentLayer = function() {
 	return this.current_layer;
 };
 
-// Function: getCurrentLayerName
-// Returns the name of the currently selected layer. If an error occurs, an empty string 
-// is returned.
-//
-// Returns:
-// The name of the currently active layer.
-svgedit.draw.Drawing.prototype.getCurrentLayerName = function() {
-	for (var i = 0; i < this.getNumLayers(); ++i) {
+/**
+ * Returns the name of the currently selected layer. If an error occurs, an empty string 
+ * is returned.
+ * @returns The name of the currently active layer (or the empty string if none found).
+*/
+svgedit.draw.Drawing.prototype.getCurrentLayerName = function () {
+	var i;
+	for (i = 0; i < this.getNumLayers(); ++i) {
 		if (this.all_layers[i][1] == this.current_layer) {
 			return this.getLayerName(i);
 		}
 	}
-	return "";
+	return '';
 };
 
-// Function: setCurrentLayer
-// Sets the current layer. If the name is not a valid layer name, then this function returns
-// false. Otherwise it returns true. This is not an undo-able action.
-//
-// Parameters:
-// name - the name of the layer you want to switch to.
-//
-// Returns:
-// true if the current layer was switched, otherwise false
+/**
+ * Sets the current layer. If the name is not a valid layer name, then this
+ * function returns false. Otherwise it returns true. This is not an
+ * undo-able action.
+ * @param {string} name - The name of the layer you want to switch to.
+ * @returns {boolean} true if the current layer was switched, otherwise false
+ */
 svgedit.draw.Drawing.prototype.setCurrentLayer = function(name) {
-	for (var i = 0; i < this.getNumLayers(); ++i) {
+	var i;
+	for (i = 0; i < this.getNumLayers(); ++i) {
 		if (name == this.getLayerName(i)) {
 			if (this.current_layer != this.all_layers[i][1]) {
 				this.current_layer.setAttribute("style", "pointer-events:none");
@@ -325,11 +337,11 @@ svgedit.draw.Drawing.prototype.setCurrentLayer = function(name) {
 };
 
 
-// Function: svgedit.draw.Drawing.deleteCurrentLayer
-// Deletes the current layer from the drawing and then clears the selection. This function 
-// then calls the 'changed' handler.  This is an undoable action.
-// Returns:
-// The SVGGElement of the layer removed or null.
+/**
+ * Deletes the current layer from the drawing and then clears the selection.
+ * This function then calls the 'changed' handler.  This is an undoable action.
+ * @returns {SVGGElement} The SVGGElement of the layer removed or null.
+ */
 svgedit.draw.Drawing.prototype.deleteCurrentLayer = function() {
 	if (this.current_layer && this.getNumLayers() > 1) {
 		// actually delete from the DOM and return it
@@ -342,9 +354,10 @@ svgedit.draw.Drawing.prototype.deleteCurrentLayer = function() {
 	return null;
 };
 
-// Function: svgedit.draw.Drawing.identifyLayers
-// Updates layer system and sets the current layer to the
-// top-most layer (last <g> child of this drawing).
+/**
+ * Updates layer system and sets the current layer to the
+ * top-most layer (last <g> child of this drawing).
+*/
 svgedit.draw.Drawing.prototype.identifyLayers = function() {
 	this.all_layers = [];
 	var numchildren = this.svgElem_.childNodes.length;
@@ -352,14 +365,15 @@ svgedit.draw.Drawing.prototype.identifyLayers = function() {
 	var orphans = [], layernames = [];
 	var a_layer = null;
 	var childgroups = false;
-	for (var i = 0; i < numchildren; ++i) {
+	var i;
+	for (i = 0; i < numchildren; ++i) {
 		var child = this.svgElem_.childNodes.item(i);
 		// for each g, find its layer name
 		if (child && child.nodeType == 1) {
 			if (child.tagName == "g") {
 				childgroups = true;
-				var name = $("title",child).text();
-				
+				var name = $("title", child).text();
+
 				// Hack for Opera 10.60
 				if(!name && svgedit.browser.isOpera() && child.querySelectorAll) {
 					name = $(child.querySelectorAll('title')).text();
@@ -368,7 +382,7 @@ svgedit.draw.Drawing.prototype.identifyLayers = function() {
 				// store layer and name in global variable
 				if (name) {
 					layernames.push(name);
-					this.all_layers.push( [name,child] );
+					this.all_layers.push( [name, child] );
 					a_layer = child;
 					svgedit.utilities.walkTree(child, function(e){e.setAttribute("style", "pointer-events:inherit");});
 					a_layer.setAttribute("style", "pointer-events:none");
@@ -379,7 +393,7 @@ svgedit.draw.Drawing.prototype.identifyLayers = function() {
 				}
 			}
 			// if child has is "visible" (i.e. not a <title> or <defs> element), then it is an orphan
-			else if(~visElems_arr.indexOf(child.nodeName)) {
+			else if(~visElems.indexOf(child.nodeName)) {
 				var bb = svgedit.utilities.getBBox(child);
 				orphans.push(child);
 			}
@@ -389,39 +403,37 @@ svgedit.draw.Drawing.prototype.identifyLayers = function() {
 	// create a new layer and add all the orphans to it
 	var svgdoc = this.svgElem_.ownerDocument;
 	if (orphans.length > 0 || !childgroups) {
-		var i = 1;
+		i = 1;
 		// TODO(codedread): What about internationalization of "Layer"?
 		while (layernames.indexOf(("Layer " + i)) >= 0) { i++; }
 		var newname = "Layer " + i;
-		a_layer = svgdoc.createElementNS(svg_ns, "g");
-		var layer_title = svgdoc.createElementNS(svg_ns, "title");
+		a_layer = svgdoc.createElementNS(NS.SVG, "g");
+		var layer_title = svgdoc.createElementNS(NS.SVG, "title");
 		layer_title.textContent = newname;
 		a_layer.appendChild(layer_title);
-		for (var j = 0; j < orphans.length; ++j) {
+		var j;
+		for (j = 0; j < orphans.length; ++j) {
 			a_layer.appendChild(orphans[j]);
 		}
 		this.svgElem_.appendChild(a_layer);
 		this.all_layers.push( [newname, a_layer] );
 	}
-	svgedit.utilities.walkTree(a_layer, function(e){e.setAttribute("style","pointer-events:inherit");});
+	svgedit.utilities.walkTree(a_layer, function(e){e.setAttribute("style", "pointer-events:inherit");});
 	this.current_layer = a_layer;
-	this.current_layer.setAttribute("style","pointer-events:all");
+	this.current_layer.setAttribute("style", "pointer-events:all");
 };
 
-// Function: svgedit.draw.Drawing.createLayer
-// Creates a new top-level layer in the drawing with the given name and 
-// sets the current layer to it.
-//
-// Parameters:
-// name - The given name
-//
-// Returns:
-// The SVGGElement of the new layer, which is also the current layer
-// of this drawing.
+/**
+ * Creates a new top-level layer in the drawing with the given name and 
+ * sets the current layer to it.
+ * @param {string} name - The given name
+ * @returns {SVGGElement} The SVGGElement of the new layer, which is
+ * also the current layer of this drawing.
+*/
 svgedit.draw.Drawing.prototype.createLayer = function(name) {
 	var svgdoc = this.svgElem_.ownerDocument;
-	var new_layer = svgdoc.createElementNS(svg_ns, "g");
-	var layer_title = svgdoc.createElementNS(svg_ns, "title");
+	var new_layer = svgdoc.createElementNS(NS.SVG, "g");
+	var layer_title = svgdoc.createElementNS(NS.SVG, "title");
 	layer_title.textContent = name;
 	new_layer.appendChild(layer_title);
 	this.svgElem_.appendChild(new_layer);
@@ -429,70 +441,66 @@ svgedit.draw.Drawing.prototype.createLayer = function(name) {
 	return new_layer;
 };
 
-// Function: svgedit.draw.Drawing.getLayerVisibility
-// Returns whether the layer is visible.  If the layer name is not valid, then this function
-// returns false.
-//
-// Parameters:
-// layername - the name of the layer which you want to query.
-//
-// Returns:
-// The visibility state of the layer, or false if the layer name was invalid.
+/**
+ * Returns whether the layer is visible.  If the layer name is not valid,
+ * then this function returns false.
+ * @param {string} layername - The name of the layer which you want to query.
+ * @returns {boolean} The visibility state of the layer, or false if the layer name was invalid.
+*/
 svgedit.draw.Drawing.prototype.getLayerVisibility = function(layername) {
 	// find the layer
 	var layer = null;
-	for (var i = 0; i < this.getNumLayers(); ++i) {
+	var i;
+	for (i = 0; i < this.getNumLayers(); ++i) {
 		if (this.getLayerName(i) == layername) {
 			layer = this.all_layers[i][1];
 			break;
 		}
 	}
-	if (!layer) return false;
-	return (layer.getAttribute('display') != 'none');
+	if (!layer) {return false;}
+	return (layer.getAttribute('display') !== 'none');
 };
 
-// Function: svgedit.draw.Drawing.setLayerVisibility
-// Sets the visibility of the layer. If the layer name is not valid, this function return 
-// false, otherwise it returns true. This is an undo-able action.
-//
-// Parameters:
-// layername - the name of the layer to change the visibility
-// bVisible - true/false, whether the layer should be visible
-//
-// Returns:
-// The SVGGElement representing the layer if the layername was valid, otherwise null.
+/**
+ * Sets the visibility of the layer. If the layer name is not valid, this
+ * function returns false, otherwise it returns true. This is an
+ * undo-able action.
+ * @param {string} layername - The name of the layer to change the visibility
+ * @param {boolean} bVisible - Whether the layer should be visible
+ * @returns {?SVGGElement} The SVGGElement representing the layer if the
+ *   layername was valid, otherwise null.
+*/
 svgedit.draw.Drawing.prototype.setLayerVisibility = function(layername, bVisible) {
-	if (typeof bVisible != typeof true) {
+	if (typeof bVisible !== 'boolean') {
 		return null;
 	}
 	// find the layer
 	var layer = null;
-	for (var i = 0; i < this.getNumLayers(); ++i) {
+	var i;
+	for (i = 0; i < this.getNumLayers(); ++i) {
 		if (this.getLayerName(i) == layername) {
 			layer = this.all_layers[i][1];
 			break;
 		}
 	}
-	if (!layer) return null;
+	if (!layer) {return null;}
 	
 	var oldDisplay = layer.getAttribute("display");
-	if (!oldDisplay) oldDisplay = "inline";
+	if (!oldDisplay) {oldDisplay = "inline";}
 	layer.setAttribute("display", bVisible ? "inline" : "none");
 	return layer;
 };
 
 
-// Function: svgedit.draw.Drawing.getLayerOpacity
-// Returns the opacity of the given layer.  If the input name is not a layer, null is returned.
-//
-// Parameters: 
-// layername - name of the layer on which to get the opacity
-//
-// Returns:
-// The opacity value of the given layer.  This will be a value between 0.0 and 1.0, or null
-// if layername is not a valid layer
+/**
+ * Returns the opacity of the given layer.  If the input name is not a layer, null is returned.
+ * @param {string} layername - name of the layer on which to get the opacity
+ * @returns {?number} The opacity value of the given layer.  This will be a value between 0.0 and 1.0, or null
+ * if layername is not a valid layer
+*/
 svgedit.draw.Drawing.prototype.getLayerOpacity = function(layername) {
-	for (var i = 0; i < this.getNumLayers(); ++i) {
+	var i;
+	for (i = 0; i < this.getNumLayers(); ++i) {
 		if (this.getLayerName(i) == layername) {
 			var g = this.all_layers[i][1];
 			var opacity = g.getAttribute('opacity');
@@ -505,18 +513,19 @@ svgedit.draw.Drawing.prototype.getLayerOpacity = function(layername) {
 	return null;
 };
 
-// Function: svgedit.draw.Drawing.setLayerOpacity
-// Sets the opacity of the given layer.  If the input name is not a layer, nothing happens.
-// If opacity is not a value between 0.0 and 1.0, then nothing happens.
-//
-// Parameters:
-// layername - name of the layer on which to set the opacity
-// opacity - a float value in the range 0.0-1.0
+/**
+ * Sets the opacity of the given layer.  If the input name is not a layer,
+ * nothing happens. If opacity is not a value between 0.0 and 1.0, then
+ * nothing happens.
+ * @param {string} layername - Name of the layer on which to set the opacity
+ * @param {number} opacity - A float value in the range 0.0-1.0
+*/
 svgedit.draw.Drawing.prototype.setLayerOpacity = function(layername, opacity) {
-	if (typeof opacity != typeof 1.0 || opacity < 0.0 || opacity > 1.0) {
+	if (typeof opacity !== 'number' || opacity < 0.0 || opacity > 1.0) {
 		return;
 	}
-	for (var i = 0; i < this.getNumLayers(); ++i) {
+	var i;
+	for (i = 0; i < this.getNumLayers(); ++i) {
 		if (this.getLayerName(i) == layername) {
 			var g = this.all_layers[i][1];
 			g.setAttribute("opacity", opacity);
@@ -525,4 +534,4 @@ svgedit.draw.Drawing.prototype.setLayerOpacity = function(layername, opacity) {
 	}
 };
 
-})();
+}());

+ 59 - 0
main/inc/lib/svg-edit/embedapi.html

@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <meta charset="utf-8" />
+    <title>Embed API</title>
+    <script src="jquery.js"></script>
+    <script src="embedapi.js"></script>
+    <script>
+    /*globals $, EmbeddedSVGEdit*/
+	var initEmbed;
+    $(function () {'use strict';
+        
+        var svgCanvas = null;
+
+        initEmbed = function () {
+            var doc, mainButton,
+                frame = document.getElementById('svgedit');
+            svgCanvas = new EmbeddedSVGEdit(frame);
+            // Hide main button, as we will be controlling new, load, save, etc. from the host document
+            doc = frame.contentDocument || frame.contentWindow.document;
+            mainButton = doc.getElementById('main_button');
+            mainButton.style.display = 'none';
+        };
+
+        function handleSvgData(data, error) {
+            if (error) {
+                alert('error ' + error);
+            } else {
+                alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data);
+            }
+        }
+
+        function loadSvg() {
+            var svgexample = '<svg width="640" height="480" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><g><title>Layer 1<\/title><rect stroke-width="5" stroke="#000000" fill="#FF0000" id="svg_1" height="35" width="51" y="35" x="32"/><ellipse ry="15" rx="24" stroke-width="5" stroke="#000000" fill="#0000ff" id="svg_2" cy="60" cx="66"/><\/g><\/svg>';
+            svgCanvas.setSvgString(svgexample);
+        }
+
+        function saveSvg() {
+            svgCanvas.getSvgString()(handleSvgData);
+        }
+        
+        // Add event handlers
+        $('#load').click(loadSvg);
+        $('#save').click(saveSvg);
+		$('body').append(
+			$('<iframe src="svg-editor.html?extensions=ext-xdomain-messaging.js' +
+				window.location.href.replace(/\?(.*)$/, '&$1') + // Append arguments to this file onto the iframe
+				'" width="900px" height="600px" id="svgedit" onload="initEmbed();"></iframe>'
+			)
+		);
+	});
+    </script>
+</head>
+<body>
+    <button id="load">Load example</button>
+    <button id="save">Save data</button>
+    <br/>
+</body>
+</html>

+ 0 - 56
main/inc/lib/svg-edit/embedapi.html_disabled

@@ -1,56 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" >
-<head>
-    <title></title>
-</head>
-<body>
-
-    <script type="text/javascript" src="embedapi.js"></script>
-    <script type="text/javascript">
-        var svgCanvas = null;
-
-        function init_embed() {
-            var frame = document.getElementById('svgedit');
-			svgCanvas = new embedded_svg_edit(frame);
-			
-			// Hide main button, as we will be controlling new/load/save etc from the host document
-			var doc;
-			doc = frame.contentDocument;
-			if (!doc)
-			{
-				doc = frame.contentWindow.document;
-			}
-			
-			var mainButton = doc.getElementById('main_button');
-			mainButton.style.display = 'none';            
-        }
-        
-        function handleSvgData(data, error) {
-			if (error)
-			{
-				alert('error ' + error);
-			}
-            else
-			{
-				alert('Congratulations. Your SVG string is back in the host page, do with it what you will\n\n' + data);
-			}			
-        }
-        
-        function loadSvg() {
-            var svgexample = '<svg width="640" height="480" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><g><title>Layer 1</title><rect stroke-width="5" stroke="#000000" fill="#FF0000" id="svg_1" height="35" width="51" y="35" x="32"/><ellipse ry="15" rx="24" stroke-width="5" stroke="#000000" fill="#0000ff" id="svg_2" cy="60" cx="66"/></g></svg>';
-            svgCanvas.setSvgString(svgexample);
-        }
-		
-		function saveSvg() {			
-			svgCanvas.getSvgString()(handleSvgData);
-		}
-    </script>
-
-
-    <button onclick="loadSvg();">Load example</button>
-    <button onclick="saveSvg();">Save data</button>
-    <br/>
-
-    <iframe src="svg-editor.html" width="900px" height="600px" id="svgedit" onload="init_embed()"></iframe>    
-</body>
-</html>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 101 - 148
main/inc/lib/svg-edit/embedapi.js


+ 11 - 0
main/inc/lib/svg-edit/extensions/allowedMimeTypes.php

@@ -0,0 +1,11 @@
+<?php
+
+$allowedMimeTypesBySuffix = array(
+    'svg' => 'image/svg+xml;charset=utf-8',
+    'png' => 'image/png',
+    'jpeg' => 'image/jpeg',
+    'bmp' => 'image/bmp',
+    'webp' => 'image/webp'
+);
+
+?>

+ 9 - 0
main/inc/lib/svg-edit/extensions/closepath_icons.svg

@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<svg width="1000" height="1000" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <g>
+  <title>Layer 1</title>
+  <rect id="svg_2" height="717.432104" width="862.814821" y="87.209869" x="63.851915" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="null" fill="#00ffff"/>
+  <text font-style="italic" font-weight="normal" transform="matrix(14.5579, 0, 0, 19.1025, -3784.43, -4491.74)" xml:space="preserve" text-anchor="middle" font-family="Cursive" font-size="24" id="svg_1" y="267.244915" x="291.750555" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" fill="#000000">ICO</text>
+ </g>
+
+</svg>

+ 260 - 265
main/inc/lib/svg-edit/extensions/ext-arrows.js

@@ -1,298 +1,293 @@
+/*globals svgEditor, svgCanvas, $*/
+/*jslint vars: true, eqeq: true*/
 /*
  * ext-arrows.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *
  */
 
- 
-svgEditor.addExtension("Arrows", function(S) {
-		var svgcontent = S.svgcontent,
-			addElem = S.addSvgElementFromJson,
-			nonce = S.nonce,
-			randomize_ids = S.randomize_ids,
-			selElems;
-
-		svgCanvas.bind('setnonce', setArrowNonce);
-		svgCanvas.bind('unsetnonce', unsetArrowNonce);
-			
-		var lang_list = {
-			"en":[
-				{"id": "arrow_none", "textContent": "No arrow" }
+svgEditor.addExtension('Arrows', function(S) {
+	var svgcontent = S.svgcontent,
+		addElem = S.addSvgElementFromJson,
+		nonce = S.nonce,
+		randomize_ids = S.randomize_ids,
+		selElems, pathdata,
+		lang_list = {
+			'en':[
+				{'id': 'arrow_none', 'textContent': 'No arrow' }
 			],
-			"fr":[
-				{"id": "arrow_none", "textContent": "Sans flèche" }
+			'fr':[
+				{'id': 'arrow_none', 'textContent': 'Sans flèche' }
 			]
-		};
-		
-		var prefix = 'se_arrow_';
-		if (randomize_ids) {
-		  var arrowprefix = prefix + nonce + '_';
-		} else {
-		  var arrowprefix = prefix;
-		}
+		},
+		arrowprefix,
+		prefix = 'se_arrow_';
 
-		var pathdata = {
-			fw: {d:"m0,0l10,5l-10,5l5,-5l-5,-5z", refx:8,  id: arrowprefix + 'fw'},
-			bk: {d:"m10,0l-10,5l10,5l-5,-5l5,-5z", refx:2, id: arrowprefix + 'bk'}
-		}
-		
-		function setArrowNonce(window, n) {
-		    randomize_ids = true;
-		    arrowprefix = prefix + n + '_';
- 			pathdata.fw.id = arrowprefix + 'fw';
-			pathdata.bk.id = arrowprefix + 'bk';
-		}
+	function setArrowNonce(window, n) {
+		randomize_ids = true;
+		arrowprefix = prefix + n + '_';
+		pathdata.fw.id = arrowprefix + 'fw';
+		pathdata.bk.id = arrowprefix + 'bk';
+	}
+
+	function unsetArrowNonce(window) {
+		randomize_ids = false;
+		arrowprefix = prefix;
+		pathdata.fw.id = arrowprefix + 'fw';
+		pathdata.bk.id = arrowprefix + 'bk';
+	}
+
+
+	svgCanvas.bind('setnonce', setArrowNonce);
+	svgCanvas.bind('unsetnonce', unsetArrowNonce);
+
+	if (randomize_ids) {
+		arrowprefix = prefix + nonce + '_';
+	} else {
+		arrowprefix = prefix;
+	}
+
+	pathdata = {
+		fw: {d: 'm0,0l10,5l-10,5l5,-5l-5,-5z', refx: 8,  id: arrowprefix + 'fw'},
+		bk: {d: 'm10,0l-10,5l10,5l-5,-5l5,-5z', refx: 2, id: arrowprefix + 'bk'}
+	};
 
-		function unsetArrowNonce(window) {
-		    randomize_ids = false;
-		    arrowprefix = prefix;
- 			pathdata.fw.id = arrowprefix + 'fw';
-			pathdata.bk.id = arrowprefix + 'bk';
+	function getLinked(elem, attr) {
+		var str = elem.getAttribute(attr);
+		if(!str) {return null;}
+		var m = str.match(/\(\#(.*)\)/);
+		if(!m || m.length !== 2) {
+			return null;
 		}
+		return S.getElem(m[1]);
+	}
 
-		function getLinked(elem, attr) {
-			var str = elem.getAttribute(attr);
-			if(!str) return null;
-			var m = str.match(/\(\#(.*)\)/);
-			if(!m || m.length !== 2) {
-				return null;
+	function showPanel(on) {
+		$('#arrow_panel').toggle(on);
+		if(on) {
+			var el = selElems[0];
+			var end = el.getAttribute('marker-end');
+			var start = el.getAttribute('marker-start');
+			var mid = el.getAttribute('marker-mid');
+			var val;
+
+			if (end && start) {
+				val = 'both';
+			} else if (end) {
+				val = 'end';
+			} else if (start) {
+				val = 'start';
+			} else if (mid) {
+				val = 'mid';
+				if (mid.indexOf('bk') !== -1) {
+					val = 'mid_bk';
+				}
+			}
+
+			if (!start && !mid && !end) {
+				val = 'none';
 			}
-			return S.getElem(m[1]);
+
+			$('#arrow_list').val(val);
 		}
-		
-		function showPanel(on) {
-			$('#arrow_panel').toggle(on);
-			
-			if(on) {
-				var el = selElems[0];
-				var end = el.getAttribute("marker-end");
-				var start = el.getAttribute("marker-start");
-				var mid = el.getAttribute("marker-mid");
-				var val;
-				
-				if(end && start) {
-					val = "both";
-				} else if(end) {
-					val = "end";
-				} else if(start) {
-					val = "start";
-				} else if(mid) {
-					val = "mid";
-					if(mid.indexOf("bk") != -1) {
-						val = "mid_bk";
-					}
+	}
+
+	function resetMarker() {
+		var el = selElems[0];
+		el.removeAttribute('marker-start');
+		el.removeAttribute('marker-mid');
+		el.removeAttribute('marker-end');
+	}
+
+	function addMarker(dir, type, id) {
+		// TODO: Make marker (or use?) per arrow type, since refX can be different
+		id = id || arrowprefix + dir;
+
+		var marker = S.getElem(id);
+		var data = pathdata[dir];
+
+		if (type == 'mid') {
+			data.refx = 5;
+		}
+
+		if (!marker) {
+			marker = addElem({
+				'element': 'marker',
+				'attr': {
+					'viewBox': '0 0 10 10',
+					'id': id,
+					'refY': 5,
+					'markerUnits': 'strokeWidth',
+					'markerWidth': 5,
+					'markerHeight': 5,
+					'orient': 'auto',
+					'style': 'pointer-events:none' // Currently needed for Opera
 				}
-				
-				if(!start && !mid && !end) {
-					val = "none";
+			});
+			var arrow = addElem({
+				'element': 'path',
+				'attr': {
+					'd': data.d,
+					'fill': '#000000'
 				}
-				
-				$("#arrow_list").val(val);
-			}
+			});
+			marker.appendChild(arrow);
+			S.findDefs().appendChild(marker);
 		}
-		
-		function resetMarker() {
-			var el = selElems[0];
-			el.removeAttribute("marker-start");
-			el.removeAttribute("marker-mid");
-			el.removeAttribute("marker-end");
+
+		marker.setAttribute('refX', data.refx);
+
+		return marker;
+	}
+
+	function setArrow() {
+		var type = this.value;
+		resetMarker();
+
+		if (type == 'none') {
+			return;
 		}
-		
-		function addMarker(dir, type, id) {
-			// TODO: Make marker (or use?) per arrow type, since refX can be different
-			id = id || arrowprefix + dir;
-			
-			var marker = S.getElem(id);
-
-			var data = pathdata[dir];
-			
-			if(type == "mid") {
-				data.refx = 5;
-			}
 
-			if(!marker) {
-				marker = addElem({
-					"element": "marker",
-					"attr": {
-						"viewBox": "0 0 10 10",
-						"id": id,
-						"refY": 5,
-						"markerUnits": "strokeWidth",
-						"markerWidth": 5,
-						"markerHeight": 5,
-						"orient": "auto",
-						"style": "pointer-events:none" // Currently needed for Opera
-					}
-				});
-				var arrow = addElem({
-					"element": "path",
-					"attr": {
-						"d": data.d,
-						"fill": "#000000"
-					}
-				});
-				marker.appendChild(arrow);
-				S.findDefs().appendChild(marker);
-			} 
-			
-			marker.setAttribute('refX', data.refx);
-			
-			return marker;
+		// Set marker on element
+		var dir = 'fw';
+		if (type == 'mid_bk') {
+			type = 'mid';
+			dir = 'bk';
+		} else if (type == 'both') {
+			addMarker('bk', type);
+			svgCanvas.changeSelectedAttribute('marker-start', 'url(#' + pathdata.bk.id + ')');
+			type = 'end';
+			dir = 'fw';
+		} else if (type == 'start') {
+			dir = 'bk';
 		}
-		
-		function setArrow() {
-			var type = this.value;
-			resetMarker();
-		
-			if(type == "none") {
-				return;
-			}
-		
-			// Set marker on element
-			var dir = "fw";
-			if(type == "mid_bk") {
-				type = "mid";
-				dir = "bk";
-			} else if(type == "both") {
-				addMarker("bk", type);
-				svgCanvas.changeSelectedAttribute("marker-start", "url(#" + pathdata.bk.id + ")");
-				type = "end";
-				dir = "fw";
-			} else if (type == "start") {
-				dir = "bk";
+
+		addMarker(dir, type);
+		svgCanvas.changeSelectedAttribute('marker-' + type, 'url(#' + pathdata[dir].id + ')');
+		S.call('changed', selElems);
+	}
+
+	function colorChanged(elem) {
+		var color = elem.getAttribute('stroke');
+		var mtypes = ['start', 'mid', 'end'];
+		var defs = S.findDefs();
+
+		$.each(mtypes, function(i, type) {
+			var marker = getLinked(elem, 'marker-'+type);
+			if(!marker) {return;}
+
+			var cur_color = $(marker).children().attr('fill');
+			var cur_d = $(marker).children().attr('d');
+			var new_marker = null;
+			if(cur_color === color) {return;}
+
+			var all_markers = $(defs).find('marker');
+			// Different color, check if already made
+			all_markers.each(function() {
+				var attrs = $(this).children().attr(['fill', 'd']);
+				if(attrs.fill === color && attrs.d === cur_d) {
+					// Found another marker with this color and this path
+					new_marker = this;
+				}
+			});
+
+			if(!new_marker) {
+				// Create a new marker with this color
+				var last_id = marker.id;
+				var dir = last_id.indexOf('_fw') !== -1?'fw':'bk';
+
+				new_marker = addMarker(dir, type, arrowprefix + dir + all_markers.length);
+
+				$(new_marker).children().attr('fill', color);
 			}
-			
-			addMarker(dir, type);
-			svgCanvas.changeSelectedAttribute("marker-"+type, "url(#" + pathdata[dir].id + ")");
-			S.call("changed", selElems);
-		}
-		
-		function colorChanged(elem) {
-			var color = elem.getAttribute('stroke');
-			
-			var mtypes = ['start','mid','end'];
-			var defs = S.findDefs();
-			
-			$.each(mtypes, function(i, type) {
-				var marker = getLinked(elem, 'marker-'+type);
-				if(!marker) return;
-				
-				var cur_color = $(marker).children().attr('fill');
-				var cur_d = $(marker).children().attr('d');
-				var new_marker = null;
-				if(cur_color === color) return;
-				
-				var all_markers = $(defs).find('marker');
-				// Different color, check if already made
-				all_markers.each(function() {
-					var attrs = $(this).children().attr(['fill', 'd']);
-					if(attrs.fill === color && attrs.d === cur_d) {
-						// Found another marker with this color and this path
-						new_marker = this;
+
+			$(elem).attr('marker-'+type, 'url(#' + new_marker.id + ')');
+
+			// Check if last marker can be removed
+			var remove = true;
+			$(S.svgcontent).find('line, polyline, path, polygon').each(function() {
+				var elem = this;
+				$.each(mtypes, function(j, mtype) {
+					if($(elem).attr('marker-' + mtype) === 'url(#' + marker.id + ')') {
+						remove = false;
+						return remove;
 					}
 				});
-				
-				if(!new_marker) {
-					// Create a new marker with this color
-					var last_id = marker.id;
-					var dir = last_id.indexOf('_fw') !== -1?'fw':'bk';
-					
-					new_marker = addMarker(dir, type, arrowprefix + dir + all_markers.length);
-
-					$(new_marker).children().attr('fill', color);
-				}
-				
-				$(elem).attr('marker-'+type, "url(#" + new_marker.id + ")");
-				
-				// Check if last marker can be removed
-				var remove = true;
-				$(S.svgcontent).find('line, polyline, path, polygon').each(function() {
-					var elem = this;
-					$.each(mtypes, function(j, mtype) {
-						if($(elem).attr('marker-' + mtype) === "url(#" + marker.id + ")") {
-							return remove = false;
-						}
-					});
-					if(!remove) return false;
-				});
-				
-				// Not found, so can safely remove
-				if(remove) {
-					$(marker).remove();
-				}
-
+				if(!remove) {return false;}
 			});
-			
-		}
-		
-		return {
-			name: "Arrows",
-			context_tools: [{
-				type: "select",
-				panel: "arrow_panel",
-				title: "Select arrow type",
-				id: "arrow_list",
-				options: {
-					none: "No arrow",
-					end: "----&gt;",
-					start: "&lt;----",
-					both: "&lt;---&gt;",
-					mid: "--&gt;--",
-					mid_bk: "--&lt;--"
-				},
-				defval: "none",
-				events: {
-					change: setArrow
-				}
-			}],
-			callback: function() {
-				$('#arrow_panel').hide();
-				// Set ID so it can be translated in locale file
-				$('#arrow_list option')[0].id = 'connector_no_arrow';
-			},
-			addLangData: function(lang) {
-				return {
-					data: lang_list[lang]
-				};
+
+			// Not found, so can safely remove
+			if(remove) {
+				$(marker).remove();
+			}
+		});
+	}
+
+	return {
+		name: 'Arrows',
+		context_tools: [{
+			type: 'select',
+			panel: 'arrow_panel',
+			title: 'Select arrow type',
+			id: 'arrow_list',
+			options: {
+				none: 'No arrow',
+				end: '----&gt;',
+				start: '&lt;----',
+				both: '&lt;---&gt;',
+				mid: '--&gt;--',
+				mid_bk: '--&lt;--'
 			},
-			selectedChanged: function(opts) {
-				
-				// Use this to update the current selected elements
-				selElems = opts.elems;
-				
-				var i = selElems.length;
-				var marker_elems = ['line','path','polyline','polygon'];
-				
-				while(i--) {
-					var elem = selElems[i];
-					if(elem && $.inArray(elem.tagName, marker_elems) != -1) {
-						if(opts.selectedElement && !opts.multiselected) {
-							showPanel(true);
-						} else {
-							showPanel(false);
-						}
+			defval: 'none',
+			events: {
+				change: setArrow
+			}
+		}],
+		callback: function() {
+			$('#arrow_panel').hide();
+			// Set ID so it can be translated in locale file
+			$('#arrow_list option')[0].id = 'connector_no_arrow';
+		},
+		addLangData: function(lang) {
+			return {
+				data: lang_list[lang]
+			};
+		},
+		selectedChanged: function(opts) {
+			// Use this to update the current selected elements
+			selElems = opts.elems;
+
+			var i = selElems.length;
+			var marker_elems = ['line', 'path', 'polyline', 'polygon'];
+			while(i--) {
+				var elem = selElems[i];
+				if(elem && $.inArray(elem.tagName, marker_elems) !== -1) {
+					if(opts.selectedElement && !opts.multiselected) {
+						showPanel(true);
 					} else {
 						showPanel(false);
 					}
+				} else {
+					showPanel(false);
 				}
-			},
-			elementChanged: function(opts) {
-				var elem = opts.elems[0];
-				if(elem && (
-					elem.getAttribute("marker-start") ||
-					elem.getAttribute("marker-mid") ||
-					elem.getAttribute("marker-end")
-				)) {
-	// 								var start = elem.getAttribute("marker-start");
-	// 								var mid = elem.getAttribute("marker-mid");
-	// 								var end = elem.getAttribute("marker-end");
-					// Has marker, so see if it should match color
-					colorChanged(elem);
-				}
-				
 			}
-		};
+		},
+		elementChanged: function(opts) {
+			var elem = opts.elems[0];
+			if(elem && (
+				elem.getAttribute('marker-start') ||
+				elem.getAttribute('marker-mid') ||
+				elem.getAttribute('marker-end')
+			)) {
+//								var start = elem.getAttribute('marker-start');
+//								var mid = elem.getAttribute('marker-mid');
+//								var end = elem.getAttribute('marker-end');
+				// Has marker, so see if it should match color
+				colorChanged(elem);
+			}
+		}
+	};
 });

+ 71 - 72
main/inc/lib/svg-edit/extensions/ext-closepath.js

@@ -1,7 +1,9 @@
+/*globals svgEditor, $*/
+/*jslint vars: true, eqeq: true*/
 /*
  * ext-closepath.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Jeff Schiller
  *
@@ -9,84 +11,81 @@
 
 // This extension adds a simple button to the contextual panel for paths
 // The button toggles whether the path is open or closed
-svgEditor.addExtension("ClosePath", function(S) {
-		var selElems,
-			updateButton = function(path) {
-				var seglist = path.pathSegList,
-					closed = seglist.getItem(seglist.numberOfItems - 1).pathSegType==1,
-					showbutton = closed ? '#tool_openpath' : '#tool_closepath',
-					hidebutton = closed ? '#tool_closepath' : '#tool_openpath';
-					$(hidebutton).hide();
-					$(showbutton).show();
-			},
-			showPanel = function(on) {
-				$('#closepath_panel').toggle(on);
-				if (on) {
-					var path = selElems[0];
-					if (path) updateButton(path);
-				}
-			},
-		
-			toggleClosed = function() {
+svgEditor.addExtension('ClosePath', function() {'use strict';
+	var selElems,
+		updateButton = function(path) {
+			var seglist = path.pathSegList,
+				closed = seglist.getItem(seglist.numberOfItems - 1).pathSegType == 1,
+				showbutton = closed ? '#tool_openpath' : '#tool_closepath',
+				hidebutton = closed ? '#tool_closepath' : '#tool_openpath';
+				$(hidebutton).hide();
+				$(showbutton).show();
+		},
+		showPanel = function(on) {
+			$('#closepath_panel').toggle(on);
+			if (on) {
 				var path = selElems[0];
-				if (path) {
-					var seglist = path.pathSegList,
-						last = seglist.numberOfItems - 1;					
-					// is closed
-					if(seglist.getItem(last).pathSegType == 1) {
-						seglist.removeItem(last);
-					}
-					else {
-						seglist.appendItem(path.createSVGPathSegClosePath());
-					}
-					updateButton(path);
+				if (path) {updateButton(path);}
+			}
+		},
+		toggleClosed = function() {
+			var path = selElems[0];
+			if (path) {
+				var seglist = path.pathSegList,
+					last = seglist.numberOfItems - 1;
+				// is closed
+				if (seglist.getItem(last).pathSegType == 1) {
+					seglist.removeItem(last);
+				} else {
+					seglist.appendItem(path.createSVGPathSegClosePath());
 				}
-			};
-		
-		return {
-			name: "ClosePath",
-			svgicons: "extensions/closepath_icons.svg",
-			buttons: [{
-				id: "tool_openpath",
-				type: "context",
-				panel: "closepath_panel",
-				title: "Open path",
-				events: {
-					'click': function() {
-						toggleClosed();
-					}
+				updateButton(path);
+			}
+		};
+
+	return {
+		name: 'ClosePath',
+		svgicons: svgEditor.curConfig.extPath + 'closepath_icons.svg',
+		buttons: [{
+			id: 'tool_openpath',
+			type: 'context',
+			panel: 'closepath_panel',
+			title: 'Open path',
+			events: {
+				click: function() {
+					toggleClosed();
 				}
-			},
-			{
-				id: "tool_closepath",
-				type: "context",
-				panel: "closepath_panel",
-				title: "Close path",
-				events: {
-					'click': function() {
-						toggleClosed();
-					}
+			}
+		},
+		{
+			id: 'tool_closepath',
+			type: 'context',
+			panel: 'closepath_panel',
+			title: 'Close path',
+			events: {
+				click: function() {
+					toggleClosed();
 				}
-			}],
-			callback: function() {
-				$('#closepath_panel').hide();
-			},
-			selectedChanged: function(opts) {
-				selElems = opts.elems;
-				var i = selElems.length;
-				
-				while(i--) {
-					var elem = selElems[i];
-					if(elem && elem.tagName == 'path') {
-						if(opts.selectedElement && !opts.multiselected) {
-							showPanel(true);
-						} else {
-							showPanel(false);
-						}
+			}
+		}],
+		callback: function() {
+			$('#closepath_panel').hide();
+		},
+		selectedChanged: function(opts) {
+			selElems = opts.elems;
+			var i = selElems.length;
+			while (i--) {
+				var elem = selElems[i];
+				if (elem && elem.tagName == 'path') {
+					if (opts.selectedElement && !opts.multiselected) {
+						showPanel(true);
 					} else {
 						showPanel(false);
 					}
+				} else {
+					showPanel(false);
 				}
 			}
-		};
+		}
+	};
 });

+ 155 - 149
main/inc/lib/svg-edit/extensions/ext-connector.js

@@ -1,7 +1,9 @@
+/*globals svgEditor, svgCanvas, $*/
+/*jslint vars: true, continue: true, eqeq: true, todo: true*/
 /*
  * ext-connector.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *
@@ -24,10 +26,9 @@ svgEditor.addExtension("Connector", function(S) {
 		connections = [],
 		conn_sel = ".se_connector",
 		se_ns,
-// 			connect_str = "-SE_CONNECT-",
-		selElems = [];
-		
-	elData = $.data;
+//		connect_str = "-SE_CONNECT-",
+		selElems = [],
+		elData = $.data;
 		
 	var lang_list = {
 		"en":[
@@ -37,10 +38,42 @@ svgEditor.addExtension("Connector", function(S) {
 			{"id": "mode_connect", "title": "Connecter deux objets"}
 		]
 	};
+
+	function getBBintersect(x, y, bb, offset) {
+		if(offset) {
+			offset -= 0;
+			bb = $.extend({}, bb);
+			bb.width += offset;
+			bb.height += offset;
+			bb.x -= offset/2;
+			bb.y -= offset/2;
+		}
 	
+		var mid_x = bb.x + bb.width/2;
+		var mid_y = bb.y + bb.height/2;
+		var len_x = x - mid_x;
+		var len_y = y - mid_y;
+		
+		var slope = Math.abs(len_y/len_x);
+		
+		var ratio;
+		
+		if(slope < bb.height/bb.width) {
+			ratio = (bb.width/2) / Math.abs(len_x);
+		} else {
+			ratio = (bb.height/2) / Math.abs(len_y);
+		}
+		
+		
+		return {
+			x: mid_x + len_x * ratio,
+			y: mid_y + len_y * ratio
+		};
+	}
+
 	function getOffset(side, line) {
 		var give_offset = !!line.getAttribute('marker-' + side);
-// 		var give_offset = $(line).data(side+'_off');
+//		var give_offset = $(line).data(side+'_off');
 
 		// TODO: Make this number (5) be based on marker width/height
 		var size = line.getAttribute('stroke-width') * 5;
@@ -57,19 +90,19 @@ svgEditor.addExtension("Connector", function(S) {
 	}
 	
 	function setPoint(elem, pos, x, y, setMid) {
-		var pts = elem.points;
+		var i, pts = elem.points;
 		var pt = svgroot.createSVGPoint();
 		pt.x = x;
 		pt.y = y;
-		if(pos === 'end') pos = pts.numberOfItems-1;
+		if (pos === 'end') {pos = pts.numberOfItems - 1;}
 		// TODO: Test for this on init, then use alt only if needed
 		try {
 			pts.replaceItem(pt, pos);
 		} catch(err) {
 			// Should only occur in FF which formats points attr as "n,n n,n", so just split
 			var pt_arr = elem.getAttribute("points").split(" ");
-			for(var i=0; i< pt_arr.length; i++) {
-				if(i == pos) {
+			for (i = 0; i < pt_arr.length; i++) {
+				if (i === pos) {
 					pt_arr[i] = x + ',' + y;
 				}
 			}
@@ -93,7 +126,7 @@ svgEditor.addExtension("Connector", function(S) {
 			var elem = conn.elem;
 			
 			var pre = conn.is_start?'start':'end';
-// 						var sw = line.getAttribute('stroke-width') * 5;
+//						var sw = line.getAttribute('stroke-width') * 5;
 			
 			// Update bbox for this element
 			var bb = elData(line, pre+'_bb');
@@ -110,36 +143,39 @@ svgEditor.addExtension("Connector", function(S) {
 			
 			// Set point of element being moved
 			var pt = getBBintersect(src_x, src_y, bb, getOffset(pre, line)); // $(line).data(pre+'_off')?sw:0
-			setPoint(line, conn.is_start?0:'end', pt.x, pt.y, true);
+			setPoint(line, conn.is_start ? 0 : 'end', pt.x, pt.y, true);
 			
 			// Set point of connected element
 			var pt2 = getBBintersect(pt.x, pt.y, elData(line, alt_pre + '_bb'), getOffset(alt_pre, line));
-			setPoint(line, conn.is_start?'end':0, pt2.x, pt2.y, true);
+			setPoint(line, conn.is_start ? 'end' : 0, pt2.x, pt2.y, true);
 
 		}
 	}
 	
 	function findConnectors(elems) {
-		if(!elems) elems = selElems;
+		var i;
+		if (!elems) {elems = selElems;}
 		var connectors = $(svgcontent).find(conn_sel);
 		connections = [];
 
 		// Loop through connectors to see if one is connected to the element
 		connectors.each(function() {
+			var add_this;
+			function add () {
+				if ($.inArray(this, elems) !== -1) {
+					// Pretend this element is selected
+					add_this = true;
+				}
+			}
 			var start = elData(this, "c_start");
 			var end = elData(this, "c_end");
 			
 			var parts = [getElem(start), getElem(end)];
-			for(var i=0; i<2; i++) {
+			for (i = 0; i < 2; i++) {
 				var c_elem = parts[i];
-				var add_this = false;
+				add_this = false;
 				// The connected element might be part of a selected group
-				$(c_elem).parents().each(function() {
-					if($.inArray(this, elems) !== -1) {
-						// Pretend this element is selected
-						add_this = true;
-					}
-				});
+				$(c_elem).parents().each(add);
 				
 				if(!c_elem || !c_elem.parentNode) {
 					$(this).remove();
@@ -163,11 +199,12 @@ svgEditor.addExtension("Connector", function(S) {
 		// Updates connector lines based on selected elements
 		// Is not used on mousemove, as it runs getStrokedBBox every time,
 		// which isn't necessary there.
+		var i, j;
 		findConnectors(elems);
-		if(connections.length) {
+		if (connections.length) {
 			// Update line with element
-			var i = connections.length;
-			while(i--) {
+			i = connections.length;
+			while (i--) {
 				var conn = connections[i];
 				var line = conn.connector;
 				var elem = conn.elem;
@@ -191,60 +228,27 @@ svgEditor.addExtension("Connector", function(S) {
 				
 				// Set point of element being moved
 				var pt = getBBintersect(src_x, src_y, bb, getOffset(pre, line));
-				setPoint(line, conn.is_start?0:'end', pt.x, pt.y, true);
+				setPoint(line, conn.is_start ? 0 : 'end', pt.x, pt.y, true);
 				
 				// Set point of connected element
 				var pt2 = getBBintersect(pt.x, pt.y, elData(line, alt_pre + '_bb'), getOffset(alt_pre, line));
-				setPoint(line, conn.is_start?'end':0, pt2.x, pt2.y, true);
+				setPoint(line, conn.is_start ? 'end' : 0, pt2.x, pt2.y, true);
 				
 				// Update points attribute manually for webkit
-				if(navigator.userAgent.indexOf('AppleWebKit') != -1) {
+				if (navigator.userAgent.indexOf('AppleWebKit') !== -1) {
 					var pts = line.points;
 					var len = pts.numberOfItems;
-					var pt_arr = Array(len);
-					for(var j=0; j< len; j++) {
-						var pt = pts.getItem(j);
+					var pt_arr = [];
+					for (j = 0; j < len; j++) {
+						pt = pts.getItem(j);
 						pt_arr[j] = pt.x + ',' + pt.y;
-					}	
-					line.setAttribute("points",pt_arr.join(" ")); 
+					}
+					line.setAttribute('points', pt_arr.join(' ')); 
 				}
-
 			}
 		}
 	}
 	
-	function getBBintersect(x, y, bb, offset) {
-		if(offset) {
-			offset -= 0;
-			bb = $.extend({}, bb);
-			bb.width += offset;
-			bb.height += offset;
-			bb.x -= offset/2;
-			bb.y -= offset/2;
-		}
-	
-		var mid_x = bb.x + bb.width/2;
-		var mid_y = bb.y + bb.height/2;
-		var len_x = x - mid_x;
-		var len_y = y - mid_y;
-		
-		var slope = Math.abs(len_y/len_x);
-		
-		var ratio;
-		
-		if(slope < bb.height/bb.width) {
-			ratio = (bb.width/2) / Math.abs(len_x);
-		} else {
-			ratio = (bb.height/2) / Math.abs(len_y);
-		}
-		
-		
-		return {
-			x: mid_x + len_x * ratio,
-			y: mid_y + len_y * ratio
-		}
-	}
-	
 	// Do once
 	(function() {
 		var gse = svgCanvas.groupSelectedElements;
@@ -252,7 +256,7 @@ svgEditor.addExtension("Connector", function(S) {
 		svgCanvas.groupSelectedElements = function() {
 			svgCanvas.removeFromSelection($(conn_sel).toArray());
 			return gse.apply(this, arguments);
-		}
+		};
 		
 		var mse = svgCanvas.moveSelectedElements;
 		
@@ -261,7 +265,7 @@ svgEditor.addExtension("Connector", function(S) {
 			var cmd = mse.apply(this, arguments);
 			updateConnectors();
 			return cmd;
-		}
+		};
 		
 		se_ns = svgCanvas.getEditorNS();
 	}());
@@ -283,27 +287,27 @@ svgEditor.addExtension("Connector", function(S) {
 				svgCanvas.getEditorNS(true);
 			}
 		});
-// 			updateConnectors();
+//		updateConnectors();
 	}
-	
-// 		$(svgroot).parent().mousemove(function(e) {
-// // 			if(started 
-// // 				|| svgCanvas.getMode() != "connector"
-// // 				|| e.target.parentNode.parentNode != svgcontent) return;
-// 			
-// 			console.log('y')
-// // 			if(e.target.parentNode.parentNode === svgcontent) {
-// // 					
-// // 			}
-// 		});
-	
+
+//		$(svgroot).parent().mousemove(function(e) {
+// //			if(started 
+// //				|| svgCanvas.getMode() != "connector"
+// //				|| e.target.parentNode.parentNode != svgcontent) return;
+//
+//			console.log('y')
+// //			if(e.target.parentNode.parentNode === svgcontent) {
+// //					
+// //			}
+//		});
+
 	return {
 		name: "Connector",
-		svgicons: "images/conn.svg",
+		svgicons: svgEditor.curConfig.imgPath + "conn.svg",
 		buttons: [{
 			id: "mode_connect",
 			type: "mode",
-			icon: "images/cut.png",
+			icon: svgEditor.curConfig.imgPath + "cut.png",
 			title: "Connect two objects",
 			includeWith: {
 				button: '#tool_line',
@@ -323,19 +327,19 @@ svgEditor.addExtension("Connector", function(S) {
 		},
 		mouseDown: function(opts) {
 			var e = opts.event;
-			start_x = opts.start_x,
+			start_x = opts.start_x;
 			start_y = opts.start_y;
 			var mode = svgCanvas.getMode();
 			
-			if(mode == "connector") {
-				
-				if(started) return;
+			if (mode == "connector") {
 				
+				if (started) {return;}
+
 				var mouse_target = e.target;
 				
 				var parents = $(mouse_target).parents();
 				
-				if($.inArray(svgcontent, parents) != -1) {
+				if($.inArray(svgcontent, parents) !== -1) {
 					// Connectable element
 					
 					// If child of foreignObject, use parent
@@ -365,7 +369,8 @@ svgEditor.addExtension("Connector", function(S) {
 				return {
 					started: true
 				};
-			} else if(mode == "select") {
+			}
+			if (mode == "select") {
 				findConnectors();
 			}
 		},
@@ -380,7 +385,7 @@ svgEditor.addExtension("Connector", function(S) {
 								
 			var mode = svgCanvas.getMode();
 			
-			if(mode == "connector" && started) {
+			if (mode == "connector" && started) {
 				
 				var sw = cur_line.getAttribute('stroke-width') * 3;
 				// Set start point (adjusts based on bb)
@@ -392,7 +397,7 @@ svgEditor.addExtension("Connector", function(S) {
 				
 				// Set end point
 				setPoint(cur_line, 'end', x, y, true);
-			} else if(mode == "select") {
+			} else if (mode == "select") {
 				var slen = selElems.length;
 				
 				while(slen--) {
@@ -421,19 +426,20 @@ svgEditor.addExtension("Connector", function(S) {
 			
 			if(svgCanvas.getMode() == "connector") {
 				var fo = $(mouse_target).closest("foreignObject");
-				if(fo.length) mouse_target = fo[0];
+				if (fo.length) {mouse_target = fo[0];}
 				
 				var parents = $(mouse_target).parents();
 
-				if(mouse_target == start_elem) {
+				if (mouse_target == start_elem) {
 					// Start line through click
 					started = true;
 					return {
 						keep: true,
 						element: null,
 						started: started
-					}						
-				} else if($.inArray(svgcontent, parents) === -1) {
+					};
+				}
+				if ($.inArray(svgcontent, parents) === -1) {
 					// Not a valid target element, so remove line
 					$(cur_line).remove();
 					started = false;
@@ -441,55 +447,54 @@ svgEditor.addExtension("Connector", function(S) {
 						keep: false,
 						element: null,
 						started: started
-					}
-				} else {
-					// Valid end element
-					end_elem = mouse_target;
-					
-					var start_id = start_elem.id, end_id = end_elem.id;
-					var conn_str = start_id + " " + end_id;
-					var alt_str = end_id + " " + start_id;
-					// Don't create connector if one already exists
-					var dupe = $(svgcontent).find(conn_sel).filter(function() {
-						var conn = this.getAttributeNS(se_ns, "connector");
-						if(conn == conn_str || conn == alt_str) return true;
-					});
-					if(dupe.length) {
-						$(cur_line).remove();
-						return {
-							keep: false,
-							element: null,
-							started: false
-						}
-					}
-					
-					var bb = svgCanvas.getStrokedBBox([end_elem]);
+					};
+				}
+				// Valid end element
+				end_elem = mouse_target;
 					
-					var pt = getBBintersect(start_x, start_y, bb, getOffset('start', cur_line));
-					setPoint(cur_line, 'end', pt.x, pt.y, true);
-					$(cur_line)
-						.data("c_start", start_id)
-						.data("c_end", end_id)
-						.data("end_bb", bb);
-					se_ns = svgCanvas.getEditorNS(true);
-					cur_line.setAttributeNS(se_ns, "se:connector", conn_str);
-					cur_line.setAttribute('class', conn_sel.substr(1));
-					cur_line.setAttribute('opacity', 1);
-					svgCanvas.addToSelection([cur_line]);
-					svgCanvas.moveToBottomSelectedElement();
-					selManager.requestSelector(cur_line).showGrips(false);
-					started = false;
+				var start_id = start_elem.id, end_id = end_elem.id;
+				var conn_str = start_id + " " + end_id;
+				var alt_str = end_id + " " + start_id;
+				// Don't create connector if one already exists
+				var dupe = $(svgcontent).find(conn_sel).filter(function() {
+					var conn = this.getAttributeNS(se_ns, "connector");
+					if (conn == conn_str || conn == alt_str) {return true;}
+				});
+				if(dupe.length) {
+					$(cur_line).remove();
 					return {
-						keep: true,
-						element: cur_line,
-						started: started
-					}
+						keep: false,
+						element: null,
+						started: false
+					};
 				}
+				
+				var bb = svgCanvas.getStrokedBBox([end_elem]);
+				
+				var pt = getBBintersect(start_x, start_y, bb, getOffset('start', cur_line));
+				setPoint(cur_line, 'end', pt.x, pt.y, true);
+				$(cur_line)
+					.data("c_start", start_id)
+					.data("c_end", end_id)
+					.data("end_bb", bb);
+				se_ns = svgCanvas.getEditorNS(true);
+				cur_line.setAttributeNS(se_ns, "se:connector", conn_str);
+				cur_line.setAttribute('class', conn_sel.substr(1));
+				cur_line.setAttribute('opacity', 1);
+				svgCanvas.addToSelection([cur_line]);
+				svgCanvas.moveToBottomSelectedElement();
+				selManager.requestSelector(cur_line).showGrips(false);
+				started = false;
+				return {
+					keep: true,
+					element: cur_line,
+					started: started
+				};
 			}
 		},
 		selectedChanged: function(opts) {
 			// TODO: Find better way to skip operations if no connectors are in use
-			if(!$(svgcontent).find(conn_sel).length) return;
+			if(!$(svgcontent).find(conn_sel).length) {return;}
 			
 			if(svgCanvas.getMode() == 'connector') {
 				svgCanvas.setMode('select');
@@ -518,19 +523,20 @@ svgEditor.addExtension("Connector", function(S) {
 		},
 		elementChanged: function(opts) {
 			var elem = opts.elems[0];
-			if (elem && elem.tagName == 'svg' && elem.id == "svgcontent") {
+			if (elem && elem.tagName === 'svg' && elem.id === 'svgcontent') {
 				// Update svgcontent (can change on import)
 				svgcontent = elem;
 				init();
 			}
 			
 			// Has marker, so change offset
-			if(elem && (
+			var start;
+			if (elem && (
 				elem.getAttribute("marker-start") ||
 				elem.getAttribute("marker-mid") ||
 				elem.getAttribute("marker-end")
 			)) {
-				var start = elem.getAttribute("marker-start");
+				start = elem.getAttribute("marker-start");
 				var mid = elem.getAttribute("marker-mid");
 				var end = elem.getAttribute("marker-end");
 				cur_line = elem;
@@ -538,13 +544,13 @@ svgEditor.addExtension("Connector", function(S) {
 					.data("start_off", !!start)
 					.data("end_off", !!end);
 				
-				if(elem.tagName == "line" && mid) {
+				if (elem.tagName === 'line' && mid) {
 					// Convert to polyline to accept mid-arrow
 					
-					var x1 = elem.getAttribute('x1')-0;
-					var x2 = elem.getAttribute('x2')-0;
-					var y1 = elem.getAttribute('y1')-0;
-					var y2 = elem.getAttribute('y2')-0;
+					var x1 = Number(elem.getAttribute('x1'));
+					var x2 = Number(elem.getAttribute('x2'));
+					var y1 = Number(elem.getAttribute('y1'));
+					var y2 = Number(elem.getAttribute('y2'));
 					var id = elem.id;
 					
 					var mid_pt = (' '+((x1+x2)/2)+','+((y1+y2)/2) + ' ');
@@ -567,17 +573,17 @@ svgEditor.addExtension("Connector", function(S) {
 				}
 			}
 			// Update line if it's a connector
-			if(elem.getAttribute('class') == conn_sel.substr(1)) {
-				var start = getElem(elData(elem, 'c_start'));
+			if (elem.getAttribute('class') == conn_sel.substr(1)) {
+				start = getElem(elData(elem, 'c_start'));
 				updateConnectors([start]);
 			} else {
 				updateConnectors();
 			}
 		},
 		toolButtonStateUpdate: function(opts) {
-			if(opts.nostroke) {
+			if (opts.nostroke) {
 				if ($('#mode_connect').hasClass('tool_button_current')) {
-					clickSelect();
+					svgEditor.clickSelect();
 				}
 			}
 			$('#mode_connect')

+ 67 - 0
main/inc/lib/svg-edit/extensions/ext-executablebuilder.js

@@ -0,0 +1,67 @@
+/*globals svgEditor*/
+/*
+Depends on Firefox add-on and executables from https://github.com/brettz9/webappfind
+
+Todos:
+1. See WebAppFind Readme for SVG-related todos
+*/
+(function () {'use strict';
+
+var pathID,
+    saveMessage = 'webapp-save',
+    readMessage = 'webapp-read',
+    excludedMessages = [readMessage, saveMessage];
+
+window.addEventListener('message', function(e) {
+    if (e.origin !== window.location.origin || // PRIVACY AND SECURITY! (for viewing and saving, respectively)
+        (!Array.isArray(e.data) || excludedMessages.indexOf(e.data[0]) > -1) // Validate format and avoid our post below
+    ) {
+        return;
+    }
+    var svgString,
+        messageType = e.data[0];
+    switch (messageType) {
+        case 'webapp-view':
+            // Populate the contents
+            pathID = e.data[1];
+            
+            svgString = e.data[2];
+            svgEditor.loadFromString(svgString);
+            
+            /*if ($('#tool_save_file')) {
+                $('#tool_save_file').disabled = false;
+            }*/
+            break;
+        case 'webapp-save-end':
+            alert('save complete for pathID ' + e.data[1] + '!');
+            break;
+        default:
+            throw 'Unexpected mode';
+    }
+}, false);
+
+window.postMessage([readMessage], window.location.origin !== 'null' ? window.location.origin : '*'); // Avoid "null" string error for file: protocol (even though file protocol not currently supported by add-on)
+
+svgEditor.addExtension('WebAppFind', function() {
+
+    return {
+        name: 'WebAppFind',
+        svgicons: svgEditor.curConfig.extPath + 'executablebuilder-icocreator.svg',
+        buttons: [{
+            id: 'webappfind_ico_export', // 
+            type: 'app_menu',
+            title: 'Export ICO Image back to Disk',
+            position: 4, // Before 0-based index position 4 (after the regular "Save Image (S)")
+            events: {
+                click: function () {
+                    if (!pathID) { // Not ready yet as haven't received first payload
+                        return;
+                    }
+                    window.postMessage([saveMessage, pathID, svgEditor.canvas.getSvgString()], window.location.origin);
+                }
+            }
+        }]
+    };
+});
+
+}());

+ 91 - 88
main/inc/lib/svg-edit/extensions/ext-eyedropper.js

@@ -1,7 +1,9 @@
+/*globals svgEditor, svgedit, $*/
+/*jslint vars: true, eqeq: true*/
 /*
  * ext-eyedropper.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Jeff Schiller
  *
@@ -13,97 +15,98 @@
 // 3) svg_editor.js
 // 4) svgcanvas.js
 
-svgEditor.addExtension("eyedropper", function(S) {
-		var svgcontent = S.svgcontent,
-			svgns = "http://www.w3.org/2000/svg",
-			svgdoc = S.svgroot.parentNode.ownerDocument,
-			svgCanvas = svgEditor.canvas,
-			ChangeElementCommand = svgedit.history.ChangeElementCommand,
-			addToHistory = function(cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); },
-			currentStyle = {fillPaint: "red", fillOpacity: 1.0,
-							strokePaint: "black", strokeOpacity: 1.0, 
-							strokeWidth: 5, strokeDashArray: null,
-							opacity: 1.0,
-							strokeLinecap: 'butt',
-							strokeLinejoin: 'miter',
-							};
-							
-		function getStyle(opts) {
-			// if we are in eyedropper mode, we don't want to disable the eye-dropper tool
-			var mode = svgCanvas.getMode();
-			if (mode == "eyedropper") return;
+svgEditor.addExtension("eyedropper", function(S) {'use strict';
+	var // NS = svgedit.NS,
+		// svgcontent = S.svgcontent,
+		// svgdoc = S.svgroot.parentNode.ownerDocument,
+		svgCanvas = svgEditor.canvas,
+		ChangeElementCommand = svgedit.history.ChangeElementCommand,
+		addToHistory = function(cmd) { svgCanvas.undoMgr.addCommandToHistory(cmd); },
+		currentStyle = {
+			fillPaint: "red", fillOpacity: 1.0,
+			strokePaint: "black", strokeOpacity: 1.0, 
+			strokeWidth: 5, strokeDashArray: null,
+			opacity: 1.0,
+			strokeLinecap: 'butt',
+			strokeLinejoin: 'miter'
+		};
 
-			var elem = null;
-			var tool = $('#tool_eyedropper');
-			// enable-eye-dropper if one element is selected
-			if (!opts.multiselected && opts.elems[0] &&
-				$.inArray(opts.elems[0].nodeName, ['svg', 'g', 'use']) == -1) 
-			{
-				elem = opts.elems[0];
-				tool.removeClass('disabled');
-				// grab the current style
-				currentStyle.fillPaint = elem.getAttribute("fill") || "black";
-				currentStyle.fillOpacity = elem.getAttribute("fill-opacity") || 1.0;
-				currentStyle.strokePaint = elem.getAttribute("stroke");
-				currentStyle.strokeOpacity = elem.getAttribute("stroke-opacity") || 1.0;
-				currentStyle.strokeWidth = elem.getAttribute("stroke-width");
-				currentStyle.strokeDashArray = elem.getAttribute("stroke-dasharray");
-				currentStyle.strokeLinecap = elem.getAttribute("stroke-linecap");
-				currentStyle.strokeLinejoin = elem.getAttribute("stroke-linejoin");
-				currentStyle.opacity = elem.getAttribute("opacity") || 1.0;
-			}
-			// disable eye-dropper tool
-			else {
-				tool.addClass('disabled');
-			}
+	function getStyle(opts) {
+		// if we are in eyedropper mode, we don't want to disable the eye-dropper tool
+		var mode = svgCanvas.getMode();
+		if (mode == "eyedropper") {return;}
 
+		var elem = null;
+		var tool = $('#tool_eyedropper');
+		// enable-eye-dropper if one element is selected
+		if (!opts.multiselected && opts.elems[0] &&
+			$.inArray(opts.elems[0].nodeName, ['svg', 'g', 'use']) === -1
+		) {
+			elem = opts.elems[0];
+			tool.removeClass('disabled');
+			// grab the current style
+			currentStyle.fillPaint = elem.getAttribute("fill") || "black";
+			currentStyle.fillOpacity = elem.getAttribute("fill-opacity") || 1.0;
+			currentStyle.strokePaint = elem.getAttribute("stroke");
+			currentStyle.strokeOpacity = elem.getAttribute("stroke-opacity") || 1.0;
+			currentStyle.strokeWidth = elem.getAttribute("stroke-width");
+			currentStyle.strokeDashArray = elem.getAttribute("stroke-dasharray");
+			currentStyle.strokeLinecap = elem.getAttribute("stroke-linecap");
+			currentStyle.strokeLinejoin = elem.getAttribute("stroke-linejoin");
+			currentStyle.opacity = elem.getAttribute("opacity") || 1.0;
 		}
-		
-		return {
-			name: "eyedropper",
-			svgicons: "extensions/eyedropper-icon.xml",
-			buttons: [{
-				id: "tool_eyedropper",
-				type: "mode",
-				title: "Eye Dropper Tool",
-				key: "I",
-				events: {
-					"click": function() {
-						svgCanvas.setMode("eyedropper");
-					}
+		// disable eye-dropper tool
+		else {
+			tool.addClass('disabled');
+		}
+
+	}
+	
+	return {
+		name: "eyedropper",
+		svgicons: svgEditor.curConfig.extPath + "eyedropper-icon.xml",
+		buttons: [{
+			id: "tool_eyedropper",
+			type: "mode",
+			title: "Eye Dropper Tool",
+			key: "I",
+			events: {
+				"click": function() {
+					svgCanvas.setMode("eyedropper");
 				}
-			}],
-			
-			// if we have selected an element, grab its paint and enable the eye dropper button
-			selectedChanged: getStyle,
-			elementChanged: getStyle,
-			
-			mouseDown: function(opts) {
-				var mode = svgCanvas.getMode();
-				if (mode == "eyedropper") {
-					var e = opts.event;
-					var target = e.target;
-					if ($.inArray(target.nodeName, ['svg', 'g', 'use']) == -1) {
-						var changes = {};
+			}
+		}],
+		
+		// if we have selected an element, grab its paint and enable the eye dropper button
+		selectedChanged: getStyle,
+		elementChanged: getStyle,
+		
+		mouseDown: function(opts) {
+			var mode = svgCanvas.getMode();
+			if (mode == "eyedropper") {
+				var e = opts.event;
+				var target = e.target;
+				if ($.inArray(target.nodeName, ['svg', 'g', 'use']) === -1) {
+					var changes = {};
 
-						var change = function(elem, attrname, newvalue) {
-							changes[attrname] = elem.getAttribute(attrname);
-							elem.setAttribute(attrname, newvalue);
-						};
-						
-						if (currentStyle.fillPaint) 		change(target, "fill", currentStyle.fillPaint);
-						if (currentStyle.fillOpacity) 		change(target, "fill-opacity", currentStyle.fillOpacity);
-						if (currentStyle.strokePaint) 		change(target, "stroke", currentStyle.strokePaint);
-						if (currentStyle.strokeOpacity) 	change(target, "stroke-opacity", currentStyle.strokeOpacity);
-						if (currentStyle.strokeWidth) 		change(target, "stroke-width", currentStyle.strokeWidth);
-						if (currentStyle.strokeDashArray) 	change(target, "stroke-dasharray", currentStyle.strokeDashArray);
-						if (currentStyle.opacity) 			change(target, "opacity", currentStyle.opacity);
-						if (currentStyle.strokeLinecap) 	change(target, "stroke-linecap", currentStyle.strokeLinecap);
-						if (currentStyle.strokeLinejoin) 	change(target, "stroke-linejoin", currentStyle.strokeLinejoin);
-						
-						addToHistory(new ChangeElementCommand(target, changes));
-					}
+					var change = function(elem, attrname, newvalue) {
+						changes[attrname] = elem.getAttribute(attrname);
+						elem.setAttribute(attrname, newvalue);
+					};
+					
+					if (currentStyle.fillPaint) {change(target, "fill", currentStyle.fillPaint);}
+					if (currentStyle.fillOpacity) {change(target, "fill-opacity", currentStyle.fillOpacity);}
+					if (currentStyle.strokePaint) {change(target, "stroke", currentStyle.strokePaint);}
+					if (currentStyle.strokeOpacity) {change(target, "stroke-opacity", currentStyle.strokeOpacity);}
+					if (currentStyle.strokeWidth) {change(target, "stroke-width", currentStyle.strokeWidth);}
+					if (currentStyle.strokeDashArray) {change(target, "stroke-dasharray", currentStyle.strokeDashArray);}
+					if (currentStyle.opacity) {change(target, "opacity", currentStyle.opacity);}
+					if (currentStyle.strokeLinecap) {change(target, "stroke-linecap", currentStyle.strokeLinecap);}
+					if (currentStyle.strokeLinejoin) {change(target, "stroke-linejoin", currentStyle.strokeLinejoin);}
+					
+					addToHistory(new ChangeElementCommand(target, changes));
 				}
-			},
-		};
+			}
+		}
+	};
 });

+ 229 - 238
main/inc/lib/svg-edit/extensions/ext-foreignobject.js

@@ -1,3 +1,5 @@
+/*globals svgEditor, svgedit, svgCanvas, $*/
+/*jslint vars: true, eqeq: true, todo: true*/
 /*
  * ext-foreignobject.js
  *
@@ -9,269 +11,258 @@
  */
 
 svgEditor.addExtension("foreignObject", function(S) {
-		var svgcontent = S.svgcontent,
-			addElem = S.addSvgElementFromJson,
-			selElems,
-			svgns = "http://www.w3.org/2000/svg",
-			xlinkns = "http://www.w3.org/1999/xlink",
-			xmlns = "http://www.w3.org/XML/1998/namespace",
-			xmlnsns = "http://www.w3.org/2000/xmlns/",
-			se_ns = "http://svg-edit.googlecode.com",
-			htmlns = "http://www.w3.org/1999/xhtml",
-			mathns = "http://www.w3.org/1998/Math/MathML",
-			editingforeign = false,
-			svgdoc = S.svgroot.parentNode.ownerDocument,
-			started,
-			newFO;
-			
-			
-		var properlySourceSizeTextArea = function(){
-			// TODO: remove magic numbers here and get values from CSS
-			var height = $('#svg_source_container').height() - 80;
-			$('#svg_source_textarea').css('height', height);
-		};
+	var NS = svgedit.NS,
+		Utils = svgedit.utilities,
+		svgcontent = S.svgcontent,
+		addElem = S.addSvgElementFromJson,
+		selElems,
+		editingforeign = false,
+		svgdoc = S.svgroot.parentNode.ownerDocument,
+		started,
+		newFO;
 
-		function showPanel(on) {
-			var fc_rules = $('#fc_rules');
-			if(!fc_rules.length) {
-				fc_rules = $('<style id="fc_rules"><\/style>').appendTo('head');
-			} 
-			fc_rules.text(!on?"":" #tool_topath { display: none !important; }");
-			$('#foreignObject_panel').toggle(on);
-		}
+	var properlySourceSizeTextArea = function () {
+		// TODO: remove magic numbers here and get values from CSS
+		var height = $('#svg_source_container').height() - 80;
+		$('#svg_source_textarea').css('height', height);
+	};
 
-		function toggleSourceButtons(on) {
-			$('#tool_source_save, #tool_source_cancel').toggle(!on);
-			$('#foreign_save, #foreign_cancel').toggle(on);
+	function showPanel(on) {
+		var fc_rules = $('#fc_rules');
+		if(!fc_rules.length) {
+			fc_rules = $('<style id="fc_rules"><\/style>').appendTo('head');
 		}
-		
-			
-		// Function: setForeignString(xmlString, elt)
-		// This function sets the content of element elt to the input XML.
-		//
-		// Parameters:
-		// xmlString - The XML text.
-		// elt - the parent element to append to
-		//
-		// Returns:
-		// This function returns false if the set was unsuccessful, true otherwise.
-		function setForeignString(xmlString) {
-			var elt = selElems[0];
-			try {
-				// convert string into XML document
-				var newDoc = Utils.text2xml('<svg xmlns="'+svgns+'" xmlns:xlink="'+xlinkns+'">'+xmlString+'</svg>');
-				// run it through our sanitizer to remove anything we do not support
-				S.sanitizeSvg(newDoc.documentElement);
-				elt.parentNode.replaceChild(svgdoc.importNode(newDoc.documentElement.firstChild, true), elt);
-				S.call("changed", [elt]);
-				svgCanvas.clearSelection();
-			} catch(e) {
-				console.log(e);
-				return false;
-			}
-	
-			return true;
-		};
+		fc_rules.text(!on?"":" #tool_topath { display: none !important; }");
+		$('#foreignObject_panel').toggle(on);
+	}
 
-		function showForeignEditor() {
-			var elt = selElems[0];
-			if (!elt || editingforeign) return;
-			editingforeign = true;
-			toggleSourceButtons(true);
-			elt.removeAttribute('fill');
+	function toggleSourceButtons(on) {
+		$('#tool_source_save, #tool_source_cancel').toggle(!on);
+		$('#foreign_save, #foreign_cancel').toggle(on);
+	}
 
-			var str = S.svgToString(elt, 0);
-			$('#svg_source_textarea').val(str);
-			$('#svg_source_editor').fadeIn();
-			properlySourceSizeTextArea();
-			$('#svg_source_textarea').focus();
-		}
-		
-		function setAttr(attr, val) {
-			svgCanvas.changeSelectedAttribute(attr, val);
-			S.call("changed", selElems);
+	// Function: setForeignString(xmlString, elt)
+	// This function sets the content of element elt to the input XML.
+	//
+	// Parameters:
+	// xmlString - The XML text.
+	// elt - the parent element to append to
+	//
+	// Returns:
+	// This function returns false if the set was unsuccessful, true otherwise.
+	function setForeignString(xmlString) {
+		var elt = selElems[0];
+		try {
+			// convert string into XML document
+			var newDoc = Utils.text2xml('<svg xmlns="' + NS.SVG + '" xmlns:xlink="' + NS.XLINK + '">' + xmlString + '</svg>');
+			// run it through our sanitizer to remove anything we do not support
+			S.sanitizeSvg(newDoc.documentElement);
+			elt.parentNode.replaceChild(svgdoc.importNode(newDoc.documentElement.firstChild, true), elt);
+			S.call("changed", [elt]);
+			svgCanvas.clearSelection();
+		} catch(e) {
+			console.log(e);
+			return false;
 		}
-		
-		
-		return {
-			name: "foreignObject",
-			svgicons: "extensions/foreignobject-icons.xml",
-			buttons: [{
-				id: "tool_foreign",
-				type: "mode",
-				title: "Foreign Object Tool",
-				events: {
-					'click': function() {
-						svgCanvas.setMode('foreign')
-					}
+
+		return true;
+	}
+
+	function showForeignEditor() {
+		var elt = selElems[0];
+		if (!elt || editingforeign) {return;}
+		editingforeign = true;
+		toggleSourceButtons(true);
+		elt.removeAttribute('fill');
+
+		var str = S.svgToString(elt, 0);
+		$('#svg_source_textarea').val(str);
+		$('#svg_source_editor').fadeIn();
+		properlySourceSizeTextArea();
+		$('#svg_source_textarea').focus();
+	}
+
+	function setAttr(attr, val) {
+		svgCanvas.changeSelectedAttribute(attr, val);
+		S.call("changed", selElems);
+	}
+
+	return {
+		name: "foreignObject",
+		svgicons: svgEditor.curConfig.extPath + "foreignobject-icons.xml",
+		buttons: [{
+			id: "tool_foreign",
+			type: "mode",
+			title: "Foreign Object Tool",
+			events: {
+				'click': function() {
+					svgCanvas.setMode('foreign');
 				}
-			},{
-				id: "edit_foreign",
-				type: "context",
-				panel: "foreignObject_panel",
-				title: "Edit ForeignObject Content",
-				events: {
-					'click': function() {
-						showForeignEditor();
-					}
+			}
+		},{
+			id: "edit_foreign",
+			type: "context",
+			panel: "foreignObject_panel",
+			title: "Edit ForeignObject Content",
+			events: {
+				'click': function() {
+					showForeignEditor();
 				}
-			}],
-			
-			context_tools: [{
-				type: "input",
-				panel: "foreignObject_panel",
-				title: "Change foreignObject's width",
-				id: "foreign_width",
-				label: "w",
-				size: 3,
-				events: {
-					change: function() {
-						setAttr('width', this.value);
-					}
+			}
+		}],
+
+		context_tools: [{
+			type: "input",
+			panel: "foreignObject_panel",
+			title: "Change foreignObject's width",
+			id: "foreign_width",
+			label: "w",
+			size: 3,
+			events: {
+				change: function() {
+					setAttr('width', this.value);
 				}
-			},{
-				type: "input",
-				panel: "foreignObject_panel",
-				title: "Change foreignObject's height",
-				id: "foreign_height",
-				label: "h",
-				events: {
-					change: function() {
-						setAttr('height', this.value);
-					}
+			}
+		},{
+			type: "input",
+			panel: "foreignObject_panel",
+			title: "Change foreignObject's height",
+			id: "foreign_height",
+			label: "h",
+			events: {
+				change: function() {
+					setAttr('height', this.value);
 				}
-			}, {
-				type: "input",
-				panel: "foreignObject_panel",
-				title: "Change foreignObject's font size",
-				id: "foreign_font_size",
-				label: "font-size",
-				size: 2,
-				defval: 16,
-				events: {
-					change: function() {
-						setAttr('font-size', this.value);
-					}
+			}
+		}, {
+			type: "input",
+			panel: "foreignObject_panel",
+			title: "Change foreignObject's font size",
+			id: "foreign_font_size",
+			label: "font-size",
+			size: 2,
+			defval: 16,
+			events: {
+				change: function() {
+					setAttr('font-size', this.value);
 				}
 			}
-			
-			
-			],
-			callback: function() {
-				$('#foreignObject_panel').hide();
+		}
 
-				var endChanges = function() {
-					$('#svg_source_editor').hide();
-					editingforeign = false;
-					$('#svg_source_textarea').blur();
-					toggleSourceButtons(false);
-				}
+		],
+		callback: function() {
+			$('#foreignObject_panel').hide();
 
-				// TODO: Needs to be done after orig icon loads
-				setTimeout(function() {				
-					// Create source save/cancel buttons
-					var save = $('#tool_source_save').clone()
-						.hide().attr('id', 'foreign_save').unbind()
-						.appendTo("#tool_source_back").click(function() {
-							
-							if (!editingforeign) return;
+			var endChanges = function() {
+				$('#svg_source_editor').hide();
+				editingforeign = false;
+				$('#svg_source_textarea').blur();
+				toggleSourceButtons(false);
+			};
 
-							if (!setForeignString($('#svg_source_textarea').val())) {
-								$.confirm("Errors found. Revert to original?", function(ok) {
-									if(!ok) return false;
-									endChanges();
-								});
-							} else {
+			// TODO: Needs to be done after orig icon loads
+			setTimeout(function() {
+				// Create source save/cancel buttons
+				var save = $('#tool_source_save').clone()
+					.hide().attr('id', 'foreign_save').unbind()
+					.appendTo("#tool_source_back").click(function() {
+
+						if (!editingforeign) {return;}
+
+						if (!setForeignString($('#svg_source_textarea').val())) {
+							$.confirm("Errors found. Revert to original?", function(ok) {
+								if(!ok) {return false;}
 								endChanges();
-							}
-							// setSelectMode();	
-						});
-						
-					var cancel = $('#tool_source_cancel').clone()
-						.hide().attr('id', 'foreign_cancel').unbind()
-						.appendTo("#tool_source_back").click(function() {
+							});
+						} else {
 							endChanges();
-						});
-					
-				}, 3000);
-			},
-			mouseDown: function(opts) {
-				var e = opts.event;
-				
-				if(svgCanvas.getMode() == "foreign") {
-
-					started = true;
-					newFO = S.addSvgElementFromJson({
-						"element": "foreignObject",
-						"attr": {
-							"x": opts.start_x,
-							"y": opts.start_y,
-							"id": S.getNextId(),
-							"font-size": 16, //cur_text.font_size,
-							"width": "48",
-							"height": "20",
-							"style": "pointer-events:inherit"
 						}
+						// setSelectMode();	
 					});
-					var m = svgdoc.createElementNS(mathns, 'math');
-					m.setAttributeNS(xmlnsns, 'xmlns', mathns);
-					m.setAttribute('display', 'inline');
-					var mi = svgdoc.createElementNS(mathns, 'mi');
-					mi.setAttribute('mathvariant', 'normal');
-					mi.textContent = "\u03A6";
-					var mo = svgdoc.createElementNS(mathns, 'mo');
-					mo.textContent = "\u222A";
-					var mi2 = svgdoc.createElementNS(mathns, 'mi');
-					mi2.textContent = "\u2133";
-					m.appendChild(mi);
-					m.appendChild(mo);
-					m.appendChild(mi2);
-					newFO.appendChild(m);
-					return {
-						started: true
-					}
-				}
-			},
-			mouseUp: function(opts) {
-				var e = opts.event;
-				if(svgCanvas.getMode() == "foreign" && started) {
-					var attrs = $(newFO).attr(["width", "height"]);
-					keep = (attrs.width != 0 || attrs.height != 0);
-					svgCanvas.addToSelection([newFO], true);
 
-					return {
-						keep: keep,
-						element: newFO
+				var cancel = $('#tool_source_cancel').clone()
+					.hide().attr('id', 'foreign_cancel').unbind()
+					.appendTo("#tool_source_back").click(function() {
+						endChanges();
+					});
+			}, 3000);
+		},
+		mouseDown: function(opts) {
+			var e = opts.event;
+
+			if(svgCanvas.getMode() == "foreign") {
+
+				started = true;
+				newFO = S.addSvgElementFromJson({
+					"element": "foreignObject",
+					"attr": {
+						"x": opts.start_x,
+						"y": opts.start_y,
+						"id": S.getNextId(),
+						"font-size": 16, //cur_text.font_size,
+						"width": "48",
+						"height": "20",
+						"style": "pointer-events:inherit"
 					}
+				});
+				var m = svgdoc.createElementNS(NS.MATH, 'math');
+				m.setAttributeNS(NS.XMLNS, 'xmlns', NS.MATH);
+				m.setAttribute('display', 'inline');
+				var mi = svgdoc.createElementNS(NS.MATH, 'mi');
+				mi.setAttribute('mathvariant', 'normal');
+				mi.textContent = "\u03A6";
+				var mo = svgdoc.createElementNS(NS.MATH, 'mo');
+				mo.textContent = "\u222A";
+				var mi2 = svgdoc.createElementNS(NS.MATH, 'mi');
+				mi2.textContent = "\u2133";
+				m.appendChild(mi);
+				m.appendChild(mo);
+				m.appendChild(mi2);
+				newFO.appendChild(m);
+				return {
+					started: true
+				};
+			}
+		},
+		mouseUp: function(opts) {
+			var e = opts.event;
+			if(svgCanvas.getMode() == "foreign" && started) {
+				var attrs = $(newFO).attr(["width", "height"]);
+				var keep = (attrs.width != 0 || attrs.height != 0);
+				svgCanvas.addToSelection([newFO], true);
 
-				}
-				
-			},
-			selectedChanged: function(opts) {
-				// Use this to update the current selected elements
-				selElems = opts.elems;
-				
-				var i = selElems.length;
-				
-				while(i--) {
-					var elem = selElems[i];
-					if(elem && elem.tagName == "foreignObject") {
-						if(opts.selectedElement && !opts.multiselected) {
-							$('#foreign_font_size').val(elem.getAttribute("font-size"));
-							$('#foreign_width').val(elem.getAttribute("width"));
-							$('#foreign_height').val(elem.getAttribute("height"));
-						
-							showPanel(true);
-						} else {
-							showPanel(false);
-						}
+				return {
+					keep: keep,
+					element: newFO
+				};
+
+			}
+
+		},
+		selectedChanged: function(opts) {
+			// Use this to update the current selected elements
+			selElems = opts.elems;
+
+			var i = selElems.length;
+
+			while(i--) {
+				var elem = selElems[i];
+				if(elem && elem.tagName === 'foreignObject') {
+					if(opts.selectedElement && !opts.multiselected) {
+						$('#foreign_font_size').val(elem.getAttribute("font-size"));
+						$('#foreign_width').val(elem.getAttribute("width"));
+						$('#foreign_height').val(elem.getAttribute("height"));
+						showPanel(true);
 					} else {
 						showPanel(false);
 					}
+				} else {
+					showPanel(false);
 				}
-			},
-			elementChanged: function(opts) {
-				var elem = opts.elems[0];
 			}
-		};
+		},
+		elementChanged: function(opts) {
+			var elem = opts.elems[0];
+		}
+	};
 });

+ 101 - 130
main/inc/lib/svg-edit/extensions/ext-grid.js

@@ -1,3 +1,5 @@
+/*globals svgEditor, svgedit, svgCanvas, $*/
+/*jslint vars: true*/
 /*
  * ext-grid.js
  *
@@ -12,119 +14,95 @@
 // 1) units.js
 // 2) everything else
 
-svgEditor.addExtension("view_grid", function(s) {
-
-    var svgdoc = document.getElementById("svgcanvas").ownerDocument,
-			svgns = "http://www.w3.org/2000/svg",
-			dims = svgEditor.curConfig.dimensions,
-			svgroot = s.svgroot;
-
-	var showGrid = false;
-    var assignAttributes = svgCanvas.assignAttributes;
-    
-    var hcanvas = document.createElement('canvas');
-    $(hcanvas).hide().appendTo('body');
-
-    var canvasgrid = svgdoc.createElementNS(svgns, "svg");
-    assignAttributes(canvasgrid, {
-        'id': 'canvasGrid',
-        'width': '100%',
-        'height': '100%',
-        'x': 0,
-        'y': 0,
-        'overflow': 'visible',
-        'display': 'none'
-    });
-    
-    var canvBG = $('#canvasBackground');
-    canvBG.append(canvasgrid);
-
-        // grid-pattern
-        var gridPattern = svgdoc.createElementNS(svgns, "pattern");
-        assignAttributes(gridPattern, {
-            'id': 'gridpattern',
-            'patternUnits': 'userSpaceOnUse',
-            'x': 0, //-(value.strokeWidth / 2), // position for strokewidth
-            'y': 0, //-(value.strokeWidth / 2), // position for strokewidth
-            'width': 100,
-            'height': 100
-        });
-        
-        var gridimg = svgdoc.createElementNS(svgns, "image");
-        assignAttributes(gridimg, {
-            'x': 0,
-            'y': 0,
-            'width': 100,
-            'height': 100
-        });
-        
-        gridPattern.appendChild(gridimg);
-        $('#svgroot defs').append(gridPattern);
-
-        // grid-box
-        var gridBox = svgdoc.createElementNS(svgns, "rect");
-        assignAttributes(gridBox, {
-            'width': '100%',
-            'height': '100%',
-            'x': 0,
-            'y': 0,
-            'stroke-width': 0,
-            'stroke': 'none',
-            'fill': 'url(#gridpattern)',
-            'style': 'pointer-events: none; display:visible;'
-        });
-        $('#canvasGrid').append(gridBox);
-//     });
+svgEditor.addExtension('view_grid', function() { 'use strict';
+
+	var NS = svgedit.NS,
+		svgdoc = document.getElementById('svgcanvas').ownerDocument,
+		showGrid = false,
+		assignAttributes = svgCanvas.assignAttributes,
+		hcanvas = document.createElement('canvas'),
+		canvBG = $('#canvasBackground'),
+		units = svgedit.units.getTypeMap(),
+		intervals = [0.01, 0.1, 1, 10, 100, 1000];
+
+	$(hcanvas).hide().appendTo('body');
+
+	var canvasGrid = svgdoc.createElementNS(NS.SVG, 'svg');
+	assignAttributes(canvasGrid, {
+		'id': 'canvasGrid',
+		'width': '100%',
+		'height': '100%',
+		'x': 0,
+		'y': 0,
+		'overflow': 'visible',
+		'display': 'none'
+	});
+	canvBG.append(canvasGrid);
+
+	// grid-pattern
+	var gridPattern = svgdoc.createElementNS(NS.SVG, 'pattern');
+	assignAttributes(gridPattern, {
+		'id': 'gridpattern',
+		'patternUnits': 'userSpaceOnUse',
+		'x': 0, //-(value.strokeWidth / 2), // position for strokewidth
+		'y': 0, //-(value.strokeWidth / 2), // position for strokewidth
+		'width': 100,
+		'height': 100
+	});
+
+	var gridimg = svgdoc.createElementNS(NS.SVG, 'image');
+	assignAttributes(gridimg, {
+		'x': 0,
+		'y': 0,
+		'width': 100,
+		'height': 100
+	});
+	gridPattern.appendChild(gridimg);
+	$('#svgroot defs').append(gridPattern);
+
+	// grid-box
+	var gridBox = svgdoc.createElementNS(NS.SVG, 'rect');
+	assignAttributes(gridBox, {
+		'width': '100%',
+		'height': '100%',
+		'x': 0,
+		'y': 0,
+		'stroke-width': 0,
+		'stroke': 'none',
+		'fill': 'url(#gridpattern)',
+		'style': 'pointer-events: none; display:visible;'
+	});
+	$('#canvasGrid').append(gridBox);
 
 	function updateGrid(zoom) {
+		var i;
 		// TODO: Try this with <line> elements, then compare performance difference
-	
-		var bgwidth = +canvBG.attr('width');
-		var bgheight = +canvBG.attr('height');
-		
-		var units = svgedit.units.getTypeMap();
 		var unit = units[svgEditor.curConfig.baseUnit]; // 1 = 1px
-		var r_intervals = [.01, .1, 1, 10, 100, 1000];
-	
-		var d = 0;
-		var is_x = (d === 0);
-		var dim = is_x ? 'x' : 'y';
-		var lentype = is_x?'width':'height';
-		var c_elem = svgCanvas.getContentElem();
-		var content_d = c_elem.getAttribute(dim)-0;
-		
-		var hcanv = hcanvas;
-		
 		var u_multi = unit * zoom;
-		
 		// Calculate the main number interval
 		var raw_m = 100 / u_multi;
 		var multi = 1;
-		for(var i = 0; i < r_intervals.length; i++) {
-			var num = r_intervals[i];
+		for (i = 0; i < intervals.length; i++) {
+			var num = intervals[i];
 			multi = num;
-			if(raw_m <= num) {
+			if (raw_m <= num) {
 				break;
 			}
 		}
-		
 		var big_int = multi * u_multi;
 
 		// Set the canvas size to the width of the container
-		hcanv.width = big_int;
-		hcanv.height = big_int;
-		var ctx = hcanv.getContext("2d");
-
-		var ruler_d = 0; 
-		var cur_d = .5;
-		
+		hcanvas.width = big_int;
+		hcanvas.height = big_int;
+		var ctx = hcanvas.getContext('2d');
+		var cur_d = 0.5;
 		var part = big_int / 10;
 
 		ctx.globalAlpha = 0.2;
-		ctx.strokeStyle = "#000";
-		for(var i = 1; i < 10; i++) {
-			var sub_d = Math.round(part * i) + .5;
-// 					var line_num = (i % 2)?12:10;
+		ctx.strokeStyle = svgEditor.curConfig.gridColor;
+		for (i = 1; i < 10; i++) {
+			var sub_d = Math.round(part * i) + 0.5;
+			// var line_num = (i % 2)?12:10;
 			var line_num = 0;
 			ctx.moveTo(sub_d, big_int);
 			ctx.lineTo(sub_d, line_num);
@@ -132,7 +110,7 @@ svgEditor.addExtension("view_grid", function(s) {
 			ctx.lineTo(line_num ,sub_d);
 		}
 		ctx.stroke();
-		ctx.beginPath();  
+		ctx.beginPath();
 		ctx.globalAlpha = 0.5;
 		ctx.moveTo(cur_d, big_int);
 		ctx.lineTo(cur_d, 0);
@@ -141,7 +119,7 @@ svgEditor.addExtension("view_grid", function(s) {
 		ctx.lineTo(0, cur_d);
 		ctx.stroke();
 
-		var datauri = hcanv.toDataURL('image/png');
+		var datauri = hcanvas.toDataURL('image/png');
 		gridimg.setAttribute('width', big_int);
 		gridimg.setAttribute('height', big_int);
 		gridimg.parentNode.setAttribute('width', big_int);
@@ -149,36 +127,29 @@ svgEditor.addExtension("view_grid", function(s) {
 		svgCanvas.setHref(gridimg, datauri);
 	}
 
-    return {
-        name: "view_grid",
-        svgicons: "extensions/grid-icon.xml",
-
-        zoomChanged: function(zoom) {
-            // update size
-            if(showGrid) updateGrid(zoom);
-        },
-
-        buttons: [{
-            id: "view_grid",
-            type: "context",
-            panel: "editor_panel",
-            title: "Show/Hide Grid",
-            events: {
-                'click': function() {
-                    var gr = !$('#view_grid').hasClass('push_button_pressed');
-                    if (gr) {
-                        svgEditor.curConfig.showGrid = showGrid = true;
-                        $('#view_grid').addClass('push_button_pressed').removeClass('tool_button');
-                        $('#canvasGrid').attr('display', 'normal');
-                        updateGrid(svgCanvas.getZoom());
-                    }
-                    else {
-                        svgEditor.curConfig.showGrid = showGrid = false;
-                        $('#view_grid').removeClass('push_button_pressed').addClass('tool_button');
-                        $('#canvasGrid').attr('display', 'none');
-                    }
-                }
-            }
-}]
-        };
-    });
+	return {
+		name: 'view_grid',
+		svgicons: svgEditor.curConfig.extPath + 'grid-icon.xml',
+
+		zoomChanged: function(zoom) {
+			if (showGrid) {updateGrid(zoom);}
+		},
+
+		buttons: [{
+			id: 'view_grid',
+			type: 'context',
+			panel: 'editor_panel',
+			title: 'Show/Hide Grid',
+			events: {
+				click: function() {
+					svgEditor.curConfig.showGrid = showGrid = !showGrid;
+					if (showGrid) {
+						updateGrid(svgCanvas.getZoom());
+					}
+					$('#canvasGrid').toggle(showGrid);
+					$('#view_grid').toggleClass('push_button_pressed tool_button');
+				}
+			}
+		}]
+	};
+});

+ 6 - 4
main/inc/lib/svg-edit/extensions/ext-helloworld.js

@@ -1,7 +1,9 @@
+/*globals svgEditor, svgCanvas, $*/
+/*jslint vars: true, eqeq: true*/
 /*
  * ext-helloworld.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *
@@ -10,16 +12,16 @@
 /* 
 	This is a very basic SVG-Edit extension. It adds a "Hello World" button in
 	the left panel. Clicking on the button, and then the canvas will show the
- 	user the point on the canvas that was clicked on.
+	user the point on the canvas that was clicked on.
 */
  
-svgEditor.addExtension("Hello World", function() {
+svgEditor.addExtension("Hello World", function() {'use strict';
 
 		return {
 			name: "Hello World",
 			// For more notes on how to make an icon file, see the source of
 			// the hellorworld-icon.xml
-			svgicons: "extensions/helloworld-icon.xml",
+			svgicons: svgEditor.curConfig.extPath + "helloworld-icon.xml",
 			
 			// Multiple buttons can be added in this array
 			buttons: [{

+ 134 - 120
main/inc/lib/svg-edit/extensions/ext-imagelib.js

@@ -1,17 +1,18 @@
+/*globals $, svgEditor, svgedit, svgCanvas, DOMParser*/
+/*jslint vars: true, eqeq: true, es5: true, todo: true */
 /*
  * ext-imagelib.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *
  */
 
-svgEditor.addExtension("imagelib", function() {
-	//Chamilo change library title and index.html by index.php
-	
+svgEditor.addExtension("imagelib", function() {'use strict';
+
 	var uiStrings = svgEditor.uiStrings;
-	
+
 	$.extend(uiStrings, {
 		imagelib: {
 			select_lib: 'Select an image library',
@@ -22,37 +23,36 @@ svgEditor.addExtension("imagelib", function() {
 		}
 	});
 
-	var img_libs = [{
-			name: 'Local library (local)',
-			url: 'extensions/imagelib/index.php',
-			//description: 'Demonstration library for SVG-edit on this server'// Chamilo change this line by below
-			description: 'Course gallery'
-		},
-		{
-			name: 'Local library',
-			url: 'extensions/imagelib/groups.php',
-			//description: 'Demonstration library for SVG-edit on this server'// Chamilo change this line by below
-			description: 'Group gallery'
-		},
-		{
-			name: 'Local library',
-			url: 'extensions/imagelib/users.php',
-			//description: 'Demonstration library for SVG-edit on this server'// Chamilo change this line by below
-			description: 'Personal gallery'
-		},		
-		{
-			name: 'IAN Symbol Libraries',
-			url: 'http://ian.umces.edu/symbols/catalog/svgedit/album_chooser.php',
-			description: 'Free library of illustrations'
-		}
+	var img_libs = [
+        {
+            name: 'Local library (local)',
+            url: 'extensions/imagelib/index.php',
+            //description: 'Demonstration library for SVG-edit on this server'// Chamilo change this line by below
+            description: 'Course gallery'
+        },
+        {
+            name: 'Local library',
+            url: 'extensions/imagelib/groups.php',
+            //description: 'Demonstration library for SVG-edit on this server'// Chamilo change this line by below
+            description: 'Group gallery'
+        },
+        {
+            name: 'Local library',
+            url: 'extensions/imagelib/users.php',
+            //description: 'Demonstration library for SVG-edit on this server'// Chamilo change this line by below
+            description: 'Personal gallery'
+        },
+        {
+            name: 'IAN Symbol Libraries',
+            url: 'http://ian.umces.edu/symbols/catalog/svgedit/album_chooser.php',
+            description: 'Free library of illustrations'
+        }
 	];
-	
-	var xlinkns = "http://www.w3.org/1999/xlink";
 
 	function closeBrowser() {
 		$('#imgbrowse_holder').hide();
 	}
-	
+
 	function importImage(url) {
 		var newImage = svgCanvas.addSvgElementFromJson({
 			"element": "image",
@@ -75,110 +75,115 @@ svgEditor.addExtension("imagelib", function() {
 	var cur_meta;
 	var tranfer_stopped = false;
 	var pending = {};
-	
-	//Juan Carlos Raña Trabado hack for Chamilo add window.top. on the bottom line to run into a frame
-	window.top.window.addEventListener("message", function(evt) {							 
-												 
+	var preview, submit;
+
+	window.addEventListener("message", function(evt) {
 		// Receive postMessage data
 		var response = evt.data;
-		
-		if(!response) {
+
+		if (!response || typeof response !== "string") { // Todo: Should namespace postMessage API for this extension and filter out here
 			// Do nothing
 			return;
 		}
-		
+		try { // This block can be removed if embedAPI moves away from a string to an object (if IE9 support not needed)
+			var res = JSON.parse(response);
+			if (res.namespace) { // Part of embedAPI communications
+				return;
+			}
+		}
+		catch (e) {}
+
 		var char1 = response.charAt(0);
-		
+		var id;
 		var svg_str;
 		var img_str;
-		
-		if(char1 != "{" && tranfer_stopped) {
+
+		if (char1 != "{" && tranfer_stopped) {
 			tranfer_stopped = false;
 			return;
 		}
-		
-		if(char1 == '|') {
+
+		if (char1 == '|') {
 			var secondpos = response.indexOf('|', 1);
-			var id = response.substr(1, secondpos-1);
+			id = response.substr(1, secondpos-1);
 			response = response.substr(secondpos+1);
 			char1 = response.charAt(0);
-
 		}
-		
-		
+
+
 		// Hide possible transfer dialog box
 		$('#dialog_box').hide();
-		
+		var entry, cur_meta;
 		switch (char1) {
 			case '{':
 				// Metadata
 				tranfer_stopped = false;
-				var cur_meta = JSON.parse(response);
-				
+				cur_meta = JSON.parse(response);
+
 				pending[cur_meta.id] = cur_meta;
-				
+
 				var name = (cur_meta.name || 'file');
-				
+
 				var message = uiStrings.notification.retrieving.replace('%s', name);
-				
-				if(mode != 'm') {
+
+				if (mode != 'm') {
 					$.process_cancel(message, function() {
 						tranfer_stopped = true;
 						// Should a message be sent back to the frame?
-						
+
 						$('#dialog_box').hide();
 					});
 				} else {
-					var entry = $('<div>' + message + '</div>').data('id', cur_meta.id);
+					entry = $('<div>' + message + '</div>').data('id', cur_meta.id);
 					preview.append(entry);
 					cur_meta.entry = entry;
 				}
-				
+
 				return;
 			case '<':
 				svg_str = true;
 				break;
 			case 'd':
-				if(response.indexOf('data:image/svg+xml') === 0) {
+				if (response.indexOf('data:image/svg+xml') === 0) {
 					var pre = 'data:image/svg+xml;base64,';
 					var src = response.substring(pre.length);
-					response = svgCanvas.Utils.decode64(src);
+					response = svgedit.utilities.decode64(src);
 					svg_str = true;
 					break;
-				} else if(response.indexOf('data:image/') === 0) {
+				} else if (response.indexOf('data:image/') === 0) {
 					img_str = true;
 					break;
 				}
 				// Else fall through
 			default:
 				// TODO: See if there's a way to base64 encode the binary data stream
-// 				var str = 'data:;base64,' + svgCanvas.Utils.encode64(response, true);
-			
+//				var str = 'data:;base64,' + svgedit.utilities.encode64(response, true);
+
 				// Assume it's raw image data
-// 				importImage(str);
-			
+//				importImage(str);
+
 				// Don't give warning as postMessage may have been used by something else
-				if(mode !== 'm') {
+				if (mode !== 'm') {
 					closeBrowser();
 				} else {
 					pending[id].entry.remove();
 				}
-// 				$.alert('Unexpected data was returned: ' + response, function() {
-// 					if(mode !== 'm') {
-// 						closeBrowser();
-// 					} else {
-// 						pending[id].entry.remove();
-// 					}
-// 				});
+//				$.alert('Unexpected data was returned: ' + response, function() {
+//					if (mode !== 'm') {
+//						closeBrowser();
+//					} else {
+//						pending[id].entry.remove();
+//					}
+//				});
 				return;
 		}
-		
+
 		switch (mode) {
 			case 's':
 				// Import one
-				if(svg_str) {
+				if (svg_str) {
 					svgCanvas.importSvgString(response);
-				} else if(img_str) {
+				} else if (img_str) {
 					importImage(response);
 				}
 				closeBrowser();
@@ -186,19 +191,20 @@ svgEditor.addExtension("imagelib", function() {
 			case 'm':
 				// Import multiple
 				multi_arr.push([(svg_str ? 'svg' : 'img'), response]);
-				var cur_meta = pending[id];
-				if(svg_str) {
-					if(cur_meta && cur_meta.name) {
-						var title = cur_meta.name;
-					}  else {
+				var title;
+				cur_meta = pending[id];
+				if (svg_str) {
+					if (cur_meta && cur_meta.name) {
+						title = cur_meta.name;
+					} else {
 						// Try to find a title
 						var xml = new DOMParser().parseFromString(response, 'text/xml').documentElement;
-						var title = $(xml).children('title').first().text() || '(SVG #' + response.length + ')';
+						title = $(xml).children('title').first().text() || '(SVG #' + response.length + ')';
 					}
-					if(cur_meta) {
+					if (cur_meta) {
 						preview.children().each(function() {
-							if($(this).data('id') == id) {
-								if(cur_meta.preview_url) {
+							if ($(this).data('id') == id) {
+								if (cur_meta.preview_url) {
 									$(this).html('<img src="' + cur_meta.preview_url + '">' + title);
 								} else {
 									$(this).text(title);
@@ -211,18 +217,18 @@ svgEditor.addExtension("imagelib", function() {
 						submit.removeAttr('disabled');
 					}
 				} else {
-					if(cur_meta && cur_meta.preview_url) {
-						var title = cur_meta.name || '';
+					if (cur_meta && cur_meta.preview_url) {
+						title = cur_meta.name || '';
 					}
-					if(cur_meta && cur_meta.preview_url) {
-						var entry = '<img src="' + cur_meta.preview_url + '">' + title;
+					if (cur_meta && cur_meta.preview_url) {
+						entry = '<img src="' + cur_meta.preview_url + '">' + title;
 					} else {
-						var entry = '<img src="' + response + '">';
+						entry = '<img src="' + response + '">';
 					}
-				
-					if(cur_meta) {
+
+					if (cur_meta) {
 						preview.children().each(function() {
-							if($(this).data('id') == id) {
+							if ($(this).data('id') == id) {
 								$(this).html(entry);
 								submit.removeAttr('disabled');
 							}
@@ -236,9 +242,9 @@ svgEditor.addExtension("imagelib", function() {
 				break;
 			case 'o':
 				// Open
-				if(!svg_str) break;
+				if (!svg_str) {break;}
 				svgEditor.openPrep(function(ok) {
-					if(!ok) return;
+					if (!ok) {return;}
 					svgCanvas.clear();
 					svgCanvas.setSvgString(response);
 					// updateCanvas();
@@ -247,13 +253,11 @@ svgEditor.addExtension("imagelib", function() {
 				break;
 		}
 	}, true);
-	
-	var preview, submit;
 
 	function toggleMulti(show) {
-	
+
 		$('#lib_framewrap, #imglib_opts').css({right: (show ? 200 : 10)});
-		if(!preview) {
+		if (!preview) {
 			preview = $('<div id=imglib_preview>').css({
 				position: 'absolute',
 				top: 45,
@@ -263,12 +267,14 @@ svgEditor.addExtension("imagelib", function() {
 				background: '#fff',
 				overflow: 'auto'
 			}).insertAfter('#lib_framewrap');
-			
-			submit = $('<button disabled>Import selected</button>').appendTo('#imgbrowse').click(function() {
+
+			submit = $('<button disabled>Import selected</button>')
+				.appendTo('#imgbrowse')
+				.on("click touchend", function() {
 				$.each(multi_arr, function(i) {
 					var type = this[0];
 					var data = this[1];
-					if(type == 'svg') {
+					if (type == 'svg') {
 						svgCanvas.importSvgString(data);
 					} else {
 						importImage(data);
@@ -285,7 +291,7 @@ svgEditor.addExtension("imagelib", function() {
 			});
 
 		}
-		
+
 		preview.toggle(show);
 		submit.toggle(show);
 	}
@@ -293,7 +299,7 @@ svgEditor.addExtension("imagelib", function() {
 	function showBrowser() {
 
 		var browser = $('#imgbrowse');
-		if(!browser.length) {
+		if (!browser.length) {
 			$('<div id=imgbrowse_holder><div id=imgbrowse class=toolbar_button>\
 			</div></div>').insertAfter('#svg_docprops');
 			browser = $('#imgbrowse');
@@ -302,25 +308,29 @@ svgEditor.addExtension("imagelib", function() {
 
 			var lib_opts = $('<ul id=imglib_opts>').appendTo(browser);
 			var frame = $('<iframe/>').prependTo(browser).hide().wrap('<div id=lib_framewrap>');
-			
+
 			var header = $('<h1>').prependTo(browser).text(all_libs).css({
 				position: 'absolute',
 				top: 0,
 				left: 0,
 				width: '100%'
 			});
-			
-			var cancel = $('<button>' + uiStrings.common.cancel + '</button>').appendTo(browser).click(function() {
+
+			var cancel = $('<button>' + uiStrings.common.cancel + '</button>')
+				.appendTo(browser)
+				.on("click touchend", function() {
 				$('#imgbrowse_holder').hide();
 			}).css({
 				position: 'absolute',
 				top: 5,
 				right: -10
 			});
-			
+
 			var leftBlock = $('<span>').css({position:'absolute',top:5,left:10}).appendTo(browser);
-			
-			var back = $('<button hidden>' + uiStrings.imagelib.show_list + '</button>').appendTo(leftBlock).click(function() {
+
+			var back = $('<button hidden>' + uiStrings.imagelib.show_list + '</button>')
+				.appendTo(leftBlock)
+				.on("click touchend", function() {
 				frame.attr('src', 'about:blank').hide();
 				lib_opts.show();
 				header.text(all_libs);
@@ -328,8 +338,8 @@ svgEditor.addExtension("imagelib", function() {
 			}).css({
 				'margin-right': 5
 			}).hide();
-			
-			var type = $('<select><option value=s>' + 
+
+			var type = $('<select><option value=s>' +
 			uiStrings.imagelib.import_single + '</option><option value=m>' +
 			uiStrings.imagelib.import_multi + '</option><option value=o>' +
 			uiStrings.imagelib.open + '</option></select>').appendTo(leftBlock).change(function() {
@@ -339,34 +349,38 @@ svgEditor.addExtension("imagelib", function() {
 					case 'o':
 						toggleMulti(false);
 						break;
-					
+
 					case 'm':
 						// Import multiple
 						toggleMulti(true);
+						break;
 				}
 			}).css({
 				'margin-top': 10
 			});
-			
+
 			cancel.prepend($.getSvgIcon('cancel', true));
 			back.prepend($.getSvgIcon('tool_imagelib', true));
-			
+
 			$.each(img_libs, function(i, opts) {
-				$('<li>').appendTo(lib_opts).text(opts.name).click(function() {
+				$('<li>')
+					.appendTo(lib_opts)
+					.text(opts.name)
+					.on("click touchend", function() {
 					frame.attr('src', opts.url).show();
 					header.text(opts.name);
 					lib_opts.hide();
 					back.show();
 				}).append('<span>' + opts.description + '</span>');
 			});
-			
+
 		} else {
 			$('#imgbrowse_holder').show();
 		}
 	}
-	
+
 	return {
-		svgicons: "extensions/ext-imagelib.xml",
+		svgicons: svgEditor.curConfig.extPath + "ext-imagelib.xml",
 		buttons: [{
 			id: "tool_imagelib",
 			type: "app_menu", // _flyout
@@ -377,7 +391,7 @@ svgEditor.addExtension("imagelib", function() {
 			}
 		}],
 		callback: function() {
-		
+
 			$('<style>').text('\
 				#imgbrowse_holder {\
 					position: absolute;\
@@ -455,6 +469,6 @@ svgEditor.addExtension("imagelib", function() {
 				}\
 			').appendTo('head');
 		}
-	}
+	};
 });
 

+ 147 - 140
main/inc/lib/svg-edit/extensions/ext-imagelib.xml

@@ -1,3 +1,5 @@
+/*globals svgEditor, svgCanvas, $*/
+/*jslint vars: true, eqeq: true, todo: true */
 /*
  * ext-markers.js
  *
@@ -22,7 +24,7 @@
  *    text marker font, size, and attributes are fixed
  *    an application specific attribute - se_type - is added to each marker element
  *        to store the type of marker
- *        
+ *
  * TODO:
  *    remove some of the restrictions above
  *    add option for keeping text aligned to horizontal
@@ -31,9 +33,9 @@
  */
 
 svgEditor.addExtension("Markers", function(S) {
-	var svgcontent = S.svgcontent,
-	addElem = S.addSvgElementFromJson,
-	selElems;
+	var // svgcontent = S.svgcontent,
+		addElem = S.addSvgElementFromJson,
+		selElems;
 
 	var mtypes = ['start','mid','end'];
 
@@ -69,8 +71,8 @@ svgEditor.addExtension("Markers", function(S) {
 		triangle:
 			{element:'path', attr:{d:'M10,80 L50,20 L80,80 Z'}},
 		mcircle:
-			{element:'circle', attr:{r:30, cx:50, cy:50}},			
-	}
+			{element:'circle', attr:{r:30, cx:50, cy:50}}			
+	};
 	
 	
 	var lang_list = {
@@ -95,7 +97,7 @@ svgEditor.addExtension("Markers", function(S) {
 			{id: "box_o", title: "Open Box" },
 			{id: "star_o", title: "Open Star" },
 			{id: "triangle_o", title: "Open Triangle" },
-			{id: "mcircle_o", title: "Open Circle" },
+			{id: "mcircle_o", title: "Open Circle" }
 		]
 	};
 
@@ -110,7 +112,7 @@ svgEditor.addExtension("Markers", function(S) {
 	// returns the marker element that is linked to the graphic element
 	function getLinked(elem, attr) {
 		var str = elem.getAttribute(attr);
-		if(!str) return null;
+		if(!str) {return null;}
 		var m = str.match(/\(\#(.*)\)/);
 		if(!m || m.length !== 2) {
 			return null;
@@ -118,7 +120,14 @@ svgEditor.addExtension("Markers", function(S) {
 		return S.getElem(m[1]);
 	}
 
-	//toggles context tool panel off/on
+	function setIcon(pos,id) {
+		if (id.substr(0,1) !== '\\') {id = '\\textmarker';}
+		var ci = '#'+id_prefix+pos+'_'+id.substr(1);
+		svgEditor.setIcon('#cur_' + pos +'_marker_list', $(ci).children());
+		$(ci).addClass('current').siblings().removeClass('current');
+	}
+
+	// toggles context tool panel off/on
 	//sets the controls with the selected element's settings
 	function showPanel(on) {
 		$('#marker_panel').toggle(on);
@@ -129,26 +138,26 @@ svgEditor.addExtension("Markers", function(S) {
 			var ci;
 
 			$.each(mtypes, function(i, pos) {
-				var m=getLinked(el,"marker-"+pos);
-				var txtbox = $('#'+pos+'_marker');
+				var m = getLinked(el, "marker-" + pos);
+				var txtbox = $('#' + pos + '_marker');
 				if (!m) {
-					val='\\nomarker';
-					ci=val;
-					txtbox.hide() // hide text box
+					val = '\\nomarker';
+					ci = val;
+					txtbox.hide(); // hide text box
 				} else {
-					if (!m.attributes.se_type) return; // not created by this extension
-					val='\\'+m.attributes.se_type.textContent;
-					ci=val;
-					if (val=='\\textmarker') {
-						val=m.lastChild.textContent;
+					if (!m.attributes.se_type) {return;} // not created by this extension
+					val = '\\' + m.attributes.se_type.textContent;
+					ci = val;
+					if (val === '\\textmarker') {
+						val = m.lastChild.textContent;
 						//txtbox.show(); // show text box
 					} else {
-						txtbox.hide() // hide text box
+						txtbox.hide(); // hide text box
 					}
 				}
 				txtbox.val(val);				
 				setIcon(pos,ci);
-			})
+			});
 		}
 	}	
 
@@ -159,9 +168,9 @@ svgEditor.addExtension("Markers", function(S) {
 		
 		var marker = S.getElem(id);
 
-		if (marker) return;
+		if (marker) {return;}
 
-		if (val=='' || val=='\\nomarker') return;
+		if (val == '' || val == '\\nomarker') {return;}
 
 		var el = selElems[0];		 
 		var color = el.getAttribute('stroke');
@@ -174,12 +183,13 @@ svgEditor.addExtension("Markers", function(S) {
 		var markerWidth = 5;
 		var markerHeight = 5;
 		var strokeWidth = 10;
-		if (val.substr(0,1)=='\\') se_type=val.substr(1);
-		else se_type='textmarker';
+		var se_type;
+		if (val.substr(0,1) === '\\') {se_type = val.substr(1);}
+		else {se_type = 'textmarker';}
 
-		if (!marker_types[se_type]) return; // an unknown type!
+		if (!marker_types[se_type]) {return;} // an unknown type!
 		
- 		// create a generic marker
+		// create a generic marker
 		marker = addElem({
 			"element": "marker",
 			"attr": {
@@ -191,21 +201,21 @@ svgEditor.addExtension("Markers", function(S) {
 		}
 		});
 
-		if (se_type!='textmarker') {
+		if (se_type != 'textmarker') {
 			var mel = addElem(marker_types[se_type]);
 			var fillcolor = color;
-			if (se_type.substr(-2)=='_o') fillcolor='none';
-			mel.setAttribute('fill',fillcolor);
-			mel.setAttribute('stroke',color);
-			mel.setAttribute('stroke-width',strokeWidth);
+			if (se_type.substr(-2) === '_o') {fillcolor = 'none';}
+			mel.setAttribute('fill', fillcolor);
+			mel.setAttribute('stroke', color);
+			mel.setAttribute('stroke-width', strokeWidth);
 			marker.appendChild(mel);
 		} else {
 			var text = addElem(marker_types[se_type]);
 			// have to add text to get bounding box
 			text.textContent = val;
-			var tb=text.getBBox();
+			var tb = text.getBBox();
 			//alert( tb.x + " " + tb.y + " " + tb.width + " " + tb.height);
-			var pad=1;
+			var pad = 1;
 			var bb = tb;
 			bb.x = 0;
 			bb.y = 0;
@@ -215,23 +225,23 @@ svgEditor.addExtension("Markers", function(S) {
 			text.setAttribute('x', pad);
 			text.setAttribute('y', bb.height - pad - tb.height/4); // kludge?
 			text.setAttribute('fill',color);
-			refX = bb.width/2+pad;
-			refY = bb.height/2+pad;
+			refX = bb.width/2 + pad;
+			refY = bb.height/2 + pad;
 			viewBox = bb.x + " " + bb.y + " " + bb.width + " " + bb.height;
-			markerWidth =bb.width/10;
+			markerWidth = bb.width/10;
 			markerHeight = bb.height/10;
 
 			var box = addElem({
 				"element": "rect",
 				"attr": {
-				"x": bb.x,
-				"y": bb.y,
-				"width": bb.width,
-				"height": bb.height,
-				"fill": txt_box_bg,
-				"stroke": txt_box_border,
-				"stroke-width": txt_box_stroke_width
-			}
+					"x": bb.x,
+					"y": bb.y,
+					"width": bb.width,
+					"height": bb.height,
+					"fill": txt_box_bg,
+					"stroke": txt_box_border,
+					"stroke-width": txt_box_stroke_width
+				}
 			});
 			marker.setAttribute("orient",0);
 			marker.appendChild(box);
@@ -248,59 +258,34 @@ svgEditor.addExtension("Markers", function(S) {
 		return marker;
 	}
 
-
-	function setMarker() {
-		var poslist={'start_marker':'start','mid_marker':'mid','end_marker':'end'};
-		var pos = poslist[this.id];
-		var marker_name = 'marker-'+pos;
-		var val = this.value;
-		var el = selElems[0];
-		var marker = getLinked(el, marker_name);
-		if (marker) $(marker).remove();
-		el.removeAttribute(marker_name);
-		if (val=='') val='\\nomarker';
-		if (val=='\\nomarker') {
-			setIcon(pos,val);
-			S.call("changed", selElems);
-			return;
-		}
-		// Set marker on element
-		var id = marker_prefix + pos + '_' + el.id;
-		addMarker(id, val);
-		svgCanvas.changeSelectedAttribute(marker_name, "url(#" + id + ")");
-		if (el.tagName == "line" && pos=='mid') el=convertline(el);
-		S.call("changed", selElems);
-		setIcon(pos,val);
-	}
-
 	function convertline(elem) {
 		// this routine came from the connectors extension
 		// it is needed because midpoint markers don't work with line elements
-		if (!(elem.tagName == "line")) return elem;
+		if (!(elem.tagName === 'line')) {return elem;}
 
 		// Convert to polyline to accept mid-arrow
 
-		var x1 = elem.getAttribute('x1')-0;
-		var x2 = elem.getAttribute('x2')-0;
-		var y1 = elem.getAttribute('y1')-0;
-		var y2 = elem.getAttribute('y2')-0;
+		var x1 = Number(elem.getAttribute('x1'));
+		var x2 = Number(elem.getAttribute('x2'));
+		var y1 = Number(elem.getAttribute('y1'));
+		var y2 = Number(elem.getAttribute('y2'));
 		var id = elem.id;
 
 		var mid_pt = (' '+((x1+x2)/2)+','+((y1+y2)/2) + ' ');
 		var pline = addElem({
 			"element": "polyline",
 			"attr": {
-			"points": (x1+','+y1+ mid_pt +x2+','+y2),
-			"stroke": elem.getAttribute('stroke'),
-			"stroke-width": elem.getAttribute('stroke-width'),
-			"fill": "none",
-			"opacity": elem.getAttribute('opacity') || 1
-		}
+				"points": (x1+','+y1+ mid_pt +x2+','+y2),
+				"stroke": elem.getAttribute('stroke'),
+				"stroke-width": elem.getAttribute('stroke-width'),
+				"fill": "none",
+				"opacity": elem.getAttribute('opacity') || 1
+			}
 		});
 		$.each(mtypes, function(i, pos) { // get any existing marker definitions
 			var nam = 'marker-'+pos;
 			var m = elem.getAttribute(nam);
-			if (m) pline.setAttribute(nam,elem.getAttribute(nam));
+			if (m) {pline.setAttribute(nam,elem.getAttribute(nam));}
 		});
 		
 		var batchCmd = new S.BatchCommand();
@@ -315,6 +300,30 @@ svgEditor.addExtension("Markers", function(S) {
 		return pline;
 	}
 
+	function setMarker() {
+		var poslist={'start_marker':'start','mid_marker':'mid','end_marker':'end'};
+		var pos = poslist[this.id];
+		var marker_name = 'marker-'+pos;
+		var val = this.value;
+		var el = selElems[0];
+		var marker = getLinked(el, marker_name);
+		if (marker) {$(marker).remove();}
+		el.removeAttribute(marker_name);
+		if (val == '') {val = '\\nomarker';}
+		if (val == '\\nomarker') {
+			setIcon(pos,val);
+			S.call("changed", selElems);
+			return;
+		}
+		// Set marker on element
+		var id = marker_prefix + pos + '_' + el.id;
+		addMarker(id, val);
+		svgCanvas.changeSelectedAttribute(marker_name, "url(#" + id + ")");
+		if (el.tagName === 'line' && pos == 'mid') {el = convertline(el);}
+		S.call("changed", selElems);
+		setIcon(pos,val);
+	}
+
 	// called when the main system modifies an object
 	// this routine changes the associated markers to be the same color
 	function colorChanged(elem) {
@@ -322,34 +331,34 @@ svgEditor.addExtension("Markers", function(S) {
 
 		$.each(mtypes, function(i, pos) {
 			var marker = getLinked(elem, 'marker-'+pos);
-			if (!marker) return;
-			if (!marker.attributes.se_type) return; //not created by this extension
+			if (!marker) {return;}
+			if (!marker.attributes.se_type) {return;} // not created by this extension
 			var ch = marker.lastElementChild;
-			if (!ch) return;
-			var curfill = ch.getAttribute("fill");
-			var curstroke = ch.getAttribute("stroke")
-			if (curfill && curfill!='none') ch.setAttribute("fill",color);
-			if (curstroke && curstroke!='none') ch.setAttribute("stroke",color);
+			if (!ch) {return;}
+			var curfill = ch.getAttribute('fill');
+			var curstroke = ch.getAttribute('stroke');
+			if (curfill && curfill != 'none') {ch.setAttribute('fill', color);}
+			if (curstroke && curstroke != 'none') {ch.setAttribute('stroke', color);}
 		});
 	}
 
 	// called when the main system creates or modifies an object
 	// primary purpose is create new markers for cloned objects
 	function updateReferences(el) {
-		$.each(mtypes, function (i,pos) {
+		$.each(mtypes, function (i, pos) {
 			var id = marker_prefix + pos + '_' + el.id;
 			var marker_name = 'marker-'+pos;
 			var marker = getLinked(el, marker_name);
-			if (!marker || !marker.attributes.se_type) return; //not created by this extension
+			if (!marker || !marker.attributes.se_type) {return;} // not created by this extension
 			var url = el.getAttribute(marker_name);
 			if (url) {
 				var len = el.id.length;
-				var linkid = url.substr(-len-1,len);
+				var linkid = url.substr(-len-1, len);
 				if (el.id != linkid) {
-					var val = $('#'+pos+'_marker').attr('value');
+					var val = $('#'+pos + '_marker').attr('value');
 					addMarker(id, val);
 					svgCanvas.changeSelectedAttribute(marker_name, "url(#" + id + ")");
-					if (el.tagName == "line" && pos=='mid') el=convertline(el);
+					if (el.tagName === 'line' && pos == 'mid') {el = convertline(el);}
 					S.call("changed", selElems);
 				}
 			}
@@ -357,21 +366,23 @@ svgEditor.addExtension("Markers", function(S) {
 	}
 
 	// simulate a change event a text box that stores the current element's marker type
-	function triggerTextEntry(pos,val) {
+	function triggerTextEntry(pos, val) {
 		$('#'+pos+'_marker').val(val);
 		$('#'+pos+'_marker').change();
-		var txtbox = $('#'+pos+'_marker');
-		//if (val.substr(0,1)=='\\') txtbox.hide();
-		//else txtbox.show();
+		// var txtbox = $('#'+pos+'_marker');
+		// if (val.substr(0,1)=='\\') {txtbox.hide();}
+		// else {txtbox.show();}
 	}
-	
-	function setIcon(pos,id) {
-		if (id.substr(0,1)!='\\') id='\\textmarker'
-		var ci = '#'+id_prefix+pos+'_'+id.substr(1);
-		svgEditor.setIcon('#cur_' + pos +'_marker_list', $(ci).children());
-		$(ci).addClass('current').siblings().removeClass('current');
+
+	function showTextPrompt(pos) {
+		var def = $('#'+pos+'_marker').val();
+		if (def.substr(0,1) === '\\') {def = '';}
+		$.prompt('Enter text for ' + pos + ' marker', def , function(txt) {
+			if (txt) {triggerTextEntry(pos, txt);}
+		});
 	}
-		
+
+	/*
 	function setMarkerSet(obj) {
 		var parts = this.id.split('_');
 		var set = parts[2];
@@ -393,32 +404,28 @@ svgEditor.addExtension("Markers", function(S) {
 			break;
 		}
 	}
-		
-	function showTextPrompt(pos) {
-		var def = $('#'+pos+'_marker').val();
-		if (def.substr(0,1)=='\\') def='';
-		$.prompt('Enter text for ' + pos + ' marker', def , function(txt) { if (txt) triggerTextEntry(pos,txt); });
-	}
-	
+	*/
 	// callback function for a toolbar button click
 	function setArrowFromButton(obj) {
 		
 		var parts = this.id.split('_');
 		var pos = parts[1];
 		var val = parts[2];
-		if (parts[3]) val+='_'+parts[3];
+		if (parts[3]) { val += '_' + parts[3];}
 		
-		if (val!='textmarker') {
-			triggerTextEntry(pos,'\\'+val);
+		if (val != 'textmarker') {
+			triggerTextEntry(pos, '\\'+val);
 		} else {
 			showTextPrompt(pos);
 		}
 	}
 	
-	function getTitle(lang,id) {
-		var list = lang_list[lang];
-		for (var i in list) {
-			if (list[i].id==id) return list[i].title;
+	function getTitle(lang, id) {
+		var i, list = lang_list[lang];
+		for (i in list) {
+			if (list.hasOwnProperty(i) && list[i].id == id) {
+				return list[i].title;
+			}
 		}
 		return id;
 	}
@@ -428,7 +435,7 @@ svgEditor.addExtension("Markers", function(S) {
 	// TODO: need to incorporate language specific titles
 	function buildButtonList() {
 		var buttons=[];
-		var i=0;
+		// var i = 0;
 /*
 		buttons.push({
 			id:id_prefix + 'markers_off',
@@ -452,30 +459,30 @@ svgEditor.addExtension("Markers", function(S) {
 			panel: 'marker_panel'
 		});
 */
-		$.each(mtypes,function(k,pos) {
+		$.each(mtypes, function (k, pos) {
 			var listname = pos + "_marker_list";
 			var def = true;
-		$.each(marker_types,function(id,v) {
-			var title = getTitle('en',id);
-			buttons.push({
-					id:id_prefix + pos + "_" + id,
-					svgicon:id,
-					title:title,
-					type:'context',
-					events: { 'click': setArrowFromButton },
-					panel:'marker_panel',
+			$.each(marker_types, function (id, v) {
+				var title = getTitle('en', id);
+				buttons.push({
+					id: id_prefix + pos + '_' + id,
+					svgicon: id,
+					title: title,
+					type: 'context',
+					events: {'click': setArrowFromButton},
+					panel: 'marker_panel',
 					list: listname,
 					isDefault: def
+				});
+				def = false;
 			});
-			def = false;
-		});
 		});
 		return buttons;
 	}
 
 	return {
 		name: "Markers",
-		svgicons: "extensions/markers-icons.xml",
+		svgicons: svgEditor.curConfig.extPath + "markers-icons.xml",
 		buttons: buildButtonList(),
 		context_tools: [
 		   {
@@ -489,7 +496,7 @@ svgEditor.addExtension("Markers", function(S) {
 		},{
 			type: "button-select",
 			panel: "marker_panel",
-			title: getTitle('en','start_marker_list'),
+			title: getTitle('en', 'start_marker_list'),
 			id: "start_marker_list",
 			colnum: 3,
 			events: { change: setArrowFromButton }
@@ -505,7 +512,7 @@ svgEditor.addExtension("Markers", function(S) {
 		},{
 			type: "button-select",
 			panel: "marker_panel",
-			title: getTitle('en','mid_marker_list'),
+			title: getTitle('en', 'mid_marker_list'),
 			id: "mid_marker_list",
 			colnum: 3,
 			events: { change: setArrowFromButton }
@@ -520,7 +527,7 @@ svgEditor.addExtension("Markers", function(S) {
 		},{
 			type: "button-select",
 			panel: "marker_panel",
-			title: getTitle('en','end_marker_list'),
+			title: getTitle('en', 'end_marker_list'),
 			id: "end_marker_list",
 			colnum: 3,
 			events: { change: setArrowFromButton }
@@ -543,8 +550,8 @@ svgEditor.addExtension("Markers", function(S) {
 
 		while(i--) {
 			var elem = selElems[i];
-			if(elem && $.inArray(elem.tagName, marker_elems) != -1) {
-				if(opts.selectedElement && !opts.multiselected) {
+			if(elem && $.inArray(elem.tagName, marker_elems) !== -1) {
+				if (opts.selectedElement && !opts.multiselected) {
 					showPanel(true);
 				} else {
 					showPanel(false);
@@ -558,7 +565,7 @@ svgEditor.addExtension("Markers", function(S) {
 	elementChanged: function(opts) {		
 		//console.log('elementChanged',opts);
 		var elem = opts.elems[0];
-		if(elem && (
+		if (elem && (
 				elem.getAttribute("marker-start") ||
 				elem.getAttribute("marker-mid") ||
 				elem.getAttribute("marker-end")
@@ -566,7 +573,7 @@ svgEditor.addExtension("Markers", function(S) {
 			colorChanged(elem);
 			updateReferences(elem);
 		}
-		changing_flag = false;
+		// changing_flag = false; // Not apparently in use
 	}
 	};
 });

+ 273 - 0
main/inc/lib/svg-edit/extensions/ext-mathjax.js

@@ -0,0 +1,273 @@
+/*globals MathJax, svgEditor, svgCanvas, $*/
+/*jslint es5: true, todo: true, vars: true*/
+/*
+ * ext-mathjax.js
+ *
+ * Licensed under the Apache License
+ *
+ * Copyright(c) 2013 Jo Segaert
+ *
+ */
+
+svgEditor.addExtension("mathjax", function() {'use strict';
+  // Configuration of the MathJax extention.
+
+  // This will be added to the head tag before MathJax is loaded.
+  var /*mathjaxConfiguration = '<script type="text/x-mathjax-config">\
+        MathJax.Hub.Config({\
+          extensions: ["tex2jax.js"],\
+			    jax: ["input/TeX","output/SVG"],\
+          showProcessingMessages: true,\
+          showMathMenu: false,\
+          showMathMenuMSIE: false,\
+          errorSettings: {\
+            message: ["[Math Processing Error]"],\
+            style: {color: "#CC0000", "font-style":"italic"}\
+          },\
+          elements: [],\
+            tex2jax: {\
+            ignoreClass: "tex2jax_ignore2", processClass: "tex2jax_process2",\
+          },\
+          TeX: {\
+            extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]\
+          },\
+          "SVG": {\
+          }\
+      });\
+      </script>',*/
+    // mathjaxSrc = 'http://cdn.mathjax.org/mathjax/latest/MathJax.js',
+    mathjaxSrcSecure = 'https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG.js',
+    math,
+    locationX,
+    locationY,
+    mathjaxLoaded = false,
+    uiStrings = svgEditor.uiStrings;
+
+  // TODO: Implement language support. Move these uiStrings to the locale files and the code to the langReady callback.
+  $.extend(uiStrings, {
+    mathjax: {
+      embed_svg: 'Save as mathematics',
+      embed_mathml: 'Save as figure',
+      svg_save_warning: 'The math will be transformed into a figure is manipulatable like everything else. You will not be able to manipulate the TeX-code anymore. ',
+      mathml_save_warning: 'Advised. The math will be saved as a figure.',
+      title: 'Mathematics code editor'
+    }
+  });
+
+
+  function saveMath() {
+    var code = $('#mathjax_code_textarea').val();
+    // displaystyle to force MathJax NOT to use the inline style. Because it is
+    // less fancy!
+    MathJax.Hub.queue.Push(['Text', math, '\\displaystyle{' + code + '}']);
+
+    /*
+     * The MathJax library doesn't want to bloat your webpage so it creates 
+     * every symbol (glymph) you need only once. These are saved in a <svg> on  
+     * the top of your html document, just under the body tag. Each glymph has
+     * its unique id and is saved as a <path> in the <defs> tag of the <svg>
+     * 
+     * Then when the symbols are needed in the rest of your html document they
+     * are refferd to by a <use> tag.
+     * Because of bug 1076 we can't just grab the defs tag on the top and add it 
+     * to your formula's <svg> and copy the lot. So we have to replace each 
+     * <use> tag by it's <path>.
+     */
+    MathJax.Hub.queue.Push(
+      function() {
+        var mathjaxMath = $('.MathJax_SVG');
+        var svg = $(mathjaxMath.html());
+        svg.find('use').each(function() {
+          var x, y, id, transform;
+
+          // TODO: find a less pragmatic and more elegant solution to this.
+          if ($(this).attr('href')) {
+            id = $(this).attr('href').slice(1); // Works in Chrome.
+          } else {
+            id = $(this).attr('xlink:href').slice(1); // Works in Firefox.
+          }
+
+          var glymph = $('#' + id).clone().removeAttr('id');
+          x = $(this).attr('x');
+          y = $(this).attr('y');
+          transform = $(this).attr('transform');
+          if (transform && ( x || y )) {
+            glymph.attr('transform', transform + ' translate(' + x + ',' + y + ')');
+          }
+          else if (transform) {
+            glymph.attr('transform', transform);
+          }
+          else if (x || y) {
+            glymph.attr('transform', 'translate(' + x + ',' + y + ')');
+          }
+          $(this).replaceWith(glymph);
+        });
+        // Remove the style tag because it interferes with SVG-Edit.
+        svg.removeAttr('style');
+        svg.attr('xmlns', 'http://www.w3.org/2000/svg');
+        svgCanvas.importSvgString($('<div>').append(svg.clone()).html(), true);
+        svgCanvas.ungroupSelectedElement();
+        // TODO: To undo the adding of the Formula you now have to undo twice.
+        // This should only be once!
+        svgCanvas.moveSelectedElements(locationX, locationY, true);
+      }
+    );
+  }
+
+  return {
+    name: "MatJax",
+    svgicons: svgEditor.curConfig.extPath + "mathjax-icons.xml",
+    buttons: [{
+        id: "tool_mathjax",
+        type: "mode",
+        title: "Add Mathematics",
+        events: {
+          'click': function() {
+            // Only load Mathjax when needed, we don't want to strain Svg-Edit any more. 
+            // From this point on it is very probable that it will be needed, so load it.
+            if (mathjaxLoaded === false) {
+
+              $('<div id="mathjax">\
+                <!-- Here is where MathJax creates the math -->\
+                  <div id="mathjax_creator" class="tex2jax_process" style="display:none">\
+                    $${}$$\
+                  </div>\
+                  <div id="mathjax_overlay"></div>\
+                  <div id="mathjax_container">\
+                    <div id="tool_mathjax_back" class="toolbar_button">\
+                      <button id="tool_mathjax_save">OK</button>\
+                      <button id="tool_mathjax_cancel">Cancel</button>\
+                    </div>\
+                    <fieldset>\
+                      <legend id="mathjax_legend">Mathematics Editor</legend>\
+                      <label>\
+                        <span id="mathjax_explication">Please type your mathematics in \
+                        <a href="http://en.wikipedia.org/wiki/Help:Displaying_a_formula" target="_blank">TeX</a> code.</span></label>\
+                      <textarea id="mathjax_code_textarea" spellcheck="false"></textarea>\
+                    </fieldset>\
+                  </div>\
+                </div>'
+                ).insertAfter('#svg_prefs').hide();
+
+              // Make the MathEditor draggable.
+              $('#mathjax_container').draggable({cancel: 'button,fieldset', containment: 'window'});
+
+              // Add functionality and picture to cancel button.
+              $('#tool_mathjax_cancel').prepend($.getSvgIcon('cancel', true))
+                .on("click touched", function() {
+                $('#mathjax').hide();
+              });
+
+              // Add functionality and picture to the save button.
+              $('#tool_mathjax_save').prepend($.getSvgIcon('ok', true))
+                .on("click touched", function() {
+                saveMath();
+                $('#mathjax').hide();
+              });
+
+              // MathJax preprocessing has to ignore most of the page.
+              $('body').addClass('tex2jax_ignore');
+
+              // Now get (and run) the MathJax Library.
+              $.getScript(mathjaxSrcSecure)
+                .done(function(script, textStatus) {
+
+                // When MathJax is loaded get the div where the math will be rendered.
+                MathJax.Hub.queue.Push(function() {
+                  math = MathJax.Hub.getAllJax('#mathjax_creator')[0];
+                  console.log(math);
+                  mathjaxLoaded = true;
+                  console.log('MathJax Loaded');
+                });
+
+              })
+                // If it fails.
+                .fail(function() {
+                console.log("Failed loadeing MathJax.");
+                $.alert("Failed loading MathJax. You will not be able to change the mathematics.");
+              });
+            }
+            // Set the mode.
+            svgCanvas.setMode("mathjax");
+          }
+        }
+      }],
+    
+    mouseDown: function() {
+      if (svgCanvas.getMode() === "mathjax") {
+        return {started: true};
+      }
+    },
+    mouseUp: function(opts) {
+      if (svgCanvas.getMode() === "mathjax") {
+        // Get the coordinates from your mouse.
+        var zoom = svgCanvas.getZoom();
+        // Get the actual coordinate by dividing by the zoom value
+        locationX = opts.mouse_x / zoom;
+        locationY = opts.mouse_y / zoom;
+
+        $('#mathjax').show();
+        return {started: false}; // Otherwise the last selected object dissapears.
+      }
+    },
+    callback: function() {
+      $('<style>').text('\
+				#mathjax fieldset{\
+					padding: 5px;\
+					margin: 5px;\
+					border: 1px solid #DDD;\
+				}\
+				#mathjax label{\
+					display: block;\
+					margin: .5em;\
+				}\
+				#mathjax legend {\
+					max-width:195px;\
+				}\
+				#mathjax_overlay {\
+					position: absolute;\
+					top: 0;\
+					left: 0;\
+					right: 0;\
+					bottom: 0;\
+					background-color: black;\
+					opacity: 0.6;\
+					z-index: 20000;\
+				}\
+				\
+				#mathjax_container {\
+					position: absolute;\
+					top: 50px;\
+					padding: 10px;\
+					background-color: #B0B0B0;\
+					border: 1px outset #777;\
+					opacity: 1.0;\
+					font-family: Verdana, Helvetica, sans-serif;\
+					font-size: .8em;\
+					z-index: 20001;\
+				}\
+				\
+				#tool_mathjax_back {\
+					margin-left: 1em;\
+					overflow: auto;\
+				}\
+				\
+				#mathjax_legend{\
+					font-weight: bold;\
+					font-size:1.1em;\
+				}\
+				\
+				#mathjax_code_textarea {\\n\
+          margin: 5px .7em;\
+					overflow: hidden;\
+          width: 416px;\
+					display: block;\
+					height: 100px;\
+				}\
+			').appendTo('head');
+
+      // Add the MathJax configuration.
+      //$(mathjaxConfiguration).appendTo('head');
+    }
+  };
+});

+ 142 - 0
main/inc/lib/svg-edit/extensions/ext-overview_window.js

@@ -0,0 +1,142 @@
+/*globals svgEditor, svgedit, $ */
+/*jslint es5: true, vars: true*/
+/*
+ * ext-overview_window.js
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2013 James Sacksteder
+ *
+ */
+
+var overviewWindowGlobals = {};
+svgEditor.addExtension("overview_window", function() {	'use strict';
+	//define and insert the base html element
+	var propsWindowHtml= "\
+		<div id=\"overview_window_content_pane\" style=\" width:100%; word-wrap:break-word;  display:inline-block; margin-top:20px;\">\
+			<div id=\"overview_window_content\" style=\"position:relative; left:12px; top:0px;\">\
+				<div style=\"background-color:#A0A0A0; display:inline-block; overflow:visible;\">\
+					<svg id=\"overviewMiniView\" width=\"150\" height=\"100\" x=\"0\" y=\"0\" viewBox=\"0 0 4800 3600\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\
+						<use x=\"0\" y=\"0\" xlink:href=\"#svgroot\"> <\/use>\
+					 </svg>\
+					 <div id=\"overview_window_view_box\" style=\"min-width:50px; min-height:50px; position:absolute; top:30px; left:30px; z-index:5; background-color:rgba(255,0,102,0.3);\">\
+					 <\/div>\
+				 <\/div>\
+			<\/div>\
+		<\/div>";
+	$("#sidepanels").append(propsWindowHtml);
+
+	//define dynamic animation of the view box.
+	var updateViewBox = function(){
+		var portHeight=parseFloat($("#workarea").css("height"));
+		var portWidth=parseFloat($("#workarea").css("width"));
+		var portX=$("#workarea").scrollLeft();
+		var portY=$("#workarea").scrollTop();
+		var windowWidth=parseFloat($("#svgcanvas").css("width"));
+		var windowHeight=parseFloat($("#svgcanvas").css("height"));
+		var overviewWidth=$("#overviewMiniView").attr("width");
+		var overviewHeight=$("#overviewMiniView").attr("height");
+		
+		var viewBoxX=portX/windowWidth*overviewWidth;
+		var viewBoxY=portY/windowHeight*overviewHeight;
+		var viewBoxWidth=portWidth/windowWidth*overviewWidth;
+		var viewBoxHeight=portHeight/windowHeight*overviewHeight;
+		
+		$("#overview_window_view_box").css("min-width",viewBoxWidth+"px");
+		$("#overview_window_view_box").css("min-height",viewBoxHeight+"px");
+		$("#overview_window_view_box").css("top",viewBoxY+"px");
+		$("#overview_window_view_box").css("left",viewBoxX+"px");
+	};
+	$("#workarea").scroll(function(){
+		if(!(overviewWindowGlobals.viewBoxDragging)){
+			updateViewBox();
+		}
+	});
+	$("#workarea").resize(updateViewBox);
+	updateViewBox();
+	
+	//comphensate for changes in zoom and canvas size
+	var updateViewDimensions= function(){
+		var viewWidth=$("#svgroot").attr("width");
+		var viewHeight=$("#svgroot").attr("height");
+		var viewX=640;
+		var viewY=480;
+		
+		if(svgedit.browser.isIE())
+		{
+			//This has only been tested with Firefox 10 and IE 9 (without chrome frame).
+			//I am not sure if if is Firefox or IE that is being non compliant here.
+			//Either way the one that is noncompliant may become more compliant later.
+			//TAG:HACK  
+			//TAG:VERSION_DEPENDENT
+			//TAG:BROWSER_SNIFFING
+			viewX=0;
+			viewY=0;
+		}
+		
+		var svgWidth_old=$("#overviewMiniView").attr("width");
+		var svgHeight_new=viewHeight/viewWidth*svgWidth_old;
+		$("#overviewMiniView").attr("viewBox",viewX+" "+viewY+" "+viewWidth+" "+viewHeight);
+		$("#overviewMiniView").attr("height",svgHeight_new);
+		updateViewBox();
+	};
+	updateViewDimensions();
+	
+	//set up the overview window as a controller for the view port.
+	overviewWindowGlobals.viewBoxDragging=false;
+	var updateViewPortFromViewBox = function(){
+	
+		var windowWidth =parseFloat($("#svgcanvas").css("width" ));
+		var windowHeight=parseFloat($("#svgcanvas").css("height"));
+		var overviewWidth =$("#overviewMiniView").attr("width" );
+		var overviewHeight=$("#overviewMiniView").attr("height");
+		var viewBoxX=parseFloat($("#overview_window_view_box").css("left"));
+		var viewBoxY=parseFloat($("#overview_window_view_box").css("top" ));
+		
+		var portX=viewBoxX/overviewWidth *windowWidth;
+		var portY=viewBoxY/overviewHeight*windowHeight;
+
+		$("#workarea").scrollLeft(portX);
+		$("#workarea").scrollTop(portY);
+	};
+	$( "#overview_window_view_box" ).draggable({  containment: "parent"
+		,drag: updateViewPortFromViewBox
+		,start:function(){overviewWindowGlobals.viewBoxDragging=true; }
+		,stop :function(){overviewWindowGlobals.viewBoxDragging=false;}
+	});  
+	$("#overviewMiniView").click(function(evt){
+		//Firefox doesn't support evt.offsetX and evt.offsetY
+		var mouseX=(evt.offsetX || evt.originalEvent.layerX);
+		var mouseY=(evt.offsetY || evt.originalEvent.layerY);
+		var overviewWidth =$("#overviewMiniView").attr("width" );
+		var overviewHeight=$("#overviewMiniView").attr("height");
+		var viewBoxWidth =parseFloat($("#overview_window_view_box").css("min-width" ));
+		var viewBoxHeight=parseFloat($("#overview_window_view_box").css("min-height"));
+ 
+		var viewBoxX=mouseX - 0.5 * viewBoxWidth;
+		var viewBoxY=mouseY- 0.5 * viewBoxHeight;
+		//deal with constraints
+		if(viewBoxX<0){
+			viewBoxX=0;
+		}
+		if(viewBoxY<0){
+			viewBoxY=0;
+		}
+		if(viewBoxX+viewBoxWidth>overviewWidth){
+			viewBoxX=overviewWidth-viewBoxWidth;
+		}
+		if(viewBoxY+viewBoxHeight>overviewHeight){
+			viewBoxY=overviewHeight-viewBoxHeight;
+		}
+		
+		$("#overview_window_view_box").css("top",viewBoxY+"px");
+		$("#overview_window_view_box").css("left",viewBoxX+"px");
+		updateViewPortFromViewBox();
+	});
+	
+	return{
+		name: "overview window",
+		canvasUpdated:updateViewDimensions,
+		workareaResized:updateViewBox
+	};
+});

+ 46 - 0
main/inc/lib/svg-edit/extensions/ext-panning.js

@@ -0,0 +1,46 @@
+/*globals svgEditor, svgCanvas*/
+/*jslint eqeq: true*/
+/*
+ * ext-panning.js
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2013 Luis Aguirre
+ *
+ */
+ 
+/* 
+	This is a very basic SVG-Edit extension to let tablet/mobile devices panning without problem
+*/
+
+svgEditor.addExtension('ext-panning', function() {'use strict';
+	return {
+		name: 'Extension Panning',
+		svgicons: svgEditor.curConfig.extPath + 'ext-panning.xml',
+		buttons: [{
+			id: 'ext-panning',
+			type: 'mode',
+			title: 'Panning',
+			events: {
+				click: function() {
+					svgCanvas.setMode('ext-panning');
+				}
+			}
+		}],
+		mouseDown: function() {
+			if (svgCanvas.getMode() == 'ext-panning') {
+				svgEditor.setPanning(true);
+				return {started: true};
+			}
+		},
+		mouseUp: function() {
+			if (svgCanvas.getMode() == 'ext-panning') {
+				svgEditor.setPanning(false);
+				return {
+					keep: false,
+					element: null
+				};
+			}
+		}
+	};
+});

+ 7 - 0
main/inc/lib/svg-edit/extensions/ext-panning.xml

@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+	<g id="ext-panning">
+	  <svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
+                  <path fill="#7f0000" stroke="#000000" stroke-width="10" d="m1.00037,150.34581l55.30305,-55.30267l0,27.65093l22.17356,0l0,-44.21833l44.21825,0l0,-22.17357l-27.65095,0l55.30267,-55.30292l55.3035,55.30292l-27.65175,0l0,22.17357l44.21835,0l0,44.21833l22.17357,0l0,-27.65093l55.30345,55.30267l-55.30345,55.3035l0,-27.65175l-22.17357,0l0,44.21834l-44.21835,0l0,22.17355l27.65175,0l-55.3035,55.30348l-55.30267,-55.30348l27.65095,0l0,-22.17355l-44.21825,0l0,-44.21834l-22.17356,0l0,27.65175l-55.30305,-55.3035z"></path>
+  	</svg>
+	</g>
+</svg>

+ 23 - 0
main/inc/lib/svg-edit/extensions/ext-php_savefile.js

@@ -0,0 +1,23 @@
+/*globals $, svgCanvas, svgEditor*/
+/*jslint regexp:true*/
+// TODO: Might add support for "exportImage" custom
+//   handler as in "ext-server_opensave.js" (and in savefile.php)
+
+svgEditor.addExtension("php_savefile", {
+	callback: function() {
+		'use strict';
+		function getFileNameFromTitle () {
+			var title = svgCanvas.getDocumentTitle();
+			return $.trim(title);
+		}
+		var save_svg_action = svgEditor.curConfig.extPath + 'savefile.php';
+		svgEditor.setCustomHandlers({
+			save: function(win, data) {
+				var svg = '<?xml version="1.0" encoding="UTF-8"?>\n' + data,
+					filename = getFileNameFromTitle();
+
+				$.post(save_svg_action, {output_svg: svg, filename: filename});
+			}
+		});
+	}
+});

+ 34 - 0
main/inc/lib/svg-edit/extensions/ext-php_savefile_chamilo.js

@@ -0,0 +1,34 @@
+/*globals $, svgCanvas, svgEditor*/
+/*jslint regexp:true*/
+// TODO: Might add support for "exportImage" custom
+//   handler as in "ext-server_opensave.js" (and in savefile.php)
+
+svgEditor.addExtension("php_savefile_chamilo", {
+	callback: function() {
+		'use strict';
+		function getFileNameFromTitle () {
+			var title = svgCanvas.getDocumentTitle();
+			return $.trim(title);
+		}
+		var save_svg_action = svgEditor.curConfig.extPath + 'savefile.php';
+		svgEditor.setCustomHandlers({
+			save: function(win, data) {
+				var svg = '<?xml version="1.0" encoding="UTF-8"?>\n' + data,
+					filename = getFileNameFromTitle();
+
+				$.post(
+                    save_svg_action,
+                    {output_svg: svg, filename: filename}
+                ).done(function(data) {
+                    var response = jQuery.parseJSON(data);
+                        console.log(response.message);
+                        alert(response.message);
+                        if (response.url != '') {
+                            window.top.location.href = response.url;
+                        }
+                    }
+                );
+			}
+		});
+	}
+});

+ 295 - 0
main/inc/lib/svg-edit/extensions/ext-polygon.js

@@ -0,0 +1,295 @@
+/*globals svgEditor, svgCanvas, svgedit, $*/
+/*jslint vars: true, eqeq: true, todo: true */
+/*
+ * ext-polygon.js
+ *
+ *
+ * Copyright(c) 2010 CloudCanvas, Inc.
+ * All rights reserved
+ *
+ */
+svgEditor.addExtension("polygon", function(S) {'use strict';
+
+    var // NS = svgedit.NS,
+		// svgcontent = S.svgcontent,
+		// addElem = S.addSvgElementFromJson,
+		selElems,
+		editingitex = false,
+		// svgdoc = S.svgroot.parentNode.ownerDocument,
+		// newFOG, newFOGParent, newDef, newImageName, newMaskID, modeChangeG,
+		// edg = 0,
+		// undoCommand = "Not image";
+		started, newFO;
+    
+    // var ccZoom;
+    // var wEl, hEl;
+    // var wOffset, hOffset;
+    // var ccRBG;
+	var ccRgbEl;
+    // var ccOpacity;
+    // var brushW, brushH;
+	var shape;
+    
+    // var ccDebug = document.getElementById('debugpanel');
+    
+    /* var properlySourceSizeTextArea = function(){
+     // TODO: remove magic numbers here and get values from CSS
+     var height = $('#svg_source_container').height() - 80;
+     $('#svg_source_textarea').css('height', height);
+     }; */
+    function showPanel(on){
+        var fc_rules = $('#fc_rules');
+        if (!fc_rules.length) {
+            fc_rules = $('<style id="fc_rules"><\/style>').appendTo('head');
+        }
+        fc_rules.text(!on ? "" : " #tool_topath { display: none !important; }");
+        $('#polygon_panel').toggle(on);
+    }
+    
+	/*
+    function toggleSourceButtons(on){
+        $('#tool_source_save, #tool_source_cancel').toggle(!on);
+        $('#polygon_save, #polygon_cancel').toggle(on);
+    }
+	*/
+    
+    function setAttr(attr, val){
+        svgCanvas.changeSelectedAttribute(attr, val);
+        S.call("changed", selElems);
+    }
+    
+    function cot(n){
+        return 1 / Math.tan(n);
+    }
+    
+    function sec(n){
+        return 1 / Math.cos(n);
+    }
+
+	/**
+	* Obtained from http://code.google.com/p/passenger-top/source/browse/instiki/public/svg-edit/editor/extensions/ext-itex.js?r=3
+	* This function sets the content of of the currently-selected foreignObject element,
+	*   based on the itex contained in string.
+	* @param {string} tex The itex text.
+	* @returns This function returns false if the set was unsuccessful, true otherwise.
+	*/
+	/*
+	function setItexString(tex) {
+		var mathns = 'http://www.w3.org/1998/Math/MathML',
+			xmlnsns = 'http://www.w3.org/2000/xmlns/',
+			ajaxEndpoint = '../../itex';
+		var elt = selElems[0];
+		try {
+			var math = svgdoc.createElementNS(mathns, 'math');
+			math.setAttributeNS(xmlnsns, 'xmlns', mathns);
+			math.setAttribute('display', 'inline');
+			var semantics = document.createElementNS(mathns, 'semantics');
+			var annotation = document.createElementNS(mathns, 'annotation');
+			annotation.setAttribute('encoding', 'application/x-tex');
+			annotation.textContent = tex;
+			var mrow = document.createElementNS(mathns, 'mrow');
+			semantics.appendChild(mrow);
+			semantics.appendChild(annotation);
+			math.appendChild(semantics);
+			// make an AJAX request to the server, to get the MathML
+			$.post(ajaxEndpoint, {'tex': tex, 'display': 'inline'}, function(data){
+				var children = data.documentElement.childNodes;
+				while (children.length > 0) {
+				     mrow.appendChild(svgdoc.adoptNode(children[0], true));
+				}
+				S.sanitizeSvg(math);
+				S.call("changed", [elt]);
+			});
+			elt.replaceChild(math, elt.firstChild);
+			S.call("changed", [elt]);
+			svgCanvas.clearSelection();
+		} catch(e) {
+			console.log(e);
+			return false;
+		}
+
+		return true;
+	}
+	*/
+    return {
+        name: "polygon",
+        svgicons: svgEditor.curConfig.extPath + "polygon-icons.svg",
+        buttons: [{
+            id: "tool_polygon",
+            type: "mode",
+            title: "Polygon Tool",
+            position: 11,
+            events: {
+                'click': function(){
+                    svgCanvas.setMode('polygon');
+					showPanel(true);
+                }
+            }
+        }],
+        
+        context_tools: [{
+            type: "input",
+            panel: "polygon_panel",
+            title: "Number of Sides",
+            id: "polySides",
+            label: "sides",
+            size: 3,
+            defval: 5,
+            events: {
+                change: function(){
+                    setAttr('sides', this.value);
+					
+                }
+            }
+        }],
+        
+        callback: function(){
+        
+            $('#polygon_panel').hide();
+            
+            var endChanges = function(){
+            };
+            
+            // TODO: Needs to be done after orig icon loads
+            setTimeout(function(){
+                // Create source save/cancel buttons
+                var save = $('#tool_source_save').clone().hide().attr('id', 'polygon_save').unbind().appendTo("#tool_source_back").click(function(){
+                
+                    if (!editingitex) {
+						return;
+					}
+					// Todo: Uncomment the setItexString() function above and handle ajaxEndpoint?
+                    if (!setItexString($('#svg_source_textarea').val())) {
+                        $.confirm("Errors found. Revert to original?", function(ok){
+                            if (!ok) {
+								return false;
+							}
+                            endChanges();
+                        });
+                    }
+                    else {
+                        endChanges();
+                    }
+                    // setSelectMode();	
+                });
+                
+                var cancel = $('#tool_source_cancel').clone().hide().attr('id', 'polygon_cancel').unbind().appendTo("#tool_source_back").click(function(){
+                    endChanges();
+                });
+                
+            }, 3000);
+        },
+        mouseDown: function(opts){
+            // var e = opts.event;
+            var rgb = svgCanvas.getColor("fill");
+            ccRgbEl = rgb.substring(1, rgb.length);
+            var sRgb = svgCanvas.getColor("stroke");
+            // ccSRgbEl = sRgb.substring(1, rgb.length);
+            var sWidth = svgCanvas.getStrokeWidth();
+            
+            if (svgCanvas.getMode() == "polygon") {
+                started = true;
+                
+                newFO = S.addSvgElementFromJson({
+                    "element": "polygon",
+                    "attr": {
+                        "cx": opts.start_x,
+                        "cy": opts.start_y,
+                        "id": S.getNextId(),
+                        "shape": "regularPoly",
+                        "sides": document.getElementById("polySides").value,
+                        "orient": "x",
+                        "edge": 0,
+                        "fill": rgb,
+                        "strokecolor": sRgb,
+                        "strokeWidth": sWidth
+                    }
+                });
+                
+                return {
+                    started: true
+                };
+            }
+        },
+        mouseMove: function(opts){
+            if (!started) {
+                return;
+			}
+            if (svgCanvas.getMode() == "polygon") {
+                // var e = opts.event;
+                var x = opts.mouse_x;
+                var y = opts.mouse_y;
+                var c = $(newFO).attr(["cx", "cy", "sides", "orient", "fill", "strokecolor", "strokeWidth"]);
+                var cx = c.cx, cy = c.cy, fill = c.fill, strokecolor = c.strokecolor, strokewidth = c.strokeWidth, sides = c.sides,
+					// orient = c.orient,
+					edg = (Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy))) / 1.5;
+                newFO.setAttributeNS(null, "edge", edg);
+                
+                var inradius = (edg / 2) * cot(Math.PI / sides);
+                var circumradius = inradius * sec(Math.PI / sides);
+                var points = '';
+				var s;
+                for (s = 0; sides >= s; s++) {
+                    var angle = 2.0 * Math.PI * s / sides;
+                    x = (circumradius * Math.cos(angle)) + cx;
+                    y = (circumradius * Math.sin(angle)) + cy;
+                    
+                    points += x + ',' + y + ' ';
+                }
+                
+                //var poly = newFO.createElementNS(NS.SVG, 'polygon');
+                newFO.setAttributeNS(null, 'points', points);
+                newFO.setAttributeNS(null, 'fill', fill);
+                newFO.setAttributeNS(null, 'stroke', strokecolor);
+                newFO.setAttributeNS(null, 'stroke-width', strokewidth);
+				// newFO.setAttributeNS(null, 'transform', "rotate(-90)");
+                shape = newFO.getAttributeNS(null, 'shape');
+                //newFO.appendChild(poly);
+                //DrawPoly(cx, cy, sides, edg, orient);
+                return {
+                    started: true
+                };
+            }
+            
+        },
+        
+        mouseUp: function(opts){
+            if (svgCanvas.getMode() == "polygon") {
+                var attrs = $(newFO).attr("edge");
+                var keep = (attrs.edge != 0);
+               // svgCanvas.addToSelection([newFO], true);
+                return {
+                    keep: keep,
+                    element: newFO
+                };
+            }
+            
+        },
+        selectedChanged: function(opts){
+            // Use this to update the current selected elements
+            selElems = opts.elems;
+            
+            var i = selElems.length;
+            
+            while (i--) {
+                var elem = selElems[i];
+                if (elem && elem.getAttributeNS(null, 'shape') === 'regularPoly') {
+                    if (opts.selectedElement && !opts.multiselected) {
+                        $('#polySides').val(elem.getAttribute("sides"));
+                        
+                        showPanel(true);
+                    }
+                    else {
+                        showPanel(false);
+                    }
+                }
+                else {
+                    showPanel(false);
+                }
+            }
+        },
+        elementChanged: function(opts){
+            // var elem = opts.elems[0];
+        }
+    };
+});

+ 23 - 19
main/inc/lib/svg-edit/extensions/ext-server_moinsave.js

@@ -1,7 +1,9 @@
+/*globals svgEditor, svgedit, svgCanvas, canvg, $, top*/
+/*jslint vars: true*/
 /*
  * ext-server_moinsave.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *              2011 MoinMoin:ReimarBauer
@@ -11,7 +13,7 @@
  */
 
 svgEditor.addExtension("server_opensave", {
-	callback: function() {
+	callback: function() {'use strict';
 
 		var save_svg_action = '/+modify';
 		
@@ -31,25 +33,27 @@ svgEditor.addExtension("server_opensave", {
 				c.width = svgCanvas.contentW;
 				c.height = svgCanvas.contentH;
 				$.getScript('canvg/canvg.js', function() {
-				canvg(c, svg, {renderCallback: function() {
-					var datauri = c.toDataURL('image/png');
-					var uiStrings = svgEditor.uiStrings;
-					var png_data = svgedit.utilities.encode64(datauri);
-					var form = $('<form>').attr({
-					method: 'post',
-					action: save_svg_action + '/' + name,
-					target: 'output_frame'
-				})	.append('<input type="hidden" name="png_data" value="' + png_data + '">')
-					.append('<input type="hidden" name="filepath" value="' + svg_data + '">')
-					.append('<input type="hidden" name="filename" value="' + 'drawing.svg">')
-					.append('<input type="hidden" name="contenttype" value="application/x-svgdraw">')
-					.appendTo('body')
-					.submit().remove();
-					}})});
+					canvg(c, svg, {renderCallback: function() {
+						var datauri = c.toDataURL('image/png');
+						// var uiStrings = svgEditor.uiStrings;
+						var png_data = svgedit.utilities.encode64(datauri);
+						var form = $('<form>').attr({
+						method: 'post',
+						action: save_svg_action + '/' + name,
+						target: 'output_frame'
+					}).append('<input type="hidden" name="png_data" value="' + png_data + '">')
+						.append('<input type="hidden" name="filepath" value="' + svg_data + '">')
+						.append('<input type="hidden" name="filename" value="' + 'drawing.svg">')
+						.append('<input type="hidden" name="contenttype" value="application/x-svgdraw">')
+						.appendTo('body')
+						.submit().remove();
+					}});
+				});
 				alert("Saved! Return to Item View!");
 				top.window.location = '/'+name;
-			},
+			}
 		});
 	
 	}
-});
+});
+

+ 80 - 51
main/inc/lib/svg-edit/extensions/ext-server_opensave.js

@@ -1,7 +1,9 @@
+/*globals svgEditor, svgedit, svgCanvas, canvg, $*/
+/*jslint eqeq: true*/
 /*
  * ext-server_opensave.js
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *
@@ -9,48 +11,72 @@
 
 svgEditor.addExtension("server_opensave", {
 	callback: function() {
-
-		var save_svg_action = 'extensions/filesave.php';
-		var save_png_action = 'extensions/filesave.php';
-	
-		// Create upload target (hidden iframe)
-		var target = $('<iframe name="output_frame" src="#"/>').hide().appendTo('body');
+		'use strict';
+		function getFileNameFromTitle () {
+			var title = svgCanvas.getDocumentTitle();
+			// We convert (to underscore) only those disallowed Win7 file name characters
+			return $.trim(title).replace(/[\/\\:*?"<>|]/g, '_');
+		}
+		function xhtmlEscape(str) {
+			return str.replace(/&(?!amp;)/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;'); // < is actually disallowed above anyways
+		}
+		function clientDownloadSupport (filename, suffix, uri) {
+			var a,
+				support = $('<a>')[0].download === '';
+			if (support) {
+				a = $('<a>hidden</a>').attr({download: (filename || 'image') + suffix, href: uri}).css('display', 'none').appendTo('body');
+				a[0].click();
+				return true;
+			}
+		}
+		var open_svg_action, import_svg_action, import_img_action,
+			open_svg_form, import_svg_form, import_img_form,
+			save_svg_action = svgEditor.curConfig.extPath + 'filesave.php',
+			save_img_action = svgEditor.curConfig.extPath + 'filesave.php',
+			// Create upload target (hidden iframe)
+			cancelled = false;
 	
+		$('<iframe name="output_frame" src="#"/>').hide().appendTo('body');
 		svgEditor.setCustomHandlers({
 			save: function(win, data) {
-				var svg = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + data; // Chamilo add encoding="UTF-8"
+				var svg = '<?xml version="1.0" encoding="UTF-8"?>\n' + data, // Firefox doesn't seem to know it is UTF-8 (no matter whether we use or skip the clientDownload code) despite the Content-Disposition header containing UTF-8, but adding the encoding works
+					filename = getFileNameFromTitle();
+
+				if (clientDownloadSupport(filename, '.svg', 'data:image/svg+xml;charset=UTF-8;base64,' + svgedit.utilities.encode64(svg))) {
+					return;
+				}
 
-				var title = svgCanvas.getDocumentTitle();
-				//var filename = title.replace(/[^a-z0-9\.\_\-]+/gi, '_');//Chamilo replace by below  
-				var filename =title;//Chamilo TODO:check if the filter through filesave.php is enough				
-				var form = $('<form>').attr({
+				$('<form>').attr({
 					method: 'post',
 					action: save_svg_action,
 					target: 'output_frame'
-				})	.append('<input type="hidden" name="output_svg" value="' + encodeURI(svg) + '">')
-					.append('<input type="hidden" name="filename" value="' + filename + '">')
+				}).append('<input type="hidden" name="output_svg" value="' + xhtmlEscape(svg) + '">')
+					.append('<input type="hidden" name="filename" value="' + xhtmlEscape(filename) + '">')
 					.appendTo('body')
 					.submit().remove();
 			},
-			pngsave: function(win, data) {
-				var issues = data.issues;
+			exportImage: function(win, data) {
+				var c,
+					issues = data.issues,
+					mimeType = data.mimeType,
+					quality = data.quality;
 				
 				if(!$('#export_canvas').length) {
 					$('<canvas>', {id: 'export_canvas'}).hide().appendTo('body');
 				}
-				var c = $('#export_canvas')[0];
+				c = $('#export_canvas')[0];
 				
 				c.width = svgCanvas.contentW;
 				c.height = svgCanvas.contentH;
 				canvg(c, data.svg, {renderCallback: function() {
-					var datauri = c.toDataURL('image/png');
+					var pre, filename, suffix,
+						datauri = quality ? c.toDataURL(mimeType, quality) : c.toDataURL(mimeType),
+						// uiStrings = svgEditor.uiStrings,
+						note = '';
 					
-					var uiStrings = svgEditor.uiStrings;
-					var note = '';
-					
-					// Check if there's issues
-					if(issues.length) {
-						var pre = "\n \u2022 ";
+					// Check if there are issues
+					if (issues.length) {
+						pre = "\n \u2022 ";
 						note += ("\n\n" + pre + issues.join(pre));
 					} 
 					
@@ -58,16 +84,20 @@ svgEditor.addExtension("server_opensave", {
 						alert(note);
 					}
 					
-					var title = svgCanvas.getDocumentTitle();
-					//var filename = title.replace(/[^a-z0-9\.\_\-]+/gi, '_');//Chamilo replace by below 
-					var filename =title;//Chamilo TODO:check if the filter through filesave.php is enough
+					filename = getFileNameFromTitle();
+					suffix = '.' + data.type.toLowerCase();
 					
-					var form = $('<form>').attr({
+					if (clientDownloadSupport(filename, suffix, datauri)) {
+						return;
+					}
+
+					$('<form>').attr({
 						method: 'post',
-						action: save_png_action,
+						action: save_img_action,
 						target: 'output_frame'
-					})	.append('<input type="hidden" name="output_png" value="' + datauri + '">')
-						.append('<input type="hidden" name="filename" value="' + filename + '">')
+					}).append('<input type="hidden" name="output_img" value="' + datauri + '">')
+						.append('<input type="hidden" name="mime" value="' + mimeType + '">')
+						.append('<input type="hidden" name="filename" value="' + xhtmlEscape(filename) + '">')
 						.appendTo('body')
 						.submit().remove();
 				}});
@@ -75,31 +105,30 @@ svgEditor.addExtension("server_opensave", {
 				
 			}
 		});
-	
+
 		// Do nothing if client support is found
-		if(window.FileReader) return;
+		if (window.FileReader) {return;}
 		
-		var cancelled = false;
-	
 		// Change these to appropriate script file
-		var open_svg_action = 'extensions/fileopen.php?type=load_svg';
-		var import_svg_action = 'extensions/fileopen.php?type=import_svg';
-		var import_img_action = 'extensions/fileopen.php?type=import_img';
+		open_svg_action = svgEditor.curConfig.extPath + 'fileopen.php?type=load_svg';
+		import_svg_action = svgEditor.curConfig.extPath + 'fileopen.php?type=import_svg';
+		import_img_action = svgEditor.curConfig.extPath + 'fileopen.php?type=import_img';
 		
 		// Set up function for PHP uploader to use
 		svgEditor.processFile = function(str64, type) {
-			if(cancelled) {
+			var xmlstr;
+			if (cancelled) {
 				cancelled = false;
 				return;
 			}
 		
 			$('#dialog_box').hide();
-		
-			if(type != 'import_img') {
-				var xmlstr = svgCanvas.Utils.decode64(str64);
+
+			if (type !== 'import_img') {
+				xmlstr = svgedit.utilities.decode64(str64);
 			}
 			
-			switch ( type ) {
+			switch (type) {
 				case 'load_svg':
 					svgCanvas.clear();
 					svgCanvas.setSvgString(xmlstr);
@@ -113,10 +142,10 @@ svgEditor.addExtension("server_opensave", {
 					svgCanvas.setGoodImage(str64);
 					break;
 			}
-		}
+		};
 	
 		// Create upload form
-		var open_svg_form = $('<form>');
+		open_svg_form = $('<form>');
 		open_svg_form.attr({
 			enctype: 'multipart/form-data',
 			method: 'post',
@@ -125,12 +154,12 @@ svgEditor.addExtension("server_opensave", {
 		});
 		
 		// Create import form
-		var import_svg_form = open_svg_form.clone().attr('action', import_svg_action);
-		
+		import_svg_form = open_svg_form.clone().attr('action', import_svg_action);
+
 		// Create image form
-		var import_img_form = open_svg_form.clone().attr('action', import_img_action);
+		import_img_form = open_svg_form.clone().attr('action', import_img_action);
 		
-		// It appears necessory to rebuild this input every time a file is 
+		// It appears necessary to rebuild this input every time a file is 
 		// selected so the same file can be picked and the change event can fire.
 		function rebuildInput(form) {
 			form.empty();
@@ -138,7 +167,7 @@ svgEditor.addExtension("server_opensave", {
 			
 			
 			function submit() {
-				// This submits the form, which returns the file data using svgEditor.uploadSVG
+				// This submits the form, which returns the file data using svgEditor.processFile()
 				form.submit();
 				
 				rebuildInput(form);
@@ -161,7 +190,7 @@ svgEditor.addExtension("server_opensave", {
 				});
 			} else {
 				inp.change(function() {
-					// This submits the form, which returns the file data using svgEditor.uploadSVG
+					// This submits the form, which returns the file data using svgEditor.processFile()
 					submit();
 				});
 			}

+ 357 - 387
main/inc/lib/svg-edit/extensions/ext-shapes.js

@@ -1,387 +1,357 @@
-/*
- * ext-shapes.js
- *
- * Licensed under the Apache License, Version 2
- *
- * Copyright(c) 2010 Christian Tzurcanu
- * Copyright(c) 2010 Alexis Deveria
- *
- */
-
-svgEditor.addExtension("shapes", function() {
-	
-
-	var current_d, cur_shape_id;
-	var canv = svgEditor.canvas;
-	var cur_shape;
-	var start_x, start_y;
-	var svgroot = canv.getRootElem();
-	var lastBBox = {};
-	
-	// This populates the category list
-	var categories = {
-		basic: 'Basic',
-		object: 'Objects',
-		symbol: 'Symbols',
-		arrow: 'Arrows',
-		flowchart: 'Flowchart',
-		animal: 'Animals',
-		game: 'Cards & Chess',
-		dialog_balloon: 'Dialog balloons',
-		electronics: 'Electronics',
-		math: 'Mathematical',
-		music: 'Music',
-		misc: 'Miscellaneous',
-		raphael_1: 'raphaeljs.com set 1',
-		raphael_2: 'raphaeljs.com set 2'
-	};
-	
-	var library = {
-		'basic': {
-			data: {
-				'heart': 'm150,73c61,-175 300,0 0,225c-300,-225 -61,-400 0,-225z',
-				'frame': 'm0,0l300,0l0,300l-300,0zm35,-265l0,230l230,0l0,-230z',
-				'donut': 'm1,150l0,0c0,-82.29042 66.70958,-149 149,-149l0,0c39.51724,0 77.41599,15.69816 105.35889,43.64108c27.94293,27.94293 43.64111,65.84165 43.64111,105.35892l0,0c0,82.29041 -66.70958,149 -149,149l0,0c-82.29041,0 -149,-66.70959 -149,-149zm74.5,0l0,0c0,41.1452 33.35481,74.5 74.5,74.5c41.14522,0 74.5,-33.3548 74.5,-74.5c0,-41.1452 -33.3548,-74.5 -74.5,-74.5l0,0c-41.14519,0 -74.5,33.35481 -74.5,74.5z',
-				"triangle": "m1,280.375l149,-260.75l149,260.75z",
-				"right_triangle": "m1,299l0,-298l298,298z",
-				"diamond": "m1,150l149,-149l149,149l-149,149l-149,-149z",
-				"pentagon": "m1.00035,116.97758l148.99963,-108.4053l148.99998,108.4053l-56.91267,175.4042l-184.1741,0l-56.91284,-175.4042z",
-				"hexagon": "m1,149.99944l63.85715,-127.71428l170.28572,0l63.85713,127.71428l-63.85713,127.71428l-170.28572,0l-63.85715,-127.71428z",
-				"septagon1": "m0.99917,191.06511l29.51249,-127.7108l119.48833,-56.83673l119.48836,56.83673l29.51303,127.7108l-82.69087,102.41679l-132.62103,0l-82.69031,-102.41679z",
-				"heptagon": "m1,88.28171l87.28172,-87.28171l123.43653,0l87.28172,87.28171l0,123.43654l-87.28172,87.28172l-123.43653,0l-87.28172,-87.28172l0,-123.43654z",
-				"decagon": "m1,150.00093l28.45646,-88.40318l74.49956,-54.63682l92.08794,0l74.50002,54.63682l28.45599,88.40318l-28.45599,88.40318l-74.50002,54.63681l-92.08794,0l-74.49956,-54.63681l-28.45646,-88.40318z",
-				"dodecagon": "m1,110.07421l39.92579,-69.14842l69.14842,-39.92579l79.85159,0l69.14842,39.92579l39.92578,69.14842l0,79.85159l-39.92578,69.14842l-69.14842,39.92578l-79.85159,0l-69.14842,-39.92578l-39.92579,-69.14842l0,-79.85159z",
-				"star_points_5": "m1,116.58409l113.82668,0l35.17332,-108.13487l35.17334,108.13487l113.82666,0l-92.08755,66.83026l35.17514,108.13487l-92.08759,-66.83208l-92.08757,66.83208l35.17515,-108.13487l-92.08758,-66.83026z",
-				"trapezoid": "m1,299l55.875,-298l186.25001,0l55.87498,298z",
-				"arrow_up": "m1.49805,149.64304l148.50121,-148.00241l148.50121,148.00241l-74.25061,0l0,148.71457l-148.5012,0l0,-148.71457z",
-				"vertical_scrool": "m37.375,261.625l0,-242.9375l0,0c0,-10.32083 8.36669,-18.6875 18.6875,-18.6875l224.25,0c10.32083,0 18.6875,8.36667 18.6875,18.6875c0,10.32081 -8.36667,18.6875 -18.6875,18.6875l-18.6875,0l0,242.9375c0,10.32083 -8.36668,18.6875 -18.6875,18.6875l-224.25,0l0,0c-10.32083,0 -18.6875,-8.36667 -18.6875,-18.6875c0,-10.32083 8.36667,-18.6875 18.6875,-18.6875zm37.375,-261.625l0,0c10.32081,0 18.6875,8.36667 18.6875,18.6875c0,10.32081 -8.36669,18.6875 -18.6875,18.6875c-5.1604,0 -9.34375,-4.18335 -9.34375,-9.34375c0,-5.16041 4.18335,-9.34375 9.34375,-9.34375l18.6875,0m186.875,18.6875l-205.5625,0m-37.375,224.25l0,0c5.1604,0 9.34375,4.18335 9.34375,9.34375c0,5.1604 -4.18335,9.34375 -9.34375,9.34375l18.6875,0m-18.6875,18.6875l0,0c10.32081,0 18.6875,-8.36667 18.6875,-18.6875l0,-18.6875",
-				"smiley": "m68.49886,214.78838q81.06408,55.67332 161.93891,0m-144.36983,-109.9558c0,-8.60432 6.97517,-15.57949 15.57948,-15.57949c8.60431,0 15.57948,6.97517 15.57948,15.57949c0,8.60431 -6.97517,15.57947 -15.57948,15.57947c-8.60431,0 -15.57948,-6.97516 -15.57948,-15.57947m95.83109,0c0,-8.60432 6.97517,-15.57949 15.57948,-15.57949c8.60431,0 15.57947,6.97517 15.57947,15.57949c0,8.60431 -6.97516,15.57947 -15.57947,15.57947c-8.60429,0 -15.57948,-6.97516 -15.57948,-15.57947m-181.89903,44.73038l0,0c0,-82.60133 66.96162,-149.56296 149.56296,-149.56296c82.60135,0 149.56296,66.96162 149.56296,149.56296c0,82.60135 -66.96161,149.56296 -149.56296,149.56296c-82.60133,0 -149.56296,-66.96161 -149.56296,-149.56296zm0,0l0,0c0,-82.60133 66.96162,-149.56296 149.56296,-149.56296c82.60135,0 149.56296,66.96162 149.56296,149.56296c0,82.60135 -66.96161,149.56296 -149.56296,149.56296c-82.60133,0 -149.56296,-66.96161 -149.56296,-149.56296z",
-				"left_braket": "m174.24565,298.5c-13.39009,0 -24.24489,-1.80908 -24.24489,-4.04065l0,-140.4187c0,-2.23158 -10.85481,-4.04065 -24.2449,-4.04065l0,0c13.39009,0 24.2449,-1.80907 24.2449,-4.04065l0,-140.4187l0,0c0,-2.23159 10.8548,-4.04066 24.24489,-4.04066",
-				"uml_actor": "m40.5,100l219,0m-108.99991,94.00006l107,105m-107.00009,-106.00006l-100,106m99.5,-231l0,125m33.24219,-158.75781c0,18.35916 -14.88303,33.24219 -33.24219,33.24219c-18.35916,0 -33.2422,-14.88303 -33.2422,-33.24219c0.00002,-18.35915 14.88304,-33.24219 33.2422,-33.24219c18.35916,0 33.24219,14.88304 33.24219,33.24219z",
-				"dialog_balloon_1": "m0.99786,35.96579l0,0c0,-19.31077 15.28761,-34.96524 34.14583,-34.96524l15.52084,0l0,0l74.50001,0l139.68748,0c9.05606,0 17.74118,3.68382 24.14478,10.24108c6.40356,6.55726 10.00107,15.45081 10.00107,24.72416l0,87.41311l0,0l0,52.44785l0,0c0,19.31078 -15.2876,34.96524 -34.14584,34.96524l-139.68748,0l-97.32507,88.90848l22.82506,-88.90848l-15.52084,0c-18.85822,0 -34.14583,-15.65446 -34.14583,-34.96524l0,0l0,-52.44785l0,0z",
-				"cloud": "m182.05086,34.31005c-0.64743,0.02048 -1.27309,0.07504 -1.92319,0.13979c-10.40161,1.03605 -19.58215,7.63722 -24.24597,17.4734l-2.47269,7.44367c0.53346,-2.57959 1.35258,-5.08134 2.47269,-7.44367c-8.31731,-8.61741 -19.99149,-12.59487 -31.52664,-10.72866c-11.53516,1.8662 -21.55294,9.3505 -27.02773,20.19925c-15.45544,-9.51897 -34.72095,-8.94245 -49.62526,1.50272c-14.90431,10.44516 -22.84828,28.93916 -20.43393,47.59753l1.57977,7.58346c-0.71388,-2.48442 -1.24701,-5.01186 -1.57977,-7.58346l-0.2404,0.69894c-12.95573,1.4119 -23.58103,11.46413 -26.34088,24.91708c-2.75985,13.45294 2.9789,27.25658 14.21789,34.21291l17.54914,4.26352c-6.1277,0.50439 -12.24542,-0.9808 -17.54914,-4.26352c-8.66903,9.71078 -10.6639,24.08736 -4.94535,35.96027c5.71854,11.87289 17.93128,18.70935 30.53069,17.15887l7.65843,-2.02692c-2.46413,1.0314 -5.02329,1.70264 -7.65843,2.02692c7.15259,13.16728 19.01251,22.77237 32.93468,26.5945c13.92217,3.82214 28.70987,1.56322 41.03957,-6.25546c10.05858,15.86252 27.91113,24.19412 45.81322,21.38742c17.90208,-2.8067 32.66954,-16.26563 37.91438,-34.52742l1.82016,-10.20447c-0.27254,3.46677 -0.86394,6.87508 -1.82016,10.20447c12.31329,8.07489 27.80199,8.52994 40.52443,1.18819c12.72244,-7.34175 20.6609,-21.34155 20.77736,-36.58929l-4.56108,-22.7823l-17.96776,-15.41455c13.89359,8.70317 22.6528,21.96329 22.52884,38.19685c16.5202,0.17313 30.55292,-13.98268 36.84976,-30.22897c6.29684,-16.24631 3.91486,-34.76801 -6.2504,-48.68089c4.21637,-10.35873 3.96622,-22.14172 -0.68683,-32.29084c-4.65308,-10.14912 -13.23602,-17.69244 -23.55914,-20.65356c-2.31018,-13.45141 -11.83276,-24.27162 -24.41768,-27.81765c-12.58492,-3.54603 -25.98557,0.82654 -34.41142,11.25287l-5.11707,8.63186c1.30753,-3.12148 3.01521,-6.03101 5.11707,-8.63186c-5.93959,-8.19432 -15.2556,-12.8181 -24.96718,-12.51096z",
-				"cylinder": "m299.0007,83.77844c0,18.28676 -66.70958,33.11111 -149.00002,33.11111m149.00002,-33.11111l0,0c0,18.28676 -66.70958,33.11111 -149.00002,33.11111c-82.29041,0 -148.99997,-14.82432 -148.99997,-33.11111m0,0l0,0c0,-18.28674 66.70956,-33.1111 148.99997,-33.1111c82.29044,0 149.00002,14.82436 149.00002,33.1111l0,132.44449c0,18.28674 -66.70958,33.11105 -149.00002,33.11105c-82.29041,0 -148.99997,-14.82431 -148.99997,-33.11105z",
-				"arrow_u_turn": "m1.00059,299.00055l0,-167.62497l0,0c0,-72.00411 58.37087,-130.37499 130.375,-130.37499l0,0l0,0c34.57759,0 67.73898,13.7359 92.18906,38.18595c24.45006,24.45005 38.18593,57.61144 38.18593,92.18904l0,18.625l37.24997,0l-74.49995,74.50002l-74.50002,-74.50002l37.25,0l0,-18.625c0,-30.8589 -25.0161,-55.87498 -55.87498,-55.87498l0,0l0,0c-30.85892,0 -55.875,25.01608 -55.875,55.87498l0,167.62497z",
-				"arrow_left_up": "m0.99865,224.5l74.50004,-74.5l0,37.25l111.74991,0l0,-111.75l-37.25,0l74.5,-74.5l74.5,74.5l-37.25,0l0,186.25l-186.24989,0l0,37.25l-74.50005,-74.5z",
-				"maximize": "m1.00037,150.34581l55.30305,-55.30267l0,27.65093l22.17356,0l0,-44.21833l44.21825,0l0,-22.17357l-27.65095,0l55.30267,-55.30292l55.3035,55.30292l-27.65175,0l0,22.17357l44.21835,0l0,44.21833l22.17357,0l0,-27.65093l55.30345,55.30267l-55.30345,55.3035l0,-27.65175l-22.17357,0l0,44.21834l-44.21835,0l0,22.17355l27.65175,0l-55.3035,55.30348l-55.30267,-55.30348l27.65095,0l0,-22.17355l-44.21825,0l0,-44.21834l-22.17356,0l0,27.65175l-55.30305,-55.3035z",
-				"cross": "m0.99844,99.71339l98.71494,0l0,-98.71495l101.26279,0l0,98.71495l98.71495,0l0,101.2628l-98.71495,0l0,98.71494l-101.26279,0l0,-98.71494l-98.71494,0z",
-				"plaque": "m-0.00197,49.94376l0,0c27.5829,0 49.94327,-22.36036 49.94327,-49.94327l199.76709,0l0,0c0,27.5829 22.36037,49.94327 49.94325,49.94327l0,199.7671l0,0c-27.58289,0 -49.94325,22.36034 -49.94325,49.94325l-199.76709,0c0,-27.58292 -22.36037,-49.94325 -49.94327,-49.94325z",
-				"page": "m249.3298,298.99744l9.9335,-39.73413l39.73413,-9.93355l-49.66763,49.66768l-248.33237,0l0,-298.00001l298.00001,0l0,248.33234"
-
-			},
-			buttons: []
-		}
-	};
-	
-	var cur_lib = library.basic;
-	
-	var mode_id = 'shapelib';
-	
-	function loadIcons() {
-		$('#shape_buttons').empty();
-		
-		// Show lib ones
-		$('#shape_buttons').append(cur_lib.buttons);
-	}
-	
-	function loadLibrary(cat_id) {
-	
-		var lib = library[cat_id];
-		
-		if(!lib) {
-			$('#shape_buttons').html('Loading...');
-			$.getJSON('extensions/shapelib/' + cat_id + '.json', function(result, textStatus) {
-				cur_lib = library[cat_id] = {
-					data: result.data,
-					size: result.size,
-					fill: result.fill
-				}
-				makeButtons(cat_id, result);
-				loadIcons();
-			});
-			return;
-		}
-		
-		cur_lib = lib;
-		if(!lib.buttons.length)	makeButtons(cat_id, lib);
-		loadIcons();
-	}
-	
-	function makeButtons(cat, shapes) {
-		var size = cur_lib.size || 300;
-		var fill = cur_lib.fill || false;
-		var off = size * .05;
-		var vb = [-off, -off, size + off*2, size + off*2].join(' ');
-		var stroke = fill ? 0: (size/30);
-		
-		var shape_icon = new DOMParser().parseFromString(
-			'<svg xmlns="http://www.w3.org/2000/svg"><svg viewBox="' + vb + '"><path fill="'+(fill?'#333':'none')+'" stroke="#000000" stroke-width="' + stroke + '" /><\/svg><\/svg>',
-			'text/xml');
-
-		var width = 24;
-		var height = 24;
-		shape_icon.documentElement.setAttribute('width', width);
-		shape_icon.documentElement.setAttribute('height', height);
-		var svg_elem = $(document.importNode(shape_icon.documentElement,true));
-	
-		var data = shapes.data;
-		
-		cur_lib.buttons = [];
-	
-		for(var id in data) {
-			var path_d = data[id];
-			var icon = svg_elem.clone();
-			icon.find('path').attr('d', path_d);
-			
-			var icon_btn = icon.wrap('<div class="tool_button">').parent().attr({
-				id: mode_id + '_' + id,
-				title: id
-			});
-			
-			
-			// Store for later use
-			cur_lib.buttons.push(icon_btn[0]);
-		}
-		
-	}
-
-	
-	return {
-		svgicons: "extensions/ext-shapes.xml",
-		buttons: [{
-			id: "tool_shapelib",
-			type: "mode_flyout", // _flyout
-			position: 6,
-			title: "Shape library",
-			events: {
-				"click": function() {
-					canv.setMode(mode_id);
-				}
-			}
-		}],
-		callback: function() {
-		
-			$('<style>').text('\
-			#shape_buttons {\
-				overflow: auto;\
-				width: 180px;\
-				max-height: 300px;\
-				display: table-cell;\
-				vertical-align: middle;\
-			}\
-			\
-			#shape_cats {\
-				min-width: 110px;\
-				display: table-cell;\
-				vertical-align: middle;\
-				height: 300px;\
-			}\
-			#shape_cats > div {\
-				line-height: 1em;\
-				padding: .5em;\
-				border:1px solid #B0B0B0;\
-				background: #E8E8E8;\
-				margin-bottom: -1px;\
-			}\
-			#shape_cats div:hover {\
-				background: #FFFFCC;\
-			}\
-			#shape_cats div.current {\
-				font-weight: bold;\
-			}').appendTo('head');
-
-		
-			var btn_div = $('<div id="shape_buttons">');
-			$('#tools_shapelib > *').wrapAll(btn_div);
-			
-			var shower = $('#tools_shapelib_show');
-
-			
-			loadLibrary('basic');
-			
-			// Do mouseup on parent element rather than each button
-			$('#shape_buttons').mouseup(function(evt) {
-				var btn = $(evt.target).closest('div.tool_button');
-				
-				if(!btn.length) return;
-				
-				var copy = btn.children().clone();
-				shower.children(':not(.flyout_arrow_horiz)').remove();
-				shower
-					.append(copy)
-					.attr('data-curopt', '#' + btn[0].id) // This sets the current mode
-					.mouseup();
-				canv.setMode(mode_id);
-				
-				cur_shape_id = btn[0].id.substr((mode_id+'_').length);
-				current_d = cur_lib.data[cur_shape_id];
-				
-				$('.tools_flyout').fadeOut();
-
-			});
-
-// 			
-			var shape_cats = $('<div id="shape_cats">');
-			
-			var cat_str = '';
-			
-			$.each(categories, function(id, label) {
-				cat_str += '<div data-cat=' + id + '>' + label + '</div>';
-			});
-			
-			shape_cats.html(cat_str).children().bind('mouseup', function() {
-				var catlink = $(this);
-				catlink.siblings().removeClass('current');
-				catlink.addClass('current');
-				
-				loadLibrary(catlink.attr('data-cat'));
-				// Get stuff
-				
-				return false;
-			});
-			
-			shape_cats.children().eq(0).addClass('current');
-			
-			$('#tools_shapelib').append(shape_cats);
-
-			shower.mouseup(function() {
-				canv.setMode(current_d ? mode_id : 'select');
-			});
-
-			
-			$('#tool_shapelib').remove();
-			
-			var h = $('#tools_shapelib').height();
-			$('#tools_shapelib').css({
-				'margin-top': -(h/2 - 15),
-				'margin-left': 3
-			});
-
-	
-		},
-		mouseDown: function(opts) {
-			var mode = canv.getMode();
-			if(mode !== mode_id) return;
-			
-			var e = opts.event;
-			var x = start_x = opts.start_x;
-			var y = start_y = opts.start_y;
-			var cur_style = canv.getStyle();
-
-			cur_shape = canv.addSvgElementFromJson({
-				"element": "path",
-				"curStyles": true,
-				"attr": {
-					"d": current_d,
-					"id": canv.getNextId(),
-					"opacity": cur_style.opacity / 2,
-					"style": "pointer-events:none"
-				}
-			});
-			
-			// Make sure shape uses absolute values
-			if(/[a-z]/.test(current_d)) {
-				current_d = cur_lib.data[cur_shape_id] = canv.pathActions.convertPath(cur_shape);
-				cur_shape.setAttribute('d', current_d);
-				canv.pathActions.fixEnd(cur_shape);
-			}
-	
-			cur_shape.setAttribute('transform', "translate(" + x + "," + y + ") scale(0.005) translate(" + -x + "," + -y + ")");
-			
-// 			console.time('b');
-			canv.recalculateDimensions(cur_shape);
-			
-			var tlist = canv.getTransformList(cur_shape);
-			
-			lastBBox = cur_shape.getBBox();
-			
-			return {
-				started: true
-			}
-			// current_d
-		},
-		mouseMove: function(opts) {
-			var mode = canv.getMode();
-			if(mode !== mode_id) return;
-			
-			var zoom = canv.getZoom();
-			var evt = opts.event
-			
-			var x = opts.mouse_x/zoom;
-			var y = opts.mouse_y/zoom;
-			
-			var tlist = canv.getTransformList(cur_shape),
-				box = cur_shape.getBBox(), 
-				left = box.x, top = box.y, width = box.width,
-				height = box.height;
-			var dx = (x-start_x), dy = (y-start_y);
-
-			var newbox = {
-				'x': Math.min(start_x,x),
-				'y': Math.min(start_y,y),
-				'width': Math.abs(x-start_x),
-				'height': Math.abs(y-start_y)
-			};
-
-			var ts = null,
-				tx = 0, ty = 0,
-				sy = height ? (height+dy)/height : 1, 
-				sx = width ? (width+dx)/width : 1;
-
-			var sx = newbox.width / lastBBox.width;
-			var sy = newbox.height / lastBBox.height;
-			
-			sx = sx || 1;
-			sy = sy || 1;
-			
-			// Not perfect, but mostly works...
-			if(x < start_x) {
-				tx = lastBBox.width;
-			}
-			if(y < start_y) ty = lastBBox.height;
-			
-			// update the transform list with translate,scale,translate
-			var translateOrigin = svgroot.createSVGTransform(),
-				scale = svgroot.createSVGTransform(),
-				translateBack = svgroot.createSVGTransform();
-				
-			translateOrigin.setTranslate(-(left+tx), -(top+ty));
-			if(!evt.shiftKey) {
-				var max = Math.min(Math.abs(sx), Math.abs(sy));
-
-				sx = max * (sx < 0 ? -1 : 1);
-				sy = max * (sy < 0 ? -1 : 1);
-			}
-			scale.setScale(sx,sy);
-			
-			translateBack.setTranslate(left+tx, top+ty);
-			var N = tlist.numberOfItems;
-			tlist.appendItem(translateBack);
-			tlist.appendItem(scale);
-			tlist.appendItem(translateOrigin);
-
-			canv.recalculateDimensions(cur_shape);
-			
-			lastBBox = cur_shape.getBBox();
-		},
-		mouseUp: function(opts) {
-			var mode = canv.getMode();
-			if(mode !== mode_id) return;
-			
-			if(opts.mouse_x == start_x && opts.mouse_y == start_y) {
-				return {
-					keep: false,
-					element: cur_shape,
-					started: false
-				}
-			}
-			
-			return {
-				keep: true,
-				element: cur_shape,
-				started: false
-			}
-		}		
-	}
-});
-
+/*globals svgEditor, $, DOMParser*/
+/*jslint es5: true, vars: true, eqeq: true*/
+/*
+ * ext-shapes.js
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2010 Christian Tzurcanu
+ * Copyright(c) 2010 Alexis Deveria
+ *
+ */
+
+svgEditor.addExtension('shapes', function() {'use strict';
+	var current_d, cur_shape_id;
+	var canv = svgEditor.canvas;
+	var cur_shape;
+	var start_x, start_y;
+	var svgroot = canv.getRootElem();
+	var lastBBox = {};
+
+	// This populates the category list
+	var categories = {
+		basic: 'Basic',
+		object: 'Objects',
+		symbol: 'Symbols',
+		arrow: 'Arrows',
+		flowchart: 'Flowchart',
+		animal: 'Animals',
+		game: 'Cards & Chess',
+		dialog_balloon: 'Dialog balloons',
+		electronics: 'Electronics',
+		math: 'Mathematical',
+		music: 'Music',
+		misc: 'Miscellaneous',
+		raphael_1: 'raphaeljs.com set 1',
+		raphael_2: 'raphaeljs.com set 2'
+	};
+
+	var library = {
+		basic: {
+			data: {
+				'heart': 'm150,73c61,-175 300,0 0,225c-300,-225 -61,-400 0,-225z',
+				'frame': 'm0,0l300,0l0,300l-300,0zm35,-265l0,230l230,0l0,-230z',
+				'donut': 'm1,150l0,0c0,-82.29042 66.70958,-149 149,-149l0,0c39.51724,0 77.41599,15.69816 105.35889,43.64108c27.94293,27.94293 43.64111,65.84165 43.64111,105.35892l0,0c0,82.29041 -66.70958,149 -149,149l0,0c-82.29041,0 -149,-66.70959 -149,-149zm74.5,0l0,0c0,41.1452 33.35481,74.5 74.5,74.5c41.14522,0 74.5,-33.3548 74.5,-74.5c0,-41.1452 -33.3548,-74.5 -74.5,-74.5l0,0c-41.14519,0 -74.5,33.35481 -74.5,74.5z',
+				'triangle': 'm1,280.375l149,-260.75l149,260.75z',
+				'right_triangle': 'm1,299l0,-298l298,298z',
+				'diamond': 'm1,150l149,-149l149,149l-149,149l-149,-149z',
+				'pentagon': 'm1.00035,116.97758l148.99963,-108.4053l148.99998,108.4053l-56.91267,175.4042l-184.1741,0l-56.91284,-175.4042z',
+				'hexagon': 'm1,149.99944l63.85715,-127.71428l170.28572,0l63.85713,127.71428l-63.85713,127.71428l-170.28572,0l-63.85715,-127.71428z',
+				'septagon1': 'm0.99917,191.06511l29.51249,-127.7108l119.48833,-56.83673l119.48836,56.83673l29.51303,127.7108l-82.69087,102.41679l-132.62103,0l-82.69031,-102.41679z',
+				'heptagon': 'm1,88.28171l87.28172,-87.28171l123.43653,0l87.28172,87.28171l0,123.43654l-87.28172,87.28172l-123.43653,0l-87.28172,-87.28172l0,-123.43654z',
+				'decagon': 'm1,150.00093l28.45646,-88.40318l74.49956,-54.63682l92.08794,0l74.50002,54.63682l28.45599,88.40318l-28.45599,88.40318l-74.50002,54.63681l-92.08794,0l-74.49956,-54.63681l-28.45646,-88.40318z',
+				'dodecagon': 'm1,110.07421l39.92579,-69.14842l69.14842,-39.92579l79.85159,0l69.14842,39.92579l39.92578,69.14842l0,79.85159l-39.92578,69.14842l-69.14842,39.92578l-79.85159,0l-69.14842,-39.92578l-39.92579,-69.14842l0,-79.85159z',
+				'star_points_5': 'm1,116.58409l113.82668,0l35.17332,-108.13487l35.17334,108.13487l113.82666,0l-92.08755,66.83026l35.17514,108.13487l-92.08759,-66.83208l-92.08757,66.83208l35.17515,-108.13487l-92.08758,-66.83026z',
+				'trapezoid': 'm1,299l55.875,-298l186.25001,0l55.87498,298z',
+				'arrow_up': 'm1.49805,149.64304l148.50121,-148.00241l148.50121,148.00241l-74.25061,0l0,148.71457l-148.5012,0l0,-148.71457z',
+				'vertical_scrool': 'm37.375,261.625l0,-242.9375l0,0c0,-10.32083 8.36669,-18.6875 18.6875,-18.6875l224.25,0c10.32083,0 18.6875,8.36667 18.6875,18.6875c0,10.32081 -8.36667,18.6875 -18.6875,18.6875l-18.6875,0l0,242.9375c0,10.32083 -8.36668,18.6875 -18.6875,18.6875l-224.25,0l0,0c-10.32083,0 -18.6875,-8.36667 -18.6875,-18.6875c0,-10.32083 8.36667,-18.6875 18.6875,-18.6875zm37.375,-261.625l0,0c10.32081,0 18.6875,8.36667 18.6875,18.6875c0,10.32081 -8.36669,18.6875 -18.6875,18.6875c-5.1604,0 -9.34375,-4.18335 -9.34375,-9.34375c0,-5.16041 4.18335,-9.34375 9.34375,-9.34375l18.6875,0m186.875,18.6875l-205.5625,0m-37.375,224.25l0,0c5.1604,0 9.34375,4.18335 9.34375,9.34375c0,5.1604 -4.18335,9.34375 -9.34375,9.34375l18.6875,0m-18.6875,18.6875l0,0c10.32081,0 18.6875,-8.36667 18.6875,-18.6875l0,-18.6875',
+				'smiley': 'm68.49886,214.78838q81.06408,55.67332 161.93891,0m-144.36983,-109.9558c0,-8.60432 6.97517,-15.57949 15.57948,-15.57949c8.60431,0 15.57948,6.97517 15.57948,15.57949c0,8.60431 -6.97517,15.57947 -15.57948,15.57947c-8.60431,0 -15.57948,-6.97516 -15.57948,-15.57947m95.83109,0c0,-8.60432 6.97517,-15.57949 15.57948,-15.57949c8.60431,0 15.57947,6.97517 15.57947,15.57949c0,8.60431 -6.97516,15.57947 -15.57947,15.57947c-8.60429,0 -15.57948,-6.97516 -15.57948,-15.57947m-181.89903,44.73038l0,0c0,-82.60133 66.96162,-149.56296 149.56296,-149.56296c82.60135,0 149.56296,66.96162 149.56296,149.56296c0,82.60135 -66.96161,149.56296 -149.56296,149.56296c-82.60133,0 -149.56296,-66.96161 -149.56296,-149.56296zm0,0l0,0c0,-82.60133 66.96162,-149.56296 149.56296,-149.56296c82.60135,0 149.56296,66.96162 149.56296,149.56296c0,82.60135 -66.96161,149.56296 -149.56296,149.56296c-82.60133,0 -149.56296,-66.96161 -149.56296,-149.56296z',
+				'left_braket': 'm174.24565,298.5c-13.39009,0 -24.24489,-1.80908 -24.24489,-4.04065l0,-140.4187c0,-2.23158 -10.85481,-4.04065 -24.2449,-4.04065l0,0c13.39009,0 24.2449,-1.80907 24.2449,-4.04065l0,-140.4187l0,0c0,-2.23159 10.8548,-4.04066 24.24489,-4.04066',
+				'uml_actor': 'm40.5,100l219,0m-108.99991,94.00006l107,105m-107.00009,-106.00006l-100,106m99.5,-231l0,125m33.24219,-158.75781c0,18.35916 -14.88303,33.24219 -33.24219,33.24219c-18.35916,0 -33.2422,-14.88303 -33.2422,-33.24219c0.00002,-18.35915 14.88304,-33.24219 33.2422,-33.24219c18.35916,0 33.24219,14.88304 33.24219,33.24219z',
+				'dialog_balloon_1': 'm0.99786,35.96579l0,0c0,-19.31077 15.28761,-34.96524 34.14583,-34.96524l15.52084,0l0,0l74.50001,0l139.68748,0c9.05606,0 17.74118,3.68382 24.14478,10.24108c6.40356,6.55726 10.00107,15.45081 10.00107,24.72416l0,87.41311l0,0l0,52.44785l0,0c0,19.31078 -15.2876,34.96524 -34.14584,34.96524l-139.68748,0l-97.32507,88.90848l22.82506,-88.90848l-15.52084,0c-18.85822,0 -34.14583,-15.65446 -34.14583,-34.96524l0,0l0,-52.44785l0,0z',
+				'cloud': 'm182.05086,34.31005c-0.64743,0.02048 -1.27309,0.07504 -1.92319,0.13979c-10.40161,1.03605 -19.58215,7.63722 -24.24597,17.4734l-2.47269,7.44367c0.53346,-2.57959 1.35258,-5.08134 2.47269,-7.44367c-8.31731,-8.61741 -19.99149,-12.59487 -31.52664,-10.72866c-11.53516,1.8662 -21.55294,9.3505 -27.02773,20.19925c-15.45544,-9.51897 -34.72095,-8.94245 -49.62526,1.50272c-14.90431,10.44516 -22.84828,28.93916 -20.43393,47.59753l1.57977,7.58346c-0.71388,-2.48442 -1.24701,-5.01186 -1.57977,-7.58346l-0.2404,0.69894c-12.95573,1.4119 -23.58103,11.46413 -26.34088,24.91708c-2.75985,13.45294 2.9789,27.25658 14.21789,34.21291l17.54914,4.26352c-6.1277,0.50439 -12.24542,-0.9808 -17.54914,-4.26352c-8.66903,9.71078 -10.6639,24.08736 -4.94535,35.96027c5.71854,11.87289 17.93128,18.70935 30.53069,17.15887l7.65843,-2.02692c-2.46413,1.0314 -5.02329,1.70264 -7.65843,2.02692c7.15259,13.16728 19.01251,22.77237 32.93468,26.5945c13.92217,3.82214 28.70987,1.56322 41.03957,-6.25546c10.05858,15.86252 27.91113,24.19412 45.81322,21.38742c17.90208,-2.8067 32.66954,-16.26563 37.91438,-34.52742l1.82016,-10.20447c-0.27254,3.46677 -0.86394,6.87508 -1.82016,10.20447c12.31329,8.07489 27.80199,8.52994 40.52443,1.18819c12.72244,-7.34175 20.6609,-21.34155 20.77736,-36.58929l-4.56108,-22.7823l-17.96776,-15.41455c13.89359,8.70317 22.6528,21.96329 22.52884,38.19685c16.5202,0.17313 30.55292,-13.98268 36.84976,-30.22897c6.29684,-16.24631 3.91486,-34.76801 -6.2504,-48.68089c4.21637,-10.35873 3.96622,-22.14172 -0.68683,-32.29084c-4.65308,-10.14912 -13.23602,-17.69244 -23.55914,-20.65356c-2.31018,-13.45141 -11.83276,-24.27162 -24.41768,-27.81765c-12.58492,-3.54603 -25.98557,0.82654 -34.41142,11.25287l-5.11707,8.63186c1.30753,-3.12148 3.01521,-6.03101 5.11707,-8.63186c-5.93959,-8.19432 -15.2556,-12.8181 -24.96718,-12.51096z',
+				'cylinder': 'm299.0007,83.77844c0,18.28676 -66.70958,33.11111 -149.00002,33.11111m149.00002,-33.11111l0,0c0,18.28676 -66.70958,33.11111 -149.00002,33.11111c-82.29041,0 -148.99997,-14.82432 -148.99997,-33.11111m0,0l0,0c0,-18.28674 66.70956,-33.1111 148.99997,-33.1111c82.29044,0 149.00002,14.82436 149.00002,33.1111l0,132.44449c0,18.28674 -66.70958,33.11105 -149.00002,33.11105c-82.29041,0 -148.99997,-14.82431 -148.99997,-33.11105z',
+				'arrow_u_turn': 'm1.00059,299.00055l0,-167.62497l0,0c0,-72.00411 58.37087,-130.37499 130.375,-130.37499l0,0l0,0c34.57759,0 67.73898,13.7359 92.18906,38.18595c24.45006,24.45005 38.18593,57.61144 38.18593,92.18904l0,18.625l37.24997,0l-74.49995,74.50002l-74.50002,-74.50002l37.25,0l0,-18.625c0,-30.8589 -25.0161,-55.87498 -55.87498,-55.87498l0,0l0,0c-30.85892,0 -55.875,25.01608 -55.875,55.87498l0,167.62497z',
+				'arrow_left_up': 'm0.99865,224.5l74.50004,-74.5l0,37.25l111.74991,0l0,-111.75l-37.25,0l74.5,-74.5l74.5,74.5l-37.25,0l0,186.25l-186.24989,0l0,37.25l-74.50005,-74.5z',
+				'maximize': 'm1.00037,150.34581l55.30305,-55.30267l0,27.65093l22.17356,0l0,-44.21833l44.21825,0l0,-22.17357l-27.65095,0l55.30267,-55.30292l55.3035,55.30292l-27.65175,0l0,22.17357l44.21835,0l0,44.21833l22.17357,0l0,-27.65093l55.30345,55.30267l-55.30345,55.3035l0,-27.65175l-22.17357,0l0,44.21834l-44.21835,0l0,22.17355l27.65175,0l-55.3035,55.30348l-55.30267,-55.30348l27.65095,0l0,-22.17355l-44.21825,0l0,-44.21834l-22.17356,0l0,27.65175l-55.30305,-55.3035z',
+				'cross': 'm0.99844,99.71339l98.71494,0l0,-98.71495l101.26279,0l0,98.71495l98.71495,0l0,101.2628l-98.71495,0l0,98.71494l-101.26279,0l0,-98.71494l-98.71494,0z',
+				'plaque': 'm-0.00197,49.94376l0,0c27.5829,0 49.94327,-22.36036 49.94327,-49.94327l199.76709,0l0,0c0,27.5829 22.36037,49.94327 49.94325,49.94327l0,199.7671l0,0c-27.58289,0 -49.94325,22.36034 -49.94325,49.94325l-199.76709,0c0,-27.58292 -22.36037,-49.94325 -49.94327,-49.94325z',
+				'page': 'm249.3298,298.99744l9.9335,-39.73413l39.73413,-9.93355l-49.66763,49.66768l-248.33237,0l0,-298.00001l298.00001,0l0,248.33234'
+
+			},
+			buttons: []
+		}
+	};
+
+	var cur_lib = library.basic;
+	var mode_id = 'shapelib';
+	var startClientPos = {};
+
+	function loadIcons() {
+		$('#shape_buttons').empty().append(cur_lib.buttons);
+	}
+
+	function makeButtons(cat, shapes) {
+		var size = cur_lib.size || 300;
+		var fill = cur_lib.fill || false;
+		var off = size * 0.05;
+		var vb = [-off, -off, size + off*2, size + off*2].join(' ');
+		var stroke = fill ? 0: (size/30);
+		var shape_icon = new DOMParser().parseFromString(
+			'<svg xmlns="http://www.w3.org/2000/svg"><svg viewBox="' + vb + '"><path fill="'+(fill?'#333':'none')+'" stroke="#000" stroke-width="' + stroke + '" /><\/svg><\/svg>',
+			'text/xml');
+
+		var width = 24;
+		var height = 24;
+		shape_icon.documentElement.setAttribute('width', width);
+		shape_icon.documentElement.setAttribute('height', height);
+		var svg_elem = $(document.importNode(shape_icon.documentElement,true));
+
+		var data = shapes.data;
+
+		cur_lib.buttons = [];
+		var id;
+		for (id in data) {
+			var path_d = data[id];
+			var icon = svg_elem.clone();
+			icon.find('path').attr('d', path_d);
+
+			var icon_btn = icon.wrap('<div class="tool_button">').parent().attr({
+				id: mode_id + '_' + id,
+				title: id
+			});
+			// Store for later use
+			cur_lib.buttons.push(icon_btn[0]);
+		}
+	}
+
+	function loadLibrary(cat_id) {
+
+		var lib = library[cat_id];
+
+		if (!lib) {
+			$('#shape_buttons').html('Loading...');
+			$.getJSON(svgEditor.curConfig.extPath + 'shapelib/' + cat_id + '.json', function(result) {
+				cur_lib = library[cat_id] = {
+					data: result.data,
+					size: result.size,
+					fill: result.fill
+				};
+				makeButtons(cat_id, result);
+				loadIcons();
+			});
+			return;
+		}
+		cur_lib = lib;
+		if (!lib.buttons.length) {makeButtons(cat_id, lib);}
+		loadIcons();
+	}
+
+	return {
+		svgicons: svgEditor.curConfig.extPath + 'ext-shapes.xml',
+		buttons: [{
+			id: 'tool_shapelib',
+			type: 'mode_flyout', // _flyout
+			position: 6,
+			title: 'Shape library',
+			events: {
+				click: function() {
+					canv.setMode(mode_id);
+				}
+			}
+		}],
+		callback: function() {
+			$('<style>').text('\
+			#shape_buttons {\
+				overflow: auto;\
+				width: 180px;\
+				max-height: 300px;\
+				display: table-cell;\
+				vertical-align: middle;\
+			}\
+			\
+			#shape_cats {\
+				min-width: 110px;\
+				display: table-cell;\
+				vertical-align: middle;\
+				height: 300px;\
+			}\
+			#shape_cats > div {\
+				line-height: 1em;\
+				padding: .5em;\
+				border:1px solid #B0B0B0;\
+				background: #E8E8E8;\
+				margin-bottom: -1px;\
+			}\
+			#shape_cats div:hover {\
+				background: #FFFFCC;\
+			}\
+			#shape_cats div.current {\
+				font-weight: bold;\
+			}').appendTo('head');
+
+			var btn_div = $('<div id="shape_buttons">');
+			$('#tools_shapelib > *').wrapAll(btn_div);
+
+			var shower = $('#tools_shapelib_show');
+
+			loadLibrary('basic');
+
+			// Do mouseup on parent element rather than each button
+			$('#shape_buttons').mouseup(function(evt) {
+				var btn = $(evt.target).closest('div.tool_button');
+
+				if (!btn.length) {return;}
+
+				var copy = btn.children().clone();
+				shower.children(':not(.flyout_arrow_horiz)').remove();
+				shower
+					.append(copy)
+					.attr('data-curopt', '#' + btn[0].id) // This sets the current mode
+					.mouseup();
+				canv.setMode(mode_id);
+
+				cur_shape_id = btn[0].id.substr((mode_id+'_').length);
+				current_d = cur_lib.data[cur_shape_id];
+
+				$('.tools_flyout').fadeOut();
+			});
+
+			var shape_cats = $('<div id="shape_cats">');
+			var cat_str = '';
+
+			$.each(categories, function(id, label) {
+				cat_str += '<div data-cat=' + id + '>' + label + '</div>';
+			});
+
+			shape_cats.html(cat_str).children().bind('mouseup', function() {
+				var catlink = $(this);
+				catlink.siblings().removeClass('current');
+				catlink.addClass('current');
+
+				loadLibrary(catlink.attr('data-cat'));
+				// Get stuff
+				return false;
+			});
+
+			shape_cats.children().eq(0).addClass('current');
+
+			$('#tools_shapelib').append(shape_cats);
+
+			shower.mouseup(function() {
+				canv.setMode(current_d ? mode_id : 'select');
+			});
+			$('#tool_shapelib').remove();
+
+			var h = $('#tools_shapelib').height();
+			$('#tools_shapelib').css({
+				'margin-top': -(h/2 - 15),
+				'margin-left': 3
+			});
+		},
+		mouseDown: function(opts) {
+			var mode = canv.getMode();
+			if (mode !== mode_id) {return;}
+
+			start_x = opts.start_x;
+			var x = start_x;
+			start_y = opts.start_y;
+			var y = start_y;
+			var cur_style = canv.getStyle();
+         
+			startClientPos.x = opts.event.clientX;
+			startClientPos.y = opts.event.clientY;
+
+			cur_shape = canv.addSvgElementFromJson({
+				'element': 'path',
+				'curStyles': true,
+				'attr': {
+					'd': current_d,
+					'id': canv.getNextId(),
+					'opacity': cur_style.opacity / 2,
+					'style': 'pointer-events:none'
+				}
+			});
+
+			// Make sure shape uses absolute values
+			if (/[a-z]/.test(current_d)) {
+				current_d = cur_lib.data[cur_shape_id] = canv.pathActions.convertPath(cur_shape);
+				cur_shape.setAttribute('d', current_d);
+				canv.pathActions.fixEnd(cur_shape);
+			}
+			cur_shape.setAttribute('transform', 'translate(' + x + ',' + y + ') scale(0.005) translate(' + -x + ',' + -y + ')');
+
+			canv.recalculateDimensions(cur_shape);
+
+			var tlist = canv.getTransformList(cur_shape);
+
+			lastBBox = cur_shape.getBBox();
+
+			return {
+				started: true
+			};
+		},
+		mouseMove: function(opts) {
+			var mode = canv.getMode();
+			if (mode !== mode_id) {return;}
+
+			var zoom = canv.getZoom();
+			var evt = opts.event;
+
+			var x = opts.mouse_x/zoom;
+			var y = opts.mouse_y/zoom;
+
+			var tlist = canv.getTransformList(cur_shape),
+				box = cur_shape.getBBox(),
+				left = box.x, top = box.y, width = box.width,
+				height = box.height;
+			var dx = (x-start_x), dy = (y-start_y);
+
+			var newbox = {
+				'x': Math.min(start_x,x),
+				'y': Math.min(start_y,y),
+				'width': Math.abs(x-start_x),
+				'height': Math.abs(y-start_y)
+			};
+
+			var tx = 0, ty = 0,
+				sy = height ? (height+dy)/height : 1,
+				sx = width ? (width+dx)/width : 1;
+
+			sx = (newbox.width / lastBBox.width) || 1;
+			sy = (newbox.height / lastBBox.height) || 1;
+
+			// Not perfect, but mostly works...
+			if (x < start_x) {
+				tx = lastBBox.width;
+			}
+			if (y < start_y) {ty = lastBBox.height;}
+
+			// update the transform list with translate,scale,translate
+			var translateOrigin = svgroot.createSVGTransform(),
+				scale = svgroot.createSVGTransform(),
+				translateBack = svgroot.createSVGTransform();
+
+			translateOrigin.setTranslate(-(left+tx), -(top+ty));
+			if (!evt.shiftKey) {
+				var max = Math.min(Math.abs(sx), Math.abs(sy));
+
+				sx = max * (sx < 0 ? -1 : 1);
+				sy = max * (sy < 0 ? -1 : 1);
+			}
+			scale.setScale(sx,sy);
+
+			translateBack.setTranslate(left+tx, top+ty);
+			tlist.appendItem(translateBack);
+			tlist.appendItem(scale);
+			tlist.appendItem(translateOrigin);
+
+			canv.recalculateDimensions(cur_shape);
+
+			lastBBox = cur_shape.getBBox();
+		},
+		mouseUp: function(opts) {
+			var mode = canv.getMode();
+			if (mode !== mode_id) {return;}
+         
+      var keepObject = (opts.event.clientX != startClientPos.x && opts.event.clientY != startClientPos.y);
+
+			return {
+				keep: keepObject,
+				element: cur_shape,
+				started: false
+			};
+		}
+	};
+});
+

+ 236 - 0
main/inc/lib/svg-edit/extensions/ext-shapes.xml

@@ -0,0 +1,236 @@
+/*globals svgEditor, svgedit, svgCanvas, $*/
+/*jslint vars: true, eqeq: true*/
+/*
+ * ext-star.js
+ *
+ *
+ * Copyright(c) 2010 CloudCanvas, Inc.
+ * All rights reserved
+ *
+ */
+
+svgEditor.addExtension('star', function(S){'use strict';
+
+	var // NS = svgedit.NS,
+		// svgcontent = S.svgcontent,
+		selElems,
+		// editingitex = false,
+		// svgdoc = S.svgroot.parentNode.ownerDocument,
+		started,
+		newFO,
+		// edg = 0,
+		// newFOG, newFOGParent, newDef, newImageName, newMaskID,
+		// undoCommand = 'Not image',
+		// modeChangeG, ccZoom, wEl, hEl, wOffset, hOffset, ccRgbEl, brushW, brushH,
+		shape;
+
+	function showPanel(on){
+		var fc_rules = $('#fc_rules');
+		if (!fc_rules.length) {
+			fc_rules = $('<style id="fc_rules"><\/style>').appendTo('head');
+		}
+		fc_rules.text(!on ? '' : ' #tool_topath { display: none !important; }');
+		$('#star_panel').toggle(on);
+	}
+
+	/*
+	function toggleSourceButtons(on){
+		$('#star_save, #star_cancel').toggle(on);
+	}
+	*/
+
+	function setAttr(attr, val){
+		svgCanvas.changeSelectedAttribute(attr, val);
+		S.call('changed', selElems);
+	}
+
+	/*
+	function cot(n){
+		return 1 / Math.tan(n);
+	}
+
+	function sec(n){
+		return 1 / Math.cos(n);
+	}
+	*/
+
+	return {
+		name: 'star',
+		svgicons: svgEditor.curConfig.extPath + 'star-icons.svg',
+		buttons: [{
+			id: 'tool_star',
+			type: 'mode',
+			title: 'Star Tool',
+			position: 12,
+			events: {
+				click: function(){
+					showPanel(true);
+					svgCanvas.setMode('star');
+				}
+			}
+		}],
+
+		context_tools: [{
+			type: 'input',
+			panel: 'star_panel',
+			title: 'Number of Sides',
+			id: 'starNumPoints',
+			label: 'points',
+			size: 3,
+			defval: 5,
+			events: {
+				change: function(){
+					setAttr('point', this.value);
+				}
+			}
+		}, {
+			type: 'input',
+			panel: 'star_panel',
+			title: 'Pointiness',
+			id: 'starRadiusMulitplier',
+			label: 'Pointiness',
+			size: 3,
+			defval: 2.5
+		}, {
+			type: 'input',
+			panel: 'star_panel',
+			title: 'Twists the star',
+			id: 'radialShift',
+			label: 'Radial Shift',
+			size: 3,
+			defval: 0,
+			events: {
+				change: function(){
+					setAttr('radialshift', this.value);
+				}
+			}
+		}],
+		callback: function(){
+			$('#star_panel').hide();
+			// var endChanges = function(){};
+		},
+		mouseDown: function(opts){
+			var rgb = svgCanvas.getColor('fill');
+			// var ccRgbEl = rgb.substring(1, rgb.length);
+			var sRgb = svgCanvas.getColor('stroke');
+			// var ccSRgbEl = sRgb.substring(1, rgb.length);
+			var sWidth = svgCanvas.getStrokeWidth();
+
+			if (svgCanvas.getMode() == 'star') {
+				started = true;
+
+				newFO = S.addSvgElementFromJson({
+					'element': 'polygon',
+					'attr': {
+						'cx': opts.start_x,
+						'cy': opts.start_y,
+						'id': S.getNextId(),
+						'shape': 'star',
+						'point': document.getElementById('starNumPoints').value,
+						'r': 0,
+						'radialshift': document.getElementById('radialShift').value,
+						'r2': 0,
+						'orient': 'point',
+						'fill': rgb,
+						'strokecolor': sRgb,
+						'strokeWidth': sWidth
+					}
+				});
+				return {
+					started: true
+				};
+			}
+		},
+		mouseMove: function(opts){
+			if (!started) {
+				return;
+			}
+			if (svgCanvas.getMode() == 'star') {
+				var x = opts.mouse_x;
+				var y = opts.mouse_y;
+				var c = $(newFO).attr(['cx', 'cy', 'point', 'orient', 'fill', 'strokecolor', 'strokeWidth', 'radialshift']);
+
+				var cx = c.cx, cy = c.cy, fill = c.fill, strokecolor = c.strokecolor, strokewidth = c.strokeWidth, radialShift = c.radialshift, point = c.point, orient = c.orient, circumradius = (Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy))) / 1.5, inradius = circumradius / document.getElementById('starRadiusMulitplier').value;
+				newFO.setAttributeNS(null, 'r', circumradius);
+				newFO.setAttributeNS(null, 'r2', inradius);
+
+				var polyPoints = '';
+				var s;
+				for (s = 0; point >= s; s++) {
+					var angle = 2.0 * Math.PI * (s / point);
+					if ('point' == orient) {
+						angle -= (Math.PI / 2);
+					} else if ('edge' == orient) {
+						angle = (angle + (Math.PI / point)) - (Math.PI / 2);
+					}
+
+					x = (circumradius * Math.cos(angle)) + cx;
+					y = (circumradius * Math.sin(angle)) + cy;
+
+					polyPoints += x + ',' + y + ' ';
+
+					if (null != inradius) {
+						angle = (2.0 * Math.PI * (s / point)) + (Math.PI / point);
+						if ('point' == orient) {
+							angle -= (Math.PI / 2);
+						} else if ('edge' == orient) {
+							angle = (angle + (Math.PI / point)) - (Math.PI / 2);
+						}
+						angle += radialShift;
+
+						x = (inradius * Math.cos(angle)) + cx;
+						y = (inradius * Math.sin(angle)) + cy;
+
+						polyPoints += x + ',' + y + ' ';
+					}
+				}
+				newFO.setAttributeNS(null, 'points', polyPoints);
+				newFO.setAttributeNS(null, 'fill', fill);
+				newFO.setAttributeNS(null, 'stroke', strokecolor);
+				newFO.setAttributeNS(null, 'stroke-width', strokewidth);
+				shape = newFO.getAttributeNS(null, 'shape');
+
+				return {
+					started: true
+				};
+			}
+
+		},
+		mouseUp: function(){
+			if (svgCanvas.getMode() == 'star') {
+				var attrs = $(newFO).attr(['r']);
+				// svgCanvas.addToSelection([newFO], true);
+				return {
+					keep: (attrs.r != 0),
+					element: newFO
+				};
+			}
+		},
+		selectedChanged: function(opts){
+			// Use this to update the current selected elements
+			selElems = opts.elems;
+
+			var i = selElems.length;
+
+			while (i--) {
+				var elem = selElems[i];
+				if (elem && elem.getAttributeNS(null, 'shape') === 'star') {
+					if (opts.selectedElement && !opts.multiselected) {
+						// $('#starRadiusMulitplier').val(elem.getAttribute('r2'));
+						$('#starNumPoints').val(elem.getAttribute('point'));
+						$('#radialShift').val(elem.getAttribute('radialshift'));
+						showPanel(true);
+					}
+					else {
+						showPanel(false);
+					}
+				} else {
+					showPanel(false);
+				}
+			}
+		},
+		elementChanged: function(opts){
+			// var elem = opts.elems[0];
+		}
+	};
+});

+ 230 - 0
main/inc/lib/svg-edit/extensions/ext-storage - Copy.js

@@ -0,0 +1,230 @@
+/*globals svgEditor, Editor, svgCanvas, $, widget*/
+/*jslint vars: true, eqeq: true, regexp: true, continue: true*/
+/*
+ * ext-storage.js
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2010 Brett Zamir
+ *
+ */
+/**
+* This extension allows automatic saving of the SVG canvas contents upon
+*  page unload and and automatic retrieval of the contents upon editor load.
+*  The functionality was originally part of the SVG Editor, but moved to a
+*  separate extension to make the behavior optional, and adapted to inform
+*  the user of its setting of local data.
+*/
+
+/*
+TODOS
+1. Revisit on whether to use $.pref over directly setting curConfig in all
+	extensions for a more public API (not only for extPath and imagePath,
+	but other currently used config in the extensions)
+*/
+svgEditor.addExtension('storage', function() { 'use strict';
+	// We could empty any already-set data for them, but it
+	// would be a risk for users who wanted to store but accidentally
+	// said "no"; instead, we'll let those who already set it, delete it themselves;
+	// to change, change the following line to true.
+	var emptyStorageOnDecline = false;
+
+	function replaceStoragePrompt (val) {
+		val = val ? 'storagePrompt=' + val : '';
+		if (window.location.href.indexOf('storagePrompt=') > -1) {
+			window.location.href = window.location.href.replace(/([&?])storagePrompt=[^&]*/, (val ? '$1' : '') + val);
+		}
+		else {
+			window.location.href += (window.location.href.indexOf('?') > -1 ? '&' : '?') + val;
+		}
+	}
+	function setSVGContentStorage (val) {
+		if (window.localStorage) {
+			var name = 'svgedit-' + svgEditor.curConfig.canvasName;
+			window.localStorage.setItem(name, val);
+		}
+	}
+	function emptyLocalStorage() {
+		document.cookie = 'store=';
+		setSVGContentStorage('');
+	}
+	
+	//emptyLocalStorage();
+
+	/**
+	* Listen for unloading: If and only if opted in by the user, set the content
+	*   document and preferences into storage:
+	* 1. Prevent save warnings (since we're automatically saving unsaved
+	*       content into storage)
+	* 2. Use localStorage to set SVG contents (potentially too large to allow in cookies)
+	* 3. Use localStorage (where available) or cookies to set preferences.
+	*/
+	function setupBeforeUnloadListener () {
+		window.addEventListener('beforeunload', function(e) {
+			// Don't save anything unless the user opted in to storage
+			if (!document.cookie.match(/store=(?:prefsAndContent|prefsOnly)/)) {
+				return;
+			}
+			var key;
+			setSVGContentStorage(svgCanvas.getSvgString());
+
+			svgEditor.showSaveWarning = false;
+
+			var curConfig = svgEditor.curConfig;
+			for (key in curConfig) {
+				if (curConfig.hasOwnProperty(key)) { // It's our own config, so we don't need to iterate up the prototype chain
+					var d,
+						storage = svgEditor.storage,
+						val = curConfig[key],
+						store = (val != undefined);
+					key = 'svg-edit-' + key;
+					
+					if (!store) {
+						continue;
+					}
+					if (storage) {
+						storage.setItem(key, val);
+					}
+					else if (window.widget) {
+						widget.setPreferenceForKey(val, key);
+					}
+					else {
+						d = new Date();
+						d.setTime(d.getTime() + 31536000000);
+						val = encodeURIComponent(val);
+						document.cookie = key + '=' + val + '; expires=' + d.toUTCString();
+					}
+				}
+			}
+		}, false);
+	}
+
+	var userStoragePref = 'noPrefsOrContent';
+
+		/*
+		// We could add locales here instead (and also thereby avoid the need
+		// to keep our content within "langReady"), but this would be less
+		// convenient for translators.
+		$.extend(uiStrings, {confirmSetStorage: {
+			message: "By default and where supported, SVG-Edit can store your editor "+
+			"preferences and SVG content locally on your machine so you do not "+
+			"need to add these back each time you load SVG-Edit. If, for privacy "+
+			"reasons, you do not wish to store this information on your machine, "+
+			"you can change away from the default option below.",
+			storagePrefsAndContent: "Store preferences and SVG content locally",
+			storagePrefsOnly: "Only store preferences locally",
+			storagePrefs: "Store preferences locally",
+			storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+			storageNoPrefs: "Do not store my preferences locally",
+			rememberLabel: "Remember this choice?",
+			rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
+		}});
+		*/
+	
+	return {
+		name: 'storage',
+		langReady: function (lang, uiStrings) {
+			var storagePrompt = $.deparam.querystring(true).storagePrompt;
+			if (
+				// If explicitly set to always prompt (e.g., so a user can alter
+				//   their settings)...
+				storagePrompt === true ||
+				// ...or...the user hasn't visited page preventing storage prompt (for
+				// users who don't want to set cookies at all but don't want
+				// continual prompts)...
+				(storagePrompt !== false &&
+					// ...and user hasn't already indicated a desire for storage
+					!document.cookie.match(/store=(?:prefsAndContent|prefsOnly)/)
+				)
+				// ...then show the storage prompt.
+			) {
+				var options = [];
+				if (window.localStorage) {
+					options.unshift(
+						{value: 'prefsAndContent', text: uiStrings.confirmSetStorage.storagePrefsAndContent},
+						{value: 'prefsOnly', text: uiStrings.confirmSetStorage.storagePrefsOnly},
+						{value: 'noPrefsOrContent', text: uiStrings.confirmSetStorage.storageNoPrefsOrContent}
+					);
+				}
+				else {
+					options.unshift(
+						{value: 'prefsOnly', text: uiStrings.confirmSetStorage.storagePrefs},
+						{value: 'noPrefsOrContent', text: uiStrings.confirmSetStorage.storageNoPrefs}
+					);
+				}
+
+				// Hack to temporarily provide a wide and high enough dialog
+				var oldContainerWidth = $('#dialog_container')[0].style.width,
+					oldContainerMarginLeft = $('#dialog_container')[0].style.marginLeft,
+					oldContentHeight = $('#dialog_content')[0].style.height,
+					oldContainerHeight = $('#dialog_container')[0].style.height;
+				$('#dialog_content')[0].style.height = '120px';
+				$('#dialog_container')[0].style.height = '170px';
+				$('#dialog_container')[0].style.width = '800px';
+				$('#dialog_container')[0].style.marginLeft = '-400px';
+
+				// Open select-with-checkbox dialog
+				$.select(
+					uiStrings.confirmSetStorage.message,
+					options,
+					function (pref, checked) {
+						// If the URL was configured to always insist on a prompt, if
+						//    the user does indicate a wish to store their info, we
+						//    don't want ask them again upon page refresh so move
+						//    them instead to a URL which does not always prompt
+						if (storagePrompt === true && pref && pref !== 'noPrefsOrContent') {
+							replaceStoragePrompt();
+							return;
+						}
+						if (pref && pref !== 'noPrefsOrContent') {
+							// Regardless of whether the user opted
+							// to remember the choice (and move to a URL which won't
+							// ask them again), we have to assume the user
+							// doesn't even want to remember their not wanting
+							// storage, so we don't set the cookie or continue on with
+							//  setting storage on beforeunload
+							document.cookie = 'store=' + pref; // 'prefsAndContent' | 'prefsOnly'
+						}
+						else {
+							if (emptyStorageOnDecline) {
+								emptyLocalStorage();
+							}
+							if (checked) {
+								// Open a URL which won't set storage and won't prompt user about storage
+								replaceStoragePrompt('false');
+							}
+						}
+						
+						// Store the preference in memory (not currently in use)
+						userStoragePref = pref;
+						// Reset width/height of dialog (e.g., for use by Export)
+						$('#dialog_container')[0].style.width = oldContainerWidth;
+						$('#dialog_container')[0].style.marginLeft = oldContainerMarginLeft;				
+						$('#dialog_content')[0].style.height = oldContentHeight;
+						$('#dialog_container')[0].style.height = oldContainerHeight;
+						
+						// It should be enough to (conditionally) add to storage on
+						//   beforeunload, but if we wished to update immediately,
+						//   we might wish to try setting:
+						//       Editor.preventStorageAccessOnLoad = true;
+						//   and then call:
+						//       Editor.loadContentAndPrefs();
+						setupBeforeUnloadListener();
+						
+						Editor.storagePromptClosed = true;
+					},
+					null,
+					null,
+					{
+						label: uiStrings.confirmSetStorage.rememberLabel,
+						checked: false,
+						tooltip: uiStrings.confirmSetStorage.rememberTooltip
+					}
+				);
+			}
+			else {
+				setupBeforeUnloadListener();
+			}
+		}
+	};
+});

+ 293 - 0
main/inc/lib/svg-edit/extensions/ext-storage.js

@@ -0,0 +1,293 @@
+/*globals svgEditor, svgCanvas, $, widget*/
+/*jslint vars: true, eqeq: true, regexp: true, continue: true*/
+/*
+ * ext-storage.js
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2010 Brett Zamir
+ *
+ */
+/**
+* This extension allows automatic saving of the SVG canvas contents upon
+*  page unload (which can later be automatically retrieved upon future
+*  editor loads).
+*
+*  The functionality was originally part of the SVG Editor, but moved to a
+*  separate extension to make the setting behavior optional, and adapted
+*  to inform the user of its setting of local data.
+*/
+
+/*
+TODOS
+1. Revisit on whether to use $.pref over directly setting curConfig in all
+	extensions for a more public API (not only for extPath and imagePath,
+	but other currently used config in the extensions)
+2. We might provide control of storage settings through the UI besides the
+    initial (or URL-forced) dialog.
+*/
+svgEditor.addExtension('storage', function() {
+	// We could empty any already-set data for users when they decline storage,
+	//  but it would be a risk for users who wanted to store but accidentally
+	// said "no"; instead, we'll let those who already set it, delete it themselves;
+	// to change, set the "emptyStorageOnDecline" config setting to true
+	// in config.js.
+	var emptyStorageOnDecline = svgEditor.curConfig.emptyStorageOnDecline,
+		// When the code in svg-editor.js prevents local storage on load per
+		//  user request, we also prevent storing on unload here so as to
+		//  avoid third-party sites making XSRF requests or providing links
+		// which would cause the user's local storage not to load and then
+		// upon page unload (such as the user closing the window), the storage
+		//  would thereby be set with an empty value, erasing any of the
+		// user's prior work. To change this behavior so that no use of storage
+		// or adding of new storage takes place regardless of settings, set
+		// the "noStorageOnLoad" config setting to true in config.js.
+		noStorageOnLoad = svgEditor.curConfig.noStorageOnLoad,
+		forceStorage = svgEditor.curConfig.forceStorage,
+		storage = svgEditor.storage;
+
+	function replaceStoragePrompt (val) {
+		val = val ? 'storagePrompt=' + val : '';
+		var loc = top.location; // Allow this to work with the embedded editor as well
+		if (loc.href.indexOf('storagePrompt=') > -1) {
+			loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (n0, n1, amp) {
+				return (val ? n1 : '') + val + (!val && amp ? n1 : (amp || ''));
+			});
+		}
+		else {
+			loc.href += (loc.href.indexOf('?') > -1 ? '&' : '?') + val;
+		}
+	}
+	function setSVGContentStorage (val) {
+		if (storage) {
+			var name = 'svgedit-' + svgEditor.curConfig.canvasName;
+			if (!val) {
+				storage.removeItem(name);
+			}
+			else {
+				storage.setItem(name, val);
+			}
+		}
+	}
+	
+	function expireCookie (cookie) {
+		document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
+	}
+	
+	function removeStoragePrefCookie () {
+		expireCookie('store');
+	}
+	
+	function emptyStorage() {
+		setSVGContentStorage('');
+		var name;
+		for (name in svgEditor.curPrefs) {
+			if (svgEditor.curPrefs.hasOwnProperty(name)) {
+				name = 'svg-edit-' + name;
+				if (storage) {
+					storage.removeItem(name);
+				}
+				expireCookie(name);
+			}
+		}
+	}
+	
+//	emptyStorage();
+
+	/**
+	* Listen for unloading: If and only if opted in by the user, set the content
+	*   document and preferences into storage:
+	* 1. Prevent save warnings (since we're automatically saving unsaved
+	*       content into storage)
+	* 2. Use localStorage to set SVG contents (potentially too large to allow in cookies)
+	* 3. Use localStorage (where available) or cookies to set preferences.
+	*/
+	function setupBeforeUnloadListener () {
+		window.addEventListener('beforeunload', function(e) {
+			// Don't save anything unless the user opted in to storage
+			if (!document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/)) {
+				return;
+			}
+			var key;
+			if (document.cookie.match(/(?:^|;\s*)store=prefsAndContent/)) {
+				setSVGContentStorage(svgCanvas.getSvgString());			
+			}
+
+			svgEditor.setConfig({no_save_warning: true}); // No need for explicit saving at all once storage is on
+			// svgEditor.showSaveWarning = false;
+
+			var curPrefs = svgEditor.curPrefs;
+
+			for (key in curPrefs) {
+				if (curPrefs.hasOwnProperty(key)) { // It's our own config, so we don't need to iterate up the prototype chain
+					var val = curPrefs[key],
+						store = (val != undefined);
+					key = 'svg-edit-' + key;
+					if (!store) {
+						continue;
+					}
+					if (storage) {
+						storage.setItem(key, val);
+					}
+					else if (window.widget) {
+						widget.setPreferenceForKey(val, key);
+					}
+					else {
+						val = encodeURIComponent(val);
+						document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';
+					}
+				}
+			}
+		}, false);
+	}
+
+	/*
+	// We could add locales here instead (and also thereby avoid the need
+	// to keep our content within "langReady"), but this would be less
+	// convenient for translators.
+	$.extend(uiStrings, {confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
+	}});
+	*/
+	var loaded = false;
+	return {
+		name: 'storage',
+		langReady: function (data) {
+			var // lang = data.lang,
+				uiStrings = data.uiStrings, // No need to store as dialog should only run once
+				storagePrompt = $.deparam.querystring(true).storagePrompt;
+
+			// No need to run this one-time dialog again just because the user
+			//   changes the language
+			if (loaded) {
+				return;
+			}
+			loaded = true;
+
+			// Note that the following can load even if "noStorageOnLoad" is
+			//   set to false; to avoid any chance of storage, avoid this
+			//   extension! (and to avoid using any prior storage, set the
+			//   config option "noStorageOnLoad" to true).
+			if (!forceStorage && (
+				// If the URL has been explicitly set to always prompt the
+				//  user (e.g., so one can be pointed to a URL where one
+				// can alter one's settings, say to prevent future storage)...
+				storagePrompt === true ||
+				(
+					// ...or...if the URL at least doesn't explicitly prevent a
+					//  storage prompt (as we use for users who
+					// don't want to set cookies at all but who don't want
+					// continual prompts about it)...
+					storagePrompt !== false &&
+					// ...and this user hasn't previously indicated a desire for storage
+					!document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/)
+				)
+				// ...then show the storage prompt.
+			)) {
+
+				var options = [];
+				if (storage) {
+					options.unshift(
+						{value: 'prefsAndContent', text: uiStrings.confirmSetStorage.storagePrefsAndContent},
+						{value: 'prefsOnly', text: uiStrings.confirmSetStorage.storagePrefsOnly},
+						{value: 'noPrefsOrContent', text: uiStrings.confirmSetStorage.storageNoPrefsOrContent}
+					);
+				}
+				else {
+					options.unshift(
+						{value: 'prefsOnly', text: uiStrings.confirmSetStorage.storagePrefs},
+						{value: 'noPrefsOrContent', text: uiStrings.confirmSetStorage.storageNoPrefs}
+					);
+				}
+
+				// Hack to temporarily provide a wide and high enough dialog
+				var oldContainerWidth = $('#dialog_container')[0].style.width,
+					oldContainerMarginLeft = $('#dialog_container')[0].style.marginLeft,
+					oldContentHeight = $('#dialog_content')[0].style.height,
+					oldContainerHeight = $('#dialog_container')[0].style.height;
+				$('#dialog_content')[0].style.height = '120px';
+				$('#dialog_container')[0].style.height = '170px';
+				$('#dialog_container')[0].style.width = '800px';
+				$('#dialog_container')[0].style.marginLeft = '-400px';
+
+				// Open select-with-checkbox dialog
+				$.select(
+					uiStrings.confirmSetStorage.message,
+					options,
+					function (pref, checked) {
+						if (pref && pref !== 'noPrefsOrContent') {
+							// Regardless of whether the user opted
+							// to remember the choice (and move to a URL which won't
+							// ask them again), we have to assume the user
+							// doesn't even want to remember their not wanting
+							// storage, so we don't set the cookie or continue on with
+							//  setting storage on beforeunload
+							document.cookie = 'store=' + encodeURIComponent(pref) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'; // 'prefsAndContent' | 'prefsOnly'
+							// If the URL was configured to always insist on a prompt, if
+							//    the user does indicate a wish to store their info, we
+							//    don't want ask them again upon page refresh so move
+							//    them instead to a URL which does not always prompt
+							if (storagePrompt === true && checked) {
+								replaceStoragePrompt();
+								return;
+							}
+						}
+						else { // The user does not wish storage (or cancelled, which we treat equivalently)
+							removeStoragePrefCookie();
+							if (pref && // If the user explicitly expresses wish for no storage
+								emptyStorageOnDecline
+							) {
+								emptyStorage();
+							}
+							if (pref && checked) {
+								// Open a URL which won't set storage and won't prompt user about storage
+								replaceStoragePrompt('false');
+								return;
+							}
+						}
+
+						// Reset width/height of dialog (e.g., for use by Export)
+						$('#dialog_container')[0].style.width = oldContainerWidth;
+						$('#dialog_container')[0].style.marginLeft = oldContainerMarginLeft;				
+						$('#dialog_content')[0].style.height = oldContentHeight;
+						$('#dialog_container')[0].style.height = oldContainerHeight;
+						
+						// It should be enough to (conditionally) add to storage on
+						//   beforeunload, but if we wished to update immediately,
+						//   we might wish to try setting:
+						//       svgEditor.setConfig({noStorageOnLoad: true});
+						//   and then call:
+						//       svgEditor.loadContentAndPrefs();
+						
+						// We don't check for noStorageOnLoad here because
+						//   the prompt gives the user the option to store data
+						setupBeforeUnloadListener();
+						
+						svgEditor.storagePromptClosed = true;
+					},
+					null,
+					null,
+					{
+						label: uiStrings.confirmSetStorage.rememberLabel,
+						checked: false,
+						tooltip: uiStrings.confirmSetStorage.rememberTooltip
+					}
+				);
+			}
+			else if (!noStorageOnLoad || forceStorage) {
+				setupBeforeUnloadListener();
+			}
+		}
+	};
+});

+ 67 - 0
main/inc/lib/svg-edit/extensions/ext-webappfind.js

@@ -0,0 +1,67 @@
+/*globals svgEditor*/
+/*
+Depends on Firefox add-on and executables from https://github.com/brettz9/webappfind
+
+Todos:
+1. See WebAppFind Readme for SVG-related todos
+*/
+(function () {'use strict';
+
+var pathID,
+    saveMessage = 'webapp-save',
+    readMessage = 'webapp-read',
+    excludedMessages = [readMessage, saveMessage];
+
+window.addEventListener('message', function(e) {
+    if (e.origin !== window.location.origin || // PRIVACY AND SECURITY! (for viewing and saving, respectively)
+        (!Array.isArray(e.data) || excludedMessages.indexOf(e.data[0]) > -1) // Validate format and avoid our post below
+    ) {
+        return;
+    }
+    var svgString,
+        messageType = e.data[0];
+    switch (messageType) {
+        case 'webapp-view':
+            // Populate the contents
+            pathID = e.data[1];
+            
+            svgString = e.data[2];
+            svgEditor.loadFromString(svgString);
+            
+            /*if ($('#tool_save_file')) {
+                $('#tool_save_file').disabled = false;
+            }*/
+            break;
+        case 'webapp-save-end':
+            alert('save complete for pathID ' + e.data[1] + '!');
+            break;
+        default:
+            throw 'Unexpected mode';
+    }
+}, false);
+
+window.postMessage([readMessage], window.location.origin !== 'null' ? window.location.origin : '*'); // Avoid "null" string error for file: protocol (even though file protocol not currently supported by add-on)
+
+svgEditor.addExtension('WebAppFind', function() {
+
+    return {
+        name: 'WebAppFind',
+        svgicons: svgEditor.curConfig.extPath + 'webappfind-icon.svg',
+        buttons: [{
+            id: 'webappfind_save', // 
+            type: 'app_menu',
+            title: 'Save Image back to Disk',
+            position: 4, // Before 0-based index position 4 (after the regular "Save Image (S)")
+            events: {
+                click: function () {
+                    if (!pathID) { // Not ready yet as haven't received first payload
+                        return;
+                    }
+                    window.postMessage([saveMessage, pathID, svgEditor.canvas.getSvgString()], window.location.origin);
+                }
+            }
+        }]
+    };
+});
+
+}());

+ 42 - 0
main/inc/lib/svg-edit/extensions/ext-xdomain-messaging.js

@@ -0,0 +1,42 @@
+/**
+* Should not be needed for same domain control (just call via child frame),
+*  but an API common for cross-domain and same domain use can be found
+*  in embedapi.js with a demo at embedapi.html
+*/
+/*globals svgEditor, svgCanvas*/
+svgEditor.addExtension('xdomain-messaging', function() {'use strict';
+	try {
+		window.addEventListener('message', function(e) {
+			// We accept and post strings for the sake of IE9 support
+			if (typeof e.data !== 'string' || e.data.charAt() === '|') {
+				return;
+			}
+			var cbid, name, args, message, allowedOrigins, data = JSON.parse(e.data);
+			if (!data || typeof data !== 'object' || data.namespace !== 'svgCanvas') {
+				return;
+			}
+			// The default is not to allow any origins, including even the same domain or if run on a file:// URL
+			//  See config-sample.js for an example of how to configure
+			allowedOrigins = svgEditor.curConfig.allowedOrigins;
+			if (allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1) {
+				return;
+			}
+			cbid = data.id;
+			name = data.name;
+			args = data.args;
+			message = {
+				namespace: 'svg-edit',
+				id: cbid
+			};
+			try {
+				message.result = svgCanvas[name].apply(svgCanvas, args);
+			} catch (err) {
+				message.error = err.message;
+			}
+			e.source.postMessage(JSON.stringify(message), '*');
+		}, false);
+	}
+	catch (err) {
+		console.log('Error with xdomain message listener: ' + err);
+	}
+});

+ 30 - 50
main/inc/lib/svg-edit/extensions/eyedropper-icon.xml

@@ -1,70 +1,50 @@
+<!DOCTYPE html>
 <?php
+exit;
 /*
  * fileopen.php
  * To be used with ext-server_opensave.js for SVG-edit
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *
- * Integrate svg-edit with Chamilo
- * @author Juan Carlos Ra�a Trabado
- * @since 25/september/2010
-*/
-
-require_once '../../../../inc/global.inc.php';//hack for chamilo
-require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
-
-api_protect_course_script();
-api_block_anonymous_users();
-
-if(!isset($_FILES['svg_file']['tmp_name'])) {
-	api_not_allowed(false);//from Chamilo
-	die();
-}
-
-
-?>
-<!doctype html>
-<?php
+ */
 	// Very minimal PHP file, all we do is Base64 encode the uploaded file and
 	// return it to the editor
-	
+
+	$type = $_REQUEST['type'];
+	if (!in_array($type, array('load_svg', 'import_svg', 'import_img'))) {
+		exit;
+	}
+	require('allowedMimeTypes.php');
+
 	$file = $_FILES['svg_file']['tmp_name'];
-	
+
 	$output = file_get_contents($file);
-	
-	$type = $_REQUEST['type'];
-	
+
 	$prefix = '';
-	
+
 	// Make Data URL prefix for import image
-	if($type == 'import_img') {
+	if ($type == 'import_img') {
 		$info = getimagesize($file);
+		if (!in_array($info['mime'], $allowedMimeTypesBySuffix)) {
+			exit;
+		}
 		$prefix = 'data:' . $info['mime'] . ';base64,';
 	}
-	
-//check the extension
-$extension = explode('.', $file);
-$extension = strtolower($extension[sizeof($extension) - 1]);	
+?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta charset="utf-8" />
+<script>
 
-//a bit title security
-$filename = addslashes(trim($file));
-$filename = Security::remove_XSS($filename);
-$filename = replace_dangerous_char($filename, 'strict');
-$filename = disable_dangerous_file($filename);	
-	
-//a bit mime security
-$current_mime = $_FILES['svg_file']['type'];
-$mime_svg='image/svg+xml';
-$mime_xml='application/xml';//hack for svg-edit because original code return application/xml; charset=us-ascii.
-		
-if(strpos($current_mime, $mime_svg)===false && strpos($current_mime, $mime_xml)===false && $extension=='svg'){
-	// die();//File extension does not match its content disabled to check into chamilo dev campus TODO:enabled
-}
+top.svgEditor.processFile("<?php
 
-?>
+// This should be safe since SVG edit does its own filtering (e.g., if an SVG file contains scripts)
+echo $prefix . base64_encode($output);
 
-<script>
-window.top.window.svgEditor.processFile("<?php echo $prefix . base64_encode($output); ?>", "<?php echo $type ?>");
-</script>
+?>", "<?php echo $type; ?>");
+</script>
+</head><body></body>
+</html>

+ 34 - 127
main/inc/lib/svg-edit/extensions/filesave.php

@@ -3,151 +3,58 @@
  * filesave.php
  * To be used with ext-server_opensave.js for SVG-edit
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Alexis Deveria
  *
- * Integrate svg-edit with Chamilo
- * @author Juan Carlos Raña Trabado
- * @since 25/september/2010
-*/
-
-$language_file = array('document');//Chamilo load lang var
+ */
+exit;
+function encodeRFC5987ValueChars ($str) {
+	// See http://tools.ietf.org/html/rfc5987#section-3.2.1
+	// For better readability within headers, add back the characters escaped by rawurlencode but still allowable
+	// Although RFC3986 reserves "!" (%21), RFC5987 does not
+	return preg_replace_callback('@%(2[1346B]|5E|60|7C)@', function ($matches) {
+		return chr('0x' . $matches[1]);
+	}, rawurlencode($str));
+}
 
-//Chamilo load libraries
-require_once '../../../../inc/global.inc.php';
-require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
-require_once api_get_path(LIBRARY_PATH).'document.lib.php';
+require('allowedMimeTypes.php');
 
-//Add security from Chamilo
-api_protect_course_script();
-api_block_anonymous_users();
+$mime = !isset($_POST['mime']) || !in_array($_POST['mime'], $allowedMimeTypesBySuffix) ? 'image/svg+xml;charset=UTF-8' : $_POST['mime'];
 
-if(!isset($_POST['output_svg']) && !isset($_POST['output_png'])) {
-	api_not_allowed();//from Chamilo
-	die();
+if (!isset($_POST['output_svg']) && !isset($_POST['output_img'])) {
+	die('post fail');
 }
 
 $file = '';
-$suffix = isset($_POST['output_svg'])?'svg':'png';
 
-if(isset($_POST['filename']) && strlen($_POST['filename']) > 0) {
-	$file = $_POST['filename'];	
+$suffix = '.' . array_search($mime, $allowedMimeTypesBySuffix);
+
+if (isset($_POST['filename']) && strlen($_POST['filename']) > 0) {
+	$file = $_POST['filename'] . $suffix;
 } else {
-	$file = 'image';
+	$file = 'image' . $suffix;
 }
 
-if($suffix == 'svg') {
-	$mime = 'image/svg+xml';
-	$contents = rawurldecode($_POST['output_svg']);
+if ($suffix == '.svg') {
+	$contents = $_POST['output_svg'];
 } else {
-	$mime = 'image/png';
-	$contents = $_POST['output_png'];
+	$contents = $_POST['output_img'];
 	$pos = (strpos($contents, 'base64,') + 7);
 	$contents = base64_decode(substr($contents, $pos));
 }
 
-//get SVG-Edit values
-$filename=$file;//from svg-edit
-$extension=$suffix;// from svg-edit
-$content=$contents;//from svg-edit
-
-$title = Database::escape_string(str_replace('_',' ',$filename));
-
-//get Chamilo variables 
-
-if(!isset($_SESSION['draw_dir']) && !isset($_SESSION['whereami'])) {
-	api_not_allowed();//from Chamilo
-	die();	
-}
-
-$current_session_id = api_get_session_id();
-$groupId    =  api_get_group_id();
-$relativeUrlPath=$_SESSION['draw_dir'];
-$currentTool=$_SESSION['whereami'];
-$dirBaseDocuments = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
-$saveDir=$dirBaseDocuments.$_SESSION['draw_dir'];
-
-//a bit title security
-
-$filename = addslashes(trim($filename));
-$filename = Security::remove_XSS($filename);
-$filename = replace_dangerous_char($filename, 'strict');
-$filename = disable_dangerous_file($filename);
-
-// a bit extension
-if ($suffix!= 'svg' && $suffix!= 'png'){
-	die();
-}
-
-//a bit mime security
-//comment because finfo seems stopping the save process files in some php vers.
-/*
-if (phpversion() >= '5.3' && extension_loaded('fileinfo')) {
-	$finfo = new finfo(FILEINFO_MIME);
-	$current_mime=$finfo->buffer($contents);
-	finfo_close($finfo);
-	$mime_png='image/png';//svg-edit return image/png; charset=binary 
-	$mime_svg='image/svg+xml';
-	$mime_xml='application/xml';//hack for svg-edit because original code return application/xml; charset=us-ascii. See	  
-	if(strpos($current_mime, $mime_png)===false && $extension=='png') {
-		die();//File extension does not match its content
-	} elseif(strpos($current_mime, $mime_svg)===false && strpos($current_mime, $mime_xml)===false && $extension=='svg') {
-		die();//File extension does not match its content
-	}
-}
-*/
-
-//checks if the file exists, then rename the new
-if (file_exists($saveDir.'/'.$filename.$i.'.'.$extension) && $currentTool=='document/createdraw'){
-	echo '<script language="javascript" type="text/javascript">';
-	echo 'alert("'.get_lang('FileExistsChangeToSave').'");';
-	echo '</script>';
-	die();
-} else {
-	$drawFileName = $filename.'.'.$extension;
-	$title = $title.'.'.$extension;
-}
-$documentPath = $saveDir.'/'.$drawFileName;
-
-//add new document to disk
-file_put_contents( $documentPath, $contents );
-
-if ($currentTool=='document/createdraw') {
-	//add document to database
-	$doc_id = add_document($_course, $relativeUrlPath.'/'.$drawFileName, 'file', filesize($documentPath), $title);
-	api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'DocumentAdded', $_user['user_id'], $groupId, null, null, null, $current_session_id);
-
-} elseif($currentTool=='document/editdraw') {
+header("Cache-Control: public");
+header("Content-Description: File Transfer");
 
-	//check path
-	if (!isset($_SESSION['draw_file'])){
-		api_not_allowed();//from Chamilo
-		die();
-	}
-	if ($_SESSION['draw_file'] == $drawFileName ){
-		$document_id = DocumentManager::get_document_id($_course, $relativeUrlPath.'/'.$drawFileName);
-		update_existing_document($_course, $document_id, filesize($documentPath), null);
-		api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentUpdated', $_user['user_id'], $groupId, null, null, null, $current_session_id);
-	} else {
-		//add a new document
-		$doc_id = add_document($_course, $relativeUrlPath.'/'.$drawFileName, 'file', filesize($documentPath), $title);
-		api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'DocumentAdded', $_user['user_id'], $groupId, null, null, null, $current_session_id);
-	}	
-}
+// See http://tools.ietf.org/html/rfc6266#section-4.1
+header("Content-Disposition: attachment; filename*=UTF-8''" . encodeRFC5987ValueChars(
+	// preg_replace('@[\\\\/:*?"<>|]@', '', $file) // If we wanted to strip Windows-disallowed characters server-side (but not a security issue, so we can strip client-side instead)
+	$file
+));
+header("Content-Type: " .  $mime);
+header("Content-Transfer-Encoding: binary");
 
-//clean sessions and add messages and return to current document list
-unset($_SESSION['draw_dir']);
-unset($_SESSION['draw_file']);
-unset($_SESSION['whereami']);
+echo $contents;
 
-echo '<script language="javascript" type="text/javascript">';
-if ($suffix!= 'png'){	
-	if ($relativeUrlPath==''){$relativeUrlPath='/';};    
-	$interbreadcrumb=api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq().'&curdirpath='.	urlencode($relativeUrlPath);
-	echo 'alert("'.get_lang('FileSavedAs').': '.$title.'");';	
-	echo 'window.top.location.href="'.$interbreadcrumb.'";';//return to current document list
-} else {
-	echo 'alert("'.get_lang('FileExportAs').': '.$title.'");';
-}
-echo '</script>';
+?>

+ 27 - 0
main/inc/lib/svg-edit/extensions/foreignobject-icons.xml

@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <meta charset="utf-8" />
+    <title></title>
+		<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
+</head>
+<body>
+<form action="filesave.php">
+<input type="hidden" value="" name="output_svg" />
+</form>
+<script>
+/*globals $*/
+function xhtmlEscape(str) {'use strict';
+		return str.replace(/&(?!amp;)/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;'); // < is actually disallowed above anyways
+	}
+$('<form>').attr({
+					method: 'post',
+					action: 'filesave.php',
+					target: 'output_frame'
+				}).append('<input type="hidden" name="output_svg" value="' + xhtmlEscape('<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><script>alert(document.cookie);<\/script><\/svg>') + '">')
+					.appendTo('body')
+					.submit().remove();
+</script>
+
+</body>
+</html>

+ 70 - 0
main/inc/lib/svg-edit/extensions/grid-icon.xml

@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta charset="utf-8" />
+<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
+</head>
+<body>
+
+<h1>Select an image:</h1>
+<a href="smiley.svg">smiley.svg</a>
+<br>
+<a href="../../images/logo.png">logo.png</a>
+
+<script>
+/*globals $*/
+/*jslint vars: true*/
+$('a').click(function() {'use strict';
+	var meta_str;
+	var href = this.href;
+	var target = window.parent;
+	// Convert Non-SVG images to data URL first 
+	// (this could also have been done server-side by the library)
+	if (this.href.indexOf('.svg') === -1) {
+
+		meta_str = JSON.stringify({
+			name: $(this).text(),
+			id: href
+		});
+		target.postMessage(meta_str, '*');
+
+		var img = new Image();
+		img.onload = function() {
+			var canvas = document.createElement('canvas');
+			canvas.width = this.width;
+			canvas.height = this.height;
+			// load the raster image into the canvas
+			canvas.getContext('2d').drawImage(this, 0, 0);
+			// retrieve the data: URL
+			var dataurl;
+			try {
+				dataurl = canvas.toDataURL();
+			} catch(err) {
+				// This fails in Firefox with file:// URLs :(
+				alert("Data URL conversion failed: " + err);
+				dataurl = "";
+			}
+			target.postMessage('|' + href + '|' + dataurl, '*');
+		};
+		img.src = href;
+	} else {
+		// Send metadata (also indicates file is about to be sent)
+		meta_str = JSON.stringify({
+			name: $(this).text(),
+			id: href
+		});
+		target.postMessage(meta_str, '*');
+		// Do ajax request for image's href value
+		$.get(href, function(data) {
+			data = '|' + href + '|' + data;
+			// This is where the magic happens!
+			target.postMessage(data, '*');
+			
+		}, 'html'); // 'html' is necessary to keep returned data as a string
+	}
+	return false;
+});
+
+</script>
+</body>
+</html>

+ 0 - 6
main/inc/lib/svg-edit/extensions/imagelib/smiley.svg

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 11 - 0
main/inc/lib/svg-edit/extensions/markers-icons.xml

@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+  <g id="tool_mathjax">
+    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 43 43">
+      <g id="svg_20" stroke="black" fill="black" transform="matrix(0.0510725, 0, 0, -0.0510725, 0, 37.4108)">
+        <path id="svg_21" stroke-width="10" d="m20.45372,122.639297q0,57 19,104t49,78.000015t67,52t70,31t63.000015,9l3,0q45,0 78,-22t33,-65q0,-90.000015 -111,-118.000015q-49.000015,-13 -134.000015,-14q-37,0 -38,-2q0,-2 -6,-35t-7,-58q0,-46.999996 21,-73.999996t63,-28t93.000015,19t92,65.999996q9,10 12,10q4,0 13,-9t10,-13.999996t-9,-16t-30,-27t-46,-31t-63,-25t-76.000015,-10q-79,0 -122,53t-44,125.999996zm334.000015,185.000015q-6,52 -68,52q-33.000015,0 -61.000015,-14t-45,-34t-29,-41t-16,-36.000015t-5,-19q0,-1 20,-1q113.000015,0 158.000015,24t46,69.000015z"/>
+        <path id="svg_22" stroke-width="10" d="m489.156433,571.236389q4.949707,29.698486 38.183716,68.589355t82.024414,39.597961q24.748657,0 45.254761,-12.727905t30.40564,-31.819824q29.698486,44.547729 71.417847,44.547729q26.162842,0 45.254761,-15.556335t19.79895,-41.719299q0,-20.506104 -9.899414,-33.234009t-19.091919,-15.556396t-16.263428,-2.82843q-13.435059,0 -21.920288,7.778198t-8.485352,20.506104q0,32.526917 35.355347,44.547729q-7.778198,9.192383 -28.284302,9.192383q-9.192383,0 -13.434937,-1.414246q-26.870117,-11.31366 -39.598022,-46.669006q-42.426392,-156.270599 -42.426392,-182.433502q0,-19.799011 11.313721,-28.284302t24.748657,-8.485291q26.162964,0 51.618896,23.334534t34.648193,57.275635q2.121338,7.071106 4.242676,7.778198t11.313721,1.414185l2.828369,0q10.606567,0 10.606567,-5.65686q0,-0.707092 -1.414185,-7.778137q-11.313721,-40.305115 -43.840576,-71.417786t-75.6604,-31.112732q-49.497559,0 -74.953369,44.547729q-28.991333,-43.840576 -66.468018,-43.840576l-4.242676,0q-34.648193,0 -49.497437,18.384766t-15.556396,38.890869q0,22.62738 13.435059,36.769531t31.819824,14.142151q30.405518,0 30.405518,-29.698486q0,-14.142151 -8.485229,-24.748718t-16.263428,-14.142151t-9.192383,-3.535522l-2.121338,-0.707153q0,-0.707092 4.242676,-2.828369t11.313599,-4.949768t13.435059,-2.121338q25.455811,0 43.840698,31.819824q6.363892,11.313721 16.263428,48.083252t19.79895,76.367523t11.313721,46.669006q3.535522,19.091919 3.535522,27.577209q0,19.79895 -10.606567,28.284241t-24.041626,8.485291q-28.284302,0 -53.033081,-22.627441t-34.648193,-57.982727q-1.414185,-6.363953 -3.535522,-7.071106t-11.313721,-1.414185l-9.899536,0q-4.242554,4.242615 -4.242554,7.778198z"/>
+      </g>
+	  </svg>
+  </g>
+  <g id="svg_eof"/>
+</svg>

+ 14 - 0
main/inc/lib/svg-edit/extensions/polygon-icons.svg

@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+	<g id="tool_polygon">
+     <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-1 -2 25 24">
+     	<defs>
+		<linearGradient y2="1" x2="1" y1="0.10156" x1="0.36328" id="svg_i22">
+			<stop stop-opacity="1" stop-color="#ffffff" offset="0"/>
+			<stop stop-opacity="1" stop-color="#666666" offset="1"/>
+		</linearGradient>
+		</defs>
+		<polygon points="6.09251,19.6032 0.577744,10.0516 6.09251,0.5 17.1217,0.5 22.6365,10.0516 17.1217,19.6032" stroke="#000000" fill="url(#svg_i22)"/>	 </svg>
+	</g>
+	
+	<g id="svg_eof"/>
+</svg>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 5 - 0
main/inc/lib/svg-edit/extensions/saved.svg


+ 16 - 0
main/inc/lib/svg-edit/extensions/savefile.php

@@ -0,0 +1,16 @@
+<?php
+	// You must first create a file "savefile_config.php" in this extensions directory and do whatever
+	//   checking of user credentials, etc. that you wish; otherwise anyone will be able to post SVG
+	//   files to your server which may cause disk space or possibly security problems
+  require('savefile_config.php');
+  if (!isset($_POST['output_svg'])) {
+		print "You must supply output_svg";
+		exit;
+	}
+	$svg = $_POST['output_svg'];
+	$filename = (isset($_POST['filename']) && !empty($_POST['filename']) ? preg_replace('@[\\\\/:*?"<>|]@u', '_', $_POST['filename']) : 'saved') . '.svg'; // These characters are indicated as prohibited by Windows
+
+	$fh = fopen($filename, 'w') or die("Can't open file");
+	fwrite($fh, $svg);
+	fclose($fh);
+?>

+ 166 - 0
main/inc/lib/svg-edit/extensions/savefile_config.php

@@ -0,0 +1,166 @@
+<?php
+/*
+ * filesave.php
+ * To be used with ext-server_opensave.js for SVG-edit
+ *
+ * Licensed under the Apache License, Version 2
+ *
+ * Copyright(c) 2010 Alexis Deveria
+ *
+ * Integrate svg-edit with Chamilo
+ * @author Juan Carlos Raña Trabado
+ * @since 25/september/2010
+*/
+
+$language_file = array('document');//Chamilo load lang var
+
+//Chamilo load libraries
+require_once '../../../../inc/global.inc.php';
+require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
+require_once api_get_path(LIBRARY_PATH).'document.lib.php';
+
+//Add security from Chamilo
+api_protect_course_script();
+api_block_anonymous_users();
+
+if(!isset($_POST['output_svg']) && !isset($_POST['output_png'])) {
+    api_not_allowed();//from Chamilo
+    die();
+}
+
+$file = '';
+$suffix = isset($_POST['output_svg'])?'svg':'png';
+
+if(isset($_POST['filename']) && strlen($_POST['filename']) > 0) {
+    $file = $_POST['filename'];
+} else {
+    $file = 'image';
+}
+
+if($suffix == 'svg') {
+    $mime = 'image/svg+xml';
+    $contents = rawurldecode($_POST['output_svg']);
+} else {
+    $mime = 'image/png';
+    $contents = $_POST['output_png'];
+    $pos = (strpos($contents, 'base64,') + 7);
+    $contents = base64_decode(substr($contents, $pos));
+}
+
+//get SVG-Edit values
+$filename=$file;//from svg-edit
+$extension=$suffix;// from svg-edit
+$content=$contents;//from svg-edit
+
+$title = Database::escape_string(str_replace('_',' ',$filename));
+
+//get Chamilo variables
+
+if(!isset($_SESSION['draw_dir']) && !isset($_SESSION['whereami'])) {
+    api_not_allowed();//from Chamilo
+    die();
+}
+
+$current_session_id = api_get_session_id();
+$groupId    =  api_get_group_id();
+$relativeUrlPath=$_SESSION['draw_dir'];
+$currentTool=$_SESSION['whereami'];
+$dirBaseDocuments = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
+$saveDir=$dirBaseDocuments.$_SESSION['draw_dir'];
+
+//a bit title security
+
+$filename = addslashes(trim($filename));
+$filename = Security::remove_XSS($filename);
+$filename = replace_dangerous_char($filename, 'strict');
+$filename = disable_dangerous_file($filename);
+
+// a bit extension
+if ($suffix != 'svg' && $suffix != 'png') {
+    die();
+}
+
+//a bit mime security
+//comment because finfo seems stopping the save process files in some php vers.
+/*
+if (phpversion() >= '5.3' && extension_loaded('fileinfo')) {
+	$finfo = new finfo(FILEINFO_MIME);
+	$current_mime=$finfo->buffer($contents);
+	finfo_close($finfo);
+	$mime_png='image/png';//svg-edit return image/png; charset=binary
+	$mime_svg='image/svg+xml';
+	$mime_xml='application/xml';//hack for svg-edit because original code return application/xml; charset=us-ascii. See
+	if(strpos($current_mime, $mime_png)===false && $extension=='png') {
+		die();//File extension does not match its content
+	} elseif(strpos($current_mime, $mime_svg)===false && strpos($current_mime, $mime_xml)===false && $extension=='svg') {
+		die();//File extension does not match its content
+	}
+}
+*/
+
+//checks if the file exists, then rename the new
+if (file_exists($saveDir.'/'.$filename.$i.'.'.$extension) && $currentTool=='document/createdraw') {
+    $message = get_lang('FileExistsChangeToSave');
+    $params = array(
+        'message' => $message,
+        'url' => ''
+    );
+    echo json_encode($params);
+    exit;
+} else {
+    $drawFileName = $filename.'.'.$extension;
+    $title = $title.'.'.$extension;
+}
+
+$documentPath = $saveDir.'/'.$drawFileName;
+
+//add new document to disk
+file_put_contents( $documentPath, $contents );
+
+if ($currentTool=='document/createdraw') {
+    //add document to database
+    $doc_id = add_document($_course, $relativeUrlPath.'/'.$drawFileName, 'file', filesize($documentPath), $title);
+    api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'DocumentAdded', $_user['user_id'], $groupId, null, null, null, $current_session_id);
+
+} elseif($currentTool=='document/editdraw') {
+
+    //check path
+    if (!isset($_SESSION['draw_file'])){
+        api_not_allowed();//from Chamilo
+        die();
+    }
+    if ($_SESSION['draw_file'] == $drawFileName ){
+        $document_id = DocumentManager::get_document_id($_course, $relativeUrlPath.'/'.$drawFileName);
+        update_existing_document($_course, $document_id, filesize($documentPath), null);
+        api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentUpdated', $_user['user_id'], $groupId, null, null, null, $current_session_id);
+    } else {
+        //add a new document
+        $doc_id = add_document($_course, $relativeUrlPath.'/'.$drawFileName, 'file', filesize($documentPath), $title);
+        api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'DocumentAdded', $_user['user_id'], $groupId, null, null, null, $current_session_id);
+    }
+}
+
+//clean sessions and add messages and return to current document list
+unset($_SESSION['draw_dir']);
+unset($_SESSION['draw_file']);
+unset($_SESSION['whereami']);
+
+if ($suffix != 'png') {
+    if ($relativeUrlPath == '') {
+        $relativeUrlPath = '/';
+    };
+    $url = api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq().'&curdirpath='.urlencode($relativeUrlPath);
+    $message = get_lang('FileSavedAs').': '.$title;
+    //echo 'alert("'.get_lang('FileSavedAs').': '.$title.'");';
+    //echo 'window.top.location.href="'.$interbreadcrumb.'";';//return to current document list
+} else {
+    $url = '';
+    $message = get_lang('FileExportAs').': '.$title;
+}
+
+$params = array(
+    'message' => $message,
+    'url' => $url
+);
+echo json_encode($params);
+exit;

+ 0 - 6
main/inc/lib/svg-edit/extensions/shapelib/animal.json

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 0 - 1
main/inc/lib/svg-edit/extensions/shapelib/math.json

@@ -5,7 +5,6 @@
 	"car_smart": "m28.92024,238.37814c-20.54175,-8.15092 -27.36674,-32.3504 -27.6178,-52.52942c-2.7836,-14.94118 14.71887,-25.93048 8.95686,-41.2937c-2.18657,-22.71175 4.68564,-45.20703 14.9548,-65.22043c0.32475,-12.85873 10.87969,-17.84956 22.37955,-17.11762c37.15603,-4.71741 74.98359,-4.84966 112.15372,-0.39277c22.18198,4.11681 39.85953,19.32546 58.61859,30.81274c11.63586,8.63808 25.21985,14.32888 36.92355,22.63131c12.64026,10.62529 24.51556,22.47708 33.27448,36.57193c3.15143,14.09033 7.38165,28.09947 9.54742,42.2829c4.39661,13.27223 -4.6037,21.62047 -11.41214,30.84103c-10.34,12.57306 -29.90723,17.53416 -44.71561,10.78313c-10.28528,-3.75465 -14.07077,-19.70746 -25.7968,-18.60466c-44.20906,-0.65964 -88.41812,-1.31926 -132.62719,-1.97884c-10.24168,13.44736 -24.40165,28.14365 -42.91705,26.05086c-4.02312,-0.33679 -7.97831,-1.33774 -11.7224,-2.83646z",
 	"car": "m26.77284,183.48201c-8.08779,-6.80998 -23.25337,0.56996 -25.08215,-11.86069c-3.52016,-11.04616 7.23365,-19.6761 14.38957,-26.36473c12.51913,-6.23676 26.95447,-6.99092 40.49323,-9.84883c16.2238,-2.35439 33.11552,-4.36668 47.10119,-13.71207c15.9294,-9.9651 33.67083,-18.25834 52.86141,-18.11141c26.44141,-0.91634 53.7986,-0.02856 78.2453,11.26058c15.36688,6.93465 31.87709,10.4591 48.27711,13.85143c13.95395,1.83385 10.70889,16.88237 15.35007,26.15973c3.54291,10.65329 -4.11594,21.25732 -15.32654,21.59605c-11.85318,2.69405 -25.72552,2.95921 -34.00009,13.30453c-8.47623,9.38277 -23.69647,9.05888 -32.32811,0.09547c-3.56866,-2.21568 -5.9512,-7.58008 -10.70335,-6.65411c-44.31184,0.24008 -88.64653,-0.63266 -132.93902,0.78894c-11.57394,-0.92139 -16.38131,13.44077 -28.69251,12.44249c-6.30352,0.19554 -12.27285,-9.98318 -17.64611,-12.94739z",
 	"coud": "m193.50864,67.27344c-28.56444,0 -53.08249,14.81322 -65.08365,36.1982c-4.7932,-2.67596 -10.08009,-4.29625 -15.72245,-4.29625c-17.06268,0 -31.26126,13.72618 -35.64974,32.35901c-7.49058,-2.96072 -16.20005,-4.7533 -25.50328,-4.7533c-27.90971,0 -50.54951,15.27039 -50.54951,34.09579c0,16.99312 18.56764,30.96303 42.68829,33.54732c-1.08106,1.83421 -1.91961,3.65059 -1.91961,5.57599c0,18.02431 49.38134,32.72462 110.23999,32.72462c60.85864,0 110.24005,-14.70032 110.23999,-32.72462c0,-1.21274 -0.75272,-2.29501 -1.18832,-3.47357c2.66562,0.6629 5.30496,1.37115 8.22687,1.37115c16.28061,0.00005 29.5253,-11.06223 29.5253,-24.6806c0,-13.61838 -13.24469,-24.68059 -29.5253,-24.68059c-1.99515,0 -3.69165,0.68915 -5.57599,1.00551c1.64767,-5.44531 2.74228,-11.00153 2.74228,-16.91078c0,-36.04859 -32.63087,-65.35786 -72.94485,-65.35786z",
-	"drop": "m101.50413,119.73441c-169.70167,149.53423 29.55369,277.04852 105.31838,71.70643l58.26123,-190.46983l-163.57961,118.7634z",
 	"drop": "m115.15536,295.759c-42.01334,-15.78687 -72.12711,-65.94934 -65.28346,-108.74701c4.3154,-26.98718 95.35947,-190.81818 103.3105,-185.90417c2.59511,1.60386 25.68835,39.79974 51.31831,84.87975c41.0565,72.21342 46.5999,85.67899 46.5999,113.19665c0,55.77716 -44.6394,101.46498 -98.23825,100.54555c-15.6409,-0.26834 -32.60906,-2.05518 -37.707,-3.97076zm42.09262,-28.05386c1.39066,-7.22116 -1.85785,-10.74289 -9.90955,-10.74289c-18.35065,0 -43.80598,-23.24161 -49.49309,-45.18889c-6.0666,-23.41179 -22.15186,-26.19615 -24.52774,-4.24574c-4.57746,42.29059 76.21872,100.22086 83.93037,60.17752z",
 	"electric_guitar": "m168.96899,1.09303c-2.62799,0.19977 -5.017,2.04496 -6.96899,4.55579c0.009,13.46278 -11.116,25.03858 -14.875,34.35928c2.30499,1.8475 1.13,2.9332 -1.03101,4.4286c-0.17,2.5165 0.468,4.9685 2.468,7.3554c-2.591,41.8755 -6.024,92.17889 -8.718,134.4599c6.14801,0.138 12.354,1.117 18.81201,0.687c-0.007,-32.55499 2.539,-84.562 3.282,-116.00749c-0.101,-10.2094 -0.504,-24.029 13.12399,-28.8618c-6.94499,-14.7833 5.922,-35.70095 -5.562,-40.97666c-0.146,-0.00122 -0.293,-0.00885 -0.438,0c-0.03099,0.00187 -0.062,-0.00236 -0.093,0l0,-0.00001zm11.692,171.76396c-4.715,6.66701 -11.79199,10.17 -14.146,13.60501c-5.37599,8.46799 -14.379,-3.16701 -26.89,2.99599c0.065,-1.02399 0.153,-2.172 0.21899,-3.20599c-0.07199,-0.002 -0.14699,0.00101 -0.21899,0c-6.659,1.013 -12.157,-7.713 -12.594,-15.44901c5.55199,-11.89 -14.242,-18.006 -19.5,-5.82899c-3.943,13.605 -0.41,27.51199 4.78201,40.672c5.933,16.162 -8.871,30.179 -13.2192,45.17601c-4.1375,9.502 -4.2709,20.46999 2.68719,29.192c9.486,13.58398 29.413,18.62799 47.563,18.758c3.23401,0.224 6.44801,0.414 9.68701,0.58499c57.61699,3.284 34.35399,-35.13699 25.282,-68.41299c-5.82201,-21.66299 1.48199,-36.38 6.269,-42.614c2.659,-7.35001 6.02899,-23 -9.92101,-15.472l0,-0.00101zm-34.567,-128.4203c2.43501,-11.918 -8.10399,0.709 0,0z",
 	"guitar": "m158.45264,1.00012c-0.96039,0.00612 -1.8884,0.06411 -2.73161,0.18457c-4.49716,0.64245 -8.99768,2.5655 -8.99768,2.5655c0,0 0.00569,8.99612 -0.63676,15.42064c-0.64244,6.42453 -3.21147,17.34015 -3.21147,17.34015l3.15611,6.32144l0.05536,-1.172l10.62189,1.86414l0.94128,0.05537c0,0 1.28059,-1.28413 2.56551,-3.21148c1.2849,-1.92736 3.85747,-3.21148 3.85747,-3.21148c0,0 0.63675,-9.63641 0.63675,-16.70339c0,-7.06698 2.57472,-18.63212 2.57472,-18.63212c0,0 -4.66991,-0.84783 -8.83156,-0.82133zm-1.74417,42.52442l-10.62189,-0.58139l-0.05536,-0.11074l-5.5278,113.65688c0.19376,0.03734 0.44296,0.08304 0.44296,0.08304c0.14801,2.18161 0.8121,3.84209 1.8549,5.14021l11.84926,5.41707c0.3562,0.09476 0.71249,0.18806 1.07048,0.28609l1.28276,-123.83578l-0.29532,-0.05537zm-0.98744,123.89115l0,0.20302l-1.07048,-0.48911c-4.80661,-1.27875 -9.3821,-2.34575 -11.84926,-5.41707l-2.5009,-1.14432l0.20303,-4.07893c-2.16553,-0.41753 -15.40038,-2.83867 -20.1179,-0.47989c-5.13962,2.56979 -8.99552,6.42279 -10.28043,14.77466c-1.2849,8.3519 2.57119,23.77351 1.92873,34.05275c-0.64246,10.27924 -7.71416,19.91469 -10.92641,28.26657c-3.21227,8.35188 -6.4202,17.99007 -2.5655,32.12404c3.85472,14.13391 17.99164,23.77151 25.70107,26.9838c7.70942,3.21225 23.7631,5.14023 23.7631,5.14023c0,0 15.42752,2.58041 25.06432,1.93796c20.23672,-3.64624 28.72404,-15.65756 29.98303,-30.66595c1.259,-15.00839 -4.71521,-33.01251 -13.40884,-48.65207c-9.50552,-17.10013 8.59656,-39.76326 -0.76596,-52.52802c-9.74182,-13.28188 -16.9409,8.64334 -23.78156,3.87593c-2.77655,-1.93504 -6.09895,-3.00655 -9.37605,-3.90359z",

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
main/inc/lib/svg-edit/extensions/shapelib/raphael.txt


+ 14 - 0
main/inc/lib/svg-edit/extensions/shapelib/raphael_2.json

@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+	<g id="tool_star">
+     <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-1 -1 26 26">
+     	<defs>
+		<linearGradient y2="1" x2="1" y1="0.10156" x1="0.36328" id="svg_i22">
+			<stop stop-opacity="1" stop-color="#ffffff" offset="0"/>
+			<stop stop-opacity="1" stop-color="#666666" offset="1"/>
+		</linearGradient>
+		</defs>
+		<polygon points="12.4062,1.55891 15.0578,9.39339 23.3283,9.49422 16.6965,14.4371 19.1564,22.3338 12.4062,17.5543 5.65596,22.3338 8.11588,14.4371 1.48406,9.49422 9.75455,9.39339" stroke="#000000" fill="url(#svg_i22)"/>	 </svg>
+	</g>
+	
+	<g id="svg_eof"/>
+</svg>

+ 23 - 0
main/inc/lib/svg-edit/extensions/webappfind-icon.svg

@@ -0,0 +1,23 @@
+<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
+<!--
+Copied from /editor/images/svg_edit_icons.svg#save but with id renamed, first stop-color changed to red, and SVG namespaces moved to root
+-->
+<g id="webappfind_save">
+	<svg viewBox="0 0 24 24">
+	 <defs>
+	  <linearGradient y2="0" x2="1" y1="0" x1="0" id="svg_41">
+	   <stop stop-opacity="1" stop-color="red" offset="0"/>
+	   <stop stop-opacity="1" stop-color="#d6d6d6" offset="1"/>
+	  </linearGradient>
+	  <linearGradient y2="0.875" x2="0.21484" y1="0.00391" x1="0.04297" id="svg_46">
+	   <stop stop-opacity="1" stop-color="#81bbf4" offset="0"/>
+	   <stop stop-opacity="1" stop-color="#376eb7" offset="1"/>
+	  </linearGradient>
+	 </defs>
+	   <path stroke="#202020" fill="#e0e0e0" id="svg_21" d="m1.51669,22.3458l21.13245,-0.10111l0,-6.06673l-2.62892,-9.80789l-16.27907,0.10111l-2.32558,9.20121l0.10111,6.67341z"/>
+	   <rect stroke="#efefef" fill="url(#svg_41)" id="svg_32" height="4.75108" width="19.21031" y="16.58227" x="2.42667"/>
+	   <path stroke="#ffffff" fill="#c0c0c0" id="svg_42" d="m4.55005,11.12235l0.70779,-2.83114l13.04348,0l0.70779,3.13448c-0.70779,2.52781 -4.04479,3.84227 -7.17897,3.84227c-2.72977,0 -6.37007,-1.41557 -7.28008,-4.1456z"/>
+	  <path stroke="#285582" fill="url(#svg_46)" id="svg_45" d="m7.14286,9.74903l5.21236,5.79151l5.50193,-5.88803l-2.50965,-0.09653l0,-2.79923c0,-2.3166 -2.3166,-5.59846 -6.56371,-5.59846c-4.05405,0 -6.27413,3.37838 -6.56371,6.75676c0.48263,-1.5444 2.7027,-4.53668 4.44015,-4.44015c2.12355,-0.09653 2.79923,1.64093 2.79923,3.37838l0.09653,2.79923l-2.41313,0.09653z"/>
+	</svg>
+</g>
+</svg>

+ 92 - 80
main/inc/lib/svg-edit/history.js

@@ -1,7 +1,9 @@
+/*globals svgedit*/
+/*jslint vars: true, eqeq: true, continue: true, forin: true*/
 /**
  * Package: svedit.history
  *
- * Licensed under the Apache License, Version 2
+ * Licensed under the MIT License
  *
  * Copyright(c) 2010 Jeff Schiller
  */
@@ -11,17 +13,13 @@
 // 2) svgtransformlist.js
 // 3) svgutils.js
 
-var svgedit = svgedit || {};
-
-(function() {
+(function() {'use strict';
 
 if (!svgedit.history) {
 	svgedit.history = {};
 }
 
 // Group: Undo/Redo history management
-
-
 svgedit.history.HistoryEventTypes = {
 	BEFORE_APPLY: 'before_apply',
 	AFTER_APPLY: 'after_apply',
@@ -32,10 +30,9 @@ svgedit.history.HistoryEventTypes = {
 var removedElements = {};
 
 /**
- * Interface: svgedit.history.HistoryCommand
  * An interface that all command objects must implement.
- *
- * interface svgedit.history.HistoryCommand {
+ * @typedef svgedit.history.HistoryCommand
+ * @type {object}
  *   void apply(svgedit.history.HistoryEventHandler);
  *   void unapply(svgedit.history.HistoryEventHandler);
  *   Element[] elements();
@@ -55,15 +52,15 @@ var removedElements = {};
  * command is an object fulfilling the HistoryCommand interface.
  */
 
-// Class: svgedit.history.MoveElementCommand
-// implements svgedit.history.HistoryCommand
-// History command for an element that had its DOM position changed
-//
-// Parameters:
-// elem - The DOM element that was moved
-// oldNextSibling - The element's next sibling before it was moved
-// oldParent - The element's parent before it was moved
-// text - An optional string visible to user related to this change
+/**
+ * @class svgedit.history.MoveElementCommand
+ * @implements svgedit.history.HistoryCommand
+ * History command for an element that had its DOM position changed
+ * @param {Element} elem - The DOM element that was moved
+ * @param {Element} oldNextSibling - The element's next sibling before it was moved
+ * @param {Element} oldParent - The element's parent before it was moved
+ * @param {string} [text] - An optional string visible to user related to this change
+*/
 svgedit.history.MoveElementCommand = function(elem, oldNextSibling, oldParent, text) {
 	this.elem = elem;
 	this.text = text ? ("Move " + elem.tagName + " to " + text) : ("Move " + elem.tagName);
@@ -72,16 +69,17 @@ svgedit.history.MoveElementCommand = function(elem, oldNextSibling, oldParent, t
 	this.newNextSibling = elem.nextSibling;
 	this.newParent = elem.parentNode;
 };
-svgedit.history.MoveElementCommand.type = function() { return 'svgedit.history.MoveElementCommand'; }
+svgedit.history.MoveElementCommand.type = function() { return 'svgedit.history.MoveElementCommand'; };
 svgedit.history.MoveElementCommand.prototype.type = svgedit.history.MoveElementCommand.type;
 
-// Function: svgedit.history.MoveElementCommand.getText
 svgedit.history.MoveElementCommand.prototype.getText = function() {
 	return this.text;
 };
 
-// Function: svgedit.history.MoveElementCommand.apply
-// Re-positions the element
+/**
+ * Re-positions the element
+ * @param {handleHistoryEvent: function}
+*/
 svgedit.history.MoveElementCommand.prototype.apply = function(handler) {
 	// TODO(codedread): Refactor this common event code into a base HistoryCommand class.
 	if (handler) {
@@ -95,13 +93,15 @@ svgedit.history.MoveElementCommand.prototype.apply = function(handler) {
 	}
 };
 
-// Function: svgedit.history.MoveElementCommand.unapply
-// Positions the element back to its original location
+/**
+ * Positions the element back to its original location
+ * @param {handleHistoryEvent: function}
+*/
 svgedit.history.MoveElementCommand.prototype.unapply = function(handler) {
 	if (handler) {
 		handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
 	}
-		
+
 	this.elem = this.oldParent.insertBefore(this.elem, this.oldNextSibling);
 
 	if (handler) {
@@ -129,7 +129,7 @@ svgedit.history.InsertElementCommand = function(elem, text) {
 	this.parent = elem.parentNode;
 	this.nextSibling = this.elem.nextSibling;
 };
-svgedit.history.InsertElementCommand.type = function() { return 'svgedit.history.InsertElementCommand'; }
+svgedit.history.InsertElementCommand.type = function() { return 'svgedit.history.InsertElementCommand'; };
 svgedit.history.InsertElementCommand.prototype.type = svgedit.history.InsertElementCommand.type;
 
 // Function: svgedit.history.InsertElementCommand.getText
@@ -139,12 +139,12 @@ svgedit.history.InsertElementCommand.prototype.getText = function() {
 
 // Function: svgedit.history.InsertElementCommand.apply
 // Re-Inserts the new element
-svgedit.history.InsertElementCommand.prototype.apply = function(handler) { 
+svgedit.history.InsertElementCommand.prototype.apply = function(handler) {
 	if (handler) {
 		handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
 	}
 
-	this.elem = this.parent.insertBefore(this.elem, this.nextSibling); 
+	this.elem = this.parent.insertBefore(this.elem, this.nextSibling);
 
 	if (handler) {
 		handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
@@ -191,7 +191,7 @@ svgedit.history.RemoveElementCommand = function(elem, oldNextSibling, oldParent,
 	// special hack for webkit: remove this element's entry in the svgTransformLists map
 	svgedit.transformlist.removeElementFromListMap(elem);
 };
-svgedit.history.RemoveElementCommand.type = function() { return 'svgedit.history.RemoveElementCommand'; }
+svgedit.history.RemoveElementCommand.type = function() { return 'svgedit.history.RemoveElementCommand'; };
 svgedit.history.RemoveElementCommand.prototype.type = svgedit.history.RemoveElementCommand.type;
 
 // Function: svgedit.history.RemoveElementCommand.getText
@@ -201,7 +201,7 @@ svgedit.history.RemoveElementCommand.prototype.getText = function() {
 
 // Function: RemoveElementCommand.apply
 // Re-removes the new element
-svgedit.history.RemoveElementCommand.prototype.apply = function(handler) {	
+svgedit.history.RemoveElementCommand.prototype.apply = function(handler) {
 	if (handler) {
 		handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
 	}
@@ -217,14 +217,16 @@ svgedit.history.RemoveElementCommand.prototype.apply = function(handler) {
 
 // Function: RemoveElementCommand.unapply
 // Re-adds the new element
-svgedit.history.RemoveElementCommand.prototype.unapply = function(handler) { 
+svgedit.history.RemoveElementCommand.prototype.unapply = function(handler) {
 	if (handler) {
 		handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
 	}
 
 	svgedit.transformlist.removeElementFromListMap(this.elem);
-	if(this.nextSibling == null) {
-		if(window.console) console.log('Error: reference element was lost');
+	if (this.nextSibling == null) {
+		if (window.console) {
+            console.log('Error: reference element was lost');
+        }
 	}
 	this.parent.insertBefore(this.elem, this.nextSibling);
 
@@ -243,7 +245,7 @@ svgedit.history.RemoveElementCommand.prototype.elements = function() {
 
 // Class: svgedit.history.ChangeElementCommand
 // implements svgedit.history.HistoryCommand
-// History command to make a change to an element. 
+// History command to make a change to an element.
 // Usually an attribute change, but can also be textcontent.
 //
 // Parameters:
@@ -255,13 +257,14 @@ svgedit.history.ChangeElementCommand = function(elem, attrs, text) {
 	this.text = text ? ("Change " + elem.tagName + " " + text) : ("Change " + elem.tagName);
 	this.newValues = {};
 	this.oldValues = attrs;
-	for (var attr in attrs) {
-		if (attr == "#text") this.newValues[attr] = elem.textContent;
-		else if (attr == "#href") this.newValues[attr] = svgedit.utilities.getHref(elem);
-		else this.newValues[attr] = elem.getAttribute(attr);
+	var attr;
+	for (attr in attrs) {
+		if (attr == "#text") {this.newValues[attr] = elem.textContent;}
+		else if (attr == "#href") {this.newValues[attr] = svgedit.utilities.getHref(elem);}
+		else {this.newValues[attr] = elem.getAttribute(attr);}
 	}
 };
-svgedit.history.ChangeElementCommand.type = function() { return 'svgedit.history.ChangeElementCommand'; }
+svgedit.history.ChangeElementCommand.type = function() { return 'svgedit.history.ChangeElementCommand'; };
 svgedit.history.ChangeElementCommand.prototype.type = svgedit.history.ChangeElementCommand.type;
 
 // Function: svgedit.history.ChangeElementCommand.getText
@@ -277,11 +280,12 @@ svgedit.history.ChangeElementCommand.prototype.apply = function(handler) {
 	}
 
 	var bChangedTransform = false;
-	for(var attr in this.newValues ) {
+	var attr;
+	for (attr in this.newValues ) {
 		if (this.newValues[attr]) {
-			if (attr == "#text") this.elem.textContent = this.newValues[attr];
-			else if (attr == "#href") svgedit.utilities.setHref(this.elem, this.newValues[attr])
-			else this.elem.setAttribute(attr, this.newValues[attr]);
+			if (attr == "#text") {this.elem.textContent = this.newValues[attr];}
+			else if (attr == "#href") {svgedit.utilities.setHref(this.elem, this.newValues[attr]);}
+			else {this.elem.setAttribute(attr, this.newValues[attr]);}
 		}
 		else {
 			if (attr == "#text") {
@@ -297,9 +301,11 @@ svgedit.history.ChangeElementCommand.prototype.apply = function(handler) {
 	}
 
 	// relocate rotational transform, if necessary
-	if(!bChangedTransform) {
+	if (!bChangedTransform) {
 		var angle = svgedit.utilities.getRotationAngle(this.elem);
 		if (angle) {
+			// TODO: These instances of elem either need to be declared as global
+			//				(which would not be good for conflicts) or declare/use this.elem
 			var bbox = elem.getBBox();
 			var cx = bbox.x + bbox.width/2,
 				cy = bbox.y + bbox.height/2;
@@ -325,22 +331,25 @@ svgedit.history.ChangeElementCommand.prototype.unapply = function(handler) {
 	}
 
 	var bChangedTransform = false;
-	for(var attr in this.oldValues ) {
+	var attr;
+	for (attr in this.oldValues ) {
 		if (this.oldValues[attr]) {
-			if (attr == "#text") this.elem.textContent = this.oldValues[attr];
-			else if (attr == "#href") svgedit.utilities.setHref(this.elem, this.oldValues[attr]);
-			else this.elem.setAttribute(attr, this.oldValues[attr]);
+			if (attr == "#text") {this.elem.textContent = this.oldValues[attr];}
+			else if (attr == "#href") {svgedit.utilities.setHref(this.elem, this.oldValues[attr]);}
+			else {
+				this.elem.setAttribute(attr, this.oldValues[attr]);
+			}
 		}
 		else {
 			if (attr == "#text") {
 				this.elem.textContent = "";
 			}
-			else this.elem.removeAttribute(attr);
+			else {this.elem.removeAttribute(attr);}
 		}
 		if (attr == "transform") { bChangedTransform = true; }
 	}
 	// relocate rotational transform, if necessary
-	if(!bChangedTransform) {
+	if (!bChangedTransform) {
 		var angle = svgedit.utilities.getRotationAngle(this.elem);
 		if (angle) {
 			var bbox = elem.getBBox();
@@ -385,7 +394,7 @@ svgedit.history.BatchCommand = function(text) {
 	this.text = text || "Batch Command";
 	this.stack = [];
 };
-svgedit.history.BatchCommand.type = function() { return 'svgedit.history.BatchCommand'; }
+svgedit.history.BatchCommand.type = function() { return 'svgedit.history.BatchCommand'; };
 svgedit.history.BatchCommand.prototype.type = svgedit.history.BatchCommand.type;
 
 // Function: svgedit.history.BatchCommand.getText
@@ -400,8 +409,9 @@ svgedit.history.BatchCommand.prototype.apply = function(handler) {
 		handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
 	}
 
-	var len = this.stack.length;
-	for (var i = 0; i < len; ++i) {
+	var i,
+		len = this.stack.length;
+	for (i = 0; i < len; ++i) {
 		this.stack[i].apply(handler);
 	}
 
@@ -417,7 +427,8 @@ svgedit.history.BatchCommand.prototype.unapply = function(handler) {
 		handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
 	}
 
-	for (var i = this.stack.length-1; i >= 0; i--) {
+	var i;
+	for (i = this.stack.length-1; i >= 0; i--) {
 		this.stack[i].unapply(handler);
 	}
 
@@ -435,7 +446,7 @@ svgedit.history.BatchCommand.prototype.elements = function() {
 		var thisElems = this.stack[cmd].elements();
 		var elem = thisElems.length;
 		while (elem--) {
-			if (elems.indexOf(thisElems[elem]) == -1) elems.push(thisElems[elem]);
+			if (elems.indexOf(thisElems[elem]) == -1) {elems.push(thisElems[elem]);}
 		}
 	}
 	return elems;
@@ -453,7 +464,7 @@ svgedit.history.BatchCommand.prototype.addSubCommand = function(cmd) {
 // Function: svgedit.history.BatchCommand.isEmpty
 // Returns a boolean indicating whether or not the batch command is empty
 svgedit.history.BatchCommand.prototype.isEmpty = function() {
-	return this.stack.length == 0;
+	return this.stack.length === 0;
 };
 
 
@@ -471,7 +482,7 @@ svgedit.history.UndoManager = function(historyEventHandler) {
 	this.undoChangeStackPointer = -1;
 	this.undoableChangeStack = [];
 };
-	
+
 // Function: svgedit.history.UndoManager.resetUndoStack
 // Resets the undo stack, effectively clearing the undo/redo history
 svgedit.history.UndoManager.prototype.resetUndoStack = function() {
@@ -480,30 +491,30 @@ svgedit.history.UndoManager.prototype.resetUndoStack = function() {
 };
 
 // Function: svgedit.history.UndoManager.getUndoStackSize
-// Returns: 
+// Returns:
 // Integer with the current size of the undo history stack
 svgedit.history.UndoManager.prototype.getUndoStackSize = function() {
 	return this.undoStackPointer;
 };
 
 // Function: svgedit.history.UndoManager.getRedoStackSize
-// Returns: 
+// Returns:
 // Integer with the current size of the redo history stack
 svgedit.history.UndoManager.prototype.getRedoStackSize = function() {
 	return this.undoStack.length - this.undoStackPointer;
 };
 
 // Function: svgedit.history.UndoManager.getNextUndoCommandText
-// Returns: 
+// Returns:
 // String associated with the next undo command
-svgedit.history.UndoManager.prototype.getNextUndoCommandText = function() { 
+svgedit.history.UndoManager.prototype.getNextUndoCommandText = function() {
 	return this.undoStackPointer > 0 ? this.undoStack[this.undoStackPointer-1].getText() : "";
 };
 
 // Function: svgedit.history.UndoManager.getNextRedoCommandText
-// Returns: 
+// Returns:
 // String associated with the next redo command
-svgedit.history.UndoManager.prototype.getNextRedoCommandText = function() { 
+svgedit.history.UndoManager.prototype.getNextRedoCommandText = function() {
 	return this.undoStackPointer < this.undoStack.length ? this.undoStack[this.undoStackPointer].getText() : "";
 };
 
@@ -516,7 +527,7 @@ svgedit.history.UndoManager.prototype.undo = function() {
 	}
 };
 
-// Function: svgedit.history.UndoManager.redo		
+// Function: svgedit.history.UndoManager.redo
 // Performs a redo step
 svgedit.history.UndoManager.prototype.redo = function() {
 	if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {
@@ -524,18 +535,18 @@ svgedit.history.UndoManager.prototype.redo = function() {
 		cmd.apply(this.handler_);
 	}
 };
-	
+
 // Function: svgedit.history.UndoManager.addCommandToHistory
 // Adds a command object to the undo history stack
 //
-// Parameters: 
+// Parameters:
 // cmd - The command object to add
 svgedit.history.UndoManager.prototype.addCommandToHistory = function(cmd) {
 	// FIXME: we MUST compress consecutive text changes to the same element
 	// (right now each keystroke is saved as a separate command that includes the
 	// entire text contents of the text element)
 	// TODO: consider limiting the history that we store here (need to do some slicing)
-	
+
 	// if our stack pointer is not at the end, then we have to remove
 	// all commands after the pointer and insert the new command
 	if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {
@@ -553,7 +564,7 @@ svgedit.history.UndoManager.prototype.addCommandToHistory = function(cmd) {
 // pop the elements and old values off the stack, gets the current values
 // from the DOM and uses all of these to construct the undo-able command.
 //
-// Parameters: 
+// Parameters:
 // attrName - The name of the attribute being changed
 // elems - Array of DOM elements being changed
 svgedit.history.UndoManager.prototype.beginUndoableChange = function(attrName, elems) {
@@ -562,13 +573,15 @@ svgedit.history.UndoManager.prototype.beginUndoableChange = function(attrName, e
 	var oldValues = new Array(i), elements = new Array(i);
 	while (i--) {
 		var elem = elems[i];
-		if (elem == null) continue;
+		if (elem == null) {continue;}
 		elements[i] = elem;
 		oldValues[i] = elem.getAttribute(attrName);
 	}
-	this.undoableChangeStack[p] = {'attrName': attrName,
-							'oldValues': oldValues,
-							'elements': elements};
+	this.undoableChangeStack[p] = {
+		'attrName': attrName,
+		'oldValues': oldValues,
+		'elements': elements
+	};
 };
 
 // Function: svgedit.history.UndoManager.finishUndoableChange
@@ -576,19 +589,19 @@ svgedit.history.UndoManager.prototype.beginUndoableChange = function(attrName, e
 // change since beginUndoableChange was called.  The command can then
 // be added to the command history
 //
-// Returns: 
+// Returns:
 // Batch command object with resulting changes
 svgedit.history.UndoManager.prototype.finishUndoableChange = function() {
 	var p = this.undoChangeStackPointer--;
 	var changeset = this.undoableChangeStack[p];
-	var i = changeset['elements'].length;
-	var attrName = changeset['attrName'];
+	var i = changeset.elements.length;
+	var attrName = changeset.attrName;
 	var batchCmd = new svgedit.history.BatchCommand("Change " + attrName);
 	while (i--) {
-		var elem = changeset['elements'][i];
-		if (elem == null) continue;
+		var elem = changeset.elements[i];
+		if (elem == null) {continue;}
 		var changes = {};
-		changes[attrName] = changeset['oldValues'][i];
+		changes[attrName] = changeset.oldValues[i];
 		if (changes[attrName] != elem.getAttribute(attrName)) {
 			batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes, attrName));
 		}
@@ -597,5 +610,4 @@ svgedit.history.UndoManager.prototype.finishUndoableChange = function() {
 	return batchCmd;
 };
 
-
-})();
+}());

+ 1 - 1
main/inc/lib/svg-edit/images/README.txt

@@ -49,7 +49,7 @@ rotate.png
 save.png
 select.png
 sep.png
-shape_group.png
+shape_group_elements.png
 shape_ungroup.png
 source.png
 square.png

+ 0 - 6
main/inc/lib/svg-edit/images/align-bottom.png

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

BIN=BIN
main/inc/lib/svg-edit/images/italic.png


+ 0 - 0
main/inc/lib/svg-edit/images/move_bottom.png → main/inc/lib/svg-edit/images/move_bottom.png


+ 1 - 3
main/inc/lib/svg-edit/images/shape_ungroup.png


+ 0 - 6
main/inc/lib/svg-edit/images/svg_edit_icons.svgz

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 1 - 1
main/inc/lib/svg-edit/jgraduate/css/jPicker.css

@@ -7,7 +7,7 @@
  * Copyright (c) 2010 Alexis Deveria
  * http://a.deveria.com/
  *
- * Licensed under the Apache License Version 2
+ * Licensed under the MIT License
  */
 
 h2.jGraduate_Title {

+ 0 - 6
main/inc/lib/svg-edit/jgraduate/images/AlphaBar.png

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 0 - 6
main/inc/lib/svg-edit/jgraduate/images/map-opacity.png

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1673 - 0
main/inc/lib/svg-edit/jgraduate/jpicker.js


+ 4 - 4
main/inc/lib/svg-edit/jgraduate/jpicker.min.js

@@ -228,7 +228,7 @@ jQuery.fn.jGraduate =
     	        		'<div class="jGraduate_Form_Section">' +
         	    			'<label>x:</label>' +
             				'<input type="text" id="' + id + '_jGraduate_x1" size="3" title="Enter starting x value between 0.0 and 1.0"/>' +
-            				'<label> y:</label>' +
+            				'<label>y:</label>' +
             				'<input type="text" id="' + id + '_jGraduate_y1" size="3" title="Enter starting y value between 0.0 and 1.0"/>' +
         	   			'</div>' +
         	   		'</div>' +
@@ -237,7 +237,7 @@ jQuery.fn.jGraduate =
     	        		'<div class="jGraduate_Form_Section">' +
 	    	        		'<label>x:</label>' +
 		    	        	'<input type="text" id="' + id + '_jGraduate_x2" size="3" title="Enter ending x value between 0.0 and 1.0"/>' +
-    		    	    	'<label> y:</label>' +
+    		    	    	'<label>y:</label>' +
         		    		'<input type="text" id="' + id + '_jGraduate_y2" size="3" title="Enter ending y value between 0.0 and 1.0"/>' +
     	    	    	'</div>' +
     	    	    '</div>' +
@@ -248,7 +248,7 @@ jQuery.fn.jGraduate =
 						'<div class="jGraduate_Form_Section">' +
 							'<label>x:</label>' +
 							'<input type="text" id="' + id + '_jGraduate_cx" size="3" title="Enter x value between 0.0 and 1.0"/>' +
-							'<label> y:</label>' +
+							'<label>y:</label>' +
 							'<input type="text" id="' + id + '_jGraduate_cy" size="3" title="Enter y value between 0.0 and 1.0"/>' +
 						'</div>' +
 					'</div>' +
@@ -258,7 +258,7 @@ jQuery.fn.jGraduate =
 							'<label>Match center: <input type="checkbox" checked="checked" id="' + id + '_jGraduate_match_ctr"/></label><br/>' +
 							'<label>x:</label>' +
 							'<input type="text" id="' + id + '_jGraduate_fx" size="3" title="Enter x value between 0.0 and 1.0"/>' +
-							'<label> y:</label>' +
+							'<label>y:</label>' +
 							'<input type="text" id="' + id + '_jGraduate_fy" size="3" title="Enter y value between 0.0 and 1.0"/>' +
 						'</div>' +
 					'</div>' +

+ 74 - 0
main/inc/lib/svg-edit/jgraduate/jquery.jgraduate.min.js

@@ -0,0 +1,74 @@
+/*globals $, jQuery */
+/*jslint vars: true */
+/**
+ * jQuery module to work with SVG.
+ *
+ * Licensed under the MIT License
+ *
+ */
+
+// Dependencies:
+// 1) jquery
+
+(function() {'use strict';
+
+  // This fixes $(...).attr() to work as expected with SVG elements.
+  // Does not currently use *AttributeNS() since we rarely need that.
+
+  // See http://api.jquery.com/attr/ for basic documentation of .attr()
+
+  // Additional functionality:
+  // - When getting attributes, a string that's a number is return as type number.
+  // - If an array is supplied as first parameter, multiple values are returned
+  // as an object with values for each given attributes
+
+  var proxied = jQuery.fn.attr,
+    // TODO use NS.SVG instead
+    svgns = "http://www.w3.org/2000/svg";
+  jQuery.fn.attr = function(key, value) {
+    var i, attr;
+	var len = this.length;
+    if (!len) {return proxied.apply(this, arguments);}
+    for (i = 0; i < len; ++i) {
+      var elem = this[i];
+      // set/get SVG attribute
+      if (elem.namespaceURI === svgns) {
+        // Setting attribute
+        if (value !== undefined) {
+          elem.setAttribute(key, value);
+        } else if ($.isArray(key)) {
+          // Getting attributes from array
+          var j = key.length, obj = {};
+
+          while (j--) {
+            var aname = key[j];
+            attr = elem.getAttribute(aname);
+            // This returns a number when appropriate
+            if (attr || attr === "0") {
+              attr = isNaN(attr) ? attr : (attr - 0);
+            }
+            obj[aname] = attr;
+          }
+          return obj;
+        }
+		if (typeof key === "object") {
+          // Setting attributes form object
+		  var v;
+          for (v in key) {
+            elem.setAttribute(v, key[v]);
+          }
+        // Getting attribute
+        } else {
+          attr = elem.getAttribute(key);
+          if (attr || attr === "0") {
+            attr = isNaN(attr) ? attr : (attr - 0);
+          }
+          return attr;
+        }
+      } else {
+        return proxied.apply(this, arguments);
+      }
+    }
+    return this;
+  };
+}());

+ 0 - 6
main/inc/lib/svg-edit/jquery-ui/index.html

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 17 - 0
main/inc/lib/svg-edit/jquery-ui/jquery-ui-1.8.17.custom.min.js


+ 0 - 6
main/inc/lib/svg-edit/jquery.js

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 0 - 6
main/inc/lib/svg-edit/jquerybbq/jquery.bbq.min.js

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 1 - 0
main/inc/lib/svg-edit/js-hotkeys/jquery.hotkeys.min.js

@@ -13,5 +13,6 @@ Languages Already Translated By Humans:
   * lang.fr.js
   * lang.ja.js
   * lang.nl.js
+  * lang.pl.js
   * lang.ro.js
   * lang.sk.js

+ 0 - 6
main/inc/lib/svg-edit/locale/index.html

@@ -1,6 +0,0 @@
-<html>
-<head>
-</head>
-<body>
-</body>
-</html>

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.af.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "af",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "kleinste voorwerp",
 		"new_doc": "Nuwe Beeld",
 		"open_doc": "Open Beeld",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Slaan Beeld",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Groep Elemente",
+		"group_elements": "Groep Elemente",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.ar.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "ar",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "أصغر كائن",
 		"new_doc": "صورة جديدة",
 		"open_doc": "فتح الصورة",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "حفظ صورة",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "مجموعة عناصر",
+		"group_elements": "مجموعة عناصر",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.az.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "az",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "smallest object",
 		"new_doc": "New Image",
 		"open_doc": "Open Image",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Save Image",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Group Elements",
+		"group_elements": "Group Elements",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.be.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "be",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "маленькі аб&#39;ект",
 		"new_doc": "Новае выява",
 		"open_doc": "Адкрыць выява",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Захаваць малюнак",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Група элементаў",
+		"group_elements": "Група элементаў",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.bg.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "bg",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "най-малката обект",
 		"new_doc": "Ню Имидж",
 		"open_doc": "Отворете изображението",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Save Image",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Група Елементи",
+		"group_elements": "Група Елементи",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.ca.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "ca",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "objecte més petit",
 		"new_doc": "Nova imatge",
 		"open_doc": "Obrir imatge",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Guardar imatge",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Elements de Grup de",
+		"group_elements": "Elements de Grup de",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.cs.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "cs",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "nejmenšímu objektu",
 		"new_doc": "Nový dokument",
 		"open_doc": "Otevřít dokument",
-		"export_png": "Exportovat jako PNG",
+		"export_img": "Export",
 		"save_doc": "Uložit dokument",
 		"import_doc": "Importovat SVG",
 		"align_to_page": "Zarovnat element na stránku",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Seskupit objekty",
+		"group_elements": "Seskupit objekty",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Objekt na křivku",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.cy.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "cy",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "lleiaf gwrthrych",
 		"new_doc": "Newydd Delwedd",
 		"open_doc": "Delwedd Agored",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Cadw Delwedd",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Elfennau Grŵp",
+		"group_elements": "Elfennau Grŵp",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.da.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "da",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "mindste objekt",
 		"new_doc": "Nyt billede",
 		"open_doc": "Open Image",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Gem billede",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Gruppe Elements",
+		"group_elements": "Gruppe Elements",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 56 - 40
main/inc/lib/svg-edit/locale/lang.de.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "de",
 	dir : "ltr",
@@ -10,8 +11,8 @@ svgEditor.readLang({
 		"key_up": "nach oben", 
 		"more_opts": "More Options",
 		"url": "URL",
-		"width": "Width",
-		"height": "Height"
+		"width": "Breite",
+		"height": "Höhe"
 	},
 	misc: {
 		"powered_by": "angetrieben durch"
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "kleinstes Objekt",
 		"new_doc": "Neues Bild",
 		"open_doc": "Bild öffnen",
-		"export_png": "Als PNG exportieren",
+		"export_img": "Export",
 		"save_doc": "Bild speichern",
 		"import_doc": "Importiere SVG",
 		"align_to_page": "Align Element to Page",
@@ -105,7 +106,7 @@ svgEditor.readLang({
 		"mode_circle": "Kreis",
 		"mode_fhellipse": "Freihand Ellipse",
 		"mode_path": "Pfad zeichnen",
-		"mode_shapelib": "Shape library",
+		"mode_shapelib": "Form-Bibliothek",
 		"mode_text": "Text erstellen und bearbeiten",
 		"mode_image": "Bild einfügen",
 		"mode_zoom": "Zoomfaktor vergrößern oder verringern",
@@ -115,39 +116,39 @@ svgEditor.readLang({
 		"redo": "Wiederherstellen",
 		"tool_source": "Quellecode bearbeiten",
 		"wireframe_mode": "Drahtmodell Modus",
-		"toggle_grid": "Show/Hide Grid",
-		"clone": "Clone Element(s)",
-		"del": "Delete Element(s)",
-		"group": "Gruppieren",
+		"toggle_grid": "Zeige/Verstecke Gitternetz",
+		"clone": "Klone Element(e)",
+		"del": "Lösche Element(e)",
+		"group_elements": "Gruppieren Element(e)",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Gewähltes Objekt in einen Pfad konvertieren",
 		"reorient_path": "Neuausrichtung des Pfades",
 		"ungroup": "Gruppierung aufheben",
 		"docprops": "Dokument-Eigenschaften",
-		"imagelib": "Image Library",
+		"imagelib": "Bilder-Bibliothek",
 		"move_bottom": "Die gewählten Objekte nach ganz unten schieben",
 		"move_top": "Die gewählten Objekte nach ganz oben anheben",
 		"node_clone": "Klone den Knoten",
 		"node_delete": "Lösche den Knoten",
 		"node_link": "Gekoppelte oder separate Kontroll Punkte für die Bearbeitung des Pfades",
 		"add_subpath": "Teilpfad hinzufügen",
-		"openclose_path": "Open/close sub-path",
+		"openclose_path": "Öffne/Verbinde Unterpfad",
 		"source_save": "Änderungen akzeptieren",
-		"cut": "Cut",
-		"copy": "Copy",
-		"paste": "Paste",
-		"paste_in_place": "Paste in Place",
-		"Löschen": "Delete",
-		"group": "Group",
-		"move_front": "Bring to Front",
-		"move_up": "Bring Forward",
-		"move_down": "Send Backward",
-		"move_back": "Send to Back"
+		"cut": "Ausschneiden",
+		"copy": "Kopieren",
+		"paste": "Einfügen",
+		"paste_in_place": "An Originalposition einfügen",
+		"Löschen": "Löschen",
+		"group": "Gruppieren",
+		"move_front": "Nach ganz oben anheben",
+		"move_up": "Anheben",
+		"move_down": "Absenken",
+		"move_back": "Nach ganz unten absenken"
 	},
 	layers: {
-		"layer":"Ebene",
-		"layers": "Layers",
+		"layer": "Ebene",
+		"layers": "Ebenen",
 		"del": "Ebene löschen",
 		"move_down": "Ebene nach unten verschieben",
 		"new": "Neue Ebene",
@@ -177,26 +178,27 @@ svgEditor.readLang({
 		"icon_small": "Klein",
 		"icon_xlarge": "Sehr Groß",
 		"select_predefined": "Auswahl einer vordefinierten:",
-		"units_and_rulers": "Units & Rulers",
-		"show_rulers": "Show rulers",
-		"base_unit": "Base Unit:",
-		"grid": "Grid",
-		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"units_and_rulers": "Einheiten und Lineale",
+		"show_rulers": "Zeige Lineale",
+		"base_unit": "Basiseinheit:",
+		"grid": "Gitternetz",
+		"snapping_onoff": "Einrasten an/aus",
+		"snapping_stepsize": "Einrastungs Abstand:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
-		"basic": "Basic",
-		"object": "Objects",
-		"symbol": "Symbols",
-		"arrow": "Arrows",
-		"flowchart": "Flowchart",
-		"animal": "Animals",
-		"game": "Cards & Chess",
-		"dialog_balloon": "Dialog balloons",
-		"electronics": "Electronics",
-		"math": "Mathematical",
-		"music": "Music",
-		"misc": "Miscellaneous",
+		"basic": "Standard",
+		"object": "Objekte",
+		"symbol": "Symbole",
+		"arrow": "Pfeile",
+		"flowchart": "Flussdiagramme",
+		"animal": "Tiere",
+		"game": "Spielkarten und Schach",
+		"dialog_balloon": "Sprechblasen",
+		"electronics": "Elektronik",
+		"math": "Mathematik",
+		"music": "Musik",
+		"misc": "Sonstige",
 		"raphael_1": "raphaeljs.com set 1",
 		"raphael_2": "raphaeljs.com set 2"
 	},
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.el.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "el",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "μικρότερο αντικείμενο",
 		"new_doc": "Νέα εικόνα",
 		"open_doc": "Άνοιγμα εικόνας",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Αποθήκευση εικόνας",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Ομάδα Στοιχεία",
+		"group_elements": "Ομάδα Στοιχεία",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.en.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "en",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "smallest object",
 		"new_doc": "New Image",
 		"open_doc": "Open Image",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Save Image",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Layer",
-		"group": "Group Elements",
+		"group_elements": "Group Elements",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 83 - 67
main/inc/lib/svg-edit/locale/lang.es.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "es",
 	dir : "ltr",
@@ -8,10 +9,10 @@ svgEditor.readLang({
 		"key_del": "suprimir", 
 		"key_down": "abajo", 
 		"key_up": "arriba", 
-		"more_opts": "Más opciones",
+		"more_opts": "More Options",
 		"url": "URL",
-		"width": "Ancho",
-		"height": "alto"
+		"width": "Width",
+		"height": "Height"
 	},
 	misc: {
 		"powered_by": "Powered by"
@@ -20,10 +21,10 @@ svgEditor.readLang({
 		"toggle_stroke_tools": "Mostrar/ocultar herramientas de trazo adicionales",
 		"palette_info": "Haga clic para cambiar el color de relleno. Pulse Mayús y haga clic para cambiar el color del contorno.",
 		"zoom_level": "Cambiar el nivel de zoom",
-		"panel_drag": "Arrastrar izquierda/derecha para cambiar el tamaño del panel"
+		"panel_drag": "Drag left/right to resize side panel"
 	},
 	properties: {
-		"id": "Identificación del elemento",
+		"id": "Identify the element",
 		"fill_color": "Cambiar el color de relleno",
 		"stroke_color": "Cambiar el color del contorno",
 		"stroke_style": "Cambiar el estilo del trazo del contorno",
@@ -84,10 +85,10 @@ svgEditor.readLang({
 		"smallest_object": "El objeto más pequeño",
 		"new_doc": "Nueva imagen",
 		"open_doc": "Abrir imagen",
-		"export_png": "Exportar como PNG",
+		"export_img": "Export",
 		"save_doc": "Guardar imagen",
 		"import_doc": "Importar un archivo SVG",
-		"align_to_page": "Alinear el elemento a la página",
+		"align_to_page": "Align Element to Page",
 		"align_bottom": "Alinear parte inferior",
 		"align_center": "Centrar verticalmente",
 		"align_left": "Alinear lado izquierdo",
@@ -95,67 +96,67 @@ svgEditor.readLang({
 		"align_right": "Alinear lado derecho",
 		"align_top": "Alinear parte superior",
 		"mode_select": "Herramienta de selección",
-		"mode_fhpath": "Lápiz",
+		"mode_fhpath": "Herramienta de lápiz",
 		"mode_line": "Trazado de líneas",
 		"mode_connect": "Conectar dos objetos",
-		"mode_rect": "Rectángulo",
-		"mode_square": "Cuadrado",
+		"mode_rect": "Rectangle Tool",
+		"mode_square": "Square Tool",
 		"mode_fhrect": "Rectángulo a mano alzada",
 		"mode_ellipse": "Elipse",
 		"mode_circle": "Círculo",
 		"mode_fhellipse": "Elipse a mano alzada",
-		"mode_path": "Trazado",
-		"mode_shapelib": "Biblioteca de figuras",
+		"mode_path": "Herramienta de trazado",
+		"mode_shapelib": "Shape library",
 		"mode_text": "Insertar texto",
 		"mode_image": "Insertar imagen",
 		"mode_zoom": "Zoom",
-		"mode_eyedropper": "Pipeta",
+		"mode_eyedropper": "Herramienta de pipeta",
 		"no_embed": "NOTA: La imagen no puede ser integrada. El contenido mostrado dependerá de la imagen ubicada en esta ruta. ",
 		"undo": "Deshacer",
 		"redo": "Rehacer",
 		"tool_source": "Editar código fuente",
 		"wireframe_mode": "Modo marco de alambre",
-		"toggle_grid": "Mostrar/Ocultar cuadrícula",
-		"clone": "Clonar elemento(s)",
-		"del": "Eliminar elemento(s)",
-		"group": "Agrupar objetos",
-		"make_link": "Crear (hiper)enlace",
-		"set_link_url": "Enlace mediante URL (dejar vacío para quitar)",
+		"toggle_grid": "Show/Hide Grid",
+		"clone": "Clone Element(s)",
+		"del": "Delete Element(s)",
+		"group_elements": "Agrupar objetos",
+		"make_link": "Make (hyper)link",
+		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convertir a trazado",
 		"reorient_path": "Reorientar el trazado",
 		"ungroup": "Desagrupar objetos",
 		"docprops": "Propiedades del documento",
-		"imagelib": "Biblioteca de imágenes",
+		"imagelib": "Image Library",
 		"move_bottom": "Mover abajo",
 		"move_top": "Mover arriba",
 		"node_clone": "Clonar nodo",
 		"node_delete": "Suprimir nodo",
 		"node_link": "Enlazar puntos de control",
 		"add_subpath": "Añadir subtrazado",
-		"openclose_path": "Abrir/cerrar sub-path",
-		"source_save": "Código fuente",
-		"cut": "Cortar",
-		"copy": "Copiar",
-		"paste": "Pegar",
-		"paste_in_place": "Pegar en un lugar",
-		"delete": "Eliminar",
-		"group": "Grupo",
-		"move_front": "Traer al comienzo",
-		"move_up": "Traer adelante",
-		"move_down": "Enviar atrás",
-		"move_back": "Enviar al final"
+		"openclose_path": "Open/close sub-path",
+		"source_save": "Aplicar cambios",
+		"cut": "Cut",
+		"copy": "Copy",
+		"paste": "Paste",
+		"paste_in_place": "Paste in Place",
+		"suprimir": "Delete",
+		"group": "Group",
+		"move_front": "Bring to Front",
+		"move_up": "Bring Forward",
+		"move_down": "Send Backward",
+		"move_back": "Send to Back"
 	},
 	layers: {
 		"layer":"Capa",
-		"layers": "Capas",
+		"layers": "Layers",
 		"del": "Suprimir capa",
 		"move_down": "Mover la capa hacia abajo",
 		"new": "Nueva capa",
 		"rename": "Renombrar capa",
 		"move_up": "Mover la capa hacia arriba",
-		"dupe": "Duplicar capa",
-		"merge_down": "Combinar abajo",
-		"merge_all": "Combinar todo",
+		"dupe": "Duplicate Layer",
+		"merge_down": "Merge Down",
+		"merge_all": "Merge All",
 		"move_elems_to": "Desplazar objetos a:",
 		"move_selected": "Mover los objetos seleccionados a otra capa"
 	},
@@ -170,42 +171,43 @@ svgEditor.readLang({
 		"icon_size": "Tamaño de los iconos",
 		"language": "Idioma",
 		"background": "Fondo del editor",
-		"editor_img_url": "URL de la imagen",
+		"editor_img_url": "Image URL",
 		"editor_bg_note": "Nota: El fondo no se guardará junto con la imagen.",
 		"icon_large": "Grande",
 		"icon_medium": "Mediano",
 		"icon_small": "Pequeño",
 		"icon_xlarge": "Muy grande",
 		"select_predefined": "Seleccionar predefinido:",
-		"units_and_rulers": "Unidades & Reglas",
-		"show_rulers": "Mostrar reglas",
-		"base_unit": "Unidad de base:",
-		"grid": "Cuadrícula",
-		"snapping_onoff": "Ajuste si/no",
-		"snapping_stepsize": "Tamaño de los pasos de ajuste:"
+		"units_and_rulers": "Units & Rulers",
+		"show_rulers": "Show rulers",
+		"base_unit": "Base Unit:",
+		"grid": "Grid",
+		"snapping_onoff": "Snapping on/off",
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
-		"basic": "Básico",
-		"object": "Objetos",
-		"symbol": "Símbolos",
-		"arrow": "Flechas",
-		"flowchart": "Diagrama de flujo",
-		"animal": "Animales",
-		"game": "Cartas & Ajedrez",
-		"dialog_balloon": "Bocadillos de diálogo",
-		"electronics": "Electrónica",
-		"math": "Matemáticas",
-		"music": "Música",
-		"misc": "Miscelánea",
-		"raphael_1": "Set 1 de raphaeljs.com",
-		"raphael_2": "Set 2 de raphaeljs.com"
+		"basic": "Basic",
+		"object": "Objects",
+		"symbol": "Symbols",
+		"arrow": "Arrows",
+		"flowchart": "Flowchart",
+		"animal": "Animals",
+		"game": "Cards & Chess",
+		"dialog_balloon": "Dialog balloons",
+		"electronics": "Electronics",
+		"math": "Mathematical",
+		"music": "Music",
+		"misc": "Miscellaneous",
+		"raphael_1": "raphaeljs.com set 1",
+		"raphael_2": "raphaeljs.com set 2"
 	},
 	imagelib: {
-		"select_lib": "Seleccione una biblioteca de imágenes",
-		"show_list": "Muestrar la lista de bibliotecas",
-		"import_single": "Importación simple",
-		"import_multi": "Importación múltiple",
-		"open": "Abrir como un nuevo documento"
+		"select_lib": "Select an image library",
+		"show_list": "Show library list",
+		"import_single": "Import single",
+		"import_multi": "Import multiple",
+		"open": "Open as new document"
 	},
 	notification: {
 		"invalidAttrValGiven":"Valor no válido",
@@ -216,7 +218,7 @@ svgEditor.readLang({
 		"layerHasThatName":"El nombre introducido es el nombre actual de la capa.",
 		"QmoveElemsToLayer":"¿Desplazar los elementos seleccionados a la capa '%s'?",
 		"QwantToClear":"¿Desea borrar el dibujo?\n¡El historial de acciones también se borrará!",
-		"QwantToOpen":"¿Quiere abrir un nuevo archivo?\nEste cambio no podrá deshacerse y volver atrás !",
+		"QwantToOpen":"Do you want to open a new file?\nThis will also erase your undo history!",
 		"QerrorsRevertToSource":"Existen errores sintácticos en su código fuente SVG.\n¿Desea volver al código fuente SVG original?",
 		"QignoreSourceChanges":"¿Desea ignorar los cambios realizados sobre el código fuente SVG?",
 		"featNotSupported":"Función no compatible.",
@@ -226,9 +228,23 @@ svgEditor.readLang({
 		"saveFromBrowser": "Seleccionar \"Guardar como...\" en su navegador para guardar la imagen en forma de archivo %s.",
 		"noteTheseIssues": "Existen además los problemas siguientes:",
 		"unsavedChanges": "There are unsaved changes.",
-		"enterNewLinkURL": "Introduzca el nuevo enlace URL",
-		"errorLoadingSVG": "Error: No se pueden cargar los datos SVG",
-		"URLloadFail": "No se puede cargar desde la URL",
-		"retrieving": "Recuperando \"%s\"..."
+		"enterNewLinkURL": "Enter the new hyperlink URL",
+		"errorLoadingSVG": "Error: Unable to load SVG data",
+		"URLloadFail": "Unable to load from URL",
+		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.et.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "et",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "väikseim objekt",
 		"new_doc": "Uus pilt",
 		"open_doc": "Pildi avamine",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Salvesta pilt",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Rühma elemendid",
+		"group_elements": "Rühma elemendid",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.fa.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "fa",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "‫کوچکترین شئ‬",
 		"new_doc": "‫تصویر جدید ‬",
 		"open_doc": "‫باز کردن تصویر ‬",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "‫ذخیره تصویر ‬",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "‫قرار دادن عناصر در گروه ‬",
+		"group_elements": "‫قرار دادن عناصر در گروه ‬",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "‫تبدیل به مسیر‬",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 19 - 3
main/inc/lib/svg-edit/locale/lang.fi.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "fi",
 	dir : "ltr",
@@ -84,7 +85,7 @@ svgEditor.readLang({
 		"smallest_object": "pienin kohde",
 		"new_doc": "Uusi kuva",
 		"open_doc": "Avaa kuva",
-		"export_png": "Export as PNG",
+		"export_img": "Export",
 		"save_doc": "Save Image",
 		"import_doc": "Import SVG",
 		"align_to_page": "Align Element to Page",
@@ -118,7 +119,7 @@ svgEditor.readLang({
 		"toggle_grid": "Show/Hide Grid",
 		"clone": "Clone Element(s)",
 		"del": "Delete Element(s)",
-		"group": "Tuoteryhmään Elements",
+		"group_elements": "Tuoteryhmään Elements",
 		"make_link": "Make (hyper)link",
 		"set_link_url": "Set link URL (leave empty to remove)",
 		"to_path": "Convert to Path",
@@ -182,7 +183,8 @@ svgEditor.readLang({
 		"base_unit": "Base Unit:",
 		"grid": "Grid",
 		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Grid color"
 	},
 	shape_cats: {
 		"basic": "Basic",
@@ -230,5 +232,19 @@ svgEditor.readLang({
 		"errorLoadingSVG": "Error: Unable to load SVG data",
 		"URLloadFail": "Unable to load from URL",
 		"retrieving": "Retrieving \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

+ 83 - 67
main/inc/lib/svg-edit/locale/lang.fr.js

@@ -1,3 +1,4 @@
+/*globals svgEditor */
 svgEditor.readLang({
 	lang: "fr",
 	dir : "ltr",
@@ -8,19 +9,19 @@ svgEditor.readLang({
 		"key_del": "Retour Arr.", 
 		"key_down": "Bas", 
 		"key_up": "Haut", 
-		"more_opts": "More Options",
+		"more_opts": "Plus d'options",
 		"url": "URL",
-		"width": "Width",
-		"height": "Height"
+		"width": "Largeur",
+		"height": "Hauteur"
 	},
 	misc: {
-		"powered_by": "Powered by"
+		"powered_by": "Propulsé par"
 	}, 
 	ui: {
 		"toggle_stroke_tools": "Montrer/Cacher plus d'outils de Contour",
-		"palette_info": "Cliquer pour changer la couleur de remplissage, Shift-Clic pour changer la couleur de contour",
+		"palette_info": "Cliquer pour changer la couleur de remplissage, Maj-Clic pour changer la couleur de contour",
 		"zoom_level": "Changer le niveau de zoom",
-		"panel_drag": "Drag left/right to resize side panel"
+		"panel_drag": "Tirer vers la gauche/droite pour redimentionner le panneau"
 	},
 	properties: {
 		"id": "Identifier l'élément",
@@ -79,12 +80,12 @@ svgEditor.readLang({
 		"align_relative_to": "Aligner par rapport à ...",
 		"relativeTo": "Relativement à:",
 		"Page": "Page",
-		"largest_object": "Plus gros objet",
+		"largest_object": "Objet plus gros ",
 		"selected_objects": "Objets sélectionnés",
-		"smallest_object": "Plus petit objet",
+		"smallest_object": "Objet plus petit",
 		"new_doc": "Nouvelle image",
 		"open_doc": "Ouvrir une image",
-		"export_png": "Exporter au format PNG",
+		"export_img": "Export",
 		"save_doc": "Enregistrer l'image",
 		"import_doc": "Importer un objet SVG",
 		"align_to_page": "Aligner l'élément relativement à la Page",
@@ -98,14 +99,14 @@ svgEditor.readLang({
 		"mode_fhpath": "Crayon à main levée",
 		"mode_line": "Tracer des lignes",
 		"mode_connect": "Connecter deux objets",
-		"mode_rect": "Rectangle Tool",
-		"mode_square": "Square Tool",
+		"mode_rect": "Outil rectangle",
+		"mode_square": "Outils carré",
 		"mode_fhrect": "Rectangle main levée",
 		"mode_ellipse": "Ellipse",
 		"mode_circle": "Cercle",
 		"mode_fhellipse": "Ellipse main levée",
-		"mode_path": "Outil Chemin",
-		"mode_shapelib": "Shape library",
+		"mode_path": "Dessiner un tracé",
+		"mode_shapelib": "Bibliothèque d'images",
 		"mode_text": "Outil Texte",
 		"mode_image": "Outil Image",
 		"mode_zoom": "Zoom",
@@ -115,47 +116,47 @@ svgEditor.readLang({
 		"redo": "Refaire l'action",
 		"tool_source": "Modifier la source",
 		"wireframe_mode": "Mode Fil de Fer",
-		"toggle_grid": "Show/Hide Grid",
-		"clone": "Clone Element(s)",
-		"del": "Delete Element(s)",
-		"group": "Grouper les éléments",
-		"make_link": "Make (hyper)link",
-		"set_link_url": "Set link URL (leave empty to remove)",
-		"to_path": "Convertir en chemin",
-		"reorient_path": "Réorienter le chemin",
+		"toggle_grid": "Montrer/cacher la grille",
+		"clone": "Cloner élement(s)",
+		"del": "Supprimer élement(s)",
+		"group_elements": "Grouper les éléments",
+		"make_link": "Créer hyperlien",
+		"set_link_url": "Définir le lien URL (laisser vide pour supprimer)",
+		"to_path": "Convertir en tracé",
+		"reorient_path": "Réorienter le tracé",
 		"ungroup": "Dégrouper les éléments",
 		"docprops": "Propriétés du document",
-		"imagelib": "Image Library",
+		"imagelib": "Bibliothèque d'images",
 		"move_bottom": "Déplacer vers le bas",
 		"move_top": "Déplacer vers le haut",
 		"node_clone": "Cloner le nœud",
 		"node_delete": "Supprimer le nœud",
 		"node_link": "Rendre les points de contrôle solidaires",
-		"add_subpath": "Ajouter un sous-chemin",
+		"add_subpath": "Ajouter un tracé secondaire",
 		"openclose_path": "Ouvrir/Fermer sous-chemin",
 		"source_save": "Appliquer Modifications",
-		"cut": "Cut",
-		"copy": "Copy",
-		"paste": "Paste",
-		"paste_in_place": "Paste in Place",
-		"Retour Arr.": "Delete",
+		"cut": "Couper",
+		"copy": "Copier",
+		"paste": "Coller",
+		"paste_in_place": "Coller sur place",
+		"Retour Arr.": "Supprimer",
 		"group": "Group",
-		"move_front": "Bring to Front",
-		"move_up": "Bring Forward",
-		"move_down": "Send Backward",
-		"move_back": "Send to Back"
+		"move_front": "Placer au premier plan",
+		"move_up": "Avancer d'un plan",
+		"move_down": "Placer en arrière plan",
+		"move_back": "Reculer d'un plan"
 	},
 	layers: {
 		"layer":"Calque",
-		"layers": "Layers",
+		"layers": "Calques",
 		"del": "Supprimer le calque",
 		"move_down": "Descendre le calque",
 		"new": "Nouveau calque",
 		"rename": "Renommer le calque",
 		"move_up": "Monter le calque",
-		"dupe": "Duplicate Layer",
-		"merge_down": "Merge Down",
-		"merge_all": "Merge All",
+		"dupe": "Dupliquer calque",
+		"merge_down": "Fusionner vers le bas",
+		"merge_all": "Tout fusionner",
 		"move_elems_to": "Déplacer éléments vers:",
 		"move_selected": "Déplacer les éléments sélectionnés vers un autre calque"
 	},
@@ -177,35 +178,36 @@ svgEditor.readLang({
 		"icon_small": "Petite",
 		"icon_xlarge": "Super-Grande",
 		"select_predefined": "Sélectionner prédéfinis:",
-		"units_and_rulers": "Units & Rulers",
-		"show_rulers": "Show rulers",
-		"base_unit": "Base Unit:",
-		"grid": "Grid",
-		"snapping_onoff": "Snapping on/off",
-		"snapping_stepsize": "Snapping Step-Size:"
+		"units_and_rulers": "Unités & Règles",
+		"show_rulers": "Afficher les règles",
+		"base_unit": "Unité de mesure:",
+		"grid": "Grille",
+		"snapping_onoff": "Épingler oui/non",
+		"snapping_stepsize": "Snapping Step-Size:",
+		"grid_color": "Couleur de la grille"
 	},
 	shape_cats: {
-		"basic": "Basic",
-		"object": "Objects",
-		"symbol": "Symbols",
-		"arrow": "Arrows",
-		"flowchart": "Flowchart",
-		"animal": "Animals",
-		"game": "Cards & Chess",
-		"dialog_balloon": "Dialog balloons",
-		"electronics": "Electronics",
-		"math": "Mathematical",
-		"music": "Music",
-		"misc": "Miscellaneous",
-		"raphael_1": "raphaeljs.com set 1",
-		"raphael_2": "raphaeljs.com set 2"
+		"basic": "Basique",
+		"object": "Objets",
+		"symbol": "Symboles",
+		"arrow": "Flèches",
+		"flowchart": "Diagramme de flux",
+		"animal": "Animaux",
+		"game": "Cartes & Echecs",
+		"dialog_balloon": "Bulles de dialogue",
+		"electronics": "Electronique",
+		"math": "Mathématiques",
+		"music": "Musique",
+		"misc": "Divers",
+		"raphael_1": "raphaeljs.com ensemble 1",
+		"raphael_2": "raphaeljs.com ensemble 2"
 	},
 	imagelib: {
-		"select_lib": "Select an image library",
-		"show_list": "Show library list",
-		"import_single": "Import single",
-		"import_multi": "Import multiple",
-		"open": "Open as new document"
+		"select_lib": "Choisir une image dans la bibliothèque",
+		"show_list": "Liste de la bibliotèque d'images",
+		"import_single": "Importation simple",
+		"import_multi": "Importation multiple",
+		"open": "Ouvrir en tant que nouveau document"
 	},
 	notification: {
 		"invalidAttrValGiven":"Valeur fournie invalide",
@@ -216,7 +218,7 @@ svgEditor.readLang({
 		"layerHasThatName":"Le calque porte déjà ce nom",
 		"QmoveElemsToLayer":"Déplacer les éléments sélectionnés vers le calque '%s' ?",
 		"QwantToClear":"Voulez-vous effacer le dessin ?\nL'historique de vos actions sera également effacé !",
-		"QwantToOpen":"Do you want to open a new file?\nThis will also erase your undo history!",
+		"QwantToOpen":"Voulez-vous ouvrir un nouveau document?\nVous perderez l'historique de vos modifications!",
 		"QerrorsRevertToSource":"Il y a des erreurs d'analyse syntaxique dans votre code-source SVG.\nRevenir au code-source SVG avant modifications ?",
 		"QignoreSourceChanges":"Ignorer les modifications faites à la source SVG ?",
 		"featNotSupported":"Fonction non supportée",
@@ -225,10 +227,24 @@ svgEditor.readLang({
 		"loadingImage":"Chargement de l'image, veuillez patienter...",
 		"saveFromBrowser": "Selectionner \"Enregistrer sous...\" dans votre navigateur pour sauvegarder l'image en tant que fichier %s.",
 		"noteTheseIssues": "Notez également les problèmes suivants : ",
-		"unsavedChanges": "There are unsaved changes.",
-		"enterNewLinkURL": "Enter the new hyperlink URL",
-		"errorLoadingSVG": "Error: Unable to load SVG data",
-		"URLloadFail": "Unable to load from URL",
-		"retrieving": "Retrieving \"%s\"..."
+		"unsavedChanges": "Il y a des changements non sauvegardés.",
+		"enterNewLinkURL": "Entrez la nouvel hyperlien URL",
+		"errorLoadingSVG": "Erreur: Impossible de charger le document SVG",
+		"URLloadFail": "Impossible de charger l'URL",
+		"retrieving": "Récupère \"%s\"..."
+	},
+	confirmSetStorage: {
+		message: "By default and where supported, SVG-Edit can store your editor "+
+		"preferences and SVG content locally on your machine so you do not "+
+		"need to add these back each time you load SVG-Edit. If, for privacy "+
+		"reasons, you do not wish to store this information on your machine, "+
+		"you can change away from the default option below.",
+		storagePrefsAndContent: "Store preferences and SVG content locally",
+		storagePrefsOnly: "Only store preferences locally",
+		storagePrefs: "Store preferences locally",
+		storageNoPrefsOrContent: "Do not store my preferences or SVG content locally",
+		storageNoPrefs: "Do not store my preferences locally",
+		rememberLabel: "Remember this choice?",
+		rememberTooltip: "If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again."
 	}
 });

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio