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 1 |
+ Colonne 3 |
+
+
+ Sous-colonne 1-1 |
+ Sous-colonne 1-2 |
+ Sous-colonne 1-3 |
+
+
+
+
+ Cellule 1.1 |
+ Cellule 1.2
+
+ Caché par None
+ Suite |
+ Cellule 1.3
+
+ Caché dans OD
+ Test à des lignes vides |
+ Cellule 1.4 |
+
+
+ Cellule 2.1 (Très grande) |
+
+ Cellule 2.3 |
+ -160 |
+
+
+ Cellule 3.1 |
+ Cellule 3.2 |
+ 1000 € |
+ -$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;
+};
+