"use strict";

var _excluded = ["modules"];
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
var DocUtils = require("./doc-utils.js");
DocUtils.traits = require("./traits.js");
DocUtils.moduleWrapper = require("./module-wrapper.js");
var createScope = require("./scope-manager.js");
var _require = require("./errors.js"),
  throwMultiError = _require.throwMultiError,
  throwResolveBeforeCompile = _require.throwResolveBeforeCompile,
  throwRenderInvalidTemplate = _require.throwRenderInvalidTemplate,
  throwRenderTwice = _require.throwRenderTwice;
var logErrors = require("./error-logger.js");
var collectContentTypes = require("./collect-content-types.js");
var ctXML = "[Content_Types].xml";
var relsFile = "_rels/.rels";
var commonModule = require("./modules/common.js");
var Lexer = require("./lexer.js");
var defaults = DocUtils.defaults,
  str2xml = DocUtils.str2xml,
  xml2str = DocUtils.xml2str,
  moduleWrapper = DocUtils.moduleWrapper,
  concatArrays = DocUtils.concatArrays,
  uniq = DocUtils.uniq,
  stableSort = DocUtils.stableSort;
var _require2 = require("./errors.js"),
  XTInternalError = _require2.XTInternalError,
  throwFileTypeNotIdentified = _require2.throwFileTypeNotIdentified,
  throwFileTypeNotHandled = _require2.throwFileTypeNotHandled,
  throwApiVersionError = _require2.throwApiVersionError;
var currentModuleApiVersion = [3, 36, 0];
function dropUnsupportedFileTypesModules(dx) {
  dx.modules = dx.modules.filter(function (module) {
    if (module.supportedFileTypes) {
      if (!Array.isArray(module.supportedFileTypes)) {
        throw new Error("The supportedFileTypes field of the module must be an array");
      }
      var isSupportedModule = module.supportedFileTypes.indexOf(dx.fileType) !== -1;
      if (!isSupportedModule) {
        module.on("detached");
      }
      return isSupportedModule;
    }
    return true;
  });
}
var Docxtemplater = /*#__PURE__*/function () {
  function Docxtemplater(zip) {
    var _this = this;
    var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
      _ref$modules = _ref.modules,
      modules = _ref$modules === void 0 ? [] : _ref$modules,
      options = _objectWithoutProperties(_ref, _excluded);
    _classCallCheck(this, Docxtemplater);
    if (!Array.isArray(modules)) {
      throw new Error("The modules argument of docxtemplater's constructor must be an array");
    }
    this.targets = [];
    this.rendered = false;
    this.scopeManagers = {};
    this.compiled = {};
    this.modules = [commonModule()];
    this.setOptions(options);
    modules.forEach(function (module) {
      _this.attachModule(module);
    });
    if (arguments.length > 0) {
      if (!zip || !zip.files || typeof zip.file !== "function") {
        throw new Error("The first argument of docxtemplater's constructor must be a valid zip file (jszip v2 or pizzip v3)");
      }
      this.loadZip(zip);
      // remove the unsupported modules
      dropUnsupportedFileTypesModules(this);
      this.compile();
      this.v4Constructor = true;
    }
  }
  _createClass(Docxtemplater, [{
    key: "verifyApiVersion",
    value: function verifyApiVersion(neededVersion) {
      neededVersion = neededVersion.split(".").map(function (i) {
        return parseInt(i, 10);
      });
      if (neededVersion.length !== 3) {
        throwApiVersionError("neededVersion is not a valid version", {
          neededVersion: neededVersion,
          explanation: "the neededVersion must be an array of length 3"
        });
      }
      if (neededVersion[0] !== currentModuleApiVersion[0]) {
        throwApiVersionError("The major api version do not match, you probably have to update docxtemplater with npm install --save docxtemplater", {
          neededVersion: neededVersion,
          currentModuleApiVersion: currentModuleApiVersion,
          explanation: "moduleAPIVersionMismatch : needed=".concat(neededVersion.join("."), ", current=").concat(currentModuleApiVersion.join("."))
        });
      }
      if (neededVersion[1] > currentModuleApiVersion[1]) {
        throwApiVersionError("The minor api version is not uptodate, you probably have to update docxtemplater with npm install --save docxtemplater", {
          neededVersion: neededVersion,
          currentModuleApiVersion: currentModuleApiVersion,
          explanation: "moduleAPIVersionMismatch : needed=".concat(neededVersion.join("."), ", current=").concat(currentModuleApiVersion.join("."))
        });
      }
      if (neededVersion[1] === currentModuleApiVersion[1] && neededVersion[2] > currentModuleApiVersion[2]) {
        throwApiVersionError("The patch api version is not uptodate, you probably have to update docxtemplater with npm install --save docxtemplater", {
          neededVersion: neededVersion,
          currentModuleApiVersion: currentModuleApiVersion,
          explanation: "moduleAPIVersionMismatch : needed=".concat(neededVersion.join("."), ", current=").concat(currentModuleApiVersion.join("."))
        });
      }
      return true;
    }
  }, {
    key: "setModules",
    value: function setModules(obj) {
      this.modules.forEach(function (module) {
        module.set(obj);
      });
    }
  }, {
    key: "sendEvent",
    value: function sendEvent(eventName) {
      this.modules.forEach(function (module) {
        module.on(eventName);
      });
    }
  }, {
    key: "attachModule",
    value: function attachModule(module) {
      if (this.v4Constructor) {
        throw new XTInternalError("attachModule() should not be called manually when using the v4 constructor");
      }
      var moduleType = _typeof(module);
      if (moduleType === "function") {
        throw new XTInternalError("Cannot attach a class/function as a module. Most probably you forgot to instantiate the module by using `new` on the module.");
      }
      if (!module || moduleType !== "object") {
        throw new XTInternalError("Cannot attachModule with a falsy value");
      }
      if (module.requiredAPIVersion) {
        this.verifyApiVersion(module.requiredAPIVersion);
      }
      if (module.attached === true) {
        if (typeof module.clone === "function") {
          module = module.clone();
        } else {
          throw new Error("Cannot attach a module that was already attached : \"".concat(module.name, "\". The most likely cause is that you are instantiating the module at the root level, and using it for multiple instances of Docxtemplater"));
        }
      }
      module.attached = true;
      var wrappedModule = moduleWrapper(module);
      this.modules.push(wrappedModule);
      wrappedModule.on("attached");
      if (this.fileType) {
        dropUnsupportedFileTypesModules(this);
      }
      return this;
    }
  }, {
    key: "setOptions",
    value: function setOptions(options) {
      var _this2 = this;
      if (this.v4Constructor) {
        throw new Error("setOptions() should not be called manually when using the v4 constructor");
      }
      if (!options) {
        throw new Error("setOptions should be called with an object as first parameter");
      }
      this.options = {};
      Object.keys(defaults).forEach(function (key) {
        var defaultValue = defaults[key];
        _this2.options[key] = options[key] != null ? options[key] : defaultValue;
        _this2[key] = _this2.options[key];
      });
      this.delimiters.start = DocUtils.utf8ToWord(this.delimiters.start);
      this.delimiters.end = DocUtils.utf8ToWord(this.delimiters.end);
      return this;
    }
  }, {
    key: "loadZip",
    value: function loadZip(zip) {
      if (this.v4Constructor) {
        throw new Error("loadZip() should not be called manually when using the v4 constructor");
      }
      if (zip.loadAsync) {
        throw new XTInternalError("Docxtemplater doesn't handle JSZip version >=3, please use pizzip");
      }
      this.zip = zip;
      this.updateFileTypeConfig();
      this.modules = concatArrays([this.fileTypeConfig.baseModules.map(function (moduleFunction) {
        return moduleFunction();
      }), this.modules]);
      dropUnsupportedFileTypesModules(this);
      return this;
    }
  }, {
    key: "precompileFile",
    value: function precompileFile(fileName) {
      var currentFile = this.createTemplateClass(fileName);
      currentFile.preparse();
      this.compiled[fileName] = currentFile;
    }
  }, {
    key: "compileFile",
    value: function compileFile(fileName) {
      this.compiled[fileName].parse();
    }
  }, {
    key: "getScopeManager",
    value: function getScopeManager(to, currentFile, tags) {
      if (!this.scopeManagers[to]) {
        this.scopeManagers[to] = createScope({
          tags: tags,
          parser: this.parser,
          cachedParsers: currentFile.cachedParsers
        });
      }
      return this.scopeManagers[to];
    }
  }, {
    key: "resolveData",
    value: function resolveData(data) {
      var _this3 = this;
      var errors = [];
      if (!Object.keys(this.compiled).length) {
        throwResolveBeforeCompile();
      }
      return Promise.resolve(data).then(function (data) {
        _this3.setData(data);
        _this3.setModules({
          data: _this3.data,
          Lexer: Lexer
        });
        _this3.mapper = _this3.modules.reduce(function (value, module) {
          return module.getRenderedMap(value);
        }, {});
        return Promise.all(Object.keys(_this3.mapper).map(function (to) {
          var _this3$mapper$to = _this3.mapper[to],
            from = _this3$mapper$to.from,
            data = _this3$mapper$to.data;
          return Promise.resolve(data).then(function (data) {
            var currentFile = _this3.compiled[from];
            currentFile.filePath = to;
            currentFile.scopeManager = _this3.getScopeManager(to, currentFile, data);
            return currentFile.resolveTags(data).then(function (result) {
              currentFile.scopeManager.finishedResolving = true;
              return result;
            }, function (errs) {
              Array.prototype.push.apply(errors, errs);
            });
          });
        })).then(function (resolved) {
          if (errors.length !== 0) {
            if (_this3.options.errorLogging) {
              logErrors(errors, _this3.options.errorLogging);
            }
            throwMultiError(errors);
          }
          return concatArrays(resolved);
        });
      });
    }
  }, {
    key: "reorderModules",
    value: function reorderModules() {
      this.modules = stableSort(this.modules, function (m1, m2) {
        return (m2.priority || 0) - (m1.priority || 0);
      });
    }
  }, {
    key: "compile",
    value: function compile() {
      var _this4 = this;
      this.updateFileTypeConfig();
      this.reorderModules();
      if (Object.keys(this.compiled).length) {
        return this;
      }
      this.options = this.modules.reduce(function (options, module) {
        return module.optionsTransformer(options, _this4);
      }, this.options);
      this.options.xmlFileNames = uniq(this.options.xmlFileNames);
      this.xmlDocuments = this.options.xmlFileNames.reduce(function (xmlDocuments, fileName) {
        var content = _this4.zip.files[fileName].asText();
        xmlDocuments[fileName] = str2xml(content);
        return xmlDocuments;
      }, {});
      this.setModules({
        zip: this.zip,
        xmlDocuments: this.xmlDocuments
      });
      this.getTemplatedFiles();
      // Loop inside all templatedFiles (ie xml files with content).
      // Sometimes they don't exist (footer.xml for example)
      this.templatedFiles.forEach(function (fileName) {
        if (_this4.zip.files[fileName] != null) {
          _this4.precompileFile(fileName);
        }
      });
      this.templatedFiles.forEach(function (fileName) {
        if (_this4.zip.files[fileName] != null) {
          _this4.compileFile(fileName);
        }
      });
      this.setModules({
        compiled: this.compiled
      });
      verifyErrors(this);
      return this;
    }
  }, {
    key: "getRelsTypes",
    value: function getRelsTypes() {
      var rootRels = this.zip.files[relsFile];
      var rootRelsXml = rootRels ? str2xml(rootRels.asText()) : null;
      var rootRelationships = rootRelsXml ? rootRelsXml.getElementsByTagName("Relationship") : [];
      var relsTypes = {};
      for (var i = 0, len = rootRelationships.length; i < len; i++) {
        var r = rootRelationships[i];
        relsTypes[r.getAttribute("Target")] = r.getAttribute("Type");
      }
      return relsTypes;
    }
  }, {
    key: "getContentTypes",
    value: function getContentTypes() {
      var contentTypes = this.zip.files[ctXML];
      var contentTypeXml = contentTypes ? str2xml(contentTypes.asText()) : null;
      var overrides = contentTypeXml ? contentTypeXml.getElementsByTagName("Override") : null;
      var defaults = contentTypeXml ? contentTypeXml.getElementsByTagName("Default") : null;
      return {
        overrides: overrides,
        defaults: defaults,
        contentTypes: contentTypes,
        contentTypeXml: contentTypeXml
      };
    }
  }, {
    key: "updateFileTypeConfig",
    value: function updateFileTypeConfig() {
      var _this5 = this;
      var fileType;
      if (this.zip.files.mimetype) {
        fileType = "odt";
      }
      this.relsTypes = this.getRelsTypes();
      var _this$getContentTypes = this.getContentTypes(),
        overrides = _this$getContentTypes.overrides,
        defaults = _this$getContentTypes.defaults,
        contentTypes = _this$getContentTypes.contentTypes,
        contentTypeXml = _this$getContentTypes.contentTypeXml;
      if (contentTypeXml) {
        this.filesContentTypes = collectContentTypes(overrides, defaults, this.zip);
        this.invertedContentTypes = DocUtils.invertMap(this.filesContentTypes);
        this.setModules({
          contentTypes: this.contentTypes,
          invertedContentTypes: this.invertedContentTypes
        });
      }
      this.modules.forEach(function (module) {
        fileType = module.getFileType({
          zip: _this5.zip,
          contentTypes: contentTypes,
          contentTypeXml: contentTypeXml,
          overrides: overrides,
          defaults: defaults,
          doc: _this5
        }) || fileType;
      });
      if (fileType === "odt") {
        throwFileTypeNotHandled(fileType);
      }
      if (!fileType) {
        throwFileTypeNotIdentified();
      }
      this.fileType = fileType;
      dropUnsupportedFileTypesModules(this);
      this.fileTypeConfig = this.options.fileTypeConfig || this.fileTypeConfig || Docxtemplater.FileTypeConfig[this.fileType]();
      return this;
    }
  }, {
    key: "renderAsync",
    value: function renderAsync(data) {
      var _this6 = this;
      return this.resolveData(data).then(function () {
        return _this6.render();
      });
    }
  }, {
    key: "render",
    value: function render(data) {
      var _this7 = this;
      if (this.rendered) {
        throwRenderTwice();
      }
      this.rendered = true;
      this.compile();
      if (this.errors.length > 0) {
        throwRenderInvalidTemplate();
      }
      if (data) {
        this.setData(data);
      }
      this.setModules({
        data: this.data,
        Lexer: Lexer
      });
      this.mapper = this.mapper || this.modules.reduce(function (value, module) {
        return module.getRenderedMap(value);
      }, {});
      Object.keys(this.mapper).forEach(function (to) {
        var _this7$mapper$to = _this7.mapper[to],
          from = _this7$mapper$to.from,
          data = _this7$mapper$to.data;
        var currentFile = _this7.compiled[from];
        currentFile.scopeManager = _this7.getScopeManager(to, currentFile, data);
        currentFile.render(to);
        _this7.zip.file(to, currentFile.content, {
          createFolders: true
        });
      });
      verifyErrors(this);
      this.sendEvent("syncing-zip");
      this.syncZip();
      return this;
    }
  }, {
    key: "syncZip",
    value: function syncZip() {
      var _this8 = this;
      Object.keys(this.xmlDocuments).forEach(function (fileName) {
        _this8.zip.remove(fileName);
        var content = xml2str(_this8.xmlDocuments[fileName]);
        return _this8.zip.file(fileName, content, {
          createFolders: true
        });
      });
    }
  }, {
    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 content = this.zip.files[path].asText();
      return this.createTemplateClassFromContent(content, path);
    }
  }, {
    key: "createTemplateClassFromContent",
    value: function createTemplateClassFromContent(content, filePath) {
      var _this9 = this;
      var xmltOptions = {
        filePath: filePath,
        contentType: this.filesContentTypes[filePath],
        relsType: this.relsTypes[filePath]
      };
      Object.keys(defaults).concat(["filesContentTypes", "fileTypeConfig", "fileType", "modules"]).forEach(function (key) {
        xmltOptions[key] = _this9[key];
      });
      return new Docxtemplater.XmlTemplater(content, xmltOptions);
    }
  }, {
    key: "getFullText",
    value: function getFullText(path) {
      return this.createTemplateClass(path || this.fileTypeConfig.textPath(this)).getFullText();
    }
  }, {
    key: "getTemplatedFiles",
    value: function getTemplatedFiles() {
      var _this10 = this;
      this.templatedFiles = this.fileTypeConfig.getTemplatedFiles(this.zip);
      this.targets.forEach(function (target) {
        _this10.templatedFiles.push(target);
      });
      this.templatedFiles = uniq(this.templatedFiles);
      return this.templatedFiles;
    }
  }]);
  return Docxtemplater;
}();
function verifyErrors(doc) {
  var compiled = doc.compiled;
  doc.errors = concatArrays(Object.keys(compiled).map(function (name) {
    return compiled[name].allErrors;
  }));
  if (doc.errors.length !== 0) {
    if (doc.options.errorLogging) {
      logErrors(doc.errors, doc.options.errorLogging);
    }
    throwMultiError(doc.errors);
  }
}
Docxtemplater.DocUtils = DocUtils;
Docxtemplater.Errors = require("./errors.js");
Docxtemplater.XmlTemplater = require("./xml-templater.js");
Docxtemplater.FileTypeConfig = require("./file-type-config.js");
Docxtemplater.XmlMatcher = require("./xml-matcher.js");
module.exports = Docxtemplater;