diff --git a/webodf/tools/odfRng2Config.js b/webodf/tools/odfRng2Config.js index a48bb1ce4..1b2f1d81e 100644 --- a/webodf/tools/odfRng2Config.js +++ b/webodf/tools/odfRng2Config.js @@ -22,28 +22,73 @@ * @source: https://github.com/kogmbh/WebODF/ */ -function aggregate(callback) { - return function (collection, individual) { - return collection.concat(callback(individual)); - }; -} +"use strict"; -function toArray(nodeList) { - "use strict"; - return Array.prototype.slice.call(nodeList); +var rngns = "http://relaxng.org/ns/structure/1.0"; + +/** + * Check if a node is an element in the rng namespace and the given localName. + * @param {!string} localName + * @param {!Node} node + * @return {!boolean} + */ +function isRng(localName, node) { + if (node.nodeType !== 1) { + return false; + } + if (node.namespaceURI !== rngns) { + return false; + } + if (node.localName !== localName) { + return false; + } + return true; } -function getName(node) { - return node && node.getAttribute("name"); +/** + * @param {!Node} node + * @return {?Element} + */ +function getFirstElementNode(node) { + node = node.firstChild; + while (node && node.nodeType !== 1) { + node = node.nextSibling; + } + return node; } +/** + * Return all explicit names for an or element. + * and return nothing. + * + * @param {!Node} node + * @return {!Array.} + */ function getNames(node) { - var name = getName(node); - return name ? [name] : toArray(node.getElementsByTagName("name")).map(function (node) { - return node.textContent; - }); + var names = []; + if (node.hasAttribute("name")) { + names.push(node.getAttribute("name")); + } else { + node = getFirstElementNode(node); + if (isRng("choice", node)) { + node = getFirstElementNode(node); + } + while (node) { + if (isRng("name", node)) { + names.push(node.textContent); + } + node = node.nextSibling; + } + } + return names; } +/** + * Increase length of string by adding spaces. + * @param {!string} str + * @param {!number} length + * @return {!string} + */ function pad(str, length) { while (str.length < length) { str += " "; @@ -52,58 +97,174 @@ function pad(str, length) { } /** - * Extract container node information out of the supplied RNG schema document. - * This only does extremely simplistic parsing. - * - * @constructor - * @param {!Document} document + * Get all the elements from an RNG grammar. + * @param {!Element} grammar + * @return {!Object.} */ -function ExtractContainerInfo(document) { - /** - * @param {!Node} node - * @return {!Array.} - */ - function findParentElements(node) { - var refs; - - while (node && /(define|element)/.test(node.localName) === false) { - node = node.parentNode; +function getDefines(grammar) { + var defines = {}, + c = grammar.firstChild; + while (c) { + if (c.nodeType === 1 && isRng("define", c)) { + defines[c.getAttribute("name")] = c; } + c = c.nextSibling; + } + return defines; +} - if (node) { - if (node.localName === "element") { - return [node]; +/** + * Information about an attribute or element. + * @constructor + */ +function Info() { + /**@type {!Object.}*/ + this.refs = {}; + /**@type {!boolean}*/ + this.text = false; + /**@type {!boolean}*/ + this.data = false; + /**@type {!boolean}*/ + this.value = false; + /**@type {!Array. to that of or . + * @param {!Info} info + * @param {!string} ref + * @param {!Object.} defines + * @return {undefined} + */ +function addDefine(info, ref, defines) { + var define = defines[ref], + c; + if (define) { + info.text = info.text || define.text; + info.data = info.data || define.data; + info.value = info.value || define.value; + define.childElements.forEach(function (ce) { + if (info.childElements.indexOf(ce) === -1) { + info.childElements.push(ce); } - refs = toArray(document.querySelectorAll("ref[name='" + getName(node) + "']")); - return refs.reduce(aggregate(findParentElements), []); - } - return []; + }); + define.attributes.forEach(function (a) { + if (info.attributes.indexOf(a) === -1) { + info.attributes.push(a); + } + }); } +} - this.getTextElements = function() { - return toArray(document.getElementsByTagName("text")).reduce(aggregate(findParentElements), []); - }; +/** + * Add information from elements to the set of or + * elements. + * @param {!Object.} infos + * @param {!Object.} defines + * @return {undefined} + */ +function resolveDefines(infos, defines) { + Object.keys(infos).forEach(function (name) { + var info = infos[name]; + Object.keys(info.refs).forEach(function (ref) { + addDefine(info, ref, defines); + }); + }); +} + +/** + * Recursively collect information from all elements in an RNG grammar. + * If a is encountered, the corresponding is traversed. + * This is done only once for each . + * + * @param {!Element} e + * @param {!Object.