node-webodf
Version:
WebODF - JavaScript Document Engine http://webodf.org/
179 lines (169 loc) • 6.72 kB
JavaScript
/**
* Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com>
*
* @licstart
* This file is part of WebODF.
*
* WebODF is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License (GNU AGPL)
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* WebODF is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
* @licend
*
* @source: http://www.webodf.org/
* @source: https://github.com/kogmbh/WebODF/
*/
/*global odf, runtime, core, Node*/
/**
* Helper object for generating unique object names. Each name is only reported once per instance,
* irrespective of whether it is actually then inserted into the dom tree in the odfContainer.
*
* There is expected to be a single instance of the object name generator created per session. This is necessary
* to close a potential race condition when generating unique names for operations. As there is no guarantee
* when a given op is executed, it is insufficient to simply rely on all previously generated names to be now present
* in the document definitions. To cope with this, the names generated by this instance are also cached for
* the lifetime of this object.
*
* Failure to do this could result in a situation like the following
* 1. SessionController generates new OpAddStyle & adds to session's queue
* 2. SessionController generates another OpAddStyle & adds to session's queue
*
* At step 2, as the session's queue implementation has no requirement that it immediately executes the operation from
* step 1, it is likely that the style created in step 1 is not yet present in the document DOM.
*
* @param {!odf.OdfContainer} odfContainer
* @param {!string} memberId
* @constructor
*/
odf.ObjectNameGenerator = function ObjectNameGenerator(odfContainer, memberId) {
"use strict";
var stylens = odf.Namespaces.stylens,
drawns = odf.Namespaces.drawns,
xlinkns = odf.Namespaces.xlinkns,
utils = new core.Utils(),
memberIdHash = utils.hashString(memberId),
styleNameGenerator = null,
frameNameGenerator = null,
imageNameGenerator = null,
existingFrameNames = {},
existingImageNames = {};
/**
* @param {string} prefix Prefix to use for unique name generation
* @param {function():!Object.<string,boolean>} findExistingNames
* @constructor
*/
function NameGenerator(prefix, findExistingNames) {
var /**@type{!Object.<string,boolean>}*/
reportedNames = {};
/**
* Generate a unique name
* @return {string}
*/
this.generateName = function () {
var existingNames = findExistingNames(),
startIndex = 0,
name;
do {
name = prefix + startIndex;
startIndex += 1;
} while (reportedNames[name] || existingNames[name]);
reportedNames[name] = true;
return name;
};
}
/**
* Get all the style names defined in the style:style elements of the
* current document including automatic styles.
*
* @return {!Object.<string,boolean>}
*/
function getAllStyleNames() {
var styleElements = [
odfContainer.rootElement.automaticStyles,
odfContainer.rootElement.styles
],
styleNames = {};
/**
* @param {!Element} styleListElement
*/
function getStyleNames(styleListElement) {
var e = styleListElement.firstElementChild;
while (e) {
if (e.namespaceURI === stylens && e.localName === "style") {
styleNames[e.getAttributeNS(stylens, 'name')] = true;
}
e = e.nextElementSibling;
}
}
styleElements.forEach(getStyleNames);
return styleNames;
}
/**
* Generate a unique style name across the style:style elements
* @return {!string}
*/
this.generateStyleName = function () {
if (styleNameGenerator === null) {
styleNameGenerator = new NameGenerator(
"auto" + memberIdHash + "_",
function () {
// TODO: can cache the existing names once we fix the todo in formatting.applyStyle
return getAllStyleNames();
}
);
}
return styleNameGenerator.generateName();
};
/**
* Generate a unique frame name
* @return {!string}
*/
this.generateFrameName = function () {
var i, nodes, node;
if (frameNameGenerator === null) {
nodes = odfContainer.rootElement.body.getElementsByTagNameNS(drawns, 'frame');
for (i = 0; i < nodes.length; i += 1) {
node = /**@type{!Element}*/(nodes.item(i));
existingFrameNames[node.getAttributeNS(drawns, 'name')] = true;
}
frameNameGenerator = new NameGenerator(
"fr" + memberIdHash + "_",
function () {
return existingFrameNames;
}
);
}
return frameNameGenerator.generateName();
};
/**
* Generate a unique image name
* @return {!string}
*/
this.generateImageName = function () {
var i, path, nodes, node;
if (imageNameGenerator === null) {
nodes = odfContainer.rootElement.body.getElementsByTagNameNS(drawns, 'image');
for (i = 0; i < nodes.length; i += 1) {
node = /**@type{!Element}*/(nodes.item(i));
path = node.getAttributeNS(xlinkns, 'href');
path = path.substring("Pictures/".length, path.lastIndexOf('.'));
existingImageNames[path] = true;
}
imageNameGenerator = new NameGenerator(
"img" + memberIdHash + "_",
function () {
return existingImageNames;
}
);
}
return imageNameGenerator.generateName();
};
};