Fix an SVG nonce bug

Dunno when this problem with randomized IDs arose.
But it's fixed now.

Also, sync with latest SVG-Edit.
master
Jacques Distler 2010-04-01 23:56:21 -05:00
parent 18b5ea9aa6
commit da0c6a2ea1
17 changed files with 577 additions and 279 deletions

View File

@ -48,7 +48,8 @@ module Sanitizer
svg_attributes = Set.new %w[accent-height accumulate additive alphabetic
arabic-form ascent attributeName attributeType baseProfile bbox begin
by calcMode cap-height class clip-path clip-rule color color-rendering
by calcMode cap-height class clip-path clip-rule color
color-interpolation-filters color-rendering
content cx cy d dx dy descent display dur end fill fill-opacity fill-rule
filterRes filterUnits font-family font-size font-stretch font-style
font-variant font-weight from fx fy g1 g2 glyph-name gradientUnits

View File

@ -86,6 +86,7 @@ function setupSVGedit(path){
editor.addEventListener("load", function() {
editor.svgEditor.setCustomHandlers({
'save': function(window,svg){
editor.svgEditor.setConfig({no_save_warning: true});
window.opener.postMessage(svg, window.location.protocol + '//' + window.location.host);
window.close();
}

View File

@ -1,8 +1,9 @@
NAME=svg-edit
VERSION=2.5
MAKEDOCS=naturaldocs/NaturalDocs
PACKAGE=$(NAME)-$(VERSION)
YUI=build/yuicompressor.jar
MAKEDOCS=naturaldocs/NaturalDocs
CLOSURE=build/tools/closure-compiler.jar
YUICOMPRESS=build/tools/yuicompressor.jar
ZIP=zip
all: release firefox opera
@ -10,18 +11,21 @@ all: release firefox opera
build/$(PACKAGE):
rm -rf config
mkdir config
$(MAKEDOCS) -i editor/ -o html docs/ -p config/ -oft -r
if [ -x $(MAKEDOCS) ] ; then $(MAKEDOCS) -i editor/ -o html docs/ -p config/ -oft -r ; fi
mkdir -p build/$(PACKAGE)
cp -r editor/* build/$(PACKAGE)
-find build/$(PACKAGE) -name .svn -type d -exec rm -rf {} \;
# minify spin button
java -jar $(YUI) build/$(PACKAGE)/spinbtn/JQuerySpinBtn.js > build/$(PACKAGE)/spinbtn/JQuerySpinBtn.min.js
java -jar $(YUICOMPRESS) build/$(PACKAGE)/spinbtn/JQuerySpinBtn.js > build/$(PACKAGE)/spinbtn/JQuerySpinBtn.min.js
java -jar $(CLOSURE) --js build/$(PACKAGE)/spinbtn/JQuerySpinBtn.js --js_output_file build/$(PACKAGE)/spinbtn/JQuerySpinBtn.min-closure.js
# minify SVG-edit files
java -jar $(YUI) build/$(PACKAGE)/svg-editor.js > build/$(PACKAGE)/svg-editor.min.js
java -jar $(YUI) build/$(PACKAGE)/svgcanvas.js > build/$(PACKAGE)/svgcanvas.min.js
java -jar $(YUICOMPRESS) build/$(PACKAGE)/svg-editor.js > build/$(PACKAGE)/svg-editor.min.js
java -jar $(CLOSURE) --js build/$(PACKAGE)/svg-editor.js --js_output_file build/$(PACKAGE)/svg-editor.min-closure.js
java -jar $(YUICOMPRESS) build/$(PACKAGE)/svgcanvas.js > build/$(PACKAGE)/svgcanvas.min.js
java -jar $(CLOSURE) --js build/$(PACKAGE)/svgcanvas.js --js_output_file build/$(PACKAGE)/svgcanvas.min-closure.js
# CSS files do not work remotely
# java -jar $(YUI) build/$(PACKAGE)/spinbtn/JQuerySpinBtn.css > build/$(PACKAGE)/spinbtn/JQuerySpinBtn.min.css
# java -jar $(YUI) build/$(PACKAGE)/svg-editor.css > build/$(PACKAGE)/svg-editor.min.css
# java -jar $(YUICOMPRESS) build/$(PACKAGE)/spinbtn/JQuerySpinBtn.css > build/$(PACKAGE)/spinbtn/JQuerySpinBtn.min.css
# java -jar $(YUICOMPRESS) build/$(PACKAGE)/svg-editor.css > build/$(PACKAGE)/svg-editor.min.css
release: build/$(PACKAGE)
cd build ; $(ZIP) $(PACKAGE).zip -r $(PACKAGE) ; cd ..

Binary file not shown.

View File

@ -0,0 +1,90 @@
/*
* ext-eyedropper.js
*
* Licensed under the Apache License, Version 2
*
* Copyright(c) 2010 Jeff Schiller
*
*/
svgEditor.addExtension("eyedropper", function(S) {
var svgcontent = S.svgcontent,
svgns = "http://www.w3.org/2000/svg",
svgdoc = S.svgroot.parentNode.ownerDocument,
ChangeElementCommand = svgCanvas.getPrivateMethods().ChangeElementCommand,
addToHistory = svgCanvas.getPrivateMethods().addCommandToHistory,
currentStyle = {fillPaint: "red", fillOpacity: 1.0,
strokePaint: "black", strokeOpacity: 1.0,
strokeWidth: 5, strokeDashArray: null,
opacity: 1.0 };
return {
name: "eyedropper",
svgicons: "extensions/eyedropper-icon.xml",
buttons: [{
id: "tool_eyedropper",
type: "mode",
title: "Eye Dropper Tool",
events: {
"click": function() {
svgCanvas.setMode("eyedropper");
}
}
}],
// if we have selected an element, grab its paint and enable the eye dropper button
selectedChanged: function(opts) {
// if we are in eyedropper mode, we don't want to disable the eye-dropper tool
var mode = svgCanvas.getMode();
if (mode == "eyedropper") return;
var elem = null;
var tool = $('#tool_eyedropper');
// enable-eye-dropper if one element is selected
if (opts.elems.length == 1 && opts.elems[0] &&
$.inArray(opts.elems[0].nodeName, ['svg', 'g', 'use']) == -1)
{
elem = opts.elems[0];
tool.removeClass('disabled');
// grab the current style
currentStyle.fillPaint = elem.getAttribute("fill") || "black";
currentStyle.fillOpacity = elem.getAttribute("fill-opacity") || 1.0;
currentStyle.strokePaint = elem.getAttribute("stroke");
currentStyle.strokeOpacity = elem.getAttribute("stroke-opacity") || 1.0;
currentStyle.strokeWidth = elem.getAttribute("stroke-width");
currentStyle.strokeDashArray = elem.getAttribute("stroke-dasharray");
currentStyle.opacity = elem.getAttribute("opacity") || 1.0;
}
// disable eye-dropper tool
else {
tool.addClass('disabled');
}
},
mouseDown: function(opts) {
var mode = svgCanvas.getMode();
if (mode == "eyedropper") {
var e = opts.event;
var target = e.target;
if ($.inArray(target.nodeName, ['svg', 'g', 'use']) == -1) {
var changes = {};
var change = function(elem, attrname, newvalue) {
changes[attrname] = elem.getAttribute(attrname);
elem.setAttribute(attrname, newvalue);
};
if (currentStyle.fillPaint) change(target, "fill", currentStyle.fillPaint);
if (currentStyle.fillOpacity) change(target, "fill-opacity", currentStyle.fillOpacity);
if (currentStyle.strokePaint) change(target, "stroke", currentStyle.strokePaint);
if (currentStyle.strokeOpacity) change(target, "stroke-opacity", currentStyle.strokeOpacity);
if (currentStyle.strokeWidth) change(target, "stroke-width", currentStyle.strokeWidth);
if (currentStyle.strokeDashArray) change(target, "stroke-dasharray", currentStyle.strokeDashArray);
if (currentStyle.opacity) change(target, "opacity", currentStyle.opacity);
addToHistory(new ChangeElementCommand(target, changes));
}
}
},
};
});

View File

@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg">
<g id="tool_eyedropper">
<svg viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
<defs>
<radialGradient id="eyedropper_svg_6" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
<stop offset="1" stop-color="#e5e5e5" stop-opacity="0.38"/>
</radialGradient>
<linearGradient id="eyedropper_svg_15" x1="0" y1="0" x2="0.58594" y2="0.55078">
<stop offset="0" stop-color="#ffffff" stop-opacity="0.57"/>
<stop offset="1" stop-color="#000056" stop-opacity="1"/>
</linearGradient>
<linearGradient id="eyedropper_svg_19" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#ffffff" stop-opacity="1"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0"/>
</linearGradient>
</defs>
<g display="inline">
<title>Layer 1</title>
<path d="m193.899994,73l-119.899979,118l-15,39.5l10.25,4.5l43.750015,-20l108.999969,-112l-28.100006,-30z" id="svg_3" fill="none" stroke="#000000" stroke-width="5"/>
<path d="m58.649994,232c-2.75,28.200012 -26.399994,28.950012 -21.899994,59c4.5,30.049988 55,28 55.5,-1.25c0.5,-29.25 -20.25,-28.75 -22.25,-54.75l-11.350006,-3z" id="svg_4" fill="#aa56ff" stroke="#000000" stroke-width="7"/>
<path d="m45.474976,269.275024l13.775024,0.474976l-0.75,16.75l-14.25,-1.25l1.224976,-15.974976z" id="svg_5" fill="url(#eyedropper_svg_6)" stroke-width="5" fill-opacity="0.73"/>
<path d="m217.899994,46c21.5,-101.549999 141.600006,20.449997 28.100006,33l-5,44l-63,-66l39.899994,-11z" id="svg_2" fill="#000000" stroke-width="5"/>
<path d="m206.825012,61.075008c3.712494,-2.46249 7.637482,-3.53751 14.424988,-5.575008c10.125,-16.5 32.875,-41.5 40.5,-35c7.625,6.5 -21.25,35.625 -37.5,39.25c-5.5,10.125 -8,13.875 -17.25,16.5c-2.837494,-8.162514 -4.262482,-12.337486 -0.174988,-15.174992z" id="svg_7" fill="url(#eyedropper_svg_15)" stroke-width="5"/>
<path d="m133.049988,134.75l46.950012,9.25l-66,70l-42.5,20.5l-11.5,-5l14,-37.5l59.049988,-57.25z" id="svg_11" fill="#aa56ff" stroke="#000000" stroke-width="7"/>
<path d="m71.425034,212.350006l9.050888,-20.022537l51.516724,-49.327469l8.507355,0.97197l-69.074966,68.378036z" id="svg_16" fill="url(#eyedropper_svg_19)" stroke-width="5"/>
</g>
</svg>
</g>
<g id="svg_eof"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

View File

@ -5,11 +5,10 @@
in the extension. The SVG inside the group makes up the actual icon, and
needs use a viewBox instead of width/height for it to scale properly.
Multiple icons can be included, each within their own group. A final element
with ID "svg_eof" must be included too.
Multiple icons can be included, each within their own group.
-->
<g id="hello_world">
<svg viewBox="0 0 102 102" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="102" height="102" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
<g>
<title>Layer 1</title>
@ -19,5 +18,4 @@
</g>
</svg>
</g>
<g id="svg_eof"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,47 +1,32 @@
#About
**jQuery.hotkeys** is a plug-in that lets you easily add and remove handlers for keyboard events anywhere in your code supporting almost any key combination.
**jQuery Hotkeys** is a plug-in that lets you easily add and remove handlers for keyboard events anywhere in your code supporting almost any key combination.
It is based on a library [Shortcut.js](http://www.openjs.com/scripts/events/keyboard_shortcuts/shortcut.js) written by [Binny V A](http://www.openjs.com/).
This plugin is based off of the plugin by Tzury Bar Yochay: [jQuery.hotkeys](http://github.com/tzuryby/hotkeys)
The syntax is as follows:
<pre>
$(expression).bind(<types>,<options>, <handler>);
$(expression).unbind(<types>,<options>, <handler>);
$(document).bind('keydown', 'Ctrl+a', fn);
// e.g. replace '$' sign with 'EUR'
$('input.foo').bind('keyup', '$', function(){
this.value = this.value.replace('$', 'EUR');
});
$('div.foo').unbind('keydown', 'Ctrl+a', fn);
</pre>
## [Live Demo](http://jshotkeys.googlepages.com/test-static-01.html)
$(expression).bind(types, keys, handler);
$(expression).unbind(types, handler);
$(document).bind('keydown', 'ctrl+a', fn);
// e.g. replace '$' sign with 'EUR'
$('input.foo').bind('keyup', '$', function(){
this.value = this.value.replace('$', 'EUR');
});
## Types
Supported types are `'keydown'`, `'keyup'` and `'keypress'`
## Options
The options are `'combi'` i.e. the key combination, and `'disableInInput'` which allow your code not to be executed when the cursor is located inside an input ( `$(elem).is('input') || $(elem).is('textarea')` ).
As you can see, the key combination can be passed as string or as an object. You may pass an object in case you wish to override the default option for `disableInInput` which is set to `false`:
<pre>
$(document).bind('keydown', {combi:'a', disableinInput: true}, fn);
</pre>
I.e. when cursor is within an input field, `'a'` will be inserted into the input field without interfering.
## Notes
If you want to use more than one modifiers (e.g. alt+ctrl+z) you should define them by an alphabetical order e.g. alt+ctrl+shift
Modifiers are case insensitive, i.e. 'Ctrl+a' 'ctrl+a'.
## Handler
In previous versions there was an option propagate which is removed now and implemented at the user code level.
When using jQuery, if an event handler returns false, jQuery will call `stopPropagation()` and `preventDefault()`
Hotkeys aren't tracked if you're inside of an input element (unless you explicitly bind the hotkey directly to the input). This helps to avoid conflict with normal user typing.
## jQuery Compatibility
Tested with *jQuery 1.2.6*
Works with jQuery 1.4.2 and newer.
It known to be working with all the major browsers on all available platforms (Win/Mac/Linux)
@ -51,43 +36,10 @@ It known to be working with all the major browsers on all available platforms (W
* Safari-3
* Chrome-0.2
## Features added in this version (0.7.x)
* Implemented as $.fn - let you use `this`.
* jQuery selectors are supported.
* Extending `$.fn.bind` and `$.fn.unbind` so you get a single interface for binding events to handlers
## Overriding jQuery
The plugin wraps the following jQuery methods:
* $.fn.bind
* $.fn.unbind
* $.find
Even though the plugin overrides these methods, the original methods will *always* be called.
The plugin will add functionality only for the `keydown`, `keyup` and `keypress` event types. Any other types are passed untouched to the original `'bind()'` and `'unbind()'` methods.
Moreover, if you call `bind()` without passing the shortcut key combination e.g. `$(document).bind('keydown', fn)` only the original `'bind()'` method will be executed.
I also modified the `$.fn.find` method by adding a single line at the top of the function body. here is the code:
<pre>
jQuery.fn.find = function( selector ) {
// the line I added
this.query=selector;
// call jQuery original find
return jQuery.fn.__find__.apply(this, arguments);
};
</pre>
You can read about this at [jQuery's User Group](http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d)
###Notes
### Addendum
Firefox is the most liberal one in the manner of letting you capture all short-cuts even those that are built-in in the browser such as `Ctrl-t` for new tab, or `Ctrl-a` for selecting all text. You can always bubble them up to the browser by returning `true` in your handler.
Others, (IE) either let you handle built-in short-cuts, but will add their functionality after your code has executed. Or (Opera/Safari) will *not* pass those events to the DOM at all.
*So, if you bind `Ctrl-Q` or `Alt-F4` and your Safari/Opera window is closed don't be surprised.*
###Current Version is: beta 0.7
*So, if you bind `Ctrl-Q` or `Alt-F4` and your Safari/Opera window is closed don't be surprised.*

View File

@ -1,19 +1,15 @@
(function(jQuery){jQuery.fn.__bind__=jQuery.fn.bind;jQuery.fn.__unbind__=jQuery.fn.unbind;jQuery.fn.__find__=jQuery.fn.find;var hotkeys={version:'0.7.9',override:/keypress|keydown|keyup/g,triggersMap:{},specialKeys:{27:'esc',9:'tab',32:'space',13:'return',8:'backspace',145:'scroll',20:'capslock',144:'numlock',19:'pause',45:'insert',36:'home',46:'del',35:'end',33:'pageup',34:'pagedown',37:'left',38:'up',39:'right',40:'down',109:'-',112:'f1',113:'f2',114:'f3',115:'f4',116:'f5',117:'f6',118:'f7',119:'f8',120:'f9',121:'f10',122:'f11',123:'f12',191:'/'},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":":","'":"\"",",":"<",".":">","/":"?","\\":"|"},newTrigger:function(type,combi,callback){var result={};result[type]={};result[type][combi]={cb:callback,disableInInput:false};return result;}};hotkeys.specialKeys=jQuery.extend(hotkeys.specialKeys,{96:'0',97:'1',98:'2',99:'3',100:'4',101:'5',102:'6',103:'7',104:'8',105:'9',106:'*',107:'+',109:'-',110:'.',111:'/'});jQuery.fn.find=function(selector){this.query=selector;return jQuery.fn.__find__.apply(this,arguments);};jQuery.fn.unbind=function(type,combi,fn){if(jQuery.isFunction(combi)){fn=combi;combi=null;}
if(combi&&typeof combi==='string'){var selectorId=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();var hkTypes=type.split(' ');for(var x=0;x<hkTypes.length;x++){delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi];}}
return this.__unbind__(type,fn);};jQuery.fn.bind=function(type,data,fn){var handle=type.match(hotkeys.override);if(jQuery.isFunction(data)||!handle){return this.__bind__(type,data,fn);}
else{var result=null,pass2jq=jQuery.trim(type.replace(hotkeys.override,''));if(pass2jq){result=this.__bind__(pass2jq,data,fn);}
if(typeof data==="string"){data={'combi':data};}
if(data.combi){for(var x=0;x<handle.length;x++){var eventType=handle[x];var combi=data.combi.toLowerCase(),trigger=hotkeys.newTrigger(eventType,combi,fn),selectorId=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();trigger[eventType][combi].disableInInput=data.disableInInput;if(!hotkeys.triggersMap[selectorId]){hotkeys.triggersMap[selectorId]=trigger;}
else if(!hotkeys.triggersMap[selectorId][eventType]){hotkeys.triggersMap[selectorId][eventType]=trigger[eventType];}
var mapPoint=hotkeys.triggersMap[selectorId][eventType][combi];if(!mapPoint){hotkeys.triggersMap[selectorId][eventType][combi]=[trigger[eventType][combi]];}
else if(mapPoint.constructor!==Array){hotkeys.triggersMap[selectorId][eventType][combi]=[mapPoint];}
else{hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length]=trigger[eventType][combi];}
this.each(function(){var jqElem=jQuery(this);if(jqElem.attr('hkId')&&jqElem.attr('hkId')!==selectorId){selectorId=jqElem.attr('hkId')+";"+selectorId;}
jqElem.attr('hkId',selectorId);});result=this.__bind__(handle.join(' '),data,hotkeys.handler)}}
return result;}};hotkeys.findElement=function(elem){if(!jQuery(elem).attr('hkId')){if(jQuery.browser.opera||jQuery.browser.safari){while(!jQuery(elem).attr('hkId')&&elem.parentNode){elem=elem.parentNode;}}}
return elem;};hotkeys.handler=function(event){var target=hotkeys.findElement(event.currentTarget),jTarget=jQuery(target),ids=jTarget.attr('hkId');if(ids){ids=ids.split(';');var code=event.which,type=event.type,special=hotkeys.specialKeys[code],character=!special&&String.fromCharCode(code).toLowerCase(),shift=event.shiftKey,ctrl=event.ctrlKey,alt=event.altKey||event.originalEvent.altKey,mapPoint=null;for(var x=0;x<ids.length;x++){if(hotkeys.triggersMap[ids[x]][type]){mapPoint=hotkeys.triggersMap[ids[x]][type];break;}}
if(mapPoint){var trigger;if(!shift&&!ctrl&&!alt){trigger=mapPoint[special]||(character&&mapPoint[character]);}
else{var modif='';if(alt)modif+='alt+';if(ctrl)modif+='ctrl+';if(shift)modif+='shift+';trigger=mapPoint[modif+special];if(!trigger){if(character){trigger=mapPoint[modif+character]||mapPoint[modif+hotkeys.shiftNums[character]]||(modif==='shift+'&&mapPoint[hotkeys.shiftNums[character]]);}}}
if(trigger){var result=false;for(var x=0;x<trigger.length;x++){if(trigger[x].disableInInput){var elem=jQuery(event.target);if(jTarget.is("input")||jTarget.is("textarea")||jTarget.is("select")||elem.is("input")||elem.is("textarea")||elem.is("select")){return true;}}
result=result||trigger[x].cb.apply(this,[event]);}
return result;}}}};window.hotkeys=hotkeys;return jQuery;})(jQuery);
/*
* jQuery Hotkeys Plugin
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* http://github.com/jeresig/jquery.hotkeys
*
* Based upon the plugin by Tzury Bar Yochay:
* http://github.com/tzuryby/hotkeys
*
* Original idea by:
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
*/
(function(b){b.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",191:"/",224:"meta"},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"}};function a(d){if(typeof d.data!=="string"){return}var c=d.handler,e=d.data.toLowerCase().split(" ");d.handler=function(n){if(this!==n.target&&(/textarea|select/i.test(n.target.nodeName)||n.target.type==="text")){return}var h=n.type!=="keypress"&&b.hotkeys.specialKeys[n.which],o=String.fromCharCode(n.which).toLowerCase(),k,m="",g={};if(n.altKey&&h!=="alt"){m+="alt+"}if(n.ctrlKey&&h!=="ctrl"){m+="ctrl+"}if(n.metaKey&&!n.ctrlKey&&h!=="meta"){m+="meta+"}if(n.shiftKey&&h!=="shift"){m+="shift+"}if(h){g[m+h]=true}else{g[m+o]=true;g[m+b.hotkeys.shiftNums[o]]=true;if(m==="shift+"){g[b.hotkeys.shiftNums[o]]=true}}for(var j=0,f=e.length;j<f;j++){if(g[e[j]]){return c.apply(this,arguments)}}}}b.each(["keydown","keyup","keypress"],function(){b.event.special[this]={add:a}})})(jQuery);

View File

@ -383,6 +383,7 @@ body {
top: 75px;
left: 0;
padding-left: 2px;
background: #D8D8D8; /* Needed so flyout icons don't appear on the left */
z-index: 4;
}
@ -466,17 +467,13 @@ body {
}
.magic_field > * {
float: left;
}
span.zoom_tool {
line-height: 26px;
padding: 3px;
}
.magic_field input {
margin-top: 5px;
#zoom_panel label {
margin-top: 2px;
}
.dropdown {
@ -701,6 +698,21 @@ span.zoom_tool {
height: 26px;
}
#toggle_stroke_tools {
letter-spacing: -.2em;
padding-right: 8px;
}
#toggle_stroke_tools:hover {
cursor: pointer;
background: #FFC;
}
.stroke_tool {
display: none;
white-space: nowrap;
}
.color_tool > *:first-child {
-moz-border-radius-topleft: 4px;
-moz-border-radius-bottomleft: 4px;
@ -739,6 +751,8 @@ span.zoom_tool {
#svg_editor #tools_bottom_3 {
// position: relative;
z-index: 2;
}
#svg_editor #copyright {
@ -1023,4 +1037,4 @@ span.zoom_tool {
-webkit-border-radius: 0px;
}
foreignObject {line-height:1.0}
foreignObject {line-height:1.0}

View File

@ -19,6 +19,8 @@
<script type="text/javascript" src="svgcanvas.js"></script>
<script type="text/javascript" src="svg-editor.js"></script>
<script type="text/javascript" src="locale/locale.js"></script>
<!-- you can load extensions here -->
<!-- <script type="text/javascript" src="extensions/ext-helloworld.js"></script> -->
<!-- Release version of script tags: >
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
@ -340,6 +342,7 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
<label id="tool_node_y">y:
<input id="path_node_y" class="attr_changer" title="Change node's y coordinate" size="3" data-attr="y"/>
</label>
<select id="seg_type" title="Change Segment type">
<option id="straight_segments" selected="selected" value="4">Straight</option>
<option id="curve_segments" value="6">Curve</option>
@ -380,9 +383,11 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
<div id="tools_bottom" class="tools_panel">
<!-- Zoom buttons -->
<div id="zoom_panel" class="magic_field">
<span id="zoomLabel" class="zoom_tool label">zoom:</span>
<div id="zoom_panel" class="toolset">
<label>
<span id="zoomLabel" class="zoom_tool">zoom:</span>
<input id="zoom" title="Change zoom level" size="3" value="100" type="text" />
</label>
<div id="zoom_dropdown" class="dropdown">
<button></button>
<ul>
@ -427,7 +432,7 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
<input id="stroke_width" title="Change stroke width by 1, shift-click to change by 0.1" size="2" value="5" type="text" data-attr="Stroke Width"/>
</label>
<label>
<label class="stroke_tool">
<select id="stroke_style" title="Change stroke dash style">
<option selected="selected" value="none">&mdash;</option>
<option value="2,2">...</option>
@ -435,7 +440,34 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
<option value="5,2,2,2">- .</option>
<option value="5,2,2,2,2,2">- ..</option>
</select>
</label>
<!-- TODO: Turn these into icon lists, rather than text ones -->
<label class="stroke_tool">
<span>Joins:</span>
<select id="stroke_linejoin" title="Change Linejoin type">
<option id="linejoin_miter" selected="selected" value="miter">Miter</option>
<option id="linejoin_round" value="round">Round</option>
<option id="linejoin_bevel" value="bevel">Bevel</option>
</select>
</label>
<label class="stroke_tool">
<span>Caps:</span>
<select id="stroke_linecap" title="Change Linecap type">
<option id="linecap_butt" selected="selected" value="butt">Butt</option>
<option id="linecap_round" value="round">Round</option>
<option id="linecap_square" value="square">Square</option>
</select>
</label>
<div id="toggle_stroke_tools" title="Show/hide more stroke tools">
&gt;&gt;
</div>
</div>
</div>

View File

@ -45,7 +45,7 @@
imgPath: 'images/',
langPath: 'locale/',
extPath: 'extensions/',
extensions: ['ext-arrows.js', 'ext-connector.js', 'ext-itex.js'],
extensions: ['ext-arrows.js', 'ext-connector.js', 'ext-eyedropper.js', 'ext-itex.js'],
initTool: 'select',
wireframe: false
},
@ -59,6 +59,7 @@
'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!',
'QwantToOpen':'Do you want to open a new file?\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?',
'featNotSupported':'Feature not supported',
@ -143,6 +144,7 @@
svgCanvas.open = opts.open;
}
if(opts.save) {
show_save_warning = false;
svgCanvas.bind("saved", opts.save);
}
}
@ -190,10 +192,19 @@
}
})();
var extFunc = function() {
$.each(curConfig.extensions, function() {
$.getScript(curConfig.extPath + this);
});
}
// Load extensions
$.each(curConfig.extensions, function() {
$.getScript(curConfig.extPath + this);
});
// Bit of a hack to run extensions in local Opera
if(window.opera && document.location.protocol === 'file:') {
setTimeout(extFunc, 1000);
} else {
extFunc();
}
$.svgIcons(curConfig.imgPath + 'svg_edit_icons.svg', {
w:24, h:24,
@ -361,7 +372,8 @@
modKey = "", //(isMac ? "meta+" : "ctrl+");
path = svgCanvas.pathActions,
default_img_url = curConfig.imgPath + "logo.png",
workarea = $("#workarea");
workarea = $("#workarea"),
show_save_warning = false;
// This sets up alternative dialog boxes. They mostly work the same way as
// their UI counterparts, expect instead of returning the result, a callback
@ -388,7 +400,7 @@
if(type == 'prompt') {
var input = $('<input type="text">').prependTo(btn_holder);
input.val(defText || '');
input.bind('keydown', {combi:'return'}, function() {ok.click();});
input.bind('keydown', 'return', function() {ok.click();});
}
box.show();
@ -445,6 +457,8 @@
var strokePaint = new $.jGraduate.Paint({solidColor: curConfig.initStroke.color});
var saveHandler = function(window,svg) {
show_save_warning = false;
// by default, we add the XML prolog back, systems integrating SVG-edit (wikis, CMSs)
// can just provide their own custom save handler and might not want the XML prolog
svg = "<?xml version='1.0'?>\n" + svg;
@ -525,6 +539,8 @@
selectedElement = elem;
}
}
show_save_warning = true;
// we update the contextual panel with potentially new
// positional/sizing information (we DON'T want to update the
@ -607,7 +623,7 @@
$(this).mouseup(func);
if(opts.key) {
$(document).bind('keydown', {combi: opts.key+'', disableInInput:true}, func);
$(document).bind('keydown', opts.key+'', func);
}
});
@ -878,7 +894,7 @@
});
}
if(btn.key) {
$(document).bind('keydown', {combi: btn.key, disableInInput: true}, func);
$(document).bind('keydown', btn.key, func);
if(btn.title) button.attr("title", btn.title + ' ['+btn.key+']');
}
} else {
@ -999,6 +1015,8 @@
$('#stroke_width').val(selectedElement.getAttribute("stroke-width")||1);
$('#stroke_style').val(selectedElement.getAttribute("stroke-dasharray")||"none");
$('#stroke_linejoin').val(selectedElement.getAttribute("stroke-linejoin")||"miter");
$('#stroke_linecap').val(selectedElement.getAttribute("stroke-linecap")||"butt");
}
// All elements including image and group have opacity
@ -1270,9 +1288,20 @@
}
$('#stroke_style').change(function(){
svgCanvas.setStrokeStyle(this.options[this.selectedIndex].value);
svgCanvas.setStrokeAttr('stroke-dasharray', $(this).val());
operaRepaint();
});
$('#stroke_linejoin').change(function(){
svgCanvas.setStrokeAttr('stroke-linejoin', $(this).val());
operaRepaint();
});
$('#stroke_linecap').change(function(){
svgCanvas.setStrokeAttr('stroke-linecap', $(this).val());
operaRepaint();
});
// Lose focus for select elements when changed (Allows keyboard shortcuts to work better)
$('select').change(function(){$(this).blur();});
@ -1383,6 +1412,14 @@
updateToolButtonState();
});
$("#toggle_stroke_tools").toggle(function() {
$(".stroke_tool").css('display','table-cell');
$(this).text('<<');
}, function() {
$(".stroke_tool").css('display','none');
$(this).text('>>');
});
// 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
@ -1401,6 +1438,44 @@
return true;
};
(function() {
var last_x = null, last_y = null, w_area = workarea[0],
panning = false, keypan = false;
$('#svgcanvas').bind('mousemove mouseup', function(evt) {
if(panning === false) return;
w_area.scrollLeft -= (evt.clientX - last_x);
w_area.scrollTop -= (evt.clientY - last_y);
last_x = evt.clientX;
last_y = evt.clientY;
if(evt.type === 'mouseup') panning = false;
return false;
}).mousedown(function(evt) {
if(evt.button === 1 || keypan === true) {
panning = true;
last_x = evt.clientX;
last_y = evt.clientY;
return false;
}
});
$(window).mouseup(function() {
panning = false;
});
$(document).bind('keydown', 'space', function(evt) {
svgCanvas.spaceKey = keypan = true;
evt.preventDefault();
}).bind('keyup', 'space', function(evt) {
evt.preventDefault();
svgCanvas.spaceKey = keypan = false;
});
}());
(function() {
var button = $('#main_icon');
var overlay = $('#main_icon span');
@ -2022,6 +2097,7 @@
".tool_button,\
.push_button,\
.tool_button_current,\
.push_button_pressed,\
.disabled,\
.tools_flyout .tool_button": {
'width': {s: '16px', l: '32px', xl: '48px'},
@ -2047,11 +2123,11 @@
"div#workarea": {
'left': {s: '27px', l: '46px', xl: '65px'},
'top': {s: '50px', l: '88px', xl: '125px'},
'bottom': {s: '55px', l: '70px', xl: '77px'}
'bottom': {s: '55px', l: '98px', xl: '145px'}
},
"#tools_bottom": {
'left': {s: '27px', l: '46px', xl: '65px'},
'height': {s: '58px', l: '70px', xl: '77px'}
'height': {s: '58px', l: '98px', xl: '145px'}
},
"#color_tools": {
'border-spacing': {s: '0 1px'}
@ -2060,7 +2136,8 @@
'height': {s: '20px'}
},
"#tool_opacity": {
'top': {s: '1px'}
'top': {s: '1px'},
'height': {s: 'auto', l:'auto', xl:'auto'}
},
"#tools_top input, #tools_bottom input": {
'margin-top': {s: '2px', l: '4px', xl: '5px'},
@ -2888,7 +2965,7 @@
keyval += '';
$.each(keyval.split('/'), function(i, key) {
$(document).bind('keydown', {combi: key, disableInInput: disInInp}, function(e) {
$(document).bind('keydown', key, function(e) {
fn();
if(pd) {
e.preventDefault();
@ -2917,7 +2994,7 @@
// Misc additional actions
// Make "return" keypress trigger the change event
$('.attr_changer, #image_url').bind('keydown', {combi:'return'},
$('.attr_changer, #image_url').bind('keydown', 'return',
function(evt) {$(this).change();evt.preventDefault();}
);
@ -2994,20 +3071,44 @@
$('#group_opacity').SpinButton({ step: 5, min: 0, max: 100, callback: changeOpacity });
$('#zoom').SpinButton({ min: 0.001, max: 10000, step: 50, stepfunc: stepZoom, callback: changeZoom });
window.onbeforeunload = function() {
// Suppress warning if page is empty
if(svgCanvas.getHistoryPosition() === 0) {
show_save_warning = false;
}
// show_save_warning is set to "false" when the page is saved.
if(!curConfig.no_save_warning && show_save_warning) {
// Browser already asks question about closing the page
return "There are unsaved changes.";
}
};
// use HTML5 File API: http://www.w3.org/TR/FileAPI/
// if browser has HTML5 File API support, then we will show the open menu item
// and provide a file input to click. When that change event fires, it will
// get the text contents of the file and send it to the canvas
if (window.FileReader) {
var inp = $('<input type="file">').change(function() {
var f = this;
var openFile = function(ok) {
if(!ok) return;
svgCanvas.clear();
if(f.files.length==1) {
var reader = new FileReader();
reader.onloadend = function(e) {
svgCanvas.setSvgString(e.target.result);
updateCanvas();
};
reader.readAsText(f.files[0]);
}
}
$('#main_menu').hide();
if(this.files.length==1) {
var reader = new FileReader();
reader.onloadend = function(e) {
svgCanvas.setSvgString(e.target.result);
updateCanvas();
};
reader.readAsText(this.files[0]);
if(svgCanvas.getHistoryPosition() === 0) {
openFile(true);
} else {
$.confirm(uiStrings.QwantToOpen, openFile);
}
});
$("#tool_open").show().prepend(inp);
@ -3092,7 +3193,7 @@
updateCanvas(true);
// });
// var revnums = "svg-editor.js ($Rev: 1470 $) ";
// var revnums = "svg-editor.js ($Rev: 1498 $) ";
// revnums += svgCanvas.getVersion();
// $('#copyright')[0].setAttribute("title", revnums);

View File

@ -97,8 +97,8 @@ var isOpera = !!window.opera,
"defs": [],
"desc": [],
"ellipse": ["class", "clip-path", "clip-rule", "cx", "cy", "fill", "fill-opacity", "fill-rule", "filter", "id", "mask", "opacity", "requiredFeatures", "rx", "ry", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
"feGaussianBlur": ["class", "id", "requiredFeatures", "stdDeviation"],
"filter": ["class", "filterRes", "filterUnits", "height", "id", "primitiveUnits", "requiredFeatures", "width", "x", "xlink:href", "y"],
"feGaussianBlur": ["class", "color-interpolation-filters", "id", "requiredFeatures", "stdDeviation"],
"filter": ["class", "color-interpolation-filters", "filterRes", "filterUnits", "height", "id", "primitiveUnits", "requiredFeatures", "width", "x", "xlink:href", "y"],
"foreignObject": ["class", "font-size", "height", "id", "opacity", "requiredFeatures", "style", "transform", "width", "x", "y"],
"g": ["class", "clip-path", "clip-rule", "id", "display", "fill", "fill-opacity", "fill-rule", "filter", "mask", "opacity", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
"image": ["class", "clip-path", "clip-rule", "filter", "height", "id", "mask", "opacity", "requiredFeatures", "style", "systemLanguage", "transform", "width", "x", "xlink:href", "xlink:title", "y"],
@ -1143,6 +1143,8 @@ function BatchCommand(text) {
'opacity':1,
'stroke':'none',
'stroke-dasharray':'none',
'stroke-linejoin':'miter',
'stroke-linecap':'butt',
'stroke-opacity':1,
'stroke-width':1,
'rx':0,
@ -1171,6 +1173,20 @@ function BatchCommand(text) {
current_layer.appendChild(shape);
}
}
if(data.curStyles) {
assignAttributes(shape, {
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_dasharray,
"stroke-linejoin": cur_shape.stroke_linejoin,
"stroke-linecap": cur_shape.stroke_linecap,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"opacity": cur_shape.opacity / 2,
"style": "pointer-events:inherit"
}, 100);
}
assignAttributes(shape, data.attr, 100);
cleanupElement(shape);
return shape;
@ -1211,7 +1227,9 @@ function BatchCommand(text) {
stroke_paint: null,
stroke_opacity: curConfig.initStroke.opacity,
stroke_width: curConfig.initStroke.width,
stroke_style: 'none',
stroke_dasharray: 'none',
stroke_linejoin: 'miter',
stroke_linecap: 'butt',
opacity: curConfig.initOpacity
}
};
@ -1320,6 +1338,10 @@ function BatchCommand(text) {
undoStack.push(cmd);
undoStackPointer = undoStack.length;
};
this.getHistoryPosition = function() {
return undoStackPointer;
};
// private functions
var getId = function() {
@ -1953,7 +1975,7 @@ function BatchCommand(text) {
break;
case 9: // relative quad (q)
case 8: // absolute quad (Q)
dstr += seg.x + "," + seg.y + " " + seg.x1 + "," + seg.y1 + " ";
dstr += seg.x1 + "," + seg.y1 + " " + seg.x + "," + seg.y + " ";
break;
case 11: // relative elliptical arc (a)
case 10: // absolute elliptical arc (A)
@ -1962,7 +1984,7 @@ function BatchCommand(text) {
break;
case 17: // relative smooth cubic (s)
case 16: // absolute smooth cubic (S)
dstr += seg.x + "," + seg.y + " " + seg.x2 + "," + seg.y2 + " ";
dstr += seg.x2 + "," + seg.y2 + " " + seg.x + "," + seg.y + " ";
break;
}
}
@ -2130,12 +2152,14 @@ function BatchCommand(text) {
var angle = canvas.getRotationAngle(child);
var old_start_transform = start_transform;
var childxforms = [];
start_transform = child.getAttribute("transform");
if(angle || hasMatrixTransform(childTlist)) {
var e2t = svgroot.createSVGTransform();
e2t.setMatrix(matrixMultiply(tm, sm, tmn, m));
childTlist.clear();
childTlist.appendItem(e2t,0);
childTlist.appendItem(e2t);
childxforms.push(e2t);
}
// if not rotated or skewed, push the [T][S][-T] down to the child
else {
@ -2144,6 +2168,7 @@ function BatchCommand(text) {
// slide the [T][S][-T] from the front to the back
// [T][S][-T][M] = [M][T2][S2][-T2]
// (only bringing [-T] to the right of [M])
// [T][S][-T][M] = [T][S][M][-T2]
// [-T2] = [M_inv][-T][M]
var t2n = matrixMultiply(m.inverse(), tmn, m);
@ -2165,8 +2190,28 @@ function BatchCommand(text) {
childTlist.appendItem(translateBack);
childTlist.appendItem(scale);
childTlist.appendItem(translateOrigin);
childxforms.push(translateBack);
childxforms.push(scale);
childxforms.push(translateOrigin);
logMatrix(translateBack.matrix);
logMatrix(scale.matrix);
} // not rotated
batchCmd.addSubCommand( recalculateDimensions(child) );
// TODO: If any <use> have this group as a parent and are
// referencing this child, then we need to impose a reverse
// scale on it so that when it won't get double-translated
// var uses = selected.getElementsByTagNameNS(svgns, "use");
// var href = "#"+child.id;
// var u = uses.length;
// while (u--) {
// var useElem = uses.item(u);
// if(href == useElem.getAttributeNS(xlinkns, "href")) {
// var usexlate = svgroot.createSVGTransform();
// usexlate.setTranslate(-tx,-ty);
// canvas.getTransformList(useElem).insertItemBefore(usexlate,0);
// batchCmd.addSubCommand( recalculateDimensions(useElem) );
// }
// }
start_transform = old_start_transform;
} // element
} // for each child
@ -2216,6 +2261,21 @@ function BatchCommand(text) {
newxlate.setTranslate(tx,ty);
childTlist.insertItemBefore(newxlate, 0);
batchCmd.addSubCommand( recalculateDimensions(child) );
// If any <use> have this group as a parent and are
// referencing this child, then impose a reverse translate on it
// so that when it won't get double-translated
var uses = selected.getElementsByTagNameNS(svgns, "use");
var href = "#"+child.id;
var u = uses.length;
while (u--) {
var useElem = uses.item(u);
if(href == useElem.getAttributeNS(xlinkns, "href")) {
var usexlate = svgroot.createSVGTransform();
usexlate.setTranslate(-tx,-ty);
canvas.getTransformList(useElem).insertItemBefore(usexlate,0);
batchCmd.addSubCommand( recalculateDimensions(useElem) );
}
}
start_transform = old_start_transform;
}
}
@ -2810,6 +2870,7 @@ function BatchCommand(text) {
// and do nothing else
var mouseDown = function(evt)
{
if(evt.button === 1 || canvas.spaceKey) return;
root_sctm = svgcontent.getScreenCTM().inverse();
var pt = transformPoint( evt.pageX, evt.pageY, root_sctm ),
mouse_x = pt.x * current_zoom,
@ -2964,17 +3025,13 @@ function BatchCommand(text) {
var stroke_w = cur_shape.stroke_width == 0?1:cur_shape.stroke_width;
addSvgElementFromJson({
"element": "polyline",
"curStyles": true,
"attr": {
"points": d_attr,
"id": getNextId(),
"fill": "none",
"stroke": cur_shape.stroke,
"stroke-width": stroke_w,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"opacity": cur_shape.opacity / 2,
"stroke-linecap": "round",
"style": "pointer-events:none"
}
});
@ -3011,20 +3068,14 @@ function BatchCommand(text) {
start_y = y;
addSvgElementFromJson({
"element": "rect",
"curStyles": true,
"attr": {
"x": x,
"y": y,
"width": 0,
"height": 0,
"id": getNextId(),
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"opacity": cur_shape.opacity / 2,
"style": "pointer-events:inherit"
"opacity": cur_shape.opacity / 2
}
});
break;
@ -3033,6 +3084,7 @@ function BatchCommand(text) {
var stroke_w = cur_shape.stroke_width == 0?1:cur_shape.stroke_width;
addSvgElementFromJson({
"element": "line",
"curStyles": true,
"attr": {
"x1": x,
"y1": y,
@ -3041,7 +3093,9 @@ function BatchCommand(text) {
"id": getNextId(),
"stroke": cur_shape.stroke,
"stroke-width": stroke_w,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-dasharray": cur_shape.stroke_dasharray,
"stroke-linejoin": cur_shape.stroke_linejoin,
"stroke-linecap": cur_shape.stroke_linecap,
"stroke-opacity": cur_shape.stroke_opacity,
"fill": "none",
"opacity": cur_shape.opacity / 2,
@ -3053,19 +3107,13 @@ function BatchCommand(text) {
started = true;
addSvgElementFromJson({
"element": "circle",
"curStyles": true,
"attr": {
"cx": x,
"cy": y,
"r": 0,
"id": getNextId(),
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"opacity": cur_shape.opacity / 2,
"style": "pointer-events:inherit"
"opacity": cur_shape.opacity / 2
}
});
break;
@ -3073,20 +3121,14 @@ function BatchCommand(text) {
started = true;
addSvgElementFromJson({
"element": "ellipse",
"curStyles": true,
"attr": {
"cx": x,
"cy": y,
"rx": 0,
"ry": 0,
"id": getNextId(),
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"opacity": cur_shape.opacity / 2,
"style": "pointer-events:inherit"
"opacity": cur_shape.opacity / 2
}
});
break;
@ -3094,22 +3136,16 @@ function BatchCommand(text) {
started = true;
var newText = addSvgElementFromJson({
"element": "text",
"curStyles": true,
"attr": {
"x": x,
"y": y,
"id": getNextId(),
"fill": cur_text.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_text.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
// fix for bug where text elements were always 50% opacity
"opacity": cur_shape.opacity,
"font-size": cur_text.font_size,
"font-family": cur_text.font_family,
"text-anchor": "middle",
"style": "pointer-events:inherit",
"xml:space": "preserve"
}
});
@ -3155,6 +3191,7 @@ function BatchCommand(text) {
var mouseMove = function(evt)
{
if (!started) return;
if(evt.button === 1 || canvas.spaceKey) return;
var selected = selectedElements[0],
pt = transformPoint( evt.pageX, evt.pageY, root_sctm ),
mouse_x = pt.x * current_zoom,
@ -3452,6 +3489,7 @@ function BatchCommand(text) {
var mouseUp = function(evt)
{
if(evt.button === 1) return;
var tempJustSelected = justSelected;
justSelected = null;
if (!started) return;
@ -3486,7 +3524,9 @@ function BatchCommand(text) {
cur_properties.stroke = selected.getAttribute("stroke");
cur_properties.stroke_opacity = selected.getAttribute("stroke-opacity");
cur_properties.stroke_width = selected.getAttribute("stroke-width");
cur_properties.stroke_style = selected.getAttribute("stroke-dasharray");
cur_properties.stroke_dasharray = selected.getAttribute("stroke-dasharray");
cur_properties.stroke_linejoin = selected.getAttribute("stroke-linejoin");
cur_properties.stroke_linecap = selected.getAttribute("stroke-linecap");
}
if (selected.tagName == "text") {
cur_text.font_size = selected.getAttribute("font-size");
@ -3577,20 +3617,13 @@ function BatchCommand(text) {
(freehand.maxy - freehand.miny) > 0) {
element = addSvgElementFromJson({
"element": "ellipse",
"curStyles": true,
"attr": {
"cx": (freehand.minx + freehand.maxx) / 2,
"cy": (freehand.miny + freehand.maxy) / 2,
"rx": (freehand.maxx - freehand.minx) / 2,
"ry": (freehand.maxy - freehand.miny) / 2,
"id": getId(),
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"opacity": cur_shape.opacity,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"style": "pointer-events:inherit"
"id": getId()
}
});
call("changed",[element]);
@ -3602,20 +3635,13 @@ function BatchCommand(text) {
(freehand.maxy - freehand.miny) > 0) {
element = addSvgElementFromJson({
"element": "rect",
"curStyles": true,
"attr": {
"x": freehand.minx,
"y": freehand.miny,
"width": (freehand.maxx - freehand.minx),
"height": (freehand.maxy - freehand.miny),
"id": getId(),
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"opacity": cur_shape.opacity,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"style": "pointer-events:inherit"
"id": getId()
}
});
call("changed",[element]);
@ -4589,19 +4615,13 @@ function BatchCommand(text) {
// create new path element
element = addSvgElementFromJson({
"element": "path",
"attr": {
"id": getId(),
"d": d,
"fill": "none",
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"opacity": cur_shape.opacity,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"style": "pointer-events:inherit"
}
});
"curStyles": true,
"attr": {
"id": getId(),
"d": d,
"fill": "none"
}
});
call("changed",[element]);
}
return element;
@ -4856,17 +4876,12 @@ function BatchCommand(text) {
d_attr = "M" + x + "," + y + " ";
addSvgElementFromJson({
"element": "path",
"curStyles": true,
"attr": {
"d": d_attr,
"id": getNextId(),
"fill": cur_shape.fill,
"fill-opacity": cur_shape.fill_opacity,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-opacity": cur_shape.stroke_opacity,
"opacity": cur_shape.opacity / 2,
"style": "pointer-events:inherit"
}
});
// set stretchy line to first point
@ -5582,7 +5597,9 @@ function BatchCommand(text) {
"fill-opacity": cur_shape.fill_opacity,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_style,
"stroke-dasharray": cur_shape.stroke_dasharray,
"stroke-linejoin": cur_shape.stroke_linejoin,
"stroke-linecap": cur_shape.stroke_linecap,
"stroke-opacity": cur_shape.stroke_opacity,
"opacity": cur_shape.opacity,
"visibility":"hidden"
@ -5819,6 +5836,10 @@ function BatchCommand(text) {
if (extensions["Arrows"]) call("unsetarrownonce") ;
} else {
randomize_ids = true;
if (!svgcontent.getAttributeNS(se_ns, 'nonce')) {
svgcontent.setAttributeNS(se_ns, 'se:nonce', nonce);
if (extensions["Arrows"]) call("setarrownonce", nonce) ;
}
}
}
@ -5876,6 +5897,53 @@ function BatchCommand(text) {
canvas.embedImage(val);
});
// convert gradients with userSpaceOnUse to objectBoundingBox
$(svgcontent).find('linearGradient, radialGradient').each(function() {
var grad = this;
if($(grad).attr('gradientUnits') === 'userSpaceOnUse') {
// TODO: Support more than one element with this ref by duplicating parent grad
var elems = $(svgcontent).find('[fill=url(#' + grad.id + ')],[stroke=url(#' + grad.id + ')]');
if(!elems.length) return;
// get object's bounding box
var bb = elems[0].getBBox();
if(grad.tagName === 'linearGradient') {
var g_coords = $(grad).attr(['x1', 'y1', 'x2', 'y2']);
$(grad).attr({
x1: (g_coords.x1 - bb.x) / bb.width,
y1: (g_coords.y1 - bb.y) / bb.height,
x2: (g_coords.x2 - bb.x) / bb.width,
y2: (g_coords.y1 - bb.y) / bb.height
});
grad.removeAttribute('gradientUnits');
} else {
// Note: radialGradient elements cannot be easily converted
// because userSpaceOnUse will keep circular gradients, while
// objectBoundingBox will x/y scale the gradient according to
// its bbox.
// For now we'll do nothing, though we should probably have
// the gradient be updated as the element is moved, as
// inkscape/illustrator do.
// var g_coords = $(grad).attr(['cx', 'cy', 'r']);
//
// $(grad).attr({
// cx: (g_coords.cx - bb.x) / bb.width,
// cy: (g_coords.cy - bb.y) / bb.height,
// r: g_coords.r
// });
//
// grad.removeAttribute('gradientUnits');
}
}
});
// Fix XML for Opera/Win/Non-EN
if(!support.goodDecimals) {
canvas.fixOperaXML(svgcontent, newDoc.documentElement);
@ -6985,12 +7053,8 @@ function BatchCommand(text) {
}
};
this.getStrokeStyle = function() {
return cur_shape.stroke_style;
};
this.setStrokeStyle = function(val) {
cur_shape.stroke_style = val;
this.setStrokeAttr = function(attr, val) {
cur_shape[attr.replace('-','_')] = val;
var elems = [];
var i = selectedElements.length;
while (i--) {
@ -7003,10 +7067,10 @@ function BatchCommand(text) {
}
}
if (elems.length > 0) {
this.changeSelectedAttribute("stroke-dasharray", val, elems);
this.changeSelectedAttribute(attr, val, elems);
}
};
this.getOpacity = function() {
return cur_shape.opacity;
};
@ -8239,7 +8303,7 @@ function BatchCommand(text) {
// Function: getVersion
// Returns a string which describes the revision number of SvgCanvas.
this.getVersion = function() {
return "svgcanvas.js ($Rev: 1470 $)";
return "svgcanvas.js ($Rev: 1498 $)";
};
this.setUiStrings = function(strs) {

View File

@ -293,6 +293,17 @@ $(function() {
var svgroot = svgdoc.createElementNS(svgns, "svg");
svgroot.setAttributeNS(svgns, 'viewBox', [0,0,icon_w,icon_h].join(' '));
// Make flexible by converting width/height to viewBox
var w = svg.getAttribute('width');
var h = svg.getAttribute('height');
svg.removeAttribute('width');
svg.removeAttribute('height');
var vb = svg.getAttribute('viewBox');
if(!vb) {
svg.setAttribute('viewBox', [0,0,w,h].join(' '));
}
$(svgroot).attr({
"xmlns": svgns,
"width": icon_w,