diff --git a/lib/index.js b/lib/index.js index 4a9ed56..cc16c33 100644 --- a/lib/index.js +++ b/lib/index.js @@ -42,8 +42,8 @@ module.exports = (function() { /** * */ - Workbook.prototype.substitute = function(sheet, substitions) { - + Workbook.prototype.substitute = function(sheet, substitutions) { + // Get sheet and parse XML tree // Loop over /worksheet/sheetData/row @@ -61,22 +61,22 @@ module.exports = (function() { // If string contains `${token}`, invoke substituion on this element // using the using the following rules: - // - if `token` is not found in `substituions`, do nothing + // - if `token` is not found in `substitutions`, do nothing - // - if `substituions[token]` is a string, or if the shared string + // - if `substitutions[token]` is a string, or if the shared string // contains other characters than the substituion reference, // replace the characters in the shared string, but leave the // shared string reference intact. - // - if `substituions[token]` is a number (`n`), boolean (`b`), or + // - if `substitutions[token]` is a number (`n`), boolean (`b`), or // date (`d`; ISO8601 format) insert it into the column // reference and change the cell type. - // - if `substituions[token]` is a list, clone and repeat the column + // - if `substitutions[token]` is a list, clone and repeat the column // for each item in the list (converting strings and types) // - if `token` starts with `table:`, assume it is in the format - // `${table:listName.varName}`. If `substituions[listName]` is a + // `${table:listName.varName}`. If `substitutions[listName]` is a // list of objects, clone the row and repeat for each item in the // list, substituting each `${table:listName.varName}` with the // property `varName` of each value in the list `listName`. @@ -108,22 +108,85 @@ module.exports = (function() { }); }; + // Write back the new shared strings list + Workbook.prototype.writeSharedStrings = function() { + // TODO + }; + + // Add a new shared string + Workbook.prototype.addSharedString = function(s) { + var self = this; + + var idx = self.sharedStrings.length; + self.sharedStrings.push(s); + self.sharedStringsLookup[s] = idx; + + return idx; + }; + // Get the number of a shared string, adding a new one if necessary. Workbook.prototype.stringIndex = function(s) { var self = this; var idx = self.sharedStringsLookup[s]; if(idx === undefined) { - idx = self.sharedStrings.length; - self.sharedStrings.push(s); - self.sharedStringsLookup[s] = idx; + idx = self.addSharedString(s); } return idx; }; - // Write back the new shared strings list - Workbook.prototype.writeSharedStrings = function() { - // TODO + // Replace a shared string with a new one at the same index. Return the + // index. + Workbook.prototype.replaceString = function(oldString, newString) { + var self = this; + + var idx = self.sharedStringsLookup[oldString]; + if(idx === undefined) { + idx = self.addSharedString(newString); + } else { + self.sharedStrings[idx] = newString; + delete self.sharedStringsLookup[oldString]; + self.sharedStringsLookup[newString] = idx; + } + + return idx; + }; + + // Return a list of tokens that may exist in the string. + // Keys are: `placeholder` (the full placeholder, including the `${}` + // delineators), `name` (the name part of the token), `key` (the object key + // for `table` tokens), `full` (boolean indicating whether this placeholder + // is the entirety of the string) and `type` (one of `table` or `cell`) + Workbook.prototype.extractPlaceholders = function(string) { + // Yes, that's right. It's a bunch of brackets and question marks and stuff. + var re = /\${(?:(.+?):)?(.+?)(?:\.(.+?))?}/g; + + var match = null, matches = []; + while((match = re.exec(string)) !== null) { + matches.push({ + placeholder: match[0], + type: match[1] || 'normal', + name: match[2], + key: match[3], + full: match[0].length === string.length + }); + } + + return matches; + }; + + // Split a reference into an object with keys `row` and `col`. + Workbook.prototype.splitRef = function(ref) { + var match = ref.match(/([A-Z]+)([0-9]+)/); + return { + col: match[1], + row: parseInt(match[2], 10) + }; + }; + + // Join an object with keys `row` and `col` into a single reference string + Workbook.prototype.joinRef = function(ref) { + return ref.col.toUpperCase() + Number(ref.row).toString(); }; // Get the next column's cell reference given a reference like "B2". diff --git a/test/helpers-test.js b/test/helpers-test.js index ceb0b6c..157d592 100644 --- a/test/helpers-test.js +++ b/test/helpers-test.js @@ -26,6 +26,146 @@ describe("Helpers", function() { }); + describe('replaceString', function() { + + it("adds new string if old string not found", function() { + var t = new XlsxTemplate(); + + expect(t.replaceString("foo", "bar")).toEqual(0); + expect(t.sharedStrings).toEqual(["bar"]); + expect(t.sharedStringsLookup).toEqual({"bar": 0}); + }); + + it("replaces strings if found", function() { + var t = new XlsxTemplate(); + + t.addSharedString("foo"); + t.addSharedString("baz"); + + expect(t.replaceString("foo", "bar")).toEqual(0); + expect(t.sharedStrings).toEqual(["bar", "baz"]); + expect(t.sharedStringsLookup).toEqual({"bar": 0, "baz": 1}); + }); + + }); + + describe('extractPlaceholders', function() { + + it("can extract simple placeholders", function() { + var t = new XlsxTemplate(); + + expect(t.extractPlaceholders("${foo}")).toEqual([{ + full: true, + key: undefined, + name: "foo", + placeholder: "${foo}", + type: "normal" + }]); + }); + + it("can extract simple placeholders inside strings", function() { + var t = new XlsxTemplate(); + + expect(t.extractPlaceholders("A string ${foo} bar")).toEqual([{ + full: false, + key: undefined, + name: "foo", + placeholder: "${foo}", + type: "normal" + }]); + }); + + it("can extract multiple placeholders from one string", function() { + var t = new XlsxTemplate(); + + expect(t.extractPlaceholders("${foo} ${bar}")).toEqual([{ + full: false, + key: undefined, + name: "foo", + placeholder: "${foo}", + type: "normal" + }, { + full: false, + key: undefined, + name: "bar", + placeholder: "${bar}", + type: "normal" + }]); + }); + + it("can extract placeholders with keys", function() { + var t = new XlsxTemplate(); + + expect(t.extractPlaceholders("${foo.bar}")).toEqual([{ + full: true, + key: "bar", + name: "foo", + placeholder: "${foo.bar}", + type: "normal" + }]); + }); + + it("can extract placeholders with types", function() { + var t = new XlsxTemplate(); + + expect(t.extractPlaceholders("${table:foo}")).toEqual([{ + full: true, + key: undefined, + name: "foo", + placeholder: "${table:foo}", + type: "table" + }]); + }); + + it("can extract placeholders with types and keys", function() { + var t = new XlsxTemplate(); + + expect(t.extractPlaceholders("${table:foo.bar}")).toEqual([{ + full: true, + key: "bar", + name: "foo", + placeholder: "${table:foo.bar}", + type: "table" + }]); + }); + + it("can handle strings with no placeholders", function() { + var t = new XlsxTemplate(); + + expect(t.extractPlaceholders("A string")).toEqual([]); + }); + + + }); + + describe('splitRef', function() { + + it("splits single digit and letter values", function() { + var t = new XlsxTemplate(); + expect(t.splitRef("A1")).toEqual({col: "A", row: 1}); + }); + + it("splits multiple digit and letter values", function() { + var t = new XlsxTemplate(); + expect(t.splitRef("AB12")).toEqual({col: "AB", row: 12}); + }); + + }); + + describe('joinRef', function() { + + it("joins single digit and letter values", function() { + var t = new XlsxTemplate(); + expect(t.joinRef({col: "A", row: 1})).toEqual("A1"); + }); + + it("joins multiple digit and letter values", function() { + var t = new XlsxTemplate(); + expect(t.joinRef({col: "AB", row: 12})).toEqual("AB12"); + }); + + }); + describe('nexCol', function() { it("increments single columns", function() { @@ -33,7 +173,6 @@ describe("Helpers", function() { expect(t.nextCol("A1")).toEqual("B1"); expect(t.nextCol("B1")).toEqual("C1"); - }); it("maintains row index", function() { @@ -41,7 +180,6 @@ describe("Helpers", function() { expect(t.nextCol("A99")).toEqual("B99"); expect(t.nextCol("B11231")).toEqual("C11231"); - }); it("captialises letters", function() { @@ -80,7 +218,6 @@ describe("Helpers", function() { expect(t.nextRow("A1")).toEqual("A2"); expect(t.nextRow("B1")).toEqual("B2"); expect(t.nextRow("AZ2")).toEqual("AZ3"); - }); it("captialises letters", function() {