Update SVG-Edit
This commit is contained in:
parent
f914a40fa9
commit
fccb1e2b01
24 changed files with 6126 additions and 3751 deletions
118
public/svg-edit/editor/browsersupport.js
Normal file
118
public/svg-edit/editor/browsersupport.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* Package: svgedit.browsersupport
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) jQuery (for $.alert())
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
|
||||
if (!svgedit.browsersupport) {
|
||||
svgedit.browsersupport = {};
|
||||
}
|
||||
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
var userAgent = navigator.userAgent;
|
||||
var svg = document.createElementNS(svgns, 'svg');
|
||||
|
||||
// Note: Browser sniffing should only be used if no other detection method is possible
|
||||
var isOpera_ = !!window.opera;
|
||||
var isWebkit_ = userAgent.indexOf("AppleWebKit") >= 0;
|
||||
var isGecko_ = userAgent.indexOf('Gecko/') >= 0;
|
||||
|
||||
svgedit.browsersupport.isOpera = function() { return isOpera_; }
|
||||
svgedit.browsersupport.isWebkit = function() { return isWebkit_; }
|
||||
svgedit.browsersupport.isGecko = function() { return isGecko_; }
|
||||
|
||||
// segList functions (for FF1.5 and 2.0)
|
||||
function supportPathReplaceItem() {
|
||||
var path = document.createElementNS(svgns, 'path');
|
||||
path.setAttribute('d','M0,0 10,10');
|
||||
var seglist = path.pathSegList;
|
||||
var seg = path.createSVGPathSegLinetoAbs(5,5);
|
||||
try {
|
||||
seglist.replaceItem(seg, 0);
|
||||
return true;
|
||||
} catch(err) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
function supportPathInsertItemBefore() {
|
||||
var path = document.createElementNS(svgns,'path');
|
||||
path.setAttribute('d','M0,0 10,10');
|
||||
var seglist = path.pathSegList;
|
||||
var seg = path.createSVGPathSegLinetoAbs(5,5);
|
||||
try {
|
||||
seglist.insertItemBefore(seg, 0);
|
||||
return true;
|
||||
} catch(err) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
// text character positioning
|
||||
function supportTextCharPos() {
|
||||
var retValue = false;
|
||||
var svgcontent = document.createElementNS(svgns, 'svg');
|
||||
document.documentElement.appendChild(svgcontent);
|
||||
try {
|
||||
var text = document.createElementNS(svgns,'text');
|
||||
text.textContent = 'a';
|
||||
svgcontent.appendChild(text);
|
||||
text.getStartPositionOfChar(0);
|
||||
retValue = true;
|
||||
} catch(err) {}
|
||||
document.documentElement.removeChild(svgcontent);
|
||||
return retValue;
|
||||
}
|
||||
|
||||
function supportEditableText() {
|
||||
// TODO: Find better way to check support for this
|
||||
return svgedit.browsersupport.isOpera();
|
||||
}
|
||||
|
||||
function supportGoodDecimals() {
|
||||
// Correct decimals on clone attributes (Opera < 10.5/win/non-en)
|
||||
var rect = document.createElementNS(svgns, 'rect');
|
||||
rect.setAttribute('x',.1);
|
||||
var crect = rect.cloneNode(false);
|
||||
var retValue = (crect.getAttribute('x').indexOf(',') == -1);
|
||||
if(!retValue) {
|
||||
$.alert("NOTE: This version of Opera is known to contain bugs in SVG-edit.\n\
|
||||
Please upgrade to the <a href='http://opera.com'>latest version</a> in which the problems have been fixed.");
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
function supportNonScalingStroke() {
|
||||
var rect = document.createElementNS(svgns, 'rect');
|
||||
rect.setAttribute('style','vector-effect:non-scaling-stroke');
|
||||
return rect.style.vectorEffect === 'non-scaling-stroke';
|
||||
}
|
||||
|
||||
function supportNativeSVGTransformLists() {
|
||||
var rect = document.createElementNS(svgns, 'rect');
|
||||
var rxform = rect.transform.baseVal;
|
||||
|
||||
var t1 = svg.createSVGTransform();
|
||||
rxform.appendItem(t1);
|
||||
return rxform.getItem(0) == t1;
|
||||
}
|
||||
|
||||
svgedit.browsersupport.pathReplaceItem = supportPathReplaceItem();
|
||||
svgedit.browsersupport.pathInsertItemBefore = supportPathInsertItemBefore();
|
||||
svgedit.browsersupport.textCharPos = supportTextCharPos();
|
||||
svgedit.browsersupport.editableText = supportEditableText();
|
||||
svgedit.browsersupport.goodDecimals = supportGoodDecimals();
|
||||
svgedit.browsersupport.nonScalingStroke = supportNonScalingStroke();
|
||||
svgedit.browsersupport.nativeTransformLists = supportNativeSVGTransformLists();
|
||||
|
||||
})();
|
|
@ -72,7 +72,7 @@ function embedded_svg_edit(frame){
|
|||
//Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API
|
||||
//var l=[];for(var i in svgCanvas){if(typeof svgCanvas[i] == "function"){l.push(i)}};
|
||||
//run in svgedit itself
|
||||
var functions = ["updateElementFromJson", "embedImage", "fixOperaXML", "clearSelection", "addToSelection", "removeFromSelection", "addNodeToSelection", "open", "save", "getSvgString", "setSvgString", "createLayer", "deleteCurrentLayer", "getNumLayers", "getLayer", "getCurrentLayer", "setCurrentLayer", "renameCurrentLayer", "setCurrentLayerPosition", "getLayerVisibility", "setLayerVisibility", "moveSelectedToLayer", "getLayerOpacity", "setLayerOpacity", "clear", "clearPath", "getNodePoint", "clonePathNode", "deletePathNode", "getResolution", "getImageTitle", "setImageTitle", "setResolution", "setBBoxZoom", "setZoom", "getMode", "setMode", "getStrokeColor", "setStrokeColor", "getFillColor", "setFillColor", "setStrokePaint", "setFillPaint", "getStrokeWidth", "setStrokeWidth", "getStrokeStyle", "setStrokeStyle", "getOpacity", "setOpacity", "getFillOpacity", "setFillOpacity", "getStrokeOpacity", "setStrokeOpacity", "getTransformList", "getBBox", "getRotationAngle", "setRotationAngle", "each", "bind", "setIdPrefix", "getBold", "setBold", "getItalic", "setItalic", "getFontFamily", "setFontFamily", "getFontSize", "setFontSize", "getText", "setTextContent", "setImageURL", "setRectRadius", "setSegType", "quickClone", "beginUndoableChange", "changeSelectedAttributeNoUndo", "finishUndoableChange", "changeSelectedAttribute", "deleteSelectedElements", "groupSelectedElements", "ungroupSelectedElement", "moveToTopSelectedElement", "moveToBottomSelectedElement", "moveSelectedElements", "getStrokedBBox", "getVisibleElements", "cycleElement", "getUndoStackSize", "getRedoStackSize", "getNextUndoCommandText", "getNextRedoCommandText", "undo", "redo", "cloneSelectedElements", "alignSelectedElements", "getZoom", "getVersion", "setIconSize", "setLang", "setCustomHandlers"]
|
||||
var functions = ["updateElementFromJson", "embedImage", "fixOperaXML", "clearSelection", "addToSelection", "removeFromSelection", "addNodeToSelection", "open", "save", "getSvgString", "setSvgString", "createLayer", "deleteCurrentLayer", "getNumLayers", "getLayer", "getCurrentLayer", "setCurrentLayer", "renameCurrentLayer", "setCurrentLayerPosition", "getLayerVisibility", "setLayerVisibility", "moveSelectedToLayer", "getLayerOpacity", "setLayerOpacity", "clear", "clearPath", "getNodePoint", "clonePathNode", "deletePathNode", "getResolution", "getImageTitle", "setImageTitle", "setResolution", "setBBoxZoom", "setZoom", "getMode", "setMode", "getStrokeColor", "setStrokeColor", "getFillColor", "setFillColor", "setStrokePaint", "setFillPaint", "getStrokeWidth", "setStrokeWidth", "getStrokeStyle", "setStrokeStyle", "getOpacity", "setOpacity", "getFillOpacity", "setFillOpacity", "getStrokeOpacity", "setStrokeOpacity", "getTransformList", "getBBox", "getRotationAngle", "setRotationAngle", "each", "bind", "setIdPrefix", "getBold", "setBold", "getItalic", "setItalic", "getFontFamily", "setFontFamily", "getFontSize", "setFontSize", "getText", "setTextContent", "setImageURL", "setRectRadius", "setSegType", "quickClone", "changeSelectedAttributeNoUndo", "changeSelectedAttribute", "deleteSelectedElements", "groupSelectedElements", "ungroupSelectedElement", "moveToTopSelectedElement", "moveToBottomSelectedElement", "moveSelectedElements", "getStrokedBBox", "getVisibleElements", "cycleElement", "getUndoStackSize", "getRedoStackSize", "getNextUndoCommandText", "getNextRedoCommandText", "undo", "redo", "cloneSelectedElements", "alignSelectedElements", "getZoom", "getVersion", "setIconSize", "setLang", "setCustomHandlers"]
|
||||
|
||||
//TODO: rewrite the following, it's pretty scary.
|
||||
for(var i = 0; i < functions.length; i++){
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) units.js
|
||||
// 2) everything else
|
||||
|
||||
svgEditor.addExtension("view_grid", function(s) {
|
||||
|
||||
var svgdoc = document.getElementById("svgcanvas").ownerDocument,
|
||||
|
@ -78,7 +82,7 @@ svgEditor.addExtension("view_grid", function(s) {
|
|||
var bgwidth = +canvBG.attr('width');
|
||||
var bgheight = +canvBG.attr('height');
|
||||
|
||||
var units = svgCanvas.getUnits();
|
||||
var units = svgedit.units.getTypeMap();
|
||||
var unit = units[svgEditor.curConfig.baseUnit]; // 1 = 1px
|
||||
var r_intervals = [.01, .1, 1, 10, 100, 1000];
|
||||
|
||||
|
|
601
public/svg-edit/editor/history.js
Normal file
601
public/svg-edit/editor/history.js
Normal file
|
@ -0,0 +1,601 @@
|
|||
/**
|
||||
* Package: svedit.history
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) jQuery
|
||||
// 2) svgtransformlist.js
|
||||
// 3) svgutils.js
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
|
||||
if (!svgedit.history) {
|
||||
svgedit.history = {};
|
||||
}
|
||||
|
||||
// Group: Undo/Redo history management
|
||||
|
||||
|
||||
svgedit.history.HistoryEventTypes = {
|
||||
BEFORE_APPLY: 'before_apply',
|
||||
AFTER_APPLY: 'after_apply',
|
||||
BEFORE_UNAPPLY: 'before_unapply',
|
||||
AFTER_UNAPPLY: 'after_unapply'
|
||||
};
|
||||
|
||||
var removedElements = {};
|
||||
|
||||
/**
|
||||
* Interface: svgedit.history.HistoryCommand
|
||||
* An interface that all command objects must implement.
|
||||
*
|
||||
* interface svgedit.history.HistoryCommand {
|
||||
* void apply();
|
||||
* void unapply();
|
||||
* Element[] elements();
|
||||
* String getText();
|
||||
*
|
||||
* static String type();
|
||||
* }
|
||||
*/
|
||||
|
||||
// Class: svgedit.history.MoveElementCommand
|
||||
// implements svgedit.history.HistoryCommand
|
||||
// History command for an element that had its DOM position changed
|
||||
//
|
||||
// Parameters:
|
||||
// elem - The DOM element that was moved
|
||||
// oldNextSibling - The element's next sibling before it was moved
|
||||
// oldParent - The element's parent before it was moved
|
||||
// text - An optional string visible to user related to this change
|
||||
svgedit.history.MoveElementCommand = function(elem, oldNextSibling, oldParent, text) {
|
||||
this.elem = elem;
|
||||
this.text = text ? ("Move " + elem.tagName + " to " + text) : ("Move " + elem.tagName);
|
||||
this.oldNextSibling = oldNextSibling;
|
||||
this.oldParent = oldParent;
|
||||
this.newNextSibling = elem.nextSibling;
|
||||
this.newParent = elem.parentNode;
|
||||
};
|
||||
svgedit.history.MoveElementCommand.type = function() { return 'svgedit.history.MoveElementCommand'; }
|
||||
svgedit.history.MoveElementCommand.prototype.type = svgedit.history.MoveElementCommand.type;
|
||||
|
||||
// Function: svgedit.history.MoveElementCommand.getText
|
||||
svgedit.history.MoveElementCommand.prototype.getText = function() {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.MoveElementCommand.apply
|
||||
// Re-positions the element
|
||||
svgedit.history.MoveElementCommand.prototype.apply = function(handler) {
|
||||
// TODO(codedread): Refactor this common event code into a base HistoryCommand class.
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
|
||||
}
|
||||
|
||||
this.elem = this.newParent.insertBefore(this.elem, this.newNextSibling);
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.MoveElementCommand.unapply
|
||||
// Positions the element back to its original location
|
||||
svgedit.history.MoveElementCommand.prototype.unapply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
|
||||
}
|
||||
|
||||
this.elem = this.oldParent.insertBefore(this.elem, this.oldNextSibling);
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.MoveElementCommand.elements
|
||||
// Returns array with element associated with this command
|
||||
svgedit.history.MoveElementCommand.prototype.elements = function() {
|
||||
return [this.elem];
|
||||
};
|
||||
|
||||
|
||||
// Class: svgedit.history.InsertElementCommand
|
||||
// implements svgedit.history.HistoryCommand
|
||||
// History command for an element that was added to the DOM
|
||||
//
|
||||
// Parameters:
|
||||
// elem - The newly added DOM element
|
||||
// text - An optional string visible to user related to this change
|
||||
svgedit.history.InsertElementCommand = function(elem, text) {
|
||||
this.elem = elem;
|
||||
this.text = text || ("Create " + elem.tagName);
|
||||
this.parent = elem.parentNode;
|
||||
this.nextSibling = this.elem.nextSibling;
|
||||
};
|
||||
svgedit.history.InsertElementCommand.type = function() { return 'svgedit.history.InsertElementCommand'; }
|
||||
svgedit.history.InsertElementCommand.prototype.type = svgedit.history.InsertElementCommand.type;
|
||||
|
||||
// Function: svgedit.history.InsertElementCommand.getText
|
||||
svgedit.history.InsertElementCommand.prototype.getText = function() {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.InsertElementCommand.apply
|
||||
// Re-Inserts the new element
|
||||
svgedit.history.InsertElementCommand.prototype.apply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
|
||||
}
|
||||
|
||||
this.elem = this.parent.insertBefore(this.elem, this.nextSibling);
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.InsertElementCommand.unapply
|
||||
// Removes the element
|
||||
svgedit.history.InsertElementCommand.prototype.unapply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
|
||||
}
|
||||
|
||||
this.parent = this.elem.parentNode;
|
||||
this.elem = this.elem.parentNode.removeChild(this.elem);
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.InsertElementCommand.elements
|
||||
// Returns array with element associated with this command
|
||||
svgedit.history.InsertElementCommand.prototype.elements = function() {
|
||||
return [this.elem];
|
||||
};
|
||||
|
||||
|
||||
// Class: svgedit.history.RemoveElementCommand
|
||||
// implements svgedit.history.HistoryCommand
|
||||
// History command for an element removed from the DOM
|
||||
//
|
||||
// Parameters:
|
||||
// elem - The removed DOM element
|
||||
// oldNextSibling - the DOM element's nextSibling when it was in the DOM
|
||||
// oldParent - The DOM element's parent
|
||||
// text - An optional string visible to user related to this change
|
||||
svgedit.history.RemoveElementCommand = function(elem, oldNextSibling, oldParent, text) {
|
||||
this.elem = elem;
|
||||
this.text = text || ("Delete " + elem.tagName);
|
||||
this.nextSibling = oldNextSibling;
|
||||
this.parent = oldParent;
|
||||
|
||||
// special hack for webkit: remove this element's entry in the svgTransformLists map
|
||||
svgedit.transformlist.removeElementFromListMap(elem);
|
||||
};
|
||||
svgedit.history.RemoveElementCommand.type = function() { return 'svgedit.history.RemoveElementCommand'; }
|
||||
svgedit.history.RemoveElementCommand.prototype.type = svgedit.history.RemoveElementCommand.type;
|
||||
|
||||
// Function: svgedit.history.RemoveElementCommand.getText
|
||||
svgedit.history.RemoveElementCommand.prototype.getText = function() {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
// Function: RemoveElementCommand.apply
|
||||
// Re-removes the new element
|
||||
svgedit.history.RemoveElementCommand.prototype.apply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
|
||||
}
|
||||
|
||||
svgedit.transformlist.removeElementFromListMap(this.elem);
|
||||
this.parent = this.elem.parentNode;
|
||||
this.elem = this.parent.removeChild(this.elem);
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: RemoveElementCommand.unapply
|
||||
// Re-adds the new element
|
||||
svgedit.history.RemoveElementCommand.prototype.unapply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
|
||||
}
|
||||
|
||||
svgedit.transformlist.removeElementFromListMap(this.elem);
|
||||
this.parent.insertBefore(this.elem, this.nextSibling);
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: RemoveElementCommand.elements
|
||||
// Returns array with element associated with this command
|
||||
svgedit.history.RemoveElementCommand.prototype.elements = function() {
|
||||
return [this.elem];
|
||||
};
|
||||
|
||||
|
||||
// Class: svgedit.history.ChangeElementCommand
|
||||
// implements svgedit.history.HistoryCommand
|
||||
// History command to make a change to an element.
|
||||
// Usually an attribute change, but can also be textcontent.
|
||||
//
|
||||
// Parameters:
|
||||
// elem - The DOM element that was changed
|
||||
// attrs - An object with the attributes to be changed and the values they had *before* the change
|
||||
// text - An optional string visible to user related to this change
|
||||
svgedit.history.ChangeElementCommand = function(elem, attrs, text) {
|
||||
this.elem = elem;
|
||||
this.text = text ? ("Change " + elem.tagName + " " + text) : ("Change " + elem.tagName);
|
||||
this.newValues = {};
|
||||
this.oldValues = attrs;
|
||||
for (var attr in attrs) {
|
||||
if (attr == "#text") this.newValues[attr] = elem.textContent;
|
||||
else if (attr == "#href") this.newValues[attr] = svgedit.utilities.getHref(elem);
|
||||
else this.newValues[attr] = elem.getAttribute(attr);
|
||||
}
|
||||
};
|
||||
svgedit.history.ChangeElementCommand.type = function() { return 'svgedit.history.ChangeElementCommand'; }
|
||||
svgedit.history.ChangeElementCommand.prototype.type = svgedit.history.ChangeElementCommand.type;
|
||||
|
||||
// Function: svgedit.history.ChangeElementCommand.getText
|
||||
svgedit.history.ChangeElementCommand.prototype.getText = function() {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.ChangeElementCommand.apply
|
||||
// Performs the stored change action
|
||||
svgedit.history.ChangeElementCommand.prototype.apply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
|
||||
}
|
||||
|
||||
var bChangedTransform = false;
|
||||
for(var attr in this.newValues ) {
|
||||
if (this.newValues[attr]) {
|
||||
if (attr == "#text") this.elem.textContent = this.newValues[attr];
|
||||
else if (attr == "#href") svgedit.utilities.setHref(this.elem, this.newValues[attr])
|
||||
else this.elem.setAttribute(attr, this.newValues[attr]);
|
||||
}
|
||||
else {
|
||||
if (attr == "#text") {
|
||||
this.elem.textContent = "";
|
||||
}
|
||||
else {
|
||||
this.elem.setAttribute(attr, "");
|
||||
this.elem.removeAttribute(attr);
|
||||
}
|
||||
}
|
||||
|
||||
if (attr == "transform") { bChangedTransform = true; }
|
||||
}
|
||||
|
||||
// relocate rotational transform, if necessary
|
||||
if(!bChangedTransform) {
|
||||
var angle = svgedit.utilities.getRotationAngle(this.elem);
|
||||
if (angle) {
|
||||
var bbox = elem.getBBox();
|
||||
var cx = bbox.x + bbox.width/2,
|
||||
cy = bbox.y + bbox.height/2;
|
||||
var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
|
||||
if (rotate != elem.getAttribute("transform")) {
|
||||
elem.setAttribute("transform", rotate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.ChangeElementCommand.unapply
|
||||
// Reverses the stored change action
|
||||
svgedit.history.ChangeElementCommand.prototype.unapply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
|
||||
}
|
||||
|
||||
var bChangedTransform = false;
|
||||
for(var attr in this.oldValues ) {
|
||||
if (this.oldValues[attr]) {
|
||||
if (attr == "#text") this.elem.textContent = this.oldValues[attr];
|
||||
else if (attr == "#href") svgedit.utilities.setHref(this.elem, this.oldValues[attr]);
|
||||
else this.elem.setAttribute(attr, this.oldValues[attr]);
|
||||
}
|
||||
else {
|
||||
if (attr == "#text") {
|
||||
this.elem.textContent = "";
|
||||
}
|
||||
else this.elem.removeAttribute(attr);
|
||||
}
|
||||
if (attr == "transform") { bChangedTransform = true; }
|
||||
}
|
||||
// relocate rotational transform, if necessary
|
||||
if(!bChangedTransform) {
|
||||
var angle = svgedit.utilities.getRotationAngle(this.elem);
|
||||
if (angle) {
|
||||
var bbox = elem.getBBox();
|
||||
var cx = bbox.x + bbox.width/2,
|
||||
cy = bbox.y + bbox.height/2;
|
||||
var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join('');
|
||||
if (rotate != elem.getAttribute("transform")) {
|
||||
elem.setAttribute("transform", rotate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove transformlist to prevent confusion that causes bugs like 575.
|
||||
svgedit.transformlist.removeElementFromListMap(this.elem);
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Function: ChangeElementCommand.elements
|
||||
// Returns array with element associated with this command
|
||||
svgedit.history.ChangeElementCommand.prototype.elements = function() {
|
||||
return [this.elem];
|
||||
};
|
||||
|
||||
|
||||
// TODO: create a 'typing' command object that tracks changes in text
|
||||
// if a new Typing command is created and the top command on the stack is also a Typing
|
||||
// and they both affect the same element, then collapse the two commands into one
|
||||
|
||||
|
||||
// Class: svgedit.history.BatchCommand
|
||||
// implements svgedit.history.HistoryCommand
|
||||
// History command that can contain/execute multiple other commands
|
||||
//
|
||||
// Parameters:
|
||||
// text - An optional string visible to user related to this change
|
||||
svgedit.history.BatchCommand = function(text) {
|
||||
this.text = text || "Batch Command";
|
||||
this.stack = [];
|
||||
};
|
||||
svgedit.history.BatchCommand.type = function() { return 'svgedit.history.BatchCommand'; }
|
||||
svgedit.history.BatchCommand.prototype.type = svgedit.history.BatchCommand.type;
|
||||
|
||||
// Function: svgedit.history.BatchCommand.getText
|
||||
svgedit.history.BatchCommand.prototype.getText = function() {
|
||||
return this.text;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.BatchCommand.apply
|
||||
// Runs "apply" on all subcommands
|
||||
svgedit.history.BatchCommand.prototype.apply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);
|
||||
}
|
||||
|
||||
var len = this.stack.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
this.stack[i].apply(handler);
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.BatchCommand.unapply
|
||||
// Runs "unapply" on all subcommands
|
||||
svgedit.history.BatchCommand.prototype.unapply = function(handler) {
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);
|
||||
}
|
||||
|
||||
for (var i = this.stack.length-1; i >= 0; i--) {
|
||||
this.stack[i].unapply(handler);
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.BatchCommand.elements
|
||||
// Iterate through all our subcommands and returns all the elements we are changing
|
||||
svgedit.history.BatchCommand.prototype.elements = function() {
|
||||
var elems = [];
|
||||
var cmd = this.stack.length;
|
||||
while (cmd--) {
|
||||
var thisElems = this.stack[cmd].elements();
|
||||
var elem = thisElems.length;
|
||||
while (elem--) {
|
||||
if (elems.indexOf(thisElems[elem]) == -1) elems.push(thisElems[elem]);
|
||||
}
|
||||
}
|
||||
return elems;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.BatchCommand.addSubCommand
|
||||
// Adds a given command to the history stack
|
||||
//
|
||||
// Parameters:
|
||||
// cmd - The undo command object to add
|
||||
svgedit.history.BatchCommand.prototype.addSubCommand = function(cmd) {
|
||||
this.stack.push(cmd);
|
||||
};
|
||||
|
||||
// Function: svgedit.history.BatchCommand.isEmpty
|
||||
// Returns a boolean indicating whether or not the batch command is empty
|
||||
svgedit.history.BatchCommand.prototype.isEmpty = function() {
|
||||
return this.stack.length == 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interface: svgedit.history.HistoryEventHandler
|
||||
* An interface for objects that will handle history events.
|
||||
*
|
||||
* interface svgedit.history.HistoryEventHandler {
|
||||
* void handleHistoryEvent(eventType, command);
|
||||
* }
|
||||
*
|
||||
* eventType is a string conforming to one of the HistoryEvent types (see above).
|
||||
* command is an object fulfilling the HistoryCommand interface (see above).
|
||||
*/
|
||||
|
||||
// Class: svgedit.history.UndoManager
|
||||
// Parameters:
|
||||
// historyEventHandler - an object that conforms to the HistoryEventHandler interface
|
||||
// (see above)
|
||||
svgedit.history.UndoManager = function(historyEventHandler) {
|
||||
this.handler_ = historyEventHandler || null;
|
||||
this.undoStackPointer = 0;
|
||||
this.undoStack = [];
|
||||
|
||||
// this is the stack that stores the original values, the elements and
|
||||
// the attribute name for begin/finish
|
||||
this.undoChangeStackPointer = -1;
|
||||
this.undoableChangeStack = [];
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.resetUndoStack
|
||||
// Resets the undo stack, effectively clearing the undo/redo history
|
||||
svgedit.history.UndoManager.prototype.resetUndoStack = function() {
|
||||
this.undoStack = [];
|
||||
this.undoStackPointer = 0;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.getUndoStackSize
|
||||
// Returns:
|
||||
// Integer with the current size of the undo history stack
|
||||
svgedit.history.UndoManager.prototype.getUndoStackSize = function() {
|
||||
return this.undoStackPointer;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.getRedoStackSize
|
||||
// Returns:
|
||||
// Integer with the current size of the redo history stack
|
||||
svgedit.history.UndoManager.prototype.getRedoStackSize = function() {
|
||||
return this.undoStack.length - this.undoStackPointer;
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.getNextUndoCommandText
|
||||
// Returns:
|
||||
// String associated with the next undo command
|
||||
svgedit.history.UndoManager.prototype.getNextUndoCommandText = function() {
|
||||
return this.undoStackPointer > 0 ? this.undoStack[this.undoStackPointer-1].getText() : "";
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.getNextRedoCommandText
|
||||
// Returns:
|
||||
// String associated with the next redo command
|
||||
svgedit.history.UndoManager.prototype.getNextRedoCommandText = function() {
|
||||
return this.undoStackPointer < this.undoStack.length ? this.undoStack[this.undoStackPointer].getText() : "";
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.undo
|
||||
// Performs an undo step
|
||||
svgedit.history.UndoManager.prototype.undo = function() {
|
||||
if (this.undoStackPointer > 0) {
|
||||
var cmd = this.undoStack[--this.undoStackPointer];
|
||||
cmd.unapply(this.handler_);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.redo
|
||||
// Performs a redo step
|
||||
svgedit.history.UndoManager.prototype.redo = function() {
|
||||
if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {
|
||||
var cmd = this.undoStack[this.undoStackPointer++];
|
||||
cmd.apply(this.handler_);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.addCommandToHistory
|
||||
// Adds a command object to the undo history stack
|
||||
//
|
||||
// Parameters:
|
||||
// cmd - The command object to add
|
||||
svgedit.history.UndoManager.prototype.addCommandToHistory = function(cmd) {
|
||||
// FIXME: we MUST compress consecutive text changes to the same element
|
||||
// (right now each keystroke is saved as a separate command that includes the
|
||||
// entire text contents of the text element)
|
||||
// TODO: consider limiting the history that we store here (need to do some slicing)
|
||||
|
||||
// if our stack pointer is not at the end, then we have to remove
|
||||
// all commands after the pointer and insert the new command
|
||||
if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {
|
||||
this.undoStack = this.undoStack.splice(0, this.undoStackPointer);
|
||||
}
|
||||
this.undoStack.push(cmd);
|
||||
this.undoStackPointer = this.undoStack.length;
|
||||
};
|
||||
|
||||
|
||||
// Function: svgedit.history.UndoManager.beginUndoableChange
|
||||
// This function tells the canvas to remember the old values of the
|
||||
// attrName attribute for each element sent in. The elements and values
|
||||
// are stored on a stack, so the next call to finishUndoableChange() will
|
||||
// pop the elements and old values off the stack, gets the current values
|
||||
// from the DOM and uses all of these to construct the undo-able command.
|
||||
//
|
||||
// Parameters:
|
||||
// attrName - The name of the attribute being changed
|
||||
// elems - Array of DOM elements being changed
|
||||
svgedit.history.UndoManager.prototype.beginUndoableChange = function(attrName, elems) {
|
||||
var p = ++this.undoChangeStackPointer;
|
||||
var i = elems.length;
|
||||
var oldValues = new Array(i), elements = new Array(i);
|
||||
while (i--) {
|
||||
var elem = elems[i];
|
||||
if (elem == null) continue;
|
||||
elements[i] = elem;
|
||||
oldValues[i] = elem.getAttribute(attrName);
|
||||
}
|
||||
this.undoableChangeStack[p] = {'attrName': attrName,
|
||||
'oldValues': oldValues,
|
||||
'elements': elements};
|
||||
};
|
||||
|
||||
// Function: svgedit.history.UndoManager.finishUndoableChange
|
||||
// This function returns a BatchCommand object which summarizes the
|
||||
// change since beginUndoableChange was called. The command can then
|
||||
// be added to the command history
|
||||
//
|
||||
// Returns:
|
||||
// Batch command object with resulting changes
|
||||
svgedit.history.UndoManager.prototype.finishUndoableChange = function() {
|
||||
var p = this.undoChangeStackPointer--;
|
||||
var changeset = this.undoableChangeStack[p];
|
||||
var i = changeset['elements'].length;
|
||||
var attrName = changeset['attrName'];
|
||||
var batchCmd = new svgedit.history.BatchCommand("Change " + attrName);
|
||||
while (i--) {
|
||||
var elem = changeset['elements'][i];
|
||||
if (elem == null) continue;
|
||||
var changes = {};
|
||||
changes[attrName] = changeset['oldValues'][i];
|
||||
if (changes[attrName] != elem.getAttribute(attrName)) {
|
||||
batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes, attrName));
|
||||
}
|
||||
}
|
||||
this.undoableChangeStack[p] = null;
|
||||
return batchCmd;
|
||||
};
|
||||
|
||||
|
||||
})();
|
|
@ -1,7 +1,11 @@
|
|||
/*
|
||||
* jGraduate Default CSS
|
||||
*
|
||||
* Copyright (c) 2009 Jeff Schiller
|
||||
* Copyright (c) 2010 Jeff Schiller
|
||||
* http://blog.codedread.com/
|
||||
*
|
||||
* Copyright (c) 2010 Alexis Deveria
|
||||
* http://a.deveria.com/
|
||||
*
|
||||
* Licensed under the Apache License Version 2
|
||||
*/
|
||||
|
@ -48,14 +52,14 @@ li.jGraduate_tab_current {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.jGraduate_lgPick {
|
||||
.jGraduate_gradPick {
|
||||
display: none;
|
||||
border: outset 1px #666;
|
||||
padding: 10px 7px 5px 5px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.jGraduate_rgPick {
|
||||
.jGraduate_gradPick {
|
||||
display: none;
|
||||
border: outset 1px #666;
|
||||
padding: 10px 7px 5px 5px;
|
||||
|
@ -80,6 +84,30 @@ div.jGraduate_GradContainer {
|
|||
background-image: url(../images/map-opacity.png);
|
||||
background-position: 0px 0px;
|
||||
height: 256px;
|
||||
width: 256px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.jGraduate_GradContainer div.grad_coord {
|
||||
background: #000;
|
||||
border: 1px solid #fff;
|
||||
z-index: 2;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
margin: -5px -5px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
font-size: xx-small;
|
||||
line-height: 10px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.jGraduate_AlphaArrows {
|
||||
|
@ -98,16 +126,14 @@ div.jGraduate_Opacity {
|
|||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
div.lg_jGraduate_OpacityField {
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
left: 292px;
|
||||
}
|
||||
|
||||
div.jGraduate_Form {
|
||||
float: left;
|
||||
width: 140px;
|
||||
margin: -3px 3px 0px 4px;
|
||||
div.jGraduate_StopSlider {
|
||||
/* border: 2px inset #eee;*/
|
||||
margin: 0 0 0 -10px;
|
||||
width: 276px;
|
||||
overflow: visible;
|
||||
background: #efefef;
|
||||
height: 45px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.jGraduate_StopSection {
|
||||
|
@ -115,49 +141,8 @@ div.jGraduate_StopSection {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
div.jGraduate_RadiusField {
|
||||
|
||||
text-align: center;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.jGraduate_RadiusField input {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.jGraduate_RadiusField .jGraduate_Form_Section {
|
||||
width: 250px;
|
||||
padding: 2px;
|
||||
height: 80px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.jGraduate_Form_Section input[type=text] {
|
||||
width: 38px;
|
||||
}
|
||||
|
||||
.jGraduate_Radius {
|
||||
border:1px solid #BBB;
|
||||
cursor:ew-resize;
|
||||
height:20px;
|
||||
margin-top:14px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.jGraduate_RadiusArrows {
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
margin-top: -10px;
|
||||
margin-left: 250.5px;
|
||||
}
|
||||
|
||||
|
||||
div.jGraduate_OkCancel {
|
||||
float: left;
|
||||
width: 113px;
|
||||
}
|
||||
|
||||
input.jGraduate_Ok, input.jGraduate_Cancel {
|
||||
display: block;
|
||||
|
@ -199,12 +184,21 @@ div.jGraduate_Form_Section {
|
|||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
padding: 15px 5px 5px 5px;
|
||||
margin: 2px;
|
||||
margin: 5px 2px;
|
||||
width: 110px;
|
||||
text-align: center;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
div.jGraduate_Form_Section label {
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
div.jGraduate_StopSection input[type=text],
|
||||
div.jGraduate_Slider input[type=text] {
|
||||
width: 33px;
|
||||
}
|
||||
|
||||
div.jGraduate_LightBox {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
|
@ -223,17 +217,47 @@ div.jGraduate_stopPicker {
|
|||
}
|
||||
|
||||
|
||||
.jGraduate_rgPick {
|
||||
width: 530px;
|
||||
.jGraduate_gradPick {
|
||||
width: 535px;
|
||||
}
|
||||
|
||||
.jGraduate_rgPick div.jGraduate_Form {
|
||||
.jGraduate_gradPick div.jGraduate_OpacField {
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 5px;
|
||||
/*
|
||||
width: 270px;
|
||||
|
||||
left: 284px;
|
||||
width: 266px;
|
||||
height: 200px;
|
||||
top: 167px;
|
||||
margin: -3px 3px 0px 4px;
|
||||
*/
|
||||
}
|
||||
|
||||
.jGraduate_gradPick .jGraduate_Form {
|
||||
float: left;
|
||||
width: 270px;
|
||||
position: absolute;
|
||||
left: 284px;
|
||||
width: 266px;
|
||||
top: 130px;
|
||||
margin: -3px 3px 0px 4px;
|
||||
height: 200px;
|
||||
top: 167px;
|
||||
margin: -3px 3px 0px 10px;
|
||||
}
|
||||
|
||||
.jGraduate_gradPick .jGraduate_Points {
|
||||
position: static;
|
||||
width: 150px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.jGraduate_SpreadMethod {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
.jGraduate_Colorblocks {
|
||||
|
@ -252,19 +276,77 @@ div.jGraduate_stopPicker {
|
|||
float: none;
|
||||
}
|
||||
|
||||
.jGraduate_rgPick div.jGraduate_StopSection {
|
||||
.jGraduate_gradPick div.jGraduate_StopSection {
|
||||
float: left;
|
||||
width: 133px;
|
||||
margin: 0;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.jGraduate_rgPick .jGraduate_OkCancel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
.jGraduate_gradPick .jGraduate_Form_Section {
|
||||
padding-top: 9px;
|
||||
}
|
||||
|
||||
.rg_jGraduate_OpacityField {
|
||||
|
||||
.jGraduate_Slider {
|
||||
text-align: center;
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.jGraduate_Slider .jGraduate_Form_Section {
|
||||
border: none;
|
||||
width: 250px;
|
||||
padding: 0 2px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.jGraduate_Slider label {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
line-height: 50px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.jGraduate_Slider label.prelabel {
|
||||
width: 40px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.jGraduate_SliderBar {
|
||||
width: 140px;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
border:1px solid #BBB;
|
||||
height:20px;
|
||||
margin-top:14px;
|
||||
margin-left:5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.jGraduate_Slider input {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
div.jGraduate_Slider img {
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
left: 288px;
|
||||
bottom: 24px;
|
||||
margin-top: -10px;
|
||||
cursor:ew-resize;
|
||||
}
|
||||
|
||||
|
||||
.jGraduate_gradPick .jGraduate_OkCancel {
|
||||
position: absolute;
|
||||
top: 39px;
|
||||
right: 10px;
|
||||
width: 113px;
|
||||
|
||||
}
|
||||
|
||||
.jGraduate_OpacField {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
bottom: 0;
|
||||
}
|
File diff suppressed because it is too large
Load diff
234
public/svg-edit/editor/math.js
Normal file
234
public/svg-edit/editor/math.js
Normal file
|
@ -0,0 +1,234 @@
|
|||
/**
|
||||
* Package: svedit.math
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// None.
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
|
||||
if (!svgedit.math) {
|
||||
svgedit.math = {};
|
||||
}
|
||||
|
||||
// Constants
|
||||
var NEAR_ZERO = 1e-14;
|
||||
|
||||
// Throw away SVGSVGElement used for creating matrices/transforms.
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
|
||||
// Function: svgedit.math.transformPoint
|
||||
// A (hopefully) quicker function to transform a point by a matrix
|
||||
// (this function avoids any DOM calls and just does the math)
|
||||
//
|
||||
// Parameters:
|
||||
// x - Float representing the x coordinate
|
||||
// y - Float representing the y coordinate
|
||||
// m - Matrix object to transform the point with
|
||||
// Returns a x,y object representing the transformed point
|
||||
svgedit.math.transformPoint = function(x, y, m) {
|
||||
return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f};
|
||||
};
|
||||
|
||||
|
||||
// Function: svgedit.math.isIdentity
|
||||
// Helper function to check if the matrix performs no actual transform
|
||||
// (i.e. exists for identity purposes)
|
||||
//
|
||||
// Parameters:
|
||||
// m - The matrix object to check
|
||||
//
|
||||
// Returns:
|
||||
// Boolean indicating whether or not the matrix is 1,0,0,1,0,0
|
||||
svgedit.math.isIdentity = function(m) {
|
||||
return (m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0);
|
||||
};
|
||||
|
||||
|
||||
// Function: svgedit.math.matrixMultiply
|
||||
// This function tries to return a SVGMatrix that is the multiplication m1*m2.
|
||||
// We also round to zero when it's near zero
|
||||
//
|
||||
// Parameters:
|
||||
// >= 2 Matrix objects to multiply
|
||||
//
|
||||
// Returns:
|
||||
// The matrix object resulting from the calculation
|
||||
svgedit.math.matrixMultiply = function() {
|
||||
var args = arguments, i = args.length, m = args[i-1];
|
||||
|
||||
while(i-- > 1) {
|
||||
var m1 = args[i-1];
|
||||
m = m1.multiply(m);
|
||||
}
|
||||
if (Math.abs(m.a) < NEAR_ZERO) m.a = 0;
|
||||
if (Math.abs(m.b) < NEAR_ZERO) m.b = 0;
|
||||
if (Math.abs(m.c) < NEAR_ZERO) m.c = 0;
|
||||
if (Math.abs(m.d) < NEAR_ZERO) m.d = 0;
|
||||
if (Math.abs(m.e) < NEAR_ZERO) m.e = 0;
|
||||
if (Math.abs(m.f) < NEAR_ZERO) m.f = 0;
|
||||
|
||||
return m;
|
||||
};
|
||||
|
||||
// Function: svgedit.math.hasMatrixTransform
|
||||
// See if the given transformlist includes a non-indentity matrix transform
|
||||
//
|
||||
// Parameters:
|
||||
// tlist - The transformlist to check
|
||||
//
|
||||
// Returns:
|
||||
// Boolean on whether or not a matrix transform was found
|
||||
svgedit.math.hasMatrixTransform = function(tlist) {
|
||||
if(!tlist) return false;
|
||||
var num = tlist.numberOfItems;
|
||||
while (num--) {
|
||||
var xform = tlist.getItem(num);
|
||||
if (xform.type == 1 && !svgedit.math.isIdentity(xform.matrix)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Function: svgedit.math.transformBox
|
||||
// Transforms a rectangle based on the given matrix
|
||||
//
|
||||
// Parameters:
|
||||
// l - Float with the box's left coordinate
|
||||
// t - Float with the box's top coordinate
|
||||
// w - Float with the box width
|
||||
// h - Float with the box height
|
||||
// m - Matrix object to transform the box by
|
||||
//
|
||||
// Returns:
|
||||
// An object with the following values:
|
||||
// * tl - The top left coordinate (x,y object)
|
||||
// * tr - The top right coordinate (x,y object)
|
||||
// * bl - The bottom left coordinate (x,y object)
|
||||
// * br - The bottom right coordinate (x,y object)
|
||||
// * aabox - Object with the following values:
|
||||
// * Float with the axis-aligned x coordinate
|
||||
// * Float with the axis-aligned y coordinate
|
||||
// * Float with the axis-aligned width coordinate
|
||||
// * Float with the axis-aligned height coordinate
|
||||
svgedit.math.transformBox = function(l, t, w, h, m) {
|
||||
var topleft = {x:l,y:t},
|
||||
topright = {x:(l+w),y:t},
|
||||
botright = {x:(l+w),y:(t+h)},
|
||||
botleft = {x:l,y:(t+h)};
|
||||
var transformPoint = svgedit.math.transformPoint;
|
||||
topleft = transformPoint( topleft.x, topleft.y, m );
|
||||
var minx = topleft.x,
|
||||
maxx = topleft.x,
|
||||
miny = topleft.y,
|
||||
maxy = topleft.y;
|
||||
topright = transformPoint( topright.x, topright.y, m );
|
||||
minx = Math.min(minx, topright.x);
|
||||
maxx = Math.max(maxx, topright.x);
|
||||
miny = Math.min(miny, topright.y);
|
||||
maxy = Math.max(maxy, topright.y);
|
||||
botleft = transformPoint( botleft.x, botleft.y, m);
|
||||
minx = Math.min(minx, botleft.x);
|
||||
maxx = Math.max(maxx, botleft.x);
|
||||
miny = Math.min(miny, botleft.y);
|
||||
maxy = Math.max(maxy, botleft.y);
|
||||
botright = transformPoint( botright.x, botright.y, m );
|
||||
minx = Math.min(minx, botright.x);
|
||||
maxx = Math.max(maxx, botright.x);
|
||||
miny = Math.min(miny, botright.y);
|
||||
maxy = Math.max(maxy, botright.y);
|
||||
|
||||
return {tl:topleft, tr:topright, bl:botleft, br:botright,
|
||||
aabox: {x:minx, y:miny, width:(maxx-minx), height:(maxy-miny)} };
|
||||
};
|
||||
|
||||
// Function: svgedit.math.transformListToTransform
|
||||
// This returns a single matrix Transform for a given Transform List
|
||||
// (this is the equivalent of SVGTransformList.consolidate() but unlike
|
||||
// that method, this one does not modify the actual SVGTransformList)
|
||||
// This function is very liberal with its min,max arguments
|
||||
//
|
||||
// Parameters:
|
||||
// tlist - The transformlist object
|
||||
// min - Optional integer indicating start transform position
|
||||
// max - Optional integer indicating end transform position
|
||||
//
|
||||
// Returns:
|
||||
// A single matrix transform object
|
||||
svgedit.math.transformListToTransform = function(tlist, min, max) {
|
||||
if(tlist == null) {
|
||||
// Or should tlist = null have been prevented before this?
|
||||
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix());
|
||||
}
|
||||
var min = min == undefined ? 0 : min;
|
||||
var max = max == undefined ? (tlist.numberOfItems-1) : max;
|
||||
min = parseInt(min);
|
||||
max = parseInt(max);
|
||||
if (min > max) { var temp = max; max = min; min = temp; }
|
||||
var m = svg.createSVGMatrix();
|
||||
for (var i = min; i <= max; ++i) {
|
||||
// if our indices are out of range, just use a harmless identity matrix
|
||||
var mtom = (i >= 0 && i < tlist.numberOfItems ?
|
||||
tlist.getItem(i).matrix :
|
||||
svg.createSVGMatrix());
|
||||
m = svgedit.math.matrixMultiply(m, mtom);
|
||||
}
|
||||
return svg.createSVGTransformFromMatrix(m);
|
||||
};
|
||||
|
||||
|
||||
// Function: svgedit.math.snapToAngle
|
||||
// Returns a 45 degree angle coordinate associated with the two given
|
||||
// coordinates
|
||||
//
|
||||
// Parameters:
|
||||
// x1 - First coordinate's x value
|
||||
// x2 - Second coordinate's x value
|
||||
// y1 - First coordinate's y value
|
||||
// y2 - Second coordinate's y value
|
||||
//
|
||||
// Returns:
|
||||
// Object with the following values:
|
||||
// x - The angle-snapped x value
|
||||
// y - The angle-snapped y value
|
||||
// snapangle - The angle at which to snap
|
||||
svgedit.math.snapToAngle = function(x1,y1,x2,y2) {
|
||||
var snap = Math.PI/4; // 45 degrees
|
||||
var dx = x2 - x1;
|
||||
var dy = y2 - y1;
|
||||
var angle = Math.atan2(dy,dx);
|
||||
var dist = Math.sqrt(dx * dx + dy * dy);
|
||||
var snapangle= Math.round(angle/snap)*snap;
|
||||
var x = x1 + dist*Math.cos(snapangle);
|
||||
var y = y1 + dist*Math.sin(snapangle);
|
||||
//console.log(x1,y1,x2,y2,x,y,angle)
|
||||
return {x:x, y:y, a:snapangle};
|
||||
};
|
||||
|
||||
|
||||
// Function: rectsIntersect
|
||||
// Check if two rectangles (BBoxes objects) intersect each other
|
||||
//
|
||||
// Paramaters:
|
||||
// r1 - The first BBox-like object
|
||||
// r2 - The second BBox-like object
|
||||
//
|
||||
// Returns:
|
||||
// Boolean that's true if rectangles intersect
|
||||
svgedit.math.rectsIntersect = function(r1, r2) {
|
||||
return r2.x < (r1.x+r1.width) &&
|
||||
(r2.x+r2.width) > r1.x &&
|
||||
r2.y < (r1.y+r1.height) &&
|
||||
(r2.y+r2.height) > r1.y;
|
||||
};
|
||||
|
||||
|
||||
})();
|
274
public/svg-edit/editor/sanitize.js
Normal file
274
public/svg-edit/editor/sanitize.js
Normal file
|
@ -0,0 +1,274 @@
|
|||
/**
|
||||
* Package: svgedit.sanitize
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) browsersupport.js
|
||||
// 2) svgutils.js
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
|
||||
if (!svgedit.sanitize) {
|
||||
svgedit.sanitize = {};
|
||||
}
|
||||
|
||||
// Namespace constants
|
||||
var svgns = "http://www.w3.org/2000/svg",
|
||||
xlinkns = "http://www.w3.org/1999/xlink",
|
||||
xmlns = "http://www.w3.org/XML/1998/namespace",
|
||||
xmlnsns = "http://www.w3.org/2000/xmlns/", // see http://www.w3.org/TR/REC-xml-names/#xmlReserved
|
||||
se_ns = "http://svg-edit.googlecode.com",
|
||||
htmlns = "http://www.w3.org/1999/xhtml",
|
||||
mathns = "http://www.w3.org/1998/Math/MathML";
|
||||
|
||||
// map namespace URIs to prefixes
|
||||
var nsMap_ = {};
|
||||
nsMap_[xlinkns] = 'xlink';
|
||||
nsMap_[xmlns] = 'xml';
|
||||
nsMap_[xmlnsns] = 'xmlns';
|
||||
nsMap_[se_ns] = 'se';
|
||||
nsMap_[htmlns] = 'xhtml';
|
||||
nsMap_[mathns] = 'mathml';
|
||||
|
||||
// map prefixes to namespace URIs
|
||||
var nsRevMap_ = {};
|
||||
$.each(nsMap_, function(key,value){
|
||||
nsRevMap_[value] = key;
|
||||
});
|
||||
|
||||
// this defines which elements and attributes that we support
|
||||
var svgWhiteList_ = {
|
||||
// SVG Elements
|
||||
"a": ["class", "clip-path", "clip-rule", "fill", "fill-opacity", "fill-rule", "filter", "id", "mask", "opacity", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform", "xlink:href", "xlink:title"],
|
||||
"circle": ["class", "clip-path", "clip-rule", "cx", "cy", "fill", "fill-opacity", "fill-rule", "filter", "id", "mask", "opacity", "r", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
|
||||
"clipPath": ["class", "clipPathUnits", "id"],
|
||||
"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", "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", "font-family", "font-size", "font-style", "font-weight", "text-anchor"],
|
||||
"image": ["class", "clip-path", "clip-rule", "filter", "height", "id", "mask", "opacity", "requiredFeatures", "style", "systemLanguage", "transform", "width", "x", "xlink:href", "xlink:title", "y"],
|
||||
"line": ["class", "clip-path", "clip-rule", "fill", "fill-opacity", "fill-rule", "filter", "id", "marker-end", "marker-mid", "marker-start", "mask", "opacity", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform", "x1", "x2", "y1", "y2"],
|
||||
"linearGradient": ["class", "id", "gradientTransform", "gradientUnits", "requiredFeatures", "spreadMethod", "systemLanguage", "x1", "x2", "xlink:href", "y1", "y2"],
|
||||
"marker": ["id", "class", "markerHeight", "markerUnits", "markerWidth", "orient", "preserveAspectRatio", "refX", "refY", "systemLanguage", "viewBox"],
|
||||
"mask": ["class", "height", "id", "maskContentUnits", "maskUnits", "width", "x", "y"],
|
||||
"metadata": ["class", "id"],
|
||||
"path": ["class", "clip-path", "clip-rule", "d", "fill", "fill-opacity", "fill-rule", "filter", "id", "marker-end", "marker-mid", "marker-start", "mask", "opacity", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
|
||||
"pattern": ["class", "height", "id", "patternContentUnits", "patternTransform", "patternUnits", "requiredFeatures", "style", "systemLanguage", "viewBox", "width", "x", "xlink:href", "y"],
|
||||
"polygon": ["class", "clip-path", "clip-rule", "id", "fill", "fill-opacity", "fill-rule", "filter", "id", "class", "marker-end", "marker-mid", "marker-start", "mask", "opacity", "points", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
|
||||
"polyline": ["class", "clip-path", "clip-rule", "id", "fill", "fill-opacity", "fill-rule", "filter", "marker-end", "marker-mid", "marker-start", "mask", "opacity", "points", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform"],
|
||||
"radialGradient": ["class", "cx", "cy", "fx", "fy", "gradientTransform", "gradientUnits", "id", "r", "requiredFeatures", "spreadMethod", "systemLanguage", "xlink:href"],
|
||||
"rect": ["class", "clip-path", "clip-rule", "fill", "fill-opacity", "fill-rule", "filter", "height", "id", "mask", "opacity", "requiredFeatures", "rx", "ry", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform", "width", "x", "y"],
|
||||
"stop": ["class", "id", "offset", "requiredFeatures", "stop-color", "stop-opacity", "style", "systemLanguage"],
|
||||
"svg": ["class", "clip-path", "clip-rule", "filter", "id", "height", "mask", "preserveAspectRatio", "requiredFeatures", "style", "systemLanguage", "viewBox", "width", "x", "xmlns", "xmlns:se", "xmlns:xlink", "y"],
|
||||
"switch": ["class", "id", "requiredFeatures", "systemLanguage"],
|
||||
"symbol": ["class", "fill", "fill-opacity", "fill-rule", "filter", "font-family", "font-size", "font-style", "font-weight", "id", "opacity", "preserveAspectRatio", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "transform", "viewBox"],
|
||||
"text": ["class", "clip-path", "clip-rule", "fill", "fill-opacity", "fill-rule", "filter", "font-family", "font-size", "font-style", "font-weight", "id", "mask", "opacity", "requiredFeatures", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "text-anchor", "transform", "x", "xml:space", "y"],
|
||||
"textPath": ["class", "id", "method", "requiredFeatures", "spacing", "startOffset", "style", "systemLanguage", "transform", "xlink:href"],
|
||||
"title": [],
|
||||
"tspan": ["class", "clip-path", "clip-rule", "dx", "dy", "fill", "fill-opacity", "fill-rule", "filter", "font-family", "font-size", "font-style", "font-weight", "id", "mask", "opacity", "requiredFeatures", "rotate", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "systemLanguage", "text-anchor", "textLength", "transform", "x", "xml:space", "y"],
|
||||
"use": ["class", "clip-path", "clip-rule", "fill", "fill-opacity", "fill-rule", "filter", "height", "id", "mask", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "style", "transform", "width", "x", "xlink:href", "y"],
|
||||
|
||||
// MathML Elements
|
||||
"annotation": ["encoding"],
|
||||
"annotation-xml": ["encoding"],
|
||||
"maction": ["actiontype", "other", "selection"],
|
||||
"math": ["class", "id", "display", "xmlns"],
|
||||
"menclose": ["notation"],
|
||||
"merror": [],
|
||||
"mfrac": ["linethickness"],
|
||||
"mi": ["mathvariant"],
|
||||
"mmultiscripts": [],
|
||||
"mn": [],
|
||||
"mo": ["fence", "lspace", "maxsize", "minsize", "rspace", "stretchy"],
|
||||
"mover": [],
|
||||
"mpadded": ["lspace", "width"],
|
||||
"mphantom": [],
|
||||
"mprescripts": [],
|
||||
"mroot": [],
|
||||
"mrow": ["xlink:href", "xlink:type", "xmlns:xlink"],
|
||||
"mspace": ["depth", "height", "width"],
|
||||
"msqrt": [],
|
||||
"mstyle": ["displaystyle", "mathbackground", "mathcolor", "mathvariant", "scriptlevel"],
|
||||
"msub": [],
|
||||
"msubsup": [],
|
||||
"msup": [],
|
||||
"mtable": ["align", "columnalign", "columnlines", "columnspacing", "displaystyle", "equalcolumns", "equalrows", "frame", "rowalign", "rowlines", "rowspacing", "width"],
|
||||
"mtd": ["columnalign", "columnspan", "rowalign", "rowspan"],
|
||||
"mtext": [],
|
||||
"mtr": ["columnalign", "rowalign"],
|
||||
"munder": [],
|
||||
"munderover": [],
|
||||
"none": [],
|
||||
"semantics": []
|
||||
};
|
||||
|
||||
// Produce a Namespace-aware version of svgWhitelist
|
||||
var svgWhiteListNS_ = {};
|
||||
$.each(svgWhiteList_, function(elt,atts){
|
||||
var attNS = {};
|
||||
$.each(atts, function(i, att){
|
||||
if (att.indexOf(':') >= 0) {
|
||||
var v = att.split(':');
|
||||
attNS[v[1]] = nsRevMap_[v[0]];
|
||||
} else {
|
||||
attNS[att] = att == 'xmlns' ? xmlnsns : null;
|
||||
}
|
||||
});
|
||||
svgWhiteListNS_[elt] = attNS;
|
||||
});
|
||||
|
||||
// temporarily expose these
|
||||
svgedit.sanitize.getNSMap = function() { return nsMap_; }
|
||||
|
||||
// Function: svgedit.sanitize.sanitizeSvg
|
||||
// Sanitizes the input node and its children
|
||||
// It only keeps what is allowed from our whitelist defined above
|
||||
//
|
||||
// Parameters:
|
||||
// node - The DOM element to be checked, will also check its children
|
||||
svgedit.sanitize.sanitizeSvg = function(node) {
|
||||
// we only care about element nodes
|
||||
// automatically return for all comment, etc nodes
|
||||
// for text, we do a whitespace trim
|
||||
if (node.nodeType == 3) {
|
||||
node.nodeValue = node.nodeValue.replace(/^\s+|\s+$/g, "");
|
||||
// Remove empty text nodes
|
||||
if(!node.nodeValue.length) node.parentNode.removeChild(node);
|
||||
}
|
||||
if (node.nodeType != 1) return;
|
||||
var doc = node.ownerDocument;
|
||||
var parent = node.parentNode;
|
||||
// can parent ever be null here? I think the root node's parent is the document...
|
||||
if (!doc || !parent) return;
|
||||
|
||||
var allowedAttrs = svgWhiteList_[node.nodeName];
|
||||
var allowedAttrsNS = svgWhiteListNS_[node.nodeName];
|
||||
|
||||
// if this element is allowed
|
||||
if (allowedAttrs != undefined) {
|
||||
|
||||
var se_attrs = [];
|
||||
|
||||
var i = node.attributes.length;
|
||||
while (i--) {
|
||||
// if the attribute is not in our whitelist, then remove it
|
||||
// could use jQuery's inArray(), but I don't know if that's any better
|
||||
var attr = node.attributes.item(i);
|
||||
var attrName = attr.nodeName;
|
||||
var attrLocalName = attr.localName;
|
||||
var attrNsURI = attr.namespaceURI;
|
||||
// Check that an attribute with the correct localName in the correct namespace is on
|
||||
// our whitelist or is a namespace declaration for one of our allowed namespaces
|
||||
if (!(allowedAttrsNS.hasOwnProperty(attrLocalName) && attrNsURI == allowedAttrsNS[attrLocalName] && attrNsURI != xmlnsns) &&
|
||||
!(attrNsURI == xmlnsns && nsMap_[attr.nodeValue]) )
|
||||
{
|
||||
// TODO(codedread): Programmatically add the se: attributes to the NS-aware whitelist.
|
||||
// Bypassing the whitelist to allow se: prefixes. Is there
|
||||
// a more appropriate way to do this?
|
||||
if(attrName.indexOf('se:') == 0) {
|
||||
se_attrs.push([attrName, attr.nodeValue]);
|
||||
}
|
||||
node.removeAttributeNS(attrNsURI, attrLocalName);
|
||||
}
|
||||
|
||||
// Add spaces before negative signs where necessary
|
||||
if(svgedit.browsersupport.isGecko()) {
|
||||
switch ( attrName ) {
|
||||
case "transform":
|
||||
case "gradientTransform":
|
||||
case "patternTransform":
|
||||
var val = attr.nodeValue.replace(/(\d)-/g, "$1 -");
|
||||
node.setAttribute(attrName, val);
|
||||
}
|
||||
}
|
||||
|
||||
// for the style attribute, rewrite it in terms of XML presentational attributes
|
||||
if (attrName == "style") {
|
||||
var props = attr.nodeValue.split(";"),
|
||||
p = props.length;
|
||||
while(p--) {
|
||||
var nv = props[p].split(":");
|
||||
// now check that this attribute is supported
|
||||
if (allowedAttrs.indexOf(nv[0]) >= 0) {
|
||||
node.setAttribute(nv[0],nv[1]);
|
||||
}
|
||||
}
|
||||
node.removeAttribute('style');
|
||||
}
|
||||
}
|
||||
|
||||
$.each(se_attrs, function(i, attr) {
|
||||
node.setAttributeNS(se_ns, attr[0], attr[1]);
|
||||
});
|
||||
|
||||
// for some elements that have a xlink:href, ensure the URI refers to a local element
|
||||
// (but not for links)
|
||||
var href = svgedit.utilities.getHref(node);
|
||||
if(href &&
|
||||
["filter", "linearGradient", "pattern",
|
||||
"radialGradient", "textPath", "use"].indexOf(node.nodeName) >= 0)
|
||||
{
|
||||
// TODO: we simply check if the first character is a #, is this bullet-proof?
|
||||
if (href[0] != "#") {
|
||||
// remove the attribute (but keep the element)
|
||||
svgedit.utilities.setHref(node, "");
|
||||
node.removeAttributeNS(xlinkns, "href");
|
||||
}
|
||||
}
|
||||
|
||||
// Safari crashes on a <use> without a xlink:href, so we just remove the node here
|
||||
if (node.nodeName == "use" && !svgedit.utilities.getHref(node)) {
|
||||
parent.removeChild(node);
|
||||
return;
|
||||
}
|
||||
// if the element has attributes pointing to a non-local reference,
|
||||
// need to remove the attribute
|
||||
$.each(["clip-path", "fill", "filter", "marker-end", "marker-mid", "marker-start", "mask", "stroke"],function(i,attr) {
|
||||
var val = node.getAttribute(attr);
|
||||
if (val) {
|
||||
val = svgedit.utilities.getUrlFromAttr(val);
|
||||
// simply check for first character being a '#'
|
||||
if (val && val[0] !== "#") {
|
||||
node.setAttribute(attr, "");
|
||||
node.removeAttribute(attr);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// recurse to children
|
||||
i = node.childNodes.length;
|
||||
while (i--) { svgedit.sanitize.sanitizeSvg(node.childNodes.item(i)); }
|
||||
}
|
||||
// else, remove this element
|
||||
else {
|
||||
// remove all children from this node and insert them before this node
|
||||
// FIXME: in the case of animation elements this will hardly ever be correct
|
||||
var children = [];
|
||||
while (node.hasChildNodes()) {
|
||||
children.push(parent.insertBefore(node.firstChild, node));
|
||||
}
|
||||
|
||||
// remove this node from the document altogether
|
||||
parent.removeChild(node);
|
||||
|
||||
// call sanitizeSvg on each of those children
|
||||
var i = children.length;
|
||||
while (i--) { svgedit.sanitize.sanitizeSvg(children[i]); }
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
531
public/svg-edit/editor/select.js
Normal file
531
public/svg-edit/editor/select.js
Normal file
|
@ -0,0 +1,531 @@
|
|||
/**
|
||||
* Package: svedit.select
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) jQuery
|
||||
// 2) browsersupport.js
|
||||
// 3) math.js
|
||||
// 4) svgutils.js
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
|
||||
if (!svgedit.select) {
|
||||
svgedit.select = {};
|
||||
}
|
||||
|
||||
var svgFactory_;
|
||||
var config_;
|
||||
var selectorManager_; // A Singleton
|
||||
|
||||
// Class: svgedit.select.Selector
|
||||
// Private class for DOM element selection boxes
|
||||
//
|
||||
// Parameters:
|
||||
// id - integer to internally indentify the selector
|
||||
// elem - DOM element associated with this selector
|
||||
svgedit.select.Selector = function(id, elem) {
|
||||
// this is the selector's unique number
|
||||
this.id = id;
|
||||
|
||||
// this holds a reference to the element for which this selector is being used
|
||||
this.selectedElement = elem;
|
||||
|
||||
// this is a flag used internally to track whether the selector is being used or not
|
||||
this.locked = true;
|
||||
|
||||
// this holds a reference to the <g> element that holds all visual elements of the selector
|
||||
this.selectorGroup = svgFactory_.createSVGElement({
|
||||
'element': 'g',
|
||||
'attr': {'id': ('selectorGroup' + this.id)}
|
||||
});
|
||||
|
||||
// this holds a reference to the path rect
|
||||
this.selectorRect = this.selectorGroup.appendChild(
|
||||
svgFactory_.createSVGElement({
|
||||
'element': 'path',
|
||||
'attr': {
|
||||
'id': ('selectedBox' + this.id),
|
||||
'fill': 'none',
|
||||
'stroke': '#22C',
|
||||
'stroke-width': '1',
|
||||
'stroke-dasharray': '5,5',
|
||||
// need to specify this so that the rect is not selectable
|
||||
'style': 'pointer-events:none'
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// this holds a reference to the grip coordinates for this selector
|
||||
this.gripCoords = {
|
||||
'nw': null,
|
||||
'n' : null,
|
||||
'ne': null,
|
||||
'e' : null,
|
||||
'se': null,
|
||||
's' : null,
|
||||
'sw': null,
|
||||
'w' : null
|
||||
};
|
||||
|
||||
this.reset(this.selectedElement);
|
||||
};
|
||||
|
||||
|
||||
// Function: svgedit.select.Selector.reset
|
||||
// Used to reset the id and element that the selector is attached to
|
||||
//
|
||||
// Parameters:
|
||||
// e - DOM element associated with this selector
|
||||
svgedit.select.Selector.prototype.reset = function(e) {
|
||||
this.locked = true;
|
||||
this.selectedElement = e;
|
||||
this.resize();
|
||||
this.selectorGroup.setAttribute('display', 'inline');
|
||||
};
|
||||
|
||||
// Function: svgedit.select.Selector.updateGripCursors
|
||||
// Updates cursors for corner grips on rotation so arrows point the right way
|
||||
//
|
||||
// Parameters:
|
||||
// angle - Float indicating current rotation angle in degrees
|
||||
svgedit.select.Selector.prototype.updateGripCursors = function(angle) {
|
||||
var dir_arr = [];
|
||||
var steps = Math.round(angle / 45);
|
||||
if(steps < 0) steps += 8;
|
||||
for (var dir in selectorManager_.selectorGrips) {
|
||||
dir_arr.push(dir);
|
||||
}
|
||||
while(steps > 0) {
|
||||
dir_arr.push(dir_arr.shift());
|
||||
steps--;
|
||||
}
|
||||
var i = 0;
|
||||
for (var dir in selectorManager_.selectorGrips) {
|
||||
selectorManager_.selectorGrips[dir].setAttribute('style', ('cursor:' + dir_arr[i] + '-resize'));
|
||||
i++;
|
||||
};
|
||||
};
|
||||
|
||||
// Function: svgedit.select.Selector.showGrips
|
||||
// Show the resize grips of this selector
|
||||
//
|
||||
// Parameters:
|
||||
// show - boolean indicating whether grips should be shown or not
|
||||
svgedit.select.Selector.prototype.showGrips = function(show) {
|
||||
// TODO: use suspendRedraw() here
|
||||
var bShow = show ? 'inline' : 'none';
|
||||
selectorManager_.selectorGripsGroup.setAttribute('display', bShow);
|
||||
var elem = this.selectedElement;
|
||||
this.hasGrips = show;
|
||||
if(elem && show) {
|
||||
this.selectorGroup.appendChild(selectorManager_.selectorGripsGroup);
|
||||
this.updateGripCursors(svgedit.utilities.getRotationAngle(elem));
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.select.Selector.resize
|
||||
// Updates the selector to match the element's size
|
||||
svgedit.select.Selector.prototype.resize = function() {
|
||||
var selectedBox = this.selectorRect,
|
||||
mgr = selectorManager_,
|
||||
selectedGrips = mgr.selectorGrips,
|
||||
selected = this.selectedElement,
|
||||
sw = selected.getAttribute('stroke-width'),
|
||||
current_zoom = svgFactory_.currentZoom();
|
||||
var offset = 1/current_zoom;
|
||||
if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) {
|
||||
offset += (sw/2);
|
||||
}
|
||||
|
||||
var tagName = selected.tagName;
|
||||
if (tagName === 'text') {
|
||||
offset += 2/current_zoom;
|
||||
}
|
||||
|
||||
// loop and transform our bounding box until we reach our first rotation
|
||||
var tlist = svgedit.transformlist.getTransformList(selected);
|
||||
var m = svgedit.math.transformListToTransform(tlist).matrix;
|
||||
|
||||
// This should probably be handled somewhere else, but for now
|
||||
// it keeps the selection box correctly positioned when zoomed
|
||||
m.e *= current_zoom;
|
||||
m.f *= current_zoom;
|
||||
|
||||
var bbox = svgedit.utilities.getBBox(selected);
|
||||
if(tagName === 'g' && !$.data(selected, 'gsvg')) {
|
||||
// The bbox for a group does not include stroke vals, so we
|
||||
// get the bbox based on its children.
|
||||
var stroked_bbox = svgFactory_.getStrokedBBox(selected.childNodes);
|
||||
if(stroked_bbox) {
|
||||
bbox = stroked_bbox;
|
||||
}
|
||||
}
|
||||
|
||||
// apply the transforms
|
||||
var l=bbox.x, t=bbox.y, w=bbox.width, h=bbox.height,
|
||||
bbox = {x:l, y:t, width:w, height:h};
|
||||
|
||||
// we need to handle temporary transforms too
|
||||
// if skewed, get its transformed box, then find its axis-aligned bbox
|
||||
|
||||
//*
|
||||
offset *= current_zoom;
|
||||
|
||||
var nbox = svgedit.math.transformBox(l*current_zoom, t*current_zoom, w*current_zoom, h*current_zoom, m),
|
||||
aabox = nbox.aabox,
|
||||
nbax = aabox.x - offset,
|
||||
nbay = aabox.y - offset,
|
||||
nbaw = aabox.width + (offset * 2),
|
||||
nbah = aabox.height + (offset * 2);
|
||||
|
||||
// now if the shape is rotated, un-rotate it
|
||||
var cx = nbax + nbaw/2,
|
||||
cy = nbay + nbah/2;
|
||||
|
||||
var angle = svgedit.utilities.getRotationAngle(selected);
|
||||
if (angle) {
|
||||
var rot = svgFactory_.svgRoot().createSVGTransform();
|
||||
rot.setRotate(-angle,cx,cy);
|
||||
var rotm = rot.matrix;
|
||||
nbox.tl = svgedit.math.transformPoint(nbox.tl.x,nbox.tl.y,rotm);
|
||||
nbox.tr = svgedit.math.transformPoint(nbox.tr.x,nbox.tr.y,rotm);
|
||||
nbox.bl = svgedit.math.transformPoint(nbox.bl.x,nbox.bl.y,rotm);
|
||||
nbox.br = svgedit.math.transformPoint(nbox.br.x,nbox.br.y,rotm);
|
||||
|
||||
// calculate the axis-aligned bbox
|
||||
var tl = nbox.tl;
|
||||
var minx = tl.x,
|
||||
miny = tl.y,
|
||||
maxx = tl.x,
|
||||
maxy = tl.y;
|
||||
|
||||
var Min = Math.min, Max = Math.max;
|
||||
|
||||
minx = Min(minx, Min(nbox.tr.x, Min(nbox.bl.x, nbox.br.x) ) ) - offset;
|
||||
miny = Min(miny, Min(nbox.tr.y, Min(nbox.bl.y, nbox.br.y) ) ) - offset;
|
||||
maxx = Max(maxx, Max(nbox.tr.x, Max(nbox.bl.x, nbox.br.x) ) ) + offset;
|
||||
maxy = Max(maxy, Max(nbox.tr.y, Max(nbox.bl.y, nbox.br.y) ) ) + offset;
|
||||
|
||||
nbax = minx;
|
||||
nbay = miny;
|
||||
nbaw = (maxx-minx);
|
||||
nbah = (maxy-miny);
|
||||
}
|
||||
var sr_handle = svgFactory_.svgRoot().suspendRedraw(100);
|
||||
|
||||
var dstr = 'M' + nbax + ',' + nbay
|
||||
+ ' L' + (nbax+nbaw) + ',' + nbay
|
||||
+ ' ' + (nbax+nbaw) + ',' + (nbay+nbah)
|
||||
+ ' ' + nbax + ',' + (nbay+nbah) + 'z';
|
||||
selectedBox.setAttribute('d', dstr);
|
||||
|
||||
var xform = angle ? 'rotate(' + [angle,cx,cy].join(',') + ')' : '';
|
||||
this.selectorGroup.setAttribute('transform', xform);
|
||||
|
||||
// TODO(codedread): Is this if needed?
|
||||
// if(selected === selectedElements[0]) {
|
||||
this.gripCoords = {
|
||||
'nw': [nbax, nbay],
|
||||
'ne': [nbax+nbaw, nbay],
|
||||
'sw': [nbax, nbay+nbah],
|
||||
'se': [nbax+nbaw, nbay+nbah],
|
||||
'n': [nbax + (nbaw)/2, nbay],
|
||||
'w': [nbax, nbay + (nbah)/2],
|
||||
'e': [nbax + nbaw, nbay + (nbah)/2],
|
||||
's': [nbax + (nbaw)/2, nbay + nbah]
|
||||
};
|
||||
|
||||
for(var dir in this.gripCoords) {
|
||||
var coords = this.gripCoords[dir];
|
||||
selectedGrips[dir].setAttribute('cx', coords[0]);
|
||||
selectedGrips[dir].setAttribute('cy', coords[1]);
|
||||
};
|
||||
|
||||
// we want to go 20 pixels in the negative transformed y direction, ignoring scale
|
||||
mgr.rotateGripConnector.setAttribute('x1', nbax + (nbaw)/2);
|
||||
mgr.rotateGripConnector.setAttribute('y1', nbay);
|
||||
mgr.rotateGripConnector.setAttribute('x2', nbax + (nbaw)/2);
|
||||
mgr.rotateGripConnector.setAttribute('y2', nbay - 20);
|
||||
|
||||
mgr.rotateGrip.setAttribute('cx', nbax + (nbaw)/2);
|
||||
mgr.rotateGrip.setAttribute('cy', nbay - 20);
|
||||
// }
|
||||
|
||||
svgFactory_.svgRoot().unsuspendRedraw(sr_handle);
|
||||
};
|
||||
|
||||
|
||||
// Class: svgedit.select.SelectorManager
|
||||
svgedit.select.SelectorManager = function() {
|
||||
// this will hold the <g> element that contains all selector rects/grips
|
||||
this.selectorParentGroup = null;
|
||||
|
||||
// this is a special rect that is used for multi-select
|
||||
this.rubberBandBox = null;
|
||||
|
||||
// this will hold objects of type svgedit.select.Selector (see above)
|
||||
this.selectors = [];
|
||||
|
||||
// this holds a map of SVG elements to their Selector object
|
||||
this.selectorMap = {};
|
||||
|
||||
// this holds a reference to the grip elements
|
||||
this.selectorGrips = {
|
||||
'nw': null,
|
||||
'n' : null,
|
||||
'ne': null,
|
||||
'e' : null,
|
||||
'se': null,
|
||||
's' : null,
|
||||
'sw': null,
|
||||
'w' : null
|
||||
};
|
||||
|
||||
this.selectorGripsGroup = null;
|
||||
this.rotateGripConnector = null;
|
||||
this.rotateGrip = null;
|
||||
|
||||
this.initGroup();
|
||||
};
|
||||
|
||||
// Function: svgedit.select.SelectorManager.initGroup
|
||||
// Resets the parent selector group element
|
||||
svgedit.select.SelectorManager.prototype.initGroup = function() {
|
||||
// remove old selector parent group if it existed
|
||||
if (this.selectorParentGroup && this.selectorParentGroup.parentNode) {
|
||||
this.selectorParentGroup.parentNode.removeChild(this.selectorParentGroup);
|
||||
}
|
||||
|
||||
// create parent selector group and add it to svgroot
|
||||
this.selectorParentGroup = svgFactory_.createSVGElement({
|
||||
'element': 'g',
|
||||
'attr': {'id': 'selectorParentGroup'}
|
||||
});
|
||||
this.selectorGripsGroup = svgFactory_.createSVGElement({
|
||||
'element': 'g',
|
||||
'attr': {'display': 'none'}
|
||||
});
|
||||
this.selectorParentGroup.appendChild(this.selectorGripsGroup);
|
||||
svgFactory_.svgRoot().appendChild(this.selectorParentGroup);
|
||||
|
||||
this.selectorMap = {};
|
||||
this.selectors = [];
|
||||
this.rubberBandBox = null;
|
||||
|
||||
// add the corner grips
|
||||
for (var dir in this.selectorGrips) {
|
||||
var grip = svgFactory_.createSVGElement({
|
||||
'element': 'circle',
|
||||
'attr': {
|
||||
'id': ('selectorGrip_resize_' + dir),
|
||||
'fill': '#22C',
|
||||
'r': 4,
|
||||
'style': ('cursor:' + dir + '-resize'),
|
||||
// This expands the mouse-able area of the grips making them
|
||||
// easier to grab with the mouse.
|
||||
// This works in Opera and WebKit, but does not work in Firefox
|
||||
// see https://bugzilla.mozilla.org/show_bug.cgi?id=500174
|
||||
'stroke-width': 2,
|
||||
'pointer-events': 'all'
|
||||
}
|
||||
});
|
||||
|
||||
$.data(grip, 'dir', dir);
|
||||
$.data(grip, 'type', 'resize');
|
||||
this.selectorGrips[dir] = this.selectorGripsGroup.appendChild(grip);
|
||||
}
|
||||
|
||||
// add rotator elems
|
||||
this.rotateGripConnector = this.selectorGripsGroup.appendChild(
|
||||
svgFactory_.createSVGElement({
|
||||
'element': 'line',
|
||||
'attr': {
|
||||
'id': ('selectorGrip_rotateconnector'),
|
||||
'stroke': '#22C',
|
||||
'stroke-width': '1'
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.rotateGrip = this.selectorGripsGroup.appendChild(
|
||||
svgFactory_.createSVGElement({
|
||||
'element': 'circle',
|
||||
'attr': {
|
||||
'id': 'selectorGrip_rotate',
|
||||
'fill': 'lime',
|
||||
'r': 4,
|
||||
'stroke': '#22C',
|
||||
'stroke-width': 2,
|
||||
'style': 'cursor:url(' + config_.imgPath + 'rotate.png) 12 12, auto;'
|
||||
}
|
||||
})
|
||||
);
|
||||
$.data(this.rotateGrip, 'type', 'rotate');
|
||||
|
||||
if($('#canvasBackground').length) return;
|
||||
|
||||
var dims = config_.dimensions;
|
||||
var canvasbg = svgFactory_.createSVGElement({
|
||||
'element': 'svg',
|
||||
'attr': {
|
||||
'id': 'canvasBackground',
|
||||
'width': dims[0],
|
||||
'height': dims[1],
|
||||
'x': 0,
|
||||
'y': 0,
|
||||
'overflow': (svgedit.browsersupport.isWebkit() ? 'none' : 'visible'), // Chrome 7 has a problem with this when zooming out
|
||||
'style': 'pointer-events:none'
|
||||
}
|
||||
});
|
||||
|
||||
var rect = svgFactory_.createSVGElement({
|
||||
'element': 'rect',
|
||||
'attr': {
|
||||
'width': '100%',
|
||||
'height': '100%',
|
||||
'x': 0,
|
||||
'y': 0,
|
||||
'stroke-width': 1,
|
||||
'stroke': '#000',
|
||||
'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 (!svgedit.browsersupport.isOpera()) rect.setAttribute('filter', 'url(#canvashadow)');
|
||||
canvasbg.appendChild(rect);
|
||||
svgFactory_.svgRoot().insertBefore(canvasbg, svgFactory_.svgContent());
|
||||
};
|
||||
|
||||
// Function: svgedit.select.SelectorManager.requestSelector
|
||||
// Returns the selector based on the given element
|
||||
//
|
||||
// Parameters:
|
||||
// elem - DOM element to get the selector for
|
||||
svgedit.select.SelectorManager.prototype.requestSelector = function(elem) {
|
||||
if (elem == null) return null;
|
||||
var N = this.selectors.length;
|
||||
// If we've already acquired one for this element, return it.
|
||||
if (typeof(this.selectorMap[elem.id]) == 'object') {
|
||||
this.selectorMap[elem.id].locked = true;
|
||||
return this.selectorMap[elem.id];
|
||||
}
|
||||
for (var i = 0; i < N; ++i) {
|
||||
if (this.selectors[i] && !this.selectors[i].locked) {
|
||||
this.selectors[i].locked = true;
|
||||
this.selectors[i].reset(elem);
|
||||
this.selectorMap[elem.id] = this.selectors[i];
|
||||
return this.selectors[i];
|
||||
}
|
||||
}
|
||||
// if we reached here, no available selectors were found, we create one
|
||||
this.selectors[N] = new svgedit.select.Selector(N, elem);
|
||||
this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup);
|
||||
this.selectorMap[elem.id] = this.selectors[N];
|
||||
return this.selectors[N];
|
||||
};
|
||||
|
||||
// Function: svgedit.select.SelectorManager.releaseSelector
|
||||
// Removes the selector of the given element (hides selection box)
|
||||
//
|
||||
// Parameters:
|
||||
// elem - DOM element to remove the selector for
|
||||
svgedit.select.SelectorManager.prototype.releaseSelector = function(elem) {
|
||||
if (elem == null) return;
|
||||
var N = this.selectors.length,
|
||||
sel = this.selectorMap[elem.id];
|
||||
for (var i = 0; i < N; ++i) {
|
||||
if (this.selectors[i] && this.selectors[i] == sel) {
|
||||
if (sel.locked == false) {
|
||||
// TODO(codedread): Ensure this exists in this module.
|
||||
console.log('WARNING! selector was released but was already unlocked');
|
||||
}
|
||||
delete this.selectorMap[elem.id];
|
||||
sel.locked = false;
|
||||
sel.selectedElement = null;
|
||||
sel.showGrips(false);
|
||||
|
||||
// remove from DOM and store reference in JS but only if it exists in the DOM
|
||||
try {
|
||||
sel.selectorGroup.setAttribute('display', 'none');
|
||||
} catch(e) { }
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.select.SelectorManager.getRubberBandBox
|
||||
// Returns the rubberBandBox DOM element. This is the rectangle drawn by the user for selecting/zooming
|
||||
svgedit.select.SelectorManager.prototype.getRubberBandBox = function() {
|
||||
if (!this.rubberBandBox) {
|
||||
this.rubberBandBox = this.selectorParentGroup.appendChild(
|
||||
svgFactory_.createSVGElement({
|
||||
'element': 'rect',
|
||||
'attr': {
|
||||
'id': 'selectorRubberBand',
|
||||
'fill': '#22C',
|
||||
'fill-opacity': 0.15,
|
||||
'stroke': '#22C',
|
||||
'stroke-width': 0.5,
|
||||
'display': 'none',
|
||||
'style': 'pointer-events:none'
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
return this.rubberBandBox;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interface: svgedit.select.SVGFactory
|
||||
* An object that creates SVG elements for the canvas.
|
||||
*
|
||||
* interface svgedit.select.SVGFactory {
|
||||
* SVGElement createSVGElement(jsonMap);
|
||||
* SVGSVGElement svgRoot();
|
||||
* SVGSVGElement svgContent();
|
||||
*
|
||||
* Number currentZoom();
|
||||
* Object getStrokedBBox(Element[]); // TODO(codedread): Remove when getStrokedBBox() has been put into svgutils.js
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function: svgedit.select.init()
|
||||
* Initializes this module.
|
||||
*
|
||||
* Parameters:
|
||||
* config - an object containing configurable parameters (imgPath)
|
||||
* svgFactory - an object implementing the SVGFactory interface (see above).
|
||||
*/
|
||||
svgedit.select.init = function(config, svgFactory) {
|
||||
config_ = config;
|
||||
svgFactory_ = svgFactory;
|
||||
selectorManager_ = new svgedit.select.SelectorManager();
|
||||
};
|
||||
|
||||
/**
|
||||
* Function: svgedit.select.getSelectorManager
|
||||
*
|
||||
* Returns:
|
||||
* The SelectorManager instance.
|
||||
*/
|
||||
svgedit.select.getSelectorManager = function() {
|
||||
return selectorManager_;
|
||||
};
|
||||
|
||||
})();
|
|
@ -1,5 +1,5 @@
|
|||
body {
|
||||
background: #D8D8D8;
|
||||
background: #D0D0D0;
|
||||
}
|
||||
|
||||
#svg_editor * {
|
||||
|
@ -223,7 +223,7 @@
|
|||
#svg_editor #sidepanel_handle {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
background-color: #D8D8D8;
|
||||
background-color: #D0D0D0;
|
||||
font-weight: bold;
|
||||
left: 0px;
|
||||
top: 40%;
|
||||
|
@ -485,7 +485,7 @@
|
|||
top: 75px;
|
||||
left: 0;
|
||||
padding-left: 2px;
|
||||
background: #D8D8D8; /* Needed so flyout icons don't appear on the left */
|
||||
background: #D0D0D0; /* Needed so flyout icons don't appear on the left */
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,14 +10,22 @@
|
|||
<link rel="stylesheet" href="svg-editor.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="spinbtn/JQuerySpinBtn.css" type="text/css"/>
|
||||
<!-- Development version of script tags: -->
|
||||
<!-- <script type="text/javascript" src="jquery.js"></script> -->
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.js"></script>
|
||||
<script type="text/javascript" src="jquery.js"></script>
|
||||
<!--script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.js"></script-->
|
||||
<script type="text/javascript" src="js-hotkeys/jquery.hotkeys.min.js"></script>
|
||||
<script type="text/javascript" src="jgraduate/jquery.jgraduate.js"></script>
|
||||
<script type="text/javascript" src="svgicons/jquery.svgicons.js"></script>
|
||||
<script type="text/javascript" src="jquerybbq/jquery.bbq.min.js"></script>
|
||||
<script type="text/javascript" src="spinbtn/JQuerySpinBtn.js"></script>
|
||||
<script type="text/javascript" src="contextmenu/jquery.contextMenu.js"></script>
|
||||
<script type="text/javascript" src="browsersupport.js"></script>
|
||||
<script type="text/javascript" src="svgtransformlist.js"></script>
|
||||
<script type="text/javascript" src="math.js"></script>
|
||||
<script type="text/javascript" src="units.js"></script>
|
||||
<script type="text/javascript" src="svgutils.js"></script>
|
||||
<script type="text/javascript" src="sanitize.js"></script>
|
||||
<script type="text/javascript" src="history.js"></script>
|
||||
<script type="text/javascript" src="select.js"></script>
|
||||
<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>
|
||||
|
@ -30,10 +38,8 @@
|
|||
<script type="text/javascript" src="jquerybbq/jquery.bbq.min.js"></script>
|
||||
<script type="text/javascript" src="jgraduate/jquery.jgraduate.min.js"></script>
|
||||
<script type="text/javascript" src="spinbtn/JQuerySpinBtn.min.js"></script>
|
||||
<script type="text/javascript" src="svgcanvas.min.js"></script>
|
||||
<script type="text/javascript" src="svg-editor.min.js"></script>
|
||||
script type="text/javascript" src="locale/locale.min.js"></script-->
|
||||
|
||||
<script type="text/javascript" src="svgedit.compiled.js"></script>
|
||||
-->
|
||||
|
||||
<!-- always minified scripts -->
|
||||
<script type="text/javascript" src="jquery-ui/jquery-ui-1.8.custom.min.js"></script>
|
||||
|
@ -397,7 +403,7 @@ script type="text/javascript" src="locale/locale.min.js"></script-->
|
|||
|
||||
<div id="path_node_panel">
|
||||
<div class="tool_sep"></div>
|
||||
<div class="tool_button" id="tool_node_link" title="Link Control Points"></div>
|
||||
<div class="tool_button push_button_pressed" id="tool_node_link" title="Link Control Points"></div>
|
||||
<div class="tool_sep"></div>
|
||||
<label id="tool_node_x">x:
|
||||
<input id="path_node_x" class="attr_changer" title="Change node's x coordinate" size="3" data-attr="x"/>
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) units.js
|
||||
// 2) svgcanvas.js
|
||||
|
||||
(function() {
|
||||
|
||||
if(!window.svgEditor) window.svgEditor = function($) {
|
||||
|
@ -462,14 +466,14 @@
|
|||
"#00007f", "#3f007f", "#7f007f", "#7f003f",
|
||||
"#ffaaaa", "#ffd4aa", "#ffffaa", "#d4ffaa",
|
||||
"#aaffaa", "#aaffd4", "#aaffff", "#aad4ff",
|
||||
"#aaaaff", "#d4aaff", "#ffaaff", "#ffaad4",
|
||||
"#aaaaff", "#d4aaff", "#ffaaff", "#ffaad4"
|
||||
],
|
||||
isMac = (navigator.platform.indexOf("Mac") >= 0),
|
||||
isWebkit = (navigator.userAgent.indexOf("AppleWebKit") >= 0),
|
||||
modKey = (isMac ? "meta+" : "ctrl+"), // ⌘
|
||||
path = svgCanvas.pathActions,
|
||||
undoMgr = svgCanvas.undoMgr,
|
||||
Utils = svgCanvas.Utils,
|
||||
Utils = svgedit.utilities,
|
||||
default_img_url = curConfig.imgPath + "logo.png",
|
||||
workarea = $("#workarea"),
|
||||
canv_menu = $("#cmenu_canvas"),
|
||||
|
@ -489,7 +493,7 @@
|
|||
// In the future we may want to add additional types of dialog boxes, since
|
||||
// they should be easy to handle this way.
|
||||
(function() {
|
||||
$('#dialog_container').draggable({cancel:'#dialog_content, #dialog_buttons *'});
|
||||
$('#dialog_container').draggable({cancel:'#dialog_content, #dialog_buttons *', containment: 'window'});
|
||||
var box = $('#dialog_box'), btn_holder = $('#dialog_buttons');
|
||||
|
||||
var dbox = function(type, msg, callback, defText) {
|
||||
|
@ -1321,11 +1325,30 @@
|
|||
// This function also updates the opacity and id elements that are in the context panel
|
||||
var updateToolbar = function() {
|
||||
if (selectedElement != null && ['use', 'image', 'foreignObject', 'g', 'a'].indexOf(selectedElement.tagName) === -1) {
|
||||
var all_swidth = null;
|
||||
|
||||
// if(selectedElement.tagName === "g" || selectedElement.tagName === "a") {
|
||||
// // Look for common styles
|
||||
// var childs = selectedElement.getElementsByTagName('*');
|
||||
// console.log('ch', childs);
|
||||
// for(var i = 0, len = childs.length; i < len; i++) {
|
||||
// var elem = childs[i];
|
||||
// var swidth = elem.getAttribute("stroke-width");
|
||||
// if(swidth && swidth !== all_swidth) {
|
||||
// // different, so do don't check more
|
||||
// all_swidth = null;
|
||||
// break;
|
||||
// } else if(swidth) {
|
||||
// console.log('e', elem, swidth);
|
||||
// all_swidth = swidth;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
paintBox.fill.update(true);
|
||||
paintBox.stroke.update(true);
|
||||
|
||||
$('#stroke_width').val(selectedElement.getAttribute("stroke-width")||1);
|
||||
$('#stroke_width').val(all_swidth || selectedElement.getAttribute("stroke-width") || 1);
|
||||
$('#stroke_style').val(selectedElement.getAttribute("stroke-dasharray")||"none");
|
||||
|
||||
var attr = selectedElement.getAttribute("stroke-linejoin") || 'miter';
|
||||
|
@ -1784,7 +1807,7 @@
|
|||
$('.attr_changer').change(function() {
|
||||
var attr = this.getAttribute("data-attr");
|
||||
var val = this.value;
|
||||
var valid = svgCanvas.isValidUnit(attr, val);
|
||||
var valid = svgedit.units.isValidUnit(attr, val);
|
||||
|
||||
if(!valid) {
|
||||
$.alert(uiStrings.invalidAttrValGiven);
|
||||
|
@ -1797,11 +1820,7 @@
|
|||
} else if(curConfig.baseUnit !== 'px') {
|
||||
// Convert unitless value to one with given unit
|
||||
|
||||
// val = svgCanvas.convertUnit(bv, "px");
|
||||
// selectedElement[attr].baseVal.newValueSpecifiedUnits();
|
||||
// this.value = val;
|
||||
// selectedElement[attr].baseVal
|
||||
var unitData = svgCanvas.getUnits();
|
||||
var unitData = svgedit.units.getTypeMap();
|
||||
|
||||
if(selectedElement[attr] || svgCanvas.getMode() === "pathedit" || attr === "x" || attr === "y") {
|
||||
val *= unitData[curConfig.baseUnit];
|
||||
|
@ -2594,7 +2613,7 @@
|
|||
$('#svg_source_textarea').focus();
|
||||
};
|
||||
|
||||
$('#svg_docprops_container, #svg_prefs_container').draggable({cancel:'button,fieldset'});
|
||||
$('#svg_docprops_container, #svg_prefs_container').draggable({cancel:'button,fieldset', containment: 'window'});
|
||||
|
||||
var showDocProperties = function(){
|
||||
if (docprops) return;
|
||||
|
@ -2605,6 +2624,11 @@
|
|||
|
||||
// update resolution option with actual resolution
|
||||
var res = svgCanvas.getResolution();
|
||||
if(curConfig.baseUnit !== "px") {
|
||||
res.w = svgCanvas.convertUnit(res.w) + curConfig.baseUnit;
|
||||
res.h = svgCanvas.convertUnit(res.h) + curConfig.baseUnit;
|
||||
}
|
||||
|
||||
$('#canvas_width').val(res.w);
|
||||
$('#canvas_height').val(res.h);
|
||||
$('#canvas_title').val(svgCanvas.getDocumentTitle());
|
||||
|
@ -2694,7 +2718,7 @@
|
|||
var width = $('#canvas_width'), w = width.val();
|
||||
var height = $('#canvas_height'), h = height.val();
|
||||
|
||||
if(w != "fit" && !svgCanvas.isValidUnit('width', w)) {
|
||||
if(w != "fit" && !svgedit.units.isValidUnit('width', w)) {
|
||||
$.alert(uiStrings.invalidAttrValGiven);
|
||||
width.parent().addClass('error');
|
||||
return false;
|
||||
|
@ -2702,7 +2726,7 @@
|
|||
|
||||
width.parent().removeClass('error');
|
||||
|
||||
if(h != "fit" && !svgCanvas.isValidUnit('height', h)) {
|
||||
if(h != "fit" && !svgedit.units.isValidUnit('height', h)) {
|
||||
$.alert(uiStrings.invalidAttrValGiven);
|
||||
height.parent().addClass('error');
|
||||
return false;
|
||||
|
@ -2920,7 +2944,7 @@
|
|||
"div#workarea": {
|
||||
'left': 38,
|
||||
'top': 74
|
||||
},
|
||||
}
|
||||
// "#tools_bottom": {
|
||||
// 'left': {s: '27px', l: '46px', xl: '65px'},
|
||||
// 'height': {s: '58px', l: '98px', xl: '145px'}
|
||||
|
@ -3170,7 +3194,7 @@
|
|||
var was_none = false;
|
||||
var pos = elem.position();
|
||||
$("#color_picker")
|
||||
.draggable({cancel:'.jGraduate_tabs,.jGraduate_colPick,.jGraduate_lgPick,.jGraduate_rgPick'})
|
||||
.draggable({cancel:'.jGraduate_tabs, .jGraduate_colPick, .jGraduate_gradPick, .jPicker', containment: 'window'})
|
||||
.css(curConfig.colorPickerCSS || {'left': pos.left, 'bottom': 50 - pos.top})
|
||||
.jGraduate(
|
||||
{
|
||||
|
@ -3197,7 +3221,7 @@
|
|||
var buttonsNeedingStroke = [ '#tool_fhpath', '#tool_line' ];
|
||||
var buttonsNeedingFillAndStroke = [ '#tools_rect .tool_button', '#tools_ellipse .tool_button', '#tool_text', '#tool_path'];
|
||||
if (bNoStroke) {
|
||||
for (index in buttonsNeedingStroke) {
|
||||
for (var index in buttonsNeedingStroke) {
|
||||
var button = buttonsNeedingStroke[index];
|
||||
if ($(button).hasClass('tool_button_current')) {
|
||||
clickSelect();
|
||||
|
@ -3206,14 +3230,14 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
for (index in buttonsNeedingStroke) {
|
||||
for (var index in buttonsNeedingStroke) {
|
||||
var button = buttonsNeedingStroke[index];
|
||||
$(button).removeClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
if (bNoStroke && bNoFill) {
|
||||
for (index in buttonsNeedingFillAndStroke) {
|
||||
for (var index in buttonsNeedingFillAndStroke) {
|
||||
var button = buttonsNeedingFillAndStroke[index];
|
||||
if ($(button).hasClass('tool_button_current')) {
|
||||
clickSelect();
|
||||
|
@ -3222,7 +3246,7 @@
|
|||
}
|
||||
}
|
||||
else {
|
||||
for (index in buttonsNeedingFillAndStroke) {
|
||||
for (var index in buttonsNeedingFillAndStroke) {
|
||||
var button = buttonsNeedingFillAndStroke[index];
|
||||
$(button).removeClass('disabled');
|
||||
}
|
||||
|
@ -3971,7 +3995,8 @@
|
|||
|
||||
// Select given tool
|
||||
Editor.ready(function() {
|
||||
var itool = curConfig.initTool,
|
||||
var tool,
|
||||
itool = curConfig.initTool,
|
||||
container = $("#tools_left, #svg_editor .tools_flyout"),
|
||||
pre_tool = container.find("#tool_" + itool),
|
||||
reg_tool = container.find("#" + itool);
|
||||
|
@ -4249,7 +4274,7 @@
|
|||
|
||||
var c_elem = svgCanvas.getContentElem();
|
||||
|
||||
var units = svgCanvas.getUnits();
|
||||
var units = svgedit.units.getTypeMap();
|
||||
var unit = units[curConfig.baseUnit]; // 1 = 1px
|
||||
|
||||
for(var d = 0; d < 2; d++) {
|
||||
|
@ -4328,6 +4353,7 @@
|
|||
}
|
||||
|
||||
var num = (label_pos - content_d) / u_multi;
|
||||
var label;
|
||||
if(multi >= 1) {
|
||||
label = Math.round(num);
|
||||
} else {
|
||||
|
@ -4389,7 +4415,7 @@
|
|||
updateCanvas(true);
|
||||
// });
|
||||
|
||||
// var revnums = "svg-editor.js ($Rev: 1814 $) ";
|
||||
// var revnums = "svg-editor.js ($Rev: 1877 $) ";
|
||||
// revnums += svgCanvas.getVersion();
|
||||
// $('#copyright')[0].setAttribute("title", revnums);
|
||||
|
||||
|
@ -4404,7 +4430,7 @@
|
|||
|
||||
// Callback handler for embedapi.js
|
||||
try{
|
||||
json_encode = function(obj){
|
||||
var 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
|
||||
|
@ -4560,7 +4586,7 @@
|
|||
Editor.ready(function() {
|
||||
var pre = 'data:image/svg+xml;base64,';
|
||||
var src = str.substring(pre.length);
|
||||
loadSvgString(svgCanvas.Utils.decode64(src));
|
||||
loadSvgString(svgedit.utilities.decode64(src));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -357,25 +357,52 @@ $(function() {
|
|||
var defs = svg_el.find('defs');
|
||||
if(!defs.length) return svg_el;
|
||||
|
||||
defs.find('[id]').each(function(i) {
|
||||
if(isOpera) {
|
||||
var id_elems = defs.find('*').filter(function() {
|
||||
return !!this.id;
|
||||
});
|
||||
} else {
|
||||
var id_elems = defs.find('[id]');
|
||||
}
|
||||
|
||||
var all_elems = svg_el[0].getElementsByTagName('*'), len = all_elems.length;
|
||||
|
||||
id_elems.each(function(i) {
|
||||
var id = this.id;
|
||||
var no_dupes = ($(svgdoc).find('#' + id).length <= 1);
|
||||
if(isOpera) no_dupes = false; // Opera didn't clone svg_el, so not reliable
|
||||
// if(!force && no_dupes) return;
|
||||
var new_id = 'x' + id + svg_num + i;
|
||||
this.id = new_id;
|
||||
|
||||
var old_val = 'url(#' + id + ')';
|
||||
var new_val = 'url(#' + new_id + ')';
|
||||
|
||||
// Selector method, possibly faster but fails in Opera / jQuery 1.4.3
|
||||
// svg_el.find('[fill="url(#' + id + ')"]').each(function() {
|
||||
// this.setAttribute('fill', 'url(#' + new_id + ')');
|
||||
// }).end().find('[stroke="url(#' + id + ')"]').each(function() {
|
||||
// this.setAttribute('stroke', 'url(#' + new_id + ')');
|
||||
// }).end().find('use').each(function() {
|
||||
// if(this.getAttribute('xlink:href') == '#' + id) {
|
||||
// this.setAttributeNS(xlinkns,'href','#' + new_id);
|
||||
// }
|
||||
// }).end().find('[filter="url(#' + id + ')"]').each(function() {
|
||||
// this.setAttribute('filter', 'url(#' + new_id + ')');
|
||||
// });
|
||||
|
||||
svg_el.find('[fill="url(#' + id + ')"]').each(function() {
|
||||
this.setAttribute('fill', 'url(#' + new_id + ')');
|
||||
}).end().find('[stroke="url(#' + id + ')"]').each(function() {
|
||||
this.setAttribute('stroke', 'url(#' + new_id + ')');
|
||||
}).end().find('use').each(function() {
|
||||
if(this.getAttribute('xlink:href') == '#' + id) {
|
||||
this.setAttributeNS(xlinkns,'href','#' + new_id);
|
||||
for(var i = 0; i < len; i++) {
|
||||
var elem = all_elems[i];
|
||||
if(elem.getAttribute('fill') === old_val) {
|
||||
elem.setAttribute('fill', new_val);
|
||||
}
|
||||
}).end().find('[filter="url(#' + id + ')"]').each(function() {
|
||||
this.setAttribute('filter', 'url(#' + new_id + ')');
|
||||
});
|
||||
if(elem.getAttribute('stroke') === old_val) {
|
||||
elem.setAttribute('stroke', new_val);
|
||||
}
|
||||
if(elem.getAttribute('filter') === old_val) {
|
||||
elem.setAttribute('filter', new_val);
|
||||
}
|
||||
}
|
||||
});
|
||||
return svg_el;
|
||||
}
|
||||
|
|
292
public/svg-edit/editor/svgtransformlist.js
Normal file
292
public/svg-edit/editor/svgtransformlist.js
Normal file
|
@ -0,0 +1,292 @@
|
|||
/**
|
||||
* SVGTransformList
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) browsersupport.js
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
if (!svgedit.transformlist) {
|
||||
svgedit.transformlist = {};
|
||||
}
|
||||
|
||||
var svgroot = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
|
||||
// Helper function.
|
||||
function transformToString(xform) {
|
||||
var m = xform.matrix,
|
||||
text = "";
|
||||
switch(xform.type) {
|
||||
case 1: // MATRIX
|
||||
text = "matrix(" + [m.a,m.b,m.c,m.d,m.e,m.f].join(",") + ")";
|
||||
break;
|
||||
case 2: // TRANSLATE
|
||||
text = "translate(" + m.e + "," + m.f + ")";
|
||||
break;
|
||||
case 3: // SCALE
|
||||
if (m.a == m.d) text = "scale(" + m.a + ")";
|
||||
else text = "scale(" + m.a + "," + m.d + ")";
|
||||
break;
|
||||
case 4: // ROTATE
|
||||
var cx = 0, cy = 0;
|
||||
// this prevents divide by zero
|
||||
if (xform.angle != 0) {
|
||||
var K = 1 - m.a;
|
||||
cy = ( K * m.f + m.b*m.e ) / ( K*K + m.b*m.b );
|
||||
cx = ( m.e - m.b * cy ) / K;
|
||||
}
|
||||
text = "rotate(" + xform.angle + " " + cx + "," + cy + ")";
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Map of SVGTransformList objects.
|
||||
*/
|
||||
var listMap_ = {};
|
||||
|
||||
|
||||
// **************************************************************************************
|
||||
// SVGTransformList implementation for Webkit
|
||||
// These methods do not currently raise any exceptions.
|
||||
// These methods also do not check that transforms are being inserted. This is basically
|
||||
// implementing as much of SVGTransformList that we need to get the job done.
|
||||
//
|
||||
// interface SVGEditTransformList {
|
||||
// attribute unsigned long numberOfItems;
|
||||
// void clear ( )
|
||||
// SVGTransform initialize ( in SVGTransform newItem )
|
||||
// SVGTransform getItem ( in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
||||
// SVGTransform insertItemBefore ( in SVGTransform newItem, in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
||||
// SVGTransform replaceItem ( in SVGTransform newItem, in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
||||
// SVGTransform removeItem ( in unsigned long index ) (DOES NOT THROW DOMException, INDEX_SIZE_ERR)
|
||||
// SVGTransform appendItem ( in SVGTransform newItem )
|
||||
// NOT IMPLEMENTED: SVGTransform createSVGTransformFromMatrix ( in SVGMatrix matrix );
|
||||
// NOT IMPLEMENTED: SVGTransform consolidate ( );
|
||||
// }
|
||||
// **************************************************************************************
|
||||
svgedit.transformlist.SVGTransformList = function(elem) {
|
||||
this._elem = elem || null;
|
||||
this._xforms = [];
|
||||
// TODO: how do we capture the undo-ability in the changed transform list?
|
||||
this._update = function() {
|
||||
var tstr = "";
|
||||
var concatMatrix = svgroot.createSVGMatrix();
|
||||
for (var i = 0; i < this.numberOfItems; ++i) {
|
||||
var xform = this._list.getItem(i);
|
||||
tstr += transformToString(xform) + " ";
|
||||
}
|
||||
this._elem.setAttribute("transform", tstr);
|
||||
};
|
||||
this._list = this;
|
||||
this._init = function() {
|
||||
// Transform attribute parser
|
||||
var str = this._elem.getAttribute("transform");
|
||||
if(!str) return;
|
||||
|
||||
// TODO: Add skew support in future
|
||||
var re = /\s*((scale|matrix|rotate|translate)\s*\(.*?\))\s*,?\s*/;
|
||||
var arr = [];
|
||||
var m = true;
|
||||
while(m) {
|
||||
m = str.match(re);
|
||||
str = str.replace(re,'');
|
||||
if(m && m[1]) {
|
||||
var x = m[1];
|
||||
var bits = x.split(/\s*\(/);
|
||||
var name = bits[0];
|
||||
var val_bits = bits[1].match(/\s*(.*?)\s*\)/);
|
||||
val_bits[1] = val_bits[1].replace(/(\d)-/g, "$1 -");
|
||||
var val_arr = val_bits[1].split(/[, ]+/);
|
||||
var letters = 'abcdef'.split('');
|
||||
var mtx = svgroot.createSVGMatrix();
|
||||
$.each(val_arr, function(i, item) {
|
||||
val_arr[i] = parseFloat(item);
|
||||
if(name == 'matrix') {
|
||||
mtx[letters[i]] = val_arr[i];
|
||||
}
|
||||
});
|
||||
var xform = svgroot.createSVGTransform();
|
||||
var fname = 'set' + name.charAt(0).toUpperCase() + name.slice(1);
|
||||
var values = name=='matrix'?[mtx]:val_arr;
|
||||
|
||||
if (name == 'scale' && values.length == 1) {
|
||||
values.push(values[0]);
|
||||
} else if (name == 'translate' && values.length == 1) {
|
||||
values.push(0);
|
||||
} else if (name == 'rotate' && values.length == 1) {
|
||||
values.push(0);
|
||||
values.push(0);
|
||||
}
|
||||
xform[fname].apply(xform, values);
|
||||
this._list.appendItem(xform);
|
||||
}
|
||||
}
|
||||
};
|
||||
this._removeFromOtherLists = function(item) {
|
||||
if (item) {
|
||||
// Check if this transform is already in a transformlist, and
|
||||
// remove it if so.
|
||||
var found = false;
|
||||
for (var id in listMap_) {
|
||||
var tl = listMap_[id];
|
||||
for (var i = 0, len = tl._xforms.length; i < len; ++i) {
|
||||
if(tl._xforms[i] == item) {
|
||||
found = true;
|
||||
tl.removeItem(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.numberOfItems = 0;
|
||||
this.clear = function() {
|
||||
this.numberOfItems = 0;
|
||||
this._xforms = [];
|
||||
};
|
||||
|
||||
this.initialize = function(newItem) {
|
||||
this.numberOfItems = 1;
|
||||
this._removeFromOtherLists(newItem);
|
||||
this._xforms = [newItem];
|
||||
};
|
||||
|
||||
this.getItem = function(index) {
|
||||
if (index < this.numberOfItems && index >= 0) {
|
||||
return this._xforms[index];
|
||||
}
|
||||
throw {code: 1}; // DOMException with code=INDEX_SIZE_ERR
|
||||
};
|
||||
|
||||
this.insertItemBefore = function(newItem, index) {
|
||||
var retValue = null;
|
||||
if (index >= 0) {
|
||||
if (index < this.numberOfItems) {
|
||||
this._removeFromOtherLists(newItem);
|
||||
var newxforms = new Array(this.numberOfItems + 1);
|
||||
// TODO: use array copying and slicing
|
||||
for ( var i = 0; i < index; ++i) {
|
||||
newxforms[i] = this._xforms[i];
|
||||
}
|
||||
newxforms[i] = newItem;
|
||||
for ( var j = i+1; i < this.numberOfItems; ++j, ++i) {
|
||||
newxforms[j] = this._xforms[i];
|
||||
}
|
||||
this.numberOfItems++;
|
||||
this._xforms = newxforms;
|
||||
retValue = newItem;
|
||||
this._list._update();
|
||||
}
|
||||
else {
|
||||
retValue = this._list.appendItem(newItem);
|
||||
}
|
||||
}
|
||||
return retValue;
|
||||
};
|
||||
|
||||
this.replaceItem = function(newItem, index) {
|
||||
var retValue = null;
|
||||
if (index < this.numberOfItems && index >= 0) {
|
||||
this._removeFromOtherLists(newItem);
|
||||
this._xforms[index] = newItem;
|
||||
retValue = newItem;
|
||||
this._list._update();
|
||||
}
|
||||
return retValue;
|
||||
};
|
||||
|
||||
this.removeItem = function(index) {
|
||||
if (index < this.numberOfItems && index >= 0) {
|
||||
var retValue = this._xforms[index];
|
||||
var newxforms = new Array(this.numberOfItems - 1);
|
||||
for (var i = 0; i < index; ++i) {
|
||||
newxforms[i] = this._xforms[i];
|
||||
}
|
||||
for (var j = i; j < this.numberOfItems-1; ++j, ++i) {
|
||||
newxforms[j] = this._xforms[i+1];
|
||||
}
|
||||
this.numberOfItems--;
|
||||
this._xforms = newxforms;
|
||||
this._list._update();
|
||||
return retValue;
|
||||
} else {
|
||||
throw {code: 1}; // DOMException with code=INDEX_SIZE_ERR
|
||||
}
|
||||
};
|
||||
|
||||
this.appendItem = function(newItem) {
|
||||
this._removeFromOtherLists(newItem);
|
||||
this._xforms.push(newItem);
|
||||
this.numberOfItems++;
|
||||
this._list._update();
|
||||
return newItem;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
svgedit.transformlist.resetListMap = function() {
|
||||
listMap_ = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes transforms of the given element from the map.
|
||||
* Parameters:
|
||||
* elem - a DOM Element
|
||||
*/
|
||||
svgedit.transformlist.removeElementFromListMap = function(elem) {
|
||||
if (elem.id && listMap_[elem.id]) {
|
||||
delete listMap_[elem.id];
|
||||
}
|
||||
};
|
||||
|
||||
// Function: getTransformList
|
||||
// Returns an object that behaves like a SVGTransformList for the given DOM element
|
||||
//
|
||||
// Parameters:
|
||||
// elem - DOM element to get a transformlist from
|
||||
svgedit.transformlist.getTransformList = function(elem) {
|
||||
if (!svgedit.browsersupport.nativeTransformLists) {
|
||||
var id = elem.id;
|
||||
if(!id) {
|
||||
// Get unique ID for temporary element
|
||||
id = 'temp';
|
||||
}
|
||||
var t = listMap_[id];
|
||||
if (!t || id == 'temp') {
|
||||
listMap_[id] = new svgedit.transformlist.SVGTransformList(elem);
|
||||
listMap_[id]._init();
|
||||
t = listMap_[id];
|
||||
}
|
||||
return t;
|
||||
}
|
||||
else if (elem.transform) {
|
||||
return elem.transform.baseVal;
|
||||
}
|
||||
else if (elem.gradientTransform) {
|
||||
return elem.gradientTransform.baseVal;
|
||||
}
|
||||
else if (elem.patternTransform) {
|
||||
return elem.patternTransform.baseVal;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
})();
|
476
public/svg-edit/editor/svgutils.js
Normal file
476
public/svg-edit/editor/svgutils.js
Normal file
|
@ -0,0 +1,476 @@
|
|||
/**
|
||||
* Package: svgedit.utilities
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) jQuery
|
||||
// 2) browsersupport.js
|
||||
// 3) svgtransformlist.js
|
||||
// 4) math.js
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
|
||||
if (!svgedit.utilities) {
|
||||
svgedit.utilities = {};
|
||||
}
|
||||
|
||||
// Constants
|
||||
|
||||
// String used to encode base64.
|
||||
var KEYSTR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
var XLINKNS = "http://www.w3.org/1999/xlink";
|
||||
|
||||
// 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';
|
||||
|
||||
// Function: svgedit.utilities.toXml
|
||||
// Converts characters in a string to XML-friendly entities.
|
||||
//
|
||||
// Example: "&" becomes "&"
|
||||
//
|
||||
// Parameters:
|
||||
// str - The string to be converted
|
||||
//
|
||||
// Returns:
|
||||
// The converted string
|
||||
svgedit.utilities.toXml = function(str) {
|
||||
return $('<p/>').text(str).html();
|
||||
};
|
||||
|
||||
// Function: svgedit.utilities.fromXml
|
||||
// Converts XML entities in a string to single characters.
|
||||
// Example: "&" becomes "&"
|
||||
//
|
||||
// Parameters:
|
||||
// str - The string to be converted
|
||||
//
|
||||
// Returns:
|
||||
// The converted string
|
||||
svgedit.utilities.fromXml = function(str) {
|
||||
return $('<p/>').html(str).text();
|
||||
};
|
||||
|
||||
|
||||
// This code was written by Tyler Akins and has been placed in the
|
||||
// public domain. It would be nice if you left this header intact.
|
||||
// Base64 code from Tyler Akins -- http://rumkin.com
|
||||
|
||||
// schiller: Removed string concatenation in favour of Array.join() optimization,
|
||||
// also precalculate the size of the array needed.
|
||||
|
||||
// Function: svgedit.utilities.encode64
|
||||
// Converts a string to base64
|
||||
svgedit.utilities.encode64 = function(input) {
|
||||
// base64 strings are 4/3 larger than the original string
|
||||
// input = svgedit.utilities.encodeUTF8(input); // convert non-ASCII characters
|
||||
input = svgedit.utilities.convertToXMLReferences(input);
|
||||
if(window.btoa) return window.btoa(input); // Use native if available
|
||||
var output = new Array( Math.floor( (input.length + 2) / 3 ) * 4 );
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0, p = 0;
|
||||
|
||||
do {
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64;
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output[p++] = KEYSTR.charAt(enc1);
|
||||
output[p++] = KEYSTR.charAt(enc2);
|
||||
output[p++] = KEYSTR.charAt(enc3);
|
||||
output[p++] = KEYSTR.charAt(enc4);
|
||||
} while (i < input.length);
|
||||
|
||||
return output.join('');
|
||||
};
|
||||
|
||||
// Function: svgedit.utilities.decode64
|
||||
// Converts a string from base64
|
||||
svgedit.utilities.decode64 = function(input) {
|
||||
if(window.atob) return window.atob(input);
|
||||
var output = "";
|
||||
var chr1, chr2, chr3 = "";
|
||||
var enc1, enc2, enc3, enc4 = "";
|
||||
var i = 0;
|
||||
|
||||
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
do {
|
||||
enc1 = KEYSTR.indexOf(input.charAt(i++));
|
||||
enc2 = KEYSTR.indexOf(input.charAt(i++));
|
||||
enc3 = KEYSTR.indexOf(input.charAt(i++));
|
||||
enc4 = KEYSTR.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output = output + String.fromCharCode(chr1);
|
||||
|
||||
if (enc3 != 64) {
|
||||
output = output + String.fromCharCode(chr2);
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output = output + String.fromCharCode(chr3);
|
||||
}
|
||||
|
||||
chr1 = chr2 = chr3 = "";
|
||||
enc1 = enc2 = enc3 = enc4 = "";
|
||||
|
||||
} while (i < input.length);
|
||||
return unescape(output);
|
||||
};
|
||||
|
||||
// Currently not being used, so commented out for now
|
||||
// based on http://phpjs.org/functions/utf8_encode:577
|
||||
// codedread:does not seem to work with webkit-based browsers on OSX
|
||||
// "encodeUTF8": function(input) {
|
||||
// //return unescape(encodeURIComponent(input)); //may or may not work
|
||||
// var output = '';
|
||||
// for (var n = 0; n < input.length; n++){
|
||||
// var c = input.charCodeAt(n);
|
||||
// if (c < 128) {
|
||||
// output += input[n];
|
||||
// }
|
||||
// else if (c > 127) {
|
||||
// if (c < 2048){
|
||||
// output += String.fromCharCode((c >> 6) | 192);
|
||||
// }
|
||||
// else {
|
||||
// output += String.fromCharCode((c >> 12) | 224) + String.fromCharCode((c >> 6) & 63 | 128);
|
||||
// }
|
||||
// output += String.fromCharCode((c & 63) | 128);
|
||||
// }
|
||||
// }
|
||||
// return output;
|
||||
// },
|
||||
|
||||
// Function: svgedit.utilities.convertToXMLReferences
|
||||
// Converts a string to use XML references
|
||||
svgedit.utilities.convertToXMLReferences = function(input) {
|
||||
var output = '';
|
||||
for (var n = 0; n < input.length; n++){
|
||||
var c = input.charCodeAt(n);
|
||||
if (c < 128) {
|
||||
output += input[n];
|
||||
} else if(c > 127) {
|
||||
output += ("&#" + c + ";");
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
// Function: text2xml
|
||||
// Cross-browser compatible method of converting a string to an XML tree
|
||||
// found this function here: http://groups.google.com/group/jquery-dev/browse_thread/thread/c6d11387c580a77f
|
||||
svgedit.utilities.text2xml = function(sXML) {
|
||||
if(sXML.indexOf('<svg:svg') >= 0) {
|
||||
sXML = sXML.replace(/<(\/?)svg:/g, '<$1').replace('xmlns:svg', 'xmlns');
|
||||
}
|
||||
|
||||
var out;
|
||||
try{
|
||||
var dXML = (window.DOMParser)?new DOMParser():new ActiveXObject("Microsoft.XMLDOM");
|
||||
dXML.async = false;
|
||||
} catch(e){
|
||||
throw new Error("XML Parser could not be instantiated");
|
||||
};
|
||||
try{
|
||||
if(dXML.loadXML) out = (dXML.loadXML(sXML))?dXML:false;
|
||||
else out = dXML.parseFromString(sXML, "text/xml");
|
||||
}
|
||||
catch(e){ throw new Error("Error parsing XML string"); };
|
||||
return out;
|
||||
};
|
||||
|
||||
// Function: bboxToObj
|
||||
// Converts a SVGRect into an object.
|
||||
//
|
||||
// Parameters:
|
||||
// bbox - a SVGRect
|
||||
//
|
||||
// Returns:
|
||||
// An object with properties names x, y, width, height.
|
||||
svgedit.utilities.bboxToObj = function(bbox) {
|
||||
return {
|
||||
x: bbox.x,
|
||||
y: bbox.y,
|
||||
width: bbox.width,
|
||||
height: bbox.height
|
||||
}
|
||||
};
|
||||
|
||||
// Function: walkTree
|
||||
// Walks the tree and executes the callback on each element in a top-down fashion
|
||||
//
|
||||
// Parameters:
|
||||
// elem - DOM element to traverse
|
||||
// cbFn - Callback function to run on each element
|
||||
svgedit.utilities.walkTree = function(elem, cbFn){
|
||||
if (elem && elem.nodeType == 1) {
|
||||
cbFn(elem);
|
||||
var i = elem.childNodes.length;
|
||||
while (i--) {
|
||||
svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function: walkTreePost
|
||||
// Walks the tree and executes the callback on each element in a depth-first fashion
|
||||
// TODO: FIXME: Shouldn't this be calling walkTreePost?
|
||||
//
|
||||
// Parameters:
|
||||
// elem - DOM element to traverse
|
||||
// cbFn - Callback function to run on each element
|
||||
svgedit.utilities.walkTreePost = function(elem, cbFn) {
|
||||
if (elem && elem.nodeType == 1) {
|
||||
var i = elem.childNodes.length;
|
||||
while (i--) {
|
||||
svgedit.utilities.walkTree(elem.childNodes.item(i), cbFn);
|
||||
}
|
||||
cbFn(elem);
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.utilities.getUrlFromAttr
|
||||
// Extracts the URL from the url(...) syntax of some attributes.
|
||||
// Three variants:
|
||||
// * <circle fill="url(someFile.svg#foo)" />
|
||||
// * <circle fill="url('someFile.svg#foo')" />
|
||||
// * <circle fill='url("someFile.svg#foo")' />
|
||||
//
|
||||
// Parameters:
|
||||
// attrVal - The attribute value as a string
|
||||
//
|
||||
// Returns:
|
||||
// String with just the URL, like someFile.svg#foo
|
||||
svgedit.utilities.getUrlFromAttr = function(attrVal) {
|
||||
if (attrVal) {
|
||||
// url("#somegrad")
|
||||
if (attrVal.indexOf('url("') === 0) {
|
||||
return attrVal.substring(5,attrVal.indexOf('"',6));
|
||||
}
|
||||
// url('#somegrad')
|
||||
else if (attrVal.indexOf("url('") === 0) {
|
||||
return attrVal.substring(5,attrVal.indexOf("'",6));
|
||||
}
|
||||
else if (attrVal.indexOf("url(") === 0) {
|
||||
return attrVal.substring(4,attrVal.indexOf(')'));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Function: svgedit.utilities.getHref
|
||||
// Returns the given element's xlink:href value
|
||||
svgedit.utilities.getHref = function(elem) {
|
||||
return elem.getAttributeNS(XLINKNS, "href");
|
||||
}
|
||||
|
||||
// Function: svgedit.utilities.setHref
|
||||
// Sets the given element's xlink:href value
|
||||
svgedit.utilities.setHref = function(elem, val) {
|
||||
elem.setAttributeNS(XLINKNS, "xlink:href", val);
|
||||
}
|
||||
|
||||
// Function: findDefs
|
||||
// Parameters:
|
||||
// svgElement - The <svg> element.
|
||||
//
|
||||
// Returns:
|
||||
// The document's <defs> element, create it first if necessary
|
||||
svgedit.utilities.findDefs = function(svgElement) {
|
||||
var svgElement = svgDoc.documentElement;
|
||||
var defs = svgElement.getElementsByTagNameNS(svgns, "defs");
|
||||
if (defs.length > 0) {
|
||||
defs = defs[0];
|
||||
}
|
||||
else {
|
||||
// first child is a comment, so call nextSibling
|
||||
defs = svgElement.insertBefore( svgElement.ownerDocument.createElementNS(svgns, "defs" ), svgElement.firstChild.nextSibling);
|
||||
}
|
||||
return defs;
|
||||
};
|
||||
|
||||
// TODO(codedread): Consider moving the next to functions to bbox.js
|
||||
|
||||
// Function: svgedit.utilities.getPathBBox
|
||||
// Get correct BBox for a path in Webkit
|
||||
// Converted from code found here:
|
||||
// http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
||||
//
|
||||
// Parameters:
|
||||
// path - The path DOM element to get the BBox for
|
||||
//
|
||||
// Returns:
|
||||
// A BBox-like object
|
||||
svgedit.utilities.getPathBBox = function(path) {
|
||||
var seglist = path.pathSegList;
|
||||
var tot = seglist.numberOfItems;
|
||||
|
||||
var bounds = [[], []];
|
||||
var start = seglist.getItem(0);
|
||||
var P0 = [start.x, start.y];
|
||||
|
||||
for(var i=0; i < tot; i++) {
|
||||
var seg = seglist.getItem(i);
|
||||
if(!seg.x) continue;
|
||||
|
||||
// Add actual points to limits
|
||||
bounds[0].push(P0[0]);
|
||||
bounds[1].push(P0[1]);
|
||||
|
||||
if(seg.x1) {
|
||||
var P1 = [seg.x1, seg.y1],
|
||||
P2 = [seg.x2, seg.y2],
|
||||
P3 = [seg.x, seg.y];
|
||||
|
||||
for(var j=0; j < 2; j++) {
|
||||
|
||||
var calc = function(t) {
|
||||
return Math.pow(1-t,3) * P0[j]
|
||||
+ 3 * Math.pow(1-t,2) * t * P1[j]
|
||||
+ 3 * (1-t) * Math.pow(t,2) * P2[j]
|
||||
+ Math.pow(t,3) * P3[j];
|
||||
};
|
||||
|
||||
var b = 6 * P0[j] - 12 * P1[j] + 6 * P2[j];
|
||||
var a = -3 * P0[j] + 9 * P1[j] - 9 * P2[j] + 3 * P3[j];
|
||||
var c = 3 * P1[j] - 3 * P0[j];
|
||||
|
||||
if(a == 0) {
|
||||
if(b == 0) {
|
||||
continue;
|
||||
}
|
||||
var t = -c / b;
|
||||
if(0 < t && t < 1) {
|
||||
bounds[j].push(calc(t));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var b2ac = Math.pow(b,2) - 4 * c * a;
|
||||
if(b2ac < 0) continue;
|
||||
var t1 = (-b + Math.sqrt(b2ac))/(2 * a);
|
||||
if(0 < t1 && t1 < 1) bounds[j].push(calc(t1));
|
||||
var t2 = (-b - Math.sqrt(b2ac))/(2 * a);
|
||||
if(0 < t2 && t2 < 1) bounds[j].push(calc(t2));
|
||||
}
|
||||
P0 = P3;
|
||||
} else {
|
||||
bounds[0].push(seg.x);
|
||||
bounds[1].push(seg.y);
|
||||
}
|
||||
}
|
||||
|
||||
var x = Math.min.apply(null, bounds[0]);
|
||||
var w = Math.max.apply(null, bounds[0]) - x;
|
||||
var y = Math.min.apply(null, bounds[1]);
|
||||
var h = Math.max.apply(null, bounds[1]) - y;
|
||||
return {
|
||||
'x': x,
|
||||
'y': y,
|
||||
'width': w,
|
||||
'height': h
|
||||
};
|
||||
};
|
||||
|
||||
// Function: svgedit.utilities.getBBox
|
||||
// Get the given/selected element's bounding box object, convert it to be more
|
||||
// usable when necessary
|
||||
//
|
||||
// Parameters:
|
||||
// elem - Optional DOM element to get the BBox for
|
||||
svgedit.utilities.getBBox = function(elem) {
|
||||
var selected = elem || selectedElements[0];
|
||||
if (elem.nodeType != 1) return null;
|
||||
var ret = null;
|
||||
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(elname === 'path' && svgedit.browsersupport.isWebkit()) {
|
||||
ret = svgedit.utilities.getPathBBox(selected);
|
||||
} else if(elname === 'use' && !svgedit.browsersupport.isWebkit() || elname === 'foreignObject') {
|
||||
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(~visElems_arr.indexOf(elname)) {
|
||||
try { ret = selected.getBBox();}
|
||||
catch(e) {
|
||||
// Check if element is child of a foreignObject
|
||||
var fo = $(selected).closest("foreignObject");
|
||||
if(fo.length) {
|
||||
try {
|
||||
ret = fo[0].getBBox();
|
||||
} catch(e) {
|
||||
ret = null;
|
||||
}
|
||||
} else {
|
||||
ret = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ret) {
|
||||
ret = svgedit.utilities.bboxToObj(ret);
|
||||
}
|
||||
|
||||
// get the bounding box from the DOM (which is in that element's coordinate system)
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Function: svgedit.utilities.getRotationAngle
|
||||
// Get the rotation angle of the given/selected DOM element
|
||||
//
|
||||
// Parameters:
|
||||
// elem - Optional DOM element to get the angle for
|
||||
// to_rad - Boolean that when true returns the value in radians rather than degrees
|
||||
//
|
||||
// Returns:
|
||||
// Float with the angle in degrees or radians
|
||||
svgedit.utilities.getRotationAngle = function(elem, to_rad) {
|
||||
var selected = elem || selectedElements[0];
|
||||
// find the rotation transform (if any) and set it
|
||||
var tlist = svgedit.transformlist.getTransformList(selected);
|
||||
if(!tlist) return 0; // <svg> elements have no tlist
|
||||
var N = tlist.numberOfItems;
|
||||
for (var i = 0; i < N; ++i) {
|
||||
var xform = tlist.getItem(i);
|
||||
if (xform.type == 4) {
|
||||
return to_rad ? xform.angle * Math.PI / 180.0 : xform.angle;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
};
|
||||
|
||||
})();
|
260
public/svg-edit/editor/units.js
Normal file
260
public/svg-edit/editor/units.js
Normal file
|
@ -0,0 +1,260 @@
|
|||
/**
|
||||
* Package: svgedit.units
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*/
|
||||
|
||||
// Dependencies:
|
||||
// 1) jQuery
|
||||
|
||||
(function() {
|
||||
|
||||
if (!window.svgedit) {
|
||||
window.svgedit = {};
|
||||
}
|
||||
|
||||
if (!svgedit.units) {
|
||||
svgedit.units = {};
|
||||
}
|
||||
|
||||
var w_attrs = ['x', 'x1', 'cx', 'rx', 'width'];
|
||||
var h_attrs = ['y', 'y1', 'cy', 'ry', 'height'];
|
||||
var unit_attrs = $.merge(['r','radius'], w_attrs);
|
||||
|
||||
var unitNumMap = {
|
||||
'%': 2,
|
||||
'em': 3,
|
||||
'ex': 4,
|
||||
'px': 5,
|
||||
'cm': 6,
|
||||
'mm': 7,
|
||||
'in': 8,
|
||||
'pt': 9,
|
||||
'pc': 10
|
||||
};
|
||||
|
||||
$.merge(unit_attrs, h_attrs);
|
||||
|
||||
// Container of elements.
|
||||
var elementContainer_;
|
||||
|
||||
/**
|
||||
* Stores mapping of unit type to user coordinates.
|
||||
*/
|
||||
var typeMap_ = {px: 1};
|
||||
|
||||
/**
|
||||
* ElementContainer interface
|
||||
*
|
||||
* function getBaseUnit() - returns a string of the base unit type of the container ("em")
|
||||
* function getElement() - returns an element in the container given an id
|
||||
* function getHeight() - returns the container's height
|
||||
* function getWidth() - returns the container's width
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function: svgedit.units.init()
|
||||
* Initializes this module.
|
||||
*
|
||||
* Parameters:
|
||||
* elementContainer - an object implementing the ElementContainer interface.
|
||||
*/
|
||||
svgedit.units.init = function(elementContainer) {
|
||||
elementContainer_ = elementContainer;
|
||||
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
|
||||
// Get correct em/ex values by creating a temporary SVG.
|
||||
var svg = document.createElementNS(svgns, 'svg');
|
||||
document.body.appendChild(svg);
|
||||
var rect = document.createElementNS(svgns,'rect');
|
||||
rect.setAttribute('width',"1em");
|
||||
rect.setAttribute('height',"1ex");
|
||||
rect.setAttribute('x',"1in");
|
||||
svg.appendChild(rect);
|
||||
var bb = rect.getBBox();
|
||||
document.body.removeChild(svg);
|
||||
|
||||
var inch = bb.x;
|
||||
typeMap_['em'] = bb.width;
|
||||
typeMap_['ex'] = bb.height;
|
||||
typeMap_['in'] = inch;
|
||||
typeMap_['cm'] = inch / 2.54;
|
||||
typeMap_['mm'] = inch / 25.4;
|
||||
typeMap_['pt'] = inch / 72;
|
||||
typeMap_['pc'] = inch / 6;
|
||||
typeMap_['%'] = 0;
|
||||
};
|
||||
|
||||
// Group: Unit conversion functions
|
||||
|
||||
// Function: svgedit.units.getTypeMap
|
||||
// Returns the unit object with values for each unit
|
||||
svgedit.units.getTypeMap = function() {
|
||||
return typeMap_;
|
||||
};
|
||||
|
||||
// Function: svgedit.units.convertUnit
|
||||
// Converts the number to given unit or baseUnit
|
||||
svgedit.units.convertUnit = function(val, unit) {
|
||||
unit = unit || elementContainer_.getBaseUnit();
|
||||
// baseVal.convertToSpecifiedUnits(unitNumMap[unit]);
|
||||
// var val = baseVal.valueInSpecifiedUnits;
|
||||
// baseVal.convertToSpecifiedUnits(1);
|
||||
return val / typeMap_[unit];
|
||||
};
|
||||
|
||||
// Function: svgedit.units.setUnitAttr
|
||||
// Sets an element's attribute based on the unit in its current value.
|
||||
//
|
||||
// Parameters:
|
||||
// elem - DOM element to be changed
|
||||
// attr - String with the name of the attribute associated with the value
|
||||
// val - String with the attribute value to convert
|
||||
svgedit.units.setUnitAttr = function(elem, attr, val) {
|
||||
if(!isNaN(val)) {
|
||||
// New value is a number, so check currently used unit
|
||||
var old_val = elem.getAttribute(attr);
|
||||
|
||||
// Enable this for alternate mode
|
||||
// if(old_val !== null && (isNaN(old_val) || elementContainer_.getBaseUnit() !== 'px')) {
|
||||
// // Old value was a number, so get unit, then convert
|
||||
// var unit;
|
||||
// if(old_val.substr(-1) === '%') {
|
||||
// var res = getResolution();
|
||||
// unit = '%';
|
||||
// val *= 100;
|
||||
// if(w_attrs.indexOf(attr) >= 0) {
|
||||
// val = val / res.w;
|
||||
// } else if(h_attrs.indexOf(attr) >= 0) {
|
||||
// val = val / res.h;
|
||||
// } else {
|
||||
// return val / Math.sqrt((res.w*res.w) + (res.h*res.h))/Math.sqrt(2);
|
||||
// }
|
||||
// } else {
|
||||
// if(elementContainer_.getBaseUnit() !== 'px') {
|
||||
// unit = elementContainer_.getBaseUnit();
|
||||
// } else {
|
||||
// unit = old_val.substr(-2);
|
||||
// }
|
||||
// val = val / typeMap_[unit];
|
||||
// }
|
||||
//
|
||||
// val += unit;
|
||||
// }
|
||||
}
|
||||
elem.setAttribute(attr, val);
|
||||
};
|
||||
|
||||
var attrsToConvert = {
|
||||
"line": ['x1', 'x2', 'y1', 'y2'],
|
||||
"circle": ['cx', 'cy', 'r'],
|
||||
"ellipse": ['cx', 'cy', 'rx', 'ry'],
|
||||
"foreignObject": ['x', 'y', 'width', 'height'],
|
||||
"rect": ['x', 'y', 'width', 'height'],
|
||||
"image": ['x', 'y', 'width', 'height'],
|
||||
"use": ['x', 'y', 'width', 'height'],
|
||||
"text": ['x', 'y']
|
||||
};
|
||||
|
||||
// Function: svgedit.units.convertAttrs
|
||||
// Converts all applicable attributes to the configured baseUnit
|
||||
//
|
||||
// Parameters:
|
||||
// element - a DOM element whose attributes should be converted
|
||||
svgedit.units.convertAttrs = function(element) {
|
||||
var elName = element.tagName;
|
||||
var unit = elementContainer_.getBaseUnit();
|
||||
var attrs = attrsToConvert[elName];
|
||||
if(!attrs) return;
|
||||
var len = attrs.length
|
||||
for(var i = 0; i < len; i++) {
|
||||
var attr = attrs[i];
|
||||
var cur = element.getAttribute(attr);
|
||||
if(cur) {
|
||||
if(!isNaN(cur)) {
|
||||
element.setAttribute(attr, (cur / typeMap_[unit]) + unit);
|
||||
} else {
|
||||
// Convert existing?
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.units.convertToNum
|
||||
// Converts given values to numbers. Attributes must be supplied in
|
||||
// case a percentage is given
|
||||
//
|
||||
// Parameters:
|
||||
// attr - String with the name of the attribute associated with the value
|
||||
// val - String with the attribute value to convert
|
||||
svgedit.units.convertToNum = function(attr, val) {
|
||||
// Return a number if that's what it already is
|
||||
if(!isNaN(val)) return val-0;
|
||||
|
||||
if(val.substr(-1) === '%') {
|
||||
// Deal with percentage, depends on attribute
|
||||
var num = val.substr(0, val.length-1)/100;
|
||||
var width = elementContainer_.getWidth();
|
||||
var height = elementContainer_.getHeight();
|
||||
|
||||
if(w_attrs.indexOf(attr) >= 0) {
|
||||
return num * width;
|
||||
} else if(h_attrs.indexOf(attr) >= 0) {
|
||||
return num * height;
|
||||
} else {
|
||||
return num * Math.sqrt((width*width) + (height*height))/Math.sqrt(2);
|
||||
}
|
||||
} else {
|
||||
var unit = val.substr(-2);
|
||||
var num = val.substr(0, val.length-2);
|
||||
// Note that this multiplication turns the string into a number
|
||||
return num * typeMap_[unit];
|
||||
}
|
||||
};
|
||||
|
||||
// Function: svgedit.units.isValidUnit
|
||||
// Check if an attribute's value is in a valid format
|
||||
//
|
||||
// Parameters:
|
||||
// attr - String with the name of the attribute associated with the value
|
||||
// val - String with the attribute value to check
|
||||
svgedit.units.isValidUnit = function(attr, val) {
|
||||
var valid = false;
|
||||
if(unit_attrs.indexOf(attr) >= 0) {
|
||||
// True if it's just a number
|
||||
if(!isNaN(val)) {
|
||||
valid = true;
|
||||
} else {
|
||||
// Not a number, check if it has a valid unit
|
||||
val = val.toLowerCase();
|
||||
$.each(typeMap_, function(unit) {
|
||||
if(valid) return;
|
||||
var re = new RegExp('^-?[\\d\\.]+' + unit + '$');
|
||||
if(re.test(val)) valid = true;
|
||||
});
|
||||
}
|
||||
} else if (attr == "id") {
|
||||
// if we're trying to change the id, make sure it's not already present in the doc
|
||||
// and the id value is valid.
|
||||
|
||||
var result = false;
|
||||
// because getElem() can throw an exception in the case of an invalid id
|
||||
// (according to http://www.w3.org/TR/xml-id/ IDs must be a NCName)
|
||||
// we wrap it in an exception and only return true if the ID was valid and
|
||||
// not already present
|
||||
try {
|
||||
var elem = elementContainer_.getElement(val);
|
||||
result = (elem == null);
|
||||
} catch(e) {}
|
||||
return result;
|
||||
} else valid = true;
|
||||
|
||||
return valid;
|
||||
};
|
||||
|
||||
|
||||
})();
|
24
public/svg-edit/test/all_tests.html
Normal file
24
public/svg-edit/test/all_tests.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>All SVG-edit Tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>All SVG-edit Tests</h1>
|
||||
<p>This file frames all SVG-edit test pages. This should only include tests known to work. These tests are known to pass 100% in the following: Firefox 3.6, Chrome 7, IE9 Preview 6 (1.9.8006.6000), Opera 10.63. If a test is broken in this page, it is possible that <em>YOU</em> broke it. Please do not submit code that breaks any of these tests.</p>
|
||||
<iframe src='svgtransformlist_test.html' width='100%' height='300'></iframe>
|
||||
<iframe src='math_test.html' width='100%' height='300'></iframe>
|
||||
<iframe src='svgutils_test.html' width='100%' height='300'></iframe>
|
||||
<iframe src='history_test.html' width='100%' height='300'></iframe>
|
||||
<iframe src='select_test.html' width='100%' height='300'></iframe>
|
||||
</body>
|
||||
<script>
|
||||
window.setTimeout(function() {
|
||||
var iframes = document.getElementsByTagName('iframe');
|
||||
for (var i = 0, len = iframes.length; i < len; ++i) {
|
||||
var f = iframes[i];
|
||||
f.style.height = (f.contentDocument.body.scrollHeight + 20) + 'px';
|
||||
}
|
||||
}, 1200);
|
||||
</script>
|
||||
</html>
|
575
public/svg-edit/test/history_test.html
Normal file
575
public/svg-edit/test/history_test.html
Normal file
|
@ -0,0 +1,575 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel='stylesheet' href='qunit/qunit.css' type='text/css'/>
|
||||
<script src='../editor/jquery.js'></script>
|
||||
<script type='text/javascript' src='../editor/history.js'></script>
|
||||
<script type='text/javascript' src='qunit/qunit.js'></script>
|
||||
<script type='text/javascript'>
|
||||
$(function() {
|
||||
// TODO(codedread): Write tests for handling history events.
|
||||
|
||||
// Mocked out methods.
|
||||
svgedit.transformlist = {};
|
||||
svgedit.transformlist.removeElementFromListMap = function(elem) {};
|
||||
svgedit.utilities = {};
|
||||
svgedit.utilities.getHref = function(elem) { return '#foo'; };
|
||||
svgedit.utilities.setHref = function(elem, val) {};
|
||||
svgedit.utilities.getRotationAngle = function(elem) { return 0; };
|
||||
|
||||
// log function
|
||||
QUnit.log = function(result, message) {
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(result +' :: '+ message);
|
||||
}
|
||||
};
|
||||
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
var svg = document.createElementNS(svgns, 'svg');
|
||||
var undoMgr = null;
|
||||
var divparent = document.getElementById('divparent');
|
||||
var div1 = document.getElementById('div1');
|
||||
var div2 = document.getElementById('div2');
|
||||
var div3 = document.getElementById('div3');
|
||||
var div4 = document.getElementById('div4');
|
||||
var div5 = document.getElementById('div5');
|
||||
|
||||
module('svgedit.history');
|
||||
|
||||
var MockCommand = function(opt_text) { this.text_ = opt_text; };
|
||||
MockCommand.prototype.apply = function() {};
|
||||
MockCommand.prototype.unapply = function() {};
|
||||
MockCommand.prototype.getText = function() { return this.text_; };
|
||||
MockCommand.prototype.elements = function() { return []; };
|
||||
|
||||
var MockHistoryEventHandler = function() {};
|
||||
MockHistoryEventHandler.prototype.handleHistoryEvent = function(eventType, command) {};
|
||||
|
||||
function setUp() {
|
||||
undoMgr = new svgedit.history.UndoManager();
|
||||
}
|
||||
function tearDown() {
|
||||
undoMgr = null;
|
||||
}
|
||||
|
||||
test('Test svgedit.history package', function() {
|
||||
expect(13);
|
||||
|
||||
ok(svgedit.history);
|
||||
ok(svgedit.history.MoveElementCommand);
|
||||
ok(svgedit.history.InsertElementCommand);
|
||||
ok(svgedit.history.ChangeElementCommand);
|
||||
ok(svgedit.history.RemoveElementCommand);
|
||||
ok(svgedit.history.BatchCommand);
|
||||
ok(svgedit.history.UndoManager);
|
||||
equals(typeof svgedit.history.MoveElementCommand, typeof function(){});
|
||||
equals(typeof svgedit.history.InsertElementCommand, typeof function(){});
|
||||
equals(typeof svgedit.history.ChangeElementCommand, typeof function(){});
|
||||
equals(typeof svgedit.history.RemoveElementCommand, typeof function(){});
|
||||
equals(typeof svgedit.history.BatchCommand, typeof function(){});
|
||||
equals(typeof svgedit.history.UndoManager, typeof function(){});
|
||||
});
|
||||
|
||||
test('Test UndoManager methods', function() {
|
||||
expect(14);
|
||||
setUp();
|
||||
|
||||
ok(undoMgr);
|
||||
ok(undoMgr.addCommandToHistory);
|
||||
ok(undoMgr.getUndoStackSize);
|
||||
ok(undoMgr.getRedoStackSize);
|
||||
ok(undoMgr.resetUndoStack);
|
||||
ok(undoMgr.getNextUndoCommandText);
|
||||
ok(undoMgr.getNextRedoCommandText);
|
||||
|
||||
equals(typeof undoMgr, typeof {});
|
||||
equals(typeof undoMgr.addCommandToHistory, typeof function(){});
|
||||
equals(typeof undoMgr.getUndoStackSize, typeof function(){});
|
||||
equals(typeof undoMgr.getRedoStackSize, typeof function(){});
|
||||
equals(typeof undoMgr.resetUndoStack, typeof function(){});
|
||||
equals(typeof undoMgr.getNextUndoCommandText, typeof function(){});
|
||||
equals(typeof undoMgr.getNextRedoCommandText, typeof function(){});
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test UndoManager.addCommandToHistory() function', function() {
|
||||
expect(3);
|
||||
|
||||
setUp();
|
||||
|
||||
equals(undoMgr.getUndoStackSize(), 0);
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
equals(undoMgr.getUndoStackSize(), 1);
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
equals(undoMgr.getUndoStackSize(), 2);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test UndoManager.getUndoStackSize() and getRedoStackSize() functions', function() {
|
||||
expect(18);
|
||||
|
||||
setUp();
|
||||
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
|
||||
equals(undoMgr.getUndoStackSize(), 3);
|
||||
equals(undoMgr.getRedoStackSize(), 0);
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getUndoStackSize(), 2);
|
||||
equals(undoMgr.getRedoStackSize(), 1);
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getUndoStackSize(), 1);
|
||||
equals(undoMgr.getRedoStackSize(), 2);
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getUndoStackSize(), 0);
|
||||
equals(undoMgr.getRedoStackSize(), 3);
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getUndoStackSize(), 0);
|
||||
equals(undoMgr.getRedoStackSize(), 3);
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getUndoStackSize(), 1);
|
||||
equals(undoMgr.getRedoStackSize(), 2);
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getUndoStackSize(), 2);
|
||||
equals(undoMgr.getRedoStackSize(), 1);
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getUndoStackSize(), 3);
|
||||
equals(undoMgr.getRedoStackSize(), 0);
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getUndoStackSize(), 3);
|
||||
equals(undoMgr.getRedoStackSize(), 0);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test UndoManager.resetUndoStackSize() function', function() {
|
||||
expect(4);
|
||||
|
||||
setUp();
|
||||
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
undoMgr.addCommandToHistory(new MockCommand());
|
||||
undoMgr.undo();
|
||||
|
||||
equals(undoMgr.getUndoStackSize(), 2);
|
||||
equals(undoMgr.getRedoStackSize(), 1);
|
||||
|
||||
undoMgr.resetUndoStack();
|
||||
|
||||
equals(undoMgr.getUndoStackSize(), 0);
|
||||
equals(undoMgr.getRedoStackSize(), 0);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test UndoManager.getNextUndoCommandText() function', function() {
|
||||
expect(9);
|
||||
|
||||
setUp();
|
||||
|
||||
equals(undoMgr.getNextUndoCommandText(), '');
|
||||
|
||||
undoMgr.addCommandToHistory(new MockCommand('First'));
|
||||
undoMgr.addCommandToHistory(new MockCommand('Second'));
|
||||
undoMgr.addCommandToHistory(new MockCommand('Third'));
|
||||
|
||||
equals(undoMgr.getNextUndoCommandText(), 'Third');
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getNextUndoCommandText(), 'Second');
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getNextUndoCommandText(), 'First');
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getNextUndoCommandText(), '');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getNextUndoCommandText(), 'First');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getNextUndoCommandText(), 'Second');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getNextUndoCommandText(), 'Third');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getNextUndoCommandText(), 'Third');
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test UndoManager.getNextRedoCommandText() function', function() {
|
||||
expect(8);
|
||||
|
||||
setUp();
|
||||
|
||||
equals(undoMgr.getNextRedoCommandText(), '');
|
||||
|
||||
undoMgr.addCommandToHistory(new MockCommand('First'));
|
||||
undoMgr.addCommandToHistory(new MockCommand('Second'));
|
||||
undoMgr.addCommandToHistory(new MockCommand('Third'));
|
||||
|
||||
equals(undoMgr.getNextRedoCommandText(), '');
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getNextRedoCommandText(), 'Third');
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getNextRedoCommandText(), 'Second');
|
||||
|
||||
undoMgr.undo();
|
||||
equals(undoMgr.getNextRedoCommandText(), 'First');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getNextRedoCommandText(), 'Second');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getNextRedoCommandText(), 'Third');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(undoMgr.getNextRedoCommandText(), '');
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test UndoManager.undo() and redo() functions', function() {
|
||||
expect(10);
|
||||
|
||||
setUp();
|
||||
|
||||
var lastCalled = null;
|
||||
var cmd1 = new MockCommand();
|
||||
var cmd2 = new MockCommand();
|
||||
var cmd3 = new MockCommand();
|
||||
cmd1.apply = function() { lastCalled = 'cmd1.apply'; };
|
||||
cmd2.apply = function() { lastCalled = 'cmd2.apply'; };
|
||||
cmd3.apply = function() { lastCalled = 'cmd3.apply'; };
|
||||
cmd1.unapply = function() { lastCalled = 'cmd1.unapply'; };
|
||||
cmd2.unapply = function() { lastCalled = 'cmd2.unapply'; };
|
||||
cmd3.unapply = function() { lastCalled = 'cmd3.unapply'; };
|
||||
|
||||
undoMgr.addCommandToHistory(cmd1);
|
||||
undoMgr.addCommandToHistory(cmd2);
|
||||
undoMgr.addCommandToHistory(cmd3);
|
||||
|
||||
ok(!lastCalled);
|
||||
|
||||
undoMgr.undo();
|
||||
equals(lastCalled, 'cmd3.unapply');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(lastCalled, 'cmd3.apply');
|
||||
|
||||
undoMgr.undo();
|
||||
undoMgr.undo();
|
||||
equals(lastCalled, 'cmd2.unapply');
|
||||
|
||||
undoMgr.undo();
|
||||
equals(lastCalled, 'cmd1.unapply');
|
||||
lastCalled = null;
|
||||
|
||||
undoMgr.undo();
|
||||
ok(!lastCalled);
|
||||
|
||||
undoMgr.redo();
|
||||
equals(lastCalled, 'cmd1.apply');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(lastCalled, 'cmd2.apply');
|
||||
|
||||
undoMgr.redo();
|
||||
equals(lastCalled, 'cmd3.apply');
|
||||
lastCalled = null;
|
||||
|
||||
undoMgr.redo();
|
||||
ok(!lastCalled);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test MoveElementCommand', function() {
|
||||
expect(26);
|
||||
|
||||
setUp();
|
||||
|
||||
var move = new svgedit.history.MoveElementCommand(div3, div1, divparent);
|
||||
ok(move.unapply);
|
||||
ok(move.apply);
|
||||
equals(typeof move.unapply, typeof function(){});
|
||||
equals(typeof move.apply, typeof function(){});
|
||||
|
||||
move.unapply();
|
||||
equals(divparent.firstElementChild, div3);
|
||||
equals(divparent.firstElementChild.nextElementSibling, div1);
|
||||
equals(divparent.lastElementChild, div2);
|
||||
|
||||
move.apply();
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(divparent.firstElementChild.nextElementSibling, div2);
|
||||
equals(divparent.lastElementChild, div3);
|
||||
|
||||
move = new svgedit.history.MoveElementCommand(div1, null, divparent);
|
||||
|
||||
move.unapply();
|
||||
equals(divparent.firstElementChild, div2);
|
||||
equals(divparent.firstElementChild.nextElementSibling, div3);
|
||||
equals(divparent.lastElementChild, div1);
|
||||
|
||||
move.apply();
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(divparent.firstElementChild.nextElementSibling, div2);
|
||||
equals(divparent.lastElementChild, div3);
|
||||
|
||||
move = new svgedit.history.MoveElementCommand(div2, div5, div4);
|
||||
|
||||
move.unapply();
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(divparent.firstElementChild.nextElementSibling, div3);
|
||||
equals(divparent.lastElementChild, div3);
|
||||
equals(div4.firstElementChild, div2);
|
||||
equals(div4.firstElementChild.nextElementSibling, div5);
|
||||
|
||||
move.apply();
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(divparent.firstElementChild.nextElementSibling, div2);
|
||||
equals(divparent.lastElementChild, div3);
|
||||
equals(div4.firstElementChild, div5);
|
||||
equals(div4.lastElementChild, div5);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test InsertElementCommand', function() {
|
||||
expect(20);
|
||||
|
||||
setUp();
|
||||
|
||||
var insert = new svgedit.history.InsertElementCommand(div3);
|
||||
ok(insert.unapply);
|
||||
ok(insert.apply);
|
||||
equals(typeof insert.unapply, typeof function(){});
|
||||
equals(typeof insert.apply, typeof function(){});
|
||||
|
||||
insert.unapply();
|
||||
equals(divparent.childElementCount, 2);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div2);
|
||||
equals(divparent.lastElementChild, div2);
|
||||
|
||||
insert.apply();
|
||||
equals(divparent.childElementCount, 3);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div2);
|
||||
equals(div2.nextElementSibling, div3);
|
||||
|
||||
insert = new svgedit.history.InsertElementCommand(div2);
|
||||
|
||||
insert.unapply();
|
||||
equals(divparent.childElementCount, 2);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div3);
|
||||
equals(divparent.lastElementChild, div3);
|
||||
|
||||
insert.apply();
|
||||
equals(divparent.childElementCount, 3);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div2);
|
||||
equals(div2.nextElementSibling, div3);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test RemoveElementCommand', function() {
|
||||
expect(22);
|
||||
|
||||
setUp();
|
||||
|
||||
var div6 = document.createElement('div');
|
||||
div6.id = 'div6';
|
||||
|
||||
var remove = new svgedit.history.RemoveElementCommand(div6, null, divparent);
|
||||
ok(remove.unapply);
|
||||
ok(remove.apply);
|
||||
equals(typeof remove.unapply, typeof function(){});
|
||||
equals(typeof remove.apply, typeof function(){});
|
||||
|
||||
remove.unapply();
|
||||
equals(divparent.childElementCount, 4);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div2);
|
||||
equals(div2.nextElementSibling, div3);
|
||||
equals(div3.nextElementSibling, div6);
|
||||
|
||||
remove.apply();
|
||||
equals(divparent.childElementCount, 3);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div2);
|
||||
equals(div2.nextElementSibling, div3);
|
||||
|
||||
remove = new svgedit.history.RemoveElementCommand(div6, div2, divparent);
|
||||
|
||||
remove.unapply();
|
||||
equals(divparent.childElementCount, 4);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div6);
|
||||
equals(div6.nextElementSibling, div2);
|
||||
equals(div2.nextElementSibling, div3);
|
||||
|
||||
remove.apply();
|
||||
equals(divparent.childElementCount, 3);
|
||||
equals(divparent.firstElementChild, div1);
|
||||
equals(div1.nextElementSibling, div2);
|
||||
equals(div2.nextElementSibling, div3);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test ChangeElementCommand', function() {
|
||||
expect(20);
|
||||
|
||||
setUp();
|
||||
|
||||
div1.setAttribute('title', 'new title');
|
||||
var change = new svgedit.history.ChangeElementCommand(div1,
|
||||
{'title': 'old title', 'class': 'foo'});
|
||||
ok(change.unapply);
|
||||
ok(change.apply);
|
||||
equals(typeof change.unapply, typeof function(){});
|
||||
equals(typeof change.apply, typeof function(){});
|
||||
|
||||
change.unapply();
|
||||
equals(div1.getAttribute('title'), 'old title');
|
||||
equals(div1.getAttribute('class'), 'foo');
|
||||
|
||||
change.apply();
|
||||
equals(div1.getAttribute('title'), 'new title');
|
||||
ok(!div1.getAttribute('class'));
|
||||
|
||||
div1.textContent = 'inner text';
|
||||
change = new svgedit.history.ChangeElementCommand(div1,
|
||||
{'#text': null});
|
||||
|
||||
change.unapply();
|
||||
ok(!div1.textContent);
|
||||
|
||||
change.apply();
|
||||
equals(div1.textContent, 'inner text');
|
||||
|
||||
div1.textContent = '';
|
||||
change = new svgedit.history.ChangeElementCommand(div1,
|
||||
{'#text': 'old text'});
|
||||
|
||||
change.unapply();
|
||||
equals(div1.textContent, 'old text');
|
||||
|
||||
change.apply();
|
||||
ok(!div1.textContent);
|
||||
|
||||
// TODO(codedread): Refactor this #href stuff in history.js and svgcanvas.js
|
||||
var rect = document.createElementNS(svgns, 'rect');
|
||||
var justCalled = null;
|
||||
var gethrefvalue = null;
|
||||
var sethrefvalue = null;
|
||||
svgedit.utilities.getHref = function(elem) {
|
||||
equals(elem, rect);
|
||||
justCalled = 'getHref';
|
||||
return gethrefvalue;
|
||||
};
|
||||
svgedit.utilities.setHref = function(elem, val) {
|
||||
equals(elem, rect);
|
||||
equals(val, sethrefvalue);
|
||||
justCalled = 'setHref';
|
||||
};
|
||||
|
||||
gethrefvalue = '#newhref';
|
||||
change = new svgedit.history.ChangeElementCommand(rect,
|
||||
{'#href': '#oldhref'});
|
||||
equals(justCalled, 'getHref');
|
||||
|
||||
justCalled = null;
|
||||
sethrefvalue = '#oldhref';
|
||||
change.unapply();
|
||||
equals(justCalled, 'setHref');
|
||||
|
||||
justCalled = null;
|
||||
sethrefvalue = '#newhref';
|
||||
change.apply();
|
||||
equals(justCalled, 'setHref');
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test BatchCommand', function() {
|
||||
expect(13);
|
||||
|
||||
setUp();
|
||||
|
||||
var concatResult = '';
|
||||
MockCommand.prototype.apply = function() { concatResult += this.text_; };
|
||||
|
||||
var batch = new svgedit.history.BatchCommand();
|
||||
ok(batch.unapply);
|
||||
ok(batch.apply);
|
||||
ok(batch.addSubCommand);
|
||||
ok(batch.isEmpty);
|
||||
equals(typeof batch.unapply, typeof function(){});
|
||||
equals(typeof batch.apply, typeof function(){});
|
||||
equals(typeof batch.addSubCommand, typeof function(){});
|
||||
equals(typeof batch.isEmpty, typeof function(){});
|
||||
|
||||
ok(batch.isEmpty());
|
||||
|
||||
batch.addSubCommand(new MockCommand('a'));
|
||||
ok(!batch.isEmpty());
|
||||
batch.addSubCommand(new MockCommand('b'));
|
||||
batch.addSubCommand(new MockCommand('c'));
|
||||
|
||||
ok(!concatResult);
|
||||
batch.apply();
|
||||
equals(concatResult, 'abc');
|
||||
|
||||
MockCommand.prototype.apply = function() {};
|
||||
MockCommand.prototype.unapply = function() { concatResult += this.text_; };
|
||||
concatResult = '';
|
||||
batch.unapply();
|
||||
equals(concatResult, 'cba');
|
||||
|
||||
MockCommand.prototype.unapply = function() {};
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='qunit-header'>Unit Tests for history.js</h1>
|
||||
<h2 id='qunit-banner'></h2>
|
||||
<h2 id='qunit-userAgent'></h2>
|
||||
<ol id='qunit-tests'>
|
||||
</ol>
|
||||
<div id='divparent' style='visibility:hidden'>
|
||||
<div id='div1'></div>
|
||||
<div id='div2'></div>
|
||||
<div id='div3'></div>
|
||||
</div>
|
||||
<div id='div4' style='visibility:hidden'>
|
||||
<div id='div5'></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
114
public/svg-edit/test/math_test.html
Normal file
114
public/svg-edit/test/math_test.html
Normal file
|
@ -0,0 +1,114 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel='stylesheet' href='qunit/qunit.css' type='text/css'/>
|
||||
<script src='../editor/jquery.js'></script>
|
||||
<script type='text/javascript' src='../editor/math.js'></script>
|
||||
<script type='text/javascript' src='qunit/qunit.js'></script>
|
||||
<script type='text/javascript'>
|
||||
$(function() {
|
||||
// log function
|
||||
QUnit.log = function(result, message) {
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(result +' :: '+ message);
|
||||
}
|
||||
};
|
||||
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
var svg = document.createElementNS(svgns, 'svg');
|
||||
|
||||
module('svgedit.math');
|
||||
|
||||
test('Test svgedit.math package', function() {
|
||||
expect(7);
|
||||
|
||||
ok(svgedit.math);
|
||||
ok(svgedit.math.transformPoint);
|
||||
ok(svgedit.math.isIdentity);
|
||||
ok(svgedit.math.matrixMultiply);
|
||||
equals(typeof svgedit.math.transformPoint, typeof function(){});
|
||||
equals(typeof svgedit.math.isIdentity, typeof function(){});
|
||||
equals(typeof svgedit.math.matrixMultiply, typeof function(){});
|
||||
});
|
||||
|
||||
test('Test svgedit.math.transformPoint() function', function() {
|
||||
expect(6);
|
||||
var transformPoint = svgedit.math.transformPoint;
|
||||
|
||||
var m = svg.createSVGMatrix();
|
||||
m.a = 1; m.b = 0;
|
||||
m.c = 0; m.d = 1;
|
||||
m.e = 0; m.f = 0;
|
||||
var pt = transformPoint(100, 200, m);
|
||||
equals(pt.x, 100);
|
||||
equals(pt.y, 200);
|
||||
|
||||
m.e = 300; m.f = 400;
|
||||
pt = transformPoint(100, 200, m);
|
||||
equals(pt.x, 400);
|
||||
equals(pt.y, 600);
|
||||
|
||||
m.a = 0.5; m.b = 0.75;
|
||||
m.c = 1.25; m.d = 2;
|
||||
pt = transformPoint(100, 200, m);
|
||||
equals(pt.x, 100 * m.a + 200 * m.c + m.e);
|
||||
equals(pt.y, 100 * m.b + 200 * m.d + m.f);
|
||||
});
|
||||
|
||||
test('Test svgedit.math.isIdentity() function', function() {
|
||||
expect(2);
|
||||
|
||||
ok(svgedit.math.isIdentity(svg.createSVGMatrix()));
|
||||
|
||||
var m = svg.createSVGMatrix();
|
||||
m.a = 1; m.b = 0;
|
||||
m.c = 0; m.d = 1;
|
||||
m.e = 0; m.f = 0;
|
||||
ok(svgedit.math.isIdentity(m));
|
||||
});
|
||||
|
||||
test('Test svgedit.math.matrixMultiply() function', function() {
|
||||
expect(5);
|
||||
var mult = svgedit.math.matrixMultiply;
|
||||
var isIdentity = svgedit.math.isIdentity;
|
||||
|
||||
// translate there and back
|
||||
var tr_1 = svg.createSVGMatrix().translate(100,50),
|
||||
tr_2 = svg.createSVGMatrix().translate(-90,0),
|
||||
tr_3 = svg.createSVGMatrix().translate(-10,-50),
|
||||
I = mult(tr_1,tr_2,tr_3);
|
||||
ok(isIdentity(I), 'Expected identity matrix when translating there and back');
|
||||
|
||||
// rotate there and back
|
||||
// TODO: currently Mozilla fails this when rotating back at -50 and then -40 degrees
|
||||
// (b and c are *almost* zero, but not zero)
|
||||
var rot_there = svg.createSVGMatrix().rotate(90),
|
||||
rot_back = svg.createSVGMatrix().rotate(-90); // TODO: set this to -50
|
||||
rot_back_more = svg.createSVGMatrix().rotate(0); // TODO: set this to -40
|
||||
I = mult(rot_there, rot_back, rot_back_more);
|
||||
ok(isIdentity(I), 'Expected identity matrix when rotating there and back');
|
||||
|
||||
// scale up and down
|
||||
var scale_up = svg.createSVGMatrix().scale(4),
|
||||
scale_down = svg.createSVGMatrix().scaleNonUniform(0.25,1);
|
||||
scale_down_more = svg.createSVGMatrix().scaleNonUniform(1,0.25);
|
||||
I = mult(scale_up, scale_down, scale_down_more);
|
||||
ok(isIdentity(I), 'Expected identity matrix when scaling up and down');
|
||||
|
||||
// test multiplication with its inverse
|
||||
I = mult(rot_there, rot_there.inverse());
|
||||
ok(isIdentity(I), 'Expected identity matrix when multiplying a matrix by its inverse');
|
||||
I = mult(rot_there.inverse(), rot_there);
|
||||
ok(isIdentity(I), 'Expected identity matrix when multiplying a matrix by its inverse');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='qunit-header'>Unit Tests for math.js</h1>
|
||||
<h2 id='qunit-banner'></h2>
|
||||
<h2 id='qunit-userAgent'></h2>
|
||||
<ol id='qunit-tests'>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
140
public/svg-edit/test/select_test.html
Normal file
140
public/svg-edit/test/select_test.html
Normal file
|
@ -0,0 +1,140 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel='stylesheet' href='qunit/qunit.css' type='text/css'/>
|
||||
<script src='../editor/jquery.js'></script>
|
||||
<script type='text/javascript' src='../editor/browsersupport.js'></script>
|
||||
<script type='text/javascript' src='../editor/math.js'></script>
|
||||
<script type='text/javascript' src='../editor/svgutils.js'></script>
|
||||
<script type='text/javascript' src='../editor/select.js'></script>
|
||||
<script type='text/javascript' src='qunit/qunit.js'></script>
|
||||
<script type='text/javascript'>
|
||||
$(function() {
|
||||
// log function
|
||||
QUnit.log = function(result, message) {
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(result +' :: '+ message);
|
||||
}
|
||||
};
|
||||
|
||||
module('svgedit.select');
|
||||
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
var sandbox = document.getElementById('sandbox');
|
||||
var svgroot;
|
||||
var svgcontent;
|
||||
var rect;
|
||||
var mockConfig = {
|
||||
dimensions: [640,480]
|
||||
};
|
||||
var mockFactory = {
|
||||
createSVGElement: function(jsonMap) {
|
||||
var elem = document.createElementNS(svgns, jsonMap['element']);
|
||||
for (var attr in jsonMap['attr']) {
|
||||
elem.setAttribute(attr, jsonMap['attr'][attr]);
|
||||
}
|
||||
return elem;
|
||||
},
|
||||
svgRoot: function() { return svgroot; },
|
||||
svgContent: function() { return svgcontent; }
|
||||
};
|
||||
|
||||
function setUp() {
|
||||
svgroot = mockFactory.createSVGElement({
|
||||
'element': 'svg',
|
||||
'attr': {'id': 'svgroot'}
|
||||
});
|
||||
svgcontent = svgroot.appendChild(
|
||||
mockFactory.createSVGElement({
|
||||
'element': 'svg',
|
||||
'attr': {'id': 'svgcontent'}
|
||||
})
|
||||
);
|
||||
rect = svgcontent.appendChild(
|
||||
mockFactory.createSVGElement({
|
||||
'element': 'rect',
|
||||
'attr': {
|
||||
'id': 'rect',
|
||||
'x': '50',
|
||||
'y': '75',
|
||||
'width': '200',
|
||||
'height': '100'
|
||||
}
|
||||
})
|
||||
);
|
||||
sandbox.appendChild(svgroot);
|
||||
|
||||
svgedit.select.init(mockConfig, mockFactory);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
while (sandbox.hasChildNodes()) {
|
||||
sandbox.removeChild(sandbox.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
test('Test svgedit.select package', function() {
|
||||
expect(10);
|
||||
|
||||
ok(svgedit.select);
|
||||
ok(svgedit.select.Selector);
|
||||
ok(svgedit.select.SelectorManager);
|
||||
ok(svgedit.select.init);
|
||||
ok(svgedit.select.getSelectorManager);
|
||||
equals(typeof svgedit.select, typeof {});
|
||||
equals(typeof svgedit.select.Selector, typeof function(){});
|
||||
equals(typeof svgedit.select.SelectorManager, typeof function(){});
|
||||
equals(typeof svgedit.select.init, typeof function(){});
|
||||
equals(typeof svgedit.select.getSelectorManager, typeof function(){});
|
||||
});
|
||||
|
||||
test('Test Selector DOM structure', function() {
|
||||
expect(20);
|
||||
|
||||
setUp();
|
||||
|
||||
ok(svgroot);
|
||||
ok(svgroot.hasChildNodes());
|
||||
equals(svgroot.childNodes.length, 3);
|
||||
|
||||
// Verify existence of canvas background.
|
||||
var cb = svgroot.childNodes.item(0);
|
||||
ok(cb);
|
||||
equals(cb.id, 'canvasBackground');
|
||||
|
||||
ok(svgroot.childNodes.item(1));
|
||||
equals(svgroot.childNodes.item(1), svgcontent);
|
||||
|
||||
// Verify existence of selectorParentGroup.
|
||||
var spg = svgroot.childNodes.item(2);
|
||||
ok(spg);
|
||||
equals(spg.id, 'selectorParentGroup');
|
||||
equals(spg.tagName, 'g');
|
||||
|
||||
// Verify existence of all grip elements.
|
||||
ok(spg.querySelector('#selectorGrip_resize_nw'));
|
||||
ok(spg.querySelector('#selectorGrip_resize_n'));
|
||||
ok(spg.querySelector('#selectorGrip_resize_ne'));
|
||||
ok(spg.querySelector('#selectorGrip_resize_e'));
|
||||
ok(spg.querySelector('#selectorGrip_resize_se'));
|
||||
ok(spg.querySelector('#selectorGrip_resize_s'));
|
||||
ok(spg.querySelector('#selectorGrip_resize_sw'));
|
||||
ok(spg.querySelector('#selectorGrip_resize_w'));
|
||||
ok(spg.querySelector('#selectorGrip_rotateconnector'));
|
||||
ok(spg.querySelector('#selectorGrip_rotate'));
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='qunit-header'>Unit Tests for select.js</h1>
|
||||
<h2 id='qunit-banner'></h2>
|
||||
<h2 id='qunit-userAgent'></h2>
|
||||
<ol id='qunit-tests'>
|
||||
</ol>
|
||||
<div id='sandbox'></div>
|
||||
</body>
|
||||
</html>
|
418
public/svg-edit/test/svgtransformlist_test.html
Normal file
418
public/svg-edit/test/svgtransformlist_test.html
Normal file
|
@ -0,0 +1,418 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel='stylesheet' href='qunit/qunit.css' type='text/css'/>
|
||||
<script src='../editor/jquery.js'></script>
|
||||
<script>
|
||||
// Mock for browsersupport
|
||||
window.svgedit = {};
|
||||
svgedit.browsersupport = {};
|
||||
svgedit.browsersupport.nativeTransformLists = false;
|
||||
</script>
|
||||
<script type='text/javascript' src='../editor/svgtransformlist.js'></script>
|
||||
<script type='text/javascript' src='qunit/qunit.js'></script>
|
||||
<script type='text/javascript'>
|
||||
$(function() {
|
||||
// log function
|
||||
QUnit.log = function(result, message) {
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(result +' :: '+ message);
|
||||
}
|
||||
};
|
||||
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
var svgroot = document.getElementById('svgroot');
|
||||
var svgcontent, rect, circle;
|
||||
|
||||
var NEAR_ZERO = 1e-7;
|
||||
function almostEquals(a, b, msg) {
|
||||
msg = msg || (a + ' did not equal ' + b);
|
||||
ok(Math.abs(a - b) < NEAR_ZERO, msg);
|
||||
}
|
||||
|
||||
function checkOutOfBoundsException(obj, fn, arg1) {
|
||||
var caughtException = false;
|
||||
try {
|
||||
obj[fn](arg1);
|
||||
}
|
||||
catch(e) {
|
||||
if (e.code == 1) {
|
||||
caughtException = true;
|
||||
}
|
||||
}
|
||||
ok(caughtException, 'Caugh an INDEX_SIZE_ERR exception');
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
svgcontent = svgroot.appendChild(document.createElementNS(svgns, 'svg'));
|
||||
rect = svgcontent.appendChild(document.createElementNS(svgns, 'rect'));
|
||||
rect.id = 'r';
|
||||
circle = svgcontent.appendChild(document.createElementNS(svgns, 'circle'));
|
||||
circle.id = 'c';
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
svgedit.transformlist.resetListMap();
|
||||
while (svgroot.hasChildNodes()) {
|
||||
svgroot.removeChild(svgroot.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
module('svgedit.svgtransformlist');
|
||||
|
||||
test('Test svgedit.transformlist package', function() {
|
||||
expect(2);
|
||||
|
||||
ok(svgedit.transformlist);
|
||||
ok(svgedit.transformlist.getTransformList);
|
||||
});
|
||||
|
||||
test('Test svgedit.transformlist.getTransformList() function', function() {
|
||||
expect(4);
|
||||
setUp();
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
var cxform = svgedit.transformlist.getTransformList(circle);
|
||||
|
||||
ok(rxform);
|
||||
ok(cxform);
|
||||
equals(typeof rxform, typeof {});
|
||||
equals(typeof cxform, typeof {});
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.numberOfItems property', function() {
|
||||
expect(2);
|
||||
setUp();
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
|
||||
equals(typeof rxform.numberOfItems, typeof 0);
|
||||
equals(rxform.numberOfItems, 0);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.initialize()', function() {
|
||||
expect(6);
|
||||
setUp();
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
var cxform = svgedit.transformlist.getTransformList(circle);
|
||||
|
||||
var t = svgcontent.createSVGTransform();
|
||||
ok(t);
|
||||
ok(rxform.initialize);
|
||||
equals(typeof rxform.initialize, typeof function(){});
|
||||
rxform.initialize(t);
|
||||
equals(rxform.numberOfItems, 1);
|
||||
equals(cxform.numberOfItems, 0);
|
||||
|
||||
// If a transform was already in a transform list, this should
|
||||
// remove it from its old list and add it to this list.
|
||||
cxform.initialize(t);
|
||||
// This also fails in Firefox native.
|
||||
// equals(rxform.numberOfItems, 0, 'Did not remove transform from list before initializing another transformlist');
|
||||
equals(cxform.numberOfItems, 1);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.appendItem() and getItem()', function() {
|
||||
expect(12);
|
||||
setUp();
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
var cxform = svgedit.transformlist.getTransformList(circle);
|
||||
|
||||
var t1 = svgcontent.createSVGTransform(),
|
||||
t2 = svgcontent.createSVGTransform(),
|
||||
t3 = svgcontent.createSVGTransform();
|
||||
|
||||
ok(rxform.appendItem);
|
||||
ok(rxform.getItem);
|
||||
equals(typeof rxform.appendItem, typeof function(){});
|
||||
equals(typeof rxform.getItem, typeof function(){});
|
||||
|
||||
rxform.appendItem(t1);
|
||||
rxform.appendItem(t2);
|
||||
rxform.appendItem(t3);
|
||||
|
||||
equals(rxform.numberOfItems, 3);
|
||||
var rxf = rxform.getItem(0);
|
||||
equals(rxf, t1);
|
||||
equals(rxform.getItem(1), t2);
|
||||
equals(rxform.getItem(2), t3);
|
||||
|
||||
checkOutOfBoundsException(rxform, 'getItem', -1);
|
||||
checkOutOfBoundsException(rxform, 'getItem', 3);
|
||||
|
||||
cxform.appendItem(t1);
|
||||
// These also fail in Firefox native.
|
||||
// equals(rxform.numberOfItems, 2, 'Did not remove a transform from a list before appending it to a new transformlist');
|
||||
// equals(rxform.getItem(0), t2, 'Found the wrong transform in a transformlist');
|
||||
// equals(rxform.getItem(1), t3, 'Found the wrong transform in a transformlist');
|
||||
|
||||
equals(cxform.numberOfItems, 1);
|
||||
equals(cxform.getItem(0), t1);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.removeItem()', function() {
|
||||
expect(7);
|
||||
setUp();
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
|
||||
var t1 = svgcontent.createSVGTransform(),
|
||||
t2 = svgcontent.createSVGTransform();
|
||||
ok(rxform.removeItem);
|
||||
equals(typeof rxform.removeItem, typeof function(){});
|
||||
rxform.appendItem(t1);
|
||||
rxform.appendItem(t2);
|
||||
|
||||
var removedTransform = rxform.removeItem(0);
|
||||
equals(rxform.numberOfItems, 1);
|
||||
equals(removedTransform, t1);
|
||||
equals(rxform.getItem(0), t2);
|
||||
|
||||
checkOutOfBoundsException(rxform, 'removeItem', -1);
|
||||
checkOutOfBoundsException(rxform, 'removeItem', 1);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.replaceItem()', function() {
|
||||
expect(8);
|
||||
setUp();
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
var cxform = svgedit.transformlist.getTransformList(circle);
|
||||
|
||||
ok(rxform.replaceItem);
|
||||
equals(typeof rxform.replaceItem, typeof function(){});
|
||||
|
||||
var t1 = svgcontent.createSVGTransform(),
|
||||
t2 = svgcontent.createSVGTransform(),
|
||||
t3 = svgcontent.createSVGTransform();
|
||||
|
||||
rxform.appendItem(t1);
|
||||
rxform.appendItem(t2);
|
||||
cxform.appendItem(t3);
|
||||
|
||||
var newItem = rxform.replaceItem(t3, 0);
|
||||
equals(rxform.numberOfItems, 2);
|
||||
equals(newItem, t3);
|
||||
equals(rxform.getItem(0), t3);
|
||||
equals(rxform.getItem(1), t2);
|
||||
// Fails in Firefox native
|
||||
// equals(cxform.numberOfItems, 0);
|
||||
|
||||
// test replaceItem within a list
|
||||
rxform.appendItem(t1);
|
||||
rxform.replaceItem(t1, 0);
|
||||
// Fails in Firefox native
|
||||
// equals(rxform.numberOfItems, 2);
|
||||
equals(rxform.getItem(0), t1);
|
||||
equals(rxform.getItem(1), t2);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.insertItemBefore()', function() {
|
||||
expect(10);
|
||||
setUp();
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
var cxform = svgedit.transformlist.getTransformList(circle);
|
||||
|
||||
ok(rxform.insertItemBefore);
|
||||
equals(typeof rxform.insertItemBefore, typeof function(){});
|
||||
|
||||
var t1 = svgcontent.createSVGTransform(),
|
||||
t2 = svgcontent.createSVGTransform(),
|
||||
t3 = svgcontent.createSVGTransform();
|
||||
|
||||
rxform.appendItem(t1);
|
||||
rxform.appendItem(t2);
|
||||
cxform.appendItem(t3);
|
||||
|
||||
var newItem = rxform.insertItemBefore(t3, 0);
|
||||
equals(rxform.numberOfItems, 3);
|
||||
equals(newItem, t3);
|
||||
equals(rxform.getItem(0), t3);
|
||||
equals(rxform.getItem(1), t1);
|
||||
equals(rxform.getItem(2), t2);
|
||||
// Fails in Firefox native
|
||||
// equals(cxform.numberOfItems, 0);
|
||||
|
||||
rxform.insertItemBefore(t2, 1);
|
||||
// Fails in Firefox native (they make copies of the transforms)
|
||||
// equals(rxform.numberOfItems, 3);
|
||||
equals(rxform.getItem(0), t3);
|
||||
equals(rxform.getItem(1), t2);
|
||||
equals(rxform.getItem(2), t1);
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.init() for translate(200,100)', function() {
|
||||
expect(8);
|
||||
setUp();
|
||||
rect.setAttribute('transform', 'translate(200,100)');
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
equals(1, rxform.numberOfItems);
|
||||
|
||||
var translate = rxform.getItem(0);
|
||||
equals(translate.type, 2);
|
||||
|
||||
var m = translate.matrix;
|
||||
equals(m.a, 1);
|
||||
equals(m.b, 0);
|
||||
equals(m.c, 0);
|
||||
equals(m.d, 1);
|
||||
equals(m.e, 200);
|
||||
equals(m.f, 100);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.init() for scale(4)', function() {
|
||||
expect(8);
|
||||
setUp();
|
||||
rect.setAttribute('transform', 'scale(4)');
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
equals(1, rxform.numberOfItems);
|
||||
|
||||
var scale = rxform.getItem(0);
|
||||
equals(3, scale.type);
|
||||
|
||||
var m = scale.matrix;
|
||||
equals(m.a, 4);
|
||||
equals(m.b, 0);
|
||||
equals(m.c, 0);
|
||||
equals(m.d, 4);
|
||||
equals(m.e, 0);
|
||||
equals(m.f, 0);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.init() for scale(4,3)', function() {
|
||||
expect(8);
|
||||
setUp();
|
||||
rect.setAttribute('transform', 'scale(4,3)');
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
equals(1, rxform.numberOfItems);
|
||||
|
||||
var scale = rxform.getItem(0);
|
||||
equals(3, scale.type);
|
||||
|
||||
var m = scale.matrix;
|
||||
equals(m.a, 4);
|
||||
equals(m.b, 0);
|
||||
equals(m.c, 0);
|
||||
equals(m.d, 3);
|
||||
equals(m.e, 0);
|
||||
equals(m.f, 0);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.init() for rotate(45)', function() {
|
||||
expect(9);
|
||||
setUp();
|
||||
rect.setAttribute('transform', 'rotate(45)');
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
equals(1, rxform.numberOfItems);
|
||||
|
||||
var rotate = rxform.getItem(0);
|
||||
equals(4, rotate.type);
|
||||
equals(45, rotate.angle);
|
||||
|
||||
var m = rotate.matrix;
|
||||
almostEquals(m.a, 1/Math.sqrt(2));
|
||||
almostEquals(m.b, 1/Math.sqrt(2));
|
||||
almostEquals(m.c, -1/Math.sqrt(2));
|
||||
almostEquals(m.d, 1/Math.sqrt(2));
|
||||
equals(m.e, 0);
|
||||
equals(m.f, 0);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.init() for rotate(45, 100, 200)', function() {
|
||||
expect(9);
|
||||
setUp();
|
||||
rect.setAttribute('transform', 'rotate(45, 100, 200)');
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
equals(1, rxform.numberOfItems);
|
||||
|
||||
var rotate = rxform.getItem(0);
|
||||
equals(4, rotate.type);
|
||||
equals(45, rotate.angle);
|
||||
|
||||
var m = rotate.matrix;
|
||||
almostEquals(m.a, 1/Math.sqrt(2));
|
||||
almostEquals(m.b, 1/Math.sqrt(2));
|
||||
almostEquals(m.c, -1/Math.sqrt(2));
|
||||
almostEquals(m.d, 1/Math.sqrt(2));
|
||||
|
||||
var r = svgcontent.createSVGMatrix();
|
||||
r.a = 1/Math.sqrt(2); r.b = 1/Math.sqrt(2);
|
||||
r.c = -1/Math.sqrt(2); r.d = 1/Math.sqrt(2);
|
||||
|
||||
var t = svgcontent.createSVGMatrix();
|
||||
t.e = -100; t.f = -200;
|
||||
|
||||
var t_ = svgcontent.createSVGMatrix();
|
||||
t_.e = 100; t_.f = 200;
|
||||
|
||||
var result = t_.multiply(r).multiply(t);
|
||||
|
||||
almostEquals(m.e, result.e);
|
||||
almostEquals(m.f, result.f);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
test('Test SVGTransformList.init() for matrix(1, 2, 3, 4, 5, 6)', function() {
|
||||
expect(8);
|
||||
setUp();
|
||||
rect.setAttribute('transform', 'matrix(1,2,3,4,5,6)');
|
||||
|
||||
var rxform = svgedit.transformlist.getTransformList(rect);
|
||||
equals(1, rxform.numberOfItems);
|
||||
|
||||
var mt = rxform.getItem(0);
|
||||
equals(1, mt.type);
|
||||
|
||||
var m = mt.matrix;
|
||||
equals(m.a, 1);
|
||||
equals(m.b, 2);
|
||||
equals(m.c, 3);
|
||||
equals(m.d, 4);
|
||||
equals(m.e, 5);
|
||||
equals(m.f, 6);
|
||||
|
||||
tearDown();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='qunit-header'>Unit Tests for svgtransformlist.js</h1>
|
||||
<h2 id='qunit-banner'></h2>
|
||||
<h2 id='qunit-userAgent'></h2>
|
||||
<ol id='qunit-tests'>
|
||||
</ol>
|
||||
<div id='svgroot' style='visibility:hidden'></div>
|
||||
</body>
|
||||
</html>
|
123
public/svg-edit/test/svgutils_test.html
Normal file
123
public/svg-edit/test/svgutils_test.html
Normal file
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel='stylesheet' href='qunit/qunit.css' type='text/css'/>
|
||||
<script src='../editor/jquery.js'></script>
|
||||
<!-- svgutils.js depends on these two... mock out? -->
|
||||
<script type='text/javascript' src='../editor/browsersupport.js'></script>
|
||||
<script type='text/javascript' src='../editor/svgtransformlist.js'></script>
|
||||
<script type='text/javascript' src='../editor/svgutils.js'></script>
|
||||
<script type='text/javascript' src='qunit/qunit.js'></script>
|
||||
<script type='text/javascript'>
|
||||
$(function() {
|
||||
// log function
|
||||
QUnit.log = function(result, message) {
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(result +' :: '+ message);
|
||||
}
|
||||
};
|
||||
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
var svg = document.createElementNS(svgns, 'svg');
|
||||
|
||||
module('svgedit.utilities');
|
||||
|
||||
test('Test svgedit.utilities package', function() {
|
||||
expect(3);
|
||||
|
||||
ok(svgedit.utilities);
|
||||
ok(svgedit.utilities.toXml);
|
||||
equals(typeof svgedit.utilities.toXml, typeof function(){});
|
||||
});
|
||||
|
||||
test('Test svgedit.utilities.toXml() function', function() {
|
||||
expect(6);
|
||||
var toXml = svgedit.utilities.toXml;
|
||||
|
||||
equals(toXml('a'), 'a');
|
||||
equals(toXml('ABC_'), 'ABC_');
|
||||
equals(toXml('PB&J'), 'PB&J');
|
||||
equals(toXml('2 < 5'), '2 < 5');
|
||||
equals(toXml('5 > 2'), '5 > 2');
|
||||
equals(toXml('\'<&>"'), '\'<&>"');
|
||||
});
|
||||
|
||||
test('Test svgedit.utilities.fromXml() function', function() {
|
||||
expect(6);
|
||||
var fromXml = svgedit.utilities.fromXml;
|
||||
|
||||
equals(fromXml('a'), 'a');
|
||||
equals(fromXml('ABC_'), 'ABC_');
|
||||
equals(fromXml('PB&J'), 'PB&J');
|
||||
equals(fromXml('2 < 5'), '2 < 5');
|
||||
equals(fromXml('5 > 2'), '5 > 2');
|
||||
equals(fromXml('<&>'), '<&>');
|
||||
});
|
||||
|
||||
test('Test svgedit.utilities.encode64() function', function() {
|
||||
expect(4);
|
||||
var encode64 = svgedit.utilities.encode64;
|
||||
|
||||
equals(encode64('abcdef'), 'YWJjZGVm');
|
||||
equals(encode64('12345'), 'MTIzNDU=');
|
||||
equals(encode64(' '), 'IA==');
|
||||
equals(encode64('`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?'), 'YH4hQCMkJV4mKigpLV89K1t7XX1cfDs6JyIsPC4+Lz8=');
|
||||
});
|
||||
|
||||
test('Test svgedit.utilities.decode64() function', function() {
|
||||
expect(4);
|
||||
var decode64 = svgedit.utilities.decode64;
|
||||
|
||||
equals(decode64('YWJjZGVm'), 'abcdef');
|
||||
equals(decode64('MTIzNDU='), '12345');
|
||||
equals(decode64('IA=='), ' ');
|
||||
equals(decode64('YH4hQCMkJV4mKigpLV89K1t7XX1cfDs6JyIsPC4+Lz8='), '`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?');
|
||||
});
|
||||
|
||||
test('Test svgedit.utilities.convertToXMLReferences() function', function() {
|
||||
expect(1);
|
||||
|
||||
var convert = svgedit.utilities.convertToXMLReferences;
|
||||
equals(convert('ABC'), 'ABC');
|
||||
// equals(convert('ÀBC'), 'ÀBC');
|
||||
});
|
||||
|
||||
test('Test svgedit.utilities.bboxToObj() function', function() {
|
||||
expect(5);
|
||||
var bboxToObj = svgedit.utilities.bboxToObj;
|
||||
|
||||
var rect = svg.createSVGRect();
|
||||
rect.x = 1;
|
||||
rect.y = 2;
|
||||
rect.width = 3;
|
||||
rect.height = 4;
|
||||
|
||||
var obj = bboxToObj(rect);
|
||||
equals(typeof obj, typeof {});
|
||||
equals(obj.x, 1);
|
||||
equals(obj.y, 2);
|
||||
equals(obj.width, 3);
|
||||
equals(obj.height, 4);
|
||||
});
|
||||
|
||||
|
||||
test("Test getUrlFromAttr", function() {
|
||||
expect(4);
|
||||
|
||||
equal(svgedit.utilities.getUrlFromAttr("url(#foo)"), "#foo");
|
||||
equal(svgedit.utilities.getUrlFromAttr("url(somefile.svg#foo)"), "somefile.svg#foo");
|
||||
equal(svgedit.utilities.getUrlFromAttr("url('#foo')"), "#foo");
|
||||
equal(svgedit.utilities.getUrlFromAttr('url("#foo")'), "#foo");
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id='qunit-header'>Unit Tests for svgutils.js</h1>
|
||||
<h2 id='qunit-banner'></h2>
|
||||
<h2 id='qunit-userAgent'></h2>
|
||||
<ol id='qunit-tests'>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
|
@ -5,6 +5,14 @@
|
|||
<script type="text/javascript" src="../editor/jquery-ui/jquery-ui-1.8.custom.min.js"></script>
|
||||
<script type="text/javascript" src="../editor/svgicons/jquery.svgicons.js"></script>
|
||||
<script type="text/javascript" src="../editor/locale/locale.js"></script>
|
||||
<script type="text/javascript" src="../editor/browsersupport.js"></script>
|
||||
<script type="text/javascript" src="../editor/svgtransformlist.js"></script>
|
||||
<script type="text/javascript" src="../editor/math.js"></script>
|
||||
<script type="text/javascript" src="../editor/units.js"></script>
|
||||
<script type="text/javascript" src="../editor/svgutils.js"></script>
|
||||
<script type="text/javascript" src="../editor/sanitize.js"></script>
|
||||
<script type="text/javascript" src="../editor/history.js"></script>
|
||||
<script type="text/javascript" src="../editor/select.js"></script>
|
||||
<script type="text/javascript" src="../editor/svgcanvas.js"></script>
|
||||
<script type="text/javascript" src="qunit/qunit.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
@ -101,46 +109,6 @@
|
|||
QUnit.log(d);
|
||||
});
|
||||
|
||||
module("Transform Module");
|
||||
|
||||
test("Test matrixMultiply", function() {
|
||||
expect(5);
|
||||
|
||||
// translate there and back
|
||||
var tr_1 = svgroot.createSVGMatrix().translate(100,50),
|
||||
tr_2 = svgroot.createSVGMatrix().translate(-90,0),
|
||||
tr_3 = svgroot.createSVGMatrix().translate(-10,-50),
|
||||
I = svgCanvas.matrixMultiply(tr_1,tr_2,tr_3);
|
||||
equal(isIdentity(I), true,
|
||||
"Expected identity matrix when translating there and back, got " + matrixString(I));
|
||||
|
||||
// rotate there and back
|
||||
// TODO: currently Mozilla fails this when rotating back at -50 and then -40 degrees
|
||||
// (b and c are *almost* zero, but not zero)
|
||||
var rot_there = svgroot.createSVGMatrix().rotate(90),
|
||||
rot_back = svgroot.createSVGMatrix().rotate(-90); // TODO: set this to -50
|
||||
rot_back_more = svgroot.createSVGMatrix().rotate(0); // TODO: set this to -40
|
||||
I = svgCanvas.matrixMultiply(rot_there, rot_back, rot_back_more);
|
||||
equal(isIdentity(I), true,
|
||||
"Expected identity matrix when rotating there and back, got " + matrixString(I));
|
||||
|
||||
// scale up and down
|
||||
var scale_up = svgroot.createSVGMatrix().scale(4),
|
||||
scale_down = svgroot.createSVGMatrix().scaleNonUniform(0.25,1);
|
||||
scale_down_more = svgroot.createSVGMatrix().scaleNonUniform(1,0.25);
|
||||
I = svgCanvas.matrixMultiply(scale_up, scale_down, scale_down_more);
|
||||
equal(isIdentity(I), true,
|
||||
"Expected identity matrix when scaling up and down, got " + matrixString(I));
|
||||
|
||||
// test multiplication with its inverse
|
||||
I = svgCanvas.matrixMultiply(rot_there, rot_there.inverse());
|
||||
equal(isIdentity(I), true,
|
||||
"Expected identity matrix when multiplying a matrix by its inverse, got " + matrixString(I));
|
||||
I = svgCanvas.matrixMultiply(rot_there.inverse(), rot_there);
|
||||
equal(isIdentity(I), true,
|
||||
"Expected identity matrix when multiplying a matrix by its inverse, got " + matrixString(I));
|
||||
});
|
||||
|
||||
module("Import Module");
|
||||
|
||||
test("Test import use", function() {
|
||||
|
@ -162,15 +130,6 @@
|
|||
equal(nfu, null, "Removed <use> element that had no href");
|
||||
});
|
||||
|
||||
test("Test getUrlFromAttr", function() {
|
||||
expect(4);
|
||||
|
||||
equal(svgCanvas.getUrlFromAttr("url(#foo)"), "#foo");
|
||||
equal(svgCanvas.getUrlFromAttr("url(somefile.svg#foo)"), "somefile.svg#foo");
|
||||
equal(svgCanvas.getUrlFromAttr("url('#foo')"), "#foo");
|
||||
equal(svgCanvas.getUrlFromAttr('url("#foo")'), "#foo");
|
||||
});
|
||||
|
||||
// This test shows that an element with an invalid attribute is still parsed in properly
|
||||
// and only the attribute is not imported
|
||||
test("Test invalid attribute", function() {
|
||||
|
@ -235,15 +194,6 @@
|
|||
equal(math.namespaceURI, "http://www.w3.org/1998/Math/MathML", "Preserved MathML namespace");
|
||||
});
|
||||
|
||||
test("Test escaping XML entities", function() {
|
||||
expect(3);
|
||||
|
||||
equal(svgCanvas.Utils.toXml("<"), "<", "Escaped < properly");
|
||||
equal(svgCanvas.Utils.toXml(">"), ">", "Escaped > properly");
|
||||
equal(svgCanvas.Utils.toXml("&"), "&", "Escaped & properly");
|
||||
// TODO: what about " and ' ?
|
||||
});
|
||||
|
||||
test("Test importing SVG into existing drawing", function() {
|
||||
expect(3);
|
||||
|
||||
|
|
Loading…
Reference in a new issue