node-red-contrib-sun-position
Version:
NodeRED nodes to get sun and moon position
627 lines (599 loc) • 27.7 kB
HTML
<!--
This code is licensed under the Apache License Version 2.0.
Copyright (c) 2022 Robert Gester
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
-->
<script type="text/javascript">
RED.nodes.registerType('rdg-delay-until', {
category: 'time and astro',
color: '#E6E0F8',
icon: 'hourglass-white.svg',
inputs: 1,
outputs: 1,
paletteLabel: 'delay until',
align: 'left',
defaults: {
name: { value: '' },
positionConfig: {
value: '',
type: 'position-config',
required: true
},
time: {
value: '',
required: true,
validate: RED.validators.typedInput('timeType')
},
timeType: { value: 'entered' },
offset: {
value: 0,
validate: RED.validators.typedInput('offsetType')
},
offsetType: { value: 'none' },
offsetMultiplier: {
value: 60000,
required: true,
validate(_v) {
return (
!isNaN(_v) ||
this.offsetType === 'none' ||
$('#node-input-offset').typedInput('type') === 'none'
);
}
},
queuingBehavior: {
value: 'all',
required: true
},
flushMsgs: {
value: '',
validate: RED.validators.typedInput('flushMsgsType')
},
flushMsgsType: { value: 'none' },
flushMsgsValue: {
value: 'true',
validate(_v) {
return (
(this.flushMsgsType === 'none') ||
($('#node-input-flushMsgs').typedInput('type') === 'none') ||
RED.validators.typedInput('flushMsgsValueType')
);
}
},
flushMsgsValueType: { value: 'bool' },
dropMsgs: {
value: '',
validate: RED.validators.typedInput('flushMsgsType')
},
dropMsgsType: { value: 'none' },
dropMsgsValue: {
value: 'true',
validate(_v) {
return (
(this.dropMsgsType === 'none') ||
($('#node-input-dropMsgs').typedInput('type') === 'none') ||
RED.validators.typedInput('dropMsgsValueType')
);
}
},
dropMsgsValueType: { value: 'bool' },
enqueueMsg: {
value: '',
validate: RED.validators.typedInput('enqueueMsgType')
},
enqueueMsgType: { value: 'none' },
enqueueMsgValue: {
value: 'true',
validate(_v) {
return (
(_v !== '') ||
(this.dropMsgsType === 'none') ||
($('#node-input-enqueueMsg').typedInput('type') === 'none') ||
RED.validators.typedInput('enqueueMsgValueType')
);
}
},
enqueueMsgValueType: { value: 'bool' },
ctrlPropChange: {
value: false
},
ctrlPropValue: {
value: '',
validate: RED.validators.typedInput('ctrlPropValueType')
},
ctrlPropValueType: { value: 'delete' },
tsCompare: {
value: '0'
}
},
label() {
if (this.name) return this.name;
const label = RED.nodes.getType('position-config').getRDGNodeValLbl(this, this.timeType, this.time, this.offsetType, this.offset, this.offsetMultiplier);
if (label === '' || label === '""') {
return this._('delay-until.label.name');
}
return label;
},
labelStyle() {
return this.name ? 'node_label_italic' : '';
},
inputLabels() {
return this._('node-red-contrib-sun-position/position-config:common.label.inputPort');
},
outputLabels(_index) {
return this._('node-red-contrib-sun-position/position-config:common.label.outputPort');
},
oneditprepare() {
setTimeout(() => {
$('.is-to-show-initially').show();
$('.is-to-hide-initially').hide();
}, 300);
$('.enhanced-row-toggle').on('click', () => {
$('.enhanced-row').toggle(); // .hide();
});
const node = this;
const $nodeConfig = $('#node-input-positionConfig');
const setup = function(node) {
/* global getTypes setupTInput getBackendData bdDateToTime initializeValue appendOptions */
const types = getTypes(node, () => $nodeConfig.val());
let onInit = true;
// #region initialize
/**
* update multiplier settings from a previous version
* @param {number} mp - the multiplier value
* @param {string} name - the name of the element
* @param {function} onchange - the function to be called on field change
* @returns {number} the updated multiplier value
*/
function multiplierUpdate(mp, name, onchange) {
const $field = $('#node-input-' + name);
appendOptions(node, $field, 'multiplier', data => (data.id > 0));
if (mp === null || typeof mp === 'undefined' || isNaN(mp) || mp === '' || mp === 0) {
mp = 60000;
} else {
mp = parseFloat(mp);
}
$field.val(mp);
$field.on('change', onchange);
return mp;
}
// #endregion initialize
// #region time
setupTInput(node, {
typeProp: 'timeType',
valueProp: 'time',
width: 'calc(100% - 110px)',
defaultType: types.TimeEntered.value,
defaultValue: '',
types: [
types.TimeEntered,
types.TimeSun,
types.TimeSunCustom,
types.TimeMoon,
'msg',
'flow',
'global',
'env',
types.SunTimeByAzimuth,
types.SunTimeByElevationNext,
types.SunTimeByElevationRise,
types.SunTimeByElevationSet
],
onChange(_type, _value) {
if ( onInit) { return; }
getBackendData(d => {
const $div = $('#node-input-time-div');
const titleOrg = $div.attr('titleOrg');
$div.attr('title', bdDateToTime(d, ' - ') + titleOrg);
}, {
nodeId: node.id,
kind: 'getTimeData',
config: $nodeConfig.val(),
type: $('#node-input-time').typedInput('type'),
value: $('#node-input-time').typedInput('value'),
offsetType: $('#node-input-offset').typedInput('type'),
offset: $('#node-input-offset').typedInput('value'),
multiplier: $('#node-input-offsetMultiplier').val(),
noOffsetError: true
});
}
});
setupTInput(node, {
typeProp: 'offsetType',
valueProp: 'offset',
width: 'calc(100% - 255px)',
defaultType: types.Undefined.value,
defaultValue: 0,
types: [
types.Undefined,
'num',
'msg',
'flow',
'global',
'env',
types.randomNumber,
types.randmNumCachedDay,
types.randmNumCachedWeek
],
onChange(_type, _value) {
if ( onInit) { return; }
const type = $('#node-input-offset').typedInput('type');
if (type === types.Undefined.value) {
$('#node-input-offsetMultiplier').prop('disabled', true);
} else {
$('#node-input-offsetMultiplier').prop('disabled', false);
}
$('#node-input-time').change();
}
});
node.offsetMultiplier = multiplierUpdate(node.offsetMultiplier, 'offsetMultiplier', () => $('#node-input-time').change());
// #endregion time
$('#node-input-queuingBehavior').on('change', () => {
$('#node-input-flushMsgs').change();
$('#node-input-dropMsgs').change();
$('#node-input-enqueueMsg').change();
});
// #region controlMsgProps
setupTInput(node, {
typeProp: 'flushMsgsType',
valueProp: 'flushMsgs',
width: 'calc(100% - 305px)',
defaultType: types.Undefined.value,
defaultValue: 0,
types: [
types.Undefined,
'msg'
],
onChange(_type, _value) {
if ( onInit) { return; }
const type = $('#node-input-flushMsgs').typedInput('type');
if (type === types.Undefined.value) {
$('#node-input-flushMsgsCmpText').hide();
$('#node-input-flushMsgsValue').typedInput('hide');
} else {
$('#node-input-flushMsgsCmpText').show();
$('#node-input-flushMsgsValue').typedInput('show');
}
$('#node-input-flushMsgsValue').change();
$('#node-input-enqueueMsg').change();
}
});
setupTInput(node, {
typeProp: 'flushMsgsValueType',
valueProp: 'flushMsgsValue',
width: '120px',
defaultType: 'bool',
defaultValue: 'true',
types: [
'bool',
'num',
'str',
'json',
'env'
]
});
setupTInput(node, {
typeProp: 'dropMsgsType',
valueProp: 'dropMsgs',
width: 'calc(100% - 305px)',
defaultType: types.Undefined.value,
defaultValue: 0,
types: [
types.Undefined,
'msg'
],
onChange(_type, _value) {
if ( onInit) { return; }
const type = $('#node-input-dropMsgs').typedInput('type');
if (type === types.Undefined.value) {
$('#node-input-dropMsgsCmpText').hide();
$('#node-input-dropMsgsValue').typedInput('hide');
} else {
$('#node-input-dropMsgsCmpText').show();
$('#node-input-dropMsgsValue').typedInput('show');
}
$('#node-input-dropMsgsValue').change();
$('#node-input-enqueueMsg').change();
}
});
setupTInput(node, {
typeProp: 'dropMsgsValueType',
valueProp: 'dropMsgsValue',
width: '120px',
defaultType: 'bool',
defaultValue: 'true',
types: [
'bool',
'num',
'str',
'json',
'env'
]
});
setupTInput(node, {
typeProp: 'enqueueMsgType',
valueProp: 'enqueueMsg',
width: 'calc(100% - 305px)',
defaultType: types.Undefined.value,
defaultValue: 0,
types: [
types.Undefined,
'msg'
],
onChange(_type, _value) {
if ( onInit) { return; }
const type = $('#node-input-enqueueMsg').typedInput('type');
if (type === types.Undefined.value) {
$('#node-input-enqueueMsgCmpText').hide();
$('#node-input-enqueueMsgValue').typedInput('hide');
} else {
$('#node-input-enqueueMsgCmpText').show();
$('#node-input-enqueueMsgValue').typedInput('show');
}
$('#node-input-enqueueMsgValue').change();
$('#node-input-ctrlPropChange').change();
}
});
setupTInput(node, {
typeProp: 'enqueueMsgValueType',
valueProp: 'enqueueMsgValue',
width: '120px',
defaultType: 'bool',
defaultValue: 'true',
types: [
'bool',
'num',
'str',
'json',
'env'
]
});
$('#node-input-ctrlPropChange').on('change', (_type, _value) => {
if ( onInit) { return; }
const typef = $('#node-input-flushMsgs').typedInput('type');
const typed = $('#node-input-dropMsgs').typedInput('type');
if (typef === types.Undefined.value &&
typed === types.Undefined.value) {
$('.row-controlMsgPropDefined').hide();
$('.row-ctrlPropChange').hide();
} else {
$('.row-controlMsgPropDefined').show();
const typee = $('#node-input-enqueueMsg').typedInput('type');
if (typee === types.Undefined.value) {
$('.row-ctrlPropChange').hide();
} else {
$('.row-ctrlPropChange').show();
// $('#node-input-ctrlPropValue').prop('disabled', !$('#node-input-ctrlPropChange').is(':checked'));
if ($('#node-input-ctrlPropChange').is(':checked')) {
$('#node-input-ctrlPropValue').typedInput('enable');
} else {
$('#node-input-ctrlPropValue').typedInput('disable');
}
}
}
});
setupTInput(node, {
typeProp: 'ctrlPropValueType',
valueProp: 'ctrlPropValue',
width: '90px',
defaultType: types.Delete.value,
defaultValue: '',
types: [
types.Delete,
'date',
'bool',
'num',
'str',
'json',
'env',
'msg',
'flow',
'global',
'bin',
'jsonata'
]
});
// #endregion controlMsgProps
// #region Enhanced settings
initializeValue(node, 'tsCompare', 0);
const chkVal = (name, val) => {
return (node[name] === null) ||
(typeof node[name] === 'undefined') ||
($('#node-input-' + name).val() === val) ||
($('#node-input-' + name).is(':checked') === val) ||
(parseFloat($('#node-input-' + name).val()) === val);
};
if (chkVal('tsCompare', 0)) {
$('.enhanced-row').hide();
} else {
$('.enhanced-row').show();
}
// #endregion Enhanced settings
onInit = false;
$('#node-input-offset').change();
$('#node-input-flushMsgs').change();
$('#node-input-dropMsgs').change();
}; // setup
$.getScript('sun-position/js/htmlglobal.js')
.done((_data, _textStatus, _jqxhr) => {
try {
setup(node);
} catch (err) {
console.log("failed to setup editor"); // eslint-disable-line
console.log(err); // eslint-disable-line
console.log(err.stack); // eslint-disable-line
}
})
.fail((_jqxhr, settings, exception) => {
console.log("failed to load htmlglobal.js"); // eslint-disable-line
console.log(exception); // eslint-disable-line
console.log(exception.stack); // eslint-disable-line
});
},
oneditsave() {
this.timeType = $('#node-input-time').typedInput('type');
this.offsetType = $('#node-input-offset').typedInput('type');
this.flushMsgsType = $('#node-input-flushMsgs').typedInput('type');
this.dropMsgsType = $('#node-input-dropMsgs').typedInput('type');
}
});</script>
<script type="text/html" data-template-name="rdg-delay-until">
<div class="form-row block-noindent is-to-show-initially">
<span><i class="fa fa-info-circle"></i> <span data-i18n="[html]node-red-contrib-sun-position/position-config:common.label.infoText" class="row-full-width"></span></span></span>
</div>
<div class="form-row block-noindent" data-i18n="[title]node-red-contrib-sun-position/position-config:common.placeholder.positionConfig">
<label for="node-input-positionConfig"><i class="fa fa-globe"></i> <span data-i18n="node-red-contrib-sun-position/position-config:common.label.positionConfig"></span></label>
<input type="text" id="node-input-positionConfig">
</div>
<hr>
<div class="form-row block-noindent is-to-show-initially" id="node-input-time-div" data-i18n="[titleOrg]delay-until.placeholder.time">
<label for="node-input-time"><i class="fa fa-clock-o"></i> <span data-i18n="delay-until.label.time"></span></label>
<input type="text" id="node-input-time" />
<input type="hidden" id="node-input-timeType">
</div>
<div class="form-row block-indent1 is-to-show-initially" data-i18n="[title]delay-until.placeholder.offset">
<label for="node-input-offset"><i class="fa fa-calculator"></i> <span data-i18n="delay-until.label.offset"></span></label>
<input id="node-input-offset" data-i18n="[placeholder]delay-until.placeholder.offset"/>
<input type="hidden" id="node-input-offsetType">
<select id="node-input-offsetMultiplier" class="node-input-multiplier"></select>
</div>
<hr>
<div class="form-row block-indent1 is-to-show-initially" data-i18n="[title]delay-until.placeholder.queuingBehavior">
<label for="node-input-queuingBehavior"><i class="fa fa-paper-plane"></i> <span data-i18n="delay-until.label.queuingBehavior"></span></label>
<select id="node-input-queuingBehavior" class="node-input-queuingBehavior">
<option value="all" data-i18n="delay-until.label.allMsgs"></option>
<option value="first" data-i18n="delay-until.label.firstMsgs"></option>
<option value="last" data-i18n="delay-until.label.lastMsgs"></option>
</select>
</div>
<hr>
<div class="form-row form-tips is-to-show-initially row-controlMsgPropsAll" data-i18n="[html]delay-until.tips.controlMsgProp"></div>
<div class="form-row block-indent1 is-to-show-initially row-controlMsgPropsAll" data-i18n="[title]delay-until.placeholder.flushMsgs">
<label for="node-input-flushMsgs"><i class="fa fa-paper-plane-o"></i> <span data-i18n="delay-until.label.flushMsgs"></span></label>
<input type="text" id="node-input-flushMsgs" />
<input type="hidden" id="node-input-flushMsgsType">
<span data-i18n="delay-until.label.equals" id="node-input-flushMsgsCmpText"></span>
<input type="text" id="node-input-flushMsgsValue" class="node-input-addpropvalue"/>
<input type="hidden" id="node-input-flushMsgsValueType">
</div>
<div class="form-row block-indent1 is-to-show-initially row-controlMsgPropsAll" data-i18n="[title]delay-until.placeholder.dropMsgs">
<label for="node-input-dropMsgs"><i class="fa fa-trash"></i> <span data-i18n="delay-until.label.dropMsgs"></span></label>
<input type="text" id="node-input-dropMsgs" />
<input type="hidden" id="node-input-dropMsgsType">
<span data-i18n="delay-until.label.equals" id="node-input-dropMsgsCmpText"></span>
<input type="text" id="node-input-dropMsgsValue" class="node-input-addpropvalue"/>
<input type="hidden" id="node-input-dropMsgsValueType">
</div>
<div class="form-row is-initial-hidden row-controlMsgPropsAll row-controlMsgPropDefined">
<span data-i18n="[html]delay-until.tips.controlMsgPropDefined" style="word-wrap:break-word;"></span>
</div>
<div class="form-row block-indent1 is-initial-hidden row-controlMsgPropsAll row-controlMsgPropDefined" data-i18n="[title]delay-until.placeholder.enqueueMsg">
<label for="node-input-enqueueMsg"><i class="fa fa-trash"></i> <span data-i18n="delay-until.label.enqueueMsg"></span></label>
<input type="text" id="node-input-enqueueMsg" />
<input type="hidden" id="node-input-enqueueMsgType">
<span data-i18n="delay-until.label.equals" id="node-input-enqueueMsgCmpText"></span>
<input type="text" id="node-input-enqueueMsgValue" class="node-input-addpropvalue"/>
<input type="hidden" id="node-input-enqueueMsgValueType">
</div>
<div class="form-row block-indent1 is-initial-hidden row-controlMsgPropsAll row-ctrlPropChange" data-i18n="[title]delay-until.placeholder.ctrlPropChange">
<label for="node-input-ctrlPropChange"><i class="fa fa-minus-circle"></i> <span data-i18n="delay-until.label.ctrlPropChange"></span></label>
<input type="checkbox" id="node-input-ctrlPropChange" />
<span data-i18n="delay-until.label.ctrlPropChangeB"></span>
<input type="text" id="node-input-ctrlPropValue" class="node-input-addpropvalue"/>
<input type="hidden" id="node-input-ctrlPropValueType">
</div>
<hr>
<div class="form-row is-to-show-initially">
<a href="#" class="enhanced-row-toggle"><span data-i18n="delay-until.label.showEnhSettings"></span></a>
</div>
<div class="form-row enhanced-row" style="display:none">
<div class="form-row compare-row" data-i18n="[title]delay-until.placeholder.compareTime">
<label for="node-input-tsCompare"><i class="fa fa-clock-o"></i> <span data-i18n="delay-until.label.compareTime"></span></label>
<select id="node-input-tsCompare" style="width:70%;" >
<option value="0" data-i18n="delay-until.label.now"></option>
<option value="1" data-i18n="delay-until.label.msgts"></option>
<option value="2" data-i18n="delay-until.label.msglc"></option>
<option value="3" data-i18n="delay-until.label.msgtime"></option>
</select>
</div>
</div>
<hr>
<div class="form-row block-noindent" data-i18n="[title]node-red:common.label.name">
<label for="node-input-name"><i class="icon-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-tips is-to-show-initially">
<span data-i18n="[html]delay-until.tips.documentation" style="word-wrap:break-word;"></span>
</div>
</script>
<style>
hr {
width: 100%
}
.is-to-show-initially {
display:none
}
.is-initial-hidden {
display:none
}
.form-tips {
width: 90%
}
.block-indent1 {
float: left;
min-height: 1px;
margin-left: 10px;
width: calc(100% - 15px);
}
.block-indent2 {
float: left;
min-height: 1px;
margin-left: 20px;
width: calc(100% - 25px);
}
.block-noindent .row-full-width {
width : calc(100% - 115px);
}
.block-indent1 .row-full-width {
width : calc(100% - 125px);
}
.block-indent2 .row-full-width {
width : calc(100% - 135px);
}
.block-noindent .ui-spinner {
width : calc(100% - 250px);
}
.block-indent1 .ui-spinner {
width : calc(100% - 260px);
}
.block-indent2 .ui-spinner {
width : calc(100% - 270px);
}
.node-input-multiplier {
width: 125px;
max-width: 125px;
margin-left: 5px;
}
.node-input-queuingBehavior {
width: 70% ;
}
.node-input-addpropvalue {
width: 90px;
max-width: 90px;
margin-left: 5px;
}
#node-input-ctrlPropChange {
width: auto ;
}
</style>