changeset 428:c5cdc6016107

updated to version of svg-edit Revision r699
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Wed, 23 Sep 2009 20:52:24 +0200
parents 8853f2b49a2a
children a3cc13004cf0
files htdocs/svg-edit/editor/svg-editor.css htdocs/svg-edit/editor/svg-editor.html htdocs/svg-edit/editor/svg-editor.js htdocs/svg-edit/editor/svgcanvas.js htdocs/svg-edit/wave/svg-edit.xml
diffstat 5 files changed, 1328 insertions(+), 360 deletions(-) [+]
line wrap: on
line diff
--- a/htdocs/svg-edit/editor/svg-editor.css	Wed Sep 23 20:50:10 2009 +0200
+++ b/htdocs/svg-edit/editor/svg-editor.css	Wed Sep 23 20:52:24 2009 +0200
@@ -3,9 +3,6 @@
 }
 
 #svg_editor {
-	position: relative;
-	width: 660px;
-	height: 640px;
 	font-size: 8pt;
 	font-family: Verdana, Helvetica, Arial;
 	color: #000000;
@@ -29,7 +26,6 @@
 }
 
 #svg_editor #svgcanvas {
-	position: relative;
 	background-color: #FFFFFF;
 	text-align: center;
 	vertical-align: middle;
@@ -39,7 +35,6 @@
 }
 
 #svg_editor div#palette_holder {
-	position: relative;
 	overflow-x: scroll;
 	overflow-y: hidden;
 	height: 31px;
@@ -62,17 +57,82 @@
 }
 
 #svg_editor div#workarea {
-	position: relative;
-	width: 100%;
-	top: 80px;
+	display: inline-block;
+	position:absolute;
+	top: 75px;
 	left: 40px;
-	right: 2px;
-	bottom: 0px;
+	bottom: 60px;
+	right: 155px;
 	background-color: #A0A0A0;
 	border: 1px solid #808080;
 	overflow: auto;
 }
 
+#svg_editor #sidepanels {
+	display: inline-block;
+	background-color: #E8E8E8;
+	position:absolute;
+	top: 75px;
+	bottom: 60px;
+	right: 0px;
+	width: 126px;
+	border-color: #808080;
+	border-style: solid;
+	border-width: 1px;
+	overflow-y: scroll;
+	padding-left: 12px;
+	padding-right: 12px;
+	margin: 0px;
+}
+
+#svg_editor #layerbuttons {
+	margin: 0px;
+	padding: 0px;
+	padding-left: 2px;
+	padding-right: 2px;
+	width: 106px;
+	height: 20px;
+	border-right: 1px solid #FFFFFF;
+	border-bottom: 1px solid #FFFFFF;
+	border-left: 1px solid #808080;
+	border-top: 1px solid #808080;
+}
+
+#svg_editor .layer_button {
+	width: 14px;
+	height: 14px;
+	padding: 1px;
+	border-left: 1px solid #FFFFFF;
+	border-top: 1px solid #FFFFFF;
+	border-right: 1px solid #808080;
+	border-bottom: 1px solid #808080;
+	cursor: pointer;
+}
+
+#svg_editor .layer_buttonpressed {
+	width: 14px;
+	height: 14px;
+	padding: 1px;
+	border-right: 1px solid #FFFFFF;
+	border-bottom: 1px solid #FFFFFF;
+	border-left: 1px solid #808080;
+	border-top: 1px solid #808080;
+	cursor: pointer;
+}
+
+#svg_editor #layerlist {
+	margin: 0px;
+	width: 110px;
+}
+
+/* TODO: Make this work in more than just Firefox */
+#svg_editor #layerlist option {
+/*	background-image: url('images/eye.png');*/
+	background-repeat: no-repeat;
+	background-position: 4px center;
+	padding-left: 25px;
+}
+
 #svg_editor div.palette_item {
 	height: 16px;
 	width: 16px;
@@ -80,7 +140,6 @@
 }
 
 #svg_editor #logo {
-	display: None;
 	position: absolute;
 	top: 4px;
 	left: 4px;
@@ -98,7 +157,7 @@
 	left: 38px;
 	right: 2px;
 	top: 2px;
-	height: 58px;
+	height: 75px;
 	border-bottom: none;
 }
 
@@ -110,7 +169,7 @@
 	position: absolute;
 	border-right: none;
 	width: 36px;
-	top: 68px;
+	top: 75px;
 	left: 2px;
 }
 
@@ -188,8 +247,6 @@
 	vertical-align:12px;
 }
 
-/* TODO: figure out why there is a couple pixels between
-   the tools with a flyout arrow */
 #svg_editor .flyout_arrow_horiz {
 	float: right;
 	position: relative;
@@ -198,6 +255,66 @@
 	margin-bottom: -13px;
 }
 
+#zoom_panel > * {
+	float: left;
+}
+
+span.zoom_tool {
+	line-height: 26px;
+	padding: 3px;
+}
+
+#zoom {
+	margin-top: 5px;
+}
+
+#zoom_dropdown {
+	position: relative;
+}
+
+#zoom_dropdown button {
+	background: transparent 3px 8px url('images/flyup.gif') no-repeat;
+	width: 15px;
+	height: 21px;
+	margin: 6px 0 0 3px;
+	padding: 0;
+	border-left: 1px solid #FFFFFF;
+	border-top: 1px solid #FFFFFF;
+	border-right: 1px solid #808080;
+	border-bottom: 1px solid #808080;
+}
+
+#zoom_dropdown button.down {
+	border-left: 1px solid #808080;
+	border-top: 1px solid #808080;
+	border-right: 1px solid #FFFFFF;
+	border-bottom: 1px solid #FFFFFF;
+	background-color: #B0B0B0;
+}
+
+#zoom_dropdown ul {
+	list-style: none;
+	position: absolute;
+	margin: 0;
+	padding: 0;
+	left: -93px;
+	bottom: 26px;
+	display: none;
+}
+
+#zoom_dropdown li {
+	display: block;
+	width: 120px;
+	padding: 4px;
+	background: #E8E8E8;
+	border: 1px solid #B0B0B0;
+	margin: 0 0 -1px 0;
+}
+
+#zoom_dropdown li:hover {
+	background-color: #B0B0B0;
+}
+
 #svg_editor .tool_button, #svg_editor .tool_button_current, #svg_editor .tool_button_disabled {
 	height: 24px;
 	width: 24px;
@@ -211,7 +328,6 @@
 }
 
 #svg_editor .tool_button_current {
-	position: relative;
 	border-left: 1px solid #808080;
 	border-top: 1px solid #808080;
 	border-right: 1px solid #FFFFFF;
@@ -220,13 +336,11 @@
 }
 
 #svg_editor .tool_button_disabled {
-	position: relative;
 	opacity: 0.5;
 	cursor: default;
 }
 
 #svg_editor .tool_sep {
-	position: relative;
 	width: 2px;
 	height: 24px;
 	margin: 2px;
@@ -238,6 +352,7 @@
 	position: absolute;
 	display: none;
 	background: #E8E8E8;
+	height: 350px;
 }
 
 #svg_editor .tools_flyout {
@@ -261,12 +376,15 @@
 #svg_editor #tool_fhellipse { background: 2px 2px url('images/freehand-circle.png') no-repeat; }
 #svg_editor #tool_stacktop { background: 2px 2px url('images/move_top.png') no-repeat; }
 #svg_editor #tool_stackbottom { background: 2px 2px url('images/move_bottom.png') no-repeat; }
-#svg_editor #tool_aligntop { background: 2px 2px url('images/align-vertical-top.png') no-repeat; }
-#svg_editor #tool_alignmiddle { background: 2px 2px url('images/align-vertical-middle.png') no-repeat; }
-#svg_editor #tool_alignbottom { background: 2px 2px url('images/align-vertical-bottom.png') no-repeat; }
-#svg_editor #tool_alignleft { background: 2px 2px url('images/align-horizontal-left.png') no-repeat; }
-#svg_editor #tool_aligncenter { background: 2px 2px url('images/align-horizontal-center.png') no-repeat; }
-#svg_editor #tool_alignright { background: 2px 2px url('images/align-horizontal-right.png') no-repeat; }
+#svg_editor #tool_aligntop { background: 2px 2px url('images/align-top.png') no-repeat; }
+#svg_editor #tool_alignmiddle { background: 2px 2px url('images/align-middle.png') no-repeat; }
+#svg_editor #tool_alignbottom { background: 2px 2px url('images/align-bottom.png') no-repeat; }
+#svg_editor #tool_alignleft { background: 2px 2px url('images/align-left.png') no-repeat; }
+#svg_editor #tool_aligncenter { background: 2px 2px url('images/align-center.png') no-repeat; }
+#svg_editor #tool_alignright { background: 2px 2px url('images/align-right.png') no-repeat; }
+#svg_editor .tool_sep { background: 2px 2px url('images/sep.png') no-repeat; }
+#svg_editor #tool_undo { background: 2px 2px url('images/undo.png') no-repeat; }
+#svg_editor #tool_redo { background: 2px 2px url('images/redo.png') no-repeat; }
 
 /* TODO: figure out what more-specific selector causes me to write this atrocity and not
          simply .tool_flyout_button */
@@ -282,7 +400,6 @@
 }
 
 #svg_editor #tools_rect .tool_flyout_button_current, #svg_editor #tools_ellipse .tool_flyout_button_current {
-	position: relative;
 	border-left: 1px solid #808080;
 	border-top: 1px solid #808080;
 	border-right: 1px solid #FFFFFF;
@@ -291,16 +408,14 @@
 }
 
 #svg_editor #tools_bottom {
-	position: relative;
-	top: 80px;
+	position: absolute;
 	left: 40px;
 	right: 2px;
 	bottom: 2px;
-	height: 0px;
+	height: 60px;
 }
 
 #svg_editor #tools_bottom_1 {
-	display: none;
 	width: 115px;
 	float: left;
 }
@@ -311,7 +426,6 @@
 }
 
 #svg_editor #tools_bottom_3 {
-	position: relative;
 }
 
 #svg_editor #copyright {
@@ -343,6 +457,31 @@
 	text-align: center;
 }
 
+/*
+	top: 100px;
+	left: 80px;
+	right: 80px;
+	bottom: 100px;
+*/
+
+#svg_docprops #svg_docprops_container {
+	position: absolute;
+	top: 50px;
+	padding: 10px;
+	background-color: #B0B0B0;
+	opacity: 1.0;
+}
+
+#svg_docprops_container fieldset {
+	padding: 5px;
+	margin: 5px;
+}
+
+#svg_docprops_container label {
+	display: block;
+	margin-bottom: .2em;
+}
+
 #svg_source_editor #svg_source_textarea {
 	position: relative;
 	width: 95%;
@@ -357,15 +496,39 @@
 	padding-left: 20px;
 }
 
-#svg_source_editor button {
-	padding: 5px 2px 6px 28px;
+#svg_docprops_container #tool_docprops_back div {
+	text-align: left;
+	padding: 20px;
+}
+
+#svg_source_editor button, #svg_docprops button {
+	padding: 5px 5px 7px 28px;
 	margin: 5px 20px 0 0;	
 }
 
-#tool_source_save {
-	background: #E8E8E8 url(images/save.png) no-repeat 2px 2px;
+#svg_docprops button {
+	margin-top: 0;
+	margin-bottom: 5px;
 }
 
-#tool_source_cancel {
-	background: #E8E8E8 url(images/cancel.png) no-repeat 2px 2px;
+#svg_docprops {
+	display: none;
 }
+
+#svg_docprops #svg_docprops_overlay {
+	position: absolute;
+	top: 0px;
+	right: 0px;
+	left: 0px;
+	bottom: 0px;
+	background-color: black;
+	opacity: 0.6;
+}
+
+#tool_source_save, #tool_docprops_save {
+	background: #E8E8E8 url(images/save.png) no-repeat 2px 0;
+}
+
+#tool_source_cancel, #tool_docprops_cancel {
+	background: #E8E8E8 url(images/cancel.png) no-repeat 2px 0;
+}
--- a/htdocs/svg-edit/editor/svg-editor.html	Wed Sep 23 20:50:10 2009 +0200
+++ b/htdocs/svg-edit/editor/svg-editor.html	Wed Sep 23 20:52:24 2009 +0200
@@ -2,6 +2,7 @@
 <html>
 <head>
 <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
+<meta http-equiv="X-UA-Compatible" content="chrome=1"/>
 <link rel="icon" type="image/png" href="images/logo.png">
 <link rel="stylesheet" href="jgraduate/css/jPicker-1.0.9.css" type="text/css"/>
 <link rel="stylesheet" href="jgraduate/css/jGraduate-0.2.0.css" type="text/css"/>
@@ -11,6 +12,7 @@
 <!--script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script-->
 <script type="text/javascript" src="js-hotkeys/jquery.hotkeys.min.js"></script>
 <!--script type="text/javascript" src="js-hotkeys/jquery.hotkeys-0.7.9.js"></script-->
+<script type="text/javascript" src="jquery-ui/jquery-ui-1.7.2.custom.min.js"></script>
 <script type="text/javascript" src="jgraduate/jpicker-1.0.9.min.js"></script>
 <script type="text/javascript" src="jgraduate/jquery.jgraduate.js"></script>
 <!--script type="text/javascript" src="jgraduate/jquery.jgraduate.min.js"></script-->
@@ -21,6 +23,10 @@
 <script type="text/javascript" src="svg-editor.js"></script>
 <!--script type="text/javascript" src="svg-editor.min.js"></script-->
 
+<!-- feeds -->
+<link rel="alternate" type="application/atom+xml" title="SVG-edit General Discussion" href="http://groups.google.com/group/svg-edit/feed/atom_v1_0_msgs.xml" />
+<link rel="alternate" type="application/atom+xml" title="SVG-edit Updates (Issues/Fixes/Commits)" href="http://code.google.com/feeds/p/svg-edit/updates/basic" />
+
 <!-- Add script with custom handlers here -->
 <title>SVG-edit demo</title>
 </head>
@@ -29,10 +35,29 @@
 <div id="svg_editor">
 
 <div id="workarea">
-<style id="styleoverrides" type="text/css"></style>
+<style id="styleoverrides" type="text/css" media="screen" scoped="scoped"></style>
 <div id="svgcanvas"></div>
 </div>
 
+<div id="sidepanels">
+	<div id="layerpanel">
+		<h3>Layers</h3>
+		<fieldset id="layerbuttons">
+			<img id="layer_new" class="layer_button" src="images/clear.png" alt="New Layer" title="New Layer"/>
+			<img id="layer_delete" class="layer_button" src="images/delete.png" alt="Delete Layer" title="Delete Layer"/>
+			<img id="layer_rename" class="layer_button" src="images/view-refresh.png" alt="Rename Layer" title="Rename Layer"/>
+			<img id="layer_up" class="layer_button" src="images/go-up.png" alt="Move Layer up" title="Move Layer Up"/>
+			<img id="layer_down" class="layer_button" src="images/go-down.png" alt="Move Layer Down" title="Move Layer Down"/>
+		</fieldset>
+		
+		<select id="layerlist" size="20" multiple="multiple">
+			<option value="Layer 1" selected="selected">Layer 1</option>
+			<option value="Layer 2">Layer 2</option>
+			<option value="Layer 3">Layer 3</option>
+		</select>
+	</div>
+</div>
+
 <div id="logo">
 	<a href="http://svg-edit.googlecode.com/" target="_blank" title="SVG-edit Home Page">
 		<img src="images/logo.png" alt="logo" />
@@ -40,22 +65,23 @@
 </div>
 
 <div id="tools_top" class="tools_panel">
+	
 	<!-- File-like buttons: New, Save, Source -->
-	<div>
-		<img class="tool_sep" src="images/sep.png" alt="|"/>
+	<div id="file_panel">
 		<img class="tool_button" id="tool_clear" src="images/clear.png" title="New Image [N]" alt="Clear" />
 		<img style="display:none" class="tool_button" id="tool_open" src="images/open.png" title="Open Image [O]" alt="Open"/>
 		<img class="tool_button" id="tool_save" src="images/save.png" title="Save Image [S]" alt="Save"/>
+		<img class="tool_button" id="tool_docprops" src="images/document-properties.png" title="Document Properties [I]"/>
 		<img class="tool_button" id="tool_source" src="images/source.png" title="Edit Source [U]" alt="Source"/>
 	</div>
 
     <!-- History buttons -->
-	<div>
-		<img class="tool_sep" src="images/sep.png" alt="|"/>
-		<img class="tool_button tool_button_disabled" id="tool_undo" src="images/undo.png" title="Undo [Z]" alt="Undo" />
-		<img class="tool_button tool_button_disabled" id="tool_redo" src="images/redo.png" title="Redo [Y]" alt="Redo"/>
+	<div id="history_panel">
+		<div class="tool_sep"></div>
+		<div class="tool_button tool_button_disabled" id="tool_undo" title="Undo [Z]"></div>
+		<div class="tool_button tool_button_disabled" id="tool_redo" title="Redo [Y]"></div>
 	</div>
-
+	
 	<!-- Buttons when a single element is selected -->
 	<div id="selected_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
@@ -79,7 +105,7 @@
 			<option value="0">0 %</option>
 		</select>
 		<span class="selected_tool">angle:</span>
-		<input id="angle" class="selected_tool" title="Change rotation angle" alt="Rotation Angle" size="2" value="0" type="text"/>
+		<input id="angle" class="selected_tool" title="Change rotation angle" size="2" value="0" type="text"/>
 	</div>
 
 	<!-- Buttons when multiple elements are selected -->
@@ -88,12 +114,12 @@
 		<img class="tool_button" id="tool_clone_multi" src="images/clone.png" title="Clone Elements [C]" alt="Clone"/>
 		<img class="tool_button" id="tool_delete_multi" src="images/delete.png" title="Delete Selected Elements [Delete/Backspace]" alt="Delete"/>
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
-		<img class="tool_button" id="tool_alignleft" src="images/align-left.png" title="Align Left" alt="Left"/>
-		<img class="tool_button" id="tool_aligncenter" src="images/align-center.png" title="Align Center" alt="Center"/>
-		<img class="tool_button" id="tool_alignright" src="images/align-right.png" title="Align Right" alt="Right"/>
-		<img class="tool_button" id="tool_aligntop" src="images/align-top.png" title="Align Top" alt="Top"/>
-		<img class="tool_button" id="tool_alignmiddle" src="images/align-middle.png" title="Align Middle" alt="Middle"/>
-		<img class="tool_button" id="tool_alignbottom" src="images/align-bottom.png" title="Align Bottom" alt="Bottom"/>
+		<div class="tool_button" id="tool_alignleft" title="Align Left"></div>
+		<div class="tool_button" id="tool_aligncenter" title="Align Center"></div>
+		<div class="tool_button" id="tool_alignright" title="Align Right"></div>
+		<div class="tool_button" id="tool_aligntop" title="Align Top"></div>
+		<div class="tool_button" id="tool_alignmiddle" title="Align Middle"></div>
+		<div class="tool_button" id="tool_alignbottom" title="Align Bottom"></div>
 		<span class="selected_tool">relative to:</span>
 		<select id="align_relative_to" class="selected_tool" title="Align relative to ...">
 		<option value="selected">selected objects</option>
@@ -113,27 +139,27 @@
 	<div id="rect_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
 		<label class="rect_tool">x:</label>
-		<input id="rect_x" class="rect_tool attr_changer" title="Change rectangle X coordinate" alt="x" size="3"/>
+		<input id="rect_x" class="rect_tool attr_changer" title="Change rectangle X coordinate" size="3"/>
 		<label class="rect_tool">y:</label>
-		<input id="rect_y" class="rect_tool attr_changer" title="Change rectangle Y coordinate" alt="y" size="3"/>
+		<input id="rect_y" class="rect_tool attr_changer" title="Change rectangle Y coordinate" size="3"/>
 		<label class="rect_tool">width:</label>
-		<input id="rect_width" class="rect_tool attr_changer" title="Change rectangle width" alt="width" size="3"/>
+		<input id="rect_width" class="rect_tool attr_changer" title="Change rectangle width" size="3"/>
 		<label class="rect_tool">height:</label>
-		<input id="rect_height" class="rect_tool attr_changer" title="Change rectangle height" alt="height" size="3"/>
+		<input id="rect_height" class="rect_tool attr_changer" title="Change rectangle height" size="3"/>
 		<label class="rect_tool">Corner Radius:</label>
-		<input id="rect_rx" size="3" value="0" class="rect_tool" type="text" title="Change Rectangle Corner Radius" alt="Corner Radius"/>
+		<input id="rect_rx" size="3" value="0" class="rect_tool" type="text" title="Change Rectangle Corner Radius"/>
 	</div>
 
 	<div id="image_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
 		<label class="image_tool">x:</label>
-		<input id="image_x" class="image_tool attr_changer" title="Change image X coordinate" alt="x" size="3"/>
+		<input id="image_x" class="image_tool attr_changer" title="Change image X coordinate" size="3"/>
 		<label class="image_tool">y:</label>
-		<input id="image_y" class="image_tool attr_changer" title="Change image Y coordinate" alt="y" size="3"/>
+		<input id="image_y" class="image_tool attr_changer" title="Change image Y coordinate" size="3"/>
 		<label class="image_tool">width:</label>
-		<input id="image_width" class="image_tool attr_changer" title="Change image width" alt="width" size="3"/>
+		<input id="image_width" class="image_tool attr_changer" title="Change image width" size="3"/>
 		<label class="image_tool">height:</label>
-		<input id="image_height" class="image_tool attr_changer" title="Change image height" alt="height" size="3"/>
+		<input id="image_height" class="image_tool attr_changer" title="Change image height" size="3"/>
     <label class="image_tool">url:</label>
     <input id="image_url" class="image_tool" type="text" title="Change URL" size="35"/>
   </div>
@@ -142,43 +168,43 @@
 	<div id="circle_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
 		<label class="circle_tool">cx:</label>
-		<input id="circle_cx" class="circle_tool attr_changer" title="Change circle's cx coordinate" alt="cx" size="3"/>
+		<input id="circle_cx" class="circle_tool attr_changer" title="Change circle's cx coordinate" size="3"/>
 		<label class="circle_tool">cy:</label>
-		<input id="circle_cy" class="circle_tool attr_changer" title="Change circle's cy coordinate" alt="cy" size="3"/>
+		<input id="circle_cy" class="circle_tool attr_changer" title="Change circle's cy coordinate" size="3"/>
 		<label class="circle_tool">r:</label>
-		<input id="circle_r" class="circle_tool attr_changer" title="Change circle's radius" alt="r" size="3"/>
+		<input id="circle_r" class="circle_tool attr_changer" title="Change circle's radius" size="3"/>
 	</div>
 
 	<div id="ellipse_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
 		<label class="ellipse_tool">cx:</label>
-		<input id="ellipse_cx" class="ellipse_tool attr_changer" title="Change ellipse's cx coordinate" alt="cx" size="3"/>
+		<input id="ellipse_cx" class="ellipse_tool attr_changer" title="Change ellipse's cx coordinate" size="3"/>
 		<label class="ellipse_tool">cy:</label>
-		<input id="ellipse_cy" class="ellipse_tool attr_changer" title="Change ellipse's cy coordinate" alt="cy" size="3"/>
+		<input id="ellipse_cy" class="ellipse_tool attr_changer" title="Change ellipse's cy coordinate" size="3"/>
 		<label class="ellipse_tool">rx:</label>
-		<input id="ellipse_rx" class="ellipse_tool attr_changer" title="Change ellipse's x radius" alt="rx" size="3"/>
+		<input id="ellipse_rx" class="ellipse_tool attr_changer" title="Change ellipse's x radius" size="3"/>
 		<label class="ellipse_tool">ry:</label>
-		<input id="ellipse_ry" class="ellipse_tool attr_changer" title="Change ellipse's y radius" alt="ry" size="3"/>
+		<input id="ellipse_ry" class="ellipse_tool attr_changer" title="Change ellipse's y radius" size="3"/>
 	</div>
 
 	<div id="line_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
 		<label class="line_tool">x1:</label>
-		<input id="line_x1" class="line_tool attr_changer" title="Change line's starting x coordinate" alt="x1" size="3"/>
+		<input id="line_x1" class="line_tool attr_changer" title="Change line's starting x coordinate" size="3"/>
 		<label class="line_tool">y1:</label>
-		<input id="line_y1" class="line_tool attr_changer" title="Change line's starting y coordinate" alt="y1" size="3"/>
+		<input id="line_y1" class="line_tool attr_changer" title="Change line's starting y coordinate" size="3"/>
 		<label class="line_tool">x2:</label>
-		<input id="line_x2" class="line_tool attr_changer" title="Change line's ending x coordinate" alt="x2" size="3"/>
+		<input id="line_x2" class="line_tool attr_changer" title="Change line's ending x coordinate" size="3"/>
 		<label class="line_tool">y2:</label>
-		<input id="line_y2" class="line_tool attr_changer" title="Change line's ending y coordinate" alt="x1" size="3"/>
+		<input id="line_y2" class="line_tool attr_changer" title="Change line's ending y coordinate" size="3"/>
 	</div>
 
 	<div id="text_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
 		<label class="text_tool">x:</label>
-		<input id="text_x" class="text_tool attr_changer" title="Change text X coordinate" alt="x" size="3"/>
+		<input id="text_x" class="text_tool attr_changer" title="Change text X coordinate" size="3"/>
 		<label class="text_tool">y:</label>
-		<input id="text_y" class="text_tool attr_changer" title="Change text Y coordinate" alt="y" size="3"/>
+		<input id="text_y" class="text_tool attr_changer" title="Change text Y coordinate" size="3"/>
 		<img class="tool_button" id="tool_bold" src="images/bold.png" title="Bold Text [B]" alt="Bold"/>
 		<img class="tool_button" id="tool_italic" src="images/italic.png" title="Italic Text [I]" alt="Italic"/>
 		<select id="font_family" class="text_tool" title="Change Font Family">
@@ -220,20 +246,32 @@
 	<img class="tool_button" id="tool_text" src="images/text.png" title="Text Tool [6]" alt="Text"/>
 	<img class="tool_button" id="tool_poly" src="images/polygon.png" title="Poly Tool [7]" alt="Poly"/>
 	<img class="tool_button" id="tool_image" src="images/image.png" title="Image Tool [8]" alt="Image"/>
+	<img class="tool_button" id="tool_zoom" src="images/zoom.png" title="Zoom Tool [Ctrl+Up/Down]" alt="Zoom"/>
 </div> <!-- tools_left -->
 
 <div id="tools_bottom" class="tools_panel">
 
-	<div id="tools_bottom_1">
-		<select id="resolution">
-			<option selected="selected">640x480</option>
-			<option>800x600</option>
-			<option>1024x768</option>
-			<option>1280x960</option>
-			<option>1600x1200</option>
-			<option>Fit to Content</option>
-			<option>Custom</option>
-		</select>
+    <!-- Zoom buttons -->
+	<div id="zoom_panel">
+		<span class="zoom_tool">zoom:</span>
+		<input id="zoom" class="zoom_tool" title="Change zoom level" size="3" value="100" type="text"/>
+		<div id="zoom_dropdown">
+			<button></button>
+			<ul>
+				<li>100%</li>
+				<li data-val="content">Fit to all content</li>
+				<li data-val="layer">Fit to layer content</li>
+				<li data-val="selection">Fit to selection</li>
+				<li data-val="canvas">Fit to canvas</li>
+				<li>25%</li>
+				<li>50%</li>
+				<li>100%</li>
+				<li>200%</li>
+				<li>400%</li>
+				<li>1000%</li>
+			</ul>
+		</div>
+		<img class="tool_sep" src="images/sep.png" alt="|"/>
 	</div>
 
 	<div id="tools_bottom_2">
@@ -241,13 +279,13 @@
 		<tr>
 			<td>fill:</td>
 			<td><div id="fill_color" class="color_block"  title="Change fill color"></div></td>
-			<td><div id="fill_opacity">100%</div></td>
+			<td colspan="3"><div id="fill_opacity">100%</div></td>
 		</tr><tr>
 			<td>stroke:</td>
 			<td><div id="stroke_color" class="color_block" title="Change stroke color"></div></td>
 			<td><div id="stroke_opacity">100 %</div></td>
 			<td>
-				<input id="stroke_width" title="Change stroke width" alt="Stroke Width" size="2" value="5" type="text"/>
+				<input id="stroke_width" title="Change stroke width" size="2" value="5" type="text"/>
 			</td>
 			<td>
 				<select id="stroke_style" title="Change stroke dash style">
@@ -283,20 +321,6 @@
 	<div id="tool_fhellipse" class="tool_flyout_button" title="Free-Hand Ellipse"></div>
 </div>
 
-<div id="tools_stacking" class="tools_flyout_v">
-	<div id="tool_stacktop" class="tool_flyout_button" title="Move to Top [Shift+Up]"></div>
-	<div id="tool_stackbottom" class="tool_flyout_button" title="Move to Bottom [Shift+Down]"></div>
-</div>
-
-<div id="tools_align" class="tools_flyout_v">
-	<div id="tool_aligntop" class="tool_flyout_button" title="Align to Top"></div>
-	<div id="tool_alignbottom" class="tool_flyout_button" title="Align to Bottom"></div>
-	<div id="tool_alignmiddle" class="tool_flyout_button" title="Align to Middle"></div>
-	<div id="tool_alignleft" class="tool_flyout_button" title="Align to Left"></div>
-	<div id="tool_alignright" class="tool_flyout_button" title="Align to Right"></div>
-	<div id="tool_aligncenter" class="tool_flyout_button" title="Align to Center"></div>
-</div>
-
 </div> <!-- svg_editor -->
 
 <div id="svg_source_editor">
@@ -312,5 +336,41 @@
 	</div>
 </div>
 
+<div id="svg_docprops">
+	<div id="svg_docprops_overlay"></div>
+	<div id="svg_docprops_container">
+		<div id="tool_docprops_back" class="toolbar_button">
+			<button id="tool_docprops_save">Save</button>
+			<button id="tool_docprops_cancel">Cancel</button>
+
+			<fieldset id="change_background">
+				<legend>Canvas Background</legend>
+					<div id="bkgnd_color" class="color_block"  title="Change background color/opacity"></div>
+			</fieldset>
+			
+			<fieldset id="change_resolution">
+				<legend>Canvas Dimensions</legend>
+
+				<label>Width: <input type="text" id="canvas_width" size="6"></label>
+					
+				<label>Height: <input type="text" id="canvas_height" size="6"></label>
+					
+				<label>
+					<select id="resolution">
+						<option selected="selected">Select predefined:</option>
+						<option>640x480</option>
+						<option>800x600</option>
+						<option>1024x768</option>
+						<option>1280x960</option>
+						<option>1600x1200</option>
+						<option value="content">Fit to Content</option>
+					</select>
+				</label>
+				
+			</fieldset>
+		</div>
+	</div>
+</div>
+
 </body>
 </html>
--- a/htdocs/svg-edit/editor/svg-editor.js	Wed Sep 23 20:50:10 2009 +0200
+++ b/htdocs/svg-edit/editor/svg-editor.js	Wed Sep 23 20:52:24 2009 +0200
@@ -1,29 +1,18 @@
+/*
 if(!window.console) {
   window.console = new function() {
     this.log = function(str) {};
     this.dir = function(str) {};
   };
 }
+*/
 
 function svg_edit_setup() {
 	var palette = ["#000000","#202020","#404040","#606060","#808080","#a0a0a0","#c0c0c0","#e0e0e0","#ffffff","#800000","#ff0000","#808000","#ffff00","#008000","#00ff00","#008080","#00ffff","#000080","#0000ff","#800080","#ff00ff","#2b0000","#550000","#800000","#aa0000","#d40000","#ff0000","#ff2a2a","#ff5555","#ff8080","#ffaaaa","#ffd5d5","#280b0b","#501616","#782121","#a02c2c","#c83737","#d35f5f","#de8787","#e9afaf","#f4d7d7","#241c1c","#483737","#6c5353","#916f6f","#ac9393","#c8b7b7","#e3dbdb","#2b1100","#552200","#803300","#aa4400","#d45500","#ff6600","#ff7f2a","#ff9955","#ffb380","#ffccaa","#ffe6d5","#28170b","#502d16","#784421","#a05a2c","#c87137","#d38d5f","#deaa87","#e9c6af","#f4e3d7","#241f1c","#483e37","#6c5d53","#917c6f","#ac9d93","#c8beb7","#e3dedb","#2b2200","#554400","#806600","#aa8800","#d4aa00","#ffcc00","#ffd42a","#ffdd55","#ffe680","#ffeeaa","#fff6d5","#28220b","#504416","#786721","#a0892c","#c8ab37","#d3bc5f","#decd87","#e9ddaf","#f4eed7","#24221c","#484537","#6c6753","#918a6f","#aca793","#c8c4b7","#e3e2db","#222b00","#445500","#668000","#88aa00","#aad400","#ccff00","#d4ff2a","#ddff55","#e5ff80","#eeffaa","#f6ffd5","#22280b","#445016","#677821","#89a02c","#abc837","#bcd35f","#cdde87","#dde9af","#eef4d7","#22241c","#454837","#676c53","#8a916f","#a7ac93","#c4c8b7","#e2e3db","#112b00","#225500","#338000","#44aa00","#55d400","#66ff00","#7fff2a","#99ff55","#b3ff80","#ccffaa","#e5ffd5","#17280b","#2d5016","#447821","#5aa02c","#71c837","#8dd35f","#aade87","#c6e9af","#e3f4d7","#1f241c","#3e4837","#5d6c53","#7c916f","#9dac93","#bec8b7","#dee3db","#002b00","#005500","#008000","#00aa00","#00d400","#00ff00","#2aff2a","#55ff55","#80ff80","#aaffaa","#d5ffd5","#0b280b","#165016","#217821","#2ca02c","#37c837","#5fd35f","#87de87","#afe9af","#d7f4d7","#1c241c","#374837","#536c53","#6f916f","#93ac93","#b7c8b7","#dbe3db","#002b11","#005522","#008033","#00aa44","#00d455","#00ff66","#2aff80","#55ff99","#80ffb3","#aaffcc","#d5ffe6","#0b2817","#16502d","#217844","#2ca05a","#37c871","#5fd38d","#87deaa","#afe9c6","#d7f4e3","#1c241f","#37483e","#536c5d","#6f917c","#93ac9d","#b7c8be","#dbe3de","#002b22","#005544","#008066","#00aa88","#00d4aa","#00ffcc","#2affd5","#55ffdd","#80ffe6","#aaffee","#d5fff6","#0b2822","#165044","#217867","#2ca089","#37c8ab","#5fd3bc","#87decd","#afe9dd","#d7f4ee","#1c2422","#374845","#536c67","#6f918a","#93aca7","#b7c8c4","#dbe3e2","#00222b","#004455","#006680","#0088aa","#00aad4","#00ccff","#2ad4ff","#55ddff","#80e5ff","#aaeeff","#d5f6ff","#0b2228","#164450","#216778","#2c89a0","#37abc8","#5fbcd3","#87cdde","#afdde9","#d7eef4","#1c2224","#374548","#53676c","#6f8a91","#93a7ac","#b7c4c8","#dbe2e3","#00112b","#002255","#003380","#0044aa","#0055d4","#0066ff","#2a7fff","#5599ff","#80b3ff","#aaccff","#d5e5ff","#0b1728","#162d50","#214478","#2c5aa0","#3771c8","#5f8dd3","#87aade","#afc6e9","#d7e3f4","#1c1f24","#373e48","#535d6c","#6f7c91","#939dac","#b7bec8","#dbdee3","#00002b","#000055","#000080","#0000aa","#0000d4","#0000ff","#2a2aff","#5555ff","#8080ff","#aaaaff","#d5d5ff","#0b0b28","#161650","#212178","#2c2ca0","#3737c8","#5f5fd3","#8787de","#afafe9","#d7d7f4","#1c1c24","#373748","#53536c","#6f6f91","#9393ac","#b7b7c8","#dbdbe3","#11002b","#220055","#330080","#4400aa","#5500d4","#6600ff","#7f2aff","#9955ff","#b380ff","#ccaaff","#e5d5ff","#170b28","#2d1650","#442178","#5a2ca0","#7137c8","#8d5fd3","#aa87de","#c6afe9","#e3d7f4","#1f1c24","#3e3748","#5d536c","#7c6f91","#9d93ac","#beb7c8","#dedbe3","#22002b","#440055","#660080","#8800aa","#aa00d4","#cc00ff","#d42aff","#dd55ff","#e580ff","#eeaaff","#f6d5ff","#220b28","#441650","#672178","#892ca0","#ab37c8","#bc5fd3","#cd87de","#ddafe9","#eed7f4","#221c24","#453748","#67536c","#8a6f91","#a793ac","#c4b7c8","#e2dbe3","#2b0022","#550044","#800066","#aa0088","#d400aa","#ff00cc","#ff2ad4","#ff55dd","#ff80e5","#ffaaee","#ffd5f6","#280b22","#501644","#782167","#a02c89","#c837ab","#d35fbc","#de87cd","#e9afdd","#f4d7ee","#241c22","#483745","#6c5367","#916f8a","#ac93a7","#c8b7c4","#e3dbe2","#2b0011","#550022","#800033","#aa0044","#d40055","#ff0066","#ff2a7f","#ff5599","#ff80b2","#ffaacc","#ffd5e5","#280b17","#50162d","#782144","#a02c5a","#c83771","#d35f8d","#de87aa","#e9afc6","#f4d7e3","#241c1f","#48373e","#6c535d","#916f7c","#ac939d","#c8b7be","#e3dbde"]
 
 	var isMac = false; //(navigator.platform.indexOf("Mac") != -1);
 	var modKey = ""; //(isMac ? "meta+" : "ctrl+");
-	var htdocs = document.getElementById("htdocs").innerHTML.replace('<!-- ', '').replace(' -->', '');
 	var svgCanvas = new SvgCanvas(document.getElementById("svgcanvas"));
-	// because moin uses currently HTML 4.0.1 we send the data as a comment in the HTML code
-	var svgdata = document.getElementById("svgdata").innerHTML;
-	var filecontent = svgdata.slice(8, svgdata.length - 9);
-	if (filecontent != '') {
-	    if (!svgCanvas.setSvgString(filecontent)) {
-	        if(!confirm('There were parsing errors in your SVG source.\nRevert back to original SVG source?') ) {
-	           return false;
-	        }
-	    }
-	    svgCanvas.clearSelection();
-	}
-
 
 	var setSelectMode = function() {
 		$('.tool_button_current').removeClass('tool_button_current').addClass('tool_button');
@@ -38,6 +27,7 @@
 	var selectedElement = null;
 	var multiselected = false;
 	var editingsource = false;
+	var docprops = false;
 	var length_attrs = ['x','y','x1','x2','y1','y2','cx','cy','width','height','r','rx','ry','width','height','radius'];
 	var length_types = ['em','ex','px','cm','mm','in','pt','pc','%'];
 	
@@ -49,17 +39,7 @@
 	// with a gradient will appear black in Firefox, etc.  See bug 308590
 	// https://bugzilla.mozilla.org/show_bug.cgi?id=308590
 	var saveHandler = function(window,svg) {
-	var titlename = window.content.parent.document.title.split('-')[0];
-	titlename = titlename.replace(' ', '');
-	var svg_data = Utils.encode64(svg);
-	var pagename = titlename.split(':')[0];
-	var svg_target = titlename.split(':')[1];
-	$.post(
-	        pagename,
-	        {'action': "SvgEditor", 'do': "save", 'target': svg_target, 'svg_data': svg_data}
-	      );
-
-		//window.open("data:image/svg+xml;base64," + Utils.encode64(svg));
+		window.open("data:image/svg+xml;base64," + Utils.encode64(svg));
 	};
 
 	// called when we've selected a different element
@@ -102,6 +82,27 @@
 		// text element was previously in focus
 		updateContextPanel();
 	};
+	
+	var zoomChanged = function(window, bbox) {
+		var scrbar = 15;
+		var res = svgCanvas.getResolution();
+		
+		var w_area = $('#workarea');
+		w_area.css('cursor','auto');
+		var z_info = svgCanvas.setBBoxZoom(bbox, w_area.width()-scrbar, w_area.height()-scrbar);
+		if(!z_info) return;
+		var zoomlevel = z_info.zoom;
+		var bb = z_info.bbox;
+		$('#zoom').val(Math.round(zoomlevel*100));
+		setResolution(res.w * zoomlevel, res.h * zoomlevel);
+		var scrLeft = bb.x * zoomlevel;
+		var scrOffX = w_area.width()/2 - (bb.width * zoomlevel)/2;
+		w_area[0].scrollLeft = Math.max(0,scrLeft - scrOffX);
+		var scrTop = bb.y * zoomlevel;
+		var scrOffY = w_area.height()/2 - (bb.height * zoomlevel)/2;
+		w_area[0].scrollTop = Math.max(0,scrTop - scrOffY);
+		clickSelect();
+	}
 
 	// updates the toolbar (colors, opacity, etc) based on the selected element
 	var updateToolbar = function() {
@@ -281,6 +282,7 @@
 	svgCanvas.bind("selected", selectedChanged);
 	svgCanvas.bind("changed", elementChanged);
 	svgCanvas.bind("saved", saveHandler);
+	svgCanvas.bind("zoomed", zoomChanged);
 
 	var str = '<div class="palette_item" style="background-image: url(\'images/none.png\');" data-rgb="none"></div>'
 	$.each(palette, function(i,item){
@@ -289,9 +291,9 @@
 	$('#palette').append(str);
 
 	var pos = $('#tools_rect_show').position();
-	$('#tools_rect').css({'left': pos.left+4, 'top': pos.top+70});
+	$('#tools_rect').css({'left': pos.left+4, 'top': pos.top+77});
 	pos = $('#tools_ellipse_show').position();
-	$('#tools_ellipse').css({'left': pos.left+4, 'top': pos.top+70});
+	$('#tools_ellipse').css({'left': pos.left+4, 'top': pos.top+77});
 
 	var changeRectRadius = function(ctl) {
 		svgCanvas.setRectRadius(ctl.value);
@@ -308,6 +310,14 @@
 	var changeRotationAngle = function(ctl) {
 		svgCanvas.setRotationAngle(ctl.value);
 	}
+	var changeZoom = function(ctl) {
+		var zoomlevel = ctl.value / 100;
+		var res = svgCanvas.getResolution();
+		// Hack to increase properly from 10%
+		if(res.zoom < zoomlevel && res.zoom == .1) $('#zoom').val(50);
+		setResolution(res.w * zoomlevel, res.h * zoomlevel, true);
+		svgCanvas.setZoom(zoomlevel);
+	}
 
 	$('#stroke_style').change(function(){
 		svgCanvas.setStrokeStyle(this.options[this.selectedIndex].value);
@@ -427,6 +437,59 @@
 		svgCanvas.clearSelection();
 		return true;
 	};
+	
+	var setZoomOpts = function() {
+		var button = $('#zoom_dropdown button');
+		var list = $('#zoom_dropdown ul');
+		var on_button = false;
+
+		$('#zoom_dropdown li').bind('mouseup',function() {
+			var item = $(this);
+			var val = item.attr('data-val');
+			var res = svgCanvas.getResolution();
+			var scrbar = 15;
+			if(val) {
+				var w_area = $('#workarea');
+				var z_info = svgCanvas.setBBoxZoom(val, w_area.width()-scrbar, w_area.height()-scrbar);
+				if(!z_info) return;
+				var zoomlevel = z_info.zoom;
+				var bb = z_info.bbox;
+				$('#zoom').val(zoomlevel*100);
+				setResolution(res.w * zoomlevel, res.h * zoomlevel);
+				var scrLeft = bb.x * zoomlevel;
+				var scrOffX = w_area.width()/2 - (bb.width * zoomlevel)/2;
+				w_area[0].scrollLeft = Math.max(0,scrLeft - scrOffX);
+				var scrTop = bb.y * zoomlevel;
+				var scrOffY = w_area.height()/2 - (bb.height * zoomlevel)/2;
+				w_area[0].scrollTop = Math.max(0,scrTop - scrOffY);
+			} else {
+				var percent = parseInt(item.text());
+				$('#zoom').val(percent);
+				var zoomlevel = percent/100;
+				setResolution(res.w * zoomlevel, res.h * zoomlevel, true);
+				svgCanvas.setZoom(zoomlevel);
+			}
+		});
+		
+		$().mouseup(function() {
+			if(!on_button) {
+				button.removeClass('down');
+				list.hide();
+			}
+			on_button = false;
+		});
+		
+		button.bind('mousedown mouseup',function() {
+			button.addClass('down');
+			list.show();
+		}).hover(function() {
+			on_button = true;
+		}).mouseout(function() {
+			on_button = false;
+		});
+	};
+	
+	setZoomOpts();
 
 	var clickSelect = function() {
 		if (toolButtonClick('#tool_select')) {
@@ -452,14 +515,14 @@
 			flyoutspeed = 'normal';
 			svgCanvas.setMode('square');
 		}
-		$('#tools_rect_show').attr('src', htdocs + 'images/square.png');
+		$('#tools_rect_show').attr('src', 'images/square.png');
 	};
 
 	var clickRect = function(){
 		if (toolButtonClick('#tools_rect_show')) {
 			svgCanvas.setMode('rect');
 		}
-		$('#tools_rect_show').attr('src', htdocs + 'images/rect.png');
+		$('#tools_rect_show').attr('src', 'images/rect.png');
 	};
 
 	var clickImage = function(){
@@ -468,11 +531,28 @@
 		}
 	};
 
+	var clickZoom = function(){
+		if (toolButtonClick('#tool_zoom')) {
+			$('#workarea').css('cursor','crosshair');
+			svgCanvas.setMode('zoom');
+		}
+	};
+
+	var dblclickZoom = function(){
+		if (toolButtonClick('#tool_zoom')) {
+			var res = svgCanvas.getResolution();
+			setResolution(res.w, res.h);
+			$('#zoom').val(100);
+			svgCanvas.setZoom(1);
+			setSelectMode();
+		}
+	};
+
 	var clickFHRect = function(){
 		if (toolButtonClick('#tools_rect_show')) {
 			svgCanvas.setMode('fhrect');
 		}
-		$('#tools_rect_show').attr('src', htdocs + 'images/freehand-square.png');
+		$('#tools_rect_show').attr('src', 'images/freehand-square.png');
 	};
 
 	var clickCircle = function(){
@@ -480,21 +560,21 @@
 			flyoutspeed = 'normal';
 			svgCanvas.setMode('circle');
 		}
-		$('#tools_ellipse_show').attr('src', htdocs + 'images/circle.png');
+		$('#tools_ellipse_show').attr('src', 'images/circle.png');
 	};
 
 	var clickEllipse = function(){
 		if (toolButtonClick('#tools_ellipse_show')) {
 			svgCanvas.setMode('ellipse');
 		}
-		$('#tools_ellipse_show').attr('src', htdocs + 'images/ellipse.png');
+		$('#tools_ellipse_show').attr('src', 'images/ellipse.png');
 	};
 
 	var clickFHEllipse = function(){
 		if (toolButtonClick('#tools_ellipse_show')) {
 			svgCanvas.setMode('fhellipse');
 		}
-		$('#tools_ellipse_show').attr('src', htdocs + 'images/freehand-circle.png');
+		$('#tools_ellipse_show').attr('src', 'images/freehand-circle.png');
 	};
 
 	var clickText = function(){
@@ -575,13 +655,17 @@
 	};
 
 	var clickUndo = function(){
-		if (svgCanvas.getUndoStackSize() > 0)
+		if (svgCanvas.getUndoStackSize() > 0) {
 			svgCanvas.undo();
+			populateLayers();
+		}
 	};
 
 	var clickRedo = function(){
-		if (svgCanvas.getRedoStackSize() > 0)
+		if (svgCanvas.getRedoStackSize() > 0) {
 			svgCanvas.redo();
+			populateLayers();
+		}
 	};
 	
 	var clickGroup = function(){
@@ -618,10 +702,11 @@
 		svgCanvas.alignSelectedElements('b', $('#align_relative_to option:selected').val() );
 	};
 
-	var clickZoom = function(zoomIn) {
+	var zoomImage = function(zoomIn) {
 		var res = svgCanvas.getResolution();
 		var multiplier = zoomIn? res.zoom * 2 : res.zoom * 0.5;
 		setResolution(res.w * multiplier, res.h * multiplier, true);
+		$('#zoom').val(multiplier * 100);
 		svgCanvas.setZoom(multiplier);
 	};
 
@@ -635,6 +720,19 @@
 		$('#svg_source_textarea').focus();
 	};
 	
+	var showDocProperties = function(){
+		if (docprops) return;
+		docprops = true;
+		
+		// update resolution option with actual resolution
+		// TODO: what if SVG source is changed?
+		var res = svgCanvas.getResolution();
+		$('#canvas_width').val(res.w);
+		$('#canvas_height').val(res.h);
+	
+		$('#svg_docprops').fadeIn();
+	};
+	
 	var properlySourceSizeTextArea = function(){
 		// TODO: remove magic numbers here and get values from CSS
 		var height = $('#svg_source_container').height() - 80;
@@ -651,18 +749,38 @@
 		}
 		svgCanvas.clearSelection();
 		hideSourceEditor();
+		populateLayers();		
+	};
+	
+	var saveDocProperties = function(){
+		// update resolution
+		var x = parseInt($('#canvas_width').val());
+		var y = parseInt($('#canvas_height').val());
+		if(isNaN(x) || isNaN(y)) {
+			x ='fit';
+		}
+		if(!svgCanvas.setResolution(x,y)) {
+			alert('No content to fit to');
+			return false;
+		}
+		hideDocProperties();
 	};
 
-	var cancelSourceEditor = function() {
-		if (!editingsource) return;
+	var cancelOverlays = function() {
+		if (!editingsource && !docprops) return;
 
-		var oldString = svgCanvas.getSvgString();
-		if (oldString != $('#svg_source_textarea').val()) {
-			if( !confirm('Ignore changes made to SVG source?') ) {
-				return false;
+		if (editingsource) {
+			var oldString = svgCanvas.getSvgString();
+			if (oldString != $('#svg_source_textarea').val()) {
+				if( !confirm('Ignore changes made to SVG source?') ) {
+					return false;
+				}
 			}
+			hideSourceEditor();
 		}
-		hideSourceEditor();
+		else if (docprops) {
+			hideDocProperties();
+		}
 	};
 
 	var hideSourceEditor = function(){
@@ -671,6 +789,13 @@
 		$('#svg_source_textarea').blur();
 	};
 	
+	var hideDocProperties = function(){
+		$('#svg_docprops').hide();
+		$('#canvas_width,#canvas_height').removeAttr('disabled');
+		$('#resolution')[0].selectedIndex = 0;
+		docprops = false;
+	};
+	
 	// TODO: add canvas-centering code in here
 	$(window).resize(function(evt) {
 		if (!editingsource) return;
@@ -687,14 +812,18 @@
 	$('#tool_ellipse').mouseup(clickEllipse);
 	$('#tool_fhellipse').mouseup(clickFHEllipse);
 	$('#tool_image').mouseup(clickImage);
+	$('#tool_zoom').mouseup(clickZoom);
+	$('#tool_zoom').dblclick(dblclickZoom);
 	$('#tool_text').click(clickText);
 	$('#tool_poly').click(clickPoly);
 	$('#tool_clear').click(clickClear);
 	$('#tool_save').click(clickSave);
 	$('#tool_open').click(clickOpen);
 	$('#tool_source').click(showSourceEditor);
-	$('#tool_source_cancel,#svg_source_overlay').click(cancelSourceEditor);
+	$('#tool_source_cancel,#svg_source_overlay,#tool_docprops_cancel').click(cancelOverlays);
 	$('#tool_source_save').click(saveSourceEditor);
+	$('#tool_docprops_save').click(saveDocProperties);
+	$('#tool_docprops').click(showDocProperties);
 	$('#tool_delete').click(deleteSelected);
 	$('#tool_delete_multi').click(deleteSelected);
 	$('#tool_move_top').click(moveToTopSelected);
@@ -781,8 +910,8 @@
 			['shift+right', function(){rotateSelected(1)}],
 			['shift+O', selectPrev],
 			['shift+P', selectNext],
-			['ctrl+up', function(evt){clickZoom(true);evt.preventDefault();}],
-			['ctrl+down', function(evt){clickZoom();evt.preventDefault();}],
+			['ctrl+up', function(evt){zoomImage(true);evt.preventDefault();}],
+			['ctrl+down', function(evt){zoomImage();evt.preventDefault();}],
 			['up', function(evt){moveSelected(0,-1);evt.preventDefault();}],
 			['down', function(evt){moveSelected(0,1);evt.preventDefault();}],
 			['left', function(evt){moveSelected(-1,0);evt.preventDefault();}],
@@ -790,9 +919,10 @@
 			[modKey+'z', function(evt){clickUndo();evt.preventDefault();}],
 			[modKey+'y', function(evt){clickRedo();evt.preventDefault();}],
 			[modKey+'u', function(evt){showSourceEditor();evt.preventDefault();}],
+			[modKey+'i', function(evt){showDocProperties();evt.preventDefault();}],
 			[modKey+'c', function(evt){clickClone();evt.preventDefault();}],
 			[modKey+'g', function(evt){clickGroup();evt.preventDefault();}],
-			['esc', cancelSourceEditor, false]
+			['esc', cancelOverlays, false],
 		];
 		
 		$.each(keys,function(i,item) {
@@ -807,7 +937,6 @@
 	
 	setKeyBindings();
 
-	// TODO: fix opacity being updated
 	// TODO: go back to the color boxes having white background-color and then setting
 	//       background-image to none.png (otherwise partially transparent gradients look weird)	
 	var colorPicker = function(elem) {
@@ -822,7 +951,10 @@
 			was_none = true;
 		}
 		var pos = elem.position();
-		$('#color_picker').css({'left': pos.left - 140, 'bottom': 124 - pos.top}).jGraduate(
+		$("#color_picker")
+			.draggable({cancel:'.jPicker_table,.jGraduate_lgPick'})
+			.css({'left': pos.left, 'bottom': 50 - pos.top})
+			.jGraduate(
 			{ 
 				paint: paint,
 				window: { pickerTitle: title },
@@ -948,23 +1080,129 @@
 	}).mouseout(function() {
 		$(this).removeClass('tool_flyout_button_current');
 	});
+	
+	$('.layer_button').mousedown(function() { 
+		$(this).addClass('layer_buttonpressed');
+	}).mouseout(function() {
+		$(this).removeClass('layer_buttonpressed');
+	}).mouseup(function() {
+		$(this).removeClass('layer_buttonpressed');
+	});
+	
+	$('#layer_new').click(function() {
+		var curNames = new Array(svgCanvas.getNumLayers());
+		for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); }
+	
+		var newName = prompt("Please enter a unique layer name","");
+		if (!newName) return;
+		if (jQuery.inArray(newName, curNames) != -1) {
+			alert("There is already a layer named that!");
+			return;
+		}
+		svgCanvas.createLayer(newName);
+		updateContextPanel();
+		populateLayers();
+		$('#layerlist option').removeAttr("selected");
+		$('#layerlist option:first').attr("selected", "selected");
+	});
+	
+	$('#layer_delete').click(function() {
+		if (svgCanvas.deleteCurrentLayer()) {
+			updateContextPanel();
+			populateLayers();
+			// This matches what SvgCanvas does
+			// TODO: make this behavior less brittle (svg-editor should get which
+			// layer is selected from the canvas and then select that one in the UI)
+			$('#layerlist option:first').attr("selected", "selected");
+		}
+	});
+	
+	$('#layer_up').click(function() {
+		// find index position of selected option
+		var curIndex = $('#layerlist option:selected').prevAll().length;
+		if (curIndex > 0) {
+			var total = $('#layerlist option').length;
+			curIndex--;
+			svgCanvas.setCurrentLayerPosition(total-curIndex-1);
+			populateLayers();
+			$('#layerlist option').removeAttr("selected");
+			$('#layerlist option:eq('+curIndex+')').attr("selected", "selected");
+		}
+	});
+
+	$('#layer_down').click(function() {
+		// find index position of selected option
+		var curIndex = $('#layerlist option:selected').prevAll().length;
+		var total = $('#layerlist option').length;
+		if (curIndex < total-1) {
+			curIndex++;
+			svgCanvas.setCurrentLayerPosition(total-curIndex-1);
+			populateLayers();
+			$('#layerlist option').removeAttr("selected");
+			$('#layerlist option:eq('+curIndex+')').attr("selected", "selected");
+		}
+	});
+
+	$('#layer_rename').click(function() {
+		var curIndex = $('#layerlist option:selected').prevAll().length;
+		var oldName = $('#layerlist option:selected').attr("value");
+		var newName = prompt("Please enter the new layer name","");
+		if (!newName) return;
+		if (oldName == newName) {
+			alert("Layer already has that name");
+			return;
+		}
+
+		var curNames = new Array(svgCanvas.getNumLayers());
+		for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); }
+		if (jQuery.inArray(newName, curNames) != -1) {
+			alert("There is already a layer named that!");
+			return;
+		}
+		
+		svgCanvas.renameCurrentLayer(newName);
+		populateLayers();
+		$('#layerlist option').removeAttr("selected");
+		$('#layerlist option:eq('+curIndex+')').attr("selected", "selected");
+	});
+
+	var populateLayers = function(){
+		$('#layerlist').empty();
+		var layer = svgCanvas.getNumLayers();
+		// we get the layers in the reverse z-order (the layer rendered on top is listed first)
+		while (layer--) {
+			var name = svgCanvas.getLayer(layer);
+			$('#layerlist').append("<option value=\"" + name + "\">" + name + "</option>");
+		}
+		// if we only have one layer, then always make sure that layer is selected
+		// (This is really only required upon first initialization)
+		if ($('#layerlist').size() == 1) {
+			$('#layerlist option:first').attr("selected", "selected");
+		}
+		$('#layerlist option').mouseup(function(evt){
+			$('#layerlist option').removeAttr("selected");
+			var option = $(this);
+			option.attr("selected", "selected");
+			svgCanvas.setCurrentLayer(option.attr("value"));
+		}).click(function(evt) {
+			var container = document.getElementById("layerlist");
+//			var mouse_x = evt.pageX - container.boxObject.x;
+//			var mouse_y = evt.pageY - container.boxObject.y;
+			// mouse_x, mouse_y contain the relative x,y position of the click
+		});
+	};
+	populateLayers();
 
 	function changeResolution(x,y) {
-		var new_res = x+'x'+y;
-		var found = false;
-		$('#resolution option').each(function() {
-			if($(this).text() == new_res) {
-				$('#resolution').val(x+'x'+y);
-				found = true;
-			}
-		});
-		if(!found) $('#resolution').val('Custom');
 		var zoom = svgCanvas.getResolution().zoom;
 		setResolution(x * zoom, y * zoom);
 	}
 	
 	function setResolution(w, h, center) {
+		w-=0; h-=0;
 		$('#svgcanvas').css( { 'width': w, 'height': h } );
+		$('#canvas_width').val(w);
+		$('#canvas_height').val(h);
 		if(center) {
 			var w_area = $('#workarea');
 			var scroll_y = h/2 - w_area.height()/2;
@@ -975,32 +1213,29 @@
 	}
 
 	$('#resolution').change(function(){
-		if(this.value == 'Custom') {
-			var cust_val = prompt("Please enter custom size (i.e. 400x300)","");
-			var res_vals = cust_val.match(/(\d+)[x \/,](\d+)/);
-			if(!res_vals) {
-				alert('Invalid size. Please format it as WIDTHxHEIGHT (like 400x300)');
-				return false;
-			} else {
-				var x = res_vals[1], y = res_vals[2];
-				if(x == '0' || y == '0') {
-					alert('Invalid size. Width or height may not be 0.');
-					return false;
-				}
+		var wh = $('#canvas_width,#canvas_height');
+		if(!this.selectedIndex) {
+			if($('#canvas_width').val() == 'fit') {
+				wh.removeAttr("disabled").val(100);
 			}
-		} else if(this.value == 'Fit to content'){
-			var x = '', y = '';
+		} else if(this.value == 'content') {
+			wh.val('fit').attr("disabled","disabled");
 		} else {
-			var res = this.value.split('x');
-			var x = parseInt(res[0]), y = parseInt(res[1]);
+			var dims = this.value.split('x');
+			$('#canvas_width').val(dims[0]);
+			$('#canvas_height').val(dims[1]);
+			wh.removeAttr("disabled");
 		}
-		svgCanvas.setResolution(x,y);
 	});
 
+	//Prevent browser from erroneously repopulating fields
+	$('input,select').attr("autocomplete","off");
+
 	$('#rect_rx').SpinButton({ min: 0, max: 1000, step: 1, callback: changeRectRadius });
 	$('#stroke_width').SpinButton({ min: 0, max: 99, step: 1, callback: changeStrokeWidth });
 	$('#angle').SpinButton({ min: -180, max: 180, step: 5, callback: changeRotationAngle });
-
+	$('#zoom').SpinButton({ min: 10, max: 10000, step: 50, callback: changeZoom });
+	
 	svgCanvas.setCustomHandlers = function(opts) {
 		if(opts.open) {
 			$('#tool_open').show();
@@ -1010,11 +1245,22 @@
 			svgCanvas.bind("saved", opts.save);
 		}
 	}
-
+	
 	return svgCanvas;
 };
 
 // This happens when the page is loaded
 $(function() {
 	svgCanvas = svg_edit_setup();
+	
+	try{
+	  window.addEventListener("message", function(e){
+	    try{
+        e.source.postMessage(eval(e.data), e.origin);
+      }catch(err){
+        e.source.postMessage("error:"+err.message, e.origin);
+      }
+    }, false)
+	}catch(err){}
+	
 });
--- a/htdocs/svg-edit/editor/svgcanvas.js	Wed Sep 23 20:50:10 2009 +0200
+++ b/htdocs/svg-edit/editor/svgcanvas.js	Wed Sep 23 20:52:24 2009 +0200
@@ -1,8 +1,24 @@
+/*
+Issue 73 (Layers) TODO:
+
+- Fit To Content must look at all layers
+- convert select/options to tables, handle 'selection' of rows
+- add visibility icon to table as a column
+- determine how to toggle visibility of layers (UI-wise)
+- hide the pointer-events stuff from the serialized SVG source somehow
+- create a mouseover region on the sidepanels that is resizable and affects all children within
+- default the side panel to closed
+- add a button that opens the side panel?
+
+*/
+
 if(!window.console) {
-  window.console = new function() {
-    this.log = function(str) {};
-    this.dir = function(str) {};
-  };
+	window.console = {};
+	window.console.log = function(str) {};
+	window.console.dir = function(str) {};
+}
+if( window.opera ) {
+	window.console.log = function(str) {opera.postError(str);}
 }
 
 // this defines which elements and attributes that we support
@@ -24,6 +40,7 @@
 	"stop": ["id", "offset", "stop-color", "stop-opacity"],
 	"svg": ["id", "height", "transform", "viewBox", "width", "xmlns", "xmlns:xlink"],
 	"text": ["fill", "fill-opacity", "font-family", "font-size", "font-style", "font-weight", "id", "opacity", "stroke", "stroke-dasharray", "stroke-linecap", "stroke-linejoin", "stroke-opacity", "stroke-width", "transform", "text-anchor", "x", "y"],
+	"title": [],
 };
 
 function SvgCanvas(c)
@@ -59,14 +76,18 @@
 			var angle = canvas.getRotationAngle(elem);
 			if (angle) {
 				var bbox = elem.getBBox();
-				var cx = parseInt(bbox.x + bbox.width/2),
-					cy = parseInt(bbox.y + bbox.height/2);
+				var cx = round(bbox.x + bbox.width/2),
+					cy = round(bbox.y + bbox.height/2);
 				var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
 				if (rotate != elem.getAttribute("transform")) {
 					elem.setAttribute("transform", rotate);
 				}
 			}
 		}
+		// if we are changing layer names, re-identify all layers
+		if (this.elem.tagName == "title" && this.elem.parentNode.parentNode == svgzoom) {
+			canvas.identifyLayers();
+		}		
 		return true;
 	};
 
@@ -88,14 +109,18 @@
 			var angle = canvas.getRotationAngle(elem);
 			if (angle) {
 				var bbox = elem.getBBox();
-				var cx = parseInt(bbox.x + bbox.width/2),
-					cy = parseInt(bbox.y + bbox.height/2);
+				var cx = round(bbox.x + bbox.width/2),
+					cy = round(bbox.y + bbox.height/2);
 				var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
 				if (rotate != elem.getAttribute("transform")) {
 					elem.setAttribute("transform", rotate);
 				}
 			}
 		}
+		// if we are changing layer names, re-identify all layers
+		if (this.elem.tagName == "title" && this.elem.parentNode.parentNode == svgzoom) {
+			canvas.identifyLayers();
+		}		
 		return true;
 	};
 
@@ -107,11 +132,19 @@
 	this.text = text || ("Create " + elem.tagName);
 	this.parent = elem.parentNode;
 
-	this.apply = function() { this.elem = this.parent.insertBefore(this.elem, this.elem.nextSibling); };
+	this.apply = function() { 
+		this.elem = this.parent.insertBefore(this.elem, this.elem.nextSibling); 
+		if (this.parent == svgzoom) {
+			canvas.identifyLayers();
+		}		
+	};
 
 	this.unapply = function() {
 		this.parent = this.elem.parentNode;
 		this.elem = this.elem.parentNode.removeChild(this.elem);
+		if (this.parent == svgzoom) {
+			canvas.identifyLayers();
+		}		
 	};
 
 	this.elements = function() { return [this.elem]; };
@@ -125,9 +158,17 @@
 	this.apply = function() {
 		this.parent = this.elem.parentNode;
 		this.elem = this.parent.removeChild(this.elem);
+		if (this.parent == svgzoom) {
+			canvas.identifyLayers();
+		}		
 	};
 
-	this.unapply = function() { this.elem = this.parent.insertBefore(this.elem, this.elem.nextSibling); };
+	this.unapply = function() { 
+		this.elem = this.parent.insertBefore(this.elem, this.elem.nextSibling); 
+		if (this.parent == svgzoom) {
+			canvas.identifyLayers();
+		}		
+	};
 
 	this.elements = function() { return [this.elem]; };
 }
@@ -142,10 +183,16 @@
 
 	this.apply = function() {
 		this.elem = this.newParent.insertBefore(this.elem, this.newNextSibling);
+		if (this.newParent == svgzoom) {
+			canvas.identifyLayers();
+		}
 	};
 
 	this.unapply = function() {
 		this.elem = this.oldParent.insertBefore(this.elem, this.oldNextSibling);
+		if (this.oldParent == svgzoom) {
+			canvas.identifyLayers();
+		}
 	};
 
 	this.elements = function() { return [this.elem]; };
@@ -300,7 +347,7 @@
 			this.rotateGrip.setAttribute("display", bShow);
 			this.rotateGripConnector.setAttribute("display", bShow);
 			var elem = this.selectedElement;
-			if(elem && elem.tagName == "text") bShow = "none";
+			if(elem && (elem.tagName == "text" || elem.tagName == "g")) bShow = "none";
 			for (dir in this.selectorGrips) {
 				this.selectorGrips[dir].setAttribute("display", bShow);
 			}
@@ -330,13 +377,13 @@
 			var selectedBox = this.selectorRect;
 			var selectedGrips = this.selectorGrips;
 			var selected = this.selectedElement;
-			var sw = parseInt(selected.getAttribute("stroke-width"));
-			var offset = 1;
-			if (!isNaN(sw)) {
+			var sw = round(selected.getAttribute("stroke-width"));
+			var offset = 1/canvas.getZoom();
+			if (selected.getAttribute("stroke") != "none" && !isNaN(sw)) {
 				offset += sw/2;
 			}
 			if (selected.tagName == "text") {
-				offset += 2;
+				offset += 2/canvas.getZoom();
 			}
 			var oldbox = canvas.getBBox(this.selectedElement);
 			var bbox = cur_bbox || oldbox;
@@ -381,8 +428,8 @@
 			var transform = elem.getAttribute("transform");
 			var angle = canvas.getRotationAngle(elem);
 			if (angle) {
-				var cx = parseInt(oldbox.x + oldbox.width/2) * current_zoom
-					cy = parseInt(oldbox.y + oldbox.height/2) * current_zoom;
+				var cx = round(oldbox.x + oldbox.width/2) * current_zoom
+					cy = round(oldbox.y + oldbox.height/2) * current_zoom;
 				this.selectorGroup.setAttribute("transform", "rotate("+angle+" " + cx + "," + cy + ")");
 			}
 			svgroot.unsuspendRedraw(sr_handle);
@@ -536,18 +583,22 @@
 		var shape = svgdoc.getElementById(data.attr.id);
 		// if shape is a path but we need to create a rect/ellipse, then remove the path
 		if (shape && data.element != shape.tagName) {
-			svgzoom.removeChild(shape);
+			current_layer.removeChild(shape);
 			shape = null;
 		}
 		if (!shape) {
 			shape = svgdoc.createElementNS(svgns, data.element);
-			svgzoom.appendChild(shape);
+			if (current_layer) {
+				current_layer.appendChild(shape);
+			}
 		}
 		assignAttributes(shape, data.attr, 100);
 		cleanupElement(shape);
 		return shape;
 	};
 
+	// 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;
 	var container = c;
 	var svgns = "http://www.w3.org/2000/svg";
@@ -569,7 +620,18 @@
 	svgroot.appendChild(svgzoom);
 	var comment = svgdoc.createComment(" created with SVG-edit - http://svg-edit.googlecode.com/ ");
 	svgzoom.appendChild(comment);
-
+	// TODO For Issue 208: this is a start on a thumbnail
+//	var svgthumb = svgdoc.createElementNS(svgns, "use");
+//	svgthumb.setAttribute('width', '100');
+//	svgthumb.setAttribute('height', '100');
+//	svgthumb.setAttributeNS(xlinkns, 'href', '#svgzoom');
+//	svgroot.appendChild(svgthumb);
+	// z-ordered array of tuples containing layer names and <g> elements
+	// the first layer is the one at the bottom of the rendering
+	var all_layers = [];
+	// pointer to the current layer <g>
+	var current_layer = null;
+	
 	var d_attr = null;
 	var started = false;
 	var obj_num = 1;
@@ -625,8 +687,12 @@
 	var events = {};
 	var undoStackPointer = 0;
 	var undoStack = [];
+	var curBBoxes = [];
 
-	var curBBoxes = [];
+	// This method rounds the incoming value to the nearest value based on the current_zoom
+	var round = function(val){
+		return parseInt(val*current_zoom)/current_zoom;
+	};
 
 	// This method sends back an array or a NodeList full of elements that
 	// intersect the multi-select rubber-band-box.
@@ -642,12 +708,12 @@
 
 		if(!curBBoxes.length) {
 			// Cache all bboxes
-			curBBoxes = canvas.getVisibleElements(true);
+			curBBoxes = canvas.getVisibleElements(svgzoom, true);
 		}
 		
 		var resultList = null;
 		try {
-			resultList = svgroot.getIntersectionList(rect, null);
+			resultList = current_layer.getIntersectionList(rect, null);
 		} catch(e) { }
 
 		if (resultList == null || typeof(resultList.item) != "function") {
@@ -823,10 +889,6 @@
 			for (i=attrs.length-1; i>=0; i--) {
 				attr = attrs.item(i);
 				if (attr.nodeValue != "") {
-					//Opera bug turns N.N to N,N in some locales
-					if (window.opera && attr.nodeName == 'opacity' && /^\d+,\d+$/.test(attr.nodeValue)) {
-						attr.nodeValue = attr.nodeValue.replace(',','.');
-					}
 					out.push(" "); 
 					// map various namespaces to our fixed namespace prefixes
 					// TODO: put this into a map and do a look-up instead of if-else
@@ -921,13 +983,16 @@
 		// after this point, we have some change to this element
 
 		var remap = function(x,y) {
+				// Prevent division by 0
+				if(!box.height) box.height = 1;
+				if(!box.width) box.width = 1;
 				return { 
-							'x':parseInt(((x-box.x)/box.width)*selectedBBox.width + selectedBBox.x),
-							'y':parseInt(((y-box.y)/box.height)*selectedBBox.height + selectedBBox.y)
+							'x':(((x-box.x)/box.width)*selectedBBox.width + selectedBBox.x),
+							'y':(((y-box.y)/box.height)*selectedBBox.height + selectedBBox.y)
 							};					
 			};
-		var scalew = function(w) {return parseInt(w*selectedBBox.width/box.width);}
-		var scaleh = function(h) {return parseInt(h*selectedBBox.height/box.height);}
+		var scalew = function(w) {return (w*selectedBBox.width/box.width);}
+		var scaleh = function(h) {return (h*selectedBBox.height/box.height);}
 
 		var batchCmd = new BatchCommand("Transform");
 		
@@ -936,8 +1001,8 @@
 		var pointGripContainer = document.getElementById("polypointgrip_container");
 		if (angle) {
 			// this is our old center upon which we have rotated the shape
-			var tr_x = parseInt(box.x + box.width/2),
-				tr_y = parseInt(box.y + box.height/2);
+			var tr_x = round(box.x + box.width/2),
+				tr_y = round(box.y + box.height/2);
 			var cx = null, cy = null;
 			
 			var bFoundScale = false;
@@ -971,12 +1036,12 @@
 					bottom = r * Math.sin(theta) + tr_y;
 			
 				// now find mid-point of line between top-left and bottom-right to find new center
-				cx = parseInt(left + (right-left)/2);
-				cy = parseInt(top + (bottom-top)/2);
+				cx = round(left + (right-left)/2);
+				cy = round(top + (bottom-top)/2);
 			
 				// now that we know the center and the axis-aligned width/height, calculate the x,y
-				selectedBBox.x = parseInt(cx - selectedBBox.width/2),
-				selectedBBox.y = parseInt(cy - selectedBBox.height/2);
+				selectedBBox.x = round(cx - selectedBBox.width/2),
+				selectedBBox.y = round(cy - selectedBBox.height/2);
 			}
 			// if it was not a resize, then it was a translation only
 			else {
@@ -1016,8 +1081,8 @@
 			while (i--) {
 				var child = children.item(i);
 				if (child.nodeType == 1) {
-					var childBox = child.getBBox();
-					if (childBox) {
+					try {
+						var childBox = child.getBBox();
 						// TODO: to fix the rotation problem, we must account for the
 						// child's rotation in the bbox adjustment
 						
@@ -1042,7 +1107,7 @@
 						childBox.x = pt.x; childBox.y = pt.y;
 						childBox.width = w; childBox.height = h;
 						batchCmd.addSubCommand(recalculateDimensions(child, childBox));
-					}
+					} catch(e) {}
 				}
 			}
 			return batchCmd;
@@ -1180,7 +1245,7 @@
 				'cy': pt.y,
 	
 				// take the minimum of the new selected box's dimensions for the new circle radius
-				'r': parseInt(Math.min(selectedBBox.width/2,selectedBBox.height/2))
+				'r': round(Math.min(selectedBBox.width/2,selectedBBox.height/2))
 			}, 1000);
 			break;
 		case "ellipse":
@@ -1245,14 +1310,15 @@
 // public events
 
 	this.clearSelection = function() {
-		if (selectedElements[0] == null) { return; }
-		var len = selectedElements.length;
-		for (var i = 0; i < len; ++i) {
-			var elem = selectedElements[i];
-			if (elem == null) break;
-			selectorManager.releaseSelector(elem);
-			selectedElements[i] = null;
-			selectedBBoxes[i] = null;
+		if (selectedElements[0] != null) {
+			var len = selectedElements.length;
+			for (var i = 0; i < len; ++i) {
+				var elem = selectedElements[i];
+				if (elem == null) break;
+				selectorManager.releaseSelector(elem);
+				selectedElements[i] = null;
+				selectedBBoxes[i] = null;
+			}
 		}
 		call("selected", selectedElements);
 	};
@@ -1351,11 +1417,10 @@
 				started = true;
 				current_resize_mode = "none";
 				var t = evt.target;
-				// if this element is in a group, go up until we reach the top-level group
-				// TODO: once we implement Layers, the top-level groups will be layers so
-				// we will want to stop just before then (parentNode.parentNode)
+				// 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
-				while (t.parentNode.tagName == "g") {
+				while (t.parentNode.parentNode.tagName == "g") {
 					t = t.parentNode;
 				}
 				// WebKit returns <div> when the canvas is clicked, Firefox/Opera return <svg>
@@ -1391,6 +1456,21 @@
 					}, 100);
 				}
 				break;
+			case "zoom": 
+				started = true;
+				start_x = x;
+				start_y = y;
+				if (rubberBox == null) {
+					rubberBox = selectorManager.getRubberBandBox();
+				}
+				assignAttributes(rubberBox, {
+						'x': start_x * current_zoom,
+						'y': start_y * current_zoom,
+						'width': 0,
+						'height': 0,
+						'display': 'inline'
+				}, 100);
+				break;
 			case "resize":
 				started = true;
 				start_x = x;
@@ -1416,7 +1496,8 @@
 						"stroke-opacity": cur_shape.stroke_opacity,
 						"stroke-linecap": "round",
 						"stroke-linejoin": "round",
-						"opacity": cur_shape.opacity / 2
+						"opacity": cur_shape.opacity / 2,
+						"style": "pointer-events:all"
 					}
 				});
 				freehand_min_x = x;
@@ -1436,7 +1517,8 @@
 						"width": 0,
 						"height": 0,
 						"id": getNextId(),
-						"opacity": cur_shape.opacity / 2
+						"opacity": cur_shape.opacity / 2,
+						"style": "pointer-events:all"
 					}
 				});
         		newImage.setAttributeNS(xlinkns, "href", "images/logo.png");
@@ -1462,7 +1544,8 @@
 						"stroke-dasharray": cur_shape.stroke_style,
 						"stroke-opacity": cur_shape.stroke_opacity,
 						"fill-opacity": cur_shape.fill_opacity,
-						"opacity": cur_shape.opacity / 2
+						"opacity": cur_shape.opacity / 2,
+						"style": "pointer-events:all"
 					}
 				});
 				break;
@@ -1482,7 +1565,8 @@
 						"stroke-dasharray": cur_shape.stroke_style,
 						"stroke-opacity": cur_shape.stroke_opacity,
 						"fill": "none",
-						"opacity": cur_shape.opacity / 2
+						"opacity": cur_shape.opacity / 2,
+						"style": "pointer-events:all"
 					}
 				});
 				break;
@@ -1501,7 +1585,8 @@
 						"stroke-dasharray": cur_shape.stroke_style,
 						"stroke-opacity": cur_shape.stroke_opacity,
 						"fill-opacity": cur_shape.fill_opacity,
-						"opacity": cur_shape.opacity / 2
+						"opacity": cur_shape.opacity / 2,
+						"style": "pointer-events:all"
 					}
 				});
 				break;
@@ -1521,7 +1606,8 @@
 						"stroke-dasharray": cur_shape.stroke_style,
 						"stroke-opacity": cur_shape.stroke_opacity,
 						"fill-opacity": cur_shape.fill_opacity,
-						"opacity": cur_shape.opacity / 2
+						"opacity": cur_shape.opacity / 2,
+						"style": "pointer-events:all"
 					}
 				});
 				break;
@@ -1543,7 +1629,8 @@
 						"opacity": cur_shape.opacity,
 						"font-size": cur_text.font_size,
 						"font-family": cur_text.font_family,
-						"text-anchor": "middle"
+						"text-anchor": "middle",
+						"style": "pointer-events:all"
 					}
 				});
 				newText.textContent = "text";
@@ -1624,8 +1711,8 @@
 							selectedBBoxes[i].y = box.y + dy;
 							var angle = canvas.getRotationAngle(selected);
 							if (angle) {
-								var cx = parseInt(box.x + box.width/2),
-									cy = parseInt(box.y + box.height/2);
+								var cx = round(box.x + box.width/2),
+									cy = round(box.y + box.height/2);
 								var xform = ts + [" rotate(", angle, " ", cx, ",", cy, ")"].join('');
  								var r = Math.sqrt( dx*dx + dy*dy );
 								var theta = Math.atan2(dy,dx) - angle * Math.PI / 180.0;
@@ -1729,8 +1816,8 @@
 				var ts = [" translate(", (left+tx), ",", (top+ty), ") scale(", sx, ",", sy,
 							") translate(", -(left+tx), ",", -(top+ty), ")"].join('');
 				if (angle) {
-					var cx = parseInt(left+width/2),
-						cy = parseInt(top+height/2);
+					var cx = round(left+width/2),
+						cy = round(top+height/2);
 					ts = ["rotate(", angle, " ", cx, ",", cy, ")", ts].join('')
 				}
 				selected.setAttribute("transform", ts);
@@ -1750,8 +1837,8 @@
 				}
 				
 				// update box width/height
-				selectedBBox.width = parseInt(width*sx);
-				selectedBBox.height = parseInt(height*sy);
+				selectedBBox.width = round(width*sx);
+				selectedBBox.height = round(height*sy);
 
 				// normalize selectedBBox
 				if (selectedBBox.width < 0) {
@@ -1778,6 +1865,16 @@
 				
 				selectorManager.requestSelector(selected).resize(selectedBBox);
 				break;
+			case "zoom":
+				x *= current_zoom;
+				y *= current_zoom;
+				assignAttributes(rubberBox, {
+					'x': Math.min(start_x*current_zoom,x),
+					'y': Math.min(start_y*current_zoom,y),
+					'width': Math.abs(x-start_x*current_zoom),
+					'height': Math.abs(y-start_y*current_zoom)
+				},100);			
+				break;
 			case "text":
 				assignAttributes(shape,{
 					'x': x,
@@ -1861,8 +1958,8 @@
 					if (angle) {
 						// calculate the shape's old center that was used for rotation
 						var box = selectedBBoxes[0];
-						var cx = parseInt(box.x + box.width/2) * current_zoom, 
-							cy = parseInt(box.y + box.height/2) * current_zoom;
+						var cx = round(box.x + box.width/2) * current_zoom, 
+							cy = round(box.y + box.height/2) * current_zoom;
 						var dx = mouse_x - cx, dy = mouse_y - cy;
  						var r = Math.sqrt( dx*dx + dy*dy );
 						var theta = Math.atan2(dy,dx) - angle;						
@@ -1884,7 +1981,7 @@
 					arr[0] = ["M", curx, ",", cury].join('');
 					for (var j = 1; j < len; ++j) {
 						var px = current_poly_pts[j*2]/current_zoom, py = current_poly_pts[j*2+1]/current_zoom;
-						arr[j] = ["l", parseInt(px-curx), ",", parseInt(py-cury)].join('');
+						arr[j] = ["l", round(px-curx), ",", round(py-cury)].join('');
 						curx = px;
 						cury = py;
 					}
@@ -1904,9 +2001,9 @@
 				break;
 			case "rotate":
 				var box = canvas.getBBox(selected),
-					cx = parseInt(box.x + box.width/2), 
-					cy = parseInt(box.y + box.height/2);
-				var angle = parseInt(((Math.atan2(cy-y,cx-x)  * (180/Math.PI))-90) % 360);
+					cx = round(box.x + box.width/2), 
+					cy = round(box.y + box.height/2);
+				var angle = round(((Math.atan2(cy-y,cx-x)  * (180/Math.PI))-90) % 360);
 				canvas.setRotationAngle(angle<-180?(360+angle):angle, true);
 				break;
 			default:
@@ -1974,7 +2071,7 @@
 				'stroke': "#00F",
 				'stroke-width': 2,
 				'cursor': 'move',
-				"pointer-events": "all"
+				'style': 'pointer-events:all'
 			});
 			pointGrip = pointGripContainer.appendChild(pointGrip);
 
@@ -2026,12 +2123,14 @@
 					if (selectedElements[1] == null) {
 						// set our current stroke/fill properties to the element's
 						var selected = selectedElements[0];
-						cur_shape.fill = selected.getAttribute("fill");
-						cur_shape.fill_opacity = selected.getAttribute("fill-opacity");
-						cur_shape.stroke = selected.getAttribute("stroke");
-						cur_shape.stroke_opacity = selected.getAttribute("stroke-opacity");
-						cur_shape.stroke_width = selected.getAttribute("stroke-width");
-						cur_shape.stroke_style = selected.getAttribute("stroke-dasharray");
+						if (selected.tagName != "g") {
+							cur_shape.fill = selected.getAttribute("fill");
+							cur_shape.fill_opacity = selected.getAttribute("fill-opacity");
+							cur_shape.stroke = selected.getAttribute("stroke");
+							cur_shape.stroke_opacity = selected.getAttribute("stroke-opacity");
+							cur_shape.stroke_width = selected.getAttribute("stroke-width");
+							cur_shape.stroke_style = selected.getAttribute("stroke-dasharray");
+						}
 						if (selected.tagName == "text") {
 							cur_text.font_size = selected.getAttribute("font-size");
 							cur_text.font_family = selected.getAttribute("font-family");
@@ -2101,6 +2200,17 @@
 				// we return immediately from select so that the obj_num is not incremented
 				return;
 				break;
+			case "zoom":
+				if (rubberBox != null) {
+					rubberBox.setAttribute("display", "none");
+				}
+				call("zoomed", {
+					'x': Math.min(start_x,x),
+					'y': Math.min(start_y,y),
+					'width': Math.abs(x-start_x),
+					'height': Math.abs(y-start_y)
+				});
+				return;
 			case "path":
 				keep = true;
 				break;
@@ -2141,7 +2251,8 @@
 							"stroke-dasharray": cur_shape.stroke_style,
 							"opacity": cur_shape.opacity,
 							"stroke-opacity": cur_shape.stroke_opacity,
-							"fill-opacity": cur_shape.fill_opacity
+							"fill-opacity": cur_shape.fill_opacity,
+							"style": "pointer-events:all"
 						}
 					});
 					call("changed",[element]);
@@ -2165,7 +2276,8 @@
 							"stroke-dasharray": cur_shape.stroke_style,
 							"opacity": cur_shape.opacity,
 							"stroke-opacity": cur_shape.stroke_opacity,
-							"fill-opacity": cur_shape.fill_opacity
+							"fill-opacity": cur_shape.fill_opacity,
+							"style": "pointer-events:all"
 						}
 					});
 					call("changed",[element]);
@@ -2209,7 +2321,8 @@
 							"stroke-width": cur_shape.stroke_width,
 							"stroke-dasharray": cur_shape.stroke_style,
 							"stroke-opacity": cur_shape.stroke_opacity,
-							"opacity": cur_shape.opacity / 2
+							"opacity": cur_shape.opacity / 2,
+							"style": "pointer-events:all"
 						}
 					});
 					// set stretchy line to first point
@@ -2224,7 +2337,7 @@
 				else {
 					// determine if we clicked on an existing point
 					var i = current_poly_pts.length;
-					var FUZZ = 6;
+					var FUZZ = 6/current_zoom;
 					var clickOnPoint = false;
 					while(i) {
 						i -= 2;
@@ -2266,7 +2379,7 @@
 						current_poly_pts.push(y);
 						// but we store relative coordinates in the d string of the poly for easy
 						// translation around the canvas in move mode
-						d_attr += "l" + parseInt(x-lastx) + "," + parseInt(y-lasty) + " ";
+						d_attr += "l" + round(x-lastx) + "," + round(y-lasty) + " ";
 						poly.setAttribute("d", d_attr);
 
 						// set stretchy line to latest point
@@ -2301,18 +2414,18 @@
 					if (angle) {
 						var box = canvas.getBBox(current_poly);
 						var oldbox = selectedBBoxes[0];
-						var oldcx = parseInt(oldbox.x + oldbox.width/2),
-							oldcy = parseInt(oldbox.y + oldbox.height/2),
-							newcx = parseInt(box.x + box.width/2),
-							newcy = parseInt(box.y + box.height/2);
+						var oldcx = round(oldbox.x + oldbox.width/2),
+							oldcy = round(oldbox.y + oldbox.height/2),
+							newcx = round(box.x + box.width/2),
+							newcy = round(box.y + box.height/2);
 						
 						// un-rotate the new center to the proper position
 						var dx = newcx - oldcx,
 							dy = newcy - oldcy;
 						var r = Math.sqrt(dx*dx + dy*dy);
 						var theta = Math.atan2(dy,dx) + angle;
-						newcx = parseInt(r * Math.cos(theta) + oldcx);
-						newcy = parseInt(r * Math.sin(theta) + oldcy);
+						newcx = round(r * Math.cos(theta) + oldcx);
+						newcy = round(r * Math.sin(theta) + oldcy);
 						
 						var i = current_poly_pts.length;
 						while (i) {
@@ -2336,8 +2449,8 @@
 							r = Math.sqrt(dx*dx + dy*dy);
 							theta = Math.atan2(dy,dx) - angle;
 							
-							current_poly_pts[i] = parseInt(r * Math.cos(theta) + newcx);
-							current_poly_pts[i+1] = parseInt(r * Math.sin(theta) + newcy);
+							current_poly_pts[i] = round(r * Math.cos(theta) + newcx);
+							current_poly_pts[i+1] = round(r * Math.sin(theta) + newcy);
 						} // loop for each point
 						
 						// now set the d attribute to the new value of current_poly_pts
@@ -2353,7 +2466,7 @@
 						for (var j = 1; j < len; ++j) {
 							var px = current_poly_pts[j*2]/current_zoom, 
 								py = current_poly_pts[j*2+1]/current_zoom;
-							arr[j] = ["l", parseInt(px-curx), ",", parseInt(py-cury)].join('');
+							arr[j] = ["l", round(px-curx), ",", round(py-cury)].join('');
 							curx = px;
 							cury = py;
 							assignAttributes(document.getElementById("polypointgrip_"+j), 
@@ -2442,12 +2555,21 @@
 		call("saved", str);
 	};
 
+	var walkTree = function(elem, cbFn){
+		if (elem && elem.nodeType == 1) {
+			cbFn(elem);
+			var i = elem.childNodes.length;
+			while (i--) {
+				walkTree(elem.childNodes.item(i), cbFn);
+			}
+		}
+	};
+	
 	this.getSvgString = function() {
 		return svgCanvasToString();
 	};
 
 	// this function returns false if the set was unsuccessful, true otherwise
-	// TODO: should this function keep throwing the exception?
 	// TODO: after parsing in the new text, do we need to synchronize getId()?
 	this.setSvgString = function(xmlString) {
 		try {
@@ -2459,11 +2581,6 @@
 
 			var batchCmd = new BatchCommand("Change Source");
 
-			// save our old selectorParentGroup
-			// not needed anymore, we can keep svgroot forever (and just replace svgzoom)
-			// TODO: reset zoom level on svgroot
-//			selectorManager.selectorParentGroup = svgroot.removeChild(selectorManager.selectorParentGroup);
-
         	// remove old svg document
     	    var oldzoom = svgroot.removeChild(svgzoom);
 			batchCmd.addSubCommand(new RemoveElementCommand(oldzoom, svgroot));
@@ -2499,10 +2616,10 @@
 			
 			// reset zoom
 			current_zoom = 1;
-
-			// add back in parentSelectorGroup
-			// not needed anymore
-//			svgroot.appendChild(selectorManager.selectorParentGroup);
+			
+			// identify layers
+			canvas.identifyLayers();
+			
 			selectorManager.update();
 
 			addCommandToHistory(batchCmd);
@@ -2515,19 +2632,220 @@
 		return true;
 	};
 
+	// Layer API Functions
+	
+	this.identifyLayers = function() {
+		all_layers = [];
+		var numchildren = svgzoom.childNodes.length;
+		// loop through all children of svgzoom
+		for (var i = 0; i < numchildren; ++i) {
+			var child = svgzoom.childNodes.item(i);
+			// for each g, find its layer name
+			if (child && child.tagName == "g") {
+				var name = getLayerName(child);
+				// store layer and name in global variable
+				if (name) {
+					all_layers.push( [name,child] );
+					current_layer = child;
+					walkTree(child, function(e){e.setAttribute("style", "pointer-events:none");});
+				}
+			}
+		}
+		walkTree(current_layer, function(e){e.setAttribute("style","pointer-events:all");});
+	};
+	
+	this.createLayer = function(name) {
+		var batchCmd = new BatchCommand("Create Layer");
+		var new_layer = svgdoc.createElementNS(svgns, "g");
+		var layer_title = svgdoc.createElementNS(svgns, "title");
+		layer_title.textContent = name;
+		new_layer.appendChild(layer_title);
+		new_layer = svgzoom.appendChild(new_layer);
+		batchCmd.addSubCommand(new InsertElementCommand(new_layer));
+		addCommandToHistory(batchCmd);
+		canvas.clearSelection();
+		canvas.identifyLayers();
+		canvas.setCurrentLayer(name);
+		call("changed", [new_layer]);
+	};
+	
+	this.deleteCurrentLayer = function() {
+		if (current_layer && all_layers.length > 1) {
+			var batchCmd = new BatchCommand("Delete Layer");
+			// actually delete from the DOM and store in our Undo History
+			var parent = current_layer.parentNode;
+			batchCmd.addSubCommand(new RemoveElementCommand(current_layer, parent));
+			parent.removeChild(current_layer);
+			addCommandToHistory(batchCmd);
+			canvas.clearSelection();
+			canvas.identifyLayers();
+			canvas.setCurrentLayer(all_layers[all_layers.length-1][0]);
+			call("changed", [svgzoom]);
+			return true;
+		}
+		return false;
+	};
+	
+	this.getNumLayers = function() {
+		return all_layers.length;
+	};
+	this.getLayer = function(i) {
+		if (i >= 0 && i < canvas.getNumLayers()) {
+			return all_layers[i][0];
+		}
+		return "";
+	};
+	
+	this.getCurrentLayer = function() {
+		for (var i = 0; i < all_layers.length; ++i) {
+			if (all_layers[i][1] == current_layer) {
+				return all_layers[i][0];
+			}
+		}
+		return "";
+	};
+	
+	this.setCurrentLayer = function(name) {
+		for (var i = 0; i < all_layers.length; ++i) {
+			if (name == all_layers[i][0]) {
+				if (current_layer != all_layers[i][1]) {
+					canvas.clearSelection();
+					walkTree(current_layer,function(e){e.setAttribute("style","pointer-events:none");});
+					current_layer = all_layers[i][1];
+					walkTree(current_layer,function(e){e.setAttribute("style","pointer-events:all");});
+				}
+				return true;
+			}
+		}
+		return false;
+	};
+	
+	this.renameCurrentLayer = function(newname) {
+		if (current_layer) {
+			var oldLayer = current_layer;
+			// setCurrentLayer will return false if the name doesn't already exists
+			if (!canvas.setCurrentLayer(newname)) {
+				var batchCmd = new BatchCommand("Rename Layer");
+				// find the index of the layer
+				for (var i = 0; i < all_layers.length; ++i) {
+					if (all_layers[i][1] == oldLayer) break;
+				}
+				var oldname = all_layers[i][0];
+				all_layers[i][0] = newname;
+			
+				// now change the underlying title element contents
+				var len = oldLayer.childNodes.length;
+				for (var i = 0; i < len; ++i) {
+					var child = oldLayer.childNodes.item(i);
+					// found the <title> element, now append all the
+					if (child && child.tagName == "title") {
+						// wipe out old name 
+						while (child.firstChild) { child.removeChild(child.firstChild); }
+						child.textContent = newname;
+
+						batchCmd.addSubCommand(new ChangeElementCommand(child, {"#text":oldname}));
+						addCommandToHistory(batchCmd);
+						call("changed", [oldLayer]);
+						return true;
+					}
+				}
+			}
+			current_layer = oldLayer;
+		}
+		return false;
+	};
+	
+	this.setCurrentLayerPosition = function(newpos) {
+		if (current_layer && newpos >= 0 && newpos < all_layers.length) {
+			for (var oldpos = 0; oldpos < all_layers.length; ++oldpos) {
+				if (all_layers[oldpos][1] == current_layer) break;
+			}
+			// some unknown error condition (current_layer not in all_layers)
+			if (oldpos == all_layers.length) { return false; }
+			
+			if (oldpos != newpos) {
+				// TODO: we could use the following loop to quickly re-establish all_layers
+				// but i think walking the DOM inside identifyLayers() is less bug-prone :)
+				/*
+				var new_layers = new Array(all_layers.length);
+				var trackIndex = 0;
+				for (var j = 0; j < new_layers.length; ++j) {
+					// new position of current_layer
+					if (newpos == j) {
+						new_layers[j] = all_layers[oldpos];
+					}
+					else {
+						// make sure we skip over the old position
+						if (trackIndex == oldpos) trackIndex++;
+						new_layers[j] = all_layers[trackIndex];
+						trackIndex++;
+					}
+				}
+				*/
+				
+				// if our new position is below us, we need to insert before the node after newpos
+				var refLayer = null;
+				var oldNextSibling = current_layer.nextSibling;
+				console.log([oldpos,newpos,all_layers.length]);
+				if (newpos > oldpos ) {
+					if (newpos < all_layers.length-1) {
+						refLayer = all_layers[newpos+1][1];
+					}
+				}
+				// if our new position is above us, we need to insert before the node at newpos
+				else {
+					refLayer = all_layers[newpos][1];
+				}
+				svgzoom.insertBefore(current_layer, refLayer);
+				addCommandToHistory(new MoveElementCommand(current_layer, oldNextSibling, svgzoom));
+				
+				canvas.identifyLayers();
+				canvas.setCurrentLayer(all_layers[newpos][0]);
+				
+				return true;
+			}
+		}
+		
+		// TODO: if i differs, then MoveElementCommand
+		return false;
+	};
+	
+	// used internally
+	var getLayerName = function(g) {
+		var name = "";
+		if (g && g.tagName == "g") {
+			var len = g.childNodes.length;
+			for (var i = 0; i < len; ++i) {
+				var child = g.childNodes.item(i);
+				// found the <title> element, now append all the
+				if (child && child.tagName == "title") {
+					name = child.textContent;
+					break;
+				}
+			}
+		}
+		return name;
+	};
+
 	this.clear = function() {
-		var nodes = svgroot.childNodes;
-		var len = svgroot.childNodes.length;
+		current_poly_pts = [];
+
+		// clear the svgzoom node
+		var nodes = svgzoom.childNodes;
+		var len = svgzoom.childNodes.length;
 		var i = 0;
-		current_poly_pts = [];
 		this.clearSelection();
 		for(var rep = 0; rep < len; rep++){
 			if (nodes[i].nodeType == 1) { // element node
-				nodes[i].parentNode.removeChild(nodes[i]);
+				svgzoom.removeChild(nodes[i]);
 			} else {
 				i++;
 			}
 		}
+		// create empty first layer
+		all_layers = [];
+		canvas.createLayer("Layer 1");
+		
 		// clear the undo stack
 		resetUndoStack();
 		// reset the selector manager
@@ -2549,48 +2867,106 @@
 		var vb = svgzoom.getAttribute("viewBox").split(' ');
 		return {'w':vb[2], 'h':vb[3], 'zoom': current_zoom};
 	};
+	
 	this.setResolution = function(x, y) {
 		var res = canvas.getResolution();
 		var w = res.w, h = res.h;
-		var batchCmd = new BatchCommand("Change Image Dimensions");
 
-		var handle = svgroot.suspendRedraw(1000);
-		
-		if(!x) {
+		if(x == 'fit') {
 			canvas.clearSelection();
 
 			// Get bounding box
-			var bbox = svgzoom.getBBox();
+			var bbox = canvas.getStrokedBBox();
 			
 			if(bbox) {
-				x = bbox.x + bbox.width;
-				y = bbox.y + bbox.height;
+				canvas.addToSelection(canvas.getVisibleElements());
+				$.each(selectedElements, function(i, item) {
+					var sel_bb = item.getBBox();
+					recalculateDimensions(item, {
+						x: sel_bb.x - bbox.x,
+						y: sel_bb.y - bbox.y,
+						width: sel_bb.width,
+						height: sel_bb.height
+					});
+				});
+				canvas.clearSelection();
+				x = Math.round(bbox.width);
+				y = Math.round(bbox.height);
 			} else {
-				alert('No content to fit to');
-				return;
+				return false;
 			}
 		}
-		svgroot.setAttribute('width', x * current_zoom);
-		svgroot.setAttribute('height', y * current_zoom);
-		batchCmd.addSubCommand(new ChangeElementCommand(svgroot, {"width":w, "height":h}));
+		x *= current_zoom;
+		y *= current_zoom;
+		if (x != w || y != h) {
+			var handle = svgroot.suspendRedraw(1000);
+			var batchCmd = new BatchCommand("Change Image Dimensions");
+			svgroot.setAttribute('width', x);
+			svgroot.setAttribute('height', y);
+			batchCmd.addSubCommand(new ChangeElementCommand(svgroot, {"width":w, "height":h}));
 
-		svgzoom.setAttribute("viewBox", ["0 0", x, y].join(' '));
-		batchCmd.addSubCommand(new ChangeElementCommand(svgzoom, {"viewBox": ["0 0", w, h].join(' ')}));
+			svgzoom.setAttribute("viewBox", ["0 0", x/current_zoom, y/current_zoom].join(' '));
+			batchCmd.addSubCommand(new ChangeElementCommand(svgzoom, {"viewBox": ["0 0", w, h].join(' ')}));
+		
+			addCommandToHistory(batchCmd);
+			svgroot.unsuspendRedraw(handle);
+			call("changed", [svgzoom]);
+		}
+		return true;
+	};
 
-		svgroot.unsuspendRedraw(handle);
+	this.setBBoxZoom = function(val, editor_w, editor_h) {
+		var spacer = .85;
+		var bb;
+		var calcZoom = function(bb) {
+			var w_zoom = Math.round((editor_w / bb.width)*100 * spacer)/100;
+			var h_zoom = Math.round((editor_h / bb.height)*100 * spacer)/100;	
+			var zoomlevel = Math.min(w_zoom,h_zoom);
+			canvas.setZoom(zoomlevel);
+			return {'zoom': zoomlevel, 'bbox': bb};
+		}
 		
-		addCommandToHistory(batchCmd);
-		call("changed", [svgzoom]);
-	};
+		if(typeof val == 'object') {
+			bb = val;
+			if(bb.width == 0 || bb.height == 0) {
+				canvas.setZoom(current_zoom * 2);
+				return {'zoom': current_zoom, 'bbox': bb};
+			}
+			return calcZoom(bb);
+		}
+	
+		switch (val) {
+			case 'selection':
+				if(!selectedElements[0]) return;
+				var sel_elems = $.map(selectedElements, function(n){ if(n) return n; });
+				bb = canvas.getStrokedBBox(sel_elems);
+				break;
+			case 'canvas':
+				var res = canvas.getResolution();
+				spacer = .95;
+				bb = {width:res.w, height:res.h ,x:0, y:0};
+				break;
+			case 'content':
+				bb = canvas.getStrokedBBox();
+				break;
+			case 'layer':
+				bb = canvas.getStrokedBBox(canvas.getVisibleElements(current_layer));
+				break;
+			default:
+				return;
+		}
+		return calcZoom(bb);
+	}
 
 	this.setZoom = function(zoomlevel) {
 		var res = canvas.getResolution();
 		svgroot.setAttribute("width", res.w * zoomlevel);
 		svgroot.setAttribute("height", res.h * zoomlevel);
 		current_zoom = zoomlevel;
-		if(selectedElements[0]) {
-			selectorManager.requestSelector(selectedElements[0]).resize();
-		}
+		$.each(selectedElements, function(i, elem) {
+			if(!elem) return;
+			selectorManager.requestSelector(elem).resize();
+		});
 	}
 
 	this.getMode = function() {
@@ -2856,7 +3232,7 @@
 		// we use the actual element's bbox (not the calculated one) since the 
 		// calculated bbox's center can change depending on the angle
 		var bbox = elem.getBBox();
-		var cx = parseInt(bbox.x+bbox.width/2), cy = parseInt(bbox.y+bbox.height/2);
+		var cx = round(bbox.x+bbox.width/2), cy = round(bbox.y+bbox.height/2);
 		var rotate = "rotate(" + val + " " + cx + "," + cy + ")";
 		if (preventUndo) {
 			this.changeSelectedAttributeNoUndo("transform", rotate, selectedElements);
@@ -3044,8 +3420,8 @@
 				// we need to update the rotational transform attribute 
 				var angle = canvas.getRotationAngle(elem);
 				if (angle && attr != "transform") {
-					var cx = parseInt(selectedBBoxes[i].x + selectedBBoxes[i].width/2),
-						cy = parseInt(selectedBBoxes[i].y + selectedBBoxes[i].height/2);
+					var cx = round(selectedBBoxes[i].x + selectedBBoxes[i].width/2),
+						cy = round(selectedBBoxes[i].y + selectedBBoxes[i].height/2);
 					var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
 					if (rotate != elem.getAttribute("transform")) {
 						elem.setAttribute("transform", rotate);
@@ -3117,7 +3493,8 @@
 			batchCmd.addSubCommand(new RemoveElementCommand(elem, parent));
 		}
 		if (!batchCmd.isEmpty()) addCommandToHistory(batchCmd);
-		call("selected", selectedCopy);
+		call("changed", selectedCopy);
+		canvas.clearSelection();
 	};
 	
 	this.groupSelectedElements = function() {
@@ -3151,7 +3528,7 @@
 	};
 
 	// TODO: when transferring group's rotational transform to the children, must deal
-	// with children who are already rotated within the group
+	// with children who are already rotated within the group (Issue 204)
 	this.ungroupSelectedElement = function() {
 		var g = selectedElements[0];
 		if (g.tagName == "g") {
@@ -3280,8 +3657,49 @@
 		}
 	};
 
-	this.getVisibleElements = function(includeBBox) {
-		var nodes = svgzoom.childNodes;
+	this.getStrokedBBox = function(elems) {
+		// TODO: Get correct BBoxes for rotated elements
+		if(!elems) elems = canvas.getVisibleElements();
+		if(!elems.length) return false;
+		var full_bb = elems[0].getBBox();
+		var max_x = full_bb.x + full_bb.width;
+		var max_y = full_bb.y + full_bb.height;
+		var min_x = full_bb.x;
+		var min_y = full_bb.y;
+		var getOffset = function(elem) {
+			var sw = round(elem.getAttribute("stroke-width"));
+			var offset = 0;
+			if (elem.getAttribute("stroke") != "none" && !isNaN(sw)) {
+				offset += sw/2;
+			}
+			return offset;
+		}
+		
+		$.each(elems, function(i, elem) {
+			var cur_bb = elem.getBBox();
+			var offset = getOffset(elem);
+			min_x = Math.min(min_x, cur_bb.x - offset);
+			min_y = Math.min(min_y, cur_bb.y - offset);
+		});
+		
+		full_bb.x = min_x;
+		full_bb.y = min_y;
+		
+		$.each(elems, function(i, elem) {
+			var cur_bb = elem.getBBox();
+			var offset = getOffset(elem);
+			max_x = Math.max(max_x, cur_bb.x + cur_bb.width + offset);
+			max_y = Math.max(max_y, cur_bb.y + cur_bb.height + offset);
+		});
+		
+		full_bb.width = max_x - min_x;
+		full_bb.height = max_y - min_y;
+		return full_bb;
+	}
+
+	this.getVisibleElements = function(parent, includeBBox) {
+		if(!parent) parent = svgzoom;
+		var nodes = parent.childNodes;
 		var i = nodes.length;
 		var contentElems = [];
 		
@@ -3364,6 +3782,31 @@
 
 	// this creates deep DOM copies (clones) of all selected elements
 	this.cloneSelectedElements = function() {
+		var copyElem = function(el) {
+			// Manual clone function for Opera/Win/non-EN. 
+			// Needed because cloneNode changes "." to "," on float values
+			if(!window.opera) return el.cloneNode(true);
+			var new_el = document.createElementNS(svgns, el.nodeName);
+			$.each(el.attributes, function(i, attr) {
+				var ns = attr.nodeName == 'href'?xlinkns:null;
+				new_el.setAttributeNS(ns, attr.nodeName, attr.nodeValue);
+			});
+			
+			$.each(el.childNodes, function(i, child) {
+				switch(child.nodeType) {
+				case 1: // element node
+					new_el.appendChild(copyElem(child));
+					break;
+				case 3: // text node
+					new_el.textContent = child.nodeValue;
+					break;
+				default:
+					break;
+				}
+			});
+			return new_el;
+		}
+	
 		var batchCmd = new BatchCommand("Clone Elements");
 		// find all the elements selected (stop at first null)
 		var len = selectedElements.length;
@@ -3379,10 +3822,10 @@
 		var i = copiedElements.length;
 		while (i--) {
 			// clone each element and replace it within copiedElements
-			var elem = copiedElements[i] = copiedElements[i].cloneNode(true);
+			var elem = copiedElements[i] = copyElem(copiedElements[i]);
 			elem.removeAttribute("id");
 			elem.id = getNextId();
-			svgzoom.appendChild(elem);
+			current_layer.appendChild(elem);
 			batchCmd.addSubCommand(new InsertElementCommand(elem));
 		}
 
@@ -3414,8 +3857,8 @@
 			if (angles[i]) {
 				var rminx = Number.MAX_VALUE, rminy = Number.MAX_VALUE, 
 					rmaxx = Number.MIN_VALUE, rmaxy = Number.MIN_VALUE;
-				var cx = parseInt(bboxes[i].x + bboxes[i].width/2),
-					cy = parseInt(bboxes[i].y + bboxes[i].height/2);
+				var cx = round(bboxes[i].x + bboxes[i].width/2),
+					cy = round(bboxes[i].y + bboxes[i].height/2);
 				var pts = [ [bboxes[i].x - cx, bboxes[i].y - cy], 
 							[bboxes[i].x + bboxes[i].width - cx, bboxes[i].y - cy],
 							[bboxes[i].x + bboxes[i].width - cx, bboxes[i].y + bboxes[i].height - cy],
@@ -3426,8 +3869,8 @@
 						y = pts[j][1],
 						r = Math.sqrt( x*x + y*y );
 					var theta = Math.atan2(y,x) + angles[i];
-					x = parseInt(r * Math.cos(theta) + cx);
-					y = parseInt(r * Math.sin(theta) + cy);
+					x = round(r * Math.cos(theta) + cx);
+					y = round(r * Math.sin(theta) + cy);
 
 					// now set the bbox for the shape after it's been rotated
 					if (x < rminx) rminx = x;
@@ -3513,9 +3956,10 @@
 		}
 		this.moveSelectedElements(dx,dy);
 	};
-
-	this.getCurrentZoom = function() { return this.current_zoom; }
-}
+	this.getZoom = function(){return current_zoom;};
+	
+	this.clear();
+};
 
 // Static class for various utility functions
 
--- a/htdocs/svg-edit/wave/svg-edit.xml	Wed Sep 23 20:50:10 2009 +0200
+++ b/htdocs/svg-edit/wave/svg-edit.xml	Wed Sep 23 20:52:24 2009 +0200
@@ -28,6 +28,14 @@
 <div id="svgcanvas"></div>
 </div>
 
+<div id="sidepanels" style="display:none">
+<p><b>TODO: Side Panels go here.</b></p>
+<p><b>By default, this panel will be collapsed.  It will be expandable by some button/handle.</b></p>
+<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+</div>
+
 <div id="logo">
 	<a href="http://svg-edit.googlecode.com/" target="_blank" title="SVG-edit Home Page">
 		<img src="images/logo.png" alt="logo" />
@@ -35,24 +43,23 @@
 </div>
 
 <div id="tools_top" class="tools_panel">
+	
 	<!-- File-like buttons: New, Save, Source -->
-	<div>
-		<img class="tool_sep" src="images/sep.png" alt="|"/>
+	<div id="file_panel">
 		<img class="tool_button" id="tool_clear" src="images/clear.png" title="New Image [N]" alt="Clear" />
 		<img style="display:none" class="tool_button" id="tool_open" src="images/open.png" title="Open Image [O]" alt="Open"/>
 		<img class="tool_button" id="tool_save" src="images/save.png" title="Save Image [S]" alt="Save"/>
 		<img class="tool_button" id="tool_source" src="images/source.png" title="Edit Source [U]" alt="Source"/>
-    <img class="tool_button" title="Wave" src="images/wave.png" alt="Wave State" onclick="alert(wave.getState().toString())" />
-    <img class="tool_button" src="images/source.png" title="Evil Eval" alt="Eval" onclick="eval(prompt('Execute Stuff'));" />
+		<img class="tool_button" id="tool_docprops" src="images/document-properties.png" title="Document Properties [I]"/>
 	</div>
 
     <!-- History buttons -->
-	<div>
-		<img class="tool_sep" src="images/sep.png" alt="|"/>
-		<img class="tool_button tool_button_disabled" id="tool_undo" src="images/undo.png" title="Undo [Z]" alt="Undo" />
-		<img class="tool_button tool_button_disabled" id="tool_redo" src="images/redo.png" title="Redo [Y]" alt="Redo"/>
+	<div id="history_panel">
+		<div class="tool_sep" alt="|"></div>
+		<div class="tool_button tool_button_disabled" id="tool_undo" title="Undo [Z]"></div>
+		<div class="tool_button tool_button_disabled" id="tool_redo" title="Redo [Y]"></div>
 	</div>
-
+	
 	<!-- Buttons when a single element is selected -->
 	<div id="selected_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
@@ -85,12 +92,12 @@
 		<img class="tool_button" id="tool_clone_multi" src="images/clone.png" title="Clone Elements [C]" alt="Clone"/>
 		<img class="tool_button" id="tool_delete_multi" src="images/delete.png" title="Delete Selected Elements [Delete/Backspace]" alt="Delete"/>
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
-		<img class="tool_button" id="tool_alignleft" src="images/align-left.png" title="Align Left" alt="Left"/>
-		<img class="tool_button" id="tool_aligncenter" src="images/align-center.png" title="Align Center" alt="Center"/>
-		<img class="tool_button" id="tool_alignright" src="images/align-right.png" title="Align Right" alt="Right"/>
-		<img class="tool_button" id="tool_aligntop" src="images/align-top.png" title="Align Top" alt="Top"/>
-		<img class="tool_button" id="tool_alignmiddle" src="images/align-middle.png" title="Align Middle" alt="Middle"/>
-		<img class="tool_button" id="tool_alignbottom" src="images/align-bottom.png" title="Align Bottom" alt="Bottom"/>
+		<div class="tool_button" id="tool_alignleft" title="Align Left"></div>
+		<div class="tool_button" id="tool_aligncenter" title="Align Center"></div>
+		<div class="tool_button" id="tool_alignright" title="Align Right"></div>
+		<div class="tool_button" id="tool_aligntop" title="Align Top"></div>
+		<div class="tool_button" id="tool_alignmiddle" title="Align Middle"></div>
+		<div class="tool_button" id="tool_alignbottom" title="Align Bottom"></div>
 		<span class="selected_tool">relative to:</span>
 		<select id="align_relative_to" class="selected_tool" title="Align relative to ...">
 		<option value="selected">selected objects</option>
@@ -98,6 +105,13 @@
 		<option value="smallest">smallest object</option>
 		<option value="page">page</option>
 		</select>
+		<img class="tool_sep" src="images/sep.png" alt="|"/>
+		<img class="tool_button" id="tool_group" src="images/shape_group.png" title="Group Elements [G]" alt="Group"/>
+	</div>
+
+	<div id="g_panel">
+		<img class="tool_sep" src="images/sep.png" alt="|"/>
+		<img class="tool_button" id="tool_ungroup" src="images/shape_ungroup.png" title="Ungroup Elements [G]" alt="Ungroup"/>
 	</div>
 
 	<div id="rect_panel">
@@ -114,6 +128,21 @@
 		<input id="rect_rx" size="3" value="0" class="rect_tool" type="text" title="Change Rectangle Corner Radius" alt="Corner Radius"/>
 	</div>
 
+	<div id="image_panel">
+		<img class="tool_sep" src="images/sep.png" alt="|"/>
+		<label class="image_tool">x:</label>
+		<input id="image_x" class="image_tool attr_changer" title="Change image X coordinate" alt="x" size="3"/>
+		<label class="image_tool">y:</label>
+		<input id="image_y" class="image_tool attr_changer" title="Change image Y coordinate" alt="y" size="3"/>
+		<label class="image_tool">width:</label>
+		<input id="image_width" class="image_tool attr_changer" title="Change image width" alt="width" size="3"/>
+		<label class="image_tool">height:</label>
+		<input id="image_height" class="image_tool attr_changer" title="Change image height" alt="height" size="3"/>
+    <label class="image_tool">url:</label>
+    <input id="image_url" class="image_tool" type="text" title="Change URL" size="35"/>
+  </div>
+
+
 	<div id="circle_panel">
 		<img class="tool_sep" src="images/sep.png" alt="|"/>
 		<label class="circle_tool">cx:</label>
@@ -194,20 +223,32 @@
 	<img class="flyout_arrow_horiz" src="images/flyouth.png"/>
 	<img class="tool_button" id="tool_text" src="images/text.png" title="Text Tool [6]" alt="Text"/>
 	<img class="tool_button" id="tool_poly" src="images/polygon.png" title="Poly Tool [7]" alt="Poly"/>
+	<img class="tool_button" id="tool_image" src="images/image.png" title="Image Tool [8]" alt="Image"/>
+	<img class="tool_button" id="tool_zoom" src="images/zoom.png" title="Zoom Tool [Ctrl+Up/Down]" alt="Zoom"/>
 </div> <!-- tools_left -->
 
 <div id="tools_bottom" class="tools_panel">
 
-	<div id="tools_bottom_1">
-		<select id="resolution">
-			<option selected="selected">640x480</option>
-			<option>800x600</option>
-			<option>1024x768</option>
-			<option>1280x960</option>
-			<option>1600x1200</option>
-			<option>Fit to Content</option>
-			<option>Custom</option>
-		</select>
+    <!-- Zoom buttons -->
+	<div id="zoom_panel">
+		<span class="zoom_tool">zoom:</span>
+		<input id="zoom" class="zoom_tool" title="Change zoom level" alt="Zoom %" size="3" value="100" type="text"/>
+		<div id="zoom_dropdown">
+			<button></button>
+			<ul>
+				<li>100%</li>
+				<li data-val="content">Fit to content</li>
+				<li data-val="selection">Fit to selection</li>
+				<li data-val="canvas">Fit to canvas</li>
+				<li>25%</li>
+				<li>50%</li>
+				<li>100%</li>
+				<li>200%</li>
+				<li>400%</li>
+				<li>1000%</li>
+			</ul>
+		</div>
+		<img class="tool_sep" src="images/sep.png" alt="|"/>
 	</div>
 
 	<div id="tools_bottom_2">
@@ -238,7 +279,7 @@
 
 	<div id="tools_bottom_3">
 		<div id="palette_holder"><div id="palette" title="Click to change fill color, shift-click to change stroke color"></div></div>
-		<div id="copyright">Powered by <a href="http://svg-edit.googlecode.com/" target="_blank">SVG-edit v2.3-Beta</a></div>
+		<div id="copyright">Powered by <a href="http://svg-edit.googlecode.com/" target="_blank">SVG-edit v2.4-unstable</a></div>
 	</div>
 </div>
 
@@ -257,27 +298,13 @@
 	<div id="tool_fhellipse" class="tool_flyout_button" title="Free-Hand Ellipse"></div>
 </div>
 
-<div id="tools_stacking" class="tools_flyout_v">
-	<div id="tool_stacktop" class="tool_flyout_button" title="Move to Top [Shift+Up]"></div>
-	<div id="tool_stackbottom" class="tool_flyout_button" title="Move to Bottom [Shift+Down]"></div>
-</div>
-
-<div id="tools_align" class="tools_flyout_v">
-	<div id="tool_aligntop" class="tool_flyout_button" title="Align to Top"></div>
-	<div id="tool_alignbottom" class="tool_flyout_button" title="Align to Bottom"></div>
-	<div id="tool_alignmiddle" class="tool_flyout_button" title="Align to Middle"></div>
-	<div id="tool_alignleft" class="tool_flyout_button" title="Align to Left"></div>
-	<div id="tool_alignright" class="tool_flyout_button" title="Align to Right"></div>
-	<div id="tool_aligncenter" class="tool_flyout_button" title="Align to Center"></div>
-</div>
-
 </div> <!-- svg_editor -->
 
 <div id="svg_source_editor">
 	<div id="svg_source_overlay"></div>
 	<div id="svg_source_container">
 		<div id="tool_source_back" class="toolbar_button">
-			<button id="tool_source_save">Load</button>
+			<button id="tool_source_save">Save</button>
 			<button id="tool_source_cancel">Cancel</button>
 		</div>
 		<form>
@@ -286,6 +313,34 @@
 	</div>
 </div>
 
+<div id="svg_docprops">
+	<div id="svg_docprops_overlay"></div>
+	<div id="svg_docprops_container">
+		<div id="tool_docprops_back" class="toolbar_button">
+			<button id="tool_docprops_save">Save</button>
+			<button id="tool_docprops_cancel">Cancel</button>
+
+			<div id="change_background">
+				<label>Canvas Background:</label>
+				<div id="bkgnd_color" class="color_block"  title="Change background color/opacity"></div>
+			</div>
+			
+			<div id="change_resolution">
+				<label>Resolution:</label>
+				<select id="resolution" label="Resolution:">
+					<option selected="selected">640x480</option>
+					<option>800x600</option>
+					<option>1024x768</option>
+					<option>1280x960</option>
+					<option>1600x1200</option>
+					<option>Fit to Content</option>
+					<option>Custom</option>
+				</select>
+			</div>
+		</div>
+	</div>
+</div>
+
 
     ]]>
   </Content>