1 /** 2 * Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com> 3 * 4 * @licstart 5 * This file is part of WebODF. 6 * 7 * WebODF is free software: you can redistribute it and/or modify it 8 * under the terms of the GNU Affero General Public License (GNU AGPL) 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * WebODF is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with WebODF. If not, see <http://www.gnu.org/licenses/>. 19 * @licend 20 * 21 * @source: http://www.webodf.org/ 22 * @source: https://github.com/kogmbh/WebODF/ 23 */ 24 25 /*global odf, runtime, core, Node*/ 26 27 /** 28 * Helper object for generating unique object names. Each name is only reported once per instance, 29 * irrespective of whether it is actually then inserted into the dom tree in the odfContainer. 30 * 31 * There is expected to be a single instance of the object name generator created per session. This is necessary 32 * to close a potential race condition when generating unique names for operations. As there is no guarantee 33 * when a given op is executed, it is insufficient to simply rely on all previously generated names to be now present 34 * in the document definitions. To cope with this, the names generated by this instance are also cached for 35 * the lifetime of this object. 36 * 37 * Failure to do this could result in a situation like the following 38 * 1. SessionController generates new OpAddStyle & adds to session's queue 39 * 2. SessionController generates another OpAddStyle & adds to session's queue 40 * 41 * At step 2, as the session's queue implementation has no requirement that it immediately executes the operation from 42 * step 1, it is likely that the style created in step 1 is not yet present in the document DOM. 43 * 44 * @param {!odf.OdfContainer} odfContainer 45 * @param {!string} memberId 46 * @constructor 47 */ 48 odf.ObjectNameGenerator = function ObjectNameGenerator(odfContainer, memberId) { 49 "use strict"; 50 var stylens = odf.Namespaces.stylens, 51 drawns = odf.Namespaces.drawns, 52 xlinkns = odf.Namespaces.xlinkns, 53 domUtils = new core.DomUtils(), 54 utils = new core.Utils(), 55 memberIdHash = utils.hashString(memberId), 56 styleNameGenerator = null, 57 frameNameGenerator = null, 58 imageNameGenerator = null, 59 existingFrameNames = {}, 60 existingImageNames = {}; 61 62 /** 63 * @param {string} prefix Prefix to use for unique name generation 64 * @param {function():!Object.<string,boolean>} findExistingNames 65 * @constructor 66 */ 67 function NameGenerator(prefix, findExistingNames) { 68 var /**@type{!Object.<string,boolean>}*/ 69 reportedNames = {}; 70 /** 71 * Generate a unique name 72 * @return {string} 73 */ 74 this.generateName = function () { 75 var existingNames = findExistingNames(), 76 startIndex = 0, 77 name; 78 do { 79 name = prefix + startIndex; 80 startIndex += 1; 81 } while (reportedNames[name] || existingNames[name]); 82 reportedNames[name] = true; 83 return name; 84 }; 85 } 86 87 /** 88 * Get all the style names defined in the style:style elements of the 89 * current document including automatic styles. 90 * 91 * @return {!Object.<string,boolean>} 92 */ 93 function getAllStyleNames() { 94 var styleElements = [ 95 odfContainer.rootElement.automaticStyles, 96 odfContainer.rootElement.styles 97 ], 98 styleNames = {}; 99 100 /** 101 * @param {!Element} styleListElement 102 */ 103 function getStyleNames(styleListElement) { 104 var e = styleListElement.firstElementChild; 105 while (e) { 106 if (e.namespaceURI === stylens && e.localName === "style") { 107 styleNames[e.getAttributeNS(stylens, 'name')] = true; 108 } 109 e = e.nextElementSibling; 110 } 111 } 112 styleElements.forEach(getStyleNames); 113 return styleNames; 114 } 115 116 /** 117 * Generate a unique style name across the style:style elements 118 * @return {!string} 119 */ 120 this.generateStyleName = function () { 121 if (styleNameGenerator === null) { 122 styleNameGenerator = new NameGenerator( 123 "auto" + memberIdHash + "_", 124 function () { 125 // TODO: can cache the existing names once we fix the todo in formatting.applyStyle 126 return getAllStyleNames(); 127 } 128 ); 129 } 130 return styleNameGenerator.generateName(); 131 }; 132 /** 133 * Generate a unique frame name 134 * @return {!string} 135 */ 136 this.generateFrameName = function () { 137 if (frameNameGenerator === null) { 138 var nodes = domUtils.getElementsByTagNameNS(odfContainer.rootElement.body, drawns, 'frame'); 139 nodes.forEach(function (frame) { 140 existingFrameNames[frame.getAttributeNS(drawns, 'name')] = true; 141 }); 142 143 frameNameGenerator = new NameGenerator( 144 "fr" + memberIdHash + "_", 145 function () { 146 return existingFrameNames; 147 } 148 ); 149 } 150 return frameNameGenerator.generateName(); 151 }; 152 /** 153 * Generate a unique image name 154 * @return {!string} 155 */ 156 this.generateImageName = function () { 157 if (imageNameGenerator === null) { 158 var nodes = domUtils.getElementsByTagNameNS(odfContainer.rootElement.body, drawns, 'image'); 159 nodes.forEach(function (image) { 160 var path = image.getAttributeNS(xlinkns, 'href'); 161 path = path.substring("Pictures/".length, path.lastIndexOf('.')); 162 existingImageNames[path] = true; 163 }); 164 165 imageNameGenerator = new NameGenerator( 166 "img" + memberIdHash + "_", 167 function () { 168 return existingImageNames; 169 } 170 ); 171 } 172 return imageNameGenerator.generateName(); 173 }; 174 }; 175