UNPKG

node-red-contrib-chronos

Version:

Time-based Node-RED scheduling, repeating, queueing, routing, filtering and manipulating nodes

799 lines (736 loc) 67.1 kB
<!-- Copyright (c) 2020 - 2025 Jens-Uwe Rossbach This code is licensed under the MIT License. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> <script type="text/html" data-template-name="chronos-filter"> <style> a:focus:not(:focus-visible) { outline: none; } </style> <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red-contrib-chronos/chronos-config:common.label.name"></span></label> <input type="text" id="node-input-name" data-i18n="[placeholder]node-red-contrib-chronos/chronos-config:common.label.name"> </div> <div class="form-row"> <label for="node-input-config"><i class="fa fa-cog"></i> <span data-i18n="node-red-contrib-chronos/chronos-config:common.label.config"></span></label> <input type="text" id="node-input-config" data-i18n="[placeholder]node-red-contrib-chronos/chronos-config:common.label.config"> </div> <div class="form-row" style="padding-top: 4px;"> <label for="node-input-baseTime"><i class="fa fa-clock-o"></i> <span data-i18n="node-red-contrib-chronos/chronos-config:common.label.baseTime"></span></label> <input type="text" id="node-input-baseTime" style="width: 70%;"> <input id="node-input-baseTimeType" type="hidden"> </div> <div class="form-row"> <label for="node-input-evaluation"><i class="fa fa-filter"></i> <span data-i18n="node-red-contrib-chronos/chronos-config:common.label.evaluation"></span></label> <input type="text" id="node-input-evaluation" style="width: 70%;"> <input id="node-input-evaluationType" type="hidden"> </div> <div class="form-row node-input-conditionList-row" style="padding-top: 10px"> <label for="node-input-conditionList"><i class="fa fa-sliders"></i> <span data-i18n="node-red-contrib-chronos/chronos-config:common.label.conditions"></span></label> <div class="form-row"> <ol id="node-input-conditionList"></ol> </div> </div> </script> <script type="text/javascript"> (function() { const PATTERN_DATETIME = /^(?:(?:(?:(?:[1-9]\d)?\d\d)-(?:[1-9]|0[1-9]|1[0-2])-(?:[1-9]|0[1-9]|[12]\d|3[01])|(?:(?:[1-9]|0[1-9]|[12]\d|3[01])\.(?:[1-9]|0[1-9]|1[0-2])\.(?:(?:[1-9]\d)?\d\d))|(?:(?:[1-9]|0[1-9]|1[0-2])\/(?:[1-9]|0[1-9]|[12]\d|3[01])\/(?:(?:[1-9]\d)?\d\d)))\s)?(?:\d|0\d|1\d|2[0-3]):(?:[0-5]\d)(?::(?:[0-5]\d))?(?:\s(?:a|am|A|AM|p|pm|P|PM))?$/; const PATTERN_CUSTOM_TIME = /^[a-zA-Z][0-9a-zA-Z_]*$/; const months = [ "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ]; const weekdays = [ "sunday", // moment.js uses US order -> Sunday is first day of the week "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ]; RED.nodes.registerType("chronos-filter", { category: "chronos", color: "#DEB887", icon: "chronos_filter.svg", inputs: 1, outputs: 1, paletteLabel: "time filter", label: function() { return (this.name || this._("filter.label.node")); }, labelStyle: function() { return (this.name ? "node_label_italic" : ""); }, inputLabels: function() { return this._("node-red-contrib-chronos/chronos-config:common.label.inputPort"); }, outputLabels: function(index) { return this._("filter.label.outputPort"); }, defaults: { name: { value: "" }, config: { value: "", type: "chronos-config", required: true }, baseTime: { value: "", validate: RED.validators.typedInput("baseTimeType") }, baseTimeType: { value: "msgIngress" }, evaluation: { value: "", validate: RED.validators.typedInput("evaluationType") }, evaluationType: { value: "or" }, conditions: { value: [{operator: "before", operands: {type: "time", value: ""}}], validate: function(items, opt) { if (!items || (items.length == 0)) { return this._("node-red-contrib-chronos/chronos-config:common.error.noConditions"); } const errors = []; for (const item of items) { if (/^equal|notEqual|before|until|since|after$/.test(item.operator)) { if ((item.operands.type == "time") && !PATTERN_DATETIME.test(item.operands.value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidTime")); } else if ((item.operands.type == "custom") && !PATTERN_CUSTOM_TIME.test(item.operands.value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidName")); } else if (/^env|global|flow|msg$/.test(item.operands.type)) { const res = RED.utils.validateTypedProperty(item.operands.value, item.operands.type, opt) if (res !== true) { errors.push(res); } } } else if (/^between|outside$/.test(item.operator)) { if ((item.operands[0].type == "time") && !PATTERN_DATETIME.test(item.operands[0].value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidTime")); } else if ((item.operands[0].type == "custom") && !PATTERN_CUSTOM_TIME.test(item.operands[0].value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidName")); } else if (/^env|global|flow|msg$/.test(item.operands[0].type)) { const res = RED.utils.validateTypedProperty(item.operands[0].value, item.operands[0].type, opt) if (res !== true) { errors.push(res); } } if ((item.operands[1].type == "time") && !PATTERN_DATETIME.test(item.operands[1].value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidTime")); } else if ((item.operands[1].type == "custom") && !PATTERN_CUSTOM_TIME.test(item.operands[1].value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidName")); } else if (/^env|global|flow|msg$/.test(item.operands[1].type)) { const res = RED.utils.validateTypedProperty(item.operands[1].value, item.operands[1].type, opt) if (res !== true) { errors.push(res); } } } else if (item.operator == "expression") { const res = RED.utils.validateTypedProperty(item.expression, "jsonata", opt) if (res !== true) { errors.push(res); } } else if (item.operator == "context") { const res = RED.utils.validateTypedProperty(item.context.value, item.context.type, opt) if (res !== true) { errors.push(res); } } } return (errors.length > 0) ? errors : true; } } }, oneditprepare: function() { let node = this; const msgIngressInput = { value: "msgIngress", label: node._("node-red-contrib-chronos/chronos-config:common.label.msgIngress"), hasValue: false }; const logicalOrInput = { value: "or", label: node._("node-red-contrib-chronos/chronos-config:common.list.evaluation.logicalOr"), hasValue: false }; const logicalAndInput = { value: "and", label: node._("node-red-contrib-chronos/chronos-config:common.list.evaluation.logicalAnd"), hasValue: false }; const annotationInput = { value: "annotation", label: node._("filter.list.evaluation.annotation"), hasValue: false }; let baseTime = $("#node-input-baseTime") .typedInput({types: [msgIngressInput, "global", "flow", "msg"], typeField: "#node-input-baseTimeType"}); let evaluation = $("#node-input-evaluation") .typedInput({types: [logicalOrInput, logicalAndInput, annotationInput, "jsonata"], typeField: "#node-input-evaluationType"}); let conditionList = $("#node-input-conditionList").css("min-width", "535px").css("min-height", "150px").editableList( { removable: true, sortable: true, addItem: function(item, index, data) { const sunTimes = [ { value: "nightEnd", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.nightEnd") }, { value: "nauticalDawn", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.nauticalDawn") }, { value: "dawn", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.dawn") }, { value: "sunrise", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.sunrise") }, { value: "sunriseEnd", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.sunriseEnd") }, { value: "goldenHourEnd", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.goldenHourEnd") }, { value: "solarNoon", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.solarNoon") }, { value: "goldenHour", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.goldenHour") }, { value: "sunsetStart", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.sunsetStart") }, { value: "sunset", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.sunset") }, { value: "dusk", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.dusk") }, { value: "nauticalDusk", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.nauticalDusk") }, { value: "night", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.night") }, { value: "nadir", label: node._("node-red-contrib-chronos/chronos-config:common.list.sun.nadir") } ]; const moonTimes = [ { value: "rise", label: node._("node-red-contrib-chronos/chronos-config:common.list.moon.rise") }, { value: "set", label: node._("node-red-contrib-chronos/chronos-config:common.list.moon.set") } ]; const timeInput = { value: "time", label: node._("node-red-contrib-chronos/chronos-config:common.label.time"), icon: "fa fa-clock-o", hasValue: true, validate: PATTERN_DATETIME }; const sunTimeInput = { value: "sun", label: node._("node-red-contrib-chronos/chronos-config:common.label.sun"), icon: "fa fa-sun-o", options: sunTimes }; const moonTimeInput = { value: "moon", label: node._("node-red-contrib-chronos/chronos-config:common.label.moon"), icon: "fa fa-moon-o", options: moonTimes }; const customInput = { value: "custom", label: node._("node-red-contrib-chronos/chronos-config:common.label.custom"), icon: "fa fa-user-o", hasValue: true, validate: PATTERN_CUSTOM_TIME }; const validateNumericInput = function(event, ui) { const value = parseInt($(this).spinner("value"), 10); const min = $(this).spinner("option", "min"); const max = $(this).spinner("option", "max"); if (isNaN(value) || (value < min)) { $(this).spinner("value", min); } else if (value > max) { $(this).spinner("value", max); } }; const offsetInput = { min: -300, max: 300, step: 1, change: validateNumericInput }; const randomInput = { min: 0, max: 300, step: 1, change: validateNumericInput }; const fixedDayInput = { min: 1, max: 31, step: 1, change: validateNumericInput }; const fragment = document.createDocumentFragment(); const operatorBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto;"}).appendTo(fragment); const timeBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 2px;"}).appendTo(fragment); const daysBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 2px;"}).appendTo(fragment); const weekdaysBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 2px;"}).appendTo(fragment); const monthsBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 2px;"}).appendTo(fragment); const expressionBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 6px;"}).appendTo(fragment); const contextBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 6px;"}).appendTo(fragment); const operator = $("<select/>", {class: "node-input-operator", style: "width: auto;"}) .appendTo(operatorBox); const operGrp = $("<optgroup></optgroup>").attr("label", node._("node-red-contrib-chronos/chronos-config:common.list.condition.operators")) .append($("<option></option>").val("equal").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.equal"))) .append($("<option></option>").val("notEqual").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.notEqual"))) .append($("<option></option>").val("before").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.before"))) .append($("<option></option>").val("until").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.until"))) .append($("<option></option>").val("since").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.since"))) .append($("<option></option>").val("after").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.after"))) .append($("<option></option>").val("between").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.between"))) .append($("<option></option>").val("outside").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.outside"))) .append($("<option></option>").val("days").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.days"))) .append($("<option></option>").val("weekdays").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.weekdays"))) .append($("<option></option>").val("months").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.months"))) .appendTo(operator); const fromGrp = $("<optgroup></optgroup>").attr("label", node._("node-red-contrib-chronos/chronos-config:common.list.condition.sources")) .append($("<option></option>").val("expression").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.source.expression"))) .append($("<option></option>").val("context").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.source.context"))) .appendTo(operator); const time1Box = $("<div/>", {style: "margin-left: 4px;"}).appendTo(timeBox); const time2Box = $("<div/>", {style: "margin-left: 4px; margin-top: 5px;"}).appendTo(timeBox); const time1Row1 = $("<div/>").appendTo(time1Box); const time1Row2 = $("<div/>", {style: "margin-top: 5px;"}).appendTo(time1Box); const time2Row1 = $("<div/>").appendTo(time2Box); const time2Row2 = $("<div/>", {style: "margin-top: 5px;"}).appendTo(time2Box); const monthsRow1 = $("<div/>").appendTo(monthsBox); const monthsRow2 = $("<div/>", {style: "margin-top: 5px;"}).appendTo(monthsBox); const monthsRow3 = $("<div/>", {style: "margin-top: 5px;"}).appendTo(monthsBox); const time1 = $("<input/>", {type: "text", class: "node-input-time1"}) .appendTo(time1Row1) .typedInput({types: [timeInput, sunTimeInput, moonTimeInput, customInput, "env", "global", "flow", "msg"]}); time1.typedInput("width", "315px"); time1._prevType = "time"; const extend1 = $("<a/>", {href: "#", title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.expand"), style: "margin-left: 6px;"}) .html("<i class='fa fa-angle-right' aria-hidden='true'></i>") .appendTo(time1Row1); const time2 = $("<input/>", {type: "text", class: "node-input-time2"}) .appendTo(time2Row1) .typedInput({types: [timeInput, sunTimeInput, moonTimeInput, customInput, "env", "global", "flow", "msg"]}); time2.typedInput("width", "315px"); time2._prevType = "time"; const extend2 = $("<a/>", {href: "#", title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.expand"), style: "margin-left: 6px;"}) .html("<i class='fa fa-angle-right' aria-hidden='true'></i>") .appendTo(time2Row1); const offset1Label = $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.offset"), style: "display: inline-block; width: auto; margin-bottom: 0px;"}) .html("<span style='margin-right: 6px;'>⇔</span>") .appendTo(time1Row2); const offset1 = $("<input/>", {class: "node-input-offset1", style: "width: 35px !important;"}) .appendTo(offset1Label) .spinner(offsetInput); const random1Label = $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.random"), style: "width: auto; margin-left: 6px; margin-bottom: 0px;"}) .html("<i class='fa fa-random' aria-hidden='true'></i>") .appendTo(time1Row2); const random1Box = $("<div/>", {style: "display: inline-block; width: auto; margin-left: 6px;"}) .appendTo(random1Label); const random1 = $("<input/>", {class: "node-input-random1", style: "width: 35px !important;"}) .appendTo(random1Box) .spinner(randomInput); $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.precision"), style: "width: auto; margin-left: 6px; margin-bottom: 0px;"}) .html("<i class='fa fa-bullseye' aria-hidden='true'></i>") .appendTo(time1Row2); const precision1 = $("<select/>", {class: "node-input-precision1", title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.precision"), style: "width: 115px; margin-left: 4px;"}) .append($("<option></option>").val("millisecond").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.millisecond"))) .append($("<option></option>").val("second").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.second"))) .append($("<option></option>").val("minute").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.minute"))) .append($("<option></option>").val("hour").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.hour"))) .append($("<option></option>").val("day").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.day"))) .append($("<option></option>").val("month").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.month"))) .append($("<option></option>").val("year").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.year"))) .appendTo(time1Row2); const offset2Label = $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.offset"), style: "display: inline-block; width: auto; margin-bottom: 0px;"}) .html("<span style='margin-right: 6px;'>⇔</span>") .appendTo(time2Row2); const offset2 = $("<input/>", {class: "node-input-offset2", style: "width: 35px !important;"}) .appendTo(offset2Label) .spinner(offsetInput); const random2Label = $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.random"), style: "width: auto; margin-left: 6px; margin-bottom: 0px;"}) .html("<i class='fa fa-random' aria-hidden='true'></i>") .appendTo(time2Row2); const random2Box = $("<div/>", {style: "display: inline-block; width: auto; margin-left: 6px;"}) .appendTo(random2Label); const random2 = $("<input/>", {class: "node-input-random2", style: "width: 35px !important;"}) .appendTo(random2Box) .spinner(randomInput); $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.precision"), style: "width: auto; margin-left: 6px; margin-bottom: 0px;"}) .html("<i class='fa fa-bullseye' aria-hidden='true'></i>") .appendTo(time2Row2); const precision2 = $("<select/>", {class: "node-input-precision2", title: node._("node-red-contrib-chronos/chronos-config:common.tooltip.precision"), style: "width: 115px; margin-left: 4px;"}) .append($("<option></option>").val("millisecond").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.millisecond"))) .append($("<option></option>").val("second").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.second"))) .append($("<option></option>").val("minute").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.minute"))) .append($("<option></option>").val("hour").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.hour"))) .append($("<option></option>").val("day").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.day"))) .append($("<option></option>").val("month").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.month"))) .append($("<option></option>").val("year").text(node._("node-red-contrib-chronos/chronos-config:common.list.unit.year"))) .appendTo(time2Row2); const dayType = $("<select/>", {class: "node-input-dayType", style: "width: auto; margin-left: 4px;"}) .append($("<option></option>").val("first").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.first"))) .append($("<option></option>").val("second").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.second"))) .append($("<option></option>").val("third").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.third"))) .append($("<option></option>").val("fourth").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.fourth"))) .append($("<option></option>").val("fifth").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.fifth"))) .append($("<option></option>").val("last").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.last"))) .append($("<option></option>").val("even").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.even"))) .append($("<option></option>").val("specific").text(node._("node-red-contrib-chronos/chronos-config:common.list.dayType.specific"))) .appendTo(daysBox); const nthDaysBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 6px;"}).appendTo(daysBox); const specificDaysBox = $("<div/>", {style: "display: inline-block; vertical-align: middle; width: auto; margin-left: 6px;"}).appendTo(daysBox); const day = $("<select/>", {class: "node-input-day", style: "width: auto;"}) .append($("<option></option>").val("monday").text(node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong.monday"))) .append($("<option></option>").val("tuesday").text(node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong.tuesday"))) .append($("<option></option>").val("wednesday").text(node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong.wednesday"))) .append($("<option></option>").val("thursday").text(node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong.thursday"))) .append($("<option></option>").val("friday").text(node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong.friday"))) .append($("<option></option>").val("saturday").text(node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong.saturday"))) .append($("<option></option>").val("sunday").text(node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong.sunday"))) .append($("<option></option>").val("day").text(node._("node-red-contrib-chronos/chronos-config:common.list.day.day"))) .append($("<option></option>").val("workday").text(node._("node-red-contrib-chronos/chronos-config:common.list.day.workday"))) .append($("<option></option>").val("weekend").text(node._("node-red-contrib-chronos/chronos-config:common.list.day.weekend"))) .appendTo(nthDaysBox); const fixedDay = $("<input/>", {class: "node-input-fixedDay", style: "width: 30px !important;"}) .appendTo(specificDaysBox) .spinner(fixedDayInput); const fixedMonth = $("<select/>", {class: "node-input-fixedMonth", style: "width: auto; margin-left: 6px;"}) .append($("<option></option>").val("january").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.january"))) .append($("<option></option>").val("february").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.february"))) .append($("<option></option>").val("march").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.march"))) .append($("<option></option>").val("april").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.april"))) .append($("<option></option>").val("may").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.may"))) .append($("<option></option>").val("june").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.june"))) .append($("<option></option>").val("july").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.july"))) .append($("<option></option>").val("august").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.august"))) .append($("<option></option>").val("september").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.september"))) .append($("<option></option>").val("october").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.october"))) .append($("<option></option>").val("november").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.november"))) .append($("<option></option>").val("december").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.december"))) .append($("<option></option>").val("any").text(node._("node-red-contrib-chronos/chronos-config:common.list.month.any"))) .appendTo(specificDaysBox); const excludeLabel = $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.label.exclude"), style: "width: auto; margin-left: 10px; margin-bottom: 0px;"}) .html("<i class='fa fa-ban' aria-hidden='true'></i>") .appendTo(daysBox); const exclude = $("<input/>", {type: "checkbox", class: "node-input-exclude", style: "width: auto; margin-left: 4px; margin-top: 0px; margin-bottom: 3px;"}) .appendTo(excludeLabel); const weekdayInputs = new Array(6); const monthInputs = new Array(12); for (let i=0; i<7; ++i) { weekdayInputs[(i+1)%7] = $("<input/>", {type: "checkbox", class: "node-input-" + weekdays[(i+1)%7], style: "width: auto; margin-top: 0px; margin-bottom: 1px; margin-left: 6px;"}) .appendTo(weekdaysBox); $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.list.weekdayLong." + weekdays[(i+1)%7]), style: "margin-left: 4px; margin-bottom: 0px; width: auto;"}) .text(node._("node-red-contrib-chronos/chronos-config:common.list.weekday." + weekdays[(i+1)%7])).appendTo(weekdaysBox); } for (let i=0; i<12; ++i) { const row = (i < 4) ? monthsRow1 : (i < 8) ? monthsRow2 : monthsRow3; monthInputs[i] = $("<input/>", {type: "checkbox", class: "node-input-" + months[i], style: "width: auto; margin-top: 0px; margin-bottom: 1px; margin-left: 6px;"}) .appendTo(row); $("<label/>", {title: node._("node-red-contrib-chronos/chronos-config:common.list.monthLong." + months[i]), style: "margin-left: 4px; margin-bottom: 0px; width: 40px;"}) .text(node._("node-red-contrib-chronos/chronos-config:common.list.month." + months[i])).appendTo(row); } const expression = $("<input/>", {type: "text", class: "node-input-expression"}) .appendTo(expressionBox) .typedInput({types: ["jsonata"]}); expression.typedInput("width", "315px"); const context = $("<input/>", {type: "text", class: "node-input-context"}) .appendTo(contextBox) .typedInput({types: ["env", "global", "flow"]}); context.typedInput("width", "315px"); operator.change(function() { timeBox.hide(); time2Box.hide(); daysBox.hide(); weekdaysBox.hide(); monthsBox.hide(); expressionBox.hide(); contextBox.hide(); var value = $(this).val(); switch (value) { case "equal": case "notEqual": case "before": case "until": case "since": case "after": { timeBox.show(); break; } case "between": case "outside": { timeBox.show(); time2Box.show(); break; } case "days": { daysBox.show(); break; } case "weekdays": { weekdaysBox.show(); break; } case "months": { monthsBox.show(); break; } case "expression": { expressionBox.show(); break; } case "context": { contextBox.show(); break; } } }); time1.on("change", function() { let type = time1.typedInput("type"); if (type != time1._prevType) { time1._prevType = type; if ((type != "sun") && (type != "moon")) { time1.typedInput("value", ""); } } }); time2.on("change", function() { let type = time2.typedInput("type"); if (type != time2._prevType) { time2._prevType = type; if ((type != "sun") && (type != "moon")) { time2.typedInput("value", ""); } } }); function showExtensionRow1() { time1Row2.show(); extend1.html("<i class='fa fa-angle-down' aria-hidden='true'></i>"); } function hideExtensionRow1() { time1Row2.hide(); extend1.html("<i class='fa fa-angle-right' aria-hidden='true'></i>"); offset1.spinner("value", 0); random1.spinner("value", 0); } function showExtensionRow2() { time2Row2.show(); extend2.html("<i class='fa fa-angle-down' aria-hidden='true'></i>"); } function hideExtensionRow2() { time2Row2.hide(); extend2.html("<i class='fa fa-angle-right' aria-hidden='true'></i>"); offset2.spinner("value", 0); random2.spinner("value", 0); } extend1.click(function() { if (time1Row2.is(":hidden")) { showExtensionRow1(); } else { hideExtensionRow1(); } }); extend2.click(function() { if (time2Row2.is(":hidden")) { showExtensionRow2(); } else { hideExtensionRow2(); } }); hideExtensionRow1(); hideExtensionRow2(); dayType.change(function() { nthDaysBox.hide(); specificDaysBox.hide(); let value = $(this).val(); if (value == "specific") { specificDaysBox.show(); } else if (value != "even") { let val = day.val(); day.children("option[value='day']").remove(); day.children("option[value='workday']").remove(); day.children("option[value='weekend']").remove(); if ((value == "first") || (value == "last")) { day.append($("<option></option>").val("day").text(node._("node-red-contrib-chronos/chronos-config:common.list.day.day"))); day.append($("<option></option>").val("workday").text(node._("node-red-contrib-chronos/chronos-config:common.list.day.workday"))); day.append($("<option></option>").val("weekend").text(node._("node-red-contrib-chronos/chronos-config:common.list.day.weekend"))); day.val(val); } nthDaysBox.show(); } }); fixedMonth.change(function() { let value = $(this).val(); switch (value) { case "january": case "march": case "may": case "july": case "august": case "october": case "december":