UNPKG

node-webodf

Version:

WebODF - JavaScript Document Engine http://webodf.org/

179 lines (169 loc) 6.72 kB
/** * 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(); }; };