Compare commits

..

64 Commits

Author SHA1 Message Date
Dave Conway-Jones
d457c81547 let multiplayer name be set in browser storage rather than fully anonymous 2024-10-11 09:08:17 +01:00
Nick O'Leary
83acc4836b Merge pull request #4768 from Rotzbua/fix_html_tags
fix(html): correct buggy html
2024-09-11 15:40:24 +01:00
Nick O'Leary
db51307e7f Merge branch 'dev' into fix_html_tags 2024-09-11 15:27:45 +01:00
Nick O'Leary
a2430b772b Merge pull request #4836 from node-red/update-dev
Update dev
2024-07-18 17:27:20 +01:00
Nick O'Leary
b3acd1a588 Merge branch 'master' into update-dev 2024-07-18 17:22:44 +01:00
Nick O'Leary
8af821d380 Merge pull request #4837 from node-red/knolleary-patch-1
Back off node 22.5
2024-07-18 17:22:32 +01:00
Nick O'Leary
97ee6c6745 Back off node 22.5 2024-07-18 17:17:50 +01:00
Nick O'Leary
b5fb15cd9b Merge pull request #4828 from GogoVega/fix-menu-selection
Fix menu to enable/disable selection when it's a group
2024-07-18 16:36:02 +01:00
Nick O'Leary
998219ae9a Merge pull request #4829 from node-red/Let-Batch-node-honour-parts-to-close-early
Let batch node terminate "early" if msg.parts set to end of sequence
2024-07-18 16:34:46 +01:00
Nick O'Leary
e8f0f80f65 Merge pull request #4835 from node-red/Fix-Split-Node-Name-Caps
Fix unintentional Capitalisation in Split node name
2024-07-18 16:33:48 +01:00
Nick O'Leary
45936285cc Bump dev to 4.1.0-beta.1 2024-07-18 16:32:12 +01:00
Dave Conway-Jones
ff35c46d5d Fix unintentianal Capitalisation in Split node
to close #4832
2024-07-16 14:37:16 +01:00
Dave Conway-Jones
05c924a9df Let batch node terminate "early" if msg.parts set to end of sequence
(ie index = count -1). Useful to close at end of files etc.
2024-07-04 15:09:55 +01:00
GogoVega
14ac309b6e Fix menu to enable/disable selection when it's a group 2024-07-02 11:38:56 +02:00
Nick O'Leary
29b128e5e0 Merge pull request #4825 from node-red/rel402
Bump for 4.0.2
2024-07-01 16:49:13 +01:00
Nick O'Leary
c0f1581370 Bump for 4.0.2 2024-07-01 16:38:28 +01:00
Nick O'Leary
9420a52ec7 Merge pull request #4818 from bonanitech/header-border
Use a more subtle border on the header
2024-07-01 12:52:09 +01:00
Mauricio Bonani
c113b3de13 Use a not-so-dark color for the header border 2024-07-01 06:18:17 -04:00
Mauricio Bonani
29058c163a Merge branch 'master' into header-border 2024-07-01 06:14:35 -04:00
Nick O'Leary
bb110ea230 Merge pull request #4824 from GogoVega/improve-french-translations
Improve the editor's French translations
2024-07-01 10:55:33 +01:00
Nick O'Leary
785f220cd8 Merge pull request #4821 from node-red/orphaned-monco-editors
Clean up orphaned editors
2024-07-01 10:48:49 +01:00
GogoVega
16570410a5 Improve French translations 2024-07-01 09:43:42 +02:00
GogoVega
8085eda431 Add missing French translations from v4 2024-06-30 22:27:38 +02:00
Steve-Mcl
6503498f0a Clean up orphaned editors
closes #4820
2024-06-29 18:14:54 +01:00
Nick O'Leary
da787a9993 Merge pull request #4812 from GogoVega/fix-required-prop
Fix node validation if the property is not required
2024-06-28 17:21:40 +01:00
Nick O'Leary
c873b57094 Merge pull request #4815 from node-red/update-cookie-auth
Allow auth cookie name to be customised
2024-06-28 16:58:36 +01:00
Nick O'Leary
93974ccd92 Merge pull request #4816 from node-red/4814-multiplayer-uncaught
Guard against undefined sessions in multiplayer
2024-06-28 16:58:25 +01:00
Nick O'Leary
d7aa792f97 Merge pull request #4817 from node-red/mermaid-caching
Ensure mermaid.min.js is cached properly between loads of the editor
2024-06-28 16:58:14 +01:00
Mauricio Bonani
375fa9da64 Use a more subtle border on the header 2024-06-28 09:51:23 -04:00
Nick O'Leary
28c41e17ad Ensure mermaid.min.js is cached properly between loads of the editor 2024-06-28 14:37:51 +01:00
Nick O'Leary
da3ad40968 Add more guards for undefined session 2024-06-28 14:19:18 +01:00
GogoVega
2464d9ad95 Check required prop for each case instead of top level 2024-06-28 14:15:51 +02:00
Nick O'Leary
011b47a108 Guard against undefined sessions in multiplayer 2024-06-28 11:41:05 +01:00
Nick O'Leary
ea747711c3 Allow auth cookie name to be customised 2024-06-28 10:24:51 +01:00
GogoVega
19a8fa09a8 Fix node validation if property is not required 2024-06-27 09:16:03 +02:00
Nick O'Leary
1b5b3f7f88 Merge pull request #4804 from node-red/rel401
Update for 4.0.1 release
2024-06-26 14:42:42 +01:00
Nick O'Leary
2123514c76 Update for 4.0.1 release 2024-06-26 14:38:28 +01:00
Nick O'Leary
379fbd7c7e Merge pull request #4803 from node-red/4798-include-groups-inflowapi
Ensure group nodes are properly exported in /flow api
2024-06-26 14:36:03 +01:00
Nick O'Leary
efdc1b1a1d Ensure group nodes are properly exported in /flow api 2024-06-26 14:30:33 +01:00
Nick O'Leary
52bbd82e18 Merge pull request #4802 from node-red/4801-fix-sf-instance-credentials
Ensure subflow instance credential property values are extracted
2024-06-26 11:57:31 +01:00
Nick O'Leary
20a9c051be Ensure subflow instance credential property values are extracted 2024-06-26 11:49:37 +01:00
Nick O'Leary
830e475969 Merge pull request #4800 from GogoVega/fix-config-select
Use `_ADD_` value for both `add new...` and `none` options
2024-06-26 10:51:39 +01:00
GogoVega
f75e2f221c Use _ADD_ value for add... and none options 2024-06-25 20:08:31 +02:00
Nick O'Leary
5a75440668 Merge pull request #4796 from node-red/Join-node-optional-use-of-parts
make using msg.parts optional in join node
2024-06-25 16:22:20 +01:00
Dave Conway-Jones
53d8b97fff join node - honour 3.x behaviour for old instances.
but don't use msg.parts for new instances in manual join mode unless set.
2024-06-25 12:57:09 +01:00
Nick O'Leary
c85667cc13 Merge pull request #4794 from node-red/4792-ui-proxy-fix
UI proxy should setup agents for both http_proxy and https_proxy
2024-06-25 10:56:44 +01:00
Dave Conway-Jones
1a8b37b4e3 make using msg.parts optional in join node 2024-06-24 21:05:00 +01:00
Steve-Mcl
40a2d90e08 remove .only 2024-06-24 20:06:22 +01:00
Steve-Mcl
ee269caa4a update and add tests to check correct proxy was returned for flow 2024-06-24 20:05:37 +01:00
Steve-Mcl
d820686e5a only initialise proxy if flow url is static 2024-06-24 20:04:26 +01:00
Steve-Mcl
aa2a585e00 should set https and http proxy agents for UI
closes #4792
2024-06-24 17:34:30 +01:00
Steve-Mcl
f9e6bccd46 Merge branch 'master' of github.com:node-red/node-red 2024-06-24 17:19:07 +01:00
Nick O'Leary
3230654ecd Merge pull request #4788 from GogoVega/fix-config-node-select
Fix the config node select value assignment
2024-06-24 16:59:16 +01:00
Gauthier Dandele
a5b53ee373 Fix the selected value if no config nodes available
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-06-24 17:37:28 +02:00
Nick O'Leary
ac420247ae Merge pull request #4786 from kazuhitoyokoi/master-addtooltip
Add tooltip for number of subflow instance on info tab
2024-06-24 16:25:54 +01:00
Steve-Mcl
61198bd7e3 Merge branch 'master' of github.com:node-red/node-red 2024-06-24 11:58:03 +01:00
GogoVega
9c511b6674 Revert the updateConfigNodeSelect changes 2024-06-24 12:39:22 +02:00
Nick O'Leary
9d054543a7 Merge pull request #4785 from kazuhitoyokoi/master-addjpn
Add Japanese translations for v4.0.0
2024-06-24 11:33:00 +01:00
Nick O'Leary
fc3ec2a0d7 Merge pull request #4791 from node-red/4787-revert-default-user-agent
Remove default user agent
2024-06-24 11:30:54 +01:00
Stephen McLaughlin
e7ef73222f Remove default user agent
closes #4787
2024-06-24 11:21:51 +01:00
GogoVega
6623e56a1e Fix setting of config node select value 2024-06-23 21:41:30 +02:00
Kazuhito Yokoi
582eca1877 Add tooltip for number of subflow instance on info tab 2024-06-23 20:59:26 +09:00
Kazuhito Yokoi
2783100f84 Add Japanese translations for v4.0.0 2024-06-23 20:48:34 +09:00
Rotzbua
8c5ddd68a4 fix(html): correct buggy html
- Merge "style" attribute
- Remove wrong end tag
- Remove trailing slash on void html elements
2024-06-22 22:38:24 +02:00
48 changed files with 601 additions and 275 deletions

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
node-version: [18, 20, 22.4.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}

View File

@@ -1,3 +1,38 @@
#### 4.0.2: Maintenance Release
Editor
- Use a more subtle border on the header (#4818) @bonanitech
- Improve the editor's French translations (#4824) @GogoVega
- Clean up orphaned editors (#4821) @Steve-Mcl
- Fix node validation if the property is not required (#4812) @GogoVega
- Ensure mermaid.min.js is cached properly between loads of the editor (#4817) @knolleary
Runtime
- Allow auth cookie name to be customised (#4815) @knolleary
- Guard against undefined sessions in multiplayer (#4816) @knolleary
#### 4.0.1: Maintenance Release
Editor
- Ensure subflow instance credential property values are extracted (#4802) @knolleary
- Use `_ADD_` value for both `add new...` and `none` options (#4800) @GogoVega
- Fix the config node select value assignment (#4788) @GogoVega
- Add tooltip for number of subflow instance on info tab (#4786) @kazuhitoyokoi
- Add Japanese translations for v4.0.0 (#4785) @kazuhitoyokoi
Runtime
- Ensure group nodes are properly exported in /flow api (#4803) @knolleary
Nodes
- Joins: make using msg.parts optional in join node (#4796) @dceejay
- HTTP Request: UI proxy should setup agents for both http_proxy and https_proxy (#4794) @Steve-Mcl
- HTTP Request: Remove default user agent (#4791) @Steve-Mcl
#### 4.0.0: Milestone Release
This marks the next major release of Node-RED. The following changes represent

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"description": "Low-code programming for event-driven applications",
"homepage": "https://nodered.org",
"license": "Apache-2.0",

View File

@@ -182,6 +182,10 @@ function genericStrategy(adminApp,strategy) {
maxAge: null,
...settings.httpAdminCookieOptions
}
if (sessionOptions.cookie.name){
sessionOptions.name = sessionOptions.cookie.name
delete sessionOptions.cookie.name
}
}
adminApp.use(session(sessionOptions));
//TODO: all passport references ought to be in ./auth
@@ -217,10 +221,10 @@ function genericStrategy(adminApp,strategy) {
adminApp.get('/auth/strategy',
passport.authenticate(strategy.name, {
session:false,
failureMessage: true,
failureRedirect: settings.httpAdminRoot + '?session_message=Login Failed'
failWithError: true,
failureMessage: true
}),
completeGenerateStrategyAuth,
completeGenericStrategyAuth,
handleStrategyError
);
@@ -232,14 +236,14 @@ function genericStrategy(adminApp,strategy) {
passport.authenticate(strategy.name, {
session:false,
failureMessage: true,
failureRedirect: settings.httpAdminRoot + '?session_message=Login Failed'
failWithError: true
}),
completeGenerateStrategyAuth,
completeGenericStrategyAuth,
handleStrategyError
);
}
function completeGenerateStrategyAuth(req,res) {
function completeGenericStrategyAuth(req,res) {
var tokens = req.user.tokens;
delete req.user.tokens;
// Successful authentication, redirect home.
@@ -249,6 +253,8 @@ function handleStrategyError(err, req, res, next) {
if (res.headersSent) {
return next(err)
}
// Remove the header that passport auto-adds as we don't need it
res.removeHeader('WWW-Authenticate')
log.audit({event: "auth.login.fail.oauth",error:err.toString()});
res.redirect(settings.httpAdminRoot + '?session_message='+err.toString());
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/util": "4.0.0",
"@node-red/editor-client": "4.0.0",
"@node-red/util": "4.1.0-beta.0",
"@node-red/editor-client": "4.1.0-beta.0",
"bcryptjs": "2.4.3",
"body-parser": "1.20.2",
"clone": "2.1.2",

View File

@@ -27,7 +27,8 @@
"lock": "Verrouiller",
"unlock": "Déverrouiller",
"locked": "Verrouillé",
"unlocked": "Déverrouillé"
"unlocked": "Déverrouillé",
"format": "Format"
},
"type": {
"string": "chaîne de caractères",
@@ -54,10 +55,10 @@
"workspace": {
"defaultName": "Flux __number__",
"editFlow": "Modifier le flux : __name__",
"confirmDelete": "Confirmation de la suppression",
"delete": "Etes-vous sûr de vouloir supprimer '__label__'?",
"dropFlowHere": "Déposer le flux ici",
"dropImageHere": "Déposer l'image ici",
"confirmDelete": "Confirmer la suppression",
"delete": "Êtes-vous sûr de vouloir supprimer '__label__' ?",
"dropFlowHere": "Lâchez le flux ici",
"dropImageHere": "Lâchez l'image ici",
"addFlow": "Ajouter un flux",
"addFlowToRight": "Ajouter un flux à droite",
"closeFlow": "Fermer le flux",
@@ -74,7 +75,7 @@
"enabled": "Activé",
"disabled": "Désactivé",
"info": "Description",
"selectNodes": "Cliquer sur les noeuds pour sélectionner",
"selectNodes": "Cliquer pour sélectionner",
"enableFlow": "Activer le flux",
"disableFlow": "Désactiver le flux",
"lockFlow": "Verrouiller le flux",
@@ -98,7 +99,7 @@
"rtl": "De droite à gauche",
"auto": "Contextuel",
"language": "Langue",
"browserDefault": "Navigateur par défaut"
"browserDefault": "Par défaut du Navigateur"
},
"sidebar": {
"show": "Afficher la barre latérale"
@@ -134,7 +135,7 @@
"disableSelectedNodes": "Désactiver les noeuds sélectionnés",
"showSelectedNodeLabels": "Afficher les étiquettes des noeuds sélectionnés",
"hideSelectedNodeLabels": "Masquer les étiquettes des noeuds sélectionnés",
"showWelcomeTours": "Afficher les visites guidées pour les nouvelles versions",
"showWelcomeTours": "Afficher les visites guidées des nouvelles versions",
"help": "Site web de Node-RED",
"projects": "Projets",
"projects-new": "Nouveau projet",
@@ -143,7 +144,7 @@
"showNodeLabelDefault": "Afficher l'étiquette des noeuds nouvellement ajoutés",
"codeEditor": "Éditeur de code",
"groups": "Groupes",
"groupSelection": "Grouper cette sélection",
"groupSelection": "Grouper la sélection",
"ungroupSelection": "Dégrouper la sélection",
"groupMergeSelection": "Fusionner la sélection",
"groupRemoveSelection": "Supprimer du groupe",
@@ -155,7 +156,7 @@
"alignMiddle": "Aligner au milieu",
"alignBottom": "Aligner en bas",
"distributeHorizontally": "Répartir horizontalement",
"distributeVertically": "Distribuer verticalement",
"distributeVertically": "Répartir verticalement",
"moveToBack": "Déplacer vers l'arrière",
"moveToFront": "Déplacer vers l'avant",
"moveBackwards": "Reculer",
@@ -163,21 +164,21 @@
}
},
"actions": {
"toggle-navigator": "Basculer de navigateur",
"zoom-out": "Dézoomer",
"zoom-reset": "Réinitialiser le zoom",
"toggle-navigator": "Basculer l'affichage du navigateur",
"zoom-out": "Réduire",
"zoom-reset": "Réinitialiser",
"zoom-in": "Agrandir",
"search-flows": "Rechercher le flux",
"search-prev": "Précédent",
"search-next": "Suivant",
"search-counter": "\"__term__\" __result__ de __count__"
"search-counter": "\"__term__\" __result__ sur __count__"
},
"user": {
"loggedInAs": "Connecté en tant que __name__",
"username": "Nom d'utilisateur",
"password": "Mot de passe",
"login": "Connexion",
"loginFailed": "Échec de la connexion",
"login": "Se connecter",
"loginFailed": "Échec de connexion",
"notAuthorized": "Pas autorisé",
"errors": {
"settings": "Vous devez être connecté pour accéder aux paramètres",
@@ -193,16 +194,16 @@
"warning": "<strong>Attention</strong> : __message__",
"warnings": {
"undeployedChanges": "Le noeud a des modifications non déployées",
"nodeActionDisabled": "Actions de noeud désactivées",
"nodeActionDisabledSubflow": "Actions de noeud désactivées dans le sous-flux",
"nodeActionDisabled": "Les actions du noeud sont désactivées",
"nodeActionDisabledSubflow": "Les actions de noeud sont désactivées à l'intérieur du sous-flux",
"missing-types": "<p>Flux arrêtés en raison de types de noeuds manquants.</p>",
"missing-modules": "<p>Flux arrêtés en raison de modules manquants.</p>",
"safe-mode": "<p>Flux arrêtés en mode sans échec.</p><p>Vous pouvez modifier vos flux et déployer les changements pour redémarrer.</p>",
"safe-mode": "<p>Flux arrêtés en mode sans échec.</p><p>Vous pouvez modifier vos flux et déployer ensuite les changements afin de démarrer vos flux.</p>",
"restartRequired": "Node-RED doit être redémarré pour mettre à jour les modules",
"credentials_load_failed": "<p>Les flux se sont arrêtés car les informations d'identification n'ont pas pu être déchiffrées.</p><p>Le fichier d'informations d'identification du flux est chiffré, mais la clé de chiffrement du projet est manquante ou invalide.</p>",
"credentials_load_failed_reset": "<p>Les informations d'identification n'ont pas pu être déchiffrées</p><p>Le fichier d'informations d'identification du flux est chiffré, mais la clé de chiffrement du projet est manquante ou invalide.</p><p>Le fichier d'informations d'identification du flux sera réinitialisé lors du prochain déploiement. Toutes les informations d'identification de flux existantes seront perdues.</p>",
"credentials_load_failed": "<p>Les flux se sont arrêtés car les informations d'identification n'ont pas pu être déchiffrées.</p><p>Le fichier d'informations d'identification du flux est chiffré mais la clé de chiffrement du projet est manquante ou invalide.</p>",
"credentials_load_failed_reset": "<p>Les informations d'identification n'ont pas pu être déchiffrées</p><p>Le fichier d'informations d'identification du flux est chiffré mais la clé de chiffrement du projet est manquante ou invalide.</p><p>Le fichier d'informations d'identification du flux sera réinitialisé lors du prochain déploiement. Toutes les informations d'identification des flux existants seront perdues.</p>",
"missing_flow_file": "<p>Fichier contenant les flux introuvable.</p><p>Le projet n'est pas configuré avec un fichier de flux.</p>",
"missing_package_file": "<p>Fichier de paquetage du projet introuvable.</p><p>Il manque au projet un fichier package.json.</p>",
"missing_package_file": "<p>Fichier de paquetage du projet introuvable.</p><p>Il manque au projet le fichier <code>package.json</code>.</p>",
"project_empty": "<p>Le projet est vide.</p><p>Voulez-vous créer un ensemble de fichiers de projet par défaut ?<br/>Sinon, vous devrez ajouter manuellement des fichiers au projet (en dehors de l'éditeur).</p>",
"project_not_found": "<p>Le projet '__project__' est introuvable.</p>",
"git_merge_conflict": "<p>La fusion automatique des modifications a échoué.</p><p>Corriger les conflits non fusionnés, puis valider le résultat.</p>"
@@ -219,7 +220,7 @@
},
"project": {
"change-branch": "Changer pour une branche locale '__project__'",
"merge-abort": "Git fusion abandonnée",
"merge-abort": "Fusion Git abandonnée",
"loaded": "Projet '__project__' chargé",
"updated": "Projet '__project__' mis à jour",
"pull": "Projet '__project__' rechargé",
@@ -352,7 +353,7 @@
"backgroundUpdate": "Les flux sur le serveur ont été mis à jour.",
"conflictChecking": "Vérifier si les modifications peuvent être fusionnées automatiquement",
"conflictAutoMerge": "Les modifications n'incluent aucun conflit et peuvent être fusionnées automatiquement.",
"conflictManualMerge": "Les changements incluent des conflits qui doivent être résolus avant de pouvoir être déployés.",
"conflictManualMerge": "Les modifications incluent des conflits qui doivent être résolus avant de pouvoir être déployées.",
"plusNMore": "+ __count__ en plus"
}
},
@@ -372,16 +373,17 @@
"deleted": "supprimé",
"flowDeleted": "flux supprimé",
"flowAdded": "flux ajouté",
"moved": "déplacé",
"movedTo": "déplacé vers __id__",
"movedFrom": "déplacé depuis __id__"
},
"nodeCount": "__count__ noeud",
"nodeCount_plural": "__count__ noeuds",
"local": "Changements locaux",
"remote": "Modifications à distance",
"remote": "Changements distants",
"reviewChanges": "Examiner les modifications",
"noBinaryFileShowed": "Impossible d'afficher le contenu du fichier binaire",
"viewCommitDiff": "Afficher les modifications de validation",
"viewCommitDiff": "Afficher les modifications de la validation",
"compareChanges": "Comparer les modifications",
"saveConflict": "Enregistrer la résolution des conflits",
"conflictHeader": "<span>__resolved__</span> sur <span>__unresolved__</span> conflit(s) résolu(s)",
@@ -395,9 +397,9 @@
"edit": "Modifier le modèle du sous-flux",
"subflowInstances": "Il existe __count__ instance de ce modèle de sous-flux",
"subflowInstances_plural": "Il existe __count__ instances de ce modèle de sous-flux",
"editSubflowProperties": "modifier les propriétés",
"input": "Entrées:",
"output": "Sorties:",
"editSubflowProperties": "Modifier les propriétés",
"input": "Entrées :",
"output": "Sorties :",
"status": "Statut du noeud",
"deleteSubflow": "Supprimer le sous-flux",
"confirmDelete": "Voulez-vous vraiment supprimer ce sous-flux ?",
@@ -411,7 +413,7 @@
"version": "Version",
"versionPlaceholder": "x.y.z",
"keys": "Mots clés",
"keysPlaceholder": "Mots clés séparés par des virgules",
"keysPlaceholder": "Mots clés séparés par une virgule",
"author": "Auteur",
"authorPlaceholder": "Votre nom <email@exemple.com>",
"desc": "Description",
@@ -468,7 +470,7 @@
"select": "sélection",
"checkbox": "case à cocher",
"spinner": "valeurs à défiler",
"none": "aucune",
"none": "aucun",
"hidden": "masquer la propriété"
},
"types": {
@@ -496,7 +498,7 @@
"max": "Maximum"
},
"errors": {
"scopeChange": "La modification de la portée la rendra indisponible pour les noeuds d'autres flux qui l'utilisent",
"scopeChange": "La modification de la portée rendra indisponible ce noeud de configuration aux noeuds d'autres flux qui l'utilisent",
"invalidProperties": "Propriétés invalides :",
"credentialLoadFailed": "Échec du chargement des identifiants du noeud"
}
@@ -510,7 +512,7 @@
"unassigned": "Non attribué",
"global": "Global",
"workspace": "Espace de travail",
"editor": "Boîte de dialogue d'édition",
"editor": "Boîte d'édition",
"selectAll": "Tout sélectionner",
"selectNone": "Ne rien sélectionner",
"selectAllConnected": "Sélectionner tous les éléments connectés",
@@ -541,7 +543,7 @@
"openLibrary": "Ouvrir la bibliothèque...",
"saveToLibrary": "Enregistrer dans la bibliothèque...",
"typeLibrary": "__type__ bibliothèque",
"unnamedType": "Innomé __type__",
"unnamedType": "Sans nom __type__",
"exportedToLibrary": "Noeuds exportés vers la bibliothèque",
"dialogSaveOverwrite": "Une __libraryType__ appelée __libraryName__ existe déjà. Écraser ?",
"invalidFilename": "Nom de fichier non valide",
@@ -558,7 +560,7 @@
"noInfo": "Pas d'information disponible",
"filter": "Rechercher le noeud",
"search": "Rechercher les modules",
"addCategory": "Ajouter un nouveau...",
"addCategory": "Ajouter une nouvelle...",
"label": {
"subflows": "Sous-flux",
"network": "Réseau",
@@ -638,7 +640,7 @@
"sortAZ": "A-Z",
"sortRecent": "Récent",
"more": "+ __count__ en plus",
"upload": "Charger le fichier tgz du module",
"upload": "Charger le fichier .tgz du module",
"refresh": "Actualiser la liste des modules",
"errors": {
"catalogLoadFailed": "<p>Échec du chargement du catalogue de noeuds.</p><p>Vérifier la console du navigateur pour plus d'informations</p>",
@@ -651,7 +653,7 @@
},
"confirm": {
"install": {
"body": "<p>Installation de '__module__'</p><p>Avant l'installation, veuiller lire la documentation du noeud. Certains noeuds ont des dépendances qui ne peuvent pas être résolues automatiquement et peuvent nécessiter un redémarrage de Node-RED.</p>",
"body": "<p>Installation de '__module__'</p><p>Avant l'installation, veuillez lire la documentation du noeud. Certains noeuds ont des dépendances qui ne peuvent pas être résolues automatiquement et peuvent nécessiter un redémarrage de Node-RED.</p>",
"title": "Installer les noeuds"
},
"remove": {
@@ -666,7 +668,7 @@
"title": "Mettre à jour les noeuds"
},
"cannotUpdate": {
"body": "Une mise à jour pour ce noeud est disponible, mais il n'est pas installé dans un emplacement que le gestionnaire de palette peut mettre à jour.<br/><br/>Veuiller vous référer à la documentation pour savoir comment mettre à jour ce noeud."
"body": "Une mise à jour pour ce noeud est disponible, mais il n'est pas installé dans un emplacement que le gestionnaire de palette peut mettre à jour.<br/><br/>Veuillez vous référer à la documentation pour savoir comment mettre à jour ce noeud."
},
"button": {
"review": "Ouvrir la documentation",
@@ -708,8 +710,8 @@
"nodeHelp": "Aide sur les noeuds",
"none": "Aucun",
"arrayItems": "__count__ éléments",
"showTips": "Vous pouvez ouvrir les astuces à partir du panneau des paramètres",
"outline": "Plan",
"showTips": "Vous pouvez afficher les astuces à partir du panneau des paramètres",
"outline": "Contour",
"empty": "Vide",
"globalConfig": "Noeuds de configuration globale",
"triggerAction": "Déclencher une action",
@@ -722,7 +724,7 @@
"help": {
"name": "Aide",
"label": "Aide",
"search": "Aide à la recherche",
"search": "Rechercher l'aide",
"nodeHelp": "Aide sur les noeuds",
"showHelp": "Afficher l'aide",
"showInOutline": "Afficher dans les grandes lignes",
@@ -801,7 +803,7 @@
"branches": "Branches",
"noBranches": "Pas de branche",
"deleteConfirm": "Êtes-vous sûr de vouloir supprimer la branche locale '__name__' ? Ça ne peut pas être annulé.",
"unmergedConfirm": "La branche locale '__name__' contient des modifications non fusionnées qui seront perdues. Etes-vous sûr de vouloir la supprimer?",
"unmergedConfirm": "La branche locale '__name__' contient des modifications non fusionnées qui seront perdues. Êtes-vous sûr de vouloir la supprimer?",
"deleteUnmergedBranch": "Supprimer la branche non fusionnée",
"gitRemotes": "Git distant",
"addRemote": "Ajout distant",
@@ -845,17 +847,17 @@
"deleteConfirm": "Êtes-vous sûr de vouloir supprimer la clé SSH __name__ ? Ça ne peut pas être annulé."
},
"versionControl": {
"unstagedChanges": "Abandon des changements",
"stagedChanges": "Changement mis en place",
"unstageChange": "Ne pas mettre en place le changement",
"stageChange": "Mettre en place le changement",
"unstageAllChange": "Ne pas mettre en place tous les changements",
"stageAllChange": "Mettre en place tous les changements",
"unstagedChanges": "Changements non indexés",
"stagedChanges": "Changements indexés",
"unstageChange": "Annuler l'indexation des changements",
"stageChange": "Indexer les changements",
"unstageAllChange": "Annuler l'indexation de tous les changements",
"stageAllChange": "Indexer tous les changements",
"commitChanges": "Valider les changements",
"resolveConflicts": "Résoudre les conflits",
"head": "En-tête",
"staged": "Mis en place",
"unstaged": "Non mis en place",
"staged": "Indexé",
"unstaged": "Non indexé",
"local": "Local",
"remote": "Distant",
"revert": "Voulez-vous vraiment annuler les modifications apportées à '__file__' ? Ça ne peut pas être annulé.",
@@ -889,11 +891,11 @@
"pushFailed": "L'envoi a échoué car la branche a des validations plus récentes. Tirer et fusionner d'abord, puis envoyer à nouveau.",
"push": "Envoyer",
"pull": "Tirer",
"unablePull": "<p>Impossible d'extraire les modifications à distance ; vos modifications locales non mises en place seraient écrasées.</p><p>Valider vos modifications et réessayer.</p>",
"showUnstagedChanges": "Afficher les modifications non mise en place",
"unablePull": "<p>Impossible d'extraire les modifications à distance; vos modifications locales non mises en place seraient écrasées.</p><p>Valider vos modifications et réessayer.</p>",
"showUnstagedChanges": "Afficher les modifications non indexées",
"connectionFailed": "Impossible de se connecter au référentiel distant: ",
"pullUnrelatedHistory": "<p>Le réferentiel distant a un historique de validations sans rapport.</p><p>Êtes-vous sûr de vouloir extraire les modifications dans votre référentiel local ?</p>",
"pullChanges": "Tirer les changements",
"pullChanges": "Tirer les changements distants",
"history": "Historique",
"projectHistory": "Historique du projet",
"daysAgo": "il y a __count__ jour",
@@ -974,7 +976,7 @@
"result": "Résultat",
"format": "Format",
"compatMode": "Mode de compatibilité activé",
"compatModeDesc": "<h3>Mode de compatibilité JSONata</h3><p> L'expression actuelle semble toujours faire référence à <code>msg</code> et sera donc évaluée en mode de compatibilité. Veuiller mettre à jour l'expression pour ne pas utiliser <code>msg</code> car ce mode sera supprimé à l'avenir.</p><p> Lorsque la prise en charge de JSONata a été ajoutée pour la première fois à Node-RED, il fallait que l'expression référencie l'objet <code>msg</code>. Par exemple, <code>msg.payload</code> serait utilisé pour accéder à la charge utile.</p><p> Cela n'est plus nécessaire car l'expression sera évaluée directement par rapport au message. Pour accéder à la charge utile, l'expression doit être simplement <code>charge utile</code>.</p>",
"compatModeDesc": "<h3>Mode de compatibilité JSONata</h3><p> L'expression actuelle semble toujours faire référence à <code>msg</code> et sera donc évaluée en mode de compatibilité. Veuillez mettre à jour l'expression pour ne pas utiliser <code>msg</code> car ce mode sera supprimé à l'avenir.</p><p> Lorsque la prise en charge de JSONata a été ajoutée pour la première fois à Node-RED, il fallait que l'expression référencie l'objet <code>msg</code>. Par exemple, <code>msg.payload</code> serait utilisé pour accéder à la charge utile.</p><p> Cela n'est plus nécessaire car l'expression sera évaluée directement par rapport au message. Pour accéder à la charge utile, l'expression doit être simplement <code>charge utile</code>.</p>",
"noMatch": "Aucun résultat correspondant",
"errors": {
"invalid-expr": "Expression JSONata non valide :\n __message__",
@@ -997,7 +999,7 @@
},
"jsonEditor": {
"title": "Éditeur JSON",
"format": "Format JSON",
"format": "Formatter JSON",
"rawMode": "Modifier JSON",
"uiMode": "Afficher l'éditeur",
"rawMode-readonly": "JSON",
@@ -1016,7 +1018,7 @@
"markdownEditor": {
"title": "Éditeur Markdown",
"expand": "Développer",
"format": "Formaté avec Markdown",
"format": "Formatter avec Markdown",
"heading1": "Rubrique 1",
"heading2": "Rubrique 2",
"heading3": "Rubrique 3",
@@ -1090,7 +1092,7 @@
"credential-key": "Clé de chiffrement des identifiants",
"cant-get-ssh-key": "Erreur! Impossible d'obtenir le chemin de la clé SSH sélectionnée.",
"already-exists2": "Existe déjà",
"git-error": "Erreur git",
"git-error": "Erreur Git",
"connection-failed": "La connexion a échoué",
"not-git-repo": "Ce n'est pas un dépôt Git",
"repo-not-found": "Référentiel introuvable"
@@ -1104,7 +1106,7 @@
"credentials-file": "Fichier d'identifiants"
},
"encryption-config": {
"setup": "Configuration du chiffrage de votre fichier d'informations d'identification",
"setup": "Configuration du chiffrement de votre fichier d'informations d'identification",
"desc0": "Votre fichier d'informations d'identification de flux peut être chiffré pour sécuriser son contenu.",
"desc1": "Si vous souhaitez stocker ces identifiants dans un référentiel Git public, vous devez les chiffrer en fournissant une phrase clé secrète.",
"desc2": "Votre fichier d'identifiants de flux n'est actuellement pas chiffré.",
@@ -1161,9 +1163,9 @@
"add-ssh-key": "Ajouter une clé ssh",
"credentials-encryption-key": "Clé de chiffrement des identifiants",
"already-exists-2": "Existe déjà",
"git-error": "Erreur git",
"git-error": "Erreur Git",
"con-failed": "La connexion a échoué",
"not-git": "Ce n'est pas un dépôt git",
"not-git": "Ce n'est pas un dépôt Git",
"no-resource": "Référentiel introuvable",
"cant-get-ssh-key-path": "Erreur! Impossible d'obtenir le chemin de la clé SSH sélectionnée.",
"unexpected_error": "Erreur inattendue",
@@ -1201,7 +1203,7 @@
},
"errors": {
"no-username-email": "Votre client Git n'est pas configuré avec un nom d'utilisateur/e-mail.",
"unexpected": "Une erreur inattendue est apparue",
"unexpected": "Une erreur inattendue est survenue",
"code": "Code"
}
},
@@ -1270,7 +1272,7 @@
"list-modified-nodes": "Afficher les flux modifiés",
"list-hidden-flows": "Afficher les flux cachés",
"list-flows": "Lister les flux",
"list-subflows": "Liste les sous-flux",
"list-subflows": "Lister les sous-flux",
"go-to-previous-location": "Aller à l'emplacement précédent",
"go-to-next-location": "Aller à l'emplacement suivant",
"copy-selection-to-internal-clipboard": "Copier la sélection dans le presse-papiers",
@@ -1330,8 +1332,8 @@
"align-selection-to-bottom": "Aligner la sélection vers le bas",
"align-selection-to-middle": "Aligner la sélection au centre verticalement",
"align-selection-to-center": "Aligner la sélection au centre horizontalement",
"distribute-selection-horizontally": "Distribuer la sélection horizontalement",
"distribute-selection-vertical": "Distribuer la sélection verticalement",
"distribute-selection-horizontally": "Répartir la sélection horizontalement",
"distribute-selection-vertical": "Répartir la sélection verticalement",
"wire-series-of-nodes": "Connecter les noeuds en série",
"wire-node-to-multiple": "Connecter les noeuds à plusieurs",
"wire-multiple-to-node": "Connecter plusieurs au noeud",

View File

@@ -27,7 +27,8 @@
"lock": "固定",
"unlock": "固定を解除",
"locked": "固定済み",
"unlocked": "固定なし"
"unlocked": "固定なし",
"format": "形式"
},
"type": {
"string": "文字列",
@@ -281,8 +282,8 @@
"selected": "選択したフロー",
"current": "現在のタブ",
"all": "全てのタブ",
"compact": "インデントのないJSONフォーマット",
"formatted": "インデント付きのJSONフォーマット",
"compact": "インデントなし",
"formatted": "インデント付き",
"copy": "書き出し",
"export": "ライブラリに書き出し",
"exportAs": "書き出し先",
@@ -923,6 +924,8 @@
}
},
"typedInput": {
"selected": "__count__個を選択",
"selected_plural": "__count__個を選択",
"type": {
"str": "文字列",
"num": "数値",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -164,7 +164,7 @@ RED.multiplayer = (function () {
$(this).show()
}
})
if (users.length < maxShown + 1) {
if (users.length < maxShown + 1) {
userCountIcon.hide()
} else {
userCountSpan.text('+'+(users.length - maxShown))
@@ -365,12 +365,12 @@ RED.multiplayer = (function () {
border.setAttribute("r",radius/2);
border.setAttribute("class", "red-ui-multiplayer-annotation-border")
group.appendChild(border)
return group
}
RED.view.annotations.register("red-ui-multiplayer",{
type: 'badge',
align: 'left',
@@ -411,7 +411,7 @@ RED.multiplayer = (function () {
// } else {
log('Session ID', activeSessionId)
// }
headerWidget = $('<li><ul id="red-ui-multiplayer-user-list"></ul></li>').prependTo('.red-ui-header-toolbar')
RED.comms.on('connect', () => {
@@ -422,6 +422,9 @@ RED.multiplayer = (function () {
if (location.workspace !== 0) {
connectInfo.location = location
}
if (localStorage.getItem("multiplayer-name") !== undefined && localStorage.getItem("multiplayer-name").length >0) {
connectInfo.name = localStorage.getItem("multiplayer-name");
}
RED.comms.send('multiplayer/connect', connectInfo)
})
RED.comms.subscribe('multiplayer/#', (topic, msg) => {

View File

@@ -32,24 +32,28 @@ RED.contextMenu = (function () {
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode;
if (hasSelection) {
selection.nodes.forEach(n => {
const nodes = selection.nodes.slice();
while (nodes.length) {
const n = nodes.shift();
if (n.type === 'group') {
hasGroup = true;
nodes.push(...n.nodes);
} else {
isAllGroups = false;
}
if (n.d) {
hasDisabledNode = true;
} else {
hasEnabledNode = true;
if (n.d) {
hasDisabledNode = true;
} else {
hasEnabledNode = true;
}
}
if (n.l === undefined || n.l) {
hasLabeledNode = true;
} else {
hasUnlabeledNode = true;
}
});
}
}
const offset = $("#red-ui-workspace-chart").offset()
let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()

View File

@@ -157,6 +157,12 @@ RED.editor = (function() {
}
}
if (valid && "validate" in definition[property]) {
if (definition[property].hasOwnProperty("required") &&
definition[property].required === false) {
if (value === "") {
return true;
}
}
try {
var opt = {};
if (label) {
@@ -183,6 +189,11 @@ RED.editor = (function() {
});
}
} else if (valid) {
if (definition[property].hasOwnProperty("required") && definition[property].required === false) {
if (value === "") {
return true;
}
}
// If the validator is not provided in node property => Check if the input has a validator
if ("category" in node._def) {
const isConfig = node._def.category === "config";
@@ -413,11 +424,8 @@ RED.editor = (function() {
if (selectedOpt?.data('env')) {
disableButton(addButton, true);
disableButton(editButton, true);
// disable the edit button if no options available
} else if (optionsLength === 1 && selectedOpt.val() === "_ADD_") {
disableButton(addButton, false);
disableButton(editButton, true);
} else if (selectedOpt.val() === "") {
// disable the edit button if no options available or 'none' selected
} else if (optionsLength === 1 || selectedOpt.val() === "_ADD_") {
disableButton(addButton, false);
disableButton(editButton, true);
} else {
@@ -426,14 +434,9 @@ RED.editor = (function() {
}
});
var label = "";
var configNode = RED.nodes.node(nodeValue);
if (configNode) {
label = RED.utils.getNodeLabel(configNode, configNode.id);
}
input.val(label);
// If the value is "", 'add new...' option if no config node available or 'none' option
// Otherwise, it's a config node
select.val(nodeValue || '_ADD_');
}
/**
@@ -934,9 +937,11 @@ RED.editor = (function() {
}
if (!configNodes.length) {
// Add 'add new...' option
select.append('<option value="_ADD_" selected>' + RED._("editor.addNewType", { type: label }) + '</option>');
} else {
select.append('<option value="">' + RED._("editor.inputs.none") + '</option>');
// Add 'none' option
select.append('<option value="_ADD_">' + RED._("editor.inputs.none") + '</option>');
}
window.setTimeout(function() { select.trigger("change");},50);

View File

@@ -165,7 +165,13 @@ RED.editor.codeEditor.monaco = (function() {
//Handles orphaned models
//ensure loaded models that are not explicitly destroyed by a call to .destroy() are disposed
RED.events.on("editor:close",function() {
let models = window.monaco ? monaco.editor.getModels() : null;
if (!window.monaco) { return; }
const editors = window.monaco.editor.getEditors()
const orphanEditors = editors.filter(editor => editor && !document.body.contains(editor.getDomNode()))
orphanEditors.forEach(editor => {
editor.dispose();
});
let models = monaco.editor.getModels()
if(models && models.length) {
console.warn("Cleaning up monaco models left behind. Any node that calls createEditor() should call .destroy().")
for (let index = 0; index < models.length; index++) {
@@ -1124,6 +1130,7 @@ RED.editor.codeEditor.monaco = (function() {
$(el).remove();
$(toolbarRow).remove();
ed.dispose();
}
ed.resize = function resize() {

View File

@@ -11,9 +11,22 @@ RED.editor.mermaid = (function () {
if (!initializing) {
initializing = true
$.getScript(
'vendor/mermaid/mermaid.min.js',
function (data, stat, jqxhr) {
// Find the cache-buster:
let cacheBuster
$('script').each(function (i, el) {
if (!cacheBuster) {
const src = el.getAttribute('src')
const m = /\?v=(.+)$/.exec(src)
if (m) {
cacheBuster = m[1]
}
}
})
$.ajax({
url: `vendor/mermaid/mermaid.min.js?v=${cacheBuster}`,
dataType: "script",
cache: true,
success: function (data, stat, jqxhr) {
mermaid.initialize({
startOnLoad: false,
theme: RED.settings.get('mermaid', {}).theme
@@ -24,7 +37,7 @@ RED.editor.mermaid = (function () {
render(pending)
}
}
)
});
}
} else {
const nodes = document.querySelectorAll(selector)

View File

@@ -1100,7 +1100,7 @@ RED.subflow = (function() {
input.val(val.value);
break;
case "cred":
input = $('<input type="password">').css('width','70%').appendTo(row);
input = $('<input type="password">').css('width','70%').attr('id', elId).appendTo(row);
if (node.credentials) {
if (node.credentials[tenv.name]) {
input.val(node.credentials[tenv.name]);
@@ -1346,7 +1346,7 @@ RED.subflow = (function() {
}
break;
case "cred":
item.value = input.val();
item.value = input.typedInput('value');
item.type = 'cred';
break;
case "spinner":

View File

@@ -103,7 +103,7 @@ RED.sidebar.info.outliner = (function() {
evt.stopPropagation();
RED.search.show("type:subflow:"+n.id);
})
// RED.popover.tooltip(userCountBadge,function() { return RED._('editor.nodesUse',{count:n.users.length})});
RED.popover.tooltip(subflowInstanceBadge,function() { return RED._('subflow.subflowInstances',{count:n.instances.length})});
}
if (n._def.category === "config" && n.type !== "group") {
var userCountBadge = $('<button type="button" class="red-ui-info-outline-item-control-users red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').text(n.users.length).appendTo(controls).on("click",function(evt) {

View File

@@ -259,7 +259,7 @@ $deploy-button-background-disabled-hover: #555;
$header-background: #000;
$header-button-background-active: #121212;
$header-accent: #d41313;
$header-accent: #C02020;
$header-menu-color: #eee;
$header-menu-color-disabled: #666;
$header-menu-heading-color: #fff;

View File

@@ -46,12 +46,12 @@
<div class="form-row inject-time-row hidden" id="inject-time-row-interval">
<span data-i18n="inject.every"></span>
<input id="inject-time-interval-count" class="inject-time-count" value="1"></input>
<input id="inject-time-interval-count" class="inject-time-count" value="1">
<select style="width:100px" id="inject-time-interval-units">
<option value="s" data-i18n="inject.seconds"></option>
<option value="m" data-i18n="inject.minutes"></option>
<option value="h" data-i18n="inject.hours"></option>
</select><br/>
</select><br>
</div>
<div class="form-row inject-time-row hidden" id="inject-time-row-interval-time">
@@ -68,46 +68,46 @@
<option value="20">20</option>
<option value="30">30</option>
<option value="0">60</option>
</select> <span data-i18n="inject.minutes"></span><br/>
</select> <span data-i18n="inject.minutes"></span><br>
<span data-i18n="inject.between"></span> <select id="inject-time-interval-time-start" class="inject-time-times"></select>
<span data-i18n="inject.and"></span> <select id="inject-time-interval-time-end" class="inject-time-times"></select><br/>
<span data-i18n="inject.and"></span> <select id="inject-time-interval-time-end" class="inject-time-times"></select><br>
<div id="inject-time-interval-time-days" class="inject-time-days" style="margin-top:5px">
<div style="display:inline-block; vertical-align:top; margin-right:5px;" data-i18n="inject.on">on</div>
<div style="display:inline-block;">
<div>
<label><input type='checkbox' checked value='1'/> <span data-i18n="inject.days.0"></span></label>
<label><input type='checkbox' checked value='2'/> <span data-i18n="inject.days.1"></span></label>
<label><input type='checkbox' checked value='3'/> <span data-i18n="inject.days.2"></span></label>
<label><input type='checkbox' checked value='1'> <span data-i18n="inject.days.0"></span></label>
<label><input type='checkbox' checked value='2'> <span data-i18n="inject.days.1"></span></label>
<label><input type='checkbox' checked value='3'> <span data-i18n="inject.days.2"></span></label>
</div>
<div>
<label><input type='checkbox' checked value='4'/> <span data-i18n="inject.days.3"></span></label>
<label><input type='checkbox' checked value='5'/> <span data-i18n="inject.days.4"></span></label>
<label><input type='checkbox' checked value='6'/> <span data-i18n="inject.days.5"></span></label>
<label><input type='checkbox' checked value='4'> <span data-i18n="inject.days.3"></span></label>
<label><input type='checkbox' checked value='5'> <span data-i18n="inject.days.4"></span></label>
<label><input type='checkbox' checked value='6'> <span data-i18n="inject.days.5"></span></label>
</div>
<div>
<label><input type='checkbox' checked value='0'/> <span data-i18n="inject.days.6"></span></label>
<label><input type='checkbox' checked value='0'> <span data-i18n="inject.days.6"></span></label>
</div>
</div>
</div>
</div>
<div class="form-row inject-time-row hidden" id="inject-time-row-time">
<span data-i18n="inject.at"></span> <input type="text" id="inject-time-time" value="12:00"></input><br/>
<span data-i18n="inject.at"></span> <input type="text" id="inject-time-time" value="12:00"><br>
<div id="inject-time-time-days" class="inject-time-days">
<div style="display:inline-block; vertical-align:top; margin-right:5px;" data-i18n="inject.on"></div>
<div style="display:inline-block;">
<div>
<label><input type='checkbox' checked value='1'/> <span data-i18n="inject.days.0"></span></label>
<label><input type='checkbox' checked value='2'/> <span data-i18n="inject.days.1"></span></label>
<label><input type='checkbox' checked value='3'/> <span data-i18n="inject.days.2"></span></label>
<label><input type='checkbox' checked value='1'> <span data-i18n="inject.days.0"></span></label>
<label><input type='checkbox' checked value='2'> <span data-i18n="inject.days.1"></span></label>
<label><input type='checkbox' checked value='3'> <span data-i18n="inject.days.2"></span></label>
</div>
<div>
<label><input type='checkbox' checked value='4'/> <span data-i18n="inject.days.3"></span></label>
<label><input type='checkbox' checked value='5'/> <span data-i18n="inject.days.4"></span></label>
<label><input type='checkbox' checked value='6'/> <span data-i18n="inject.days.5"></span></label>
<label><input type='checkbox' checked value='4'> <span data-i18n="inject.days.3"></span></label>
<label><input type='checkbox' checked value='5'> <span data-i18n="inject.days.4"></span></label>
<label><input type='checkbox' checked value='6'> <span data-i18n="inject.days.5"></span></label>
</div>
<div>
<label><input type='checkbox' checked value='0'/> <span data-i18n="inject.days.6"></span></label>
<label><input type='checkbox' checked value='0'> <span data-i18n="inject.days.6"></span></label>
</div>
</div>
</div>

View File

@@ -21,8 +21,8 @@
</div>
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="switch.label.property"></span></label>
<input type="text" id="node-input-property" style="width: calc(100% - 105px)"/>
<input type="hidden" id="node-input-outputs"/>
<input type="text" id="node-input-property" style="width: calc(100% - 105px)">
<input type="hidden" id="node-input-outputs">
</div>
<div class="form-row node-input-rule-container-row">
<ol id="node-input-rule-container"></ol>
@@ -35,7 +35,7 @@
</div>
<div class="form-row">
<input type="checkbox" id="node-input-repair" style="display: inline-block; width: auto; vertical-align: top;">
<label style="width: auto;" for="node-input-repair"><span data-i18n="switch.label.repair"></span></label></input>
<label style="width: auto;" for="node-input-repair"><span data-i18n="switch.label.repair"></span></label>
</div>
</script>

View File

@@ -2,7 +2,7 @@
<script type="text/html" data-template-name="range">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:calc(70% - 1px)"/>
<input type="text" id="node-input-property" style="width:calc(70% - 1px)">
</div>
<div class="form-row">
<label for="node-input-action"><i class="fa fa-dot-circle-o"></i> <span data-i18n="range.label.action"></span></label>
@@ -13,23 +13,23 @@
<option value="drop" data-i18n="range.scale.drop"></option>
</select>
</div>
<br/>
<br>
<div class="form-row"><i class="fa fa-sign-in"></i> <span data-i18n="range.label.inputrange"></span>:</div>
<div class="form-row"><label></label>
<span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minin" data-i18n="[placeholder]range.placeholder.min" style="width:100px;"/>
&nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxin" data-i18n="[placeholder]range.placeholder.maxin" style="width:100px;"/>
<span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minin" data-i18n="[placeholder]range.placeholder.min" style="width:100px;">
&nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxin" data-i18n="[placeholder]range.placeholder.maxin" style="width:100px;">
</div>
<div class="form-row"><i class="fa fa-sign-out"></i> <span data-i18n="range.label.resultrange"></span>:</div>
<div class="form-row"><label></label>
<span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minout" data-i18n="[placeholder]range.placeholder.min" style="width:100px;"/>
&nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxout" data-i18n="[placeholder]range.placeholder.maxout" style="width:100px;"/>
<span data-i18n="range.label.from"></span>: <input type="text" id="node-input-minout" data-i18n="[placeholder]range.placeholder.min" style="width:100px;">
&nbsp;&nbsp;<span data-i18n="range.label.to"></span>: <input type="text" id="node-input-maxout" data-i18n="[placeholder]range.placeholder.maxout" style="width:100px;">
</div>
<br/>
<br>
<div class="form-row"><label></label>
<input type="checkbox" id="node-input-round" style="display: inline-block; width: auto; vertical-align: top;">
<label style="width: auto;" for="node-input-round"><span data-i18n="range.label.roundresult"></span></label></input>
<label style="width: auto;" for="node-input-round"><span data-i18n="range.label.roundresult"></span></label>
</div>
<br/>
<br>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">

View File

@@ -63,7 +63,7 @@
<li><span data-i18n="trigger.label.resetPayload"></span> <input type="text" id="node-input-reset" style="width:150px" data-i18n="[placeholder]trigger.label.resetprompt"></li>
</ul>
</div>
<br/>
<br>
<div class="form-row">
<label data-i18n="trigger.for" for="node-input-bytopic"></label>
<select id="node-input-bytopic" style="width:120px;">
@@ -71,12 +71,12 @@
<option value="topic" data-i18n="trigger.bytopics"></option>
</select>
<span class="form-row" id="node-stream-topic">
<input type="text" id="node-input-topic" style="width:46%;"/>
<input type="text" id="node-input-topic" style="width:46%;">
</span>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></input>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
<input type="hidden" id="node-input-outputs" value="1">
</div>
</script>

View File

@@ -25,7 +25,7 @@
</div>
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
<input type="text" id="node-input-property" style="width:70%;">
</div>
<div class="form-row" style="margin-bottom: 0px;">
<label> </label>
@@ -34,7 +34,7 @@
</div>
<div class="form-row">
<label> </label>
<input type="text" id="node-input-topi" style="width:70%;"/>
<input type="text" id="node-input-topi" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="rbe.label.name"></span></label>

View File

@@ -108,12 +108,13 @@ in your Node-RED user directory (${RED.settings.userDir}).
if (n.proxy && proxyConfig) {
proxyOptions.env = {
no_proxy: (proxyConfig.noproxy || []).join(','),
http_proxy: (proxyConfig.url)
http_proxy: (proxyConfig.url),
https_proxy: (proxyConfig.url)
}
}
return getProxyForUrl(url, proxyOptions)
}
let prox = getProxy(nodeUrl || '')
let prox = nodeUrl ? getProxy(nodeUrl) : null
let timingLog = false;
if (RED.settings.hasOwnProperty("httpRequestTimingLog")) {
@@ -534,9 +535,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
opts.headers[clSet] = opts.headers['content-length'];
delete opts.headers['content-length'];
}
if (!opts.headers.hasOwnProperty('user-agent')) {
opts.headers['user-agent'] = 'Mozilla/5.0 (Node-RED)';
}
if (proxyUrl) {
const match = proxyUrl.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i);
if (match) {
@@ -566,7 +565,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
//need both incase of http -> https redirect
opts.agent = {
http: new HttpProxyAgent(proxyOptions),
https: new HttpProxyAgent(proxyOptions)
https: new HttpsProxyAgent(proxyOptions)
};
} else {

View File

@@ -50,7 +50,7 @@
</div>
<div id="node-row-newline" class="form-row hidden" style="padding-left:110px;">
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional"><br/>
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional"><br>
<input type="checkbox" id="node-input-trim" style="display:inline-block; width:auto; vertical-align:top;"> <span data-i18n="tcpin.label.reattach"></span>
</div>
@@ -317,7 +317,7 @@
<span id="node-units"></span>
</div>
<div id="node-row-newline" class="form-row hidden" style="padding-left:162px;">
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional"><br/>
<span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional"><br>
<input type="checkbox" id="node-input-trim" style="display:inline-block; width:auto; vertical-align:top;"> <span data-i18n="tcpin.label.reattach"></span>
</div>
<div class="form-row">

View File

@@ -35,21 +35,21 @@
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<hr align="middle"/>
<hr align="middle">
<div class="form-row">
<label style="width:100%;"><span data-i18n="csv.label.c2o"></span></label>
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.input"></span></label>
<span data-i18n="csv.label.skip-s"></span>&nbsp;<input type="text" id="node-input-skip" style="width:40px; height:25px;"/>&nbsp;<span data-i18n="csv.label.skip-e"></span><br/>
<span data-i18n="csv.label.skip-s"></span>&nbsp;<input type="text" id="node-input-skip" style="width:40px; height:25px;">&nbsp;<span data-i18n="csv.label.skip-e"></span><br>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-hdrin"><label style="width:auto; margin-top:7px;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></label><br/>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-hdrin"><label style="width:auto; margin-top:7px;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></label><br>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-strings"><label style="width:auto; margin-top:7px;" for="node-input-strings"><span data-i18n="csv.label.usestrings"></span></label><br/>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-strings"><label style="width:auto; margin-top:7px;" for="node-input-strings"><span data-i18n="csv.label.usestrings"></span></label><br>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_empty_strings"><label style="width:auto; margin-top:7px;" for="node-input-include_empty_strings"><span data-i18n="csv.label.include_empty_strings"></span></label><br/>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_empty_strings"><label style="width:auto; margin-top:7px;" for="node-input-include_empty_strings"><span data-i18n="csv.label.include_empty_strings"></span></label><br>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_null_values"><label style="width:auto; margin-top:7px;" for="node-input-include_null_values"><span data-i18n="csv.label.include_null_values"></span></label><br/>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_null_values"><label style="width:auto; margin-top:7px;" for="node-input-include_null_values"><span data-i18n="csv.label.include_null_values"></span></label><br>
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>

View File

@@ -33,7 +33,7 @@
<label for="node-input-chr" style="width: 230px;"><i class="fa fa-tag"></i> <span data-i18n="html.label.prefix"></span></label>
<input type="text" id="node-input-chr" style="text-align:center; width: 40px;" placeholder="_">
</div>
<br/>
<br>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" style="width:70%" data-i18n="[placeholder]common.label.name">

View File

@@ -10,13 +10,13 @@
</div>
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="json.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
<input type="text" id="node-input-property" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<hr align="middle"/>
<hr align="middle">
<div class="form-row node-json-to-json-options">
<label style="width:100%;"><span data-i18n="json.label.o2j"></span></label>
</div>

View File

@@ -2,13 +2,13 @@
<script type="text/html" data-template-name="xml">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
<input type="text" id="node-input-property" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<hr align="middle"/>
<hr align="middle">
<div class="form-row">
<label style="width:100%;"><span data-i18n="xml.label.x2o"></span></label>
</div>

View File

@@ -2,7 +2,7 @@
<script type="text/html" data-template-name="yaml">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
<input type="text" id="node-input-property" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>

View File

@@ -21,12 +21,8 @@
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div>
<div class="form-row">
<label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.split"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<input type="checkbox" id="node-input-addcomplete" style="margin-left:10px; vertical-align:baseline; width:auto;">
<label for="node-input-addcomplete" style="width:auto;" data-i18n="split.addcomplete"></label>
<label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.splitThe"></span></label>
<input type="text" id="node-input-property" style="width:70%;">
</div>
<div class="form-row"><span data-i18n="[html]split.strBuff"></span></div>
<div class="form-row">
@@ -65,7 +61,6 @@
arraySpltType: {value:"len"},
stream: {value:false},
addname: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
addcomplete: {value:false},
property: {value:"payload",required:true}
},
inputs:1,
@@ -127,6 +122,10 @@
<script type="text/html" data-template-name="join">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div class="form-row">
<label data-i18n="join.mode.mode"></label>
<select id="node-input-mode" style="width:200px;">
@@ -162,6 +161,12 @@
<input type="text" id="node-input-joiner" style="width:70%">
<input type="hidden" id="node-input-joinerType">
</div>
<div class="form-row">
<input type="checkbox" id="node-input-useparts" style="margin-left:8px; margin-right:8px; vertical-align:baseline; width:auto;">
<label for="node-input-useparts" style="width:auto;" data-i18n="join.useparts"></label>
</div>
<div class="form-row node-row-trigger" id="trigger-row">
<label style="width:auto;" data-i18n="join.send"></label>
<ul>
@@ -197,13 +202,9 @@
<div class="form-row">
<label>&nbsp;</label>
<input type="checkbox" id="node-input-reduceRight" style="display:inline-block; width:auto; vertical-align:top; margin-left:10px;">
<label for="node-input-reduceRight" style="width:70%;" data-i18n="join.reduce.right" style="margin-left:10px;"/>
<label for="node-input-reduceRight" data-i18n="join.reduce.right" style="width:70%; margin-left:10px;"></label>
</div>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div class="form-tips form-tips-auto hide" data-i18n="[html]join.tip"></div>
</script>
@@ -239,6 +240,7 @@
},
joiner: { value:"\\n"},
joinerType: { value:"str"},
useparts: { value:false },
accumulate: { value:"false" },
timeout: {value:""},
count: {value:""},
@@ -264,6 +266,12 @@
},
oneditprepare: function() {
var node = this;
$("#node-input-useparts").on("change", function(e) {
if (node.useparts === undefined) {
node.useparts = true;
$("#node-input-useparts").attr('checked', true);
}
});
$("#node-input-mode").on("change", function(e) {
var val = $(this).val();

View File

@@ -21,17 +21,13 @@ module.exports = function(RED) {
for (var i = 0; i < array.length-1; i++) {
RED.util.setMessageProperty(msg,node.property,array[i]);
msg.parts.index = node.c++;
if (node.stream !== true) {
msg.parts.count = array.length;
if (node.addcomplete === true) { msg.complete = true; }
}
if (node.stream !== true) { msg.parts.count = array.length; }
send(RED.util.cloneMessage(msg));
}
if (node.stream !== true) {
RED.util.setMessageProperty(msg,node.property,array[i]);
msg.parts.index = node.c++;
msg.parts.count = array.length;
if (node.addcomplete === true) { msg.complete = true; }
send(RED.util.cloneMessage(msg));
node.c = 0;
}
@@ -44,7 +40,6 @@ module.exports = function(RED) {
node.stream = n.stream;
node.spltType = n.spltType || "str";
node.addname = n.addname || "";
node.addcomplete = n.addcomplete || false;
node.property = n.property||"payload";
try {
if (node.spltType === "str") {
@@ -116,7 +111,6 @@ module.exports = function(RED) {
if ((node.stream !== true) || (node.remainder.length === node.splt)) {
RED.util.setMessageProperty(msg,node.property,node.remainder);
msg.parts.index = node.c++;
if (node.addcomplete === true) { msg.complete = true; }
send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
@@ -159,7 +153,6 @@ module.exports = function(RED) {
}
RED.util.setMessageProperty(msg,node.property,m);
msg.parts.index = i;
if (i === count-1 && node.addcomplete === true) { msg.complete = true; }
pos += node.arraySplt;
send(RED.util.cloneMessage(msg));
}
@@ -179,7 +172,6 @@ module.exports = function(RED) {
msg.parts.key = p;
msg.parts.index = j;
msg.parts.count = l;
if (j == l-1 && node.addcomplete === true) { msg.complete = true; }
send(RED.util.cloneMessage(msg));
j += 1;
}
@@ -215,7 +207,6 @@ module.exports = function(RED) {
if ((node.stream !== true) || (node.buffer.length === node.splt)) {
RED.util.setMessageProperty(msg,node.property,node.buffer);
msg.parts.index = node.c++;
if (node.addcomplete === true) { msg.complete = true; }
send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
@@ -262,7 +253,6 @@ module.exports = function(RED) {
RED.util.setMessageProperty(msg,node.property,buff.slice(p,buff.length));
msg.parts.index = node.c++;
msg.parts.count = node.c++;
if (node.addcomplete === true) { msg.complete = true; }
send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
@@ -454,6 +444,8 @@ module.exports = function(RED) {
this.count = Number(n.count || 0);
this.joiner = n.joiner||"";
this.joinerType = n.joinerType||"str";
if (n.useparts === undefined) { this.useparts = true; }
else { this.useparts = n.useparts || false; }
this.reduce = (this.mode === "reduce");
if (this.reduce) {
@@ -621,7 +613,7 @@ module.exports = function(RED) {
return;
}
if (node.mode === 'custom' && msg.hasOwnProperty('parts')) {
if (node.mode === 'custom' && msg.hasOwnProperty('parts') && node.useparts === false ) {
if (msg.parts.hasOwnProperty('parts')) {
msg.parts = { parts: msg.parts.parts };
}

View File

@@ -36,6 +36,10 @@
<label style="margin-left: 10px; width: 175px;" for="node-input-overlap" data-i18n="batch.count.overlap"></label>
<input type="text" id="node-input-overlap" style="width: 50px;">
</div>
<div class="form-row">
<input type="checkbox" id="node-input-honourParts" style="margin-left: 10px; margin-right:10px; vertical-align:top; width:auto;">
<label for="node-input-honourParts" style="width:auto;" data-i18n="batch.honourParts"></label>
</div>
</div>
<div class="node-row-msg-interval">
@@ -45,7 +49,7 @@
<span data-i18n="batch.interval.seconds"></span>
</div>
<div class="form-row">
<input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right: 10px; vertical-align:top; width:auto;">
<input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right:10px; vertical-align:top; width:auto;">
<label for="node-input-allowEmptySequence" style="width:auto;" data-i18n="batch.interval.empty"></label>
</div>
</div>
@@ -101,6 +105,7 @@
}
},
allowEmptySequence: {value:false},
honourParts: {value:false},
topics: {value:[{topic:""}]}
},
inputs:1,

View File

@@ -181,6 +181,8 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n);
var node = this;
var mode = n.mode || "count";
var eof = false;
node.honourParts = n.honourParts || false;
node.pending_count = 0;
if (mode === "count") {
@@ -201,9 +203,12 @@ module.exports = function(RED) {
return;
}
var queue = node.pending;
if (node.honourParts && msg.hasOwnProperty("parts")) {
if (msg.parts.index + 1 === msg.parts.count) { eof = true; }
}
queue.push({msg, send, done});
node.pending_count++;
if (queue.length === count) {
if (queue.length === count || eof === true) {
send_msgs(node, queue, is_overlap);
for (let i = 0; i < queue.length-overlap; i++) {
queue[i].done();
@@ -211,6 +216,7 @@ module.exports = function(RED) {
node.pending =
(overlap === 0) ? [] : queue.slice(-overlap);
node.pending_count = 0;
eof = false;
}
var max_msgs = max_kept_msgs_count(node);
if ((max_msgs > 0) && (node.pending_count > max_msgs)) {

View File

@@ -912,6 +912,7 @@
"objectSend": "Sende eine Nachricht für jedes Schlüssel/Wert-Paar",
"strBuff": "<b>string</b> / <b>buffer</b>",
"array": "<b>array</b>",
"splitThe": "Split",
"splitUsing": "Aufteilung",
"splitLength": "feste Längen von",
"stream": "Als Nachrichtenstrom behandeln (Streaming-Modus)",

View File

@@ -1011,17 +1011,17 @@
"tip": "Tip: The filename should be an absolute path, otherwise it will be relative to the working directory of the Node-RED process."
},
"split": {
"split": "Split",
"split": "split",
"intro": "Split <code>msg.payload</code> based on type:",
"object": "<b>Object</b>",
"objectSend": "Send a message for each key/value pair",
"strBuff": "<b>String</b> / <b>Buffer</b>",
"array": "<b>Array</b>",
"splitThe": "Split the",
"splitUsing": "Split using",
"splitLength": "Fixed length of",
"stream": "Handle as a stream of messages",
"addname": " Copy key to ",
"addcomplete": " Add msg.complete to last element of split."
"addname": " Copy key to "
},
"join": {
"join": "join",
@@ -1047,6 +1047,7 @@
"joinedUsing": "joined using",
"send": "Send the message:",
"afterCount": "After a number of message parts",
"useparts": "Use existing msg.parts property",
"count": "count",
"subsequent": "and every subsequent message.",
"afterTimeout": "After a timeout following the first message",
@@ -1113,6 +1114,7 @@
"too-many": "too many pending messages in batch node",
"unexpected": "unexpected mode",
"no-parts": "no parts property in message",
"honourParts": "Allow msg.parts to also complete batch operation.",
"error": {
"invalid-count": "Invalid count",
"invalid-overlap": "Invalid overlap",

View File

@@ -1017,6 +1017,7 @@
"objectSend": "各key/valueペアのメッセージを送信",
"strBuff": "<b>文字列</b> / <b>バッファ</b>",
"array": "<b>配列</b>",
"splitThe": "に基づく",
"splitUsing": "分割",
"splitLength": "固定長",
"stream": "メッセージのストリームとして処理",

View File

@@ -44,7 +44,7 @@
"global": "contexto global",
"str": "Cadeia de caracteres",
"num": "número",
"bool": "booliano",
"bool": "booliano",
"json": "objeto",
"bin": "Armazenamento temporário",
"date": "Carimbo de data/hora",
@@ -352,8 +352,8 @@
}
},
"trigger": {
"send": "Enviar",
"then": "então",
"send": "Enviar",
"then": "então",
"then-send": "então enviem",
"output": {
"string": "a cadeia de caracteres",
@@ -446,7 +446,7 @@
"staticTopic": "Assinar um tópico único",
"dynamicTopic": "Assinatura dinâmica",
"auto-connect": "Conectar automaticamente",
"auto-mode-depreciated": "Esta opção está deprecada. Favor utilizar o novo modo de auto-detecção."
"auto-mode-depreciated": "Esta opção está deprecada. Favor utilizar o novo modo de auto-detecção."
},
"sections-label": {
"birth-message": "Mensagem enviada na conexão (mensagem de nascimento)",
@@ -466,8 +466,8 @@
"close-topic": "Deixe em branco para desativar a mensagem de fechamento"
},
"state": {
"connected": "Conectado ao negociante: _ broker _",
"disconnected": "Desconectado do negociante: _ broker _",
"connected": "Conectado ao negociante: _ broker _",
"disconnected": "Desconectado do negociante: _ broker _",
"connect-failed": "Falha na conexão com o negociante: __broker__",
"broker-disconnected": "Cliente de negociante __broker__ desconectado: __reasonCode__ __reasonString__"
},
@@ -898,7 +898,7 @@
"o2j": "Objeto para opções JSON",
"pretty": "Formatar cadeia de caracteres JSON",
"action": "Ação",
"property": "Propriedade",
"property": "Propriedade",
"actions": {
"toggle": "Converter entre cadeia de caracteres JSON e Objeto",
"str": "Sempre converter em cadeia de caracteres JSON",
@@ -929,7 +929,7 @@
"write": "escrever arquivo",
"read": "ler arquivo",
"filename": "Nome do arquivo",
"path": "caminho",
"path": "caminho",
"action": "Ação",
"addnewline": "Adicionar nova linha (\\n) a cada carga útil?",
"createdir": "Criar diretório se não existir?",
@@ -994,6 +994,7 @@
"objectSend": "Envia uma mensagem para cada par chave/valor",
"strBuff": "<b>Cadeia de caracteres</b> / <b>Armazenamento Temporário</b>",
"array": "<b>Matriz</b>",
"splitThe": "Dividir",
"splitUsing": "Dividir usando",
"splitLength": "Comprimento fixo de",
"stream": "Tratar como uma transmissão de mensagens",
@@ -1066,9 +1067,9 @@
"batch" : {
"batch": "lote",
"mode": {
"label": "Modo",
"num-msgs": "Agrupar por número de mensagens",
"interval": "Agrupar por intervalo de tempo",
"label": "Modo",
"num-msgs": "Agrupar por número de mensagens",
"interval": "Agrupar por intervalo de tempo",
"concat": "Concatenar sequências"
},
"count": {

View File

@@ -874,6 +874,7 @@
"objectSend":"Отправлять сообщение для каждой пары ключ/значение",
"strBuff":"<b>Строка</b> / <b>Буфер</b>",
"array":"<b>Массив</b>",
"splitThe": "Pазделить",
"splitUsing":"С помощью",
"splitLength":"Фикс. длина",
"stream":"Обрабатывать как поток сообщений",

View File

@@ -997,6 +997,7 @@
"objectSend": "每个键值对作为单个消息发送",
"strBuff": "<b>字符串</b> / <b>Buffer</b>",
"array": "<b>数组</b>",
"splitThe": "Split",
"splitUsing": "拆分使用",
"splitLength": "固定长度",
"stream": "作为消息流处理",

View File

@@ -866,6 +866,7 @@
"objectSend": "每個鍵值對作為單個消息發送",
"strBuff": "<b>字串</b> / <b>Buffer</b>",
"array": "<b>陣列</b>",
"splitThe": "Split",
"splitUsing": "拆分使用",
"splitLength": "固定長度",
"stream": "作為消息流處理",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/nodes",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/registry",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,7 +16,7 @@
}
],
"dependencies": {
"@node-red/util": "4.0.0",
"@node-red/util": "4.1.0-beta.0",
"clone": "2.1.2",
"fs-extra": "11.2.0",
"semver": "7.5.4",

View File

@@ -645,16 +645,27 @@ function getFlow(id) {
if (id !== 'global') {
result.nodes = [];
}
if (flow.groups) {
var nodeIds = Object.keys(flow.groups);
if (nodeIds.length > 0) {
nodeIds.forEach(function(nodeId) {
var node = jsonClone(flow.groups[nodeId]);
delete node.credentials;
result.nodes.push(node)
})
}
}
if (flow.nodes) {
var nodeIds = Object.keys(flow.nodes);
if (nodeIds.length > 0) {
result.nodes = nodeIds.map(function(nodeId) {
nodeIds.forEach(function(nodeId) {
var node = jsonClone(flow.nodes[nodeId]);
if (node.type === 'link out') {
delete node.wires;
}
delete node.credentials;
return node;
result.nodes.push(node)
})
}
}
@@ -680,6 +691,17 @@ function getFlow(id) {
delete node.credentials
return node
});
if (subflow.groups) {
var nodeIds = Object.keys(subflow.groups);
if (nodeIds.length > 0) {
nodeIds.forEach(function(nodeId) {
var node = jsonClone(subflow.groups[nodeId]);
delete node.credentials;
subflow.nodes.push(node)
})
}
delete subflow.groups
}
if (subflow.configs) {
var configIds = Object.keys(subflow.configs);
subflow.configs = configIds.map(function(id) {

View File

@@ -23,14 +23,16 @@ module.exports = {
if (existingSessionId) {
connections.delete(opts.session)
const session = sessions.get(existingSessionId)
session.active = false
session.idleTimeout = setTimeout(() => {
sessions.delete(existingSessionId)
}, 30000)
runtime.events.emit('comms', {
topic: "multiplayer/connection-removed",
data: { session: existingSessionId }
})
if (session) {
session.active = false
session.idleTimeout = setTimeout(() => {
sessions.delete(existingSessionId)
}, 30000)
runtime.events.emit('comms', {
topic: "multiplayer/connection-removed",
data: { session: existingSessionId }
})
}
}
})
runtime.events.on('comms:message:multiplayer/connect', (opts) => {
@@ -40,7 +42,7 @@ module.exports = {
let user = opts.user
if (!user || user.anonymous) {
user = user || { anonymous: true }
user.username = `Anon ${Math.floor(Math.random()*100)}`
user.username = opts?.data?.name || `Anon ${Math.floor(Math.random()*100)}`
}
session = {
session: opts.data.session,
@@ -91,29 +93,31 @@ module.exports = {
const sessionId = connections.get(opts.session)
const session = sessions.get(sessionId)
if (opts.user) {
if (session.user.anonymous !== opts.user.anonymous) {
session.user = opts.user
runtime.events.emit('comms', {
topic: 'multiplayer/connection-added',
excludeSession: opts.session,
data: session
})
if (session) {
if (opts.user) {
if (session.user.anonymous !== opts.user.anonymous) {
session.user = opts.user
runtime.events.emit('comms', {
topic: 'multiplayer/connection-added',
excludeSession: opts.session,
data: session
})
}
}
}
session.location = opts.data
session.location = opts.data
const payload = {
session: sessionId,
workspace: opts.data.workspace,
node: opts.data.node
const payload = {
session: sessionId,
workspace: opts.data.workspace,
node: opts.data.node
}
runtime.events.emit('comms', {
topic: 'multiplayer/location',
data: payload,
excludeSession: opts.session
})
}
runtime.events.emit('comms', {
topic: 'multiplayer/location',
data: payload,
excludeSession: opts.session
})
})
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/runtime",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/registry": "4.0.0",
"@node-red/util": "4.0.0",
"@node-red/registry": "4.1.0-beta.0",
"@node-red/util": "4.1.0-beta.0",
"async-mutex": "0.5.0",
"clone": "2.1.2",
"express": "4.19.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/util",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "4.0.0",
"version": "4.1.0-beta.0",
"description": "Low-code programming for event-driven applications",
"homepage": "https://nodered.org",
"license": "Apache-2.0",
@@ -31,10 +31,10 @@
"flow"
],
"dependencies": {
"@node-red/editor-api": "4.0.0",
"@node-red/runtime": "4.0.0",
"@node-red/util": "4.0.0",
"@node-red/nodes": "4.0.0",
"@node-red/editor-api": "4.1.0-beta.0",
"@node-red/runtime": "4.1.0-beta.0",
"@node-red/util": "4.1.0-beta.0",
"@node-red/nodes": "4.1.0-beta.0",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"cors": "2.8.5",

View File

@@ -17,6 +17,8 @@
var http = require("http");
var https = require("https");
var should = require("should");
var sinon = require("sinon");
var httpProxyHelper = require("nr-test-utils").require("@node-red/nodes/core/network/lib/proxyHelper.js");
var express = require("express");
var bodyParser = require('body-parser');
var stoppable = require('stoppable');
@@ -493,6 +495,7 @@ describe('HTTP Request Node', function() {
});
afterEach(function() {
sinon.restore();
process.env.http_proxy = preEnvHttpProxyLowerCase;
process.env.HTTP_PROXY = preEnvHttpProxyUpperCase;
// On Windows, if environment variable of NO_PROXY that includes lower cases
@@ -1799,27 +1802,80 @@ describe('HTTP Request Node', function() {
})
});
//Removing HTTP Proxy testcases as GOT + Proxy_Agent doesn't work with mock'd proxy
/* */
it('should use http_proxy', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')},
{id:"n2", type:"helper"}];
it('should use env var http_proxy', function(done) {
const url = getTestURL('/postInspect')
const proxyUrl = "http://localhost:" + testProxyPort
const flow = [
{ id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url },
{ id: "n2", type: "helper" },
];
const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl')
const testNode = [httpRequestNode, httpProxyNode];
deleteProxySetting();
process.env.http_proxy = "http://localhost:" + testProxyPort;
helper.load(httpRequestNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('statusCode',200);
msg.payload.should.have.property('headers');
//msg.payload.headers.should.have.property('x-testproxy-header','foobar');
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:"foo"});
process.env.http_proxy = proxyUrl
helper.load(testNode, flow, function (msg) {
try {
// static URL set in the nodes configuration and the proxy will be setup upon initialisation
proxySpy.calledOnce.should.be.true()
proxySpy.calledWith(url, { }).should.be.true()
proxySpy.returnValues[0].should.be.equal(proxyUrl)
done()
} catch (err) {
done(err);
}
});
});
it('should use env var https_proxy', function(done) {
const url = getSslTestURL('/postInspect')
const proxyUrl = "http://localhost:" + testProxyPort
const flow = [
{ id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url },
{ id: "n2", type: "helper" },
];
const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl')
const testNode = [httpRequestNode, httpProxyNode];
deleteProxySetting();
process.env.https_proxy = proxyUrl
helper.load(testNode, flow, function (msg) {
try {
// static URL set in the nodes configuration and the proxy will be setup upon initialisation
proxySpy.calledOnce.should.be.true()
proxySpy.calledWith(url, { }).should.be.true()
proxySpy.returnValues[0].should.be.equal(proxyUrl)
done()
} catch (err) {
done(err);
}
});
});
it('should not use env var http*_proxy when no_proxy is set', function(done) {
const url = getSslTestURL('/postInspect')
const proxyUrl = "http://localhost:" + testProxyPort
const flow = [
{ id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url },
{ id: "n2", type: "helper" },
];
const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl')
const testNode = [httpRequestNode, httpProxyNode];
deleteProxySetting();
process.env.http_proxy = proxyUrl
process.env.https_proxy = proxyUrl
process.env.no_proxy = "localhost"
helper.load(testNode, flow, function (msg) {
try {
// static URL set in the nodes configuration and the proxy will be setup upon initialisation
proxySpy.calledOnce.should.be.true()
proxySpy.calledWith(url, { }).should.be.true()
proxySpy.returnValues[0].should.be.equal('')
done()
} catch (err) {
done(err);
}
});
});
@@ -1997,6 +2053,135 @@ describe('HTTP Request Node', function() {
});
});
it('should use UI proxy for statically configured URL', function (done) {
const url = getTestURL('/postInspect')
const proxyUrl = "http://localhost:" + testProxyPort
const flow = [
{ id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url, proxy: "n3" },
{ id: "n2", type: "helper" },
{ id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo"] }
];
const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl')
const testNode = [httpRequestNode, httpProxyNode];
deleteProxySetting();
// static URL set in the nodes configuration will cause the proxy setup to be called
// no no need to send a message to the node
helper.load(testNode, flow, function () {
try {
// ensure getProxyForUrl was called and returned the correct proxy URL
proxySpy.calledOnce.should.be.true()
proxySpy.calledWith(url, { env: { no_proxy: "foo", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true()
proxySpy.returnValues[0].should.be.equal(proxyUrl)
done();
} catch (err) {
done(err);
}
});
});
it('should use UI proxy for HTTP URL passed in via msg', function (done) {
const url = getTestURL('/postInspect')
const proxyUrl = "http://localhost:" + testProxyPort
const flow = [
{ id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: "", proxy: "n3" },
{ id: "n2", type: "helper" },
{ id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo,bar"] }
];
const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl')
const testNode = [httpRequestNode, httpProxyNode];
deleteProxySetting();
helper.load(testNode, flow, function () {
const n1 = helper.getNode("n1");
const n2 = helper.getNode("n2");
try {
proxySpy.calledOnce.should.be.false() // proxy setup should not be called when there is no URL to check needs proxying
} catch (err) {
done(err);
return
}
n2.on("input", function (msg) {
try {
// ensure getProxyForUrl was called and returned the correct proxy URL
proxySpy.calledOnce.should.be.true()
proxySpy.calledWith(url, { env: { no_proxy: "foo,bar", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true()
proxySpy.returnValues[0].should.be.equal(proxyUrl)
done();
} catch (err) {
done(err);
}
});
n1.receive({ url: url });
});
});
it('should use UI proxy for HTTPS URL passed in via msg', function (done) {
const url = getSslTestURL('/postInspect')
const proxyUrl = "http://localhost:" + testProxyPort
const flow = [
{ id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: "", proxy: "n3" },
{ id: "n2", type: "helper" },
{ id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo,bar,baz"] }
];
const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl')
const testNode = [httpRequestNode, httpProxyNode];
deleteProxySetting();
helper.load(testNode, flow, function () {
const n1 = helper.getNode("n1");
const n2 = helper.getNode("n2");
try {
proxySpy.calledOnce.should.be.false() // proxy setup should not be called when there is no URL to check needs proxying
} catch (err) {
done(err);
return
}
n2.on("input", function (msg) {
try {
// ensure getProxyForUrl was called and returned the correct proxy URL
proxySpy.calledOnce.should.be.true()
proxySpy.calledWith(url, { env: { no_proxy: "foo,bar,baz", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true()
proxySpy.returnValues[0].should.be.equal(proxyUrl)
done();
} catch (err) {
done(err);
}
});
n1.receive({ url: url });
});
});
it('should not use UI proxy if noproxy excludes it', function (done) {
const url = getSslTestURL('/postInspect')
const proxyUrl = "http://localhost:" + testProxyPort
const flow = [
{ id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: "", proxy: "n3" },
{ id: "n2", type: "helper" },
{ id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo,localhost,baz"] }
];
const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl')
const testNode = [httpRequestNode, httpProxyNode];
deleteProxySetting();
helper.load(testNode, flow, function () {
const n1 = helper.getNode("n1");
const n2 = helper.getNode("n2");
try {
proxySpy.calledOnce.should.be.false() // proxy setup should not be called when there is no URL to check needs proxying
} catch (err) {
done(err);
return
}
n2.on("input", function (msg) {
try {
// ensure getProxyForUrl was called and returned no proxy
proxySpy.calledOnce.should.be.true()
proxySpy.calledWith(url, { env: { no_proxy: "foo,localhost,baz", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true()
proxySpy.returnValues[0].should.be.equal('')
done();
} catch (err) {
done(err);
}
});
n1.receive({ url: url });
});
});
});
describe('authentication', function() {

View File

@@ -98,7 +98,7 @@ describe('BATCH node', function() {
var n2 = helper.getNode("n2");
check_data(n1, n2, results, done);
for(var i = 0; i < 6; i++) {
n1.receive({payload: i});
n1.receive({payload: i, parts: { count:6, index:i }});
}
});
}
@@ -168,6 +168,25 @@ describe('BATCH node', function() {
check_count(flow, results, done);
});
it('should create seq. with count (more sent than count)', function(done) {
var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 4, overlap: 0, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = [
[0, 1, 2, 3]
];
check_count(flow, results, done);
});
it('should create seq. with count and terminate early if parts honoured', function(done) {
var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 4, overlap: 0, interval: 10, allowEmptySequence:false, honourParts:true, topics: [], wires:[["n2"]]},
{id:"n2", type:"helper"}];
var results = [
[0, 1, 2, 3],
[4, 5]
];
check_count(flow, results, done);
});
it('should create seq. with count and overlap', function(done) {
var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 3, overlap: 2, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]},
{id:"n2", type:"helper"}];
@@ -455,7 +474,7 @@ describe('BATCH node', function() {
function mapiDoneTestHelper(done, mode, count, overlap, interval, allowEmptySequence, msgAndTimings) {
const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js");
const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js");
const flow = [{id:"batchNode1", type:"batch", name: "BatchNode", mode, count, overlap, interval,
const flow = [{id:"batchNode1", type:"batch", name: "BatchNode", mode, count, overlap, interval,
allowEmptySequence, topics: [{topic: "TA"}], wires:[[]]},
{id:"completeNode1",type:"complete",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]},
{id:"catchNode1", type:"catch",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]},
@@ -482,13 +501,13 @@ describe('BATCH node', function() {
}
it('should call done() when message is sent (mode: count)', function(done) {
mapiDoneTestHelper(done, "count", 2, 0, 2, false, [
mapiDoneTestHelper(done, "count", 2, 0, 2, false, [
{ msg: {payload: 0}, delay: 0, avr: 0, var: 100},
{ msg: {payload: 1}, delay: 0, avr: 0, var: 100}
]);
});
it('should call done() when reset (mode: count)', function(done) {
mapiDoneTestHelper(done, "count", 2, 0, 2, false, [
mapiDoneTestHelper(done, "count", 2, 0, 2, false, [
{ msg: {payload: 0}, delay: 0, avr: 200, var: 100},
{ msg: {payload: 1, reset:true}, delay: 200, avr: 200, var: 100}
]);