diff --git a/editor/images/spinbtn_updn_big.png b/editor/images/spinbtn_updn_big.png new file mode 100644 index 00000000..3873736f Binary files /dev/null and b/editor/images/spinbtn_updn_big.png differ diff --git a/editor/images/svg_edit_icons.svg b/editor/images/svg_edit_icons.svg index 6b2609ed..82782e11 100644 --- a/editor/images/svg_edit_icons.svg +++ b/editor/images/svg_edit_icons.svg @@ -1,32 +1,168 @@ + - - - - - Layer 1 - - + + - + + + + + + + + + + + + + + + - - - Layer 1 - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -34,13 +170,46 @@ - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -53,11 +222,10 @@ - + - + - @@ -68,49 +236,37 @@ - - Layer 1 - - + - + - - - Layer 1 A - - + - + - - - Layer 1 - - - + + - + - @@ -121,65 +277,53 @@ - - Layer 1 - - + - + - - - Layer 1 - - + - + - + - + - + - + - - - Layer 1 - - - + + - + - @@ -190,29 +334,39 @@ - - Layer 1 - - - - + - + + + + + + + + + + + + + + + + + + + - - - Layer 1 @@ -224,15 +378,11 @@ - - + - + - - - Layer 1 s v g @@ -240,56 +390,42 @@ - - - + + - + - - - Layer 1 - - - + + - + - - - Layer 1 - - - + + - + - - - Layer 1 - - - + + - + - @@ -300,47 +436,36 @@ - - Layer 1 - - + - + - - - Layer 1 - - - + + - + - - - Layer 1 - - + - + @@ -348,14 +473,11 @@ - - Layer 1 - - - + + - + @@ -363,19 +485,16 @@ - - Layer 1 - - - + + - + @@ -383,17 +502,172 @@ - - Layer 1 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/editor/images/svg_edit_icons.svgz b/editor/images/svg_edit_icons.svgz new file mode 100644 index 00000000..cb8c80cc Binary files /dev/null and b/editor/images/svg_edit_icons.svgz differ diff --git a/editor/locale/lang.en.js b/editor/locale/lang.en.js index a10547a3..d7505e51 100644 --- a/editor/locale/lang.en.js +++ b/editor/locale/lang.en.js @@ -116,5 +116,19 @@ {"id":"tool_node_delete","title":"Delete Node"}, {"id":"selLayerLabel","textContent":"Move elements to:"}, {"id":"selLayerNames","title":"Move selected elements to a different layer"}, -{"id":"sidepanel_handle","title":"Drag left/right to resize side panel [X]","textContent":"L a y e r s"} +{"id":"sidepanel_handle","title":"Drag left/right to resize side panel [X]","textContent":"L a y e r s"}, +{"js_strings": { + "invalidAttrValGiven":"Invalid value given", + "noContentToFitTo":"No content to fit to", + 'layer':"Layer", + "dupeLayerName":"There is already a layer named that!", + "enterUniqueLayerName":"Please enter a unique layer name", + "enterNewLayerName":"Please enter the new layer name", + "layerHasThatName":"Layer already has that name", + "QmoveElemsToLayer":"Move selected elements to layer '%s'?", + "QwantToClear":"Do you want to clear the drawing?\nThis will also erase your undo history!", + "QerrorsRevertToSource":"There were parsing errors in your SVG source.\nRevert back to original SVG source?", + "QignoreSourceChanges":"Ignore changes made to SVG source?" + } +} ]; diff --git a/editor/locale/locale.js b/editor/locale/locale.js index 39fb761c..e016d206 100644 --- a/editor/locale/locale.js +++ b/editor/locale/locale.js @@ -1,33 +1,43 @@ -// function GUP is taken from http://www.netlobo.com/url_query_string_javascript.html -// gup = get URL parameter -function gup( name ) -{ - name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); - var regexS = "[\\?&]"+name+"=([^&#]*)"; - var regex = new RegExp( regexS ); - var results = regex.exec( window.location.href ); - if( results == null ) - return ""; - else - return results[1]; -} - -var put_locale = function(){ - var lang_param = gup("lang"); - if (lang_param == "") - return; +var put_locale = function(svgCanvas, given_param){ + var lang_param; + if(given_param) { + lang_param = given_param; + } else { + lang_param = $.pref('lang'); + if(!lang_param) { + if (navigator.userLanguage) // Explorer + lang_param = navigator.userLanguage; + else if (navigator.language) // FF, Opera, ... + lang_param = navigator.language; + if (lang_param == "") + return; + } + // don't bother on first run if language is English + if(lang_param.indexOf("en") == 0) return; + } + var url = "locale/lang." + lang_param + ".js"; $.get(url, function(data){ - var LangData = eval(data); - for (var i=0;i coord(el,'offsetLeft') + el.offsetWidth - this.spinCfg._btn_width) - ? ((y < coord(el,'offsetTop') + this.spinCfg._btn_height) ? 1 : -1) : 0; + ? ((y < coord(el,'offsetTop') + height) ? 1 : -1) : 0; if (direction !== this.spinCfg._direction) { // Style up/down buttons: diff --git a/editor/svg-editor-classic.css b/editor/svg-editor-classic.css new file mode 100644 index 00000000..99925f5b --- /dev/null +++ b/editor/svg-editor-classic.css @@ -0,0 +1,679 @@ +body { + background: #E8E8E8; +} + +#svg_editor { + font-size: 8pt; + font-family: Verdana, Helvetica, Arial; + color: #000000; +} + +#svg_editor a { + color: #0000FF; +} + +#svg_editor hr { + border: none; + border-bottom: 1px solid #808080; +} + +#svg_editor select { + margin-top: 4px; +} + +#svg_editor #svgroot { + -moz-user-select: none; + position: absolute; + top: 0; + left: 0; +} + +#svg_editor #svgcanvas { + background-color: #FFFFFF; + text-align: center; + vertical-align: middle; + width: 640px; + height: 480px; + -apple-dashboard-region:dashboard-region(control rectangle 0px 0px 0px 0px); /* for widget regions that shouldn't react to dragging */ + position: relative; +} + +#svg_editor div#palette_holder { + overflow-x: scroll; + overflow-y: hidden; + height: 31px; + border: 1px solid #808080; + border-top: none; + margin-top: 2px; +} + +#svg_editor #fill_color, #svg_editor #stroke_color { + height: 16px; + width: 16px; + border: 1px solid #808080; + cursor: pointer; +} + +#svg_editor div#palette { + float: left; + width: 6848px; + height: 16px; +} + +#svg_editor div#workarea { + display: inline-block; + position:absolute; + top: 75px; + left: 40px; + bottom: 60px; + right: 14px; + background-color: #A0A0A0; + border: 1px solid #808080; + overflow: auto; +} + +#svg_editor #sidepanels { + display: inline-block; + position:absolute; + top: 75px; + bottom: 60px; + right: 0px; + width: 2px; + padding: 10px; + border-color: #808080; + border-style: solid; + border-width: 1px; + border-left: none; +} + +#svg_editor #layerpanel { + display: inline-block; + background-color: #E8E8E8; + position:absolute; + top: 1px; + bottom: 0px; + right: 0px; + width: 0px; + overflow: hidden; + margin: 0px; +} + +/* + border-style: solid; + border-color: #666; + border-width: 0px 0px 0px 1px; +*/ +#svg_editor #sidepanel_handle { + display: inline-block; + position: absolute; + background-color: #E8E8E8; + left: 0px; + top: 40%; + width: 1em; + padding: 5px 1px 5px 5px; + margin-left: 3px; + cursor: pointer; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -moz-user-select: none; +} + +#svg_editor #sidepanel_handle:hover { + font-weight: bold; +} + +#svg_editor #sidepanel_handle * { + cursor: pointer; + -moz-user-select: none; +} +#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: 1px; + padding: 0px; + width: 110px; + border-collapse: collapse; + border: 1px solid #808080; + background-color: #FFFFFF; +} + +#svg_editor #layerlist tr.layer { + background-color: #FFFFFF; + margin: 0px; + padding: 0px; +} +#svg_editor #layerlist tr.layersel { + border: 1px solid #808080; + background-color: #CCCCCC; +} + +#svg_editor #layerlist td.layervis { + background-image: url('images/eye.png'); + background-repeat: no-repeat; + background-position: 3px center; + width: 22px; + cursor:pointer; +} +#svg_editor #layerlist td.layerinvis { + background-image: none; + cursor:pointer; +} + +#svg_editor #layerlist td.layername { + cursor: pointer; +} + +#svg_editor #layerlist tr.layersel td.layername { + font-weight: bold; +} + +#svg_editor #selLayerLabel { + white-space: nowrap; +} + +#svg_editor #selLayerNames { + display: block; +} + +#svg_editor div.palette_item { + height: 16px; + width: 16px; + float: left; +} + +#svg_editor #logo { + position: absolute; + top: 4px; + left: 4px; + padding: 0px; +} + +#svg_editor #logo a img { + border: 0; + width: 32px; + height: 32px; +} + +#svg_editor #tools_top { + position: absolute; + left: 38px; + right: 2px; + top: 2px; + height: 75px; + border-bottom: none; +} + +#svg_editor #tools_top > div { + float: left; +} + +#svg_editor #tools_left { + position: absolute; + border-right: none; + width: 36px; + top: 75px; + left: 2px; +} + +#workarea.wireframe #svgcontent * { + fill: none; + stroke: #000; + stroke-width: 1px; + stroke-opacity: 1.0; + stroke-dasharray: none; + opacity: 1; + pointer-events: stroke; +} + +#workarea.wireframe #svgcontent text { + fill: #000; + stroke: none; +} + + +#svg_editor #selected_panel, +#svg_editor #multiselected_panel, +#svg_editor #g_panel, +#svg_editor #rect_panel, +#svg_editor #circle_panel, +#svg_editor #ellipse_panel, +#svg_editor #line_panel, +#svg_editor #image_panel, +#svg_editor #text_panel, +#svg_editor #path_node_panel { + display: none; +} + +#svg_editor #selected_panel .selected_tool { + vertical-align: 12px; +} + +#svg_editor #multiselected_panel .selected_tool { + vertical-align: 12px; +} + +#svg_editor #tools_top > div, #tools_top { + float: left; + line-height: 26px; +} + +#tools_top > div > * { + float: left; + margin-right: 2px; +} + +#tools_top label { + margin-top: 3px; + margin-left: 5px; +} + +#tools_top input { + margin-top: 5px; + height: 15px; +} + +#svg_editor .flyout_arrow_horiz { + float: right; + position: relative; + top: -13px; + left: -5px; + margin-bottom: -13px; +} + +.magic_field > * { + float: left; +} + +span.zoom_tool { + line-height: 26px; + padding: 3px; +} + +.magic_field input { + margin-top: 5px; +} + +.dropdown { + position: relative; +} + +.dropdown button { + background: transparent 3px 8px url('images/dropdown.gif') no-repeat; + width: 15px; + height: 21px; + margin: 6px 0 0 1px; + padding: 0; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; +} + +.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; +} + +.dropdown ul { + list-style: none; + position: absolute; + margin: 0; + padding: 0; + left: -93px; + top: 26px; + display: none; +} + +.dropup ul { + top: auto; + bottom: 26px; +} + +.dropdown li { + display: block; + width: 120px; + padding: 4px; + background: #E8E8E8; + border: 1px solid #B0B0B0; + margin: 0 0 -1px 0; + line-height: 16px; +} + +.dropdown li:hover { + background-color: #B0B0B0; +} + +.dropdown li.special { + padding: 10px 4px; +} + +.dropdown li.special:hover { + background: #E8E8E8; +} + +#opacity_dropdown li { + width: 140px; +} + +#svg_editor .tool_button, +#svg_editor .push_button, +#svg_editor .tool_button_current, +#svg_editor .tool_button_disabled { + height: 24px; + width: 24px; + margin: 2px; + padding: 2px; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + cursor: pointer; +} + +#svg_editor .tool_button_current, +#svg_editor .push_button_pressed { + border-left: 1px solid #808080; + border-top: 1px solid #808080; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + background-color: #B0B0B0; +} + +#svg_editor .tool_button_disabled { + opacity: 0.5; + cursor: default; +} + +#svg_editor .tool_sep { + width: 2px; + height: 24px; + margin: 2px; + margin-right: 0; + padding: 2px; +} + +#svg_editor #color_picker { + position: absolute; + display: none; + background: #E8E8E8; + height: 350px; +} + +#svg_editor .tools_flyout { + position: absolute; + display: none; + cursor: pointer; +} + +#svg_editor .tools_flyout_v { + position: absolute; + display: none; + cursor: pointer; + width: 30px; +} + +#svg_editor #tool_square { background: 2px 2px url('images/square.png') no-repeat; } +#svg_editor #tool_rect { background: 2px 2px url('images/rect.png') no-repeat; } +#svg_editor #tool_fhrect { background: 2px 2px url('images/freehand-square.png') no-repeat; } +#svg_editor #tool_circle { background: 2px 2px url('images/circle.png') no-repeat; } +#svg_editor #tool_ellipse { background: 2px 2px url('images/ellipse.png') no-repeat; } +#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-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; } + +/* TODO: figure out what more-specific selector causes me to write this atrocity and not + simply .tool_flyout_button */ +#svg_editor #tools_rect .tool_flyout_button, #svg_editor #tools_ellipse .tool_flyout_button { + float: left; + background-color: #E8E8E8; + border-left: 1px solid #FFFFFF; + border-top: 1px solid #FFFFFF; + border-right: 1px solid #808080; + border-bottom: 1px solid #808080; + height: 28px; + width: 28px; +} + +#svg_editor .tool_button:hover, +#svg_editor .push_button:hover { + background-color: #FFF; +} + +#svg_editor #tools_rect .tool_flyout_button_current, #svg_editor #tools_ellipse .tool_flyout_button_current { + border-left: 1px solid #808080; + border-top: 1px solid #808080; + border-right: 1px solid #FFFFFF; + border-bottom: 1px solid #FFFFFF; + background-color: #B0B0B0; +} + +#svg_editor #tools_bottom { + position: absolute; + left: 40px; + right: 2px; + bottom: 2px; + height: 60px; +} + +#svg_editor #tools_bottom_1 { + width: 115px; + float: left; +} + +#svg_editor #tools_bottom_2 { + width: 250px; + float: left; +} + +#svg_editor #tools_bottom_3 { +} + +#svg_editor #copyright { + text-align: right; +} + +#svg_source_editor { + display: none; +} + +#svg_source_editor #svg_source_overlay { + position: absolute; + top: 0px; + right: 0px; + left: 0px; + bottom: 0px; + background-color: black; + opacity: 0.6; +} + +#svg_source_editor #svg_source_container { + position: absolute; + top: 30px; + left: 30px; + right: 30px; + bottom: 30px; + background-color: #B0B0B0; + opacity: 1.0; + 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; +} + +#canvas_title { + display: block; +} + +#svg_source_editor #svg_source_textarea { + position: relative; + width: 95%; + top: 5px; + height: 250px; + padding: 5px; + font-size: 12px; +} + +#svg_source_editor #tool_source_back { + text-align: left; + padding-left: 20px; +} + +#svg_docprops_container div.color_block { + float: left; + margin: 2px; + padding: 20px; +} + +#change_background div.cur_background { + border: 2px solid blue; + padding: 18px; +} + +#change_background input { + color: #888; +} + +#change_background input.cur_background { + border: 2px solid blue; + color: #000; +} + +#background_img { + position: absolute; + top: 0; + left: 0; + text-align: left; +} + +#svg_source_editor button, #svg_docprops button { + padding: 5px 5px 7px 28px; + margin: 5px 20px 0 0; +} + +#svg_docprops button { + margin-top: 0; + margin-bottom: 5px; +} + +#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; +} + +#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; +} + +/* Slider +----------------------------------*/ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; } + +.ui-slider { + border: 1px solid #B0B0B0; +} + +.ui-slider-handle { + background: #B0B0B0; + border: 1px solid #000; +} \ No newline at end of file diff --git a/editor/svg-editor-classic.html b/editor/svg-editor-classic.html new file mode 100644 index 00000000..0cc36a9d --- /dev/null +++ b/editor/svg-editor-classic.html @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +SVG-edit demo (Raster icons) + + + +
+ +
+ +
+ +
+ +
+
+

Layers

+
+ New Layer + Delete Layer + Rename Layer + Move Layer up + + Move Layer Down +
+ + + + + + +
Layer 1
+ + Move elements to: + +
+
L a y e r s
+
+ + + +
+ + +
+ Clear + + + Save + + Source + Wireframe +
+ + +
+
+ + + +
+ + +
+ | + Copy + Delete + | + + Top + Bottom + | + + + + + + + +
+ + + +
+ | + Clone + Delete + | + Group +
+
+
+ +
+
+
+ relative to: + + | + +
+ +
+ | + + Ungroup +
+ +
+ | + + + + + + + + + + + + +
+ +
+ | + + + + + + + + + + + +
+ +
+ | + + + + + + + +
+ +
+ | + + + + + + + + + +
+ +
+ + | + + + + + + + + + +
+ +
+ | + + + + + + Bold + Italic + + + + + +
+ +
+ | + + + + + + Clone + Delete +
+ +
+ +
+ Select
+ Pencil
+ Line
+ Square + + Circle
+ + Path + + Text + Image + Zoom +
+ +
+ + +
+ zoom: + + + + | +
+ +
+ + + + + + + + + + + + + + + +
fill:
100%
stroke:
100 %
+ + + +
+
+ +
+
+
+ +
+ + +
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+
+ + + +
+
+ +
+
+
+ +
+ +
+
+
+ + + + + + +
+ Editor Background +
+ +
+ +
+ + Canvas Dimensions + + + + + + + +
+
+
+
+ + + diff --git a/editor/svg-editor-classic.js b/editor/svg-editor-classic.js new file mode 100644 index 00000000..9b9e5723 --- /dev/null +++ b/editor/svg-editor-classic.js @@ -0,0 +1,1689 @@ +/* +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 svgCanvas = new SvgCanvas(document.getElementById("svgcanvas")); + + var setSelectMode = function() { + $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); + $('#tool_select').addClass('tool_button_current'); + $('#styleoverrides').text('#svgcanvas svg *{cursor:move;pointer-events:all} #svgcanvas svg{cursor:default}'); + svgCanvas.setMode('select'); + }; + + // used to make the flyouts stay on the screen longer the very first time + var flyoutspeed = 1250; + var textBeingEntered = false; + 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','%']; + + var fillPaint = new $.jGraduate.Paint({solidColor: "FF0000"}); // solid red + var strokePaint = new $.jGraduate.Paint({solidColor: "000000"}); // solid black + + // TODO: Unfortunately Mozilla does not handle internal references to gradients + // inside a data: URL document. This means that any elements filled/stroked + // 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) { + window.open("data:image/svg+xml;base64," + Utils.encode64(svg)); + }; + + // called when we've selected a different element + var selectedChanged = function(window,elems) { + // if elems[1] is present, then we have more than one element + selectedElement = (elems.length == 1 || elems[1] == null ? elems[0] : null); + multiselected = (elems.length >= 2 && elems[1] != null); + var is_node = false; + if (selectedElement != null) { + // unless we're already in always set the mode of the editor to select because + // upon creation of a text element the editor is switched into + // select mode and this event fires - we need our UI to be in sync + + is_node = !!(selectedElement.id && selectedElement.id.indexOf('pathpointgrip') == 0); + + if (svgCanvas.getMode() != "multiselect" && !is_node) { + setSelectMode(); + updateToolbar(); + } + + } // if (elem != null) + + + // Deal with pathedit mode + $('#path_node_panel').toggle(is_node); + $('#tools_bottom_2,#tools_bottom_3').toggle(!is_node); + if(is_node) { + // Change select icon + $('.tool_button').removeClass('tool_button_current'); + $('#tool_select').attr('src','images/select_node.png').addClass('tool_button_current'); + } else { + $('#tool_select').attr('src','images/select.png'); + + } + + updateContextPanel(); + }; + + // called when any element has changed + var elementChanged = function(window,elems) { + for (var i = 0; i < elems.length; ++i) { + var elem = elems[i]; + // if the element changed was the svg, then it could be a resolution change + if (elem && elem.tagName == "svg" && elem.getAttribute("viewBox")) { + var vb = elem.getAttribute("viewBox").split(' '); + changeResolution(parseInt(vb[2]), + parseInt(vb[3])); + } + } + + // we update the contextual panel with potentially new + // positional/sizing information (we DON'T want to update the + // toolbar here as that creates an infinite loop) + // also this updates the history buttons + + // we tell it to skip focusing the text control if the + // text element was previously in focus + updateContextPanel(); + }; + + var updateBgImage = function() { + var bg_img = $('#background_img'); + if(!bg_img.length) return; + var img = bg_img.find('img'); + var zoomlevel = svgCanvas.getZoom(); + img.width(zoomlevel*100 + '%'); + } + + var zoomChanged = function(window, bbox) { + var scrbar = 15; + var res = svgCanvas.getResolution(); + var w_area = $('#workarea'); + var canvas_pos = $('#svgcanvas').position(); + 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) + Math.max(0,canvas_pos.left); + var scrTop = bb.y * zoomlevel; + var scrOffY = w_area.height()/2 - (bb.height * zoomlevel)/2; + w_area[0].scrollTop = Math.max(0,scrTop - scrOffY) + Math.max(0,canvas_pos.top); + if(svgCanvas.getMode() == 'zoom' && bb.width) { + // Go to select if a zoom box was drawn + setSelectMode(); + } + } + + // updates the toolbar (colors, opacity, etc) based on the selected element + var updateToolbar = function() { + if (selectedElement != null && + selectedElement.tagName != "image" && + selectedElement.tagName != "g") + { + // get opacity values + var fillOpacity = parseFloat(selectedElement.getAttribute("fill-opacity")); + if (isNaN(fillOpacity)) { + fillOpacity = 1.0; + } + + var strokeOpacity = parseFloat(selectedElement.getAttribute("stroke-opacity")); + if (isNaN(strokeOpacity)) { + strokeOpacity = 1.0; + } + + // update fill color and opacity + var fillColor = selectedElement.getAttribute("fill")||"none"; + // prevent undo on these canvas changes + svgCanvas.setFillColor(fillColor, true); + svgCanvas.setFillOpacity(fillOpacity, true); + + // update stroke color and opacity + var strokeColor = selectedElement.getAttribute("stroke")||"none"; + // prevent undo on these canvas changes + svgCanvas.setStrokeColor(strokeColor, true); + svgCanvas.setStrokeOpacity(strokeOpacity, true); + + fillOpacity *= 100; + strokeOpacity *= 100; + + var getPaint = function(color, opac) { + // update the editor's fill paint + var opts = null; + + if (color.substr(0,5) == "url(#") { + opts = { + alpha: opac, + linearGradient: document.getElementById(color.substr(5,color.length-6)) + }; + } + else if (color.substr(0,1) == "#") { + opts = { + alpha: opac, + solidColor: color.substr(1) + }; + } + return new $.jGraduate.Paint(opts); + } + + fillPaint = getPaint(fillColor, fillOpacity); + strokePaint = getPaint(strokeColor, strokeOpacity); + + fillOpacity = fillOpacity + " %"; + strokeOpacity = strokeOpacity + " %"; + + // update fill color + if (fillColor == "none") { + fillOpacity = "N/A"; + } + document.getElementById("gradbox_fill").parentNode.firstChild.setAttribute("fill", fillColor); + + if (strokeColor == null || strokeColor == "" || strokeColor == "none") { + strokeColor = "none"; + strokeOpacity = "N/A"; + } + + // update the rect inside #fill_color + document.getElementById("gradbox_stroke").parentNode.firstChild.setAttribute("fill", strokeColor); + + $('#fill_opacity').html(fillOpacity); + $('#stroke_opacity').html(strokeOpacity); + var opac_perc = ((selectedElement.getAttribute("opacity")||1.0)*100); + $('#group_opacity').val(opac_perc); + $('#opac_slider').slider('option', 'value', opac_perc); + $('#stroke_width').val(selectedElement.getAttribute("stroke-width")||1); + $('#stroke_style').val(selectedElement.getAttribute("stroke-dasharray")||"none"); + } + + updateToolButtonState(); + }; + + // updates the context panel tools based on the selected element + var updateContextPanel = function() { + var elem = selectedElement; + var currentLayer = svgCanvas.getCurrentLayer(); + + // No need to update anything else in rotate mode + if (svgCanvas.getMode() == 'rotate' && elem != null) { + $('#angle').val(svgCanvas.getRotationAngle(elem)); + return; + } + + var is_node = elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false; + + $('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,\ + #ellipse_panel, #line_panel, #text_panel, #image_panel').hide(); + if (elem != null) { + $('#angle').val(svgCanvas.getRotationAngle(elem)); + + if(!is_node) { + $('#selected_panel').show(); + } else { + var point = svgCanvas.getNodePoint(); + if(point) { + var seg_type = $('#seg_type'); + $('#path_node_x').val(point.x); + $('#path_node_y').val(point.y); + if(point.type) { + seg_type.val(point.type).removeAttr('disabled'); + } else { + seg_type.val(4).attr('disabled','disabled'); + } + } + return; + } + + // update contextual tools here + var panels = { + g: [], + rect: ['rx','x','y','width','height'], + image: ['x','y','width','height'], + circle: ['cx','cy','r'], + ellipse: ['cx','cy','rx','ry'], + line: ['x1','y1','x2','y2'], + text: ['x','y'] + }; + + var el_name = elem.tagName; + + if(panels[el_name]) { + var cur_panel = panels[el_name]; + + + $('#' + el_name + '_panel').show(); + + $.each(cur_panel, function(i, item) { + $('#' + el_name + '_' + item).val(elem.getAttribute(item) || 0); + }); + + if(el_name == 'text') { + $('#text_panel').css("display", "inline"); + if (svgCanvas.getItalic()) { + $('#tool_italic').addClass('tool_button_current'); + } + else { + $('#tool_italic').removeClass('tool_button_current'); + } + if (svgCanvas.getBold()) { + $('#tool_bold').addClass('tool_button_current'); + } + else { + $('#tool_bold').removeClass('tool_button_current'); + } + $('#font_family').val(elem.getAttribute("font-family")); + $('#font_size').val(elem.getAttribute("font-size")); + $('#text').val(elem.textContent); + if (svgCanvas.addedNew) { + $('#text').focus().select(); + } + } // text + else if(el_name == 'image') { + var xlinkNS="http://www.w3.org/1999/xlink"; + $('#image_url').val(elem.getAttributeNS(xlinkNS, "href")); + } // image + } + } // if (elem != null) + else if (multiselected) { + $('#multiselected_panel').show(); + } + + // update history buttons + if (svgCanvas.getUndoStackSize() > 0) { + $('#tool_undo').removeClass( 'tool_button_disabled'); + } + else { + $('#tool_undo').addClass( 'tool_button_disabled'); + } + if (svgCanvas.getRedoStackSize() > 0) { + $('#tool_redo').removeClass( 'tool_button_disabled'); + } + else { + $('#tool_redo').addClass( 'tool_button_disabled'); + } + + svgCanvas.addedNew = false; + + if ( (elem && !is_node) || multiselected) { + // update the selected elements' layer + $('#selLayerNames').removeAttr('disabled').val(currentLayer); + } + else { + $('#selLayerNames').attr('disabled', 'disabled'); + } + + }; + + $('#text').focus( function(){ textBeingEntered = true; } ); + $('#text').blur( function(){ textBeingEntered = false; } ); + + // bind the selected event to our function that handles updates to the UI + svgCanvas.bind("selected", selectedChanged); + svgCanvas.bind("changed", elementChanged); + svgCanvas.bind("saved", saveHandler); + svgCanvas.bind("zoomed", zoomChanged); + + var str = '
' + $.each(palette, function(i,item){ + str += '
'; + }); + $('#palette').append(str); + + // Set up editor background functionality + var color_blocks = ['#FFF','#888','#000','url(data:image/gif;base64,R0lGODlhEAAQAIAAAP%2F%2F%2F9bW1iH5BAAAAAAALAAAAAAQABAAAAIfjG%2Bgq4jM3IFLJgpswNly%2FXkcBpIiVaInlLJr9FZWAQA7)']; + var str = ''; + $.each(color_blocks, function() { + str += '
'; + }); + $('#bg_blocks').append(str); + var blocks = $('#bg_blocks div'); + var cur_bg = 'cur_background'; + blocks.each(function() { + var blk = $(this); + blk.click(function() { + blocks.removeClass(cur_bg); + $(this).addClass(cur_bg); + $('#canvas_bg_url').removeClass(cur_bg); + }); + }); + $('#canvas_bg_url').focus(function() { + blocks.removeClass(cur_bg); + $(this).addClass(cur_bg); + }); + + var pos = $('#tools_rect_show').position(); + $('#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+77}); + + var changeRectRadius = function(ctl) { + svgCanvas.setRectRadius(ctl.value); + } + + var changeFontSize = function(ctl) { + svgCanvas.setFontSize(ctl.value); + } + + var changeStrokeWidth = function(ctl) { + var val = ctl.value; + if(val == 0 && selectedElement && $.inArray(selectedElement.nodeName, ['line', 'polyline']) != -1) { + val = ctl.value = 1; + } + svgCanvas.setStrokeWidth(val); + } + + var changeRotationAngle = function(ctl) { + svgCanvas.setRotationAngle(ctl.value); + } + var changeZoom = function(ctl) { + var zoomlevel = ctl.value / 100; + var zoom = svgCanvas.getZoom(); + var w_area = $('#workarea'); + + zoomChanged(window, { + width: 0, + height: 0, + x: (w_area[0].scrollLeft + w_area.width()/2)/zoom, + y: (w_area[0].scrollTop + w_area.height()/2)/zoom, + zoom: zoomlevel + }); + } + + var changeOpacity = function(ctl, val) { + if(val == null) val = ctl.value; + $('#group_opacity').val(val); + if(!ctl || !ctl.handle) { + $('#opac_slider').slider('option', 'value', val); + } + svgCanvas.setOpacity(val/100); + } + + $('#stroke_style').change(function(){ + svgCanvas.setStrokeStyle(this.options[this.selectedIndex].value); + }); + + // Lose focus for select elements when changed (Allows keyboard shortcuts to work better) + $('select').change(function(){$(this).blur();}); + + // fired when user wants to move elements to another layer + var promptMoveLayerOnce = false; + $('#selLayerNames').change(function(){ + var destLayer = this.options[this.selectedIndex].value; + // TODO: localize this prompt + if (destLayer && (promptMoveLayerOnce || confirm('Move selected elements to layer \'' + destLayer + '\'?'))) { + promptMoveLayerOnce = true; + svgCanvas.moveSelectedToLayer(destLayer); + svgCanvas.clearSelection(); + populateLayers(); + } + }); + + $('#font_family').change(function(){ + svgCanvas.setFontFamily(this.options[this.selectedIndex].value); + }); + + $('#seg_type').change(function() { + svgCanvas.setSegType($(this).val()); + }); + + $('#text').keyup(function(){ + svgCanvas.setTextContent(this.value); + }); + + // TODO: consider only setting the URL once Enter has been pressed? + $('#image_url').keyup(function(){ + svgCanvas.setImageURL(this.value); + }); + + $('.attr_changer').change(function() { + var attr = this.getAttribute("data-attr"); + var val = this.value; + var valid = false; + if($.inArray(attr, length_attrs) != -1) { + if(!isNaN(val)) { + valid = true; + } else { + //TODO: Allow the values in length_types, then uncomment this: +// val = val.toLowerCase(); +// $.each(length_types, function(i, unit) { +// if(valid) return; +// var re = new RegExp('^-?[\\d\\.]+' + unit + '$'); +// if(re.test(val)) valid = true; +// }); + } + } else valid = true; + + if(!valid) { + // TODO: localize this + alert('Invalid value given for' + $(this).attr('title').replace('Change','') + + '.'); + this.value = selectedElement.getAttribute(attr); + return false; + } + svgCanvas.changeSelectedAttribute(attr, val); + }); + + // Prevent selection of elements when shift-clicking + $('#palette').mouseover(function() { + var inp = $(''); + $(this).append(inp); + inp.focus().remove(); + }); + + $('.palette_item').click(function(evt){ + var picker = (evt.shiftKey ? "stroke" : "fill"); + var id = (evt.shiftKey ? '#stroke_' : '#fill_'); + var color = $(this).attr('data-rgb'); + var rectbox = document.getElementById("gradbox_"+picker).parentNode.firstChild; + var paint = null; + + // Webkit-based browsers returned 'initial' here for no stroke + if (color == 'transparent' || color == 'initial') { + color = 'none'; + $(id + "opacity").html("N/A"); + paint = new $.jGraduate.Paint(); + } + else { + paint = new $.jGraduate.Paint({alpha: 100, solidColor: color.substr(1)}); + } + rectbox.setAttribute("fill", color); + + if (evt.shiftKey) { + strokePaint = paint; + if (svgCanvas.getStrokeColor() != color) { + svgCanvas.setStrokeColor(color); + } + if (color != 'none' && svgCanvas.getStrokeOpacity() != 1) { + svgCanvas.setStrokeOpacity(1.0); + $("#stroke_opacity").html("100 %"); + } + } else { + fillPaint = paint; + if (svgCanvas.getFillColor() != color) { + svgCanvas.setFillColor(color); + } + if (color != 'none' && svgCanvas.getFillOpacity() != 1) { + svgCanvas.setFillOpacity(1.0); + $("#fill_opacity").html("100 %"); + } + } + updateToolButtonState(); + }); + + // This is a common function used when a tool has been clicked (chosen) + // It does several common things: + // - removes the tool_button_current class from whatever tool currently has it + // - hides any flyouts + // - adds the tool_button_current class to the button passed in + var toolButtonClick = function(button, fadeFlyouts) { + if ($(button).hasClass('tool_button_disabled')) return false; + var fadeFlyouts = fadeFlyouts || 'normal'; + $('.tools_flyout').fadeOut(fadeFlyouts); + $('#styleoverrides').text(''); + $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); + $(button).addClass('tool_button_current'); + // when a tool is selected, we should deselect any currently selected elements + svgCanvas.clearSelection(); + return true; + }; + + var addDropDown = function(elem, callback, dropUp) { + var button = $(elem).find('button'); + var list = $(elem).find('ul'); + var on_button = false; + if(dropUp) { + $(elem).addClass('dropup'); + } + + $(elem).find('li').bind('mouseup', callback); + + $().mouseup(function(evt) { + if(!on_button) { + button.removeClass('down'); + list.hide(); + } + on_button = false; + }); + + button.bind('mousedown',function() { + if (!button.hasClass('down')) { + button.addClass('down'); + list.show(); + on_button = true; + } else { + button.removeClass('down'); + list.hide(); + } + }).hover(function() { + on_button = true; + }).mouseout(function() { + on_button = false; + }); + } + + addDropDown('#opacity_dropdown', function() { + if($(this).find('div').length) return; + var perc = parseInt($(this).text().split('%')[0]); + changeOpacity(false, perc); + }); + + // For slider usage, see: http://jqueryui.com/demos/slider/ + $("#opac_slider").slider({ + start: function() { + $('#opacity_dropdown li:not(.special)').hide(); + }, + stop: function() { + $('#opacity_dropdown li').show(); + }, + slide: function(evt, ui){ + changeOpacity(ui); + } + }); + + addDropDown('#zoom_dropdown', function() { + var item = $(this); + var val = item.attr('data-val'); + if(val) { + zoomChanged(window, val); + } else { + changeZoom({value:parseInt(item.text())}); + } + }, true); + + var clickSelect = function() { + if (toolButtonClick('#tool_select')) { + svgCanvas.setMode('select'); + $('#styleoverrides').text('#svgcanvas svg *{cursor:move;pointer-events:all}, #svgcanvas svg{cursor:default}'); + } + }; + + var clickFHPath = function() { + if (toolButtonClick('#tool_fhpath')) { + svgCanvas.setMode('fhpath'); + } + }; + + var clickLine = function() { + if (toolButtonClick('#tool_line')) { + svgCanvas.setMode('line'); + } + }; + + var clickSquare = function(){ + if (toolButtonClick('#tools_rect_show', flyoutspeed)) { + flyoutspeed = 'normal'; + svgCanvas.setMode('square'); + } + $('#tools_rect_show').attr('src', 'images/square.png'); + }; + + var clickRect = function(){ + if (toolButtonClick('#tools_rect_show')) { + svgCanvas.setMode('rect'); + } + $('#tools_rect_show').attr('src', 'images/rect.png'); + }; + + var clickImage = function(){ + if (toolButtonClick('#tool_image')) { + svgCanvas.setMode('image'); + } + }; + + 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', 'images/freehand-square.png'); + }; + + var clickCircle = function(){ + if (toolButtonClick('#tools_ellipse_show', flyoutspeed)) { + flyoutspeed = 'normal'; + svgCanvas.setMode('circle'); + } + $('#tools_ellipse_show').attr('src', 'images/circle.png'); + }; + + var clickEllipse = function(){ + if (toolButtonClick('#tools_ellipse_show')) { + svgCanvas.setMode('ellipse'); + } + $('#tools_ellipse_show').attr('src', 'images/ellipse.png'); + }; + + var clickFHEllipse = function(){ + if (toolButtonClick('#tools_ellipse_show')) { + svgCanvas.setMode('fhellipse'); + } + $('#tools_ellipse_show').attr('src', 'images/freehand-circle.png'); + }; + + var clickText = function(){ + toolButtonClick('#tool_text'); + svgCanvas.setMode('text'); + }; + + var clickPath = function(){ + toolButtonClick('#tool_path'); + svgCanvas.setMode('path'); + }; + + // Delete is a contextual tool that only appears in the ribbon if + // an element has been selected + var deleteSelected = function() { + if (selectedElement != null || multiselected) { + svgCanvas.deleteSelectedElements(); + } + }; + + var moveToTopSelected = function() { + if (selectedElement != null) { + svgCanvas.moveToTopSelectedElement(); + } + }; + + var moveToBottomSelected = function() { + if (selectedElement != null) { + svgCanvas.moveToBottomSelectedElement(); + } + }; + + var moveSelected = function(dx,dy) { + if (selectedElement != null || multiselected) { + svgCanvas.moveSelectedElements(dx,dy); + } + }; + + var clonePathNode = function() { + if (svgCanvas.getNodePoint()) { + svgCanvas.clonePathNode(); + } + }; + + var deletePathNode = function() { + if (svgCanvas.getNodePoint()) { + svgCanvas.deletePathNode(); + } + }; + + var selectNext = function() { + svgCanvas.cycleElement(1); + } + + var selectPrev = function() { + svgCanvas.cycleElement(0); + } + + var rotateSelected = function(cw) { + if (selectedElement == null || multiselected) return; + var step = 5; + if(!cw) step *= -1; + var new_angle = $('#angle').val()*1 + step; + svgCanvas.setRotationAngle(new_angle); + } + + var clickClear = function(){ + // TODO: localize this prompt + if( confirm('Do you want to clear the drawing?\nThis will also erase your undo history!') ) { + svgCanvas.clear(); + updateContextPanel(); + } + }; + + var clickBold = function(){ + svgCanvas.setBold( !svgCanvas.getBold() ); + updateContextPanel(); + }; + + var clickItalic = function(){ + svgCanvas.setItalic( !svgCanvas.getItalic() ); + updateContextPanel(); + }; + + var clickSave = function(){ + svgCanvas.save(); + }; + + var clickOpen = function(){ + svgCanvas.open(); + }; + + var clickUndo = function(){ + if (svgCanvas.getUndoStackSize() > 0) { + svgCanvas.undo(); + populateLayers(); + } + }; + + var clickRedo = function(){ + if (svgCanvas.getRedoStackSize() > 0) { + svgCanvas.redo(); + populateLayers(); + } + }; + + var clickGroup = function(){ + // group + if (multiselected) { + svgCanvas.groupSelectedElements(); + } + // ungroup + else { + svgCanvas.ungroupSelectedElement(); + } + }; + + var clickClone = function(){ + svgCanvas.cloneSelectedElements(); + }; + + var clickAlignLeft = function(){ + svgCanvas.alignSelectedElements('l', $('#align_relative_to option:selected').val() ); + }; + var clickAlignCenter = function(){ + svgCanvas.alignSelectedElements('c', $('#align_relative_to option:selected').val() ); + }; + var clickAlignRight = function(){ + svgCanvas.alignSelectedElements('r', $('#align_relative_to option:selected').val() ); + }; + var clickAlignTop = function(){ + svgCanvas.alignSelectedElements('t', $('#align_relative_to option:selected').val() ); + }; + var clickAlignMiddle = function(){ + svgCanvas.alignSelectedElements('m', $('#align_relative_to option:selected').val() ); + }; + var clickAlignBottom = function(){ + svgCanvas.alignSelectedElements('b', $('#align_relative_to option:selected').val() ); + }; + + 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); + }; + + var clickWireframe = function() { + $('#tool_wireframe').toggleClass('push_button_pressed'); + $('#workarea').toggleClass('wireframe'); + } + + var showSourceEditor = function(){ + if (editingsource) return; + editingsource = true; + var str = svgCanvas.getSvgString(); + $('#svg_source_textarea').val(str); + $('#svg_source_editor').fadeIn(); + properlySourceSizeTextArea(); + $('#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); + $('#canvas_title').val(svgCanvas.getImageTitle()); + + // Update background color with current one + var blocks = $('#bg_blocks div'); + var cur_bg = 'cur_background'; + var canvas_bg = $('#svgcanvas').css('background'); + var url = canvas_bg.match(/url\("?(.*?)"?\)/); + if(url) url = url[1]; + blocks.each(function() { + var blk = $(this); + var is_bg = blk.css('background') == canvas_bg; + blk.toggleClass(cur_bg, is_bg); + if(is_bg) $('#canvas_bg_url').removeClass(cur_bg); + }); + if(!canvas_bg) blocks.eq(0).addClass(cur_bg); + if(!$('#bg_blocks .' + cur_bg).length && url) { + $('#canvas_bg_url').val(url); + } + + $('#svg_docprops').fadeIn(); + }; + + var properlySourceSizeTextArea = function(){ + // TODO: remove magic numbers here and get values from CSS + var height = $('#svg_source_container').height() - 80; + $('#svg_source_textarea').css('height', height); + }; + + var saveSourceEditor = function(){ + if (!editingsource) return; + + if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) { + // TODO: localize this prompt + if( !confirm('There were parsing errors in your SVG source.\nRevert back to original SVG source?') ) { + return false; + } + } + svgCanvas.clearSelection(); + hideSourceEditor(); + populateLayers(); + }; + + var saveDocProperties = function(){ + // set title + svgCanvas.setImageTitle($('#canvas_title').val()); + + // 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; + } + + // set background + var new_bg, bg_url = $('#canvas_bg_url').val(); + var bg_blk = $('#bg_blocks div.cur_background'); + if(bg_blk.length) { + new_bg = bg_blk.css('background'); + $('#svgcanvas').css('background',new_bg); + $('#background_img').remove(); + } else if(bg_url) { + if(!$('#background_img').length) { + $('
') + .prependTo('#svgcanvas'); + } else { + $('#background_img img').attr('src',bg_url); + } + } else { + new_bg = '#FFF'; + $('#svgcanvas').css('background',new_bg); + $('#background_img').remove(); + } + + hideDocProperties(); + }; + + var cancelOverlays = function() { + if (!editingsource && !docprops) return; + + if (editingsource) { + var oldString = svgCanvas.getSvgString(); + if (oldString != $('#svg_source_textarea').val()) { + // TODO: localize this prompt + if( !confirm('Ignore changes made to SVG source?') ) { + return false; + } + } + hideSourceEditor(); + } + else if (docprops) { + hideDocProperties(); + } + }; + + var hideSourceEditor = function(){ + $('#svg_source_editor').hide(); + editingsource = false; + $('#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; + properlySourceSizeTextArea(); + }); + + $('#tool_select').click(clickSelect); + $('#tool_fhpath').click(clickFHPath); + $('#tool_line').click(clickLine); + $('#tool_square').mouseup(clickSquare); + $('#tool_rect').mouseup(clickRect); + $('#tool_fhrect').mouseup(clickFHRect); + $('#tool_circle').mouseup(clickCircle); + $('#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_path').click(clickPath); + $('#tool_clear').click(clickClear); + $('#tool_save').click(clickSave); + $('#tool_open').click(clickOpen); + $('#tool_source').click(showSourceEditor); + $('#tool_wireframe').click(clickWireframe); + $('#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_node_clone').click(clonePathNode); + $('#tool_node_delete').click(deletePathNode); + $('#tool_move_top').click(moveToTopSelected); + $('#tool_move_bottom').click(moveToBottomSelected); + $('#tool_undo').click(clickUndo); + $('#tool_redo').click(clickRedo); + $('#tool_clone').click(clickClone); + $('#tool_clone_multi').click(clickClone); + $('#tool_group').click(clickGroup); + $('#tool_ungroup').click(clickGroup); + $('#tool_alignleft').click(clickAlignLeft); + $('#tool_aligncenter').click(clickAlignCenter); + $('#tool_alignright').click(clickAlignRight); + $('#tool_aligntop').click(clickAlignTop); + $('#tool_alignmiddle').click(clickAlignMiddle); + $('#tool_alignbottom').click(clickAlignBottom); + // these two lines are required to make Opera work properly with the flyout mechanism + $('#tools_rect_show').click(clickSquare); + $('#tools_ellipse_show').click(clickCircle); + $('#tool_bold').mousedown(clickBold); + $('#tool_italic').mousedown(clickItalic); + + // added these event handlers for all the push buttons so they + // behave more like buttons being pressed-in and not images + function setPushButtons() { + var toolnames = ['clear','open','save','source','delete','delete_multi','paste','clone','clone_multi','move_top','move_bottom']; + var all_tools = ''; + var cur_class = 'tool_button_current'; + + $.each(toolnames, function(i,item) { + all_tools += '#tool_' + item + (i==toolnames.length-1?',':''); + }); + + $(all_tools).mousedown(function() { + $(this).addClass(cur_class); + }).bind('mousedown mouseout', function() { + $(this).removeClass(cur_class); + }); + + $('#tool_undo, #tool_redo').mousedown(function(){ + if (!$(this).hasClass('tool_button_disabled')) $(this).addClass(cur_class); + }).bind('mousedown mouseout',function(){ + $(this).removeClass(cur_class);} + ); + } + + setPushButtons(); + + $('#workarea').bind("mousewheel DOMMouseScroll", function(e){ + if(!e.shiftKey) return; + e.preventDefault(); + var off = $('#svgcanvas').offset(); + var zoom = svgCanvas.getZoom(); + var bbox = { + 'x': (e.pageX - off.left)/zoom, + 'y': (e.pageY - off.top)/zoom, + 'width': 0, + 'height': 0 + }; + + // Respond to mouse wheel in IE/Webkit/Opera. + // (It returns up/dn motion in multiples of 120) + if(e.wheelDelta) { + if (e.wheelDelta >= 120) { + bbox.factor = 2; + } else if (e.wheelDelta <= -120) { + bbox.factor = .5; + } + } else if(e.detail) { + if (e.detail > 0) { + bbox.factor = .5; + } else if (e.detail < 0) { + bbox.factor = 2; + } + } + + if(!bbox.factor) return; + zoomChanged(window, bbox); + }); + + // switch modifier key in tooltips if mac + // NOTE: This code is not used yet until I can figure out how to successfully bind ctrl/meta + // in Opera and Chrome + if (isMac) { + var shortcutButtons = ["tool_clear", "tool_save", "tool_source", "tool_undo", "tool_redo", "tool_clone"]; + var i = shortcutButtons.length; + while (i--) { + var button = document.getElementById(shortcutButtons[i]); + var title = button.title; + var index = title.indexOf("Ctrl+"); + button.title = [title.substr(0,index), "Cmd+", title.substr(index+5)].join(''); + } + } + + // do keybindings using jquery-hotkeys plugin + function setKeyBindings() { + var keys = [ + ['1', clickSelect], + ['2', clickFHPath], + ['3', clickLine], + ['Shift+4', clickSquare], + ['4', clickRect], + ['Shift+5', clickCircle], + ['5', clickEllipse], + ['6', clickPath], + ['7', clickText], + ['8', clickImage], + [modKey+'N', function(evt){clickClear();evt.preventDefault();}], + [modKey+'S', function(evt){editingsource?saveSourceEditor():clickSave();evt.preventDefault();}], + [modKey+'O', function(evt){clickOpen();evt.preventDefault();}], + ['del', function(evt){deleteSelected();evt.preventDefault();}], + ['backspace', function(evt){deleteSelected();evt.preventDefault();}], + ['shift+up', moveToTopSelected], + ['shift+down', moveToBottomSelected], + ['shift+left', function(){rotateSelected(0)}], + ['shift+right', function(){rotateSelected(1)}], + ['shift+O', selectPrev], + ['shift+P', selectNext], + ['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();}], + ['right', function(evt){moveSelected(1,0);evt.preventDefault();}], + [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();}], + [modKey+'f', function(evt){clickWireframe();evt.preventDefault();}], + [modKey+'x', function(evt){toggleSidePanel();}], + ['esc', cancelOverlays, false], + ]; + + $.each(keys,function(i,item) { + var disable = !(item.length > 2 && !item[2]); + $(document).bind('keydown', {combi:item[0], disableInInput: disable}, item[1]); + }); + + $('.attr_changer').bind('keydown', {combi:'return', disableInInput: false}, + function(evt) {$(this).change();evt.preventDefault();} + ); + } + + setKeyBindings(); + + // 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) { + var picker = elem.attr('id') == 'stroke_color' ? 'stroke' : 'fill'; + var opacity = (picker == 'stroke' ? $('#stroke_opacity') : $('#fill_opacity')); + var paint = (picker == 'stroke' ? strokePaint : fillPaint); + var title = (picker == 'stroke' ? 'Pick a Stroke Paint and Opacity' : 'Pick a Fill Paint and Opacity'); + var was_none = false; + if (paint.type == "none") { + // if it was none, then set to solid white + paint = new $.jGraduate.Paint({solidColor: 'ffffff'}); + was_none = true; + } + var pos = elem.position(); + $("#color_picker") + .draggable({cancel:'.jPicker_table,.jGraduate_lgPick'}) + .css({'left': pos.left, 'bottom': 50 - pos.top}) + .jGraduate( + { + paint: paint, + window: { pickerTitle: title }, + images: { clientPath: "jgraduate/images/" }, + }, + function(p) { + paint = new $.jGraduate.Paint(p); + + var oldgrad = document.getElementById("gradbox_"+picker); + var svgbox = oldgrad.parentNode; + var rectbox = svgbox.firstChild; + + if (paint.type == "linearGradient") { + svgbox.removeChild(oldgrad); + var newgrad = svgbox.appendChild(document.importNode(paint.linearGradient, true)); + newgrad.id = "gradbox_"+picker; + rectbox.setAttribute("fill", "url(#gradbox_" + picker + ")"); + } + else { + rectbox.setAttribute("fill", "#" + paint.solidColor); + } + opacity.html(paint.alpha + " %"); + + if (picker == 'stroke') { + svgCanvas.setStrokePaint(paint, true); + } + else { + svgCanvas.setFillPaint(paint, true); + } + updateToolbar(); + $('#color_picker').hide(); + }, + function(p) { + $('#color_picker').hide(); + }); + }; + + var updateToolButtonState = function() { + var bNoFill = (svgCanvas.getFillColor() == 'none'); + var bNoStroke = (svgCanvas.getStrokeColor() == 'none'); + var buttonsNeedingStroke = [ '#tool_path', '#tool_line' ]; + var buttonsNeedingFillAndStroke = [ '#tools_rect_show', '#tools_ellipse_show', '#tool_text' ]; + if (bNoStroke) { + for (index in buttonsNeedingStroke) { + var button = buttonsNeedingStroke[index]; + if ($(button).hasClass('tool_button_current')) { + clickSelect(); + } + $(button).removeClass('tool_button').addClass('tool_button_disabled'); + } + } + else { + for (index in buttonsNeedingStroke) { + var button = buttonsNeedingStroke[index]; + $(button).removeClass('tool_button_disabled').addClass('tool_button'); + } + } + + if (bNoStroke && bNoFill) { + for (index in buttonsNeedingFillAndStroke) { + var button = buttonsNeedingFillAndStroke[index]; + if ($(button).hasClass('tool_button_current')) { + clickSelect(); + } + $(button).removeClass('tool_button').addClass('tool_button_disabled'); + } + } + else { + for (index in buttonsNeedingFillAndStroke) { + var button = buttonsNeedingFillAndStroke[index]; + $(button).removeClass('tool_button_disabled').addClass('tool_button'); + } + } + }; + + // set up gradients to be used for the buttons + var svgdocbox = new DOMParser().parseFromString( + '\ + \ + \ + \ + ', 'text/xml'); + + var boxgrad = svgdocbox.getElementById('gradbox_'); + boxgrad.id = 'gradbox_fill'; + $('#fill_color').append( document.importNode(svgdocbox.documentElement,true) ); + + boxgrad.id = 'gradbox_stroke'; + $(svgdocbox.documentElement.firstChild).attr('fill', '#000000'); + $('#stroke_color').append( document.importNode(svgdocbox.documentElement,true) ); + + $('#fill_color').click(function(){ + colorPicker($(this)); + updateToolButtonState(); + }); + + $('#stroke_color').click(function(){ + colorPicker($(this)); + updateToolButtonState(); + }); + + $('#tools_rect_show').mousedown(function(evt){ + $('#tools_rect').show(); + // this prevents the 'image drag' behavior in Firefox + evt.preventDefault(); + }); + $('#tools_rect').mouseleave(function(){$('#tools_rect').fadeOut();}); + + $('#tool_move_top').mousedown(function(evt){ + $('#tools_stacking').show(); + evt.preventDefault(); + }); + + $('#tools_ellipse_show').mousedown(function(evt){ + $('#tools_ellipse').show(); + // this prevents the 'image drag' behavior in Firefox + evt.preventDefault(); + }); + $('#tools_ellipse').mouseleave(function() {$('#tools_ellipse').fadeOut();}); + + $('.tool_flyout_button').mouseover(function() { + $(this).addClass('tool_flyout_button_current'); + }).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'); + }); + + $('.push_button').mousedown(function() { + if (!$(this).hasClass('tool_button_disabled')) { + $(this).addClass('push_button_pressed'); + } + }).mouseout(function() { + $(this).removeClass('push_button_pressed'); + }).mouseup(function() { + $(this).removeClass('push_button_pressed'); + }); + + $('#layer_new').click(function() { + var curNames = new Array(svgCanvas.getNumLayers()); + for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); } + + var j = (curNames.length+1); + var uniqName = "Layer " + j; + while (jQuery.inArray(uniqName, curNames) != -1) { + j++; + uniqName = "Layer " + j; + } + // TODO: localize this + var newName = prompt("Please enter a unique layer name",uniqName); + if (!newName) return; + if (jQuery.inArray(newName, curNames) != -1) { + alert("There is already a layer named that!"); + return; + } + svgCanvas.createLayer(newName); + updateContextPanel(); + populateLayers(); + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:first').addClass("layersel"); + }); + + $('#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 tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:first').addClass("layersel"); + } + }); + + $('#layer_up').click(function() { + // find index position of selected option + var curIndex = $('#layerlist tr.layersel').prevAll().length; + if (curIndex > 0) { + var total = $('#layerlist tr.layer').length; + curIndex--; + svgCanvas.setCurrentLayerPosition(total-curIndex-1); + populateLayers(); + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel"); + } + }); + + $('#layer_down').click(function() { + // find index position of selected option + var curIndex = $('#layerlist tr.layersel').prevAll().length; + var total = $('#layerlist tr.layer').length; + if (curIndex < total-1) { + curIndex++; + svgCanvas.setCurrentLayerPosition(total-curIndex-1); + populateLayers(); + $('#layerlist tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel"); + } + }); + + $('#layer_rename').click(function() { + var curIndex = $('#layerlist tr.layersel').prevAll().length; + var oldName = $('#layerlist tr.layersel td.layername').text(); + // TODO: localize this + 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 tr.layer').removeClass("layersel"); + $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel"); + }); + + var SIDEPANEL_MAXWIDTH = 300; + var SIDEPANEL_OPENWIDTH = 150; + var sidedrag = -1, sidedragging = false; + $('#sidepanel_handle') + .mousedown(function(evt) {sidedrag = evt.pageX;}) + .mouseup(function(evt) { + if (!sidedragging) toggleSidePanel(); + sidedrag = -1; + sidedragging = false; + }); + $('#svg_editor') + .mouseup(function(){sidedrag=-1;}) + .mouseout(function(evt){ + if (sidedrag == -1) return; + // if we've moused out of the browser window, then we can stop dragging + // and close the drawer + if (evt.pageX > this.clientWidth) { + sidedrag = -1; + toggleSidePanel(true); + } + }) + .mousemove(function(evt) { + if (sidedrag == -1) return; + sidedragging = true; + var deltax = sidedrag - evt.pageX; + + var sidepanels = $('#sidepanels'); + var sidewidth = parseInt(sidepanels.css('width')); + if (sidewidth+deltax > SIDEPANEL_MAXWIDTH) { + deltax = SIDEPANEL_MAXWIDTH - sidewidth; + sidewidth = SIDEPANEL_MAXWIDTH; + } + else if (sidewidth+deltax < 2) { + deltax = 2 - sidewidth; + sidewidth = 2; + } + + if (deltax == 0) return; + sidedrag -= deltax; + + var workarea = $('#workarea'); + var layerpanel = $('#layerpanel'); + workarea.css('right', parseInt(workarea.css('right'))+deltax); + sidepanels.css('width', parseInt(sidepanels.css('width'))+deltax); + layerpanel.css('width', parseInt(layerpanel.css('width'))+deltax); + centerCanvasIfNeeded(); + }); + + // if width is non-zero, then fully close it, otherwise fully open it + // the optional close argument forces the side panel closed + var toggleSidePanel = function(close){ + var w = parseInt($('#sidepanels').css('width')); + var deltax = (w > 2 || close ? 2 : SIDEPANEL_OPENWIDTH) - w; + var workarea = $('#workarea'); + var sidepanels = $('#sidepanels'); + var layerpanel = $('#layerpanel'); + workarea.css('right', parseInt(workarea.css('right'))+deltax); + sidepanels.css('width', parseInt(sidepanels.css('width'))+deltax); + layerpanel.css('width', parseInt(layerpanel.css('width'))+deltax); + centerCanvasIfNeeded(); + }; + + // this function highlights the layer passed in (by fading out the other layers) + // if no layer is passed in, this function restores the other layers + var toggleHighlightLayer = function(layerNameToHighlight) { + var curNames = new Array(svgCanvas.getNumLayers()); + for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); } + + if (layerNameToHighlight) { + for (var i = 0; i < curNames.length; ++i) { + if (curNames[i] != layerNameToHighlight) { + svgCanvas.setLayerOpacity(curNames[i], 0.5); + } + } + } + else { + for (var i = 0; i < curNames.length; ++i) { + svgCanvas.setLayerOpacity(curNames[i], 1.0); + } + } + }; + + var populateLayers = function(){ + var layerlist = $('#layerlist tbody'); + var selLayerNames = $('#selLayerNames'); + layerlist.empty(); + selLayerNames.empty(); + var currentlayer = svgCanvas.getCurrentLayer(); + 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); + // contenteditable=\"true\" + var appendstr = ""; + + if (svgCanvas.getLayerVisibility(name)) { + appendstr += "" + name + ""; + } + else { + appendstr += "" + name + ""; + } + layerlist.append(appendstr); + selLayerNames.append(""); + } + // handle selection of layer + $('#layerlist td.layername') + .click(function(evt){ + $('#layerlist tr.layer').removeClass("layersel"); + var row = $(this.parentNode); + row.addClass("layersel"); + svgCanvas.setCurrentLayer(this.textContent); + evt.preventDefault(); + }) + .mouseover(function(evt){ + $(this).css({"font-style": "italic", "color":"blue"}); + toggleHighlightLayer(this.textContent); + }) + .mouseout(function(evt){ + $(this).css({"font-style": "normal", "color":"black"}); + toggleHighlightLayer(); + }); + $('#layerlist td.layervis').click(function(evt){ + var row = $(this.parentNode).prevAll().length; + var name = $('#layerlist tr.layer:eq(' + row + ') td.layername').text(); + var vis = $(this).hasClass('layerinvis'); + svgCanvas.setLayerVisibility(name, vis); + if (vis) { + $(this).removeClass('layerinvis'); + } + else { + $(this).addClass('layerinvis'); + } + }); + + // if there were too few rows, let's add a few to make it not so lonely + var num = 5 - $('#layerlist tr.layer').size(); + while (num-- > 0) { + // FIXME: there must a better way to do this + layerlist.append("_"); + } + }; + populateLayers(); + + function changeResolution(x,y) { + var zoom = svgCanvas.getResolution().zoom; + setResolution(x * zoom, y * zoom); + } + + var centerCanvasIfNeeded = function() { + // this centers the canvas in the workarea if it's small enough + var wa = {w: parseInt($('#workarea').css('width')), + h: parseInt($('#workarea').css('height'))}; + var ca = {w: parseInt($('#svgcanvas').css('width')), + h: parseInt($('#svgcanvas').css('height'))}; + if (wa.w > ca.w) { + $('#svgcanvas').css({'left': (wa.w-ca.w)/2}); + } + if (wa.h > ca.h) { + $('#svgcanvas').css({'top': (wa.h-ca.h)/2}); + } + }; + + $(window).resize( centerCanvasIfNeeded ); + + function stepFontSize(elem, step) { + var orig_val = elem.value-0; + var sug_val = orig_val + step; + var increasing = sug_val >= orig_val; + if(step === 0) return orig_val; + + if(orig_val >= 24) { + if(increasing) { + return Math.round(orig_val * 1.1); + } else { + return Math.round(orig_val / 1.1); + } + } else if(orig_val <= 1) { + if(increasing) { + return orig_val * 2; + } else { + return orig_val / 2; + } + } else { + return sug_val; + } + } + + function stepZoom(elem, step) { + var orig_val = elem.value-0; + var sug_val = orig_val + step; + if(step === 0) return orig_val; + + if(orig_val >= 100) { + return sug_val; + } else { + if(sug_val >= orig_val) { + return orig_val * 2; + } else { + return orig_val / 2; + } + } + } + + function setResolution(w, h, center) { + w-=0; h-=0; + $('#svgcanvas').css( { 'width': w, 'height': h } ); + $('#canvas_width').val(w); + $('#canvas_height').val(h); + + centerCanvasIfNeeded(); + setTimeout(updateBgImage,10); + + if(center) { + var w_area = $('#workarea'); + var scroll_y = h/2 - w_area.height()/2; + var scroll_x = w/2 - w_area.width()/2; + w_area[0].scrollTop = scroll_y; + w_area[0].scrollLeft = scroll_x; + } + } + + + $('#resolution').change(function(){ + var wh = $('#canvas_width,#canvas_height'); + if(!this.selectedIndex) { + if($('#canvas_width').val() == 'fit') { + wh.removeAttr("disabled").val(100); + } + } else if(this.value == 'content') { + wh.val('fit').attr("disabled","disabled"); + } else { + var dims = this.value.split('x'); + $('#canvas_width').val(dims[0]); + $('#canvas_height').val(dims[1]); + wh.removeAttr("disabled"); + } + }); + + //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 }); + $('#font_size').SpinButton({ step: 1, min: 0.001, stepfunc: stepFontSize, callback: changeFontSize }); + $('#group_opacity').SpinButton({ step: 5, min: 0, max: 100, callback: changeOpacity }); + $('#zoom').SpinButton({ min: 0.001, max: 10000, step: 50, stepfunc: stepZoom, callback: changeZoom }); + + svgCanvas.setCustomHandlers = function(opts) { + if(opts.open) { + $('#tool_open').show(); + svgCanvas.bind("opened", opts.open); + } + if(opts.save) { + svgCanvas.bind("saved", opts.save); + } + } + + // set starting resolution (centers canvas) + setResolution(640,480); + +// var revnums = "svg-editor.js ($Rev: 902 $) "; +// revnums += svgCanvas.getVersion(); +// $('#copyright')[0].setAttribute("title", revnums); + + return svgCanvas; +}; + +// This happens when the page is loaded +$(function() { + put_locale(); + 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){} + +}); diff --git a/editor/svg-editor-svgicons.html b/editor/svg-editor-svgicons.html index 0de8419f..6cff2c67 100644 --- a/editor/svg-editor-svgicons.html +++ b/editor/svg-editor-svgicons.html @@ -11,25 +11,54 @@ + + + - - @@ -299,7 +524,7 @@ script type="text/javascript" src="locale/locale.min.js"> -SVG-edit demo (w/SVG icons) +SVG-edit demo @@ -337,7 +562,6 @@ script type="text/javascript" src="locale/locale.min.js"> @@ -346,7 +570,7 @@ script type="text/javascript" src="locale/locale.min.js">
- +
@@ -362,13 +586,13 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
- | +
- | +
@@ -391,11 +615,11 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
- | - Group +
+
@@ -409,17 +633,17 @@ script type="text/javascript" src="locale/locale.min.js"> - | +
- | - Ungroup +
+
- | +
@@ -433,7 +657,7 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
@@ -447,7 +671,7 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
@@ -457,7 +681,7 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
@@ -469,7 +693,7 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
@@ -481,7 +705,7 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
@@ -501,7 +725,7 @@ script type="text/javascript" src="locale/locale.min.js">
- | +
@@ -552,19 +776,19 @@ script type="text/javascript" src="locale/locale.min.js">
  • 1000%
  • - | +
    - + - + - + - + @@ -659,6 +883,7 @@ script type="text/javascript" src="locale/locale.min.js"> + diff --git a/editor/svg-editor.css b/editor/svg-editor.css index 99925f5b..563ebaf9 100644 --- a/editor/svg-editor.css +++ b/editor/svg-editor.css @@ -47,11 +47,20 @@ body { margin-top: 2px; } +#svg_editor #stroke_bg, +#svg_editor #fill_bg { + height: 16px; + width: 16px; + margin: 2px; +} + #svg_editor #fill_color, #svg_editor #stroke_color { height: 16px; width: 16px; border: 1px solid #808080; cursor: pointer; + margin-top: -19px; + margin-left: 1px; } #svg_editor div#palette { @@ -138,6 +147,7 @@ body { border-bottom: 1px solid #FFFFFF; border-left: 1px solid #808080; border-top: 1px solid #808080; + overflow: auto; } #svg_editor .layer_button { @@ -149,6 +159,8 @@ body { border-right: 1px solid #808080; border-bottom: 1px solid #808080; cursor: pointer; + float: left; + margin-right: 3px; } #svg_editor .layer_buttonpressed { @@ -182,9 +194,6 @@ body { } #svg_editor #layerlist td.layervis { - background-image: url('images/eye.png'); - background-repeat: no-repeat; - background-position: 3px center; width: 22px; cursor:pointer; } @@ -193,6 +202,14 @@ body { cursor:pointer; } +#svg_editor #layerlist td.layervis * { + display: block; +} + +#svg_editor #layerlist td.layerinvis * { + display: none; +} + #svg_editor #layerlist td.layername { cursor: pointer; } @@ -311,6 +328,7 @@ body { position: relative; top: -13px; left: -5px; + margin-top: -3px; margin-bottom: -13px; } @@ -331,8 +349,7 @@ span.zoom_tool { position: relative; } -.dropdown button { - background: transparent 3px 8px url('images/dropdown.gif') no-repeat; +#svg_editor .dropdown button { width: 15px; height: 21px; margin: 6px 0 0 1px; @@ -422,13 +439,37 @@ span.zoom_tool { } #svg_editor .tool_sep { - width: 2px; + width: 1px; + background: #888; + border-left: 1px outset #EEE; + margin: 2px 3px; + padding: 0; height: 24px; - margin: 2px; - margin-right: 0; - padding: 2px; + } +#tool_bold, #tool_italic { + font: bold 2.1em/1.1em serif; + text-align: center; + padding-left: 2px; + position: relative; +} + +#tool_bold span, #tool_italic span { + position: absolute; + width: 100%; + height: 100%; + top: 0; left: 0; + background: #000; + opacity: 0; +} + +#tool_italic { + font-weight: normal; + font-style: italic; +} + + #svg_editor #color_picker { position: absolute; display: none; @@ -449,22 +490,6 @@ span.zoom_tool { width: 30px; } -#svg_editor #tool_square { background: 2px 2px url('images/square.png') no-repeat; } -#svg_editor #tool_rect { background: 2px 2px url('images/rect.png') no-repeat; } -#svg_editor #tool_fhrect { background: 2px 2px url('images/freehand-square.png') no-repeat; } -#svg_editor #tool_circle { background: 2px 2px url('images/circle.png') no-repeat; } -#svg_editor #tool_ellipse { background: 2px 2px url('images/ellipse.png') no-repeat; } -#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-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; } - /* TODO: figure out what more-specific selector causes me to write this atrocity and not simply .tool_flyout_button */ #svg_editor #tools_rect .tool_flyout_button, #svg_editor #tools_ellipse .tool_flyout_button { @@ -539,6 +564,7 @@ span.zoom_tool { background-color: #B0B0B0; opacity: 1.0; text-align: center; + border: 1px outset #777; } /* @@ -548,22 +574,52 @@ span.zoom_tool { bottom: 100px; */ + + #svg_docprops #svg_docprops_container { position: absolute; top: 50px; padding: 10px; background-color: #B0B0B0; + border: 1px outset #777; opacity: 1.0; +/* width: 450px;*/ + font-family: Verdana, Helvetica, sans-serif; + font-size: .8em; } +#tool_docprops_back { + margin-left: 1em; +} + +#svg_docprops_container #svg_docprops_docprops, +#svg_docprops_container #svg_docprops_prefs { + float: left; + width: 200px; + margin: 5px .7em; +} + +#svg_docprops_docprops > legend, #svg_docprops_prefs > legend { + font-weight: bold; + font-size: 1.1em; +} + + #svg_docprops_container fieldset { padding: 5px; margin: 5px; + border: 1px solid #DDD; } #svg_docprops_container label { display: block; - margin-bottom: .2em; + margin: .5em; +} + +#svginfo_bg_note { + font-size: .9em; + font-style: italic; + color: #444; } #canvas_title { @@ -595,15 +651,6 @@ span.zoom_tool { padding: 18px; } -#change_background input { - color: #888; -} - -#change_background input.cur_background { - border: 2px solid blue; - color: #000; -} - #background_img { position: absolute; top: 0; @@ -635,21 +682,25 @@ span.zoom_tool { opacity: 0.6; } -#tool_source_save, #tool_docprops_save { - background: #E8E8E8 url(images/save.png) no-repeat 2px 0; +button#tool_source_save, +button#tool_source_cancel, +button#tool_docprops_save, +button#tool_docprops_cancel { + border:1px solid #dedede; + line-height:130%; + background: #E8E8E8 none; + padding:5px 10px 5px 7px; /* Firefox */ + line-height:17px; /* Safari */ } -#tool_source_cancel, #tool_docprops_cancel { - background: #E8E8E8 url(images/cancel.png) no-repeat 2px 0; +.toolbar_button button .svg_icon { + margin:0 3px -3px 0 !important; + padding:0; + border:none; + width:16px; + height:16px; } -#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; -} /* Slider ----------------------------------*/ @@ -676,4 +727,12 @@ span.zoom_tool { .ui-slider-handle { background: #B0B0B0; border: 1px solid #000; -} \ No newline at end of file +} + +/* Possibly not necessary */ +#svg_editor #tools_rect .tool_flyout_button, +#svg_editor #tools_ellipse .tool_flyout_button { + padding: 2px; + width: 24px; + height: 24px; +} diff --git a/editor/svg-editor.html b/editor/svg-editor.html index d01f8e45..c58a7aca 100644 --- a/editor/svg-editor.html +++ b/editor/svg-editor.html @@ -13,12 +13,12 @@ + -

    Layers

    - New Layer - Delete Layer - Rename Layer - Move Layer up - Move Layer Down +
    +
    +
    +
    +
    fill:fill:
    100%
    100%
    stroke:stroke:
    100 %
    100 %
    @@ -76,7 +76,6 @@ script type="text/javascript" src="locale/locale.min.js"> @@ -84,30 +83,30 @@ script type="text/javascript" src="locale/locale.min.js">
    - Clear - - Save - - Source - Wireframe +
    + +
    +
    +
    +
    - - +
    +
    - | - Copy - Delete - | - Top - Bottom - | +
    +
    +
    +
    +
    +
    +
    @@ -130,11 +129,11 @@ script type="text/javascript" src="locale/locale.min.js">
    - | - Clone - Delete - | - Group +
    +
    +
    +
    +
    @@ -148,17 +147,17 @@ script type="text/javascript" src="locale/locale.min.js"> - | +
    - | - Ungroup +
    +
    - | +
    @@ -172,7 +171,7 @@ script type="text/javascript" src="locale/locale.min.js">
    - | +
    @@ -186,7 +185,7 @@ script type="text/javascript" src="locale/locale.min.js">
    - | +
    @@ -196,7 +195,7 @@ script type="text/javascript" src="locale/locale.min.js">
    - | +
    @@ -208,7 +207,7 @@ script type="text/javascript" src="locale/locale.min.js">
    - | +
    @@ -220,13 +219,13 @@ script type="text/javascript" src="locale/locale.min.js">
    - | +
    - Bold - Italic +
    B
    +
    i
    @@ -249,24 +248,24 @@ script type="text/javascript" src="locale/locale.min.js"> - Clone - Delete +
    +
    - Select
    - Pencil
    - Line
    - Square - - Circle
    - - Path - Text - Image - Zoom +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    @@ -291,19 +290,19 @@ script type="text/javascript" src="locale/locale.min.js">
  • 1000%
  • - | +
    - - - + + + - - - + + + @@ -330,14 +329,14 @@ script type="text/javascript" src="locale/locale.min.js">
    -
    +
    -
    +
    @@ -362,16 +361,16 @@ script type="text/javascript" src="locale/locale.min.js">
    - - +
    -
    - Editor Background -
    - -
    - +
    + Image Properties + +
    Canvas Dimensions @@ -390,9 +389,43 @@ script type="text/javascript" src="locale/locale.min.js"> -
    - + +
    + +
    + Editor Preferences + + + + + +
    + Editor Background +
    + +

    Note: Background will not be saved with image.

    +
    + +
    + diff --git a/editor/svg-editor.js b/editor/svg-editor.js index d0f004aa..3ada6d4a 100644 --- a/editor/svg-editor.js +++ b/editor/svg-editor.js @@ -8,12 +8,72 @@ if(!window.console) { */ function svg_edit_setup() { + var uiStrings = { + 'invalidAttrValGiven':'Invalid value given', + 'noContentToFitTo':'No content to fit to', + 'layer':"Layer", + 'dupeLayerName':"There is already a layer named that!", + 'enterUniqueLayerName':"Please enter a unique layer name", + 'enterNewLayerName':"Please enter the new layer name", + 'layerHasThatName':"Layer already has that name", + 'QmoveElemsToLayer':"Move selected elements to layer '%s'?", + 'QwantToClear':'Do you want to clear the drawing?\nThis will also erase your undo history!', + 'QerrorsRevertToSource':'There were parsing errors in your SVG source.\nRevert back to original SVG source?', + 'QignoreSourceChanges':'Ignore changes made to SVG source?' + }; + 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 svgCanvas = new SvgCanvas(document.getElementById("svgcanvas")); + // Store and retrieve preferences + $.pref = function(key, val) { + curPrefs[key] = val; + key = 'svg-edit-'+key; + var host = location.hostname; + var onweb = host && host.indexOf('.') != -1; + var store = (val != undefined); + var storage = false; + // Some FF versions throw security errors here + try { + if(window.localStorage && onweb) { + storage = localStorage; + } + } catch(e) {} + try { + if(window.globalStorage && onweb) { + storage = globalStorage[host]; + } + } catch(e) {} + + if(storage) { + if(store) storage.setItem(key, val); + else return storage.getItem(key); + } else if(window.widget) { + if(store) widget.setPreferenceForKey(val, key); + else return widget.preferenceForKey(key); + } else { + if(store) { + var d = new Date(); + d.setTime(d.getTime() + 31536000000); + val = encodeURIComponent(val); + document.cookie = key+'='+val+'; expires='+d.toUTCString(); + } else { + var result = document.cookie.match(new RegExp(key + "=([^;]+)")); + return result?decodeURIComponent(result[1]):''; + } + } + } + + var curPrefs = { + lang:'en', + iconsize:'m', + bg_color:'#FFF', + bg_url:'' + }; + var setSelectMode = function() { $('.tool_button_current').removeClass('tool_button_current').addClass('tool_button'); $('#tool_select').addClass('tool_button_current'); @@ -41,7 +101,7 @@ function svg_edit_setup() { var saveHandler = function(window,svg) { window.open("data:image/svg+xml;base64," + Utils.encode64(svg)); }; - + // called when we've selected a different element var selectedChanged = function(window,elems) { // if elems[1] is present, then we have more than one element @@ -66,14 +126,16 @@ function svg_edit_setup() { // Deal with pathedit mode $('#path_node_panel').toggle(is_node); $('#tools_bottom_2,#tools_bottom_3').toggle(!is_node); + var size = $('#tool_select > svg')[0].getAttribute('width'); if(is_node) { // Change select icon $('.tool_button').removeClass('tool_button_current'); - $('#tool_select').attr('src','images/select_node.png').addClass('tool_button_current'); + $('#tool_select').addClass('tool_button_current') + .empty().append($.getSvgIcon('select_node')); } else { - $('#tool_select').attr('src','images/select.png'); - + $('#tool_select').empty().append($.getSvgIcon('select')); } + $.resizeSvgIcons({'#tool_select .svg_icon':size}); updateContextPanel(); }; @@ -194,7 +256,6 @@ function svg_edit_setup() { fillOpacity = "N/A"; } document.getElementById("gradbox_fill").parentNode.firstChild.setAttribute("fill", fillColor); - if (strokeColor == null || strokeColor == "" || strokeColor == "none") { strokeColor = "none"; strokeOpacity = "N/A"; @@ -322,20 +383,10 @@ function svg_edit_setup() { if ( (elem && !is_node) || multiselected) { // update the selected elements' layer - $('#selLayerNames')[0].removeAttribute('disabled'); - var opts = $('#selLayerNames option'); - for (var i = 0; i < opts.length; ++i) { - var opt = opts[i]; - if (currentLayer == opt.textContent) { - opt.setAttribute('selected', 'selected'); - } - else { - opt.removeAttribute('selected'); - } - } + $('#selLayerNames').removeAttr('disabled').val(currentLayer); } else { - $('#selLayerNames')[0].setAttribute('disabled', 'disabled'); + $('#selLayerNames').attr('disabled', 'disabled'); } }; @@ -349,7 +400,7 @@ function svg_edit_setup() { svgCanvas.bind("saved", saveHandler); svgCanvas.bind("zoomed", zoomChanged); - var str = '
    ' + var str = '
    ' $.each(palette, function(i,item){ str += '
    '; }); @@ -369,13 +420,12 @@ function svg_edit_setup() { blk.click(function() { blocks.removeClass(cur_bg); $(this).addClass(cur_bg); - $('#canvas_bg_url').removeClass(cur_bg); }); }); - $('#canvas_bg_url').focus(function() { - blocks.removeClass(cur_bg); - $(this).addClass(cur_bg); - }); + + if($.pref('bg_color')) { + setBackground($.pref('bg_color'), $.pref('bg_url')); + } var pos = $('#tools_rect_show').position(); $('#tools_rect').css({'left': pos.left+4, 'top': pos.top+77}); @@ -435,8 +485,8 @@ function svg_edit_setup() { var promptMoveLayerOnce = false; $('#selLayerNames').change(function(){ var destLayer = this.options[this.selectedIndex].value; - // TODO: localize this prompt - if (destLayer && (promptMoveLayerOnce || confirm('Move selected elements to layer \'' + destLayer + '\'?'))) { + var confirm_str = uiStrings.QmoveElemsToLayer.replace('%s',destLayer); + if (destLayer && (promptMoveLayerOnce || confirm(confirm_str))) { promptMoveLayerOnce = true; svgCanvas.moveSelectedToLayer(destLayer); svgCanvas.clearSelection(); @@ -480,9 +530,7 @@ function svg_edit_setup() { } else valid = true; if(!valid) { - // TODO: localize this - alert('Invalid value given for' + $(this).attr('title').replace('Change','') - + '.'); + alert(uiStrings.invalidAttrValGiven); this.value = selectedElement.getAttribute(attr); return false; } @@ -616,6 +664,14 @@ function svg_edit_setup() { } }, true); + var setIcon = function(holder_sel, id) { + var icon = $.getSvgIcon(id).clone(); + var holder = $(holder_sel); + icon[0].setAttribute('width',holder.width()); + icon[0].setAttribute('height',holder.height()); + holder.empty().append(icon); + } + var clickSelect = function() { if (toolButtonClick('#tool_select')) { svgCanvas.setMode('select'); @@ -640,16 +696,45 @@ function svg_edit_setup() { flyoutspeed = 'normal'; svgCanvas.setMode('square'); } - $('#tools_rect_show').attr('src', 'images/square.png'); + setIcon('#tools_rect_show','square'); }; - + var clickRect = function(){ if (toolButtonClick('#tools_rect_show')) { svgCanvas.setMode('rect'); } - $('#tools_rect_show').attr('src', 'images/rect.png'); + setIcon('#tools_rect_show','rect'); + }; + + var clickFHRect = function(){ + if (toolButtonClick('#tools_rect_show')) { + svgCanvas.setMode('fhrect'); + } + setIcon('#tools_rect_show','fh_rect'); + }; + + var clickCircle = function(){ + if (toolButtonClick('#tools_ellipse_show', flyoutspeed)) { + flyoutspeed = 'normal'; + svgCanvas.setMode('circle'); + } + setIcon('#tools_ellipse_show','circle'); }; + var clickEllipse = function(){ + if (toolButtonClick('#tools_ellipse_show')) { + svgCanvas.setMode('ellipse'); + } + setIcon('#tools_ellipse_show','ellipse'); + }; + + var clickFHEllipse = function(){ + if (toolButtonClick('#tools_ellipse_show')) { + svgCanvas.setMode('fhellipse'); + } + setIcon('#tools_ellipse_show','fh_ellipse'); + }; + var clickImage = function(){ if (toolButtonClick('#tool_image')) { svgCanvas.setMode('image'); @@ -673,35 +758,6 @@ function svg_edit_setup() { } }; - var clickFHRect = function(){ - if (toolButtonClick('#tools_rect_show')) { - svgCanvas.setMode('fhrect'); - } - $('#tools_rect_show').attr('src', 'images/freehand-square.png'); - }; - - var clickCircle = function(){ - if (toolButtonClick('#tools_ellipse_show', flyoutspeed)) { - flyoutspeed = 'normal'; - svgCanvas.setMode('circle'); - } - $('#tools_ellipse_show').attr('src', 'images/circle.png'); - }; - - var clickEllipse = function(){ - if (toolButtonClick('#tools_ellipse_show')) { - svgCanvas.setMode('ellipse'); - } - $('#tools_ellipse_show').attr('src', 'images/ellipse.png'); - }; - - var clickFHEllipse = function(){ - if (toolButtonClick('#tools_ellipse_show')) { - svgCanvas.setMode('fhellipse'); - } - $('#tools_ellipse_show').attr('src', 'images/freehand-circle.png'); - }; - var clickText = function(){ toolButtonClick('#tool_text'); svgCanvas.setMode('text'); @@ -767,8 +823,7 @@ function svg_edit_setup() { } var clickClear = function(){ - // TODO: localize this prompt - if( confirm('Do you want to clear the drawing?\nThis will also erase your undo history!') ) { + if( confirm(uiStrings.QwantToClear) ) { svgCanvas.clear(); updateContextPanel(); } @@ -863,6 +918,8 @@ function svg_edit_setup() { $('#svg_source_textarea').focus(); }; + $('#svg_docprops_container').draggable({cancel:'button,fieldset'}); + var showDocProperties = function(){ if (docprops) return; docprops = true; @@ -904,19 +961,27 @@ function svg_edit_setup() { if (!editingsource) return; if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) { - // TODO: localize this prompt - if( !confirm('There were parsing errors in your SVG source.\nRevert back to original SVG source?') ) { + if( !confirm(uiStrings.QerrorsRevertToSource) ) { return false; } } svgCanvas.clearSelection(); hideSourceEditor(); - populateLayers(); + populateLayers(); + setTitle(svgCanvas.getImageTitle()); }; + var setTitle = function(title) { + var editor_title = $('title:first').text().split(':')[0]; + var new_title = editor_title + (title?': ' + title:''); + $('title:first').text(new_title); + } + var saveDocProperties = function(){ // set title - svgCanvas.setImageTitle($('#canvas_title').val()); + var new_title = $('#canvas_title').val(); + setTitle(new_title); + svgCanvas.setImageTitle(new_title); // update resolution var x = parseInt($('#canvas_width').val()); @@ -925,32 +990,185 @@ function svg_edit_setup() { x ='fit'; } if(!svgCanvas.setResolution(x,y)) { - alert('No content to fit to'); + alert(uiStrings.noContentToFitTo); return false; } // set background - var new_bg, bg_url = $('#canvas_bg_url').val(); - var bg_blk = $('#bg_blocks div.cur_background'); - if(bg_blk.length) { - new_bg = bg_blk.css('background'); - $('#svgcanvas').css('background',new_bg); - $('#background_img').remove(); - } else if(bg_url) { - if(!$('#background_img').length) { - $('
    ') - .prependTo('#svgcanvas'); - } else { - $('#background_img img').attr('src',bg_url); - } - } else { - new_bg = '#FFF'; - $('#svgcanvas').css('background',new_bg); - $('#background_img').remove(); + var color = $('#bg_blocks div.cur_background').css('background') || '#FFF'; + setBackground(color, $('#canvas_bg_url').val()); + + // set language + var lang = $('#lang_select').val(); + if(lang != curPrefs.lang) { + put_locale(svgCanvas, lang); } + // set icon size + setIconSize($('#iconsize').val()); + hideDocProperties(); }; + + function setBackground(color, url) { + if(color == curPrefs.bg_color && url == curPrefs.bg_url) return; + $.pref('bg_color', color); + $.pref('bg_url', url); + $('#svgcanvas').css('background',color); + if(url) { + if(!$('#background_img').length) { + $('
    ') + .prependTo('#svgcanvas'); + } else { + $('#background_img img').attr('src',url); + } + } else { + $('#background_img').remove(); + } + } + + var setIconSize = function(size) { + if(size == curPrefs.size) return; + $.pref('iconsize', size); + $('#iconsize').val(size); + var icon_sizes = { s:16, m:24, l:32, xl:48 }; + var size_num = icon_sizes[size]; + + // Change icon size + $('.tool_button, .push_button, .tool_button_current, .tool_button_disabled, .tool_flyout_button') + .find('> svg').each(function() { + this.setAttribute('width',size_num); + this.setAttribute('height',size_num); + }); + + $.resizeSvgIcons({ + '.flyout_arrow_horiz svg': size_num / 3, + '#logo a > svg': size_num * 1.3 + }); + if(size != 's') { + $.resizeSvgIcons({'#layerbuttons svg': size_num * .6}); + } + + // Note that all rules will be prefixed with '#svg_editor' when parsed + var cssResizeRules = { + ".tool_button,\ + .push_button,\ + .tool_button_current,\ + .tool_button_disabled,\ + #tools_rect .tool_flyout_button,\ + #tools_ellipse .tool_flyout_button": { + 'width': {s: '16px', l: '32px', xl: '48px'}, + 'height': {s: '16px', l: '32px', xl: '48px'}, + 'padding': {s: '1px', l: '2px', xl: '3px'} + }, + ".tool_sep": { + 'height': {s: '16px', l: '32px', xl: '48px'}, + 'margin': {s: '2px 2px', l: '2px 5px', xl: '2px 8px'} + }, + "#tools_top": { + 'left': {s: '27px', l: '50px', xl: '70px'}, + 'height': {s: '50px', l: '88px', xl: '125px'} + }, + "#tools_left": { + 'width': {s: '26px', l: '34px', xl: '42px'}, + 'top': {s: '50px', l: '87px', xl: '125px'} + }, + "div#workarea": { + 'left': {s: '27px', l: '46px', xl: '65px'}, + 'top': {s: '50px', l: '88px', xl: '125px'}, + 'bottom': {s: '52px', l: '68px', xl: '75px'} + }, + "#tools_bottom": { + 'left': {s: '27px', l: '46px', xl: '65px'}, + 'height': {s: '52px', l: '68px', xl: '75px'} + }, + "#tools_top input, #tools_bottom input": { + 'margin-top': {s: '2px', l: '4px', xl: '5px'}, + 'height': {s: 'auto', l: 'auto', xl: 'auto'}, + 'border': {s: '1px solid #555', l: 'auto', xl: 'auto'}, + 'font-size': {s: '.9em', l: '2em', xl: '2.5em'} + }, + "#tools_bottom input": { + 'margin-top': {s: '6px', l: '4px', xl: '5px'}, + }, + "#tools_bottom span, #copyright, #tools_bottom .label": { + 'font-size': {l: '1.5em', xl: '2em'} + }, + "#tools_bottom_2": { + 'width': {l: '295px', xl: '355px'} + }, + "#tools_top > div, #tools_top": { + 'line-height': {s: '17px', l: '34px', xl: '50px'} + }, + ".dropdown button": { + 'height': {s: '18px', l: '34px', xl: '40px'} + }, + "#tools_top label, #tools_bottom label": { + 'font-size': {s: '1em', l: '1.5em', xl: '2em'}, + 'margin-top': {s: '1px', l: '3px', xl: '5px'} + }, + "#tool_bold, #tool_italic": { + 'font-size': {s: '1.5em', l: '3em', xl: '4.5em'} + }, + "#sidepanels": { + 'top': {s: '50px', l: '88px', xl: '125px'}, + 'bottom': {s: '52px', l: '68px', xl: '65px'}, + }, + '#layerbuttons': { + 'width': {l: '130px', xl: '175px'}, + 'height': {l: '24px', xl: '30px'} + }, + '#layerlist': { + 'width': {l: '128px', xl: '150px'} + }, + '.layer_button': { + 'width': {l: '19px', xl: '28px'}, + 'height': {l: '19px', xl: '28px'} + }, + ".flyout_arrow_horiz": { + 'left': {s: '-5px', l: '5px', xl: '14px'}, + 'top': {s: '-13px', l: '-13px', xl: '-20px'} + }, + "input.spin-button": { + 'background-image': {l: "url('images/spinbtn_updn_big.png')", xl: "url('images/spinbtn_updn_big.png')"}, + 'background-position': {l: '100% -5px', xl: '100% -2px'}, + 'padding-right': {l: '24px', xl: '24px' } + }, + "input.spin-button.up": { + 'background-position': {l: '100% -45px', xl: '100% -42px'} + }, + "input.spin-button.down": { + 'background-position': {l: '100% -85px', xl: '100% -82px'} + } + }; + + var rule_elem = $('#tool_size_rules'); + if(!rule_elem.length) { + rule_elem = $('
    fill:
    100%
    fill:
    100%
    stroke:
    100 %
    stroke:
    100 %