@@ -685,8 +688,10 @@ script type="text/javascript" src="locale/locale.min.js">
Paste
Paste in Place
Delete
-
Bring Forward
-
Send Backward
+
GroupG
+
UngroupG
+
Bring ForwardCTRL+]
+
Send BackwardCTRL+[
diff --git a/public/svg-edit/editor/svg-editor.js b/public/svg-edit/editor/svg-editor.js
index 15b2b060..67dce636 100644
--- a/public/svg-edit/editor/svg-editor.js
+++ b/public/svg-edit/editor/svg-editor.js
@@ -11,8 +11,6 @@
*/
(function() {
- // TODO: Find out what causes bugs in jQuery animate for IE9
- if($.browser.msie) $.fx.off = true;
if(!window.svgEditor) window.svgEditor = function($) {
var svgCanvas;
@@ -53,7 +51,7 @@
wireframe: false,
colorPickerCSS: null,
gridSnapping: false,
- snappingStep: 5
+ snappingStep: 10
},
uiStrings = Editor.uiStrings = {
"invalidAttrValGiven":"Invalid value given",
@@ -175,6 +173,20 @@
}
Editor.init = function() {
+ // For external openers
+ (function() {
+ // let the opener know SVG Edit is ready
+ var w = window.opener;
+ if (w) {
+ try {
+ var svgEditorReadyEvent = w.document.createEvent("Event");
+ svgEditorReadyEvent.initEvent("svgEditorReady", true, true);
+ w.document.documentElement.dispatchEvent(svgEditorReadyEvent);
+ }
+ catch(e) {}
+ }
+ })();
+
(function() {
// Load config/data from URL if given
var urldata = $.deparam.querystring(true);
@@ -229,9 +241,9 @@
}
// Load extensions
- // Bit of a hack to run extensions in local Opera
- if(window.opera && document.location.protocol === 'file:') {
- setTimeout(extFunc, 1000);
+ // Bit of a hack to run extensions in local Opera/IE9
+ if(document.location.protocol === 'file:') {
+ setTimeout(extFunc, 100);
} else {
extFunc();
}
@@ -359,6 +371,7 @@
'#layer_up':'go_up',
'#layer_down':'go_down',
+ '#layer_moreopts':'context_menu',
'#layerlist td.layervis':'eye',
'#tool_source_save,#tool_docprops_save':'ok',
@@ -447,9 +460,11 @@
default_img_url = curConfig.imgPath + "logo.png",
workarea = $("#workarea"),
canv_menu = $("#cmenu_canvas"),
+ layer_menu = $("#cmenu_layers"),
show_save_warning = false,
exportWindow = null,
- tool_scale = 1;
+ tool_scale = 1,
+ ui_context = 'toolbars';
// This sets up alternative dialog boxes. They mostly work the same way as
// their UI counterparts, expect instead of returning the result, a callback
@@ -502,7 +517,7 @@
var setSelectMode = function() {
var curr = $('.tool_button_current');
- if(curr[0].id !== 'tool_select') {
+ if(curr.length && curr[0].id !== 'tool_select') {
curr.removeClass('tool_button_current').addClass('tool_button');
$('#tool_select').addClass('tool_button_current').removeClass('tool_button');
$('#styleoverrides').text('#svgcanvas svg *{cursor:move;pointer-events:all} #svgcanvas svg{cursor:default}');
@@ -534,6 +549,8 @@
var multiselected = false;
var editingsource = false;
var docprops = false;
+ var cur_context = '';
+ var orig_title = $('title:first').text();
var fillPaint = new $.jGraduate.Paint({solidColor: curConfig.initFill.color});
var strokePaint = new $.jGraduate.Paint({solidColor: curConfig.initStroke.color});
@@ -617,6 +634,7 @@
// called when we've selected a different element
var selectedChanged = function(window,elems) {
var mode = svgCanvas.getMode();
+ if(mode === "select") setSelectMode();
var is_node = (mode == "pathedit");
// if elems[1] is present, then we have more than one element
selectedElement = (elems.length == 1 || elems[1] == null ? elems[0] : null);
@@ -705,6 +723,51 @@
zoomDone();
}
+ $('#cur_context_panel').delegate('a', 'click', function() {
+ var link = $(this);
+ if(link.attr('data-root')) {
+ svgCanvas.leaveContext();
+ } else {
+ svgCanvas.setContext(link.text());
+ }
+ return false;
+ });
+
+ var contextChanged = function(win, context) {
+ $('#workarea,#sidepanels').css('top', context?100:75);
+ if(cur_context && !context) {
+ // Back to normal
+ workarea[0].scrollTop -= 25;
+ } else if(!cur_context && context) {
+ workarea[0].scrollTop += 25;
+ }
+
+ var link_str = '';
+ if(context) {
+ var str = '';
+ link_str = '
' + svgCanvas.getCurrentLayer() + '';
+
+ $(context).parentsUntil('#svgcontent > g').andSelf().each(function() {
+ if(this.id) {
+ str += ' > ' + this.id;
+ if(this !== context) {
+ link_str += ' >
' + this.id + '';
+ } else {
+ link_str += ' > ' + this.id;
+ }
+ }
+ });
+
+ cur_context = str;
+ } else {
+ cur_context = null;
+ }
+ $('#cur_context_panel').toggle(!!context).html(link_str);
+
+
+ updateTitle();
+ }
+
var flyout_funcs = {};
var setupFlyouts = function(holders) {
@@ -1239,7 +1302,7 @@
// prevent undo on these canvas changes
svgCanvas.setColor('stroke', strokeColor, true);
svgCanvas.setPaintOpacity('stroke', strokeOpacity, true);
-
+
// update the rect inside #fill_color
$("#stroke_color rect").attr({
fill: strokeColor,
@@ -1343,6 +1406,7 @@
return;
}
var is_node = currentMode == 'pathedit'; //elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false;
+ var menu_items = $('#cmenu_canvas li');
$('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,\
#ellipse_panel, #line_panel, #text_panel, #image_panel, #container_panel, #use_panel').hide();
if (elem != null) {
@@ -1469,13 +1533,16 @@
$('#font_size').val(elem.getAttribute("font-size"));
$('#text').val(elem.textContent);
if (svgCanvas.addedNew) {
- $('#text').focus().select();
+ // Timeout needed for IE9
+ setTimeout(function() {
+ $('#text').focus().select();
+ },100);
}
} // text
else if(el_name == 'image') {
setImageURL(svgCanvas.getHref(elem));
} // image
- else if(el_name == 'g' || el_name == 'use') {
+ else if(el_name === 'g' || el_name === 'use') {
$('#container_panel').show();
var title = svgCanvas.getTitle();
var label = $('#g_title')[0];
@@ -1489,11 +1556,16 @@
}
}
}
+ menu_items[(el_name === 'g' ? 'en':'dis') + 'ableContextMenuItems']('#ungroup');
+ menu_items[((el_name === 'g' || !multiselected) ? 'dis':'en') + 'ableContextMenuItems']('#group');
} // if (elem != null)
else if (multiselected) {
$('#multiselected_panel').show();
+ menu_items
+ .enableContextMenuItems('#group')
+ .disableContextMenuItems('#ungroup');
} else {
- $('#cmenu_canvas li').disableContextMenuItems('#delete,#cut,#copy,#move_up,#move_down');
+ menu_items.disableContextMenuItems('#delete,#cut,#copy,#group,#ungroup,#move_up,#move_down');
}
// update history buttons
@@ -1533,6 +1605,7 @@
svgCanvas.bind("saved", saveHandler);
svgCanvas.bind("exported", exportHandler);
svgCanvas.bind("zoomed", zoomChanged);
+ svgCanvas.bind("contextset", contextChanged);
svgCanvas.bind("extension_added", extAdded);
svgCanvas.textActions.setInputElem($("#text")[0]);
@@ -1722,11 +1795,12 @@
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_');
+ })
+
+ $('.palette_item').mousedown(function(evt){
+ var right_click = evt.button === 2;
+ var picker = ((evt.shiftKey || right_click) ? "stroke" : "fill");
+ var id = ((evt.shiftKey || right_click) ? '#stroke_' : '#fill_');
var color = $(this).attr('data-rgb');
var rectbox = document.getElementById("gradbox_"+picker).parentNode.firstChild;
var paint = null;
@@ -1761,7 +1835,7 @@
}
}
updateToolButtonState();
- });
+ }).bind('contextmenu', function(e) {e.preventDefault()});
$("#toggle_stroke_tools").toggle(function() {
$(".stroke_tool").css('display','table-cell');
@@ -1786,10 +1860,6 @@
$('#styleoverrides').text('');
$('.tool_button_current').removeClass('tool_button_current').addClass('tool_button');
$(button).addClass('tool_button_current').removeClass('tool_button');
- // when a tool is selected, we should deselect any currently selected elements
- if(button !== '#tool_select') {
- svgCanvas.clearSelection();
- }
return true;
};
@@ -2115,19 +2185,19 @@
$(inp).blur();
}
- // Do not include the #text input, as it needs to remain focused
- // when clicking on an SVG text element.
- $('#svg_editor input:text:not(#text)').focus(function() {
+ $('#svg_editor').find('button, select, input:not(#text)').focus(function() {
inp = this;
+ ui_context = 'toolbars';
workarea.mousedown(unfocus);
}).blur(function() {
+ ui_context = 'canvas';
workarea.unbind('mousedown', unfocus);
-
// Go back to selecting text if in textedit mode
if(svgCanvas.getMode() == 'textedit') {
$('#text').focus();
}
});
+
}());
var clickSelect = function() {
@@ -2206,13 +2276,15 @@
};
var clickText = function(){
- toolButtonClick('#tool_text');
- svgCanvas.setMode('text');
+ if (toolButtonClick('#tool_text')) {
+ svgCanvas.setMode('text');
+ }
};
var clickPath = function(){
- toolButtonClick('#tool_path');
- svgCanvas.setMode('path');
+ if (toolButtonClick('#tool_path')) {
+ svgCanvas.setMode('path');
+ }
};
// Delete is a contextual tool that only appears in the ribbon if
@@ -2267,6 +2339,12 @@
var moveSelected = function(dx,dy) {
if (selectedElement != null || multiselected) {
+ if(curConfig.gridSnapping) {
+ // Use grid snap value regardless of zoom level
+ var multi = svgCanvas.getZoom() * curConfig.snappingStep;
+ dx *= multi;
+ dy *= multi;
+ }
svgCanvas.moveSelectedElements(dx,dy);
}
};
@@ -2318,9 +2396,8 @@
svgCanvas.cycleElement(0);
};
- var rotateSelected = function(cw) {
+ var rotateSelected = function(cw,step) {
if (selectedElement == null || multiselected) return;
- var step = 5;
if(!cw) step *= -1;
var new_angle = $('#angle').val()*1 + step;
svgCanvas.setRotationAngle(new_angle);
@@ -2535,7 +2612,7 @@
hideSourceEditor();
zoomImage();
populateLayers();
- setTitle(svgCanvas.getDocumentTitle());
+ updateTitle();
}
if (!svgCanvas.setSvgString($('#svg_source_textarea').val())) {
@@ -2549,16 +2626,19 @@
setSelectMode();
};
- var setTitle = function(title) {
- var editor_title = $('title:first').text().split(':')[0];
- var new_title = editor_title + (title?': ' + title:'');
+ var updateTitle = function(title) {
+ title = title || svgCanvas.getDocumentTitle();
+ var new_title = orig_title + (title?': ' + title:'');
+ if(cur_context) {
+ new_title = new_title + cur_context;
+ }
$('title:first').text(new_title);
}
var saveDocProperties = function(){
// set title
var new_title = $('#canvas_title').val();
- setTitle(new_title);
+ updateTitle(new_title);
svgCanvas.setDocumentTitle(new_title);
// update resolution
@@ -2604,8 +2684,9 @@
setIconSize($('#iconsize').val());
// set grid setting
- curConfig.gridSnapping = $('#grid_snapping_on').attr('checked');
+ curConfig.gridSnapping = $('#grid_snapping_on')[0].checked;
curConfig.snappingStep = $('#grid_snapping_step').val();
+ svgCanvas.setConfig(curConfig);
updateCanvas();
hideDocProperties();
@@ -2626,13 +2707,8 @@
console.log('NOTE: Icon image missing: ' + icon_id);
return;
}
- try {
- icon = icon.clone();
- $(elem).empty().append(icon);
- } catch(e) {
-// icon = svgCanvas.copyElem(icon[0]);
- }
-
+
+ $(elem).empty().append(icon);
}
var ua_prefix;
@@ -2906,7 +2982,12 @@
var cancelOverlays = function() {
$('#dialog_box').hide();
- if (!editingsource && !docprops) return;
+ if (!editingsource && !docprops) {
+ if(cur_context) {
+ svgCanvas.leaveContext();
+ }
+ return;
+ };
if (editingsource) {
var oldString = svgCanvas.getSvgString();
@@ -3115,70 +3196,45 @@
operaRepaint();
};
- if(window.DOMParser) {
- // set up gradients to be used for the buttons
- var svgdocbox = new DOMParser().parseFromString(
- '
', 'text/xml');
- var docElem = svgdocbox.documentElement;
+ // set up gradients to be used for the buttons
+ var svgdocbox = new DOMParser().parseFromString(
+ '
', 'text/xml');
+ var docElem = svgdocbox.documentElement;
- var boxgrad = svgdocbox.getElementById('gradbox_');
- boxgrad.id = 'gradbox_fill';
- docElem.setAttribute('width',16.5);
- $('#fill_color').append( document.importNode(docElem,true) );
-
- boxgrad.id = 'gradbox_stroke';
- docElem.setAttribute('width',16.5);
- $('#stroke_color').append( document.importNode(docElem,true) );
- $('#stroke_color rect').attr({
- 'fill': '#' + curConfig.initStroke.color,
- 'opacity': curConfig.initStroke.opacity
- });
-
- $('#stroke_width').val(curConfig.initStroke.width);
- $('#group_opacity').val(curConfig.initOpacity * 100);
-
- // Use this SVG elem to test vectorEffect support
- var test_el = docElem.firstChild;
- test_el.setAttribute('style','vector-effect:non-scaling-stroke');
- var supportsNonSS = (test_el.style.vectorEffect == 'non-scaling-stroke');
- test_el.removeAttribute('style');
-
- // Use this to test support for blur element. Seems to work to test support in Webkit
- var blur_test = svgdocbox.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur');
- if(typeof blur_test.stdDeviationX === "undefined") {
- $('#tool_blur').hide();
- }
- $(blur_test).remove();
- } else {
- var svgns = "http://www.w3.org/2000/svg";
- var svgdocbox = document.createElementNS(svgns, 'svg');
- var rect = svgCanvas.addSvgElementFromJson({
- element: 'rect',
- attr: {
- width: '100%',
- height: '100%',
- fill: '#' + curConfig.initFill.color,
- opacity: curConfig.initFill.opacity
- }
- });
- svgdocbox.appendChild(rect);
- var linearGradient = svgCanvas.addSvgElementFromJson({
- element: 'linearGradient',
- attr: {
- id: 'gradbox_'
- }
- });
- svgdocbox.appendChild(linearGradient);
- var docElem = svgdocbox;
+ var boxgrad = svgdocbox.getElementById('gradbox_');
+ boxgrad.id = 'gradbox_fill';
+ docElem.setAttribute('width',16.5);
+ $('#fill_color').append( document.importNode(docElem,true) );
+
+ boxgrad.id = 'gradbox_stroke';
+ docElem.setAttribute('width',16.5);
+ $('#stroke_color').append( document.importNode(docElem,true) );
+ $('#stroke_color rect').attr({
+ 'fill': '#' + curConfig.initStroke.color,
+ 'opacity': curConfig.initStroke.opacity
+ });
+
+ $('#stroke_width').val(curConfig.initStroke.width);
+ $('#group_opacity').val(curConfig.initOpacity * 100);
+
+ // Use this SVG elem to test vectorEffect support
+ var test_el = docElem.firstChild;
+ test_el.setAttribute('style','vector-effect:non-scaling-stroke');
+ var supportsNonSS = (test_el.style.vectorEffect == 'non-scaling-stroke');
+ test_el.removeAttribute('style');
+
+ // Use this to test support for blur element. Seems to work to test support in Webkit
+ var blur_test = svgdocbox.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur');
+ if(typeof blur_test.stdDeviationX === "undefined") {
+ $('#tool_blur').hide();
}
-
-
+ $(blur_test).remove();
// Test for embedImage support (use timeout to not interfere with page load)
setTimeout(function() {
@@ -3237,30 +3293,24 @@
});
$('#layer_new').click(function() {
- var curNames = new Array(svgCanvas.getNumLayers());
- for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); }
+ var i = svgCanvas.getNumLayers();
+ do {
+ var uniqName = uiStrings.layer + " " + ++i;
+ } while(svgCanvas.hasLayer(uniqName));
- var j = (curNames.length+1);
- var uniqName = uiStrings.layer + " " + j;
- while ($.inArray(uniqName, curNames) != -1) {
- j++;
- uniqName = uiStrings.layer + " " + j;
- }
$.prompt(uiStrings.enterUniqueLayerName,uniqName, function(newName) {
if (!newName) return;
- if ($.inArray(newName, curNames) != -1) {
+ if (svgCanvas.hasLayer(newName)) {
$.alert(uiStrings.dupeLayerName);
return;
}
svgCanvas.createLayer(newName);
updateContextPanel();
populateLayers();
- $('#layerlist tr.layer').removeClass("layersel");
- $('#layerlist tr.layer:first').addClass("layersel");
});
});
- $('#layer_delete').click(function() {
+ function deleteLayer() {
if (svgCanvas.deleteCurrentLayer()) {
updateContextPanel();
populateLayers();
@@ -3270,32 +3320,48 @@
$('#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--;
+ function cloneLayer() {
+ var name = svgCanvas.getCurrentLayer() + ' copy';
+
+ $.prompt(uiStrings.enterUniqueLayerName, name, function(newName) {
+ if (!newName) return;
+ if (svgCanvas.hasLayer(newName)) {
+ $.alert(uiStrings.dupeLayerName);
+ return;
+ }
+ svgCanvas.cloneLayer(newName);
+ updateContextPanel();
+ populateLayers();
+ });
+ }
+
+ function mergeLayer() {
+ if($('#layerlist tr.layersel').index() == svgCanvas.getNumLayers()-1) return;
+ svgCanvas.mergeLayer();
+ updateContextPanel();
+ populateLayers();
+ }
+
+ function moveLayer(pos) {
+ var curIndex = $('#layerlist tr.layersel').index();
+ var total = svgCanvas.getNumLayers();
+ if(curIndex > 0 || curIndex < total-1) {
+ curIndex += pos;
svgCanvas.setCurrentLayerPosition(total-curIndex-1);
populateLayers();
- $('#layerlist tr.layer').removeClass("layersel");
- $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel");
}
+ }
+
+ $('#layer_delete').click(deleteLayer);
+
+ $('#layer_up').click(function() {
+ moveLayer(-1);
});
$('#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");
- }
+ moveLayer(1);
});
$('#layer_rename').click(function() {
@@ -3303,22 +3369,13 @@
var oldName = $('#layerlist tr.layersel td.layername').text();
$.prompt(uiStrings.enterNewLayerName,"", function(newName) {
if (!newName) return;
- if (oldName == newName) {
- $.alert(uiStrings.layerHasThatName);
- return;
- }
-
- var curNames = new Array(svgCanvas.getNumLayers());
- for (var i = 0; i < curNames.length; ++i) { curNames[i] = svgCanvas.getLayer(i); }
- if ($.inArray(newName, curNames) != -1) {
+ if (oldName == newName || svgCanvas.hasLayer(newName)) {
$.alert(uiStrings.layerHasThatName);
return;
}
svgCanvas.renameCurrentLayer(newName);
populateLayers();
- $('#layerlist tr.layer').removeClass("layersel");
- $('#layerlist tr.layer:eq('+curIndex+')').addClass("layersel");
});
});
@@ -3440,7 +3497,7 @@
}
// handle selection of layer
$('#layerlist td.layername')
- .click(function(evt){
+ .mouseup(function(evt){
$('#layerlist tr.layer').removeClass("layersel");
var row = $(this.parentNode);
row.addClass("layersel");
@@ -3618,8 +3675,10 @@
{sel:'#copy_save_done', fn: cancelOverlays, evt: 'click'},
// Shortcuts not associated with buttons
- {key: 'shift+left', fn: function(){rotateSelected(0)}},
- {key: 'shift+right', fn: function(){rotateSelected(1)}},
+ {key: 'ctrl+left', fn: function(){rotateSelected(0,1)}},
+ {key: 'ctrl+right', fn: function(){rotateSelected(1,1)}},
+ {key: 'ctrl+shift+left', fn: function(){rotateSelected(0,5)}},
+ {key: 'ctrl+shift+right', fn: function(){rotateSelected(1,5)}},
{key: 'shift+O', fn: selectPrev},
{key: 'shift+P', fn: selectNext},
{key: [modKey+'up', true], fn: function(){zoomImage(2);}},
@@ -3630,6 +3689,10 @@
{key: ['down', true], fn: function(){moveSelected(0,1);}},
{key: ['left', true], fn: function(){moveSelected(-1,0);}},
{key: ['right', true], fn: function(){moveSelected(1,0);}},
+ {key: 'shift+up', fn: function(){moveSelected(0,-10)}},
+ {key: 'shift+down', fn: function(){moveSelected(0,10)}},
+ {key: 'shift+left', fn: function(){moveSelected(-10,0)}},
+ {key: 'shift+right', fn: function(){moveSelected(10,0)}},
{key: 'A', fn: function(){svgCanvas.selectAllInCurrentLayer();}}
];
@@ -3716,6 +3779,18 @@
function(evt) {$(this).change();evt.preventDefault();}
);
+ $(window).bind('keydown', 'tab', function(e) {
+ if(ui_context === 'canvas') {
+ e.preventDefault();
+ selectNext();
+ }
+ }).bind('keydown', 'shift+tab', function(e) {
+ if(ui_context === 'canvas') {
+ e.preventDefault();
+ selectPrev();
+ }
+ });
+
$('#tool_zoom').dblclick(dblclickZoom);
},
setTitles: function() {
@@ -3780,6 +3855,14 @@
if(curConfig.showlayers) {
toggleSidePanel();
}
+
+ if(curConfig.gridSnapping) {
+ $('#grid_snapping_on')[0].checked = true;
+ }
+
+ if(curConfig.snappingStep) {
+ $('#grid_snapping_step').val(curConfig.snappingStep);
+ }
});
$('#rect_rx').SpinButton({ min: 0, max: 1000, step: 1, callback: changeRectRadius });
@@ -3811,6 +3894,12 @@
case 'paste_in_place':
svgCanvas.pasteElements('in_place');
break;
+ case 'group':
+ svgCanvas.groupSelectedElements();
+ break;
+ case 'ungroup':
+ svgCanvas.ungroupSelectedElement();
+ break;
case 'move_down':
moveUpDownSelected('Down');
break;
@@ -3825,6 +3914,40 @@
}
});
+ var lmenu_func = function(action, el, pos) {
+ switch ( action ) {
+ case 'dupe':
+ cloneLayer();
+ break;
+ case 'delete':
+ deleteLayer();
+ break;
+ case 'merge_down':
+ mergeLayer();
+ break;
+ case 'merge_all':
+ svgCanvas.mergeAllLayers();
+ updateContextPanel();
+ populateLayers();
+ break;
+ }
+ }
+
+ $("#layerlist").contextMenu({
+ menu: 'cmenu_layers',
+ inSpeed: 0
+ },
+ lmenu_func
+ );
+
+ $("#layer_moreopts").contextMenu({
+ menu: 'cmenu_layers',
+ inSpeed: 0,
+ allowLeft: true
+ },
+ lmenu_func
+ );
+
$('.contextMenu li').mousedown(function(ev) {
ev.preventDefault();
})
@@ -3962,7 +4085,7 @@
updateCanvas(true);
// });
- // var revnums = "svg-editor.js ($Rev: 1706 $) ";
+ // var revnums = "svg-editor.js ($Rev: 1758 $) ";
// revnums += svgCanvas.getVersion();
// $('#copyright')[0].setAttribute("title", revnums);
@@ -3975,48 +4098,47 @@
// var lang = ('lang' in curPrefs) ? curPrefs.lang : null;
Editor.putLocale(null, good_langs);
- // Not sure what this was being used for...commented out until known.
- // The "message" event listener was interfering with image lib responder
-// try{
-// json_encode = function(obj){
-// //simple partial JSON encoder implementation
-// if(window.JSON && JSON.stringify) return JSON.stringify(obj);
-// var enc = arguments.callee; //for purposes of recursion
-// if(typeof obj == "boolean" || typeof obj == "number"){
-// return obj+'' //should work...
-// }else if(typeof obj == "string"){
-// //a large portion of this is stolen from Douglas Crockford's json2.js
-// return '"'+
-// obj.replace(
-// /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
-// , function (a) {
-// return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-// })
-// +'"'; //note that this isn't quite as purtyful as the usualness
-// }else if(obj.length){ //simple hackish test for arrayish-ness
-// for(var i = 0; i < obj.length; i++){
-// obj[i] = enc(obj[i]); //encode every sub-thingy on top
-// }
-// return "["+obj.join(",")+"]";
-// }else{
-// var pairs = []; //pairs will be stored here
-// for(var k in obj){ //loop through thingys
-// pairs.push(enc(k)+":"+enc(obj[k])); //key: value
-// }
-// return "{"+pairs.join(",")+"}" //wrap in the braces
-// }
-// }
-// window.addEventListener("message", function(e){
-// var cbid = parseInt(e.data.substr(0, e.data.indexOf(";")));
-// try{
-// e.source.postMessage("SVGe"+cbid+";"+json_encode(eval(e.data)), e.origin);
-// }catch(err){
-// e.source.postMessage("SVGe"+cbid+";error:"+err.message, e.origin);
-// }
-// }, false)
-// }catch(err){
-// window.embed_error = err;
-// }
+ // Callback handler for embedapi.js
+ try{
+ json_encode = function(obj){
+ //simple partial JSON encoder implementation
+ if(window.JSON && JSON.stringify) return JSON.stringify(obj);
+ var enc = arguments.callee; //for purposes of recursion
+ if(typeof obj == "boolean" || typeof obj == "number"){
+ return obj+'' //should work...
+ }else if(typeof obj == "string"){
+ //a large portion of this is stolen from Douglas Crockford's json2.js
+ return '"'+
+ obj.replace(
+ /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
+ , function (a) {
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ })
+ +'"'; //note that this isn't quite as purtyful as the usualness
+ }else if(obj.length){ //simple hackish test for arrayish-ness
+ for(var i = 0; i < obj.length; i++){
+ obj[i] = enc(obj[i]); //encode every sub-thingy on top
+ }
+ return "["+obj.join(",")+"]";
+ }else{
+ var pairs = []; //pairs will be stored here
+ for(var k in obj){ //loop through thingys
+ pairs.push(enc(k)+":"+enc(obj[k])); //key: value
+ }
+ return "{"+pairs.join(",")+"}" //wrap in the braces
+ }
+ }
+ window.addEventListener("message", function(e){
+ var cbid = parseInt(e.data.substr(0, e.data.indexOf(";")));
+ try{
+ e.source.postMessage("SVGe"+cbid+";"+json_encode(eval(e.data)), "*");
+ }catch(err){
+ e.source.postMessage("SVGe"+cbid+";error:"+err.message, "*");
+ }
+ }, false)
+ }catch(err){
+ window.embed_error = err;
+ }
@@ -4122,6 +4244,9 @@
Editor.addExtension = function() {
var args = arguments;
+
+ // Note that we don't want this on Editor.ready since some extensions
+ // may want to run before then (like server_opensave).
$(function() {
if(svgCanvas) svgCanvas.addExtension.apply(this, args);
});
diff --git a/public/svg-edit/editor/svgcanvas.js b/public/svg-edit/editor/svgcanvas.js
index 7a559422..65eb27ef 100644
--- a/public/svg-edit/editor/svgcanvas.js
+++ b/public/svg-edit/editor/svgcanvas.js
@@ -184,6 +184,7 @@ var isOpera = !!window.opera,
// Much faster than running getBBox() every time
var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use';
+ var visElems_arr = visElems.split(',');
// var hidElems = 'clipPath,defs,desc,feGaussianBlur,filter,linearGradient,marker,mask,metadata,pattern,radialGradient,stop,switch,symbol,title,textPath';
@@ -396,7 +397,7 @@ var Utils = this.Utils = function() {
// Function: snapToGrid
// round value to for snapping
"snapToGrid" : function(value){
- var stepSize = svgEditor.curConfig.snappingStep;
+ var stepSize = curConfig.snappingStep;
value = Math.round(value/stepSize)*stepSize;
return value;
},
@@ -427,6 +428,7 @@ var Utils = this.Utils = function() {
}();
+var snapToGrid = Utils.snapToGrid;
var elData = $.data;
// TODO: declare the variables and set them as null, then move this setup stuff to
@@ -457,28 +459,20 @@ var canvas = this,
// Array with width/height of canvas
dimensions = curConfig.dimensions;
- if($.browser.msie) {
- var svgroot = document.createElementNS(svgns, 'svg');
- svgroot.id = 'svgroot';
- svgroot.setAttribute('width', dimensions[0]);
- svgroot.setAttribute('height', dimensions[1]);
-
- } else {
- // Create Root SVG element. This is a container for the document being edited, not the document itself.
- var svgroot = svgdoc.importNode(Utils.text2xml('
').documentElement, true);
- }
+ // Create Root SVG element. This is a container for the document being edited, not the document itself.
+ var svgroot = svgdoc.importNode(Utils.text2xml('
').documentElement, true);
container.appendChild(svgroot);
@@ -956,22 +950,22 @@ var BatchCommand = this.undoCmd.batch = function(text) {
this.isEmpty = function() { return this.stack.length == 0; };
}
-// Set scope for these undo functions
-var resetUndoStack, addCommandToHistory;
+// Set scope for addCommandToHistory
+var addCommandToHistory;
// Undo/redo stack related functions
(function(c) {
var undoStackPointer = 0,
undoStack = [];
- // Function: resetUndoStack
- // Resets the undo stack, effectively clearing the undo/redo history
- resetUndoStack = function() {
- undoStack = [];
- undoStackPointer = 0;
- };
-
c.undoMgr = {
+ // Function: undoMgr.resetUndoStack
+ // Resets the undo stack, effectively clearing the undo/redo history
+ resetUndoStack: function() {
+ undoStack = [];
+ undoStackPointer = 0;
+ },
+
// Function: undoMgr.getUndoStackSize
// Returns:
// Integer with the current size of the undo history stack
@@ -1231,9 +1225,11 @@ var SelectorManager;
// get the bbox based on its children.
var stroked_bbox = getStrokedBBox(selected.childNodes);
if(stroked_bbox) {
+ var bb = {};
$.each(bbox, function(key, val) {
- bbox[key] = stroked_bbox[key];
+ bb[key] = stroked_bbox[key];
});
+ bbox = bb;
}
}
@@ -1262,6 +1258,7 @@ var SelectorManager;
// now if the shape is rotated, un-rotate it
var cx = nbax + nbaw/2,
cy = nbay + nbah/2;
+
var angle = getRotationAngle(selected);
if (angle) {
@@ -1459,6 +1456,7 @@ var SelectorManager;
'fill': '#FFF',
'style': 'pointer-events:none'
});
+
// Both Firefox and WebKit are too slow with this filter region (especially at higher
// zoom levels) and Opera has at least one bug
// if (!window.opera) rect.setAttribute('filter', 'url(#canvashadow)');
@@ -1848,7 +1846,7 @@ var addSvgElementFromJson = this.addSvgElementFromJson = function(data) {
if (!shape) {
shape = svgdoc.createElementNS(svgns, data.element);
if (current_layer) {
- current_layer.appendChild(shape);
+ (current_group || current_layer).appendChild(shape);
}
}
if(data.curStyles) {
@@ -1873,8 +1871,8 @@ var addSvgElementFromJson = this.addSvgElementFromJson = function(data) {
(function() {
// TODO: make this string optional and set by the client
var comment = svgdoc.createComment(" Created with SVG-edit - http://svg-edit.googlecode.com/ ");
- // Lead to invalid content with Instiki's Sanitizer
- // svgcontent.appendChild(comment);
+ // Lead to invalid content with Instiki's Sanitizer
+ // svgcontent.appendChild(comment);
// TODO For Issue 208: this is a start on a thumbnail
// var svgthumb = svgdoc.createElementNS(svgns, "use");
@@ -1898,6 +1896,12 @@ var all_layers = [],
// pointer to the current layer
current_layer = null,
+ // pointer to current group (for in-group editing)
+ current_group = null,
+
+ // Array with current disabled elements (for in-group editing)
+ disabled_elems = [],
+
// Object with save options
save_options = {round_digits: 5},
@@ -2059,14 +2063,16 @@ var round = this.round = function(val) {
var getIntersectionList = this.getIntersectionList = function(rect) {
if (rubberBox == null) { return null; }
+ var parent = current_group || current_layer;
+
if(!curBBoxes.length) {
// Cache all bboxes
- curBBoxes = getVisibleElements(current_layer, true);
+ curBBoxes = getVisibleElements(parent, true);
}
var resultList = null;
try {
- resultList = current_layer.getIntersectionList(rect, null);
+ resultList = parent.getIntersectionList(rect, null);
} catch(e) { }
if (resultList == null || typeof(resultList.item) != "function") {
@@ -2075,10 +2081,10 @@ var getIntersectionList = this.getIntersectionList = function(rect) {
if(!rect) {
var rubberBBox = rubberBox.getBBox();
var bb = {};
- $.each(rubberBBox, function(key, val) {
- // Can't set values to a real BBox object, so make a fake one
- bb[key] = val / current_zoom;
- });
+
+ for(var o in rubberBBox) {
+ bb[o] = rubberBBox[o] / current_zoom;
+ }
rubberBBox = bb;
} else {
@@ -2409,6 +2415,7 @@ var getId, getNextId;
return id;
};
+ // Function: call
// Run the callback function associated with the given event
//
// Parameters:
@@ -2616,22 +2623,24 @@ var getBBox = this.getBBox = function(elem) {
var selected = elem || selectedElements[0];
if (elem.nodeType != 1) return null;
var ret = null;
- if(elem.nodeName == 'text' && selected.textContent == '') {
+ var elname = selected.nodeName;
+
+ if(elname === 'text' && selected.textContent === '') {
selected.textContent = 'a'; // Some character needed for the selector to use.
ret = selected.getBBox();
selected.textContent = '';
- } else if(elem.nodeName == 'path' && isWebkit) {
+ } else if(elname === 'path' && isWebkit) {
ret = getPathBBox(selected);
- } else if(elem.nodeName == 'use' && !isWebkit) {
+ } else if(elname === 'use' && !isWebkit || elname === 'foreignObject') {
ret = selected.getBBox();
- ret.x += parseFloat(selected.getAttribute('x')||0);
- ret.y += parseFloat(selected.getAttribute('y')||0);
- } else if(elem.nodeName == 'foreignObject') {
- ret = selected.getBBox();
- ret.x += parseFloat(selected.getAttribute('x')||0);
- ret.y += parseFloat(selected.getAttribute('y')||0);
- } else {
- try { ret = selected.getBBox(); }
+ var bb = {};
+ bb.width = ret.width;
+ bb.height = ret.height;
+ bb.x = ret.x + parseFloat(selected.getAttribute('x')||0);
+ bb.y = ret.y + parseFloat(selected.getAttribute('y')||0);
+ ret = bb;
+ } else if(~$.inArray(elname, visElems_arr)) {
+ try { ret = selected.getBBox();}
catch(e) {
// Check if element is child of a foreignObject
var fo = $(selected).closest("foreignObject");
@@ -3063,41 +3072,41 @@ var remapElement = this.remapElement = function(selected,changes,m) {
changes.y = changes.y-0 + Math.min(0,changes.height);
changes.width = Math.abs(changes.width);
changes.height = Math.abs(changes.height);
- if(svgEditor.curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
- changes.x = Utils.snapToGrid(changes.x);
- changes.y = Utils.snapToGrid(changes.y);
- changes.width = Utils.snapToGrid(changes.width);
- changes.height = Utils.snapToGrid(changes.height);
+ if(curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
+ changes.x = snapToGrid(changes.x);
+ changes.y = snapToGrid(changes.y);
+ changes.width = snapToGrid(changes.width);
+ changes.height = snapToGrid(changes.height);
}
assignAttributes(selected, changes, 1000, true);
break;
case "ellipse":
changes.rx = Math.abs(changes.rx);
changes.ry = Math.abs(changes.ry);
- if(svgEditor.curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
- changes.cx = Utils.snapToGrid(changes.cx);
- changes.cy = Utils.snapToGrid(changes.cy);
- changes.rx = Utils.snapToGrid(changes.rx);
- changes.ry = Utils.snapToGrid(changes.ry);
+ if(curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
+ changes.cx = snapToGrid(changes.cx);
+ changes.cy = snapToGrid(changes.cy);
+ changes.rx = snapToGrid(changes.rx);
+ changes.ry = snapToGrid(changes.ry);
}
case "circle":
if(changes.r) changes.r = Math.abs(changes.r);
- if(svgEditor.curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
- changes.cx = Utils.snapToGrid(changes.cx);
- changes.cy = Utils.snapToGrid(changes.cy);
- changes.r = Utils.snapToGrid(changes.r);
+ if(curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
+ changes.cx = snapToGrid(changes.cx);
+ changes.cy = snapToGrid(changes.cy);
+ changes.r = snapToGrid(changes.r);
}
case "line":
- if(svgEditor.curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
- changes.x1 = Utils.snapToGrid(changes.x1);
- changes.y1 = Utils.snapToGrid(changes.y1);
- changes.x2 = Utils.snapToGrid(changes.x2);
- changes.y2 = Utils.snapToGrid(changes.y2);
+ if(curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
+ changes.x1 = snapToGrid(changes.x1);
+ changes.y1 = snapToGrid(changes.y1);
+ changes.x2 = snapToGrid(changes.x2);
+ changes.y2 = snapToGrid(changes.y2);
}
case "text":
- if(svgEditor.curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
- changes.x = Utils.snapToGrid(changes.x);
- changes.y = Utils.snapToGrid(changes.y);
+ if(curConfig.gridSnapping && selected.parentNode.parentNode.localName == "svg"){
+ changes.x = snapToGrid(changes.x);
+ changes.y = snapToGrid(changes.y);
}
case "use":
assignAttributes(selected, changes, 1000, true);
@@ -3165,7 +3174,6 @@ var remapElement = this.remapElement = function(selected,changes,m) {
selected.setAttribute("d", dstr);
break;
}
-
};
// Function: updateClipPath
@@ -3509,7 +3517,11 @@ var recalculateDimensions = this.recalculateDimensions = function(selected) {
if (childTlist) {
var newxlate = svgroot.createSVGTransform();
newxlate.setTranslate(tx,ty);
- childTlist.insertItemBefore(newxlate, 0);
+ if(childTlist.numberOfItems) {
+ childTlist.insertItemBefore(newxlate, 0);
+ } else {
+ childTlist.appendItem(newxlate);
+ }
batchCmd.addSubCommand( recalculateDimensions(child) );
// If any