docxtemplater
Version:
.docx generator working with templates and data (like Mustache)
1,560 lines (1,410 loc) • 1.3 MB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.DocxtemplaterTest = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var memoize = require("./memoize");
var DOMParser = require("xmldom").DOMParser;
var XMLSerializer = require("xmldom").XMLSerializer;
var Errors = require("./errors");
var DocUtils = {};
function parser(tag) {
return _defineProperty({}, "get", function get(scope) {
if (tag === ".") {
return scope;
}
return scope[tag];
});
}
DocUtils.defaults = {
nullGetter: function nullGetter(part) {
if (!part.module) {
return "undefined";
}
if (part.module === "rawxml") {
return "";
}
return "";
},
parser: memoize(parser),
delimiters: {
start: "{",
end: "}"
}
};
DocUtils.mergeObjects = function () {
var resObj = {};
var obj = void 0,
keys = void 0;
for (var i = 0; i < arguments.length; i += 1) {
obj = arguments[i];
keys = Object.keys(obj);
for (var j = 0; j < keys.length; j += 1) {
resObj[keys[j]] = obj[keys[j]];
}
}
return resObj;
};
DocUtils.xml2str = function (xmlNode) {
var a = new XMLSerializer();
return a.serializeToString(xmlNode);
};
DocUtils.decodeUtf8 = function (s) {
try {
if (s === undefined) {
return undefined;
}
// replace Ascii 160 space by the normal space, Ascii 32
return decodeURIComponent(escape(DocUtils.convertSpaces(s)));
} catch (e) {
var err = new Error("End");
err.properties.data = s;
err.properties.explanation = "Could not decode string to UTF8";
throw err;
}
};
DocUtils.encodeUtf8 = function (s) {
return unescape(encodeURIComponent(s));
};
DocUtils.str2xml = function (str, errorHandler) {
var parser = new DOMParser({ errorHandler: errorHandler });
return parser.parseFromString(str, "text/xml");
};
DocUtils.charMap = {
"&": "&",
"'": "'",
"<": "<",
">": ">"
};
var regexStripRegexp = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;
DocUtils.escapeRegExp = function (str) {
return str.replace(regexStripRegexp, "\\$&");
};
DocUtils.charMapRegexes = Object.keys(DocUtils.charMap).map(function (endChar) {
var startChar = DocUtils.charMap[endChar];
return {
rstart: new RegExp(DocUtils.escapeRegExp(startChar), "g"),
rend: new RegExp(DocUtils.escapeRegExp(endChar), "g"),
start: startChar,
end: endChar
};
});
DocUtils.wordToUtf8 = function (string) {
var r = void 0;
for (var i = 0, l = DocUtils.charMapRegexes.length; i < l; i++) {
r = DocUtils.charMapRegexes[i];
string = string.replace(r.rstart, r.end);
}
return string;
};
DocUtils.utf8ToWord = function (string) {
if (typeof string !== "string") {
string = string.toString();
}
var r = void 0;
for (var i = 0, l = DocUtils.charMapRegexes.length; i < l; i++) {
r = DocUtils.charMapRegexes[i];
string = string.replace(r.rend, r.start);
}
return string;
};
DocUtils.cloneDeep = function (obj) {
return JSON.parse(JSON.stringify(obj));
};
DocUtils.concatArrays = function (arrays) {
return arrays.reduce(function (result, array) {
Array.prototype.push.apply(result, array);
return result;
}, []);
};
var spaceRegexp = new RegExp(String.fromCharCode(160), "g");
DocUtils.convertSpaces = function (s) {
return s.replace(spaceRegexp, " ");
};
DocUtils.pregMatchAll = function (regex, content) {
/* regex is a string, content is the content. It returns an array of all matches with their offset, for example:
regex=la
content=lolalolilala
returns: [{array: {0: 'la'},offset: 2},{array: {0: 'la'},offset: 8},{array: {0: 'la'} ,offset: 10}]
*/
var matchArray = [];
var match = void 0;
while ((match = regex.exec(content)) != null) {
matchArray.push({ array: match, offset: match.index });
}
return matchArray;
};
DocUtils.sizeOfObject = function (obj) {
return Object.keys(obj).length;
};
function throwXmlTagNotFound(options) {
var err = new Errors.XTTemplateError("No tag '" + options.element + "' was found at the " + options.position);
err.properties = {
id: "no_xml_tag_found_at_" + options.position,
explanation: "No tag '" + options.element + "' was found at the " + options.position,
parsed: options.parsed,
index: options.index,
element: options.element
};
throw err;
}
DocUtils.getRight = function (parsed, element, index) {
for (var i = index, l = parsed.length; i < l; i++) {
var part = parsed[i];
if (part.value === "</" + element + ">") {
return i;
}
}
throwXmlTagNotFound({ position: "right", element: element, parsed: parsed, index: index });
};
DocUtils.getLeft = function (parsed, element, index) {
for (var i = index; i >= 0; i--) {
var part = parsed[i];
if (part.value.indexOf("<" + element) === 0 && [">", " "].indexOf(part.value[element.length + 1]) !== -1) {
return i;
}
}
throwXmlTagNotFound({ position: "left", element: element, parsed: parsed, index: index });
};
module.exports = DocUtils;
},{"./errors":3,"./memoize":6,"xmldom":114}],2:[function(require,module,exports){
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DocUtils = require("./doc-utils");
DocUtils.traits = require("./traits");
DocUtils.moduleWrapper = require("./module-wrapper");
var wrapper = DocUtils.moduleWrapper;
var Docxtemplater = function () {
function Docxtemplater() {
_classCallCheck(this, Docxtemplater);
if (arguments.length > 0) {
throw new Error("The constructor with parameters have been removed in docxtemplater 3.0, please check the upgrade guide.");
}
this.compiled = {};
this.modules = [];
this.setOptions({});
}
_createClass(Docxtemplater, [{
key: "attachModule",
value: function attachModule(module) {
this.modules.push(wrapper(module));
return this;
}
}, {
key: "setOptions",
value: function setOptions(options) {
var _this = this;
this.options = options;
Object.keys(DocUtils.defaults).forEach(function (key) {
var defaultValue = DocUtils.defaults[key];
_this[key] = _this.options[key] != null ? _this.options[key] : defaultValue;
});
if (this.zip) {
this.updateFileTypeConfig();
}
return this;
}
}, {
key: "loadZip",
value: function loadZip(zip) {
if (zip.loadAsync) {
throw new Error("Docxtemplater doesn't handle JSZip version >=3, see changelog");
}
this.zip = zip;
this.updateFileTypeConfig();
return this;
}
}, {
key: "compileFile",
value: function compileFile(fileName) {
var currentFile = this.createTemplateClass(fileName);
currentFile.parse();
this.compiled[fileName] = currentFile;
}
}, {
key: "compile",
value: function compile() {
this.templatedFiles = this.fileTypeConfig.getTemplatedFiles(this.zip);
return this;
}
}, {
key: "updateFileTypeConfig",
value: function updateFileTypeConfig() {
this.fileType = this.zip.files["word/document.xml"] ? "docx" : "pptx";
this.fileTypeConfig = this.options.fileTypeConfig || Docxtemplater.FileTypeConfig[this.fileType];
return this;
}
}, {
key: "render",
value: function render() {
var _this2 = this;
this.options.xmlFileNames = [];
this.modules = this.fileTypeConfig.baseModules.map(function (moduleFunction) {
return moduleFunction();
}).concat(this.modules);
this.options = this.modules.reduce(function (options, module) {
return module.optionsTransformer(options, _this2);
}, this.options);
this.xmlDocuments = this.options.xmlFileNames.reduce(function (xmlDocuments, fileName) {
var content = _this2.zip.files[fileName].asText();
xmlDocuments[fileName] = DocUtils.str2xml(content);
return xmlDocuments;
}, {});
this.modules.forEach(function (module) {
module.set({ zip: _this2.zip, xmlDocuments: _this2.xmlDocuments, data: _this2.data });
});
this.compile();
this.modules.forEach(function (module) {
module.set({ compiled: _this2.compiled });
});
// Loop inside all templatedFiles (ie xml files with content).
// Sometimes they don't exist (footer.xml for example)
this.templatedFiles.forEach(function (fileName) {
if (_this2.zip.files[fileName] != null) {
_this2.compileFile(fileName);
}
});
this.mapper = this.modules.reduce(function (value, module) {
return module.getRenderedMap(value);
}, {});
Object.keys(this.mapper).forEach(function (to) {
var mapped = _this2.mapper[to];
var from = mapped.from;
var currentFile = _this2.compiled[from];
currentFile.setTags(mapped.data);
currentFile.render(to);
_this2.zip.file(to, currentFile.content);
});
Object.keys(this.xmlDocuments).forEach(function (fileName) {
_this2.zip.remove(fileName);
var content = DocUtils.xml2str(_this2.xmlDocuments[fileName]);
return _this2.zip.file(fileName, content, {});
});
return this;
}
}, {
key: "setData",
value: function setData(data) {
this.data = data;
return this;
}
}, {
key: "getZip",
value: function getZip() {
return this.zip;
}
}, {
key: "createTemplateClass",
value: function createTemplateClass(path) {
var usedData = this.zip.files[path].asText();
return this.createTemplateClassFromContent(usedData, path);
}
}, {
key: "createTemplateClassFromContent",
value: function createTemplateClassFromContent(content, filePath) {
var _this3 = this;
var xmltOptions = {
filePath: filePath
};
Object.keys(DocUtils.defaults).forEach(function (key) {
xmltOptions[key] = _this3[key];
});
xmltOptions.fileTypeConfig = this.fileTypeConfig;
xmltOptions.modules = this.modules;
return new Docxtemplater.XmlTemplater(content, xmltOptions);
}
}, {
key: "getFullText",
value: function getFullText(path) {
return this.createTemplateClass(path || this.fileTypeConfig.textPath).getFullText();
}
}, {
key: "getTemplatedFiles",
value: function getTemplatedFiles() {
this.compile();
return this.templatedFiles;
}
}]);
return Docxtemplater;
}();
Docxtemplater.DocUtils = require("./doc-utils");
Docxtemplater.Errors = require("./errors");
Docxtemplater.XmlTemplater = require("./xml-templater");
Docxtemplater.FileTypeConfig = require("./file-type-config");
Docxtemplater.XmlMatcher = require("./xml-matcher");
module.exports = Docxtemplater;
},{"./doc-utils":1,"./errors":3,"./file-type-config":4,"./module-wrapper":8,"./traits":27,"./xml-matcher":28,"./xml-templater":29}],3:[function(require,module,exports){
"use strict";
function XTError(message) {
this.name = "GenericError";
this.message = message;
this.stack = new Error(message).stack;
}
XTError.prototype = Error.prototype;
function XTTemplateError(message) {
this.name = "TemplateError";
this.message = message;
this.stack = new Error(message).stack;
}
XTTemplateError.prototype = new XTError();
function XTScopeParserError(message) {
this.name = "ScopeParserError";
this.message = message;
this.stack = new Error(message).stack;
}
XTScopeParserError.prototype = new XTError();
function XTInternalError(message) {
this.name = "InternalError";
this.properties = { explanation: "InternalError" };
this.message = message;
this.stack = new Error(message).stack;
}
XTInternalError.prototype = new XTError();
module.exports = {
XTError: XTError,
XTTemplateError: XTTemplateError,
XTInternalError: XTInternalError,
XTScopeParserError: XTScopeParserError
};
},{}],4:[function(require,module,exports){
"use strict";
var loopModule = require("./modules/loop");
var spacePreserveModule = require("./modules/space-preserve");
var rawXmlModule = require("./modules/rawxml");
var expandPairTrait = require("./modules/expand-pair-trait");
var render = require("./modules/render");
var PptXFileTypeConfig = {
getTemplatedFiles: function getTemplatedFiles(zip) {
var slideTemplates = zip.file(/ppt\/(slides|slideMasters)\/(slide|slideMaster)\d+\.xml/).map(function (file) {
return file.name;
});
return slideTemplates.concat(["ppt/presentation.xml"]);
},
textPath: "ppt/slides/slide1.xml",
tagsXmlTextArray: ["a:t", "m:t"],
tagsXmlLexedArray: ["p:sp", "a:tc", "a:tr", "a:table", "a:p", "a:r"],
tagRawXml: "p:sp",
tagTextXml: "a:t",
baseModules: [render, expandPairTrait, rawXmlModule, loopModule]
};
var DocXFileTypeConfig = {
getTemplatedFiles: function getTemplatedFiles(zip) {
var slideTemplates = zip.file(/word\/(header|footer)\d+\.xml/).map(function (file) {
return file.name;
});
return slideTemplates.concat(["word/document.xml"]);
},
textPath: "word/document.xml",
tagsXmlTextArray: ["w:t", "m:t"],
tagsXmlLexedArray: ["w:tc", "w:tr", "w:table", "w:p", "w:r"],
tagRawXml: "w:p",
tagTextXml: "w:t",
baseModules: [render, spacePreserveModule, expandPairTrait, rawXmlModule, loopModule]
};
module.exports = {
docx: DocXFileTypeConfig,
pptx: PptXFileTypeConfig
};
},{"./modules/expand-pair-trait":9,"./modules/loop":10,"./modules/rawxml":11,"./modules/render":12,"./modules/space-preserve":13}],5:[function(require,module,exports){
"use strict";
var Errors = require("./errors");
var DocUtils = require("./doc-utils");
function inRange(range, match) {
return range[0] <= match.offset && match.offset < range[1];
}
function updateInTextTag(part, inTextTag) {
if (part.type === "tag" && part.position === "start" && part.text) {
if (inTextTag) {
throw new Error("Malformed xml : Already in text tag");
}
return true;
}
if (part.type === "tag" && part.position === "end" && part.text) {
if (!inTextTag) {
throw new Error("Malformed xml : Already not in text tag");
}
return false;
}
return inTextTag;
}
function offsetSort(a, b) {
return a.offset - b.offset;
}
function getTag(tag) {
var start = 1;
if (tag[1] === "/") {
start = 2;
}
var index = tag.indexOf(" ");
var end = index === -1 ? tag.length - 1 : index;
return {
tag: tag.slice(start, end),
position: start === 1 ? "start" : "end"
};
}
function tagMatcher(content, textMatchArray, othersMatchArray) {
var cursor = 0;
var contentLength = content.length;
var allMatches = DocUtils.concatArrays([textMatchArray.map(function (tag) {
return { tag: tag, text: true };
}), othersMatchArray.map(function (tag) {
return { tag: tag, text: false };
})]).reduce(function (allMatches, t) {
allMatches[t.tag] = t.text;
return allMatches;
}, {});
var totalMatches = [];
while (cursor < contentLength) {
cursor = content.indexOf("<", cursor);
if (cursor === -1) {
break;
}
var offset = cursor;
cursor = content.indexOf(">", cursor);
var tagText = content.slice(offset, cursor + 1);
var _getTag = getTag(tagText),
tag = _getTag.tag,
position = _getTag.position;
var text = allMatches[tag];
if (text == null) {
continue;
}
totalMatches.push({ type: "tag", position: position, text: text, offset: offset, value: tagText });
}
return totalMatches;
}
function throwUnopenedTagException(options) {
var err = new Errors.XTTemplateError("Unopened tag");
err.properties = {
xtag: options.xtag.split(" ")[0],
id: "unopened_tag",
context: options.xtag,
explanation: "The tag beginning with '" + options.xtag.substr(0, 10) + "' is unclosed"
};
throw err;
}
function throwUnclosedTagException(options) {
var err = new Errors.XTTemplateError("Unclosed tag");
err.properties = {
xtag: options.xtag.split(" ")[0].substr(1),
id: "unclosed_tag",
context: options.xtag,
explanation: "The tag beginning with '" + options.xtag.substr(0, 10) + "' is unclosed"
};
throw err;
}
function assertDelimiterOrdered(delimiterMatches, fullText) {
var inDelimiter = false;
var lastDelimiterMatch = { offset: 0 };
var xtag = void 0;
delimiterMatches.forEach(function (delimiterMatch) {
xtag = fullText.substr(lastDelimiterMatch.offset, delimiterMatch.offset - lastDelimiterMatch.offset);
if (delimiterMatch.position === "start" && inDelimiter || delimiterMatch.position === "end" && !inDelimiter) {
if (delimiterMatch.position === "start") {
throwUnclosedTagException({ xtag: xtag });
} else {
throwUnopenedTagException({ xtag: xtag });
}
}
inDelimiter = !inDelimiter;
lastDelimiterMatch = delimiterMatch;
});
var delimiterMatch = { offset: fullText.length };
xtag = fullText.substr(lastDelimiterMatch.offset, delimiterMatch.offset - lastDelimiterMatch.offset);
if (inDelimiter) {
throwUnclosedTagException({ xtag: xtag });
}
}
function getAllIndexes(arr, val, position) {
var indexes = [];
var offset = -1;
do {
offset = arr.indexOf(val, offset + 1);
if (offset !== -1) {
indexes.push({ offset: offset, position: position });
}
} while (offset !== -1);
return indexes;
}
function Reader(innerContentParts) {
var _this = this;
this.innerContentParts = innerContentParts;
this.full = "";
this.parseDelimiters = function (delimiters) {
_this.full = _this.innerContentParts.join("");
var offset = 0;
_this.ranges = _this.innerContentParts.map(function (part) {
offset += part.length;
return offset - part.length;
});
var delimiterMatches = DocUtils.concatArrays([getAllIndexes(_this.full, delimiters.start, "start"), getAllIndexes(_this.full, delimiters.end, "end")]).sort(offsetSort);
assertDelimiterOrdered(delimiterMatches, _this.full);
var delimiterLength = { start: delimiters.start.length, end: delimiters.end.length };
var cutNext = 0;
var delimiterIndex = 0;
_this.parsed = _this.ranges.map(function (offset, i) {
var range = [offset, offset + this.innerContentParts[i].length];
var partContent = this.innerContentParts[i];
var delimitersInOffset = [];
while (delimiterIndex < delimiterMatches.length && inRange(range, delimiterMatches[delimiterIndex])) {
delimitersInOffset.push(delimiterMatches[delimiterIndex]);
delimiterIndex++;
}
var parts = [];
var cursor = 0;
if (cutNext > 0) {
cursor = cutNext;
cutNext = 0;
}
delimitersInOffset.forEach(function (delimiterInOffset) {
var value = partContent.substr(cursor, delimiterInOffset.offset - offset - cursor);
if (value.length > 0) {
parts.push({ type: "content", value: value });
}
parts.push({ type: "delimiter", position: delimiterInOffset.position });
cursor = delimiterInOffset.offset - offset + delimiterLength[delimiterInOffset.position];
});
cutNext = cursor - partContent.length;
var value = partContent.substr(cursor);
if (value.length > 0) {
parts.push({ type: "content", value: value });
}
return parts;
}, _this);
};
}
module.exports = {
parse: function parse(xmlparsed, delimiters) {
var inTextTag = false;
var innerContentParts = [];
xmlparsed.forEach(function (part) {
inTextTag = updateInTextTag(part, inTextTag);
if (inTextTag && part.type === "content") {
innerContentParts.push(part.value);
}
});
var reader = new Reader(innerContentParts);
reader.parseDelimiters(delimiters);
var newArray = [];
var index = 0;
xmlparsed.forEach(function (part) {
inTextTag = updateInTextTag(part, inTextTag);
if (part.type === "content") {
part.position = inTextTag ? "insidetag" : "outsidetag";
}
if (inTextTag && part.type === "content") {
Array.prototype.push.apply(newArray, reader.parsed[index].map(function (p) {
if (p.type === "content") {
p.position = "insidetag";
}
return p;
}));
index++;
} else {
newArray.push(part);
}
});
return newArray;
},
xmlparse: function xmlparse(content, xmltags) {
var matches = tagMatcher(content, xmltags.text, xmltags.other);
var cursor = 0;
var parsed = matches.reduce(function (parsed, match) {
var value = content.substr(cursor, match.offset - cursor);
if (value.length > 0) {
parsed.push({ type: "content", value: value });
}
cursor = match.offset + match.value.length;
delete match.offset;
if (match.value.length > 0) {
parsed.push(match);
}
return parsed;
}, []);
var value = content.substr(cursor);
if (value.length > 0) {
parsed.push({ type: "content", value: value });
}
return parsed;
}
};
},{"./doc-utils":1,"./errors":3}],6:[function(require,module,exports){
"use strict";
function memoize(func) {
var stringifyJson = JSON.stringify,
cache = {};
function cachedfun() {
var hash = stringifyJson(arguments);
return hash in cache ? cache[hash] : cache[hash] = func.apply(this, arguments);
}
return cachedfun;
}
module.exports = memoize;
},{}],7:[function(require,module,exports){
"use strict";
function getMinFromArrays(arrays, state) {
var minIndex = -1;
for (var i = 0, l = arrays.length; i < l; i++) {
if (state[i] >= arrays[i].length) {
continue;
}
if (minIndex === -1 || arrays[i][state[i]].offset < arrays[minIndex][state[minIndex]].offset) {
minIndex = i;
}
}
if (minIndex === -1) {
throw new Error("minIndex negative");
}
return minIndex;
}
module.exports = function (arrays) {
var totalLength = arrays.reduce(function (sum, array) {
return sum + array.length;
}, 0);
arrays = arrays.filter(function (array) {
return array.length > 0;
});
var resultArray = new Array(totalLength);
var state = arrays.map(function () {
return 0;
});
var i = 0;
while (i <= totalLength - 1) {
var arrayIndex = getMinFromArrays(arrays, state);
resultArray[i] = arrays[arrayIndex][state[arrayIndex]];
state[arrayIndex]++;
i++;
}
return resultArray;
};
},{}],8:[function(require,module,exports){
"use strict";
function emptyFun() {}
function identity(i) {
return i;
}
module.exports = function (module) {
var defaults = {
set: emptyFun,
parse: emptyFun,
render: emptyFun,
getTraits: emptyFun,
optionsTransformer: identity,
getRenderedMap: identity,
postparse: identity
};
if (Object.keys(defaults).every(function (key) {
return !module[key];
})) {
throw new Error("This module cannot be wrapped, because it doesn't define any of the necessary functions");
}
Object.keys(defaults).forEach(function (key) {
module[key] = module[key] || defaults[key];
});
return module;
};
},{}],9:[function(require,module,exports){
"use strict";
var traitName = "expandPair";
var mergeSort = require("../mergesort");
var DocUtils = require("../doc-utils");
var wrapper = require("../module-wrapper");
var _require = require("../traits"),
getExpandToDefault = _require.getExpandToDefault;
var Errors = require("../errors");
function throwUnmatchedLoopException(options) {
var location = options.location;
var t = location === "start" ? "unclosed" : "unopened";
var T = location === "start" ? "Unclosed" : "Unopened";
var err = new Errors.XTTemplateError(T + " loop");
var tag = options.part.value;
err.properties = {
id: t + "_loop",
explanation: "The loop with tag " + tag + " is " + t,
xtag: tag
};
throw err;
}
function throwClosingTagNotMatchOpeningTag(options) {
var tags = options.tags;
var err = new Errors.XTTemplateError("Closing tag does not match opening tag");
err.properties = {
id: "closing_tag_does_not_match_opening_tag",
explanation: "The tag \"" + tags[0].value + "\" is closed by the tag \"" + tags[1].value + "\"",
openingtag: tags[0].value,
closingtag: tags[1].value
};
throw err;
}
function getOpenCountChange(part) {
switch (part.location) {
case "start":
return 1;
case "end":
return -1;
default:
throw new Error("Location should be one of 'start' or 'end' (given : " + part.location + ")");
}
}
function getPairs(traits) {
if (traits.length === 0) {
return [];
}
var countOpen = 1;
var firstTrait = traits[0];
for (var i = 1; i < traits.length; i++) {
var currentTrait = traits[i];
countOpen += getOpenCountChange(currentTrait.part);
if (countOpen === 0) {
if (currentTrait.part.value !== firstTrait.part.value && currentTrait.part.value !== "") {
throwClosingTagNotMatchOpeningTag({ tags: [firstTrait.part, currentTrait.part] });
}
var outer = getPairs(traits.slice(i + 1));
return [[firstTrait, currentTrait]].concat(outer);
}
}
var part = firstTrait.part;
throwUnmatchedLoopException({ part: part, location: part.location });
}
var expandPairTrait = {
name: "ExpandPairTrait",
postparse: function postparse(parsed, _ref) {
var getTraits = _ref.getTraits,
_postparse = _ref.postparse;
var traits = getTraits(traitName, parsed);
traits = traits.map(function (trait) {
return trait || [];
});
traits = mergeSort(traits);
var pairs = getPairs(traits);
var expandedPairs = pairs.map(function (pair) {
var expandTo = pair[0].part.expandTo;
if (expandTo === "auto") {
expandTo = getExpandToDefault(parsed.slice(pair[0].offset, pair[1].offset));
}
if (!expandTo) {
return [pair[0].offset, pair[1].offset];
}
var left = DocUtils.getLeft(parsed, expandTo, pair[0].offset);
var right = DocUtils.getRight(parsed, expandTo, pair[1].offset);
return [left, right];
});
var currentPairIndex = 0;
var innerParts = void 0;
return parsed.reduce(function (newParsed, part, i) {
var inPair = currentPairIndex < pairs.length && expandedPairs[currentPairIndex][0] <= i;
var pair = pairs[currentPairIndex];
var expandedPair = expandedPairs[currentPairIndex];
if (!inPair) {
newParsed.push(part);
return newParsed;
}
if (expandedPair[0] === i) {
innerParts = [];
}
if (pair[0].offset !== i && pair[1].offset !== i) {
innerParts.push(part);
}
if (expandedPair[1] === i) {
var basePart = parsed[pair[0].offset];
delete basePart.location;
delete basePart.expandTo;
basePart.subparsed = _postparse(innerParts);
newParsed.push(basePart);
currentPairIndex++;
}
return newParsed;
}, []);
}
};
module.exports = function () {
return wrapper(expandPairTrait);
};
},{"../doc-utils":1,"../errors":3,"../mergesort":7,"../module-wrapper":8,"../traits":27}],10:[function(require,module,exports){
"use strict";
var DocUtils = require("../doc-utils");
var dashInnerRegex = /^-([^\s]+)\s(.+)$/;
var wrapper = require("../module-wrapper");
var moduleName = "loop";
var loopModule = {
name: "LoopModule",
parse: function parse(placeHolderContent) {
var module = moduleName;
var type = "placeholder";
if (placeHolderContent[0] === "#") {
return { type: type, value: placeHolderContent.substr(1), expandTo: "auto", module: module, location: "start", inverted: false };
}
if (placeHolderContent[0] === "^") {
return { type: type, value: placeHolderContent.substr(1), expandTo: "auto", module: module, location: "start", inverted: true };
}
if (placeHolderContent[0] === "/") {
return { type: type, value: placeHolderContent.substr(1), module: module, location: "end" };
}
if (placeHolderContent[0] === "-") {
var value = placeHolderContent.replace(dashInnerRegex, "$2");
var expandTo = placeHolderContent.replace(dashInnerRegex, "$1");
return { type: type, value: value, expandTo: expandTo, module: module, location: "start", inverted: false };
}
return null;
},
getTraits: function getTraits(traitName, parsed) {
if (traitName !== "expandPair") {
return;
}
return parsed.reduce(function (tags, part, offset) {
if (part.type === "placeholder" && part.module === moduleName) {
tags.push({ part: part, offset: offset });
}
return tags;
}, []);
},
render: function render(part, options) {
if (!part.type === "placeholder" || part.module !== moduleName) {
return null;
}
var totalValue = [];
function loopOver(scope) {
var scopeManager = options.scopeManager.createSubScopeManager(scope, part.value);
totalValue.push(options.render(DocUtils.mergeObjects({}, options, {
compiled: part.subparsed,
tags: {},
scopeManager: scopeManager
})));
}
options.scopeManager.loopOver(part.value, loopOver, part.inverted);
return { value: totalValue.join("") };
}
};
module.exports = function () {
return wrapper(loopModule);
};
},{"../doc-utils":1,"../module-wrapper":8}],11:[function(require,module,exports){
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DocUtils = require("../doc-utils");
var Errors = require("../errors");
var moduleName = "rawxml";
var wrapper = require("../module-wrapper");
function throwRawTagShouldBeOnlyTextInParagraph(options) {
var err = new Errors.XTTemplateError("Raw tag should be the only text in paragraph");
var tag = options.part.value;
err.properties = {
id: "raw_xml_tag_should_be_only_text_in_paragraph",
explanation: "The tag " + tag,
xtag: options.part.value,
paragraphParts: options.paragraphParts
};
throw err;
}
function getInner(_ref) {
var part = _ref.part,
left = _ref.left,
right = _ref.right,
postparsed = _ref.postparsed,
index = _ref.index;
var paragraphParts = postparsed.slice(left + 1, right);
paragraphParts.forEach(function (p, i) {
if (i === index - left - 1) {
return;
}
if (p.type === "placeholder" || p.type === "content" && p.position === "insidetag") {
throwRawTagShouldBeOnlyTextInParagraph({ paragraphParts: paragraphParts, part: part });
}
});
return part;
}
var RawXmlModule = function () {
function RawXmlModule() {
_classCallCheck(this, RawXmlModule);
this.name = "RawXmlModule";
}
_createClass(RawXmlModule, [{
key: "optionsTransformer",
value: function optionsTransformer(options, docxtemplater) {
this.fileTypeConfig = docxtemplater.fileTypeConfig;
return options;
}
}, {
key: "parse",
value: function parse(placeHolderContent) {
var type = "placeholder";
if (placeHolderContent[0] !== "@") {
return null;
}
return { type: type, value: placeHolderContent.substr(1), module: moduleName };
}
}, {
key: "postparse",
value: function postparse(parsed) {
return DocUtils.traits.expandToOne(parsed, { moduleName: moduleName, getInner: getInner, expandTo: this.fileTypeConfig.tagRawXml });
}
}, {
key: "render",
value: function render(part, options) {
if (part.module !== moduleName) {
return null;
}
var value = options.scopeManager.getValue(part.value);
if (value == null) {
value = options.nullGetter(part);
}
return { value: value };
}
}]);
return RawXmlModule;
}();
module.exports = function () {
return wrapper(new RawXmlModule());
};
},{"../doc-utils":1,"../errors":3,"../module-wrapper":8}],12:[function(require,module,exports){
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var wrapper = require("../module-wrapper");
var Render = function () {
function Render() {
_classCallCheck(this, Render);
this.name = "Render";
}
_createClass(Render, [{
key: "set",
value: function set(obj) {
if (obj.compiled) {
this.compiled = obj.compiled;
}
if (obj.data != null) {
this.data = obj.data;
}
}
}, {
key: "getRenderedMap",
value: function getRenderedMap(mapper) {
var _this = this;
return Object.keys(this.compiled).reduce(function (mapper, from) {
mapper[from] = { from: from, data: _this.data };
return mapper;
}, mapper);
}
}]);
return Render;
}();
module.exports = function () {
return wrapper(new Render());
};
},{"../module-wrapper":8}],13:[function(require,module,exports){
"use strict";
var wrapper = require("../module-wrapper");
var spacePreserve = {
name: "SpacePreserveModule",
postparse: function postparse(parsed) {
var chunk = [];
var inChunk = false;
var result = parsed.reduce(function (parsed, part) {
if (part.type === "tag" && part.position === "start" && part.text && part.value === "<w:t>") {
inChunk = true;
}
if (inChunk) {
if (part.type === "placeholder" && !part.module) {
chunk[0].value = '<w:t xml:space="preserve">';
}
chunk.push(part);
} else {
parsed.push(part);
}
if (part.type === "tag" && part.position === "end" && part.text && part.value === "</w:t>") {
Array.prototype.push.apply(parsed, chunk);
inChunk = false;
chunk = [];
}
return parsed;
}, []);
Array.prototype.push.apply(result, chunk);
return result;
}
};
module.exports = function () {
return wrapper(spacePreserve);
};
},{"../module-wrapper":8}],14:[function(require,module,exports){
"use strict";
var DocUtils = require("./doc-utils");
var parser = {
postparse: function postparse(parsed, modules) {
function getTraits(traitName, parsed) {
return modules.map(function (module) {
return module.getTraits(traitName, parsed);
});
}
function postparse(parsed) {
return modules.reduce(function (parsed, module) {
return module.postparse(parsed, { postparse: postparse, getTraits: getTraits });
}, parsed);
}
return postparse(parsed);
},
parse: function parse(lexed, modules) {
function moduleParse(placeHolderContent, parsed) {
var moduleParsed = void 0;
for (var i = 0, l = modules.length; i < l; i++) {
var _module = modules[i];
moduleParsed = _module.parse(placeHolderContent);
if (moduleParsed) {
parsed.push(moduleParsed);
return moduleParsed;
}
}
return null;
}
var inPlaceHolder = false;
var placeHolderContent = void 0;
var tailParts = [];
return lexed.reduce(function (parsed, token) {
if (token.type === "delimiter") {
inPlaceHolder = token.position === "start";
if (token.position === "end") {
placeHolderContent = DocUtils.wordToUtf8(placeHolderContent);
if (!moduleParse(placeHolderContent, parsed)) {
parsed.push({ type: "placeholder", value: placeHolderContent });
}
Array.prototype.push.apply(parsed, tailParts);
tailParts = [];
return parsed;
}
placeHolderContent = "";
return parsed;
}
if (inPlaceHolder) {
if (token.type === "content" && token.position === "insidetag") {
placeHolderContent += token.value;
} else {
tailParts.push(token);
}
return parsed;
}
parsed.push(token);
return parsed;
}, []);
}
};
module.exports = parser;
},{"./doc-utils":1}],15:[function(require,module,exports){
"use strict";
var ScopeManager = require("./scope-manager");
var DocUtils = require("./doc-utils");
function moduleRender(part, options) {
var moduleRendered = void 0;
for (var i = 0, l = options.modules.length; i < l; i++) {
var _module = options.modules[i];
moduleRendered = _module.render(part, options);
if (moduleRendered) {
return moduleRendered;
}
}
return false;
}
function render(options) {
options.render = render;
options.modules = options.modules;
if (!options.scopeManager) {
options.scopeManager = ScopeManager.createBaseScopeManager(options);
}
return options.compiled.map(function (part) {
var moduleRendered = moduleRender(part, options);
if (moduleRendered) {
return moduleRendered.value;
}
if (part.type === "placeholder") {
var value = options.scopeManager.getValue(part.value);
if (value == null) {
value = options.nullGetter(part);
}
return DocUtils.utf8ToWord(value);
}
if (part.type === "content" || part.type === "tag") {
return part.value;
}
throw new Error("Unimplemented tag type \"" + part.type + "\"");
}).join("");
}
module.exports = render;
},{"./doc-utils":1,"./scope-manager":16}],16:[function(require,module,exports){
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Errors = require("./errors");
// This class responsibility is to manage the scope
var ScopeManager = function () {
function ScopeManager(options) {
_classCallCheck(this, ScopeManager);
this.scopePath = options.scopePath;
this.scopeList = options.scopeList;
this.parser = options.parser;
}
_createClass(ScopeManager, [{
key: "loopOver",
value: function loopOver(tag, callback, inverted) {
inverted = inverted || false;
return this.loopOverValue(this.getValue(tag), callback, inverted);
}
}, {
key: "functorIfInverted",
value: function functorIfInverted(inverted, functor, value) {
if (inverted) {
functor(value);
}
}
}, {
key: "isValueFalsy",
value: function isValueFalsy(value, type) {
return value == null || !value || type === "[object Array]" && value.length === 0;
}
}, {
key: "loopOverValue",
value: function loopOverValue(value, functor, inverted) {
var type = Object.prototype.toString.call(value);
var currentValue = this.scopeList[this.num];
if (this.isValueFalsy(value, type)) {
return this.functorIfInverted(inverted, functor, currentValue);
}
if (type === "[object Array]") {
for (var i = 0, scope; i < value.length; i++) {
scope = value[i];
this.functorIfInverted(!inverted, functor, scope);
}
return;
}
if (type === "[object Object]") {
return this.functorIfInverted(!inverted, functor, value);
}
if (value === true) {
return this.functorIfInverted(!inverted, functor, currentValue);
}
}
}, {
key: "getValue",
value: function getValue(tag, num) {
// search in the scopes (in reverse order) and keep the first defined value
this.num = num == null ? this.scopeList.length - 1 : num;
var err = void 0;
var parser = void 0;
var result = void 0;
var scope = this.scopeList[this.num];
try {
parser = this.parser(tag);
} catch (error) {
err = new Errors.XTScopeParserError("Scope parser compilation failed");
err.properties = {
id: "scopeparser_compilation_failed",
tag: tag,
explanation: "The scope parser for the tag " + tag + " failed to compile",
rootError: error
};
throw err;
}
try {
result = parser.get(scope, { num: this.num, scopeList: this.scopeList });
} catch (error) {
err = new Errors.XTScopeParserError("Scope parser execution failed");
err.properties = {
id: "scopeparser_execution_failed",
explanation: "The scope parser for the tag " + tag + " failed to execute",
scope: scope,
tag: tag,
rootError: error
};
throw err;
}
if (result == null && this.num > 0) {
return this.getValue(tag, this.num - 1);
}
return result;
}
}, {
key: "createSubScopeManager",
value: function createSubScopeManager(scope, tag) {
var options = {
scopePath: this.scopePath.slice(0),
scopeList: this.scopeList.slice(0)
};
options.parser = this.parser;
options.scopeList = this.scopeList.concat(scope);
options.scopePath = this.scopePath.concat(tag);
return new ScopeManager(options);
}
}]);
return ScopeManager;
}();
ScopeManager.createBaseScopeManager = function (_ref) {
var parser = _ref.parser,
tags = _ref.tags;
var options = { parser: parser, tags: tags };
options.scopePath = [];
options.scopeList = [tags];
return new ScopeManager(options);
};
module.exports = ScopeManager;
},{"./errors":3}],17:[function(require,module,exports){
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var testUtils = require("./utils");
var expect = testUtils.expect;
var JSZip = require("jszip");
var Docxtemplater = require("../docxtemplater.js");
var DocUtils = require("../doc-utils.js");
var _ = require("lodash");
var inspectModule = require("./inspect-module.js");
var expressions = require("angular-expressions");
function angularParser(tag) {
var expr = expressions.compile(tag);
return {
get: function get(scope) {
return expr(scope);
}
};
}
describe("DocxtemplaterBasis", function () {
it("should be defined", function () {
expect(Docxtemplater).not.to.be.equal(undefined);
});
it("should construct", function () {
var doc = new Docxtemplater();
expect(doc).not.to.be.equal(undefined);
});
});
function getLength(obj) {
if (obj instanceof ArrayBuffer) {
return obj.byteLength;
}
return obj.length;
}
describe("DocxtemplaterLoading", function () {
describe("ajax done correctly", function () {
it("doc and img Data should have the expected length", function () {
var doc = testUtils.createDoc("image-example.docx");
expect(getLength(doc.loadedContent)).to.be.equal(729580);
expect(getLength(testUtils.imageData["image.png"])).to.be.equal(18062);
});
it("should have the right number of files (the docx unzipped)", function () {
var doc = testUtils.createDoc("image-example.docx");
expect(DocUtils.sizeOfObject(doc.zip.files)).to.be.equal(16);
});
});
describe("basic loading", function () {
it("should load file image-example.docx", function () {
var doc = testUtils.createDoc("image-example.docx");
expect(typeof doc === "undefined" ? "undefined" : _typeof(doc)).to.be.equal("object");
});
});
describe("content_loading", function () {
it("should load the right content for the footer", function () {
var doc = testUtils.createDoc("image-example.docx");
var fullText = doc.getFullText("word/footer1.xml");
expect(fullText.length).not.to.be.equal(0);
expect(fullText).to.be.equal("{last_name}{first_name}{phone}");
});
it("should load the right content for the document", function () {
var doc = testUtils.createDoc("image-example.docx");
// default value document.xml
var fullText = doc.getFullText();
expect(fullText).to.be.equal("");
});
it("should load the right template files for the document", function () {
var doc = testUtils.createDoc("tag-example.docx");
var templatedFiles = doc.getTemplatedFiles();
expect(templatedFiles).to.be.eql(["word/header1.xml", "word/footer1.xml", "word/document.xml"]);
});
});
describe("output and input", function () {
it("should be the same", function () {
var zip = new JSZip(testUtils.createDoc("tag-example.docx").loadedContent);
var doc = new Docxtemplater().loadZip(zip);
var output = doc.getZip().generate({ type: "base64" });
expect(output.length).to.be.equal(90732);
expect(output.substr(0, 50)).to.be.equal("UEsDBAoAAAAAAAAAIQAMTxYSlgcAAJYHAAATAAAAW0NvbnRlbn");
});
});
});
describe("DocxtemplaterTemplating", function () {
describe("text templating", function () {
it("should change values with template data", function () {
var tags = {
first_name: "Hipp",
last_name: "Edgar",
phone: "0652455478",
description: "New Website"
};
var doc = testUtils.createDoc("tag-example.docx");
doc.setData(tags);
doc.render();
expect(doc.getFullText()).to.be.equal("Edgar Hipp");
expect(doc.getFullText("word/header1.xml")).to.be.equal("Edgar Hipp0652455478New Website");
expect(doc.getFullText("word/footer1.xml")).to.be.equal("EdgarHipp0652455478");
testUtils.shouldBeSame({ doc: doc, expectedName: "tag-example-expected.docx" });
});
});
});
describe("inspect module", function () {
function getTags(postParsed) {
return postParsed.filter(function (part) {
return part.type === "placeholder";
}).reduce(function (tags, part) {
tags[part.value] = {};
if (part.subparsed) {
tags[part.value] = getTags(part.subparsed);
}
return tags;
}, {});
}
var doc = testUtils.createDoc("tag-loop-example.docx");
var iModule = inspectModule();
doc.attachModule(iModule);
doc.render();
var postParsed = iModule.fullInspected["word/document.xml"].postparsed;
var tags = getTags(postParsed);
expect(tags).to.be.deep.equal({
offre: {
nom: {},
prix: {},
titre: {}
},
nom: {},
prenom: {}
});
});
describe("DocxtemplaterTemplatingForLoop", function () {
describe("textLoop templating", function () {
it("should replace all the tags", function () {
var tags = {
nom: "Hipp",
prenom: "Edgar",
telephone: "0652455478",
description: "New Website",
offre: [{ titre: "titre1", prix: "1250" }, { titre: "titre2", prix: "2000" }, { titre: "titre3", prix: "1400", nom: "Offre" }]
};
var doc = testUtils.createDoc("tag-loop-example.docx");
doc.setData(tags);
doc.render();
expect(doc.getFullText()).to.be.equal("Votre proposition commercialeHippPrix: 1250Titre titre1HippPrix: 2000Titre titre2OffrePrix: 1400Titre titre3HippEdgar");
});
it("should work with loops inside loops", function () {
var tags = { products: [{ title: "Microsoft", name: "DOS", reference: "Win7", avantages: [{ title: "Everyone uses it", proof: [{ reason: "it is quite cheap" }, { reason: "it is quit simple" }, { reason: "it works on a lot of different Hardware" }] }] }, { title: "Linux", name: "Ubuntu", reference: "Ubuntu10", avantages: [{ title: "It's very powerful", proof: [{ reason: "the terminal is your friend" }, { reason: "Hello world" }, { reason: "it's free" }] }] }, { title: "Apple", name: "Mac", reference: "OSX", avantages: [{ title: "It's very easy", proof: [{ reason: "you can do a lot just with the mouse" }, { reason: "It's nicely designed" }] }] }] };
var doc = testUtils.createDoc("tag-produit-loop.docx");
doc.setData(tags);
doc.render();
var text = doc.getFullText();
var expectedText = "MicrosoftProduct name : DOSProduct reference : Win7Everyone uses itProof that it works nicely : It works because it is quite cheap It works because it is quit simple It works because it works on a lot of different HardwareLinuxProduct name : UbuntuProduct reference : Ubuntu10It's very powerfulProof that it works nicely : It works because the terminal is your friend It works because Hello world It works because it's freeAppleProduct name : MacProduct reference : OSXIt's very easyProof that it works nicely : It works because you can do a lot just with the mouse It works because It's nicely designed";
expect(text.length).to.be.equal(expectedText.length);
expect(text).to.be.equal(expectedText);
});
it("should not have sideeffects with inverted with array length 3", function () {
var content = "<w:t>{^todos}No {/todos}Todos</w:t><w:t>{#todos}{.}{/todos}</w:t>";
var expectedContent = '<w:t>Todos</w:t><w:t xml:space="preserve">ABC</w:t>';
var scope = { todos: ["A", "B", "C"] };
var xmlTemplater = testUtils.createXmlTemplaterDocx(content, { tags: scope });
xmlTemplater.render();
expect(xmlTemplater.content).to.be.deep.equal(expectedContent);
});
it("should not have sideeffects with inverted with empty array", function () {
var content = "<w:t>{^todos}No {/todos}Todos</w:t>\n\t\t\t<w:t>{#todos}{.}{/todos}</w:t>";
var expectedContent = "<w:t>No Todos</w:t>\n\t\t\t<w:t xml:space=\"preserve\"></w:t>";