UNPKG

docxtemplater

Version:

.docx generator working with templates and data (like Mustache)

1,560 lines (1,410 loc) 1.3 MB
(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 = { "&": "&amp;", "'": "&apos;", "<": "&lt;", ">": "&gt;" }; 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>";