Merge pull request #107 from gec/createLayerFix

Fixed Layers in svgcanvas. Moved Layer class. HistoryRecordingservice
master
Flint O'Brien 2016-05-02 12:02:42 -04:00
commit 53cfe68afa
8 changed files with 528 additions and 267 deletions

View File

@ -20,8 +20,6 @@ if (!svgedit.draw) {
} }
// alias // alias
var NS = svgedit.NS; var NS = svgedit.NS;
var LAYER_CLASS = svgedit.LAYER_CLASS;
var LAYER_CLASS_REGEX = svgedit.LAYER_CLASS_REGEX;
var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use'.split(','); var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use'.split(',');
@ -32,147 +30,6 @@ var RandomizeModes = {
}; };
var randomize_ids = RandomizeModes.LET_DOCUMENT_DECIDE; var randomize_ids = RandomizeModes.LET_DOCUMENT_DECIDE;
/**
* Add class 'layer' to the element
*
* Parameters:
* @param {SVGGElement} elem - The SVG element to update
*/
function addLayerClass(elem) {
var classes = elem.getAttribute('class');
if (classes === null || classes === undefined || classes.length === 0) {
elem.setAttribute('class', LAYER_CLASS);
} else if (! LAYER_CLASS_REGEX.test(classes)) {
elem.setAttribute('class', classes + ' ' + LAYER_CLASS);
}
}
function createLayer(name, svgElem) {
if (!svgElem) {
return undefined;
}
var svgdoc = svgElem.ownerDocument;
var new_layer = svgdoc.createElementNS(NS.SVG, "g");
var layer_title = svgdoc.createElementNS(NS.SVG, "title");
layer_title.textContent = name;
new_layer.appendChild(layer_title);
svgElem.appendChild(new_layer);
return new_layer;
}
/**
* This class encapsulates the concept of a layer in the drawing. It can be constructed with
* an existing group element or, with three parameters, will create a new layer group element.
* @param {string} name - Layer name
* @param {SVGGElement} group - SVG group element that constitutes the layer or null if a group should be created and added to the DOM..
* @param {SVGGElement} svgElem - The SVG DOM element. If defined, use this to add
* a new layer to the document.
*/
var Layer = svgedit.draw.Layer = function(name, group, svgElem) {
this.name_ = name;
this.group_ = group || createLayer(name, svgElem);
addLayerClass(this.group_);
svgedit.utilities.walkTree(this.group_, function(e){e.setAttribute("style", "pointer-events:inherit");});
this.group_.setAttribute("style", svgElem ? "pointer-events:all" : "pointer-events:none");
};
/**
* Get the layer's name.
* @returns {string} The layer name
*/
Layer.prototype.getName = function() {
return this.name_;
};
/**
* Get the group element for this layer.
* @returns {SVGGElement} The layer SVG group
*/
Layer.prototype.getGroup = function() {
return this.group_;
};
/**
* Active this layer so it takes pointer events.
*/
Layer.prototype.activate = function() {
this.group_.setAttribute("style", "pointer-events:all");
};
/**
* Deactive this layer so it does NOT take pointer events.
*/
Layer.prototype.deactivate = function() {
this.group_.setAttribute("style", "pointer-events:none");
};
/**
* Set this layer visible or hidden based on 'visible' parameter.
* @param {boolean} visible - If true, make visible; otherwise, hide it.
*/
Layer.prototype.setVisible = function(visible) {
var expected = visible === undefined || visible ? "inline" : "none";
var oldDisplay = this.group_.getAttribute("display");
if (oldDisplay !== expected) {
this.group_.setAttribute("display", expected);
}
};
/**
* Is this layer visible?
* @returns {boolean} True if visible.
*/
Layer.prototype.isVisible = function() {
return this.group_.getAttribute('display') !== 'none';
};
/**
* Get layer opacity.
* @returns {number} Opacity value.
*/
Layer.prototype.getOpacity = function() {
var opacity = this.group_.getAttribute('opacity');
if (opacity === null || opacity === undefined) {
return 1;
}
return parseFloat(opacity);
};
/**
* Sets the opacity of this layer. If opacity is not a value between 0.0 and 1.0,
* nothing happens.
* @param {number} opacity - A float value in the range 0.0-1.0
*/
Layer.prototype.setOpacity = function(opacity) {
if (typeof opacity === 'number' && opacity >= 0.0 && opacity <= 1.0) {
this.group_.setAttribute('opacity', opacity);
}
};
/**
* Append children to this layer.
* @param {SVGGElement} children - The children to append to this layer.
*/
Layer.prototype.appendChildren = function(children) {
for (var i = 0; i < children.length; ++i) {
this.group_.appendChild(children[i]);
}
};
/**
* Remove this layer's group from the DOM. No more functions on group can be called after this.
* @param {SVGGElement} children - The children to append to this layer.
* @returns {SVGGElement} The layer SVG group that was just removed.
*/
Layer.prototype.removeGroup = function() {
var parent = this.group_.parentNode;
var group = parent.removeChild(this.group_);
this.group_ = undefined;
return group;
};
@ -235,13 +92,17 @@ svgedit.draw.Drawing = function(svgElem, opt_idPrefix) {
* The z-ordered array of Layer objects. Each layer has a name * The z-ordered array of Layer objects. Each layer has a name
* and group element. * and group element.
* The first layer is the one at the bottom of the rendering. * The first layer is the one at the bottom of the rendering.
* @type {Layer[]} * @type {Array.<Layer>}
*/ */
this.all_layers = []; this.all_layers = [];
/** /**
* Map of all_layers by name. * Map of all_layers by name.
* @type {Object.<string,Layer>} *
* Note: Layers are ordered, but referenced externally by name; so, we need both container
* types depending on which function is called (i.e. all_layers and layer_map).
*
* @type {Object.<string, Layer>}
*/ */
this.layer_map = {}; this.layer_map = {};
@ -418,6 +279,15 @@ svgedit.draw.Drawing.prototype.getCurrentLayer = function() {
return this.current_layer ? this.current_layer.getGroup() : null; return this.current_layer ? this.current_layer.getGroup() : null;
}; };
/**
* Get a layer by name.
* @returns {SVGGElement} The SVGGElement representing the named layer or null.
*/
svgedit.draw.Drawing.prototype.getLayerByName = function(name) {
var layer = this.layer_map[name];
return layer ? layer.getGroup() : null;
};
/** /**
* Returns the name of the currently selected layer. If an error occurs, an empty string * Returns the name of the currently selected layer. If an error occurs, an empty string
* is returned. * is returned.
@ -427,6 +297,107 @@ svgedit.draw.Drawing.prototype.getCurrentLayerName = function () {
return this.current_layer ? this.current_layer.getName() : ''; return this.current_layer ? this.current_layer.getName() : '';
}; };
/**
* Set the current layer's name.
* @param {string} name - The new name.
* @returns {Object} If the name was changed, returns {title:SVGGElement, previousName:string}; otherwise null.
*/
svgedit.draw.Drawing.prototype.setCurrentLayerName = function (name) {
return this.current_layer ? this.current_layer.setName(name) : null;
};
/**
* Set the current layer's position.
* @param {number} newpos - The zero-based index of the new position of the layer. Range should be 0 to layers-1
* @returns {Object} If the name was changed, returns {title:SVGGElement, previousName:string}; otherwise null.
*/
svgedit.draw.Drawing.prototype.setCurrentLayerPosition = function (newpos) {
var layer_count = this.getNumLayers();
if (!this.current_layer || newpos < 0 || newpos >= layer_count) {
return null;
}
var oldpos;
for (oldpos = 0; oldpos < layer_count; ++oldpos) {
if (this.all_layers[oldpos] == this.current_layer) {break;}
}
// some unknown error condition (current_layer not in all_layers)
if (oldpos == layer_count) { return null; }
if (oldpos != newpos) {
// if our new position is below us, we need to insert before the node after newpos
var refGroup = null;
var current_group = this.current_layer.getGroup();
var oldNextSibling = current_group.nextSibling;
if (newpos > oldpos ) {
if (newpos < layer_count-1) {
refGroup = this.all_layers[newpos+1].getGroup();
}
}
// if our new position is above us, we need to insert before the node at newpos
else {
refGroup = this.all_layers[newpos].getGroup();
}
this.svgElem_.insertBefore(current_group, refGroup);
this.identifyLayers();
this.setCurrentLayer(this.getLayerName(newpos));
return {
currentGroup: current_group,
oldNextSibling: oldNextSibling
};
}
return null;
};
svgedit.draw.Drawing.prototype.mergeLayer = function (hrService) {
var current_group = this.current_layer.getGroup();
var prevGroup = $(current_group).prev()[0];
if (!prevGroup) {return null;}
hrService.startBatchCommand('Merge Layer');
var layerNextSibling = current_group.nextSibling;
hrService.removeElement(current_group, layerNextSibling, this.svgElem_);
while (current_group.firstChild) {
var child = current_group.firstChild;
if (child.localName == 'title') {
hrService.removeElement(child, child.nextSibling, current_group);
current_group.removeChild(child);
continue;
}
var oldNextSibling = child.nextSibling;
prevGroup.appendChild(child);
hrService.moveElement(child, oldNextSibling, current_group);
}
// Remove current layer's group
this.current_layer.removeGroup();
// Remove the current layer and set the previous layer as the new current layer
var index = this.all_layers.indexOf(this.current_layer);
if (index > 0) {
var name = this.current_layer.getName();
this.current_layer = this.all_layers[index-1]
this.all_layers.splice(index, 1);
delete this.layer_map[name];
}
hrService.endBatchCommand();
};
svgedit.draw.Drawing.prototype.mergeAllLayers = function (hrService) {
// Set the current layer to the last layer.
this.current_layer = this.all_layers[this.all_layers.length-1];
hrService.startBatchCommand('Merge all Layers');
while (this.all_layers.length > 1) {
this.mergeLayer(hrService);
}
hrService.endBatchCommand();
};
/** /**
* Sets the current layer. If the name is not a valid layer name, then this * Sets the current layer. If the name is not a valid layer name, then this
* function returns false. Otherwise it returns true. This is not an * function returns false. Otherwise it returns true. This is not an
@ -479,7 +450,7 @@ function findLayerNameInGroup(group) {
/** /**
* Given a set of names, return a new unique name. * Given a set of names, return a new unique name.
* @param {string[]} existingLayerNames - Existing layer names. * @param {Array.<string>} existingLayerNames - Existing layer names.
* @returns {string} - The new name. * @returns {string} - The new name.
*/ */
function getNewLayerName(existingLayerNames) { function getNewLayerName(existingLayerNames) {
@ -510,9 +481,9 @@ svgedit.draw.Drawing.prototype.identifyLayers = function() {
var name = findLayerNameInGroup(child); var name = findLayerNameInGroup(child);
if (name) { if (name) {
layernames.push(name); layernames.push(name);
layer = new Layer(name, child); layer = new svgedit.draw.Layer(name, child);
this.all_layers.push(layer); this.all_layers.push(layer);
this.layer_map[ name] = layer; this.layer_map[name] = layer;
} else { } else {
// if group did not have a name, it is an orphan // if group did not have a name, it is an orphan
orphans.push(child); orphans.push(child);
@ -526,10 +497,10 @@ svgedit.draw.Drawing.prototype.identifyLayers = function() {
// If orphans or no layers found, create a new layer and add all the orphans to it // If orphans or no layers found, create a new layer and add all the orphans to it
if (orphans.length > 0 || !childgroups) { if (orphans.length > 0 || !childgroups) {
layer = new Layer(getNewLayerName(layernames), null, this.svgElem_); layer = new svgedit.draw.Layer(getNewLayerName(layernames), null, this.svgElem_);
layer.appendChildren(orphans); layer.appendChildren(orphans);
this.all_layers.push(layer); this.all_layers.push(layer);
this.layer_map[ name] = layer; this.layer_map[name] = layer;
} else { } else {
layer.activate(); layer.activate();
} }
@ -551,9 +522,9 @@ svgedit.draw.Drawing.prototype.createLayer = function(name) {
if (name === undefined || name === null || name === '' || this.layer_map[name]) { if (name === undefined || name === null || name === '' || this.layer_map[name]) {
name = getNewLayerName(Object.keys(this.layer_map)); name = getNewLayerName(Object.keys(this.layer_map));
} }
var layer = new Layer(name, null, this.svgElem_); var layer = new svgedit.draw.Layer(name, null, this.svgElem_);
this.all_layers.push(layer); this.all_layers.push(layer);
this.layer_map[ name] = layer; this.layer_map[name] = layer;
this.current_layer = layer; this.current_layer = layer;
return layer.getGroup(); return layer.getGroup();
}; };

173
editor/historyrecording.js Normal file
View File

@ -0,0 +1,173 @@
/*globals svgedit*/
/*jslint vars: true, eqeq: true */
/**
* Package: svgedit.history
*
* Licensed under the MIT License
*
* Copyright(c) 2010 Alexis Deveria
* Copyright(c) 2010 Jeff Schiller
* Copyright(c) 2016 Flint O'Brien
*/
// Dependencies:
// 1) history.js
(function() {
'use strict';
if (!svgedit.history) {
svgedit.history = {};
}
var history = svgedit.history;
/**
* History recording service.
*
* A <strong>single</strong> service object that can be passed around to provide history
* recording. There is a simple start/end interface for batch commands.
* Easy to mock for unit tests. Built on top of history classes in history.js.
*
* HistoryRecordingService.NO_HISTORY is a singleton that can be passed in to functions
* that record history. This helps when the caller requires that no history be recorded.
*
* Usage:
* The following will record history: insert, batch, insert.
* ```
* hrService = new svgedit.history.HistoryRecordingService(this.undoMgr);
* hrService.insertElement(elem, text); // add simple command to history.
* hrService.startBatchCommand('create two elements');
* hrService.changeElement(elem, attrs, text); // add to batchCommand
* hrService.changeElement(elem, attrs2, text); // add to batchCommand
* hrService.endBatchCommand(); // add batch command with two change commands to history.
* hrService.insertElement(elem, text); // add simple command to history.
* ```
*
* Note that all functions return this, so commands can be chained, like so:
*
* ```
* hrService
* .startBatchCommand('create two elements')
* .insertElement(elem, text)
* .changeElement(elem, attrs, text)
* .endBatchCommand();
* ```
*
* @param {svgedit.history.UndoManager} undoManager - The undo manager.
* A value of null is valid for cases where no history recording is required.
* See singleton: HistoryRecordingService.NO_HISTORY
*/
var HistoryRecordingService = history.HistoryRecordingService = function(undoManager) {
this.undoManager = undoManager;
this.currentBatchCommand = null;
this.batchCommandStack = [];
};
/**
* @type {svgedit.history.HistoryRecordingService} NO_HISTORY - Singleton that can be passed
* in to functions that record history, but the caller requires that no history be recorded.
*/
HistoryRecordingService.NO_HISTORY = new HistoryRecordingService();
/**
* Start a batch command so multiple commands can recorded as a single history command.
* Requires a corresponding call to endBatchCommand. Start and end commands can be nested.
*
* @param {string} text - Optional string describing the batch command.
* @returns {svgedit.history.HistoryRecordingService}
*/
HistoryRecordingService.prototype.startBatchCommand = function(text) {
if (!this.undoManager) {return this;}
this.currentBatchCommand = new history.BatchCommand(text);
this.batchCommandStack.push(this.currentBatchCommand);
return this;
};
/**
* End a batch command and add it to the history or a parent batch command.
* @returns {svgedit.history.HistoryRecordingService}
*/
HistoryRecordingService.prototype.endBatchCommand = function() {
if (!this.undoManager) {return this;}
if (this.currentBatchCommand) {
var batchCommand = this.currentBatchCommand;
this.batchCommandStack.pop();
var length = this.batchCommandStack.length;
this.currentBatchCommand = length ? this.batchCommandStack[length-1] : null;
this._addCommand(batchCommand);
}
return this;
};
/**
* Add a MoveElementCommand to the history or current batch command
* @param {Element} elem - The DOM element that was moved
* @param {Element} oldNextSibling - The element's next sibling before it was moved
* @param {Element} oldParent - The element's parent before it was moved
* @param {string} [text] - An optional string visible to user related to this change
* @returns {svgedit.history.HistoryRecordingService}
*/
HistoryRecordingService.prototype.moveElement = function(elem, oldNextSibling, oldParent, text) {
if (!this.undoManager) {return this;}
this._addCommand(new history.MoveElementCommand(elem, oldNextSibling, oldParent, text));
return this;
};
/**
* Add an InsertElementCommand to the history or current batch command
* @param {Element} elem - The DOM element that was added
* @param {string} [text] - An optional string visible to user related to this change
* @returns {svgedit.history.HistoryRecordingService}
*/
HistoryRecordingService.prototype.insertElement = function(elem, text) {
if (!this.undoManager) {return this;}
this._addCommand(new history.InsertElementCommand(elem, text));
return this;
};
/**
* Add a RemoveElementCommand to the history or current batch command
* @param {Element} elem - The DOM element that was removed
* @param {Element} oldNextSibling - The element's next sibling before it was removed
* @param {Element} oldParent - The element's parent before it was removed
* @param {string} [text] - An optional string visible to user related to this change
* @returns {svgedit.history.HistoryRecordingService}
*/
HistoryRecordingService.prototype.removeElement = function(elem, oldNextSibling, oldParent, text) {
if (!this.undoManager) {return this;}
this._addCommand(new history.RemoveElementCommand(elem, oldNextSibling, oldParent, text));
return this;
};
/**
* Add a ChangeElementCommand to the history or current batch command
* @param {Element} elem - The DOM element that was changed
* @param {object} attrs - An object with the attributes to be changed and the values they had *before* the change
* @param {string} [text] - An optional string visible to user related to this change
* @returns {svgedit.history.HistoryRecordingService}
*/
HistoryRecordingService.prototype.changeElement = function(elem, attrs, text) {
if (!this.undoManager) {return this;}
this._addCommand(new history.ChangeElementCommand(elem, attrs, text));
return this;
};
/**
* Private function to add a command to the history or current batch command.
* @param cmd
* @returns {svgedit.history.HistoryRecordingService}
* @private
*/
HistoryRecordingService.prototype._addCommand = function(cmd) {
if (!this.undoManager) {return this;}
if (this.currentBatchCommand) {
this.currentBatchCommand.addSubCommand(cmd);
} else {
this.undoManager.addCommandToHistory(cmd);
}
};
}());

200
editor/layer.js Normal file
View File

@ -0,0 +1,200 @@
/*globals svgedit*/
/*jslint vars: true, eqeq: true */
/**
* Package: svgedit.history
*
* Licensed under the MIT License
*
* Copyright(c) 2011 Jeff Schiller
* Copyright(c) 2016 Flint O'Brien
*/
// Dependencies:
// 1) svgedit.js
// 2) draw.js
(function() {
'use strict';
if (!svgedit.draw) {
svgedit.draw = {};
}
var NS = svgedit.NS;
/**
* This class encapsulates the concept of a layer in the drawing. It can be constructed with
* an existing group element or, with three parameters, will create a new layer group element.
* @param {string} name - Layer name
* @param {SVGGElement} group - SVG group element that constitutes the layer or null if a group should be created and added to the DOM..
* @param {SVGGElement} svgElem - The SVG DOM element. If defined, use this to add
* a new layer to the document.
*/
var Layer = svgedit.draw.Layer = function(name, group, svgElem) {
this.name_ = name;
this.group_ = group;
if (!group) {
// Create a group element with title and add it to the DOM.
var svgdoc = svgElem.ownerDocument;
this.group_ = svgdoc.createElementNS(NS.SVG, "g");
var layer_title = svgdoc.createElementNS(NS.SVG, "title");
layer_title.textContent = name;
this.group_.appendChild(layer_title);
svgElem.appendChild(this.group_);
}
addLayerClass(this.group_);
svgedit.utilities.walkTree(this.group_, function(e){e.setAttribute("style", "pointer-events:inherit");});
this.group_.setAttribute("style", svgElem ? "pointer-events:all" : "pointer-events:none");
};
/**
* @type {string} CLASS_NAME - class attribute assigned to all layer groups.
*/
Layer.CLASS_NAME = 'layer';
/**
* @type {RegExp} CLASS_REGEX - Used to test presence of class Layer.CLASS_NAME
*/
Layer.CLASS_REGEX = new RegExp('(\\s|^)' + Layer.CLASS_NAME + '(\\s|$)');
/**
* Get the layer's name.
* @returns {string} The layer name
*/
Layer.prototype.getName = function() {
return this.name_;
};
/**
* Get the group element for this layer.
* @returns {SVGGElement} The layer SVG group
*/
Layer.prototype.getGroup = function() {
return this.group_;
};
/**
* Active this layer so it takes pointer events.
*/
Layer.prototype.activate = function() {
this.group_.setAttribute("style", "pointer-events:all");
};
/**
* Deactive this layer so it does NOT take pointer events.
*/
Layer.prototype.deactivate = function() {
this.group_.setAttribute("style", "pointer-events:none");
};
/**
* Set this layer visible or hidden based on 'visible' parameter.
* @param {boolean} visible - If true, make visible; otherwise, hide it.
*/
Layer.prototype.setVisible = function(visible) {
var expected = visible === undefined || visible ? "inline" : "none";
var oldDisplay = this.group_.getAttribute("display");
if (oldDisplay !== expected) {
this.group_.setAttribute("display", expected);
}
};
/**
* Is this layer visible?
* @returns {boolean} True if visible.
*/
Layer.prototype.isVisible = function() {
return this.group_.getAttribute('display') !== 'none';
};
/**
* Get layer opacity.
* @returns {number} Opacity value.
*/
Layer.prototype.getOpacity = function() {
var opacity = this.group_.getAttribute('opacity');
if (opacity === null || opacity === undefined) {
return 1;
}
return parseFloat(opacity);
};
/**
* Sets the opacity of this layer. If opacity is not a value between 0.0 and 1.0,
* nothing happens.
* @param {number} opacity - A float value in the range 0.0-1.0
*/
Layer.prototype.setOpacity = function(opacity) {
if (typeof opacity === 'number' && opacity >= 0.0 && opacity <= 1.0) {
this.group_.setAttribute('opacity', opacity);
}
};
/**
* Append children to this layer.
* @param {SVGGElement} children - The children to append to this layer.
*/
Layer.prototype.appendChildren = function(children) {
for (var i = 0; i < children.length; ++i) {
this.group_.appendChild(children[i]);
}
};
Layer.prototype.getTitleElement = function() {
var len = this.group_.childNodes.length;
for (var i = 0; i < len; ++i) {
var child = this.group_.childNodes.item(i);
if (child && child.tagName === 'title') {
return child;
}
}
return null;
};
Layer.prototype.setName = function(name) {
var previousName = this.name_;
name = svgedit.utilities.toXml(name);
// now change the underlying title element contents
var title = this.getTitleElement();
if (title) {
while (title.firstChild) { title.removeChild(title.firstChild); }
title.textContent = name;
this.name_ = name;
return {title: title, previousName: previousName};
}
return null;
};
/**
* Remove this layer's group from the DOM. No more functions on group can be called after this.
* @param {SVGGElement} children - The children to append to this layer.
* @returns {SVGGElement} The layer SVG group that was just removed.
*/
Layer.prototype.removeGroup = function() {
var parent = this.group_.parentNode;
var group = parent.removeChild(this.group_);
this.group_ = undefined;
return group;
};
/**
* Add class Layer.CLASS_NAME to the element (usually class='layer').
*
* Parameters:
* @param {SVGGElement} elem - The SVG element to update
*/
function addLayerClass(elem) {
var classes = elem.getAttribute('class');
if (classes === null || classes === undefined || classes.length === 0) {
elem.setAttribute('class', Layer.CLASS_NAME);
} else if (! Layer.CLASS_REGEX.test(classes)) {
elem.setAttribute('class', classes + ' ' + Layer.CLASS_NAME);
}
}
}());

View File

@ -39,10 +39,12 @@
<script src="svgutils.js"></script> <script src="svgutils.js"></script>
<script src="sanitize.js"></script> <script src="sanitize.js"></script>
<script src="history.js"></script> <script src="history.js"></script>
<script src="historyrecording.js"></script>
<script src="coords.js"></script> <script src="coords.js"></script>
<script src="recalculate.js"></script> <script src="recalculate.js"></script>
<script src="select.js"></script> <script src="select.js"></script>
<script src="draw.js"></script> <script src="draw.js"></script>
<script src="layer.js"></script>
<script src="path.js"></script> <script src="path.js"></script>
<script src="svgcanvas.js"></script> <script src="svgcanvas.js"></script>
<script src="svg-editor.js"></script> <script src="svg-editor.js"></script>

View File

@ -1841,7 +1841,7 @@ TODOS
* @returns {boolean} True if the element is a layer * @returns {boolean} True if the element is a layer
*/ */
function isLayer(elem) { function isLayer(elem) {
return elem && elem.tagName === 'g' && svgedit.LAYER_CLASS_REGEX.test(elem.getAttribute('class')) return elem && elem.tagName === 'g' && svgedit.draw.Layer.CLASS_REGEX.test(elem.getAttribute('class'))
} }
// called when any element has changed // called when any element has changed

View File

@ -5055,40 +5055,18 @@ this.setCurrentLayer = function(name) {
// Returns: // Returns:
// true if the rename succeeded, false otherwise. // true if the rename succeeded, false otherwise.
this.renameCurrentLayer = function(newname) { this.renameCurrentLayer = function(newname) {
var i;
var drawing = getCurrentDrawing(); var drawing = getCurrentDrawing();
if (drawing.current_layer) { var layer = drawing.getCurrentLayer();
var oldLayer = drawing.current_layer; if (layer) {
// setCurrentLayer will return false if the name doesn't already exist var result = drawing.setCurrentLayerName( newname);
// this means we are free to rename our oldLayer if (result) {
if (!canvas.setCurrentLayer(newname)) {
var batchCmd = new svgedit.history.BatchCommand('Rename Layer'); var batchCmd = new svgedit.history.BatchCommand('Rename Layer');
// find the index of the layer batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(result.title, {'#text':result.previousName}));
for (i = 0; i < drawing.getNumLayers(); ++i) {
if (drawing.all_layers[i][1] == oldLayer) {break;}
}
var oldname = drawing.getLayerName(i);
drawing.all_layers[i][0] = svgedit.utilities.toXml(newname);
// now change the underlying title element contents
var len = oldLayer.childNodes.length;
for (i = 0; i < len; ++i) {
var child = oldLayer.childNodes.item(i);
// found the <title> element, now append all the
if (child && child.tagName == 'title') {
// wipe out old name
while (child.firstChild) { child.removeChild(child.firstChild); }
child.textContent = newname;
batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(child, {'#text':oldname}));
addCommandToHistory(batchCmd); addCommandToHistory(batchCmd);
call('changed', [oldLayer]); call('changed', [layer]);
return true; return true;
} }
} }
}
drawing.current_layer = oldLayer;
}
return false; return false;
}; };
@ -5105,36 +5083,11 @@ this.renameCurrentLayer = function(newname) {
// true if the current layer position was changed, false otherwise. // true if the current layer position was changed, false otherwise.
this.setCurrentLayerPosition = function(newpos) { this.setCurrentLayerPosition = function(newpos) {
var oldpos, drawing = getCurrentDrawing(); var oldpos, drawing = getCurrentDrawing();
if (drawing.current_layer && newpos >= 0 && newpos < drawing.getNumLayers()) { var result = drawing.setCurrentLayerPosition(newpos);
for (oldpos = 0; oldpos < drawing.getNumLayers(); ++oldpos) { if (result) {
if (drawing.all_layers[oldpos][1] == drawing.current_layer) {break;} addCommandToHistory(new svgedit.history.MoveElementCommand(result.currentGroup, result.oldNextSibling, svgcontent));
}
// some unknown error condition (current_layer not in all_layers)
if (oldpos == drawing.getNumLayers()) { return false; }
if (oldpos != newpos) {
// if our new position is below us, we need to insert before the node after newpos
var refLayer = null;
var oldNextSibling = drawing.current_layer.nextSibling;
if (newpos > oldpos ) {
if (newpos < drawing.getNumLayers()-1) {
refLayer = drawing.all_layers[newpos+1][1];
}
}
// if our new position is above us, we need to insert before the node at newpos
else {
refLayer = drawing.all_layers[newpos][1];
}
svgcontent.insertBefore(drawing.current_layer, refLayer);
addCommandToHistory(new svgedit.history.MoveElementCommand(drawing.current_layer, oldNextSibling, svgcontent));
identifyLayers();
canvas.setCurrentLayer(drawing.getLayerName(newpos));
return true; return true;
} }
}
return false; return false;
}; };
@ -5179,14 +5132,8 @@ this.setLayerVisibility = function(layername, bVisible) {
this.moveSelectedToLayer = function(layername) { this.moveSelectedToLayer = function(layername) {
// find the layer // find the layer
var i; var i;
var layer = null;
var drawing = getCurrentDrawing(); var drawing = getCurrentDrawing();
for (i = 0; i < drawing.getNumLayers(); ++i) { var layer = drawing.getLayerByName(layername);
if (drawing.getLayerName(i) == layername) {
layer = drawing.all_layers[i][1];
break;
}
}
if (!layer) {return false;} if (!layer) {return false;}
var batchCmd = new svgedit.history.BatchCommand('Move Elements to Layer'); var batchCmd = new svgedit.history.BatchCommand('Move Elements to Layer');
@ -5209,57 +5156,25 @@ this.moveSelectedToLayer = function(layername) {
return true; return true;
}; };
this.mergeLayer = function(skipHistory) {
var batchCmd = new svgedit.history.BatchCommand('Merge Layer');
var drawing = getCurrentDrawing();
var prev = $(drawing.current_layer).prev()[0];
if (!prev) {return;}
var childs = drawing.current_layer.childNodes;
var len = childs.length;
var layerNextSibling = drawing.current_layer.nextSibling;
batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(drawing.current_layer, layerNextSibling, svgcontent));
while (drawing.current_layer.firstChild) { this.mergeLayer = function(hrService) {
var ch = drawing.current_layer.firstChild; if (!hrService) {
if (ch.localName == 'title') { hrService = new svgedit.history.HistoryRecordingService(this.undoMgr);
var chNextSibling = ch.nextSibling;
batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(ch, chNextSibling, drawing.current_layer));
drawing.current_layer.removeChild(ch);
continue;
} }
var oldNextSibling = ch.nextSibling; getCurrentDrawing().mergeLayer(hrService);
prev.appendChild(ch);
batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(ch, oldNextSibling, drawing.current_layer));
}
// Remove current layer
svgcontent.removeChild(drawing.current_layer);
if (!skipHistory) {
clearSelection(); clearSelection();
identifyLayers(); leaveContext();
call('changed', [svgcontent]); call('changed', [svgcontent]);
addCommandToHistory(batchCmd);
}
drawing.current_layer = prev;
return batchCmd;
}; };
this.mergeAllLayers = function() { this.mergeAllLayers = function(hrService) {
var batchCmd = new svgedit.history.BatchCommand('Merge all Layers'); if (!hrService) {
var drawing = getCurrentDrawing(); hrService = new svgedit.history.HistoryRecordingService(this.undoMgr);
drawing.current_layer = drawing.all_layers[drawing.getNumLayers()-1][1];
while ($(svgcontent).children('g').length > 1) {
batchCmd.addSubCommand(canvas.mergeLayer(true));
} }
getCurrentDrawing().mergeAllLayers(hrService);
clearSelection(); clearSelection();
identifyLayers(); leaveContext();
call('changed', [svgcontent]); call('changed', [svgcontent]);
addCommandToHistory(batchCmd);
}; };
// Function: leaveContext // Function: leaveContext

View File

@ -15,10 +15,8 @@ svgedit = {
XLINK: 'http://www.w3.org/1999/xlink', XLINK: 'http://www.w3.org/1999/xlink',
XML: 'http://www.w3.org/XML/1998/namespace', XML: 'http://www.w3.org/XML/1998/namespace',
XMLNS: 'http://www.w3.org/2000/xmlns/' // see http://www.w3.org/TR/REC-xml-names/#xmlReserved XMLNS: 'http://www.w3.org/2000/xmlns/' // see http://www.w3.org/TR/REC-xml-names/#xmlReserved
}, }
LAYER_CLASS: 'layer'
}; };
svgedit.LAYER_CLASS_REGEX = new RegExp('(\\s|^)' + svgedit.LAYER_CLASS + '(\\s|$)');
// return the svgedit.NS with key values switched and lowercase // return the svgedit.NS with key values switched and lowercase
svgedit.getReverseNS = function() {'use strict'; svgedit.getReverseNS = function() {'use strict';

View File

@ -6,9 +6,11 @@
<link rel='stylesheet' href='qunit/qunit.css' type='text/css'/> <link rel='stylesheet' href='qunit/qunit.css' type='text/css'/>
<script src='../editor/jquery.js'></script> <script src='../editor/jquery.js'></script>
<script src='../editor/svgedit.js'></script> <script src='../editor/svgedit.js'></script>
<script src='../editor/pathseg.js'></script>
<script src='../editor/browser.js'></script> <script src='../editor/browser.js'></script>
<script src='../editor/svgutils.js'></script> <script src='../editor/svgutils.js'></script>
<script src='../editor/draw.js'></script> <script src='../editor/draw.js'></script>
<script src='../editor/layer.js'></script>
<script src='qunit/qunit.js'></script> <script src='qunit/qunit.js'></script>
<script> <script>
$(function() { $(function() {
@ -19,7 +21,7 @@
} }
}; };
var NS = svgedit.NS; var NS = svgedit.NS;
var LAYER_CLASS = svgedit.LAYER_CLASS; var LAYER_CLASS = svgedit.draw.Layer.CLASS_NAME;
var NONCE = 'foo'; var NONCE = 'foo';
var LAYER1 = 'Layer 1'; var LAYER1 = 'Layer 1';
var LAYER2 = 'Layer 2'; var LAYER2 = 'Layer 2';