Move Selector and SelectorManager into select.js. Start of unit test file for select.js.

git-svn-id: http://svg-edit.googlecode.com/svn/trunk@1873 eee81c28-f429-11dd-99c0-75d572ba1ddd
master
Jeff Schiller 2010-11-15 09:25:49 +00:00
parent 383d52e157
commit 3ef0229180
11 changed files with 781 additions and 638 deletions

View File

@ -30,6 +30,7 @@ build/$(PACKAGE):
--js svgutils.js \ --js svgutils.js \
--js sanitize.js \ --js sanitize.js \
--js history.js \ --js history.js \
--js select.js \
--js svgcanvas.js \ --js svgcanvas.js \
--js svg-editor.js \ --js svg-editor.js \
--js locale/locale.js \ --js locale/locale.js \

531
editor/select.js Normal file
View 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_;
};
})();

View File

@ -25,6 +25,7 @@
<script type="text/javascript" src="svgutils.js"></script> <script type="text/javascript" src="svgutils.js"></script>
<script type="text/javascript" src="sanitize.js"></script> <script type="text/javascript" src="sanitize.js"></script>
<script type="text/javascript" src="history.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="svgcanvas.js"></script>
<script type="text/javascript" src="svg-editor.js"></script> <script type="text/javascript" src="svg-editor.js"></script>
<script type="text/javascript" src="locale/locale.js"></script> <script type="text/javascript" src="locale/locale.js"></script>

View File

@ -119,20 +119,85 @@ if(config) {
$.extend(curConfig, config); $.extend(curConfig, config);
} }
// Array with width/height of canvas
var dimensions = curConfig.dimensions;
var canvas = this; var canvas = this;
// "document" element associated with the container (same as window.document using default svg-editor.js) // "document" element associated with the container (same as window.document using default svg-editor.js)
var svgdoc = container.ownerDocument; var svgdoc = container.ownerDocument;
// This is a container for the document being edited, not the document itself.
var svgroot = svgdoc.importNode(svgedit.utilities.text2xml(
'<svg id="svgroot" xmlns="' + svgns + '" xlinkns="' + xlinkns + '" ' +
'width="' + dimensions[0] + '" height="' + dimensions[1] + '" x="' + dimensions[0] + '" y="' + dimensions[1] + '" overflow="visible">' +
'<defs>' +
'<filter id="canvashadow" filterUnits="objectBoundingBox">' +
'<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>'+
'<feOffset in="blur" dx="5" dy="5" result="offsetBlur"/>'+
'<feMerge>'+
'<feMergeNode in="offsetBlur"/>'+
'<feMergeNode in="SourceGraphic"/>'+
'</feMerge>'+
'</filter>'+
'</defs>'+
'</svg>').documentElement, true);
container.appendChild(svgroot);
// The actual element that represents the final output SVG element // The actual element that represents the final output SVG element
var svgcontent = svgdoc.createElementNS(svgns, "svg"); var svgcontent = svgdoc.createElementNS(svgns, "svg");
// This is a container for the document being edited, not the document itself. Initialized later. $(svgcontent).attr({
var svgroot = null; id: 'svgcontent',
width: dimensions[0],
height: dimensions[1],
x: dimensions[0],
y: dimensions[1],
overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden',
xmlns: svgns,
"xmlns:se": se_ns,
"xmlns:xlink": xlinkns
}).appendTo(svgroot);
// Float displaying the current zoom level (1 = 100%, .5 = 50%, etc) // Float displaying the current zoom level (1 = 100%, .5 = 50%, etc)
var current_zoom = 1; var current_zoom = 1;
// pointer to the current layer <g>
var current_layer = null;
// pointer to current group (for in-group editing)
var current_group = null;
// Object containing data for the currently selected styles
var all_properties = {
shape: {
fill: "#" + curConfig.initFill.color,
fill_paint: null,
fill_opacity: curConfig.initFill.opacity,
stroke: "#" + curConfig.initStroke.color,
stroke_paint: null,
stroke_opacity: curConfig.initStroke.opacity,
stroke_width: curConfig.initStroke.width,
stroke_dasharray: 'none',
stroke_linejoin: 'miter',
stroke_linecap: 'butt',
opacity: curConfig.initOpacity
}
};
all_properties.text = $.extend(true, {}, all_properties.shape);
$.extend(all_properties.text, {
fill: "#000000",
stroke_width: 0,
font_size: 24,
font_family: 'serif'
});
// Current shape style properties
var cur_shape = all_properties.shape;
// Function: getElem // Function: getElem
// Get a DOM element by ID within the SVG root element. // Get a DOM element by ID within the SVG root element.
// //
@ -154,6 +219,112 @@ var getElem = function(id) {
// return svgdoc.getElementById(id); // return svgdoc.getElementById(id);
}; };
// Function: assignAttributes
// Assigns multiple attributes to an element.
//
// Parameters:
// node - DOM element to apply new attribute values to
// attrs - Object with attribute keys/values
// suspendLength - Optional integer of milliseconds to suspend redraw
// unitCheck - Boolean to indicate the need to use svgedit.units.setUnitAttr
var assignAttributes = canvas.assignAttributes = function(node, attrs, suspendLength, unitCheck) {
if(!suspendLength) suspendLength = 0;
// Opera has a problem with suspendRedraw() apparently
var handle = null;
if (!svgedit.browsersupport.isOpera()) svgroot.suspendRedraw(suspendLength);
for (var i in attrs) {
var ns = (i.substr(0,4) === "xml:" ? xmlns :
i.substr(0,6) === "xlink:" ? xlinkns : null);
if(ns) {
node.setAttributeNS(ns, i, attrs[i]);
} else if(!unitCheck) {
node.setAttribute(i, attrs[i]);
} else {
svgedit.units.setUnitAttr(node, i, attrs[i]);
}
}
if (!svgedit.browsersupport.isOpera()) svgroot.unsuspendRedraw(handle);
};
// Function: cleanupElement
// Remove unneeded (default) attributes, makes resulting SVG smaller
//
// Parameters:
// element - DOM element to clean up
var cleanupElement = this.cleanupElement = function(element) {
var handle = svgroot.suspendRedraw(60);
var defaults = {
'fill-opacity':1,
'stop-opacity':1,
'opacity':1,
'stroke':'none',
'stroke-dasharray':'none',
'stroke-linejoin':'miter',
'stroke-linecap':'butt',
'stroke-opacity':1,
'stroke-width':1,
'rx':0,
'ry':0
}
for(var attr in defaults) {
var val = defaults[attr];
if(element.getAttribute(attr) == val) {
element.removeAttribute(attr);
}
}
svgroot.unsuspendRedraw(handle);
};
// Function: addSvgElementFromJson
// Create a new SVG element based on the given object keys/values and add it to the current layer
// The element will be ran through cleanupElement before being returned
//
// Parameters:
// data - Object with the following keys/values:
// * element - tag name of the SVG element to create
// * attr - Object with attributes key-values to assign to the new element
// * curStyles - Boolean indicating that current style attributes should be applied first
//
// Returns: The new element
var addSvgElementFromJson = this.addSvgElementFromJson = function(data) {
var shape = getElem(data.attr.id);
// if shape is a path but we need to create a rect/ellipse, then remove the path
if (shape && data.element != shape.tagName) {
current_layer.removeChild(shape);
shape = null;
}
if (!shape) {
shape = svgdoc.createElementNS(svgns, data.element);
if (current_layer) {
(current_group || current_layer).appendChild(shape);
}
}
if(data.curStyles) {
assignAttributes(shape, {
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_dasharray,
"stroke-linejoin": cur_shape.stroke_linejoin,
"stroke-linecap": cur_shape.stroke_linecap,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"opacity": cur_shape.opacity / 2,
"style": "pointer-events:inherit"
}, 100);
}
assignAttributes(shape, data.attr, 100);
cleanupElement(shape);
return shape;
};
// import svgtransformlist.js // import svgtransformlist.js
var getTransformList = canvas.getTransformList = svgedit.transformlist.getTransformList; var getTransformList = canvas.getTransformList = svgedit.transformlist.getTransformList;
@ -240,6 +411,17 @@ var addCommandToHistory = function(cmd) {
canvas.undoMgr.addCommandToHistory(cmd); canvas.undoMgr.addCommandToHistory(cmd);
}; };
// import from select.js
svgedit.select.init(curConfig, {
createSVGElement: function(jsonMap) { return canvas.addSvgElementFromJson(jsonMap); },
svgRoot: function() { return svgroot; },
svgContent: function() { return svgcontent; },
currentZoom: function() { return current_zoom; },
// TODO(codedread): Remove when getStrokedBBox() has been put into svgutils.js.
getStrokedBBox: function(elems) { return canvas.getStrokedBBox([elems]); }
});
// this object manages selectors for us
var selectorManager = this.selectorManager = svgedit.select.getSelectorManager();
// Function: snapToGrid // Function: snapToGrid
// round value to for snapping // round value to for snapping
@ -272,43 +454,10 @@ var ref_attrs = ["clip-path", "fill", "filter", "marker-end", "marker-mid", "mar
var elData = $.data; var elData = $.data;
// nonce to uniquify id's // nonce to uniquify id's
var nonce = Math.floor(Math.random()*100001), var nonce = Math.floor(Math.random() * 100001);
// Boolean to indicate whether or not IDs given to elements should be random // Boolean to indicate whether or not IDs given to elements should be random
randomize_ids = false, var randomize_ids = false;
// Array with width/height of canvas
dimensions = curConfig.dimensions;
// Create Root SVG element. This is a container for the document being edited, not the document itself.
svgroot = svgdoc.importNode(svgedit.utilities.text2xml(
'<svg id="svgroot" xmlns="' + svgns + '" xlinkns="' + xlinkns + '" ' +
'width="' + dimensions[0] + '" height="' + dimensions[1] + '" x="' + dimensions[0] + '" y="' + dimensions[1] + '" overflow="visible">' +
'<defs>' +
'<filter id="canvashadow" filterUnits="objectBoundingBox">' +
'<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>'+
'<feOffset in="blur" dx="5" dy="5" result="offsetBlur"/>'+
'<feMerge>'+
'<feMergeNode in="offsetBlur"/>'+
'<feMergeNode in="SourceGraphic"/>'+
'</feMerge>'+
'</filter>'+
'</defs>'+
'</svg>').documentElement, true);
container.appendChild(svgroot);
$(svgcontent).attr({
id: 'svgcontent',
width: dimensions[0],
height: dimensions[1],
x: dimensions[0],
y: dimensions[1],
overflow: curConfig.show_outside_canvas ? 'visible' : 'hidden',
xmlns: svgns,
"xmlns:se": se_ns,
"xmlns:xlink": xlinkns
}).appendTo(svgroot);
// Set nonce if randomize_ids = true // Set nonce if randomize_ids = true
if (randomize_ids) svgcontent.setAttributeNS(se_ns, 'se:nonce', nonce); if (randomize_ids) svgcontent.setAttributeNS(se_ns, 'se:nonce', nonce);
@ -338,557 +487,6 @@ var restoreRefElems = function(elem) {
} }
}; };
// Put SelectorManager in this scope
var SelectorManager;
(function() {
// Interface: Selector
// Private class for DOM element selection boxes
//
// Parameters:
// id - integer to internally indentify the selector
// elem - DOM element associated with this selector
function Selector(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;
// Function: Selector.reset
// Used to reset the id and element that the selector is attached to
//
// Parameters:
// e - DOM element associated with this selector
this.reset = function(e) {
this.locked = true;
this.selectedElement = e;
this.resize();
this.selectorGroup.setAttribute("display", "inline");
};
// this holds a reference to the <g> element that holds all visual elements of the selector
this.selectorGroup = addSvgElementFromJson({ "element": "g",
"attr": {"id": ("selectorGroup"+this.id)}
});
// this holds a reference to the path rect
this.selectorRect = this.selectorGroup.appendChild( addSvgElementFromJson({
"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
};
// Function: Selector.showGrips
// Show the resize grips of this selector
//
// Parameters:
// show - boolean indicating whether grips should be shown or not
this.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: Selector.updateGripCursors
// Updates cursors for corner grips on rotation so arrows point the right way
//
// Parameters:
// angle - Float indicating current rotation angle in degrees
this.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: Selector.resize
// Updates the selector to match the element's size
this.resize = function() {
var selectedBox = this.selectorRect,
mgr = selectorManager,
selectedGrips = mgr.selectorGrips,
selected = this.selectedElement,
sw = selected.getAttribute("stroke-width");
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;
}
var bbox = getBBox(selected);
if(tagName === 'g' && !elData(selected, 'gsvg')) {
// The bbox for a group does not include stroke vals, so we
// get the bbox based on its children.
var stroked_bbox = getStrokedBBox(selected.childNodes);
if(stroked_bbox) {
bbox = stroked_bbox;
}
}
// loop and transform our bounding box until we reach our first rotation
var m = getMatrix(selected);
// 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;
// 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 = svgroot.createSVGTransform();
rot.setRotate(-angle,cx,cy);
var rotm = rot.matrix;
nbox.tl = transformPoint(nbox.tl.x,nbox.tl.y,rotm);
nbox.tr = transformPoint(nbox.tr.x,nbox.tr.y,rotm);
nbox.bl = transformPoint(nbox.bl.x,nbox.bl.y,rotm);
nbox.br = 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 = 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);
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];
assignAttributes(selectedGrips[dir], {
cx: coords[0], cy: coords[1]
});
};
// we want to go 20 pixels in the negative transformed y direction, ignoring scale
assignAttributes(mgr.rotateGripConnector, { x1: nbax + (nbaw)/2,
y1: nbay,
x2: nbax + (nbaw)/2,
y2: nbay- 20});
assignAttributes(mgr.rotateGrip, { cx: nbax + (nbaw)/2,
cy: nbay - 20 });
}
svgroot.unsuspendRedraw(sr_handle);
};
// now initialize the selector
this.reset(elem);
};
// Interface: SelectorManager
// Public class to manage all selector objects (selection boxes)
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 Selector (see above)
this.selectors = [];
// this holds a map of SVG elements to their Selector object
this.selectorMap = {};
// local reference to this object
var mgr = this;
// Function: SelectorManager.initGroup
// Resets the parent selector group element
this.initGroup = function() {
// remove old selector parent group if it existed
if (mgr.selectorParentGroup && mgr.selectorParentGroup.parentNode) {
mgr.selectorParentGroup.parentNode.removeChild(mgr.selectorParentGroup);
}
// create parent selector group and add it to svgroot
mgr.selectorParentGroup = svgdoc.createElementNS(svgns, "g");
mgr.selectorParentGroup.setAttribute("id", "selectorParentGroup");
mgr.selectorGripsGroup = svgdoc.createElementNS(svgns, "g");
mgr.selectorGripsGroup.setAttribute('display','none');
svgroot.appendChild(mgr.selectorParentGroup);
mgr.selectorParentGroup.appendChild(mgr.selectorGripsGroup);
mgr.selectorMap = {};
mgr.selectors = [];
mgr.rubberBandBox = null;
// this holds a reference to the grip elements
mgr.selectorGrips = { "nw":null,
"n":null,
"ne":null,
"e":null,
"se":null,
"s":null,
"sw":null,
"w":null
};
// add the corner grips
for (var dir in mgr.selectorGrips) {
var grip = addSvgElementFromJson({
"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"
}
});
elData(grip, "dir", dir);
elData(grip, "type", "resize");
this.selectorGrips[dir] = mgr.selectorGripsGroup.appendChild(grip);
}
// add rotator elems
this.rotateGripConnector = this.selectorGripsGroup.appendChild( addSvgElementFromJson({
"element": "line",
"attr": {
"id": ("selectorGrip_rotateconnector"),
"stroke": "#22C",
"stroke-width": "1"
}
}) );
this.rotateGrip = this.selectorGripsGroup.appendChild( addSvgElementFromJson({
"element": "circle",
"attr": {
"id": "selectorGrip_rotate",
"fill": "lime",
"r": 4,
"stroke": "#22C",
"stroke-width": 2,
"style": "cursor:url(" + curConfig.imgPath + "rotate.png) 12 12, auto;"
}
}) );
elData(this.rotateGrip, "type", "rotate");
if($("#canvasBackground").length) return;
var canvasbg = svgdoc.createElementNS(svgns, "svg");
var dims = curConfig.dimensions;
assignAttributes(canvasbg, {
'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 = svgdoc.createElementNS(svgns, "rect");
assignAttributes(rect, {
'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 (!window.opera) rect.setAttribute('filter', 'url(#canvashadow)');
canvasbg.appendChild(rect);
svgroot.insertBefore(canvasbg, svgcontent);
};
// Function: SelectorManager.requestSelector
// Returns the selector based on the given element
//
// Parameters:
// elem - DOM element to get the selector for
this.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 Selector(N, elem);
this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup);
this.selectorMap[elem.id] = this.selectors[N];
return this.selectors[N];
};
// Function: SelectorManager.releaseSelector
// Removes the selector of the given element (hides selection box)
//
// Parameters:
// elem - DOM element to remove the selector for
this.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) {
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: SelectorManager.getRubberBandBox
// Returns the rubberBandBox DOM element. This is the rectangle drawn by the user for selecting/zooming
this.getRubberBandBox = function() {
if (!this.rubberBandBox) {
this.rubberBandBox = this.selectorParentGroup.appendChild(
addSvgElementFromJson({ "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;
};
this.initGroup();
};
}());
// Function: assignAttributes
// Assigns multiple attributes to an element.
//
// Parameters:
// node - DOM element to apply new attribute values to
// attrs - Object with attribute keys/values
// suspendLength - Optional integer of milliseconds to suspend redraw
// unitCheck - Boolean to indicate the need to use svgedit.units.setUnitAttr
var assignAttributes = this.assignAttributes = function(node, attrs, suspendLength, unitCheck) {
if(!suspendLength) suspendLength = 0;
// Opera has a problem with suspendRedraw() apparently
var handle = null;
if (!svgedit.browsersupport.isOpera()) svgroot.suspendRedraw(suspendLength);
for (var i in attrs) {
var ns = (i.substr(0,4) === "xml:" ? xmlns :
i.substr(0,6) === "xlink:" ? xlinkns : null);
if(ns) {
node.setAttributeNS(ns, i, attrs[i]);
} else if(!unitCheck) {
node.setAttribute(i, attrs[i]);
} else {
svgedit.units.setUnitAttr(node, i, attrs[i]);
}
}
if (!svgedit.browsersupport.isOpera()) svgroot.unsuspendRedraw(handle);
};
// Function: cleanupElement
// Remove unneeded (default) attributes, makes resulting SVG smaller
//
// Parameters:
// element - DOM element to clean up
var cleanupElement = this.cleanupElement = function(element) {
var handle = svgroot.suspendRedraw(60);
var defaults = {
'fill-opacity':1,
'stop-opacity':1,
'opacity':1,
'stroke':'none',
'stroke-dasharray':'none',
'stroke-linejoin':'miter',
'stroke-linecap':'butt',
'stroke-opacity':1,
'stroke-width':1,
'rx':0,
'ry':0
}
for(var attr in defaults) {
var val = defaults[attr];
if(element.getAttribute(attr) == val) {
element.removeAttribute(attr);
}
}
svgroot.unsuspendRedraw(handle);
};
// Function: addSvgElementFromJson
// Create a new SVG element based on the given object keys/values and add it to the current layer
// The element will be ran through cleanupElement before being returned
//
// Parameters:
// data - Object with the following keys/values:
// * element - DOM element to create
// * attr - Object with attributes/values to assign to the new element
// * curStyles - Boolean indicating that current style attributes should be applied first
//
// Returns: The new element
var addSvgElementFromJson = this.addSvgElementFromJson = function(data) {
var shape = getElem(data.attr.id);
// if shape is a path but we need to create a rect/ellipse, then remove the path
if (shape && data.element != shape.tagName) {
current_layer.removeChild(shape);
shape = null;
}
if (!shape) {
shape = svgdoc.createElementNS(svgns, data.element);
if (current_layer) {
(current_group || current_layer).appendChild(shape);
}
}
if(data.curStyles) {
assignAttributes(shape, {
"fill": cur_shape.fill,
"stroke": cur_shape.stroke,
"stroke-width": cur_shape.stroke_width,
"stroke-dasharray": cur_shape.stroke_dasharray,
"stroke-linejoin": cur_shape.stroke_linejoin,
"stroke-linecap": cur_shape.stroke_linecap,
"stroke-opacity": cur_shape.stroke_opacity,
"fill-opacity": cur_shape.fill_opacity,
"opacity": cur_shape.opacity / 2,
"style": "pointer-events:inherit"
}, 100);
}
assignAttributes(shape, data.attr, 100);
cleanupElement(shape);
return shape;
};
(function() { (function() {
// TODO: make this string optional and set by the client // TODO: make this string optional and set by the client
var comment = svgdoc.createComment(" Created with SVG-edit - http://svg-edit.googlecode.com/ "); var comment = svgdoc.createComment(" Created with SVG-edit - http://svg-edit.googlecode.com/ ");
@ -913,12 +511,6 @@ var all_layers = [],
// String with image URL of last loadable image // String with image URL of last loadable image
last_good_img_url = curConfig.imgPath + 'logo.png', last_good_img_url = curConfig.imgPath + 'logo.png',
// pointer to the current layer <g>
current_layer = null,
// pointer to current group (for in-group editing)
current_group = null,
// Array with current disabled elements (for in-group editing) // Array with current disabled elements (for in-group editing)
disabled_elems = [], disabled_elems = [],
@ -938,38 +530,10 @@ var all_layers = [],
current_mode = "select", current_mode = "select",
// String with the current direction in which an element is being resized // String with the current direction in which an element is being resized
current_resize_mode = "none", current_resize_mode = "none";
// Object containing data for the currently selected styles
all_properties = {
shape: {
fill: "#" + curConfig.initFill.color,
fill_paint: null,
fill_opacity: curConfig.initFill.opacity,
stroke: "#" + curConfig.initStroke.color,
stroke_paint: null,
stroke_opacity: curConfig.initStroke.opacity,
stroke_width: curConfig.initStroke.width,
stroke_dasharray: 'none',
stroke_linejoin: 'miter',
stroke_linecap: 'butt',
opacity: curConfig.initOpacity
}
};
all_properties.text = $.extend(true, {}, all_properties.shape);
$.extend(all_properties.text, {
fill: "#000000",
stroke_width: 0,
font_size: 24,
font_family: 'serif'
});
// Current shape style properties
var cur_shape = all_properties.shape,
// Current text style properties // Current text style properties
cur_text = all_properties.text, var cur_text = all_properties.text,
// Current general properties // Current general properties
cur_properties = cur_shape, cur_properties = cur_shape,
@ -984,9 +548,6 @@ var cur_shape = all_properties.shape,
// The DOM element that was just selected // The DOM element that was just selected
justSelected = null, justSelected = null,
// this object manages selectors for us
selectorManager = this.selectorManager = new SelectorManager(),
// DOM element for selection rectangle drawn by the user // DOM element for selection rectangle drawn by the user
rubberBox = null, rubberBox = null,
@ -1141,7 +702,7 @@ var getIntersectionList = this.getIntersectionList = function(rect) {
// //
// Returns: // Returns:
// A single bounding box object // A single bounding box object
var getStrokedBBox = this.getStrokedBBox = function(elems) { getStrokedBBox = this.getStrokedBBox = function(elems) {
if(!elems) elems = getVisibleElements(); if(!elems) elems = getVisibleElements();
if(!elems.length) return false; if(!elems.length) return false;
// Make sure the expected BBox is returned if the element is a group // Make sure the expected BBox is returned if the element is a group
@ -1156,7 +717,7 @@ var getStrokedBBox = this.getStrokedBBox = function(elems) {
var angle = svgedit.utilities.getRotationAngle(elem); var angle = svgedit.utilities.getRotationAngle(elem);
if ((angle && angle % 90) || if ((angle && angle % 90) ||
svgedit.math.hasMatrixTransform(getTransformList(elem))) { svgedit.math.hasMatrixTransform(svgedit.transformlist.getTransformList(elem))) {
// Accurate way to get BBox of rotated element in Firefox: // Accurate way to get BBox of rotated element in Firefox:
// Put element in group and get its BBox // Put element in group and get its BBox
@ -8894,6 +8455,7 @@ this.setSegType = function(new_type) {
pathActions.setSegType(new_type); pathActions.setSegType(new_type);
} }
// TODO(codedread): Remove the getBBox argument and split this function into two.
// Function: convertToPath // Function: convertToPath
// Convert selected element to a path, or get the BBox of an element-as-path // Convert selected element to a path, or get the BBox of an element-as-path
// //
@ -10018,7 +9580,6 @@ this.getPrivateMethods = function() {
round: round, round: round,
runExtensions: runExtensions, runExtensions: runExtensions,
sanitizeSvg: sanitizeSvg, sanitizeSvg: sanitizeSvg,
SelectorManager: SelectorManager,
shortFloat: shortFloat, shortFloat: shortFloat,
SVGEditTransformList: svgedit.transformlist.SVGTransformList, SVGEditTransformList: svgedit.transformlist.SVGTransformList,
toString: toString, toString: toString,

View File

@ -11,6 +11,7 @@
// 1) jQuery // 1) jQuery
// 2) browsersupport.js // 2) browsersupport.js
// 3) svgtransformlist.js // 3) svgtransformlist.js
// 4) math.js
(function() { (function() {

View File

@ -60,7 +60,7 @@ var typeMap_ = {px: 1};
* Initializes this module. * Initializes this module.
* *
* Parameters: * Parameters:
* elementContainer - an object implemnting the ElementContainer interface. * elementContainer - an object implementing the ElementContainer interface.
*/ */
svgedit.units.init = function(elementContainer) { svgedit.units.init = function(elementContainer) {
elementContainer_ = elementContainer; elementContainer_ = elementContainer;

View File

@ -18,6 +18,6 @@ window.setTimeout(function() {
var f = iframes[i]; var f = iframes[i];
f.style.height = (f.contentDocument.body.scrollHeight + 20) + 'px'; f.style.height = (f.contentDocument.body.scrollHeight + 20) + 'px';
} }
}, 1000); }, 1200);
</script> </script>
</html> </html>

View File

@ -7,6 +7,7 @@
<script type='text/javascript' src='qunit/qunit.js'></script> <script type='text/javascript' src='qunit/qunit.js'></script>
<script type='text/javascript'> <script type='text/javascript'>
$(function() { $(function() {
// TODO(codedread): Write tests for handling history events.
// Mocked out methods. // Mocked out methods.
svgedit.transformlist = {}; svgedit.transformlist = {};
@ -547,6 +548,8 @@
batch.unapply(); batch.unapply();
equals(concatResult, 'cba'); equals(concatResult, 'cba');
MockCommand.prototype.unapply = function() {};
tearDown(); tearDown();
}); });

44
test/select_test.html Normal file
View File

@ -0,0 +1,44 @@
<!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/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');
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(){});
});
});
</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>
</body>
</html>

View File

@ -12,6 +12,7 @@
<script type="text/javascript" src="../editor/svgutils.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/sanitize.js"></script>
<script type="text/javascript" src="../editor/history.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="../editor/svgcanvas.js"></script>
<script type="text/javascript" src="qunit/qunit.js"></script> <script type="text/javascript" src="qunit/qunit.js"></script>
<script type="text/javascript"> <script type="text/javascript">