• operators.js
  • class ConditionOperator {
        id = undefined;
        caption = undefined;
    
        /**
         * @type {mixed} variable
         * @type {mixed} value
         */
        test(variable, value) {
            throw new Error("You need to override test()");
        }
    }
    
    const OP_STR_EQUALS = "equals";
    
    const OP_STR_NOT_EQUALS = "not_equals";
    
    const OP_CONTAINS = "contains";
    
    const OP_NOT_CONTAINS = "not_contains";
    
    const OP_STARTS_WITH = "starts_with";
    
    const OP_NOT_STARTS_WITH = "not_starts_with";
    
    const OP_ENDS_WITH = "ends_with";
    
    const OP_NOT_ENDS_WITH = "not_ends_with";
    
    const OP_NUM_EQUALS = "num_equals";
    
    const OP_NUM_NOT_EQUALS = "num_not_equals";
    
    const OP_LT = "lt";
    
    const OP_GT = "gt";
    
    const OP_LTE = "lte";
    
    const OP_GTE = "gte";
    
    /**
     * Asserts a variable is equal to a value.
     */
    class StringEqualsOperator extends ConditionOperator {
        id = OP_STR_EQUALS;
        caption = "equals";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable === value;
        }
    }
    
    /**
     * Asserts a variable is not equal to a value.
     */
    class StringNotEqualsOperator extends ConditionOperator {
        id = OP_STR_NOT_EQUALS;
        caption = "not equals";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable !== value;
        }
    }
    
    /**
     * Asserts a variable contains a value.
     * Handles both Array and String variable types.
     */
    class ContainsOperator extends ConditionOperator {
        id = OP_CONTAINS;
        caption = "contains";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            if (Array.isArray(variable)) {
                return variable.includes(value);
            }
    
            return variable.indexOf(value) !== -1;
        }
    }
    
    /**
     * Asserts a variable does not contain a value.
     * Handles both Array and String variable types.
     */
    class NotContainsOperator extends ConditionOperator {
        id = OP_NOT_CONTAINS;
        caption = "not contains";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            if (Array.isArray(variable)) {
                return !variable.includes(value);
            }
    
            return variable.indexOf(value) === -1;
        }
    }
    
    /**
     * Asserts a variable starts with a value.
     */
    class StartsWithOperator extends ConditionOperator {
        id = OP_STARTS_WITH;
        caption = "starts with";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable.startsWith(value);
        }
    }
    
    /**
     * Asserts a variable does not start with a value.
     */
    class NotStartsWithOperator extends ConditionOperator {
        id = OP_NOT_STARTS_WITH;
        caption = "not starts with";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return !variable.startsWith(value);
        }
    }
    
    /**
     * Asserts a variable ends with a value.
     */
    class EndsWithOperator extends ConditionOperator {
        id = OP_ENDS_WITH;
        caption = "ends with";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable.endsWith(value);
        }
    }
    
    /**
     * Asserts a variable does not end with a value.
     */
    class NotEndsWithOperator extends ConditionOperator {
        id = OP_NOT_ENDS_WITH;
        caption = "not ends with";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return !variable.endsWith(value);
        }
    }
    
    /**
     * Asserts a numeric variable is equal to a value.
     */
    class NumberEqualsOperator extends ConditionOperator {
        id = OP_NUM_EQUALS;
        caption = "equals";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable === Number(value);
        }
    }
    
    /**
     * Asserts a numeric variable is not equal to a value.
     */
    class NumberNotEqualsOperator extends ConditionOperator {
        id = OP_NUM_NOT_EQUALS;
        caption = "not equals";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable !== Number(value);
        }
    }
    
    /**
     * Asserts a variable is less than a value.
     */
    class LessThanOperator extends ConditionOperator {
        id = OP_LT;
        caption = "less than";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable < Number(value);
        }
    }
    
    /**
     * Asserts a variable is greater than a value.
     */
    class GreaterThanOperator extends ConditionOperator {
        id = OP_GT;
        caption = "greater than";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable > Number(value);
        }
    }
    
    /**
     * Asserts a variable is less than or equal to a value.
     */
    class LessThanOrEqualToOperator extends ConditionOperator {
        id = OP_LTE;
        caption = "less than or equal to";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable <= Number(value);
        }
    }
    
    /**
     * Asserts a variable is greater than or equal to a value.
     */
    class GreaterThanOrEqualToOperator extends ConditionOperator {
        id = OP_GTE;
        caption = "greater than or equal to";
    
        /**
         * @inheritdoc
         */
        test(variable, value) {
            return variable >= Number(value);
        }
    }
    
    const operatorMap = new Map([
        [ OP_STR_EQUALS, new StringEqualsOperator ],
        [ OP_STR_NOT_EQUALS, new StringNotEqualsOperator ],
        [ OP_CONTAINS, new ContainsOperator ],
        [ OP_NOT_CONTAINS, new NotContainsOperator ],
        [ OP_STARTS_WITH, new StartsWithOperator ],
        [ OP_NOT_STARTS_WITH, new NotStartsWithOperator ],
        [ OP_ENDS_WITH, new EndsWithOperator ],
        [ OP_NOT_ENDS_WITH, new NotEndsWithOperator ],
        [ OP_NUM_EQUALS, new NumberEqualsOperator ],
        [ OP_NUM_NOT_EQUALS, new NumberNotEqualsOperator ],
        [ OP_LT, new LessThanOperator ],
        [ OP_GT, new GreaterThanOperator ],
        [ OP_LTE, new LessThanOrEqualToOperator ],
        [ OP_GTE, new GreaterThanOrEqualToOperator ],
    ]);
    
    const defaultStringOperators = [
        operatorMap.get(OP_STR_EQUALS),
        operatorMap.get(OP_STR_NOT_EQUALS),
        operatorMap.get(OP_CONTAINS),
        operatorMap.get(OP_NOT_CONTAINS),
        operatorMap.get(OP_STARTS_WITH),
        operatorMap.get(OP_NOT_STARTS_WITH),
        operatorMap.get(OP_ENDS_WITH),
        operatorMap.get(OP_NOT_ENDS_WITH)
    ];
    
    const defaultNumberOperators = [
        operatorMap.get(OP_NUM_EQUALS),
        operatorMap.get(OP_NUM_NOT_EQUALS),
        operatorMap.get(OP_LT),
        operatorMap.get(OP_GT),
        operatorMap.get(OP_LTE),
        operatorMap.get(OP_GTE)
    ];
    
    module.exports = {
        OP_STR_EQUALS,
        OP_STR_NOT_EQUALS,
        OP_CONTAINS,
        OP_NOT_CONTAINS,
        OP_STARTS_WITH,
        OP_NOT_STARTS_WITH,
        OP_ENDS_WITH,
        OP_NOT_ENDS_WITH,
        OP_NUM_EQUALS,
        OP_NUM_NOT_EQUALS,
        OP_LT,
        OP_GT,
        OP_LTE,
        OP_GTE,
        operatorMap,
        defaultStringOperators,
        defaultNumberOperators,
        ConditionOperator,
    };