docxtemplater/es6/scope-manager.js
2021-09-04 10:15:02 +05:00

209 lines
5.0 KiB
JavaScript

"use strict";
const { getScopeParserExecutionError } = require("./errors");
const { last } = require("./utils");
const { concatArrays } = require("./doc-utils");
function find(list, fn) {
const length = list.length >>> 0;
let value;
for (let i = 0; i < length; i++) {
value = list[i];
if (fn.call(this, value, i, list)) {
return value;
}
}
return undefined;
}
function getValue(tag, meta, num) {
const scope = this.scopeList[num];
if (this.resolved) {
let w = this.resolved;
this.scopePath.forEach((p, index) => {
const lIndex = this.scopeLindex[index];
w = find(w, function (r) {
return r.lIndex === lIndex;
});
w = w.value[this.scopePathItem[index]];
});
return [
this.scopePath.length - 1,
find(w, function (r) {
return meta.part.lIndex === r.lIndex;
}).value,
];
}
// search in the scopes (in reverse order) and keep the first defined value
let result;
let parser;
if (!this.cachedParsers || !meta.part) {
parser = this.parser(tag, {
scopePath: this.scopePath,
});
} else if (this.cachedParsers[meta.part.lIndex]) {
parser = this.cachedParsers[meta.part.lIndex];
} else {
parser = this.cachedParsers[meta.part.lIndex] = this.parser(tag, {
scopePath: this.scopePath,
});
}
try {
result = parser.get(scope, this.getContext(meta, num));
} catch (error) {
throw getScopeParserExecutionError({
tag,
scope,
error,
offset: meta.part.offset,
});
}
if (result == null && num > 0) {
return getValue.call(this, tag, meta, num - 1);
}
return [num, result];
}
function getValueAsync(tag, meta, num) {
const scope = this.scopeList[num];
// search in the scopes (in reverse order) and keep the first defined value
let parser;
if (!this.cachedParsers || !meta.part) {
parser = this.parser(tag, {
scopePath: this.scopePath,
});
} else if (this.cachedParsers[meta.part.lIndex]) {
parser = this.cachedParsers[meta.part.lIndex];
} else {
parser = this.cachedParsers[meta.part.lIndex] = this.parser(tag, {
scopePath: this.scopePath,
});
}
return Promise.resolve()
.then(() => {
return parser.get(scope, this.getContext(meta, num));
})
.catch(function (error) {
throw getScopeParserExecutionError({
tag,
scope,
error,
offset: meta.part.offset,
});
})
.then((result) => {
if (result == null && num > 0) {
return getValueAsync.call(this, tag, meta, num - 1);
}
return result;
});
}
// This class responsibility is to manage the scope
const ScopeManager = class ScopeManager {
constructor(options) {
this.scopePath = options.scopePath;
this.scopePathItem = options.scopePathItem;
this.scopePathLength = options.scopePathLength;
this.scopeList = options.scopeList;
this.scopeLindex = options.scopeLindex;
this.parser = options.parser;
this.resolved = options.resolved;
this.cachedParsers = options.cachedParsers;
}
loopOver(tag, functor, inverted, meta) {
return this.loopOverValue(this.getValue(tag, meta), functor, inverted);
}
functorIfInverted(inverted, functor, value, i, length) {
if (inverted) {
functor(value, i, length);
}
return inverted;
}
isValueFalsy(value, type) {
return (
value == null ||
!value ||
(type === "[object Array]" && value.length === 0)
);
}
loopOverValue(value, functor, inverted) {
if (this.resolved) {
inverted = false;
}
const type = Object.prototype.toString.call(value);
if (this.isValueFalsy(value, type)) {
return this.functorIfInverted(
inverted,
functor,
last(this.scopeList),
0,
1
);
}
if (type === "[object Array]") {
for (let i = 0; i < value.length; i++) {
this.functorIfInverted(!inverted, functor, value[i], i, value.length);
}
return true;
}
if (type === "[object Object]") {
return this.functorIfInverted(!inverted, functor, value, 0, 1);
}
return this.functorIfInverted(
!inverted,
functor,
last(this.scopeList),
0,
1
);
}
getValue(tag, meta) {
const [num, result] = getValue.call(
this,
tag,
meta,
this.scopeList.length - 1
);
this.num = num;
return result;
}
getValueAsync(tag, meta) {
return getValueAsync.call(this, tag, meta, this.scopeList.length - 1);
}
getContext(meta, num) {
return {
num,
meta,
scopeList: this.scopeList,
resolved: this.resolved,
scopePath: this.scopePath,
scopePathItem: this.scopePathItem,
scopePathLength: this.scopePathLength,
};
}
createSubScopeManager(scope, tag, i, part, length) {
return new ScopeManager({
resolved: this.resolved,
parser: this.parser,
cachedParsers: this.cachedParsers,
scopeList: concatArrays([this.scopeList, [scope]]),
scopePath: concatArrays([this.scopePath, [tag]]),
scopePathItem: concatArrays([this.scopePathItem, [i]]),
scopePathLength: concatArrays([this.scopePathLength, [length]]),
scopeLindex: concatArrays([this.scopeLindex, [part.lIndex]]),
});
}
};
module.exports = function (options) {
options.scopePath = [];
options.scopePathItem = [];
options.scopePathLength = [];
options.scopeLindex = [];
options.scopeList = [options.tags];
return new ScopeManager(options);
};