foreignObject Support in SVG-Edit

Added support so that you can
create new foreignObjects, manipulate
existing ones, and edit their content.

No itex support. You need to use MathML
in there. But it's a start ...
master
Jacques Distler 2010-02-12 00:01:27 -06:00
parent 9047e5d460
commit 502d4f20bb
6 changed files with 366 additions and 77 deletions

View File

@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg">
<svg xmlns="http://www.w3.org/2000/svg">
<!-- All images created with SVG-edit - http://svg-edit.googlecode.com/ -->
<g id="logo">
@ -235,6 +235,92 @@
</svg>
</g>
<g id="foreign">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 84 84">
<g fill="#444" opacity="0.2" transform="translate(6,6)">
<path d="M42.8,74.3c0,4.3,0,5.9,11.8,5.9l4.1,0l0,3.8c-4.5-0.4-16.1-0.4-21.2-0.3c-5.1,0-16.6,0-21,0.4l0-3.8l4.1,0
c11.8,0,11.8-1.7,11.8-5.9l0-6.9C13.9,65.6,0,54.6,0,42c0-12.2,13.3-23.5,32.4-25.4l0-6.9c0-4.3,0-5.9-11.8-5.9l-4.1,0l0-3.8
c4.5,0.4,16.1,0.4,21.2,0.3c5.1,0,16.6,0,21-0.4l0,3.8l-4.1,0c-11.8,0-11.8,1.7-11.8,5.9l0,6.9C61.6,18.1,75.8,29.2,75.8,42
c0,12.4-13.8,23.9-33.1,25.4L42.8,74.3z M32.4,19.4c-18.7,2.5-19.9,16.2-19.9,22.6c0,7.6,2.3,20.2,20,22.5L32.4,19.4z M42.7,64.7
c18.8-2.2,20.7-15.4,20.6-22.8c0-9.3-3.5-20.6-20.7-22.6L42.7,64.7z"/>
</g>
<g fill="#444" opacity="0.3" transform="translate(4,4)">
<path d="M42.8,74.3c0,4.3,0,5.9,11.8,5.9l4.1,0l0,3.8c-4.5-0.4-16.1-0.4-21.2-0.3c-5.1,0-16.6,0-21,0.4l0-3.8l4.1,0
c11.8,0,11.8-1.7,11.8-5.9l0-6.9C13.9,65.6,0,54.6,0,42c0-12.2,13.3-23.5,32.4-25.4l0-6.9c0-4.3,0-5.9-11.8-5.9l-4.1,0l0-3.8
c4.5,0.4,16.1,0.4,21.2,0.3c5.1,0,16.6,0,21-0.4l0,3.8l-4.1,0c-11.8,0-11.8,1.7-11.8,5.9l0,6.9C61.6,18.1,75.8,29.2,75.8,42
c0,12.4-13.8,23.9-33.1,25.4L42.8,74.3z M32.4,19.4c-18.7,2.5-19.9,16.2-19.9,22.6c0,7.6,2.3,20.2,20,22.5L32.4,19.4z M42.7,64.7
c18.8-2.2,20.7-15.4,20.6-22.8c0-9.3-3.5-20.6-20.7-22.6L42.7,64.7z"/>
</g>
<g fill="#444" opacity="0.5" transform="translate(2,2)">
<path d="M42.8,74.3c0,4.3,0,5.9,11.8,5.9l4.1,0l0,3.8c-4.5-0.4-16.1-0.4-21.2-0.3c-5.1,0-16.6,0-21,0.4l0-3.8l4.1,0
c11.8,0,11.8-1.7,11.8-5.9l0-6.9C13.9,65.6,0,54.6,0,42c0-12.2,13.3-23.5,32.4-25.4l0-6.9c0-4.3,0-5.9-11.8-5.9l-4.1,0l0-3.8
c4.5,0.4,16.1,0.4,21.2,0.3c5.1,0,16.6,0,21-0.4l0,3.8l-4.1,0c-11.8,0-11.8,1.7-11.8,5.9l0,6.9C61.6,18.1,75.8,29.2,75.8,42
c0,12.4-13.8,23.9-33.1,25.4L42.8,74.3z M32.4,19.4c-18.7,2.5-19.9,16.2-19.9,22.6c0,7.6,2.3,20.2,20,22.5L32.4,19.4z M42.7,64.7
c18.8-2.2,20.7-15.4,20.6-22.8c0-9.3-3.5-20.6-20.7-22.6L42.7,64.7z"/>
</g>
<g fill="#0000CC">
<path id="xyz321" d="M42.8,74.3c0,4.3,0,5.9,11.8,5.9l4.1,0l0,3.8c-4.5-0.4-16.1-0.4-21.2-0.3c-5.1,0-16.6,0-21,0.4l0-3.8l4.1,0
c11.8,0,11.8-1.7,11.8-5.9l0-6.9C13.9,65.6,0,54.6,0,42c0-12.2,13.3-23.5,32.4-25.4l0-6.9c0-4.3,0-5.9-11.8-5.9l-4.1,0l0-3.8
c4.5,0.4,16.1,0.4,21.2,0.3c5.1,0,16.6,0,21-0.4l0,3.8l-4.1,0c-11.8,0-11.8,1.7-11.8,5.9l0,6.9C61.6,18.1,75.8,29.2,75.8,42
c0,12.4-13.8,23.9-33.1,25.4L42.8,74.3z M32.4,19.4c-18.7,2.5-19.9,16.2-19.9,22.6c0,7.6,2.3,20.2,20,22.5L32.4,19.4z M42.7,64.7
c18.8-2.2,20.7-15.4,20.6-22.8c0-9.3-3.5-20.6-20.7-22.6L42.7,64.7z"/>
</g>
</svg>
</g>
<g id="edit_foreign">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="30" height="30" viewBox="34 38 205 205" overflow="hidden">
<g fill="#000088">
<path d="M30.1,63.9v-4.3l30.2-14.9V50L36.5,61.7l23.8,11.7v5.3L30.1,63.9z"/>
<path d="M106.1,79.7v-1.1c4.2-0.5,4.8-1.1,4.8-5.2V58.2c0-6-1.3-7.9-5.4-7.9c-3.3,0-5.7,1.3-7.8,4.4v18.1
c0,4.5,1.1,5.7,5.2,5.8v1.1H86.8v-1.1c4.1-0.3,4.9-1.1,4.9-5.1V57.9c0-5-1.6-7.6-4.8-7.6c-2.5,0-5.6,1.2-7.4,2.9
c-0.5,0.5-1.1,1.4-1.1,1.4v20.3c0,2.8,1.1,3.6,4.9,3.7v1.1h-16v-1.1c4-0.1,5-1.2,5-5V55.4c0-3.5-0.6-4.6-2.5-4.6
c-0.8,0-1.4,0.1-2.3,0.3v-1.2c4-1.1,6.4-1.9,10.1-3.2l0.5,0.1v5.4c6-4.5,8-5.5,11.2-5.5c3.9,0,6.3,1.9,7.6,6c3.9-4.2,7.6-6,11.7-6
c5.5,0,8.4,4.3,8.4,12.8v14.8c0,2.8,0.9,4.1,3.1,4.2l1.9,0.1v1.1H106.1z"/>
<path d="M147.3,80.5c-3,0-4.2-1.4-4.6-5.3c-4.4,3.7-7.3,5.3-10.5,5.3c-4.5,0-7.6-3.2-7.6-7.7c0-2.4,1-4.8,2.6-6.3
c3.1-2.7,4.3-3.3,15.4-7.8v-4.4c0-3.9-1.9-6-5.5-6c-2.9,0-5.2,1.6-5.2,3.5c0,0.5,0.1,1.1,0.2,1.7c0.1,0.5,0.1,0.9,0.1,1.2
c0,1.6-1.5,3-3.2,3s-3.1-1.4-3.1-3.1c0-1.8,1.2-3.9,3-5.4c2-1.7,5.5-2.7,9.1-2.7c4.4,0,7.5,1.4,9,4.2c1,1.7,1.4,3.7,1.4,7.3v14
c0,3.2,0.5,4.2,2.2,4.2c1.1,0,1.9-0.4,3.2-1.4v1.9C151.3,79.6,149.8,80.5,147.3,80.5z M142.6,60.5c-8.7,3.2-11.7,5.8-11.7,10v0.3
c0,3.1,2,5.5,4.5,5.5c1.5,0,3.5-0.6,5.3-1.6c1.5-0.9,1.9-1.6,1.9-3.8V60.5z"/>
<path d="M165.3,80.5c-4.2,0-6.3-3.1-6.3-9.1V49.7h-3.8c-0.2-0.1-0.3-0.3-0.3-0.5c0-0.4,0.4-0.9,1.2-1.4
c1.9-1.1,4.3-3.7,7-7.7c0.5-0.6,1-1.3,1.4-2c0.4,0,0.5,0.2,0.5,0.9v8.4h7.3v2.3h-7.3v20.6c0,4.6,1.1,6.5,3.7,6.5
c1.6,0,2.7-0.6,4.3-2.5l0.9,0.8C171.8,78.7,169,80.5,165.3,80.5z"/>
<path d="M193.8,79.7v-1.1c4.1-0.4,4.9-1.3,4.9-6.2V58.1c0-5-1.8-7.6-5.4-7.6c-2.8,0-5,1.2-8,4.5v17.4
c0,5,0.7,5.8,4.9,6.3v1.1h-15.6v-1.1c4.2-0.6,4.6-1.2,4.6-6.3V38.5c0-3.1-0.6-3.7-3.7-3.7c-0.4,0-0.6,0-0.9,0.1v-1.2l1.9-0.6
c4-1.2,5.8-1.7,8.3-2.6l0.4,0.2v21.9c3.3-4.3,6.3-6,10.6-6c5.9,0,8.9,3.9,8.9,11.5v14.3c0,5,0.4,5.5,4.3,6.3v1.1h-15.2V79.7z"/>
<path d="M59.1,116.1v-4.3l30.2-14.9v5.3l-23.8,11.7l23.8,11.7v5.3L59.1,116.1z"/>
<path d="M135.1,131.9v-1.1c4.2-0.5,4.8-1.1,4.8-5.2v-15.1c0-6-1.3-7.9-5.4-7.9c-3.3,0-5.7,1.3-7.8,4.4v18.1
c0,4.5,1.1,5.7,5.2,5.8v1.1h-16.1v-1.1c4.1-0.3,4.9-1.1,4.9-5.1v-15.7c0-5-1.6-7.6-4.8-7.6c-2.5,0-5.6,1.2-7.4,2.9
c-0.5,0.5-1.1,1.4-1.1,1.4v20.3c0,2.8,1.1,3.6,4.9,3.7v1.1h-16v-1.1c4-0.1,5-1.2,5-5v-18.2c0-3.5-0.6-4.6-2.5-4.6
c-0.8,0-1.4,0.1-2.3,0.3v-1.2c4-1.1,6.4-1.9,10.1-3.2l0.5,0.1v5.4c6-4.5,8-5.5,11.2-5.5c3.9,0,6.3,1.9,7.6,6c3.9-4.2,7.6-6,11.7-6
c5.5,0,8.4,4.3,8.4,12.8v14.8c0,2.8,0.9,4.1,3.1,4.2l1.9,0.1v1.1H135.1z"/>
<path d="M152.1,131.9v-1.1c5-0.3,5.7-1.1,5.7-6.3v-16.6c0-3.2-0.6-4.3-2.4-4.3c-0.6,0-1.6,0.1-2.4,0.2l-0.6,0.1v-1.1
l11.2-4L164,99v25.6c0,5.2,0.6,5.9,5.3,6.3v1.1L152.1,131.9L152.1,131.9z M160.8,93.1c-2,0-3.7-1.6-3.7-3.7c0-2,1.7-3.7,3.7-3.7
c2.1,0,3.7,1.7,3.7,3.7C164.6,91.6,163,93.1,160.8,93.1z"/>
<path d="M175.8,131v-5.3l23.7-11.8l-23.7-11.7v-5.3l30.1,14.9v4.3L175.8,131z"/>
<path d="M31.1,169.5v-4.3l30.2-14.9v5.3l-23.8,11.7L61.3,179v5.3L31.1,169.5z"/>
<path d="M71.3,186.4h-4.9l16.5-49.7h4.8L71.3,186.4z"/>
<path d="M127.1,185.3v-1.1c4.2-0.5,4.8-1.1,4.8-5.2v-15.2c0-6-1.3-7.9-5.4-7.9c-3.3,0-5.7,1.3-7.8,4.4v18.1
c0,4.5,1.1,5.7,5.2,5.8v1.1h-16.1v-1.1c4.1-0.3,4.9-1.1,4.9-5.1v-15.6c0-5-1.6-7.6-4.8-7.6c-2.5,0-5.6,1.2-7.4,2.9
c-0.5,0.5-1.1,1.4-1.1,1.4v20.3c0,2.8,1.1,3.6,4.9,3.7v1.1h-16v-1.1c4-0.1,5-1.2,5-5V161c0-3.5-0.6-4.6-2.5-4.6
c-0.8,0-1.4,0.1-2.3,0.3v-1.2c4-1.1,6.4-1.9,10.1-3.2l0.5,0.1v5.4c6-4.5,8-5.5,11.2-5.5c3.9,0,6.3,1.9,7.6,6c3.9-4.2,7.6-6,11.7-6
c5.5,0,8.4,4.3,8.4,12.8v14.8c0,2.8,0.9,4.1,3.1,4.2l1.9,0.1v1.1H127.1L127.1,185.3z"/>
<path d="M168.3,186.1c-3,0-4.2-1.4-4.6-5.3c-4.4,3.7-7.3,5.3-10.5,5.3c-4.5,0-7.6-3.2-7.6-7.7c0-2.4,1-4.8,2.6-6.3
c3.1-2.7,4.3-3.3,15.4-7.8v-4.4c0-3.9-1.9-6-5.5-6c-2.9,0-5.2,1.6-5.2,3.5c0,0.5,0.1,1.1,0.2,1.7c0.1,0.5,0.1,0.9,0.1,1.2
c0,1.6-1.5,3-3.2,3s-3.1-1.4-3.1-3.1c0-1.8,1.2-3.9,3-5.4c2-1.7,5.5-2.7,9.1-2.7c4.4,0,7.5,1.4,9,4.2c1,1.7,1.4,3.7,1.4,7.3v14
c0,3.2,0.5,4.2,2.2,4.2c1.1,0,1.9-0.4,3.2-1.4v1.9C172.3,185.2,170.8,186.1,168.3,186.1z M163.8,166.1c-8.7,3.2-11.7,5.8-11.7,10
v0.3c0,3.1,2,5.5,4.5,5.5c1.5,0,3.5-0.6,5.3-1.6c1.5-0.9,1.9-1.6,1.9-3.8V166.1z"/>
<path d="M186.3,186.1c-4.2,0-6.3-3.1-6.3-9.1v-21.7h-3.8c-0.2-0.1-0.3-0.3-0.3-0.5c0-0.4,0.4-0.9,1.2-1.4
c1.9-1.1,4.3-3.7,7-7.7c0.5-0.6,1-1.3,1.4-2c0.4,0,0.5,0.2,0.5,0.9v8.4h7.3v2.3h-7.3v20.6c0,4.6,1.1,6.5,3.7,6.5
c1.6,0,2.7-0.6,4.3-2.5l0.9,0.8C192.8,184.3,190,186.1,186.3,186.1z"/>
<path d="M214.8,185.3v-1.1c4.2-0.4,4.9-1.4,4.9-6.3v-14.3c0-5-1.8-7.6-5.4-7.6c-2.8,0-5,1.2-8,4.5v17.4
c0,5,0.7,5.8,4.9,6.3v1.1h-15.6v-1.1c4.2-0.6,4.6-1.2,4.6-6.3V144c0-3.1-0.6-3.7-3.7-3.7c-0.4,0-0.6,0-0.9,0.1v-1.2l1.9-0.6
c4-1.2,5.8-1.7,8.3-2.6l0.4,0.2v21.9c3.3-4.3,6.3-6,10.6-6c5.8,0,8.9,3.9,8.9,11.4v14.3c0,5,0.4,5.5,4.3,6.3v1.1L214.8,185.3
L214.8,185.3z"/>
</g>
</svg>
</g>
<g id="arrow_right">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 50">
<path stroke="#000000" fill="#000000" d="m0,0l0,50l25,-25l-25,-25z"/>

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -6,10 +6,9 @@
* Copyright(c) 2009 Narendra Sisodya
*
*/
var put_locale = function(svgCanvas, given_param){
var put_locale = function(svgCanvas, given_param, good_langs){
var lang_param;
// TODO: Make this array be based on entries in svg-editor.html
var good_langs = ['ar','cs','de','en','es','fa','fr','fy','hi','ja','nl','ro','ru','sk','zh-TW'];
if(given_param) {
lang_param = given_param;
} else {
@ -28,7 +27,7 @@ var put_locale = function(svgCanvas, given_param){
if($.inArray(lang_param, good_langs) == -1) {
lang_param = "en";
}
// don't bother on first run if language is English
if(lang_param.indexOf("en") == 0) return;
}

View File

@ -1,4 +1,4 @@
body {
body {
background: #E8E8E8;
}
@ -681,11 +681,12 @@ span.zoom_tool {
text-align: right;
}
#svg_source_editor {
#svg_source_editor, #svg_foreign_editor{
display: none;
}
#svg_source_editor #svg_source_overlay {
#svg_source_editor #svg_source_overlay,
#svg_foreign_editor #svg_foreign_overlay {
position: absolute;
top: 0px;
right: 0px;
@ -696,7 +697,8 @@ span.zoom_tool {
z-index: 5;
}
#svg_source_editor #svg_source_container {
#svg_source_editor #svg_source_container,
#svg_foreign_editor #svg_foreign_container {
position: absolute;
top: 30px;
left: 30px;
@ -778,7 +780,8 @@ span.zoom_tool {
display: block;
}
#svg_source_editor #svg_source_textarea {
#svg_source_editor #svg_source_textarea,
#svg_foreign_editor #svg_foreign_textarea {
position: relative;
width: 95%;
top: 5px;
@ -787,7 +790,8 @@ span.zoom_tool {
font-size: 12px;
}
#svg_source_editor #tool_source_back {
#svg_source_editor #tool_source_back,
#svg_foreign_editor #tool_foreign_back {
text-align: left;
padding-left: 20px;
}
@ -810,7 +814,7 @@ span.zoom_tool {
text-align: left;
}
#svg_source_editor button, #svg_docprops button {
#svg_source_editor button, #svg_foreign_editor button, #svg_docprops button {
padding: 5px 5px 7px 28px;
margin: 5px 20px 0 0;
}
@ -845,6 +849,8 @@ span.zoom_tool {
button#tool_source_save,
button#tool_source_cancel,
button#tool_foreign_save,
button#tool_foreign_cancel,
button#tool_docprops_save,
button#tool_docprops_cancel {
border:1px solid #dedede;
@ -963,4 +969,4 @@ button#tool_docprops_cancel {
-webkit-border-radius: 0px;
}
foreignObject { line-height:1.0; }
foreignObject { line-height:1.0; }

View File

@ -330,6 +330,22 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
</label>
<input id="text" type="text" title="Change text contents" size="35"/>
</div>
<div id="foreignObject_panel">
<div class="toolset">
<label id="tool_foreign_width">w:
<input id="foreign_width" class="attr_changer" title="Change foreignObject's width" size="3" data-attr="width"/>
</label>
<label id="tool_foreign_height">h:
<input id="foreign_height" class="attr_changer" title="Change foreignObject's height" size="3" data-attr="height"/>
</label>
<label id="tool_foreign_font_size">
<span id="foreign_font_sizeLabel">font-size:</span>
<input id="foreign_font_size" class="attr_changer" title="Change Font Size" size="2" value="16" type="text" data-attr="font-size"/>
</label>
<div class="tool_button" id="tool_edit_foreign" title="Edit ForeignObject Content"><span></span></div>
</div>
</div>
<div id="path_node_panel">
<div class="tool_sep"></div>
@ -365,6 +381,7 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
<div class="tool_button" id="tool_text" title="Text Tool [6]"></div>
<div class="tool_button" id="tool_image" title="Image Tool [8]"></div>
<div class="tool_button" id="tool_zoom" title="Zoom Tool [Ctrl+Up/Down]"></div>
<div class="tool_button" id="tool_foreign" title="Foreign Object Tool"></div>
<div style="display: none">
<div id="tool_rect" title="Rectangle"></div>
@ -452,6 +469,19 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
</div>
</div>
<div id="svg_foreign_editor">
<div id="svg_foreign_overlay"></div>
<div id="svg_foreign_container">
<div id="tool_foreign_back" class="toolbar_button">
<button id="tool_foreign_save">Apply Changes</button>
<button id="tool_foreign_cancel">Cancel</button>
</div>
<form>
<textarea id="svg_foreign_textarea" spellcheck="false"></textarea>
</form>
</div>
</div>
<div id="svg_docprops">
<div id="svg_docprops_overlay"></div>
<div id="svg_docprops_container">

View File

@ -165,6 +165,7 @@ function svg_edit_setup() {
var selectedElement = null;
var multiselected = false;
var editingsource = false;
var editingforeign = false;
var docprops = false;
var fillPaint = new $.jGraduate.Paint({solidColor: "FF0000"}); // solid red
@ -705,7 +706,7 @@ function svg_edit_setup() {
var is_node = currentMode == 'pathedit'; //elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false;
$('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,\
#ellipse_panel, #line_panel, #text_panel, #image_panel').hide();
#ellipse_panel, #line_panel, #text_panel, #image_panel, #foreignObject_panel').hide();
if (elem != null) {
var elname = elem.nodeName;
var angle = svgCanvas.getRotationAngle(elem);
@ -771,6 +772,7 @@ function svg_edit_setup() {
circle: ['cx','cy','r'],
ellipse: ['cx','cy','rx','ry'],
line: ['x1','y1','x2','y2'],
foreignObject: [],
text: []
};
@ -807,6 +809,12 @@ function svg_edit_setup() {
$('#text').focus().select();
}
} // text
else if (el_name == 'foreignObject') {
$('#foreignObject_panel').css("display", "inline");
$('#foreign_font_size').val(elem.getAttribute("font-size"));
$('#foreign_width').val(elem.getAttribute("width"));
$('#foreign_height').val(elem.getAttribute("height"));
} //foreignObject
else if(el_name == 'image') {
var xlinkNS="http://www.w3.org/1999/xlink";
var href = elem.getAttributeNS(xlinkNS, "href");
@ -1294,7 +1302,12 @@ function svg_edit_setup() {
toolButtonClick('#tool_text');
svgCanvas.setMode('text');
};
var clickForeign = function(){
toolButtonClick('#tool_foreign');
svgCanvas.setMode('foreign');
};
var clickPath = function(){
toolButtonClick('#tool_path');
svgCanvas.setMode('path');
@ -1499,10 +1512,21 @@ function svg_edit_setup() {
var str = svgCanvas.getSvgString();
$('#svg_source_textarea').val(str);
$('#svg_source_editor').fadeIn();
properlySourceSizeTextArea();
properlySourceSizeTextArea('source');
$('#svg_source_textarea').focus();
};
var showForeignEditor = function(elt){
if (!elt || editingforeign) return;
editingforeign = true;
elt.removeAttribute('fill');
var str = svgCanvas.getForeignString(elt);
$('#svg_foreign_textarea').val(str);
$('#svg_foreign_editor').fadeIn();
properlySourceSizeTextArea('foreign');
$('#svg_foreign_textarea').focus();
};
$('#svg_docprops_container').draggable({cancel:'button,fieldset'});
var showDocProperties = function(){
@ -1538,18 +1562,40 @@ function svg_edit_setup() {
$('#svg_docprops').fadeIn();
};
var properlySourceSizeTextArea = function(){
var properlySourceSizeTextArea = function(str){
// TODO: remove magic numbers here and get values from CSS
var height = $('#svg_source_container').height() - 80;
$('#svg_source_textarea').css('height', height);
var height = $('#svg_'+str+'_container').height() - 80;
$('#svg_'+str+'_textarea').css('height', height);
};
var saveForeignEditor = function(elt){
if (!editingforeign) return;
var saveChanges = function() {
svgCanvas.clearSelection();
hideSourceEditor('foreign');
zoomImage();
populateLayers();
setTitle(svgCanvas.getImageTitle());
}
if (!svgCanvas.setForeignString($('#svg_foreign_textarea').val(), elt)) {
$.confirm(uiStrings.QerrorsRevertToSource, function(ok) {
if(!ok) return false;
saveChanges();
});
} else {
saveChanges();
}
setSelectMode();
}
var saveSourceEditor = function(){
if (!editingsource) return;
var saveChanges = function() {
svgCanvas.clearSelection();
hideSourceEditor();
hideSourceEditor('source');
zoomImage();
populateLayers();
setTitle(svgCanvas.getImageTitle());
@ -1776,18 +1822,28 @@ function svg_edit_setup() {
setFlyoutPositions();
}
var cancelOverlays = function() {
var cancelOverlays = function(elt) {
$('#dialog_box').hide();
if (!editingsource && !docprops) return;
if (!editingsource && !editingforeign && !docprops) return;
if (editingsource) {
var oldString = svgCanvas.getSvgString();
if (oldString != $('#svg_source_textarea').val()) {
$.confirm(uiStrings.QignoreSourceChanges, function(ok) {
if(ok) hideSourceEditor();
if(ok) hideSourceEditor('source');
});
} else {
hideSourceEditor();
hideSourceEditor('source');
}
}
else if (editingforeign) {
var oldString = svgCanvas.getForeignString(elt);
if (oldString != $('#svg_foreign_textarea').val()) {
$.confirm(uiStrings.QignoreSourceChanges, function(ok) {
if(ok) hideSourceEditor('foreign');
});
} else {
hideSourceEditor('foreign');
}
}
else if (docprops) {
@ -1796,10 +1852,11 @@ function svg_edit_setup() {
};
var hideSourceEditor = function(){
$('#svg_source_editor').hide();
var hideSourceEditor = function(str){
$('#svg_'+str+'_editor').hide();
editingsource = false;
$('#svg_source_textarea').blur();
editingforeign = false;
$('#svg_'+str+'_textarea').blur();
};
var hideDocProperties = function(){
@ -1812,8 +1869,11 @@ function svg_edit_setup() {
// TODO: add canvas-centering code in here
$(window).resize(function(evt) {
if (!editingsource) return;
properlySourceSizeTextArea();
if (editingsource) {
properlySourceSizeTextArea('source');
} else if(editingforeign) {
properlySourceSizeTextArea('foreign');
}
});
$('#url_notice').click(function() {
@ -1949,7 +2009,7 @@ function svg_edit_setup() {
var bNoFill = (svgCanvas.getFillColor() == 'none');
var bNoStroke = (svgCanvas.getStrokeColor() == 'none');
var buttonsNeedingStroke = [ '#tool_fhpath', '#tool_line' ];
var buttonsNeedingFillAndStroke = [ '#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text', '#tool_path'];
var buttonsNeedingFillAndStroke = [ '#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text', '#tool_foreign', '#tool_path', '#tool_edit_foreign'];
if (bNoStroke) {
for (index in buttonsNeedingStroke) {
var button = buttonsNeedingStroke[index];
@ -2412,13 +2472,19 @@ function svg_edit_setup() {
{sel:'#tool_text', fn: clickText, evt: 'click', key: 7},
{sel:'#tool_image', fn: clickImage, evt: 'mouseup', key: 8},
{sel:'#tool_zoom', fn: clickZoom, evt: 'mouseup', key: 9},
{sel:'#tool_foreign', fn: clickForeign, evt: 'mouseup'},
{sel:'#tool_edit_foreign', fn: function(){showForeignEditor(selectedElement)}, evt: 'mouseup'},
{sel:'#tool_clear', fn: clickClear, evt: 'mouseup', key: [modKey+'N', true]},
{sel:'#tool_save', fn: function() { editingsource?saveSourceEditor():clickSave()}, evt: 'mouseup', key: [modKey+'S', true]},
{sel:'#tool_save', fn: function() { if(editingsource){saveSourceEditor()}
else if(editingforeign){saveForeignEditor(selectedElement)}
else clickSave()}, evt: 'mouseup', key: [modKey+'S', true]},
{sel:'#tool_open', fn: clickOpen, evt: 'mouseup', key: [modKey+'O', true]},
{sel:'#tool_source', fn: showSourceEditor, evt: 'click', key: ['U', true]},
{sel:'#tool_wireframe', fn: clickWireframe, evt: 'click', key: ['F', true]},
{sel:'#tool_source_cancel,#svg_source_overlay,#tool_docprops_cancel', fn: cancelOverlays, evt: 'click', key: ['esc', false, false], hidekey: true},
{sel:'#tool_foreign_cancel,#svg_source_overlay,#tool_docprops_cancel', fn: function(){cancelOverlays(selectedElement)}, evt: 'click', key: ['esc', false, false], hidekey: true},
{sel:'#tool_source_save', fn: saveSourceEditor, evt: 'click'},
{sel:'#tool_foreign_save', fn: function(){saveForeignEditor(selectedElement)}, evt: 'click'},
{sel:'#tool_docprops_save', fn: saveDocProperties, evt: 'click'},
{sel:'#tool_docprops', fn: showDocProperties, evt: 'mouseup', key: [modKey+'I', true]},
{sel:'#tool_delete,#tool_delete_multi', fn: deleteSelected, evt: 'click', key: ['del/backspace', true]},
@ -2718,7 +2784,7 @@ function svg_edit_setup() {
updateCanvas(true);
});
// var revnums = "svg-editor.js ($Rev: 1372 $) ";
// var revnums = "svg-editor.js ($Rev: 1377 $) ";
// revnums += svgCanvas.getVersion();
// $('#copyright')[0].setAttribute("title", revnums);
return svgCanvas;
@ -2756,6 +2822,8 @@ function svg_edit_setup() {
'text':'text.png',
'image':'image.png',
'zoom':'zoom.png',
'foreign':'foreign.png',
'edit_foreign':'edit_foreign.png',
'clone':'clone.png',
'delete':'delete.png',
@ -2809,6 +2877,8 @@ function svg_edit_setup() {
'#tool_text,#layer_rename':'text',
'#tool_image':'image',
'#tool_zoom':'zoom',
'#tool_foreign':'foreign',
'#tool_edit_foreign':'edit_foreign',
'#tool_clone,#tool_clone_multi,#tool_node_clone':'clone',
'#layer_delete,#tool_delete,#tool_delete_multi,#tool_node_delete':'delete',
@ -2833,8 +2903,8 @@ function svg_edit_setup() {
'#layer_down':'go_down',
'#layerlist td.layervis':'eye',
'#tool_source_save,#tool_docprops_save':'ok',
'#tool_source_cancel,#tool_docprops_cancel':'cancel',
'#tool_source_save,#tool_foreign_save,#tool_docprops_save':'ok',
'#tool_source_cancel,#tool_foreign_cancel,#tool_docprops_cancel':'cancel',
'.flyout_arrow_horiz':'arrow_right',
'.dropdown button, #main_button .dropdown':'arrow_down',
@ -2907,7 +2977,11 @@ function svg_edit_setup() {
// This happens when the page is loaded
$(function() {
svgCanvas = svg_edit_setup();
put_locale(svgCanvas);
var good_langs = [];
$('#lang_select option').each(function() {
good_langs.push(this.value);
});
put_locale(svgCanvas, null, good_langs);
try{
json_encode = function(obj){

View File

@ -1,4 +1,4 @@
/*
/*
* svgcanvas.js
*
* Licensed under the Apache License, Version 2
@ -99,7 +99,7 @@ var isOpera = !!window.opera,
"ellipse": ["class", "clip-path", "clip-rule", "cx", "cy", "fill", "fill-opacity", "fill-rule", "filter", "id", "mask", "opacity", "requiredFeatures", "rx", "ry", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
"feGaussianBlur": ["class", "id", "requiredFeatures", "stdDeviation"],
"filter": ["class", "filterRes", "filterUnits", "height", "id", "primitiveUnits", "requiredFeatures", "width", "x", "xlink:href", "y"],
"foreignObject": ["class", "font-size", "height", "id", "markdown", "opacity", "overflow", "requiredFeatures", "style", "width", "x", "y"],
"foreignObject": ["class", "color", "font-size", "height", "id", "markdown", "opacity", "overflow", "requiredFeatures", "style", "transform", "width", "x", "y"],
"g": ["class", "clip-path", "clip-rule", "id", "display", "fill", "fill-opacity", "fill-rule", "filter", "mask", "opacity", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
"image": ["class", "clip-path", "clip-rule", "filter", "height", "id", "mask", "opacity", "requiredFeatures", "style", "systemLanguage", "transform", "width", "x", "xlink:href", "xlink:title", "y"],
"line": ["class", "clip-path", "clip-rule", "fill", "fill-opacity", "fill-rule", "filter", "id", "marker-end", "marker-mid", "marker-start", "mask", "opacity", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform", "x1", "x2", "y1", "y2"],
@ -913,6 +913,7 @@ function BatchCommand(text) {
// TODO: declare the variables and set them as null, then move this setup stuff to
// an initialization function - probably just use clear()
var canvas = this,
svgns = "http://www.w3.org/2000/svg",
xlinkns = "http://www.w3.org/1999/xlink",
@ -922,7 +923,7 @@ function BatchCommand(text) {
mathns = "http://www.w3.org/1998/Math/MathML",
idprefix = "svg_",
svgdoc = container.ownerDocument,
svgroot = svgdoc.importNode(Utils.text2xml('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
svgroot = svgdoc.importNode(Utils.text2xml('<svg id="svgroot" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' +
'width="640" height="480" x="640" y="480" overflow="visible">' +
'<defs>' +
'<filter id="canvashadow" filterUnits="objectBoundingBox">' +
@ -938,6 +939,10 @@ function BatchCommand(text) {
$(svgroot).appendTo(container);
var nsMap = {};
nsMap[xlinkns] = 'xlink';
nsMap[se_ns] = 'se';
var svgcontent = svgdoc.createElementNS(svgns, "svg");
$(svgcontent).attr({
id: 'svgcontent',
@ -1476,6 +1481,7 @@ function BatchCommand(text) {
var svgToString = function(elem, indent) {
var out = new Array();
if (elem) {
cleanupElement(elem);
var attrs = elem.attributes,
@ -1486,15 +1492,24 @@ function BatchCommand(text) {
for (var i=0; i<indent; i++) out.push(" ");
out.push("<"); out.push(elem.nodeName);
if(elem.id == 'svgcontent') {
// Process root element separately; Prevents errors caused
// in webkit when removing attributes
// Process root element separately
var res = canvas.getResolution();
out.push(' width="' + res.w + '" height="' + res.h
+ '" xmlns:xlink="'+xlinkns+'" xmlns="'+svgns+'"');
if(svgcontent.getAttribute("xmlns:se")) {
// TODO: Check if any se: attributes are actually used
out.push(' xmlns:se="'+se_ns+'"');
}
out.push(' width="' + res.w + '" height="' + res.h + '" xmlns="'+svgns+'"');
var nsuris = {};
// Check elements for namespaces, add if found
$(elem).find('*').each(function() {
var el = this;
$.each(this.attributes, function(i, attr) {
var uri = attr.namespaceURI;
if(uri && !nsuris[uri]) {
nsuris[uri] = true;
out.push(" xmlns:" + nsMap[uri] + '="' + uri +'"');
}
});
});
} else {
for (var i=attrs.length-1; i>=0; i--) {
attr = attrs.item(i);
@ -1520,19 +1535,10 @@ function BatchCommand(text) {
}
// map various namespaces to our fixed namespace prefixes
// TODO: put this into a map and do a look-up instead of if-else
if (attr.namespaceURI == xlinkns) {
out.push('xlink:');
}
else if(attr.namespaceURI == 'http://www.w3.org/2000/xmlns/' && attr.localName != 'xmlns') {
out.push('xmlns:');
}
else if(attr.namespaceURI == xmlns) {
out.push('xml:');
}
else if(attr.namespaceURI == se_ns) {
out.push('se:');
if(attr.namespaceURI && nsMap[attr.namespaceURI]) {
out.push(nsMap[attr.namespaceURI]+':');
}
out.push(attr.localName); out.push("=\"");
out.push(attrVal); out.push("\"");
}
@ -1766,8 +1772,8 @@ function BatchCommand(text) {
// 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 ? seg.x : currentpt.x, // for V commands
thisy = seg.y ? seg.y : currentpt.y, // for H commands
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);
@ -1914,7 +1920,7 @@ function BatchCommand(text) {
}
// if this element had no transforms, we are done
if (tlist.numberOfItems == 0) {
if (!tlist || tlist.numberOfItems == 0) {
selected.removeAttribute("transform");
return null;
}
@ -2012,8 +2018,7 @@ function BatchCommand(text) {
break;
}
}
}
}
var tx = 0, ty = 0,
operation = 0,
N = tlist.numberOfItems;
@ -2229,15 +2234,20 @@ function BatchCommand(text) {
// temporarily strip off the rotate and save the old center
angle = canvas.getRotationAngle(selected);
if (angle) {
var a = angle * Math.PI / 180;
if ( Math.abs(a) > (1.0e-10) ) {
var s = Math.sin(a)/(1 - Math.cos(a));
} else {
// FIXME: This blows up if the angle is exactly 0!
var s = 2/a;
}
for (var i = 0; i < tlist.numberOfItems; ++i) {
var xform = tlist.getItem(i);
if (xform.type == 4) {
// extract old center through mystical arts
var rm = xform.matrix;
var a = angle * Math.PI / 180;
// FIXME: This blows up if the angle is exactly 0 or 180 degrees!
oldcenter.y = 0.5 * (Math.sin(a)*rm.e + (1-Math.cos(a))*rm.f) / (1 - Math.cos(a));
oldcenter.x = ((1 - Math.cos(a)) * oldcenter.y - rm.f) / Math.sin(a);
oldcenter.y = (s*rm.e + rm.f)/2;
oldcenter.x = (rm.e - s*rm.f)/2;
tlist.removeItem(i);
break;
}
@ -2734,9 +2744,12 @@ function BatchCommand(text) {
if (mouse_target.correspondingUseElement)
mouse_target = mouse_target.correspondingUseElement;
// for foreign content, go up until we find the foreignObject
if ($.inArray(mouse_target.namespaceURI, [mathns, htmlns]) != -1) {
while (mouse_target.nodeName != "foreignObject") {
// for foreign content, go up until we find the foreignObject
// WebKit browsers set the mouse target to the svgcanvas div
if ($.inArray(mouse_target.namespaceURI, [mathns, htmlns]) != -1 &&
mouse_target.id != "svgcanvas")
{
while (mouse_target.nodeName != "foreignObject") {
mouse_target = mouse_target.parentNode;
}
}
@ -3016,6 +3029,28 @@ function BatchCommand(text) {
});
newText.textContent = "text";
break;
case "foreign":
started = true;
var newText = addSvgElementFromJson({
"element": "foreignObject",
"attr": {
"x": x,
"y": y,
"id": getNextId(),
"font-size": cur_text.font_size,
"width": "24",
"height": "24",
"style": "pointer-events:inherit"
}
});
var m = svgdoc.createElementNS(mathns, 'math');
m.setAttribute('xmlns', mathns);
m.setAttributeNS(mathns, 'display', 'inline');
var mi = svgdoc.createElementNS(mathns, 'mo');
mi.textContent = "\u03A6";
m.appendChild(mi);
newText.appendChild(m);
break;
case "path":
// Fall through
case "pathedit":
@ -3234,6 +3269,14 @@ function BatchCommand(text) {
'height': Math.abs(y-start_y*current_zoom)
},100);
break;
case "foreignObject":
assignAttributes(shape,{
'width': 24,
'height': 24,
'x': x,
'y': y
},1000);
break;
case "text":
assignAttributes(shape,{
'x': x,
@ -3248,8 +3291,6 @@ function BatchCommand(text) {
shape.setAttributeNS(null, "y2", y);
if (!window.opera) svgroot.unsuspendRedraw(handle);
break;
case "foreignObject":
// fall through
case "square":
// fall through
case "rect":
@ -3457,7 +3498,7 @@ function BatchCommand(text) {
var attrs = $(element).attr(["x1", "x2", "y1", "y2"]);
keep = (attrs.x1 != attrs.x2 || attrs.y1 != attrs.y2);
break;
case "foreignObject":
case "foreign":
case "square":
case "rect":
case "image":
@ -3573,7 +3614,7 @@ function BatchCommand(text) {
element = null;
var t = evt.target;
// if this element is in a group, go up until we reach the top-level group
// just below the layer groups
// TODO: once we implement links, we also would have to check for <a> elements
@ -3599,7 +3640,7 @@ function BatchCommand(text) {
cleanupElement(element);
if(current_mode == "path") {
pathActions.toEditMode(element);
} else if (current_mode == "text" || current_mode == "image") {
} else if (current_mode == "text" || current_mode == "image" || current_mode == "foreign") {
// keep us in the tool we were in unless it was a text or image element
canvas.addToSelection([element], true);
}
@ -5495,6 +5536,19 @@ function BatchCommand(text) {
return svgCanvasToString();
};
// Function: getForeignString(elt)
// Returns the contents of element elt as XML text.
//
// Parameters:
// elt - The foreignObject Element.
//
// Returns:
// The contents of elt as raw XML text.
this.getForeignString = function(elt) {
var s = svgToString(elt, 0);
return s;
};
// Function: setSvgString
// This function sets the current drawing as the input SVG XML.
//
@ -5542,6 +5596,20 @@ function BatchCommand(text) {
canvas.fixOperaXML(svgcontent, newDoc.documentElement);
}
// recalculate dimensions on the top-level children so that unnecessary transforms
// are removed
var deepdive = function(node) {
if (node.nodeType == 1) {
var children = node.children;
var i = children.length;
while (i--) { deepdive(children.item(i)); }
try {
recalculateDimensions(node);
} catch(e) { console.log(e); }
}
}
deepdive(svgcontent);
var content = $(svgcontent);
// determine proper size
@ -5592,6 +5660,32 @@ function BatchCommand(text) {
return true;
};
// 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.
this.setForeignString = function(xmlString, elt) {
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
sanitizeSvg(newDoc.documentElement);
elt.parentNode.replaceChild(newDoc.documentElement.firstChild, elt);
call("changed", [elt]);
} catch(e) {
console.log(e);
return false;
}
return true;
};
// Layer API Functions
// Group: Layers
@ -7649,7 +7743,7 @@ function BatchCommand(text) {
// Function: getVersion
// Returns a string which describes the revision number of SvgCanvas.
this.getVersion = function() {
return "svgcanvas.js ($Rev: 1375 $)";
return "svgcanvas.js ($Rev: 1380 $)";
};
this.setUiStrings = function(strs) {