bpmn-elements
Version:
Executable workflow elements based on BPMN 2.0
140 lines (117 loc) • 3.37 kB
JavaScript
const propertyPattern = /(\w+)\((.*?)(?:\))|(\.|\[|^)(.+?)(?:\]|\[|\.|$)/;
const stringConstantPattern = /^(['"])(.*)\1$/;
const numberConstantPattern = /^\W*-?\d+(.\d+)?\W*$/;
const negativeIndexPattern = /^-\d+$/;
export default function getPropertyValue(inputContext, propertyPath, fnScope) {
if (!inputContext) return;
let resultValue;
let next = iterateProps(inputContext, inputContext, propertyPath.trim(), fnScope);
while (next) {
resultValue = next.getResult();
next = next();
}
return resultValue;
}
function iterateProps(base, iterateContext, iteratePropertyPath, fnScope) {
let result;
const rest = iteratePropertyPath.replace(propertyPattern, (match, fnName, args, p, prop) => {
if (fnName) {
result = executeFn(getNamedValue(iterateContext, fnName), args, base, fnScope);
} else {
result = getNamedValue(iterateContext, prop);
}
return '';
});
if (rest === iteratePropertyPath) return;
if (result === undefined || result === null) return;
const iterateNext = () => iterateProps(base, result, rest, fnScope);
iterateNext.getResult = () => {
if (rest !== '') return;
return result;
};
return iterateNext;
}
function executeFn(fn, args, base, fnScope) {
if (!fn) return;
let callArguments = [];
if (args) {
callArguments = splitArguments(args, base, fnScope);
} else {
callArguments.push(base);
}
if (!fnScope) return fn.apply(null, callArguments);
return function ScopedIIFE() {
return fn.apply(this, callArguments);
}.call(fnScope);
}
function splitArguments(args, base, fnScope) {
let insideString = false;
let delimiter = '';
let argCompleted = false;
let arg = '';
const callArguments = [];
for (let i = 0; i < args.length; i++) {
const charPos = args.charAt(i);
if (!insideString) {
if (charPos === ',') {
argCompleted = true;
} else if (charPos !== ' ') {
arg += charPos;
if (charPos === "'" || charPos === '"') {
insideString = true;
delimiter = charPos;
}
}
} else {
arg += charPos;
if (charPos === delimiter) {
argCompleted = true;
delimiter = '';
}
}
if (argCompleted) {
if (arg.length) {
callArguments.push(getFunctionArgument(base, arg.trim(), fnScope));
}
arg = '';
insideString = false;
argCompleted = false;
}
}
if (arg.trim() !== '') {
callArguments.push(getFunctionArgument(base, arg.trim(), fnScope));
}
return callArguments;
}
function getFunctionArgument(obj, argument, fnScope) {
const stringMatch = argument.match(stringConstantPattern);
if (stringMatch) {
return stringMatch[2];
} else if (numberConstantPattern.test(argument)) {
return Number(argument);
}
switch (argument) {
case 'true':
return true;
case 'false':
return false;
case 'null':
return null;
default:
return getPropertyValue(obj, argument, fnScope);
}
}
function getNamedValue(obj, property) {
if (Array.isArray(obj)) {
return getArrayItem(obj, property);
}
return obj[property];
}
function getArrayItem(list, idx) {
if (negativeIndexPattern.test(idx)) {
const nidx = Number(idx);
const aidx = nidx === 0 ? 0 : list.length + nidx;
return list[aidx];
}
return list[idx];
}