diff --git a/editor/document.js b/editor/document.js index ce6d779a..83843c18 100644 --- a/editor/document.js +++ b/editor/document.js @@ -10,7 +10,6 @@ TODOs: Phase 1: - - migrate svgcanvas to using a Document object for its calls to getNextId() and getId() - migrate usages of randomizeIds() to proxy into the Document Phase 2: @@ -51,6 +50,7 @@ svgedit.document.Document = function(svgElem, opt_idPrefix) { this.svgElem_ = svgElem; this.obj_num = 0; this.idPrefix = opt_idPrefix || "svg_"; + this.releasedNums = []; // Determine if the element has a nonce on it this.nonce_ = this.svgElem_.getAttributeNS(se_ns, 'nonce') || ""; @@ -89,16 +89,65 @@ svgedit.document.Document.prototype.getId = function() { * @return {String} The next object Id to use. */ svgedit.document.Document.prototype.getNextId = function() { - // always increment the obj_num every time we call getNextId() - this.obj_num++; + var oldObjNum = this.obj_num; + var restoreOldObjNum = false; - // ensure the ID does not exist + // If there are any released numbers in the release stack, + // use the last one instead of the next obj_num. + // We need to temporarily use obj_num as that is what getId() depends on. + if (this.releasedNums.length > 0) { + this.obj_num = this.releasedNums.pop(); + restoreOldObjNum = true; + } else { + // If we are not using a released id, then increment the obj_num. + this.obj_num++; + } + + // Ensure the ID does not exist. var id = this.getId(); while (this.getElem_(id)) { + if (restoreOldObjNum) { + this.obj_num = oldObjNum; + restoreOldObjNum = false; + } this.obj_num++; id = this.getId(); } + // Restore the old object number if required. + if (restoreOldObjNum) { + this.obj_num = oldObjNum; + } return id; }; +/** + * Releases the object Id, letting it be used as the next id in getNextId(). + * This method DOES NOT remove any elements from the DOM, it is expected + * that client code will do this. + * + * @param {String} The id to release. + * @return {boolean} Returns true if the id was valid to be released, + * false otherwise. + */ +svgedit.document.Document.prototype.releaseId = function(id) { + // confirm if this is a valid id for this Document, else return false + var front = this.idPrefix + (this.nonce_ ? this.nonce_ +'_' : ''); + if (typeof id != typeof '' || id.indexOf(front) != 0) { + return false; + } + // extract the obj_num of this id + var num = parseInt(id.substr(front.length)); + + // if we didn't get a positive number or we already released this number + // then return false. + if (typeof num != typeof 1 || num <= 0 || this.releasedNums.indexOf(num) != -1) { + return false; + } + + // push the released number into the released queue + this.releasedNums.push(num); + + return true; +}; + })(); diff --git a/editor/svgcanvas.js b/editor/svgcanvas.js index ea9d5bd2..3a7990c3 100644 --- a/editor/svgcanvas.js +++ b/editor/svgcanvas.js @@ -19,6 +19,7 @@ // 7) sanitize.js // 8) history.js // 9) select.js +// 10) document.js if(!window.console) { window.console = {}; @@ -162,6 +163,18 @@ $(svgcontent).attr({ "xmlns:xlink": xlinkns }).appendTo(svgroot); +// Prefix string for element IDs +var idprefix = "svg_"; + +// Function: setIdPrefix +// Changes the ID prefix to the given value +// +// Parameters: +// p - String with the new prefix +canvas.setIdPrefix = function(p) { + idprefix = p; +}; + // nonce to uniquify id's var nonce = Math.floor(Math.random() * 100001); @@ -171,6 +184,16 @@ var randomize_ids = false; // Set nonce if randomize_ids = true if (randomize_ids) svgcontent.setAttributeNS(se_ns, 'se:nonce', nonce); +// Current svgedit.document.Document object +// @type {svgedit.document.Document} +var current_doc = new svgedit.document.Document(svgcontent, idprefix); + +// Function: getCurrentDoc +// Returns the current Document JS object. +// @return {svgedit.document.Document} +canvas.getCurrentDoc = function() { + return current_doc; +}; // Float displaying the current zoom level (1 = 100%, .5 = 50%, etc) var current_zoom = 1; @@ -536,9 +559,6 @@ var all_layers = [], // Boolean indicating whether or not a draw action has been started started = false, - // Integer with internal ID number for the latest element - obj_num = 0, - // String with an element's initial transform attribute value start_transform = null, @@ -981,31 +1001,17 @@ var getId, getNextId, call; // Object to contain editor event names and callback functions var events = {}; - // Prefix string for element IDs - var idprefix = "svg_"; - // Function: getId // Returns the last created DOM element ID string getId = c.getId = function() { - if (randomize_ids) { - return idprefix + nonce +'_' + obj_num; - } else { - return idprefix + obj_num; - } + return current_doc.getId(); }; // Function: getNextId // Creates and returns a unique ID string for a DOM element getNextId = c.getNextId = function() { - // always increment the obj_num every time we call getNextId() - obj_num++; - - // ensure the ID does not exist - var id = getId(); - while (getElem(id)) { - obj_num++; - id = getId(); - } + var id = current_doc.getNextId(); + console.log("getNextId() returned " + id); return id; }; @@ -1036,14 +1042,6 @@ var getId, getNextId, call; return old; }; - // Function: setIdPrefix - // Changes the ID prefix to the given value - // - // Parameters: - // p - String with the new prefix - c.setIdPrefix = function(p) { - idprefix = p; - }; }(canvas)); // Function: canvas.prepareSvg @@ -3259,7 +3257,6 @@ var getMouseTarget = this.getMouseTarget = function(evt) { // TODO: Make true when in multi-unit mode var useUnit = false; // (curConfig.baseUnit !== 'px'); - started = false; switch (current_mode) { @@ -3476,6 +3473,7 @@ var getMouseTarget = this.getMouseTarget = function(evt) { }); if (!keep && element != null) { + current_doc.releaseId(getId()); element.parentNode.removeChild(element); element = null; @@ -6693,7 +6691,10 @@ this.setSvgString = function(xmlString) { svgcontent.setAttributeNS(xmlnsns, 'xmlns:se', se_ns); svgcontent.setAttributeNS(se_ns, 'se:nonce', nonce); if (extensions["Arrows"]) call("setarrownonce", nonce) ; - } + } + + current_doc = new svgedit.document.Document(svgcontent, idprefix); + // change image href vals if possible content.find('image').each(function() { var image = this; diff --git a/test/document_test.html b/test/document_test.html index 6d7ceda5..b3157705 100644 --- a/test/document_test.html +++ b/test/document_test.html @@ -153,6 +153,24 @@ while(svg_n.firstChild) {svg_n.removeChild(svg.firstChild);} }); + + test('Test releaseId()', function() { + expect(6); + + var doc = new svgedit.document.Document(svg); + + var firstId = doc.getNextId(); + var secondId = doc.getNextId(); + + var result = doc.releaseId(firstId); + ok(result); + equals(doc.getNextId(), firstId); + equals(doc.getNextId(), "svg_3"); + + ok(!doc.releaseId("bad-id")); + ok(doc.releaseId(firstId)); + ok(!doc.releaseId(firstId)); + }); });