diff --git a/demos/dev.css b/demos/dev.css new file mode 100644 index 0000000..d6cef21 --- /dev/null +++ b/demos/dev.css @@ -0,0 +1,52 @@ +body { + font-family: sans-serif; +} + +table { + border-collapse: collapse; + margin-left: 40px; +} + +td { + border-width: 1px; + border-color: #2e3436; + border-style: solid; + font-size: small; +} + +.hidden { + display: none; +} + +.grandecellule { + font-size: 1.4rem; +} + + +/*************************** + * Styles ODS + * *************************/ + +cell.Header { + font-style: italic; + background-color: #2244aa; +} + +cell.Grande ~ .essai { + font-size: 12pt; + padding: 0.3cm; + border: 0.75pt solid #333; +} + +cell.EncorePlusGrande ~ .grandecellule { + font-size: 18pt; +} + +column.Longue ~ .longuecolonne { + width: 7cm; +} + +row.Entete { + height: 2cm; + background-color: #ff00ff; +} diff --git a/demos/dev.html b/demos/dev.html new file mode 100644 index 0000000..6670389 --- /dev/null +++ b/demos/dev.html @@ -0,0 +1,92 @@ + + + +Exemple pour le développement + + + + + + + + + + + + + + + +

Exemple pour le développement

+

Le contenu du fichier .fods est affiché après la table

+
+

Version ODS

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Colonne 1Colonne 3
Sous-colonne 1-1Sous-colonne 1-2Sous-colonne 1-3
Cellule 1.1Cellule 1.2 + SuiteCellule 1.3 +
+ Caché dans OD +
Test à des lignes vides
Cellule 1.4
Cellule 2.1 (Très grande)Cellule 2.3-160
Cellule 3.1Cellule 3.21000 €-$2500
+
 
+
+ + diff --git a/src/js/currency/Currency.js b/src/js/currency/Currency.js new file mode 100644 index 0000000..e6cce2f --- /dev/null +++ b/src/js/currency/Currency.js @@ -0,0 +1,90 @@ +/* global Intl */ + +/** + * + * @constructor + */ +function Currency (code, symbol, fractionDigit, symbolBefore, formatFunction) { + this.code = code; + this.symbol = symbol; + this.fractionDigit = fractionDigit; + this.symbolBefore = symbolBefore; + var multiplicator = 1; + for(var i = 0; i < fractionDigit; i++) { + multiplicator = multiplicator * 10; + } + this.subunitMultiplicator = multiplicator; + if (formatFunction) { + this.formatFunction = formatFunction; + } +}; + +Currency.prototype.formatAmount = function (moneyLong, locale) { + var decimalValue = moneyLong / (this.subunitMultiplicator); + if (this.formatFunction) { + return this.formatFunction(decimalValue, locale); + } else { + return decimalValue.toLocaleString(locale, { + style: "currency", + currency: this.code + }); + } +}; + +Currency.prototype.toDecimalValue = function (moneyLong) { + return moneyLong / (this.subunitMultiplicator); +}; + +Currency.MAP = {}; + +Currency.formatAmount = function (moneyLong, currencyCode, locale) { + var currency = Currency.get(currencyCode); + return currency.formatAmount(moneyLong, locale); +}; + +Currency.toDecimalValue = function (moneyLong, currencyCode) { + var currency = Currency.get(currencyCode); + return currency.toDecimalValue(moneyLong); +}; + +/** + * + * @param {String} code + * @returns {Currency} + */ +Currency.get = function (code) { + if (Currency.MAP.hasOwnProperty(code)) { + return Currency.MAP[code]; + } + var currency; + var symbolBefore = _isSymbolBefore(code); + if (code === 'CFA') { + currency = new Currency("CFA", "CFA", 0, symbolBefore, function(decimalValue, locale) { + return decimalValue.toLocaleString(locale, { + style: "decimal", + maximumFractionDigits: 0 + }) + " CFA"; + }); + } else { + var numberFormat = new Intl.NumberFormat('en', { style: 'currency', currency: code }); + var options = numberFormat.resolvedOptions(); + var fractionDigit = options.maximumFractionDigits; + var format = numberFormat.format(987); + var idx = format.indexOf(9); + var symbol = format.substring(0, idx); + currency = new Currency(code, symbol, fractionDigit, symbolBefore); + } + Currency.MAP[code] = currency; + return currency; + + function _isSymbolBefore (code) { + switch(code) { + case "USD": + case "GBP": + return true; + default: + return false; + } + } +}; + diff --git a/src/js/opendocument/OpenDocument.CellCounter.js b/src/js/opendocument/OpenDocument.CellCounter.js new file mode 100644 index 0000000..3e23909 --- /dev/null +++ b/src/js/opendocument/OpenDocument.CellCounter.js @@ -0,0 +1,57 @@ +/* global OpenDocument,Currency,XmlWriter */ + +OpenDocument.CellCounter = function () { + this.row = 0; + this.column = 0; + this.rowMap = new Map(); +}; + +OpenDocument.CellCounter.prototype.reinit = function () { + this.row = 0; + this.column = 0; + this.rowMap.clear(); +}; + +OpenDocument.CellCounter.prototype.newRow = function () { + this.row = this.row + 1; + this.column = 1; +}; + +OpenDocument.CellCounter.prototype.newCell = function (rowSpan, colSpan) { + let currentRow = this.row; + let currentColumn = this.column; + let jump = 0; + let coveredCellArray = this.rowMap.get(currentRow); + if (coveredCellArray) { + while(true) { + if (coveredCellArray.indexOf(currentColumn) !== -1) { + currentColumn++; + jump++; + } else { + break; + } + } + } + if (rowSpan > 1) { + for(let i = 1; i < rowSpan; i++) { + for(let j = 0; j < colSpan; j++) { + this.putCoveredCell(currentRow + i, currentColumn + j); + } + } + } + if (colSpan > 1) { + currentColumn += (colSpan - 1); + } + this.column = currentColumn + 1; + return jump; +}; + +OpenDocument.CellCounter.prototype.putCoveredCell = function (row, column) { + if (this.rowMap.has(row)) { + this.rowMap.get(row).push(column); + } else { + let array = new Array(); + array.push(column); + this.rowMap.set(row, array); + } +}; diff --git a/src/js/opendocument/OpenDocument.Elements.js b/src/js/opendocument/OpenDocument.Elements.js new file mode 100644 index 0000000..3042f0f --- /dev/null +++ b/src/js/opendocument/OpenDocument.Elements.js @@ -0,0 +1,29 @@ +/* global OpenDocument */ + +OpenDocument.Elements = {}; + +OpenDocument.Elements.Cell = function (value, styleName, rowSpan, colSpan) { + this.value = value; + this.styleName = styleName; + this.rowSpan = rowSpan; + this.colSpan = colSpan; +}; + +/*********************************************************** + * + * @constructor + * @param {String} styleName + * @param {Number} columnsRepeated + * @param {String} defaultCellStyleName + * @returns {OpenDocument.TableColumn} + */ +OpenDocument.Elements.TableColumn = function (styleName, columnsRepeated, defaultCellStyleName) { + this.styleName = styleName; + this.columnsRepeated = columnsRepeated; + if (defaultCellStyleName) { + this.defaultCellStyleName = defaultCellStyleName; + } else { + this.defaultCellStyleName = "Default"; + } +}; + diff --git a/src/js/opendocument/OpenDocument.OdsConverter.js b/src/js/opendocument/OpenDocument.OdsConverter.js new file mode 100644 index 0000000..8788e2c --- /dev/null +++ b/src/js/opendocument/OpenDocument.OdsConverter.js @@ -0,0 +1,328 @@ +/* global Currency,OpenDocument,Node */ + +/** + * + * @constructor + * @param {OpenDocument.XmlWriter} xmlWriter + */ +OpenDocument.OdsConverter = function (xmlWriter) { + this.xmlWriter = xmlWriter; + this.sheetName = ""; + this.nameMap = {}; + this.flatOds = true; + this.styleManager = new OpenDocument.StyleManager(); + this.cellStyleNumber = 1; + this.currencyMap = {}; + this.fixedRows = 0; + this.fixedColumns = 0; +}; + +OpenDocument.OdsConverter.convertToBlob = function (table, options) { + var xml = OpenDocument.OdsConverter.convertToXml(table, options); + return new Blob([xml], {type: OpenDocument.SPREADSHEET_MIMETYPE }); +}; + +OpenDocument.OdsConverter.convertToXml = function (table, options) { + var xmlWriter = new OpenDocument.XmlWriter({prettyXml: true}); + var odsConverter = new OpenDocument.OdsConverter(xmlWriter); + odsConverter.convert(table, options); + return xmlWriter.xml; +}; + +OpenDocument.OdsConverter.prototype.convert = function (table, options) { + if (typeof table === "string") { + table = document.getElementById(table); + } + var converter = this; + var styleManager = this.styleManager; + var _getRowStyleName = OpenDocument.OdsConverter.default_getRowStyleName; + var _getCellStyleName = OpenDocument.OdsConverter.default_getCellStyleName; + var textDataAttribute = "text"; + var defaultCurrencyCode = ""; + _checkArguments(); + var columnArray = OpenDocument.OdsConverter.readTableColumns(table, styleManager); + var xw = new OpenDocument.XmlWriter({indentLength: 3}); + xw + .openTable(converter.sheetName); + for(let tableColumn of columnArray) { + xw + .addTableColumn(tableColumn); + } + for(let row of table.rows) { + xw + .openTableRow(_getRowStyleName(row, styleManager)); + for(let cell of row.cells) { + _addCell(cell); + } + xw + .closeTableRow(); + } + xw + .closeTable(xw); + this.ods(xw.xml); + + function _checkArguments() { + if (options) { + if (options.rowStyleName) { + _getRowStyleName = options.rowStyleName; + } + if (options.cellStyleName) { + _getCellStyleName = options.cellStyleName; + } + if (options.textDataAttribute) { + textDataAttribute = options.textDataAttribute; + } + } + defaultCurrencyCode = table.dataset["odCurrency"]; + let sheetName = table.dataset["odSheetname"]; + if (sheetName) { + converter.sheetName = sheetName; + } + let fixedRows = table.dataset["odFixedRows"]; + if (fixedRows) { + converter.fixedRows = fixedRows; + } + let fixedColumns = table.dataset["odFixedColumns"]; + if (fixedColumns) { + converter.fixedColumns = fixedColumns; + } + } + + function _addCell(cell) { + let text = _getCellText(cell); + let odCell = new OpenDocument.Elements.Cell(text, _getCellStyleName(cell, styleManager), cell.rowSpan, cell.colSpan); + if (!text) { + xw + .addEmptyTableCell(odCell); + return; + } + let type = cell.dataset["odType"]; + switch(type) { + case "number": + xw + .addNumberTableCell(odCell); + break; + case "date": + odCell.styleName = styleManager.getAutomaticCellStyleName("date", odCell.styleName); + xw + .addDateTableCell(odCell); + break; + case "currency": + let currencyCode = cell.dataset["odCurrency"]; + if (!currencyCode) { + currencyCode = defaultCurrencyCode; + } + odCell.styleName = styleManager.getAutomaticCellStyleName("currency", odCell.styleName, currencyCode); + xw + .addCurrencyTableCell(odCell, currencyCode); + break; + default: + xw + .addStringTableCell(odCell); + } + } + + function _getCellText(cellElement) { + let cellText = cellElement.dataset[textDataAttribute]; + if (cellText) { + return cellText; + } + cellText = ""; + let previousBlock = false; + for(let node of cellElement.childNodes) { + if (node.nodeType === Node.ELEMENT_NODE) { + let display = window.getComputedStyle(node).display; + let odHidden = node.dataset["odHidden"]; + switch(display) { + case "none": + break; + case "block": + if (!odHidden) { + __addText(node.innerText, true); + } + previousBlock = true; + break; + default: + if (!odHidden) { + __addText(node.innerText, false); + } + } + } else { + if (node.textContent) { + let text = node.textContent; + text = text.replace(/[\s\uFEFF\xA0]/g, ' '); + __addText(text, false); + } + } + } + return cellText.trim().replace(/ +/g, ' '); + + function __addText(text, isBlock) { + if (!text) { + return false; + } + if ((previousBlock) || (isBlock)) { + cellText = cellText.trimEnd() + "\n"; + previousBlock = false; + text = text.trimStart(); + } + cellText += text; + return true; + } + } + +}; + +OpenDocument.OdsConverter.prototype.ods = function (tableXml) { + var xw = this.xmlWriter; + xw.appendXMLDeclaration(); + if (this.flatOds) { + xw + .openDocument(OpenDocument.SPREADSHEET_MIMETYPE); + } else { + xw + .openDocumentContent(); + } + if ((this.fixedColumns > 0) || (this.fixedRows > 0)) { + xw + .openSettings() + .openConfigItemSet("ooo:view-settings") + .openConfigItemMapIndexed("Views") + .openConfigItemMapEntry( "") + .addConfigItem("ViewId", "string", "view1") + .openConfigItemMapNamed("Tables") + .openConfigItemMapEntry(this.sheetName); + if (this.fixedColumns > 0) { + xw + .addConfigItem("HorizontalSplitMode", "short", "2") + .addConfigItem("HorizontalSplitPosition", "int", this.fixedColumns) + .addConfigItem("PositionLeft", "int", "0") + .addConfigItem("PositionRight", "int", this.fixedColumns); + } + if (this.fixedRows > 0) { + xw + .addConfigItem("VerticalSplitMode", "short", "2") + .addConfigItem("VerticalSplitPosition", "int", this.fixedRows) + .addConfigItem("PositionTop", "int", "0") + .addConfigItem("PositionBottom", "int", this.fixedRows); + } + xw + .closeConfigItemMapEntry() + .closeConfigItemMapNamed() + .closeConfigItemMapEntry() + .closeConfigItemMapIndexed() + .closeConfigItemSet() + .closeSettings(); + } + xw + .openStyles() + this.styleManager.writeStyles("cell-named", xw); + xw + .closeStyles(); + xw + .openAutomaticStyles(); + this.styleManager.writeStyles("row-named", xw); + this.styleManager.writeStyles("column-automatic", xw); + this.styleManager.writeDataStyles(xw); + this.styleManager.writeStyles("cell-automatic", xw); + xw + .closeAutomaticStyles() + .openBody() + .openSpreadsheet(xw) + .write(tableXml) + .closeSpreadsheet() + .closeBody(); + if (this.flatOds) { + xw + .closeDocument(); + } else { + xw + .closeDocumentContent(); + } +}; + +OpenDocument.OdsConverter.prototype.checkSheetName = function (name) { + if (!name) { + name = "sheet"; + } + name = OpenDocument.checkSheetName(name); + if (!this.nameMap.hasOwnProperty(name)) { + this.nameMap[name] = true; + return name; + } else { + var p = 2; + while (true) { + var newName = name + " (" + p + ")"; + if (!this.nameMap.hasOwnProperty(newName)) { + this.nameMap[newName] = true; + return newName; + } + p++; + } + } +}; + +OpenDocument.OdsConverter.readTableColumns = function(table, styleManager) { + let colElementArray = new Array(); + let result = new Array(); + let colgroupList = table.getElementsByTagName("colgroup"); + for(let colgroup of colgroupList) { + let colList = colgroup.getElementsByTagName("col"); + if (colList.length > 0) { + for(let col of colList) { + colElementArray.push(col); + } + } else { + colElementArray.push(colgroup); + } + } + let columnNumber = 1; + for(let col of colElementArray) { + let columnName = OpenDocument.COLUMNSTYLE_PREFIX + columnNumber; + columnNumber++; + let columnStyle = new OpenDocument.Style("column", columnName); + let originalStyleName = col.dataset["odStyle"]; + if (originalStyleName) { + let originalStyle = styleManager.getStyle("column-named", originalStyleName); + if (originalStyle) { + columnStyle.copyProperties(originalStyle); + } + } + let customWidth = col.dataset["odWidth"]; + if (customWidth) { + columnStyle.putStyleProperty(OpenDocument.Style.STYLEPROPERTYDEFS["column-width"], customWidth); + } + styleManager.putStyle("column-automatic", columnStyle); + result.push(new OpenDocument.Elements.TableColumn(columnName, col.span, "Standard")); + } + return result; +}; + + +OpenDocument.OdsConverter.default_getRowStyleName = function (row, styleManager) { + let styleName = row.dataset["odStyle"]; + if (styleName) { + return styleName; + } + styleName = styleManager.getMatchingStyleName("row", row); + if (styleName) { + return styleName; + } + return ""; +}; + +OpenDocument.OdsConverter.default_getCellStyleName = function (cell, styleManager) { + let styleName = cell.dataset["odStyle"]; + if (styleName) { + return styleName; + } + styleName = styleManager.getMatchingStyleName("cell", cell); + if (styleName) { + return styleName; + } + if (cell.tagName.toLowerCase() === "th") { + return "Header"; + } else { + return "Standard"; + } +}; diff --git a/src/js/opendocument/OpenDocument.Style.js b/src/js/opendocument/OpenDocument.Style.js new file mode 100644 index 0000000..5e8285c --- /dev/null +++ b/src/js/opendocument/OpenDocument.Style.js @@ -0,0 +1,255 @@ +/* global OpenDocument */ + +/*********************************************************** + * + * @constructor + * @param {String} type + * @param {String} styleName + * @returns {OpenDocument.Style} + */ +OpenDocument.Style = function (type, styleName) { + this.type = type; + this.styleName = styleName; + this.styleFamily = OpenDocument.Style.getMatchingStyleFamily(type); + this.parentStyleName = ""; + this.dataStyleName = ""; + this.propertiesMaps = new Map([ + ["paragraph", new Map()], + ["text", new Map()], + ["table-cell", new Map()], + ["table-row", new Map()], + ["table-column", new Map()] + ]); +}; + +OpenDocument.Style.prototype.putStyleProperty = function (stylePropertyDef, value) { + var propertyName = stylePropertyDef.name; + switch(stylePropertyDef.format) { + case "color": + value = OpenDocument.Style.formatColor(value); + break; + } + var property = { + name: propertyName, + value: value + }; + this.propertiesMaps.get(stylePropertyDef.category).set(propertyName, property); +}; + +OpenDocument.Style.prototype.setParent = function (parentStyleName) { + this.parentStyleName = parentStyleName; +}; + +OpenDocument.Style.prototype.setDataStyle = function (dataStyleName) { + this.dataStyleName = dataStyleName; +}; + +OpenDocument.Style.prototype.isEmpty = function () { + for(let map of this.propertiesMaps.values()) { + if (map.size > 0) { + return false; + } + } + return true; +}; + +OpenDocument.Style.prototype.copyProperties = function (otherStyle) { + if (otherStyle.parentStyleName) { + this.setParent(otherStyle.parentStyleName); + } + for(let entry of otherStyle.propertiesMaps) { + let otherMap = entry[1]; + let thisMap = this.propertiesMaps.get(entry[0]); + for(let entry2 of otherMap) { + thisMap.set(entry2[0], entry2[1]); + } + } +}; + +/** + * + * @param {OpenDocument.XmlWriter} xmlWriter + * @returns {undefined} + */ +OpenDocument.Style.prototype.write = function (xmlWriter) { + xmlWriter + .startStyleOpenTag(this.styleName, this.styleFamily, this.parentStyleName) + .addAttribute("style:data-style-name", this.dataStyleName); + if (this.isEmpty()) { + xmlWriter + .closeEmptyTag(); + } else { + xmlWriter + .endOpenTag(); + for(let entry of this.propertiesMaps) { + let category = entry[0]; + let map = entry[1]; + if (map.size > 0) { + xmlWriter + .startOpenTag("style:" + category + "-properties"); + for(let property of map.values()) { + xmlWriter + .addAttribute(property.name, property.value); + } + xmlWriter + .closeEmptyTag(); + } + } + xmlWriter + .closeStyle(); + } +}; + +OpenDocument.Style.getStylePropertyDef = function (type, name) { + name = _checkAlias(); + if (OpenDocument.Style.STYLEPROPERTYDEFS.hasOwnProperty(name)) { + let propertyDef = OpenDocument.Style.STYLEPROPERTYDEFS[name]; + if (propertyDef.categories) { + for(let propKey in propertyDef.categories) { + if (propKey === type) { + return Object.assign({}, propertyDef, {category: propertyDef.categories[propKey]}); + } + } + return null; + } else { + return propertyDef; + } + } else { + return null; + } + + function _checkAlias() { + switch(type) { + case "row": + switch(name) { + case "height": + return "row-height"; + } + break; + case "column": + switch(name) { + case "width": + return "column-width"; + } + break; + } + return name; + } + +}; + +OpenDocument.Style.getMatchingStyleFamily = function (type) { + switch(type) { + case "cell": + return "table-cell"; + case "row": + return "table-row"; + case "column": + return "table-column"; + } +}; + +OpenDocument.Style.STYLEPROPERTYDEFS = { + "background-color": { + name: "fo:background-color", + categories: { + "cell": "table-cell", + "row": "table-row" + }, + format: "color" + }, + "border": { + name: "fo:border", + category: "table-cell" + }, + "border-bottom": { + name: "fo:border-bottom", + category: "table-cell" + }, + "border-left": { + name: "fo:border-left", + category: "table-cell" + }, + "border-right": { + name: "fo:border-right", + category: "table-cell" + }, + "border-top": { + name: "fo:border-top", + category: "table-cell" + }, + "color": { + name: "fo:color", + category: "text", + format: "color" + }, + "column-width": { + name: "style:column-width", + category: "table-column" + }, + "font-size": { + name: "fo:font-size", + category: "text" + }, + "font-style": { + name: "fo:font-style", + category: "text" + }, + "font-weight": { + name: "fo:font-weight", + category: "text" + }, + "padding": { + name: "fo:padding", + category: "table-cell" + }, + "padding-bottom": { + name: "fo:padding-bottom", + category: "table-cell" + }, + "padding-left": { + name: "fo:padding-left", + category: "table-cell" + }, + "padding-right": { + name: "fo:padding-right", + category: "table-cell" + }, + "padding-top": { + name: "fo:padding-top", + category: "table-cell" + }, + "row-height": { + name: "style:row-height", + category: "table-row" + }, + "text-align": { + name: "fo:text-align", + category: "paragraph" + }, + "use-optimal-row-height": { + name: "style:use-optimal-row-height", + category: "table-row" + }, + "vertical-align": { + name: "style:vertical-align", + category: "table-cell" + } +}; + + +OpenDocument.Style.formatColor = function (color) { + let colorList = color.match(/rgb\((.*)\)/); + if (colorList) { + let hexresult = "#"; + for(let token of colorList[1].split(",")) { + let hex = Number.parseInt(token.trim(), 10).toString(16); + if (hex.length === 1) { + hex = "0" + hex; + } + hexresult += hex; + } + color = hexresult; + } + return color; +}; diff --git a/src/js/opendocument/OpenDocument.StyleManager.js b/src/js/opendocument/OpenDocument.StyleManager.js new file mode 100644 index 0000000..4eaf001 --- /dev/null +++ b/src/js/opendocument/OpenDocument.StyleManager.js @@ -0,0 +1,399 @@ +/* global OpenDocument */ + +OpenDocument.StyleManager = function () { + this.maps = new Map([ + ["cell-named", new Map()], + ["row-named", new Map()], + ["cell-automatic", new Map()], + ["column-named", new Map()], + ["column-automatic", new Map()] + ]); + this.matchingClassMaps = new Map([ + ["cell", new Map()], + ["row", new Map()], + ["column", new Map()] + ]); + for(let object of OpenDocument.StyleManager.DEFAULT_STYLES ) { + this.putStyle(object.type + "-named", _buildDefault(object)); + } + this.automaticStyleNumber = 1; + this.currencyMap = new Map(); + OpenDocument.StyleManager.readDocumentStyleSheets(this); + + function _buildDefault(object) { + let style = new OpenDocument.Style(object.type, object.name); + if (object.parent) { + style.setParent(object.parent); + } + for(let propertyName in object.properties) { + let stylePropertyDef = OpenDocument.Style.getStylePropertyDef(object.type, propertyName); + if (stylePropertyDef) { + style.putStyleProperty(stylePropertyDef, object.properties[propertyName]); + } + } + return style; + } +}; + +OpenDocument.StyleManager.prototype.putStyle = function (mapName, style, styleKey) { + if (!styleKey) { + styleKey = style.styleName; + } + var map = this.maps.get(mapName); + if (map.has(styleKey)) { + let existingStyle = map.get(styleKey); + existingStyle.copyProperties(style); + } else { + map.set(styleKey, style); + } +}; + +OpenDocument.StyleManager.prototype.hasStyle = function (mapName, styleKey) { + var map = this.maps.get(mapName); + if (map) { + return map.has(styleKey); + } else { + return false; + } +}; + + +OpenDocument.StyleManager.prototype.getStyle = function (mapName, styleKey) { + var map = this.maps.get(mapName); + if (map) { + return map.get(styleKey); + } else { + return null; + } + return this.map.get(styleKey); +}; + +OpenDocument.StyleManager.prototype.getAutomaticCellStyleName = function (type, parentStyleName, currencyCode) { + var styleKey = type + ":"; + if (type === "currency") { + styleKey += currencyCode + ":"; + } + styleKey += parentStyleName; + if (this.hasStyle("cell-automatic", styleKey)) { + return this.getStyle("cell-automatic", styleKey).name; + } + var name = OpenDocument.CELLSTYLE_PREFIX + this.automaticStyleNumber; + this.automaticStyleNumber++; + var dataStyleName = null; + switch(type) { + case "date": + dataStyleName = OpenDocument.ISODATE_DATASTYLE_NAME; + break; + case "currency": + let number; + if (this.currencyMap.has(currencyCode)) { + number = this.currencyMap.get(currencyCode); + } else { + number = this.automaticStyleNumber; + this.automaticStyleNumber++; + this.currencyMap.set(currencyCode, number); + } + dataStyleName = OpenDocument.DATASTYLE_PREFIX + number; + break; + } + var cellStyle = new OpenDocument.Style("cell", name); + if (parentStyleName) { + cellStyle.setParent(parentStyleName); + } else { + cellStyle.setParent(OpenDocument.DEFAULT_CELLSTYLE_NAME); + } + if (dataStyleName) { + cellStyle.setDataStyle(dataStyleName); + } + this.putStyle("cell-automatic", cellStyle, styleKey); + return name; +}; + +OpenDocument.StyleManager.prototype.getMatchingStyleName = function (type, element) { + var map = this.matchingClassMaps.get(type); + if (map) { + for(let className of element.classList) { + if (map.has(className)) { + return map.get(className); + } + } + } + return ""; +}; +/** + * + * @param {OpenDocument.XmlWriter} xmlWriter + * @returns {undefined} + */ +OpenDocument.StyleManager.prototype.writeStyles = function (mapName, xmlWriter) { + var map = this.maps.get(mapName); + if (map) { + for(let style of map.values()) { + style.write(xmlWriter); + } + } +}; + +OpenDocument.StyleManager.prototype.writeDataStyles = function (xmlWriter) { + xmlWriter + .addDateStyle(); + for(let entry of this.currencyMap) { + xmlWriter + .addCurrencyStyle(entry[0], entry[1]); + } +}; + +OpenDocument.StyleManager.readDocumentStyleSheets = function (styleManager) { + var styleSheetList = document.styleSheets; + for(let i = 0, len = styleSheetList.length; i < len; i++) { + OpenDocument.StyleManager.readStyleSheet(styleManager, styleSheetList[i]); + } +}; + +OpenDocument.StyleManager.readStyleSheet = function (styleManager, styleSheet) { + var ruleList = styleSheet.cssRules; + for(let i = 0, len = ruleList.length; i < len; i++) { + let rule = ruleList[i]; + if ((rule.selectorText) && (rule.style)) { + let selectorArray = _parseSelectorText(rule.selectorText); + for(let selector of selectorArray) { + _addStyle(selector, rule.style); + } + + } + } + + function _parseSelectorText(selectorText) { + let resultArray = new Array(); + for(let token of selectorText.split(',')) { + let result = __parseToken(token); + if (result) { + switch(result[0]) { + case "cell": + case "row": + case "column": + resultArray.push(result); + break; + } + } + } + return resultArray; + + + function __parseToken(token) { + token = token.trim(); + let idx = token.indexOf(' '); + let followingPart = ""; + if (idx !== -1) { + followingPart = token.substring(idx +1).trim(); + token = token.substring(0, idx); + } + let dotIndex = token.indexOf('.'); + if (dotIndex === -1) { + return null; + } + let type = token.substring(0, dotIndex); + let name = token.substring(dotIndex + 1); + return [type, name, followingPart]; + } + } + + function _addStyle(selector, cssStyleDeclaration) { + let type = selector[0]; + let style = new OpenDocument.Style(type, selector[1]); + let borderBuffer = false; + if (type === "cell") { + borderBuffer = new OpenDocument.StyleManager.BorderBuffer(); + } + for(let i = 0, len = cssStyleDeclaration.length; i < len; i++) { + let propertyName = cssStyleDeclaration[i]; + let propertyValue = cssStyleDeclaration.getPropertyValue(propertyName); + if (borderBuffer) { + let borderTest = propertyName.match(/^border-([a-z]+)-([a-z]+)/); + if (borderTest) { + borderBuffer.putSubproperty(borderTest[1], borderTest[2], propertyValue); + continue; + } + } + if (propertyName === "content") { + __parseDeclarationText(propertyValue); + } else { + let stylePropertyDef = OpenDocument.Style.getStylePropertyDef(type, propertyName); + if (stylePropertyDef) { + style.putStyleProperty(stylePropertyDef, propertyValue); + } + } + } + if (borderBuffer) { + borderBuffer.fillStyle(style); + } + _parseFollowingPart(selector[2]); + styleManager.putStyle(type + "-named", style); + + function __parseDeclarationText(declarationText) { + let start = declarationText.indexOf('"'); + if (start === -1) { + return; + } + let end = declarationText.lastIndexOf('"'); + if (end === start) { + return; + } + let tokens = declarationText.substring(start + 1, end).split(';'); + for(let token of tokens) { + let paire = token.split(':'); + if (paire.length === 2) { + let name = paire[0].trim(); + let value = paire[1].trim(); + let stylePropertyDef = OpenDocument.Style.getStylePropertyDef(type, name); + if (stylePropertyDef) { + style.putStyleProperty(stylePropertyDef, value); + } + } + } + } + + function _parseFollowingPart(followingPart) { + if (!followingPart) { + return; + } + let tokens = new Array(); + for(let token of followingPart.split(" ")) { + token = token.trim(); + if (token.length > 0) { + tokens.push(token); + } + } + let index = 0; + let length = tokens.length - 1; + while(index < length) { + let operatorToken = tokens[index]; + switch(operatorToken) { + case '~': + __addMatchingClass(_parseClass(tokens[index +1])); + index ++; + break; + case '+': + __setParent(_parseClass(tokens[index +1])); + index ++; + break; + } + index ++; + } + } + + function __addMatchingClass(matchingClass) { + if (!matchingClass) { + return; + } + let map = styleManager.matchingClassMaps.get(type); + if (map) { + map.set(matchingClass, style.styleName); + } + } + + function __setParent(parentStyleName) { + if (!parentStyleName) { + return; + } + style.setParent(parentStyleName); + } + + } + + function _parseClass(classToken) { + if (!classToken.startsWith('.')) { + return false; + } + return classToken.substring(1); + } + +}; + +OpenDocument.StyleManager.BorderBuffer = function () { + this.map = new Map([ + ["bottom", false], + ["left", false], + ["right", false], + ["top", false] + ]); +}; + +OpenDocument.StyleManager.BorderBuffer.prototype.putSubproperty = function (position, subproperty, value) { + if (!this.map.has(position)) { + return; + } + let positionObject = this.map.get(position); + if (!positionObject) { + positionObject = { + width: "0.75pt", + color: "rgb(0, 0, 0)", + style: "solid" + }; + this.map.set(position, positionObject); + } + if (positionObject.hasOwnProperty(subproperty)) { + if (subproperty === "color") { + value = OpenDocument.Style.formatColor(value); + } + positionObject[subproperty] = value; + }; +}; + +OpenDocument.StyleManager.BorderBuffer.prototype.fillStyle = function (style) { + for(let entry of this.map) { + let position = entry[0]; + let object = entry[1]; + if (object) { + let stylePropertyDef = OpenDocument.Style.getStylePropertyDef("type", "border-" + position); + if (stylePropertyDef) { + let value = object.width + " " + object.style + " " + object.color; + style.putStyleProperty(stylePropertyDef, value); + } + } + } +}; + +OpenDocument.StyleManager.DEFAULT_STYLES = [ + { + type: "cell", + name: "Standard", + parent: "", + properties: { + } + }, + { + type: "cell", + name: "Bold", + parent: "Standard", + properties: { + "font-weight": "bold" + } + }, + { + type: "cell", + name: "Italic", + parent: "Standard", + properties: { + "font-weight": "italic" + } + }, + { + type: "cell", + name: "BoldItalic", + parent: "Standard", + properties: { + "font-weight": "bold", + "font-style": "italic" + } + }, + { + type: "cell", + name: "Header", + properties: { + "font-weight": "bold", + "text-align": "center", + "vertical-align": "middle" + } + } +]; diff --git a/src/js/opendocument/OpenDocument.XmlWriter.js b/src/js/opendocument/OpenDocument.XmlWriter.js new file mode 100644 index 0000000..cebddd4 --- /dev/null +++ b/src/js/opendocument/OpenDocument.XmlWriter.js @@ -0,0 +1,568 @@ +/* global OpenDocument,Currency,XmlWriter */ + +/** + * + * @constructor + * @param {type} options + */ +OpenDocument.XmlWriter = function (options) { + XmlWriter.call(this, options); + this.cellCounter = new OpenDocument.CellCounter(); +}; + +OpenDocument.XmlWriter.prototype = Object.create(XmlWriter.prototype); + +OpenDocument.XmlWriter.prototype.constructor = OpenDocument.XmlWriter; + +OpenDocument.XmlWriter.prototype.openDocument = function (mimetype) { + this + .startOpenTag("office:document") + .addXmlnsAttributes() + .addAttribute("office:mimetype", mimetype) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeDocument = function () { + this + .closeTag("office:document"); + return this; +}; + + +OpenDocument.XmlWriter.prototype.openDocumentContent = function () { + this + .startOpenTag("office:document-content") + .addXmlnsAttributes() + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.addXmlnsAttributes = function () { + this + .addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0") + .addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0") + .addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0") + .addAttribute("xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0") + .addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0") + .addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0") + .addAttribute("xmlns:config", "urn:oasis:names:tc:opendocument:xmlns:config:1.0") + .addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink") + .addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/") + .addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0") + .addAttribute("xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0") + .addAttribute("xmlns:presentation", "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0") + .addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0") + .addAttribute("xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0") + .addAttribute("xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0") + .addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML") + .addAttribute("xmlns:form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0") + .addAttribute("xmlns:script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0") + .addAttribute("xmlns:ooo", "http://openoffice.org/2004/office") + .addAttribute("xmlns:ooow", "http://openoffice.org/2004/writer") + .addAttribute("xmlns:oooc", "http://openoffice.org/2004/calc") + .addAttribute("xmlns:dom", "http://www.w3.org/2001/xml-events") + .addAttribute("xmlns:xforms", "http://www.w3.org/2002/xforms") + .addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema") + .addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") + .addAttribute("xmlns:rpt", "http://openoffice.org/2005/report") + .addAttribute("xmlns:of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2") + .addAttribute("xmlns:xhtml", "http://www.w3.org/1999/xhtml") + .addAttribute("xmlns:grddl", "http://www.w3.org/2003/g/data-view#") + .addAttribute("xmlns:tableooo", "http://openoffice.org/2009/table") + .addAttribute("xmlns:vdrawooo", "http://openoffice.org/2010/draw") + .addAttribute("xmlns:calcext", "urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0") + .addAttribute("xmlns:loext", "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0") + .addAttribute("xmlns:field", "urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0") + .addAttribute("xmlns:formx", "urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0") + .addAttribute("xmlns:css3t", "http://www.w3.org/TR/css3-text/") + .addAttribute("office:version", "1.2"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openDocumentContentVersion1 = function () { + this + .startOpenTag("office:document-content") + .addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0") + .addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0") + .addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0") + .addAttribute("xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0") + .addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0") + .addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0") + .addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink") + .addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/") + .addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0") + .addAttribute("xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0") + .addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0") + .addAttribute("xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0") + .addAttribute("xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0") + .addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML") + .addAttribute("xmlns:form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0") + .addAttribute("xmlns:script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0") + .addAttribute("xmlns:ooo", "http://openoffice.org/2004/office") + .addAttribute("xmlns:ooow", "http://openoffice.org/2004/writer") + .addAttribute("xmlns:oooc", "http://openoffice.org/2004/calc") + .addAttribute("xmlns:dom", "http://www.w3.org/2001/xml-events") + .addAttribute("xmlns:xforms", "http://www.w3.org/2002/xforms") + .addAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema") + .addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") + .addAttribute("office:version", "1.0") + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeDocumentContent = function () { + this + .closeTag("office:document-content"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openStyles = function () { + this + .openTag("office:styles"); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeStyles = function () { + this + .closeTag("office:styles"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openAutomaticStyles = function () { + this + .openTag("office:automatic-styles"); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeAutomaticStyles = function () { + this + .closeTag("office:automatic-styles"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openBody = function () { + this + .openTag("office:body"); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeBody = function () { + this + .closeTag("office:body"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openSpreadsheet = function () { + this + .openTag("office:spreadsheet"); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeSpreadsheet = function () { + this + .closeTag("office:spreadsheet"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openStyle = function (styleName, styleFamily, parentStyleName) { + this + .startStyleOpenTag(styleName, styleFamily, parentStyleName) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.startStyleOpenTag = function (styleName, styleFamily, parentStyleName) { + this + .startOpenTag("style:style") + .addAttribute("style:name", styleName); + if (styleName.indexOf("_20_") > -1) { + let displayName = styleName.replace("_20_", " "); + this + .addAttribute("style:display-name", displayName); + } + this + .addAttribute("style:family", styleFamily) + .addAttribute("style:parent-style-name", parentStyleName); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeStyle = function () { + this + .closeTag("style:style"); + return this; +}; + +OpenDocument.XmlWriter.prototype.addDateStyle = function () { + this + .startOpenTag("number:date-style") + .addAttribute("style:name", OpenDocument.ISODATE_DATASTYLE_NAME) + .endOpenTag() + .startOpenTag("number:year") + .addAttribute("number:style", "long") + .closeEmptyTag() + .addSimpleElement("number:text", "-") + .startOpenTag("number:month") + .addAttribute("number:style", "long") + .closeEmptyTag() + .addSimpleElement("number:text", "-") + .startOpenTag("number:day") + .addAttribute("number:style", "long") + .closeEmptyTag() + .closeTag("number:date-style"); + return this; +}; + +OpenDocument.XmlWriter.prototype.addCurrencyStyle = function (currencyCode, number) { + var currency = Currency.get(currencyCode); + var dataStyleName = OpenDocument.DATASTYLE_PREFIX + number; + this + .startOpenTag("number:currency-style") + .addAttribute("style:name", dataStyleName + "P0") + .addAttribute("style:volatile", "true") + .endOpenTag(); + if (currency.symbolBefore) { + this + .addSimpleElement("number:currency-symbol", currency.symbol); + } + this + .startOpenTag("number:number") + .addAttribute("number:decimal-places", currency.fractionDigits) + .addAttribute("number:min-integer-digits", "1") + .addAttribute("number:grouping", "true") + .closeEmptyTag(); + if (!currency.symbolBefore) { + this + .addSimpleElement("number:text", " ") + .addSimpleElement("number:currency-symbol", currency.symbol); + } + this + .closeTag("number:currency-style") + .startOpenTag("number:currency-style") + .addAttribute("style:name", dataStyleName) + .endOpenTag() + .startOpenTag("style:text-properties") + .addAttribute("fo:color", "#ff0000") + .closeEmptyTag() + .addSimpleElement("number:text", "-"); + if (currency.symbolBefore) { + this + .addSimpleElement("number:currency-symbol", currency.symbol); + } + this + .startOpenTag("number:number") + .addAttribute("number:decimal-places", currency.fractionDigits) + .addAttribute("number:min-integer-digits", "1") + .addAttribute("number:grouping", "true") + .closeEmptyTag(); + if (!currency.symbolBefore) { + this + .addSimpleElement("number:text", " ") + .addSimpleElement("number:currency-symbol", currency.symbol); + } + this + .startOpenTag("style:map") + .addAttribute("style:condition", "value()>=0") + .addAttribute("style:apply-style-name", dataStyleName + "P0") + .closeEmptyTag() + .closeTag("number:currency-style"); + return this; +}; + + +OpenDocument.XmlWriter.prototype.openNamedExpressions = function () { + this + .openTag("table:named-expressions"); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeNamedExpressions = function () { + this + .closeTag("table:named-expressions"); + return this; +}; + +OpenDocument.XmlWriter.prototype.addNamedRange = function (name, address) { + this + .startOpenTag("table:named-range") + .addAttribute("table:name", name) + .addAttribute("table:base-cell-address", address) + .addAttribute("table:cell-range-address", address) + .closeEmptyTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.openTable = function (name) { + this.cellCounter.reinit(); + this + .startOpenTag("table:table") + .addAttribute("table:name", OpenDocument.checkSheetName(name)) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeTable = function () { + this + .closeTag("table:table"); + return this; +}; + +OpenDocument.XmlWriter.prototype.addTableColumn = function (tableColumn) { + this + .startOpenTag("table:table-column") + .addAttribute("table:style-name", tableColumn.styleName); + if (tableColumn.columnsRepeated > 1) { + this + .addAttribute("table:number-columns-repeated", tableColumn.columnsRepeated); + } + this + .addAttribute("table:default-cell-style-name", tableColumn.defaultCellStyleName) + .closeEmptyTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.openTableRow = function (rowStyleName) { + this.cellCounter.newRow(); + this + .startOpenTag("table:table-row") + .addAttribute("table:style-name", rowStyleName) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeTableRow = function () { + this + .closeTag("table:table-row"); + return this; +}; + +OpenDocument.XmlWriter.prototype.addEmptyTableCell = function (cell) { + this + .startCell(cell) + .endCell(cell, true); + return this; +}; + +OpenDocument.XmlWriter.prototype.addNumberTableCell = function (cell) { + this + .startCell(cell) + .addAttribute("office:value-type", "float") + .addAttribute("office:value", cell.value) + .endCell(cell, true); + return this; +}; + +OpenDocument.XmlWriter.prototype.addPercentageTableCell = function (cell) { + this + .startCell(cell) + .addAttribute("office:value-type", "percentage") + .addAttribute("office:value", cell.value) + .endCell(cell, true); + return this; +}; + +OpenDocument.XmlWriter.prototype.addDateTableCell = function (cell) { + this + .startCell(cell) + .addAttribute("office:value-type", "date") + .addAttribute("office:date-value", cell.value) + .endCell(cell, true); + return this; +}; + +OpenDocument.XmlWriter.prototype.addCurrencyTableCell = function (cell, currency) { + this + .startCell(cell) + .addAttribute("office:value-type", "currency") + .addAttribute("office:currency", currency) + .addAttribute("office:value", cell.value) + .endCell(cell, true); + return this; +}; + +OpenDocument.XmlWriter.prototype.addLinkStringTableCell = function (cell, linkString) { + this + .startCell(cell) + .addAttribute("office:value-type", "string") + .endOpenTag(); + if ((linkString) && (linkString.length > 0)) { + this + .startOpenTag("text:p") + .endOpenTag() + .XmlWriter.prototype.addLink(linkString) + .closeTag("text:p", false); + } + this + .endCell(cell, false); + + return this; +}; + +OpenDocument.XmlWriter.prototype.addStringTableCell = function (cell) { + this + .startCell(cell) + .addAttribute("office:value-type", "string") + .endOpenTag() + .splitText(cell.value) + .endCell(cell, false); + return this; +}; + +OpenDocument.XmlWriter.prototype.addFormulaTableCell = function (cell, valueType) { + this + .startCell(cell) + .addAttribute("table:formula", cell.value) + .addAttribute("office:value-type", valueType) + .endCell(cell, true); + return this; +}; + +OpenDocument.XmlWriter.prototype.splitText = function (value) { + if (value) { + var tokens = value.split('\n'); + for (let token of tokens) { + this + .startOpenTag("text:p") + .endOpenTag() + .addText(token) + .closeTag("text:p", false); + } + } + return this; +}; + +OpenDocument.XmlWriter.prototype.addLink = function (link) { + this + .startOpenTag("text:a", false) + .addAttribute("xlink:href", link) + .endOpenTag() + .addText(link) + .closeTag("text:a", false); + return this; +}; + +OpenDocument.XmlWriter.prototype.startCell = function (cell) { + var jump = this.cellCounter.newCell(cell.rowSpan, cell.colSpan); + if (jump > 0) { + this + .startOpenTag("table:covered-table-cell"); + if (jump > 1) { + this + .addAttribute("table:number-columns-repeated", jump) + } + this + .closeEmptyTag(); + } + this + .startOpenTag("table:table-cell") + .addAttribute("table:style-name", cell.styleName); + if (cell.rowSpan > 1) { + this.addAttribute("table:number-rows-spanned", cell.rowSpan); + } + if (cell.colSpan > 1) { + this + .addAttribute("table:number-columns-spanned", cell.colSpan); + } + return this; +}; + +OpenDocument.XmlWriter.prototype.endCell = function (cell, emptyTag) { + if (emptyTag) { + this + .closeEmptyTag(); + } else { + this + .closeTag("table:table-cell"); + } + if (cell.colSpan > 1) { + this + .startOpenTag("table:covered-table-cell"); + if (cell.colSpan > 2) { + this + .addAttribute("table:number-columns-repeated", (cell.colSpan - 1)); + } + this + .closeEmptyTag(); + } + return this; +}; + +OpenDocument.XmlWriter.prototype.openSettings = function () { + this + .openTag("office:settings"); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeSettings = function () { + this + .closeTag("office:settings"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openConfigItemSet = function (name) { + this + .startOpenTag("config:config-item-set") + .addAttribute("config:name", name) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeConfigItemSet = function () { + this + .closeTag("config:config-item-set"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openConfigItemMapIndexed = function (name) { + this + .startOpenTag("config:config-item-map-indexed") + .addAttribute("config:name", name) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeConfigItemMapIndexed = function () { + this + .closeTag("config:config-item-map-indexed"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openConfigItemMapNamed = function (name) { + this + .startOpenTag("config:config-item-map-named") + .addAttribute("config:name", name) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeConfigItemMapNamed = function () { + this + .closeTag("config:config-item-map-named"); + return this; +}; + +OpenDocument.XmlWriter.prototype.openConfigItemMapEntry = function (name) { + this + .startOpenTag("config:config-item-map-entry") + .addAttribute("config:name", name) + .endOpenTag(); + return this; +}; + +OpenDocument.XmlWriter.prototype.closeConfigItemMapEntry = function () { + this + .closeTag("config:config-item-map-entry"); + return this; +}; + +OpenDocument.XmlWriter.prototype.addConfigItem = function (name, type, value) { + this + .startOpenTag("config:config-item", true) + .addAttribute("config:name", name) + .addAttribute("config:type", type) + .endOpenTag() + .addText(value) + .closeTag("config:config-item", false); + return this; +}; + diff --git a/src/js/opendocument/OpenDocument.js b/src/js/opendocument/OpenDocument.js new file mode 100644 index 0000000..4b5a9c7 --- /dev/null +++ b/src/js/opendocument/OpenDocument.js @@ -0,0 +1,42 @@ +/** + * Objet global définissant l'espace de nom OpenDocument + * + * @namespace OpenDocument + */ +var OpenDocument = {}; + +OpenDocument.DEFAULT_CELLSTYLE_NAME = "Default"; +OpenDocument.ISODATE_DATASTYLE_NAME = "N101"; +OpenDocument.COLUMNSTYLE_PREFIX = "co"; +OpenDocument.CELLSTYLE_PREFIX = "ce"; +OpenDocument.DATASTYLE_PREFIX = "N"; +OpenDocument.SPREADSHEET_MIMETYPE = "application/vnd.oasis.opendocument.spreadsheet"; + +OpenDocument.checkSheetName = function (name) { + var result = ""; + var carac; + for (let i = 0, len = name.length; i < len; i++) { + carac = name.charAt(i); + switch (carac) { + case '[': + carac = '('; + break; + case ']': + carac = ')'; + break; + case '*': + case ':': + case '/': + case '?': + case '\\': + carac = '-'; + break; + } + result += carac; + } + return result; +}; + +OpenDocument.toChar = function (columnNumber) { + return (columnNumber + 64); +}; diff --git a/src/js/xmlwriter/XmlWriter.js b/src/js/xmlwriter/XmlWriter.js new file mode 100644 index 0000000..f08dc2e --- /dev/null +++ b/src/js/xmlwriter/XmlWriter.js @@ -0,0 +1,191 @@ +/** + * + * @constructor + * @param {type} options + */ +XmlWriter = function (options) { + this.xml = ""; + this.indentLength = -999999; + if (options) { + if (options.indentLength) { + this.indentLength = options.indentLength; + } else if (options.prettyXml) { + this.indentLength = 0; + } + } +}; + +XmlWriter.prototype.appendXMLDeclaration = function () { + this.write(''); + if (this.indentLength < 0) { + this.write('\n'); + } + return this; +}; + +XmlWriter.prototype.startOpenTag = function (tagName, indentBefore) { + if (indentBefore === undefined) { + indentBefore = true; + } + if (indentBefore) { + this.appendIndent(); + } + this.write('<'); + this.write(tagName); + return this; +}; + +XmlWriter.prototype.endOpenTag = function () { + this.write('>'); + this.increaseIndentValue(); + return this; +}; + +XmlWriter.prototype.closeEmptyTag = function () { + this.write('/'); + this.write('>'); + return this; +}; + +XmlWriter.prototype.openTag = function (tagName, indentBefore) { + if (indentBefore === undefined) { + indentBefore = true; + } + if (indentBefore) { + this.appendIndent(); + } + this.write('<'); + this.write(tagName); + this.write('>'); + this.increaseIndentValue(); + return this; +}; + +XmlWriter.prototype.closeTag = function (tagName, indentBefore) { + if (indentBefore === undefined) { + indentBefore = true; + } + this.decreaseIndentValue(); + if (indentBefore) { + this.appendIndent(); + } + this.write('<'); + this.write('/'); + this.write(tagName); + this.write('>'); + return this; +}; + +XmlWriter.prototype.addText = function (text) { + if (text) { + this.escape(text); + } + return this; +}; + +XmlWriter.prototype.addCData = function (text) { + this.write(""); + this.write("]]"); + this.write(""); + return this; +}; + +XmlWriter.prototype.addAttribute = function (attributeName, value) { + if ((value === 0) || (value)) { + this.write(' '); + this.write(attributeName); + this.write('='); + this.write('\"'); + this.escape(value.toString()); + this.write('\"'); + } + return this; +}; + +XmlWriter.prototype.addSimpleElement = function (tagName, value) { + if (value) { + this.startOpenTag(tagName); + this.endOpenTag(); + this.addText(value); + this.closeTag(tagName, false); + } + return this; +}; + +XmlWriter.prototype.addEmptyElement = function (tagName) { + this.startOpenTag(tagName); + this.closeEmptyTag(); + return this; +}; + +XmlWriter.prototype.write = function (text) { + this.xml += text; + return this; +}; + +XmlWriter.prototype.escape = function (text) { + var carac; + for(let i = 0, len = text.length; i < len; i++) { + carac = text.charAt(i); + switch (carac) { + case '&': + this.write("&"); + break; + case '"': + this.write("""); + break; + case '<': + this.write("<"); + break; + case '>': + this.write(">"); + break; + case '\'': + this.write("'"); + break; + case '\u00A0': + this.write(" "); + break; + default: + this.write(carac); + } + } + return this; +}; + +XmlWriter.prototype.appendIndent = function () { + if (this.indentLength > -1) { + this.write('\n'); + for(let i = 0, len = this.indentLength; i < len; i++) { + this.write('\t'); + } + } + return this; +}; + +XmlWriter.prototype.increaseIndentValue = function () { + this.indentLength = this.indentLength + 1; + return this; +}; + +XmlWriter.prototype.decreaseIndentValue = function () { + this.indentLength = this.indentLength - 1; + return this; +}; +