UNPKG

node-red-contrib-chronos

Version:

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

782 lines (728 loc) 114 kB
<!-- Copyright (c) 2020 - 2026 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-switch"> <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-reference"><i class="fa fa-dot-circle-o"></i> <span data-i18n="node-red-contrib-chronos/chronos-config:common.label.reference"></span></label> <input type="text" id="node-input-reference" style="width: 70%;"> <input id="node-input-referenceType" type="hidden"> </div> <div class="form-row"> <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" style="padding-top: 10px; margin-bottom: 0px;"> <label style="width: auto;"><i class="fa fa-sliders"></i> <span data-i18n="node-red-contrib-chronos/chronos-config:common.label.conditions"></span></label> </div> <div id="node-input-momentConditions-row" class="form-row node-input-list-row"> <div class="form-row"> <ol id="node-input-momentConditions"></ol> </div> </div> <div id="node-input-durationConditions-row" class="form-row node-input-list-row"> <div class="form-row"> <ol id="node-input-durationConditions"></ol> </div> </div> <div class="form-row" style="margin-bottom: 0px;"> <input id="node-input-stopOnFirstMatch" type="checkbox" style="margin-top: 0px; margin-bottom: 1px; width: auto;"> <label for="node-input-stopOnFirstMatch" style="margin-bottom: 0px; width: auto;" data-i18n="switch.label.stopOnFirstMatch"></label> </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-switch", { category: "chronos", color: "#DEB887", icon: "chronos_switch.svg", inputs: 1, outputs: 1, paletteLabel: "time switch", label: function() { return (this.name || this._("switch.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.conditions[index].label; }, defaults: { name: { value: "" }, config: { value: "", type: "chronos-config", required: true }, reference: { value: "", validate: RED.validators.typedInput("referenceType") }, referenceType: { value: "absTime" }, baseTime: { value: "", validate: RED.validators.typedInput("baseTimeType") }, baseTimeType: { value: "msgIngress" }, conditions: { value: [{operator: "before", operands: {type: "time", value: "", offset: 0, random: 0, precision: "milliseconds"}, label: ""}], 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 ((this.referenceType == "absTime") && /^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 ((this.referenceType == "absTime") && /^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 ((this.referenceType != "absTime") && /^equal|notEqual|less|lessOrEqual|greaterOrEqual|greater$/.test(item.operator)) { if ((item.operands.type == "num") && (+item.operands.value !== +item.operands.value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidDuration")); } else if ((item.operands.type == "str") && !item.operands.value) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidDuration")); } 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 ((this.referenceType != "absTime") && /^between|outside$/.test(item.operator)) { if ((item.operands[0].type == "num") && (+item.operands[0].value !== +item.operands[0].value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidDuration")); } else if ((item.operands[0].type == "str") && !item.operands[0].value) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidDuration")); } 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 == "num") && (+item.operands[1].value !== +item.operands[1].value)) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidDuration")); } else if ((item.operands[1].type == "str") && !item.operands[1].value) { errors.push(this._("node-red-contrib-chronos/chronos-config:common.error.invalidDuration")); } 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; } }, stopOnFirstMatch: { value: false }, outputs: { value: 1 } }, oneditprepare: function() { let node = this; let tokenCounter = 0; $("body").on("mouseenter.chronos_switch", ".red-ui-typedInput-option-trigger", function() { const _this = this; const input = $(_this).parent().parent().find("input"); if (input.length >= 3) { const type = input.eq(2).val(); if ((type == "sun") || (type == "moon")) { const token = tokenCounter++; _this._chronos_lastToken = token; $.getJSON( "chronos_config", { command: "gettime", timeType: type, timeName: input.eq(0).val(), currentDay: true, config: $("#node-input-config").val()}) .done(result => { if (_this._chronos_lastToken === token) { const label = $(_this).find("span.red-ui-typedInput-option-label"); _this._chronos_lastOptionLabel = label.html(); label.html(result); } }); } } }).on("mouseleave.chronos_switch", ".red-ui-typedInput-option-trigger", function() { delete this._chronos_lastToken; if (this._chronos_lastOptionLabel) { const label = $(this).find("span.red-ui-typedInput-option-label"); label.html(this._chronos_lastOptionLabel); delete this._chronos_lastOptionLabel; } }); 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 absTimeInput = { value: "absTime", label: node._("node-red-contrib-chronos/chronos-config:common.label.absTime"), hasValue: false }; const msgIngressInput = { value: "msgIngress", label: node._("node-red-contrib-chronos/chronos-config:common.label.msgIngress"), hasValue: false }; 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 handleTypeChange = function(event, type, value) { if (type != this._prevType) { this._prevType = type; if ((type != "sun") && (type != "moon")) { $(this).typedInput("value", ""); } } }; const reference = $("#node-input-reference") .typedInput({ types: [ absTimeInput, timeInput, sunTimeInput, moonTimeInput, customInput, "env", "global", "flow", "msg"], typeField: "#node-input-referenceType"}); reference.on("change", function(event, type, value) { $("#node-input-momentConditions-row").hide(); $("#node-input-durationConditions-row").hide(); if (type == "absTime") { $("#node-input-momentConditions-row").show(); } else { $("#node-input-durationConditions-row").show(); } RED.tray.resize(); handleTypeChange.call(this, event, type, value); }); $("#node-input-baseTime") .typedInput({types: [msgIngressInput, "global", "flow", "msg"], typeField: "#node-input-baseTimeType"}); const momentConditions = $("#node-input-momentConditions").css("min-width", "545px").css("min-height", "150px").editableList( { removable: true, sortable: true, addItem: function(item, index, data) { 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); $("<div/>", {class: "node-input-index", style: "position: absolute; width: auto; top: 50%; right: 26px; margin-top: -9px;"}) .html("&#8594; " + (index + 1)) .appendTo(fragment); const operator = $("<select/>", {class: "node-input-operator", style: "width: auto;"}) .appendTo(operatorBox); $("<optgroup></optgroup>").attr("label", node._("node-red-contrib-chronos/chronos-config:common.list.condition.operators")) .append($("<option></option>").val("equal").html(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.equal"))) .append($("<option></option>").val("notEqual").html(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"))) .append($("<option></option>").val("otherwise").text(node._("node-red-contrib-chronos/chronos-config:common.list.condition.operator.otherwise"))) .appendTo(operator); $("<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", "317px"); time1[0]._prevType = "time"; time1.on("change", handleTypeChange); 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", "317px"); time2[0]._prevType = "time"; time2.on("change", handleTypeChange); 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-signal' 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-signal' 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", "317px"); const context = $("<input/>", {type: "text", class: "node-input-context"}) .appendTo(contextBox) .typedInput({types: ["env", "global", "flow"]}); context.typedInput("width", "317px"); 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":