mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
104 Commits
bcrypt-bum
...
allow-name
Author | SHA1 | Date | |
---|---|---|---|
|
d457c81547 | ||
|
83acc4836b | ||
|
db51307e7f | ||
|
a2430b772b | ||
|
b3acd1a588 | ||
|
8af821d380 | ||
|
97ee6c6745 | ||
|
b5fb15cd9b | ||
|
998219ae9a | ||
|
e8f0f80f65 | ||
|
45936285cc | ||
|
ff35c46d5d | ||
|
05c924a9df | ||
|
14ac309b6e | ||
|
29b128e5e0 | ||
|
c0f1581370 | ||
|
9420a52ec7 | ||
|
c113b3de13 | ||
|
29058c163a | ||
|
bb110ea230 | ||
|
785f220cd8 | ||
|
16570410a5 | ||
|
8085eda431 | ||
|
6503498f0a | ||
|
da787a9993 | ||
|
c873b57094 | ||
|
93974ccd92 | ||
|
d7aa792f97 | ||
|
375fa9da64 | ||
|
28c41e17ad | ||
|
da3ad40968 | ||
|
2464d9ad95 | ||
|
011b47a108 | ||
|
ea747711c3 | ||
|
19a8fa09a8 | ||
|
1b5b3f7f88 | ||
|
2123514c76 | ||
|
379fbd7c7e | ||
|
efdc1b1a1d | ||
|
52bbd82e18 | ||
|
20a9c051be | ||
|
830e475969 | ||
|
f75e2f221c | ||
|
5a75440668 | ||
|
53d8b97fff | ||
|
c85667cc13 | ||
|
1a8b37b4e3 | ||
|
40a2d90e08 | ||
|
ee269caa4a | ||
|
d820686e5a | ||
|
aa2a585e00 | ||
|
f9e6bccd46 | ||
|
3230654ecd | ||
|
a5b53ee373 | ||
|
ac420247ae | ||
|
61198bd7e3 | ||
|
9c511b6674 | ||
|
9d054543a7 | ||
|
fc3ec2a0d7 | ||
|
e7ef73222f | ||
|
6623e56a1e | ||
|
582eca1877 | ||
|
2783100f84 | ||
|
8c5ddd68a4 | ||
|
cb0c484579 | ||
|
a1bf270ba6 | ||
|
be5694f149 | ||
|
4ff364e2c3 | ||
|
2fa6f35873 | ||
|
2a4fb7123d | ||
|
38a77d2b78 | ||
|
dc239db256 | ||
|
a622d19ba7 | ||
|
9842d9116c | ||
|
19ea8f8515 | ||
|
4ba3c937a8 | ||
|
dbd3f0f85b | ||
|
48a2876c48 | ||
|
10398b05d8 | ||
|
3a91fc17fd | ||
|
02893d3e78 | ||
|
5124bc6bf8 | ||
|
3952a23ba3 | ||
|
1048b16f3c | ||
|
bbbbb1b1e0 | ||
|
14b452c996 | ||
|
bb91a08939 | ||
|
bffa923f05 | ||
|
526b3fda91 | ||
|
27fc89ba33 | ||
|
d70b7ea924 | ||
|
1d342a778d | ||
|
476016cbcc | ||
|
bd2c020e84 | ||
|
da7c7ede02 | ||
|
34ed9c5cd8 | ||
|
47dd08e74a | ||
|
6aae50294f | ||
|
ec2f6ec46f | ||
|
36805b6872 | ||
|
d78cb2fec7 | ||
|
ca37d1ec9d | ||
|
282d52cf0b | ||
|
ba08cf0417 |
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -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 }}
|
||||
|
55
CHANGELOG.md
55
CHANGELOG.md
@@ -1,3 +1,58 @@
|
||||
#### 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
|
||||
those added since the last beta. Check the beta release details below for the complete
|
||||
list.
|
||||
|
||||
Breaking Changes
|
||||
|
||||
- Node-RED now requires Node 18.x or later. At the time of release, we recommend
|
||||
using Node 20.
|
||||
|
||||
Editor
|
||||
|
||||
- Add `httpStaticCors` (#4761) @knolleary
|
||||
- Update dependencies (#4763) @knolleary
|
||||
- Sync master to dev (#4756) @knolleary
|
||||
- Add tooltip and message validation to `typedInput` (#4747) @GogoVega
|
||||
- Replace bcrypt with @node-rs/bcrypt (#4744) @knolleary
|
||||
- Export Nodes dialog refinement (#4746) @Steve-Mcl
|
||||
|
||||
#### 4.0.0-beta.4: Beta Release
|
||||
|
||||
Editor
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "4.0.0-beta.4",
|
||||
"version": "4.1.0-beta.0",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "https://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -64,7 +64,7 @@
|
||||
"mqtt": "5.7.0",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"mustache": "4.2.0",
|
||||
"node-red-admin": "^3.1.3",
|
||||
"node-red-admin": "^4.0.0",
|
||||
"node-watch": "0.7.4",
|
||||
"nopt": "5.0.0",
|
||||
"oauth2orize": "1.12.0",
|
||||
@@ -79,7 +79,7 @@
|
||||
"tough-cookie": "4.1.4",
|
||||
"uglify-js": "3.17.4",
|
||||
"uuid": "9.0.1",
|
||||
"ws": "7.5.6",
|
||||
"ws": "7.5.10",
|
||||
"xml2js": "0.6.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "4.0.0-beta.4",
|
||||
"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-beta.4",
|
||||
"@node-red/editor-client": "4.0.0-beta.4",
|
||||
"@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",
|
||||
@@ -32,7 +32,7 @@
|
||||
"passport-http-bearer": "1.0.1",
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
"passport": "0.7.0",
|
||||
"ws": "7.5.6"
|
||||
"ws": "7.5.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@node-rs/bcrypt": "1.10.4"
|
||||
|
@@ -27,7 +27,8 @@
|
||||
"lock": "Lock",
|
||||
"unlock": "Unlock",
|
||||
"locked": "Locked",
|
||||
"unlocked": "Unlocked"
|
||||
"unlocked": "Unlocked",
|
||||
"format": "Format"
|
||||
},
|
||||
"type": {
|
||||
"string": "string",
|
||||
|
@@ -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",
|
||||
|
@@ -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": "数値",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "4.0.0-beta.4",
|
||||
"version": "4.1.0-beta.0",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -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) => {
|
||||
|
@@ -2406,6 +2406,13 @@ RED.nodes = (function() {
|
||||
} else {
|
||||
delete n.g
|
||||
}
|
||||
// If importing into a subflow, ensure an outbound-link doesn't get added
|
||||
if (activeSubflow && /^link /.test(n.type) && n.links) {
|
||||
n.links = n.links.filter(function(id) {
|
||||
const otherNode = node_map[id] || RED.nodes.node(id);
|
||||
return (otherNode && otherNode.z === activeWorkspace);
|
||||
});
|
||||
}
|
||||
for (var d3 in n._def.defaults) {
|
||||
if (n._def.defaults.hasOwnProperty(d3)) {
|
||||
if (n._def.defaults[d3].type) {
|
||||
@@ -2429,14 +2436,6 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// If importing into a subflow, ensure an outbound-link doesn't
|
||||
// get added
|
||||
if (activeSubflow && /^link /.test(n.type) && n.links) {
|
||||
n.links = n.links.filter(function(id) {
|
||||
const otherNode = node_map[id] || RED.nodes.node(id);
|
||||
return (otherNode && otherNode.z === activeWorkspace)
|
||||
});
|
||||
}
|
||||
}
|
||||
for (i=0;i<new_subflows.length;i++) {
|
||||
n = new_subflows[i];
|
||||
|
@@ -26,6 +26,7 @@ RED.clipboard = (function() {
|
||||
var currentPopoverError;
|
||||
var activeTab;
|
||||
var libraryBrowser;
|
||||
var clipboardTabs;
|
||||
|
||||
var activeLibraries = {};
|
||||
|
||||
@@ -215,6 +216,13 @@ RED.clipboard = (function() {
|
||||
open: function( event, ui ) {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
beforeClose: function(e) {
|
||||
if (clipboardTabs && activeTab === "red-ui-clipboard-dialog-export-tab-clipboard") {
|
||||
const jsonTabIndex = clipboardTabs.getTabIndex('red-ui-clipboard-dialog-export-tab-clipboard-json')
|
||||
const activeTabIndex = clipboardTabs.activeIndex()
|
||||
RED.settings.set("editor.dialog.export.json-view", activeTabIndex === jsonTabIndex )
|
||||
}
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
if (popover) {
|
||||
@@ -228,12 +236,23 @@ RED.clipboard = (function() {
|
||||
|
||||
exportNodesDialog =
|
||||
'<div class="form-row">'+
|
||||
'<label style="width:auto;margin-right: 10px;" data-i18n="common.label.export"></label>'+
|
||||
'<span id="red-ui-clipboard-dialog-export-rng-group" class="button-group">'+
|
||||
'<a id="red-ui-clipboard-dialog-export-rng-selected" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
|
||||
'<a id="red-ui-clipboard-dialog-export-rng-flow" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
|
||||
'<a id="red-ui-clipboard-dialog-export-rng-full" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
|
||||
'</span>'+
|
||||
'<div style="display: flex; justify-content: space-between;">'+
|
||||
'<div class="form-row">'+
|
||||
'<label style="width:auto;margin-right: 10px;" data-i18n="common.label.export"></label>'+
|
||||
'<span id="red-ui-clipboard-dialog-export-rng-group" class="button-group">'+
|
||||
'<a id="red-ui-clipboard-dialog-export-rng-selected" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
|
||||
'<a id="red-ui-clipboard-dialog-export-rng-flow" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
|
||||
'<a id="red-ui-clipboard-dialog-export-rng-full" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
|
||||
'</span>'+
|
||||
'</div>'+
|
||||
'<div class="form-row">'+
|
||||
'<label style="width:auto;margin-right: 10px;" data-i18n="common.label.format"></label>'+
|
||||
'<span id="red-ui-clipboard-dialog-export-fmt-group" class="button-group">'+
|
||||
'<a id="red-ui-clipboard-dialog-export-fmt-mini" class="red-ui-button red-ui-button toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
|
||||
'<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
|
||||
'</span>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="red-ui-clipboard-dialog-box">'+
|
||||
'<div class="red-ui-clipboard-dialog-tabs">'+
|
||||
@@ -248,15 +267,9 @@ RED.clipboard = (function() {
|
||||
'<div id="red-ui-clipboard-dialog-export-tab-clipboard-preview-list"></div>'+
|
||||
'</div>'+
|
||||
'<div class="red-ui-clipboard-dialog-export-tab-clipboard-tab" id="red-ui-clipboard-dialog-export-tab-clipboard-json">'+
|
||||
'<div class="form-row" style="height:calc(100% - 40px)">'+
|
||||
'<div class="form-row" style="height:calc(100% - 10px)">'+
|
||||
'<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+
|
||||
'</div>'+
|
||||
'<div class="form-row" style="text-align: right;">'+
|
||||
'<span id="red-ui-clipboard-dialog-export-fmt-group" class="button-group">'+
|
||||
'<a id="red-ui-clipboard-dialog-export-fmt-mini" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
|
||||
'<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
|
||||
'</span>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="form-row" id="red-ui-clipboard-dialog-export-tab-library-filename">'+
|
||||
@@ -569,7 +582,7 @@ RED.clipboard = (function() {
|
||||
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(exportNodesDialog));
|
||||
|
||||
clipboardTabs = null
|
||||
var tabs = RED.tabs.create({
|
||||
id: "red-ui-clipboard-dialog-export-tabs",
|
||||
vertical: true,
|
||||
@@ -630,7 +643,7 @@ RED.clipboard = (function() {
|
||||
$("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
|
||||
$("#red-ui-clipboard-dialog-export").button("enable");
|
||||
|
||||
var clipboardTabs = RED.tabs.create({
|
||||
clipboardTabs = RED.tabs.create({
|
||||
id: "red-ui-clipboard-dialog-export-tab-clipboard-tabs",
|
||||
onchange: function(tab) {
|
||||
$(".red-ui-clipboard-dialog-export-tab-clipboard-tab").hide();
|
||||
@@ -647,6 +660,9 @@ RED.clipboard = (function() {
|
||||
id: "red-ui-clipboard-dialog-export-tab-clipboard-json",
|
||||
label: RED._("editor.types.json")
|
||||
});
|
||||
if (RED.settings.get("editor.dialog.export.json-view") === true) {
|
||||
clipboardTabs.activateTab("red-ui-clipboard-dialog-export-tab-clipboard-json");
|
||||
}
|
||||
|
||||
var previewList = $("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
|
||||
data: []
|
||||
|
@@ -358,61 +358,64 @@
|
||||
{ value: "_session", source: ["websocket out","tcp out"] },
|
||||
]
|
||||
var allOptions = {
|
||||
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions)},
|
||||
flow: {value:"flow",label:"flow.",hasValue:true,
|
||||
options:[],
|
||||
validate:RED.utils.validatePropertyExpression,
|
||||
msg: { value: "msg", label: "msg.", validate: RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions) },
|
||||
flow: { value: "flow", label: "flow.", hasValue: true,
|
||||
options: [],
|
||||
validate: RED.utils.validatePropertyExpression,
|
||||
parse: contextParse,
|
||||
export: contextExport,
|
||||
valueLabel: contextLabel,
|
||||
autoComplete: contextAutoComplete
|
||||
},
|
||||
global: {value:"global",label:"global.",hasValue:true,
|
||||
options:[],
|
||||
validate:RED.utils.validatePropertyExpression,
|
||||
global: {
|
||||
value: "global", label: "global.", hasValue: true,
|
||||
options: [],
|
||||
validate: RED.utils.validatePropertyExpression,
|
||||
parse: contextParse,
|
||||
export: contextExport,
|
||||
valueLabel: contextLabel,
|
||||
autoComplete: contextAutoComplete
|
||||
},
|
||||
str: {value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
|
||||
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate: function(v) {
|
||||
return (true === RED.utils.validateTypedProperty(v, "num"));
|
||||
str: { value: "str", label: "string", icon: "red/images/typedInput/az.svg" },
|
||||
num: { value: "num", label: "number", icon: "red/images/typedInput/09.svg", validate: function (v, o) {
|
||||
return RED.utils.validateTypedProperty(v, "num", o);
|
||||
} },
|
||||
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg",options:["true","false"]},
|
||||
bool: { value: "bool", label: "boolean", icon: "red/images/typedInput/bool.svg", options: ["true", "false"] },
|
||||
json: {
|
||||
value:"json",
|
||||
label:"JSON",
|
||||
icon:"red/images/typedInput/json.svg",
|
||||
validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}},
|
||||
expand: function() {
|
||||
value: "json",
|
||||
label: "JSON",
|
||||
icon: "red/images/typedInput/json.svg",
|
||||
validate: function (v, o) {
|
||||
return RED.utils.validateTypedProperty(v, "json", o);
|
||||
},
|
||||
expand: function () {
|
||||
var that = this;
|
||||
var value = this.value();
|
||||
try {
|
||||
value = JSON.stringify(JSON.parse(value),null,4);
|
||||
} catch(err) {
|
||||
value = JSON.stringify(JSON.parse(value), null, 4);
|
||||
} catch (err) {
|
||||
}
|
||||
RED.editor.editJSON({
|
||||
value: value,
|
||||
stateId: RED.editor.generateViewStateId("typedInput", that, "json"),
|
||||
focus: true,
|
||||
complete: function(v) {
|
||||
complete: function (v) {
|
||||
var value = v;
|
||||
try {
|
||||
value = JSON.stringify(JSON.parse(v));
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
}
|
||||
that.value(value);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
|
||||
re: { value: "re", label: "regular expression", icon: "red/images/typedInput/re.svg" },
|
||||
date: {
|
||||
value:"date",
|
||||
label:"timestamp",
|
||||
icon:"fa fa-clock-o",
|
||||
options:[
|
||||
value: "date",
|
||||
label: "timestamp",
|
||||
icon: "fa fa-clock-o",
|
||||
options: [
|
||||
{
|
||||
label: 'milliseconds since epoch',
|
||||
value: ''
|
||||
@@ -431,15 +434,17 @@
|
||||
value: "jsonata",
|
||||
label: "expression",
|
||||
icon: "red/images/typedInput/expr.svg",
|
||||
validate: function(v) { try{jsonata(v);return true;}catch(e){return false;}},
|
||||
expand:function() {
|
||||
validate: function (v, o) {
|
||||
return RED.utils.validateTypedProperty(v, "jsonata", o);
|
||||
},
|
||||
expand: function () {
|
||||
var that = this;
|
||||
RED.editor.editExpression({
|
||||
value: this.value().replace(/\t/g,"\n"),
|
||||
value: this.value().replace(/\t/g, "\n"),
|
||||
stateId: RED.editor.generateViewStateId("typedInput", that, "jsonata"),
|
||||
focus: true,
|
||||
complete: function(v) {
|
||||
that.value(v.replace(/\n/g,"\t"));
|
||||
complete: function (v) {
|
||||
that.value(v.replace(/\n/g, "\t"));
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -448,13 +453,13 @@
|
||||
value: "bin",
|
||||
label: "buffer",
|
||||
icon: "red/images/typedInput/bin.svg",
|
||||
expand: function() {
|
||||
expand: function () {
|
||||
var that = this;
|
||||
RED.editor.editBuffer({
|
||||
value: this.value(),
|
||||
stateId: RED.editor.generateViewStateId("typedInput", that, "bin"),
|
||||
focus: true,
|
||||
complete: function(v) {
|
||||
complete: function (v) {
|
||||
that.value(v);
|
||||
}
|
||||
})
|
||||
@@ -470,9 +475,9 @@
|
||||
value: "node",
|
||||
label: "node",
|
||||
icon: "red/images/typedInput/target.svg",
|
||||
valueLabel: function(container,value) {
|
||||
valueLabel: function (container, value) {
|
||||
var node = RED.nodes.node(value);
|
||||
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).css({
|
||||
var nodeDiv = $('<div>', { class: "red-ui-search-result-node" }).css({
|
||||
"margin-top": "2px",
|
||||
"margin-left": "3px"
|
||||
}).appendTo(container);
|
||||
@@ -481,117 +486,117 @@
|
||||
"margin-left": "6px"
|
||||
}).appendTo(container);
|
||||
if (node) {
|
||||
var colour = RED.utils.getNodeColor(node.type,node._def);
|
||||
var icon_url = RED.utils.getNodeIcon(node._def,node);
|
||||
var colour = RED.utils.getNodeColor(node.type, node._def);
|
||||
var icon_url = RED.utils.getNodeIcon(node._def, node);
|
||||
if (node.type === 'tab') {
|
||||
colour = "#C0DEED";
|
||||
}
|
||||
nodeDiv.css('backgroundColor',colour);
|
||||
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
||||
nodeDiv.css('backgroundColor', colour);
|
||||
var iconContainer = $('<div/>', { class: "red-ui-palette-icon-container" }).appendTo(nodeDiv);
|
||||
RED.utils.createIconElement(icon_url, iconContainer, true);
|
||||
var l = RED.utils.getNodeLabel(node,node.id);
|
||||
var l = RED.utils.getNodeLabel(node, node.id);
|
||||
nodeLabel.text(l);
|
||||
} else {
|
||||
nodeDiv.css({
|
||||
'backgroundColor': '#eee',
|
||||
'border-style' : 'dashed'
|
||||
'border-style': 'dashed'
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
expand: function() {
|
||||
expand: function () {
|
||||
var that = this;
|
||||
RED.tray.hide();
|
||||
RED.view.selectNodes({
|
||||
single: true,
|
||||
selected: [that.value()],
|
||||
onselect: function(selection) {
|
||||
onselect: function (selection) {
|
||||
that.value(selection.id);
|
||||
RED.tray.show();
|
||||
},
|
||||
oncancel: function() {
|
||||
oncancel: function () {
|
||||
RED.tray.show();
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
cred:{
|
||||
value:"cred",
|
||||
label:"credential",
|
||||
icon:"fa fa-lock",
|
||||
cred: {
|
||||
value: "cred",
|
||||
label: "credential",
|
||||
icon: "fa fa-lock",
|
||||
inputType: "password",
|
||||
valueLabel: function(container,value) {
|
||||
valueLabel: function (container, value) {
|
||||
var that = this;
|
||||
container.css("pointer-events","none");
|
||||
container.css("flex-grow",0);
|
||||
container.css("pointer-events", "none");
|
||||
container.css("flex-grow", 0);
|
||||
this.elementDiv.hide();
|
||||
var buttons = $('<div>').css({
|
||||
position: "absolute",
|
||||
right:"6px",
|
||||
right: "6px",
|
||||
top: "6px",
|
||||
"pointer-events":"all"
|
||||
"pointer-events": "all"
|
||||
}).appendTo(container);
|
||||
var eyeButton = $('<button type="button" class="red-ui-button red-ui-button-small"></button>').css({
|
||||
width:"20px"
|
||||
}).appendTo(buttons).on("click", function(evt) {
|
||||
width: "20px"
|
||||
}).appendTo(buttons).on("click", function (evt) {
|
||||
evt.preventDefault();
|
||||
var cursorPosition = that.input[0].selectionStart;
|
||||
var currentType = that.input.attr("type");
|
||||
if (currentType === "text") {
|
||||
that.input.attr("type","password");
|
||||
that.input.attr("type", "password");
|
||||
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
that.input.focus();
|
||||
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
|
||||
},50);
|
||||
}, 50);
|
||||
} else {
|
||||
that.input.attr("type","text");
|
||||
that.input.attr("type", "text");
|
||||
eyeCon.removeClass("fa-eye").addClass("fa-eye-slash");
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
that.input.focus();
|
||||
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
|
||||
},50);
|
||||
}, 50);
|
||||
}
|
||||
}).hide();
|
||||
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-2px").appendTo(eyeButton);
|
||||
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left", "-2px").appendTo(eyeButton);
|
||||
|
||||
if (value === "__PWRD__") {
|
||||
var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({
|
||||
padding:"6px 6px",
|
||||
borderRadius:"4px"
|
||||
padding: "6px 6px",
|
||||
borderRadius: "4px"
|
||||
}).addClass("red-ui-typedInput-value-label-inactive").appendTo(container);
|
||||
var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function(evt) {
|
||||
var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function (evt) {
|
||||
evt.preventDefault();
|
||||
innerContainer.hide();
|
||||
container.css("background","none");
|
||||
container.css("pointer-events","none");
|
||||
container.css("background", "none");
|
||||
container.css("pointer-events", "none");
|
||||
that.input.val("");
|
||||
that.element.val("");
|
||||
that.elementDiv.show();
|
||||
editButton.hide();
|
||||
cancelButton.show();
|
||||
eyeButton.show();
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
that.input.focus();
|
||||
},50);
|
||||
}, 50);
|
||||
});
|
||||
var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left","3px").appendTo(buttons).on("click", function(evt) {
|
||||
var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left", "3px").appendTo(buttons).on("click", function (evt) {
|
||||
evt.preventDefault();
|
||||
innerContainer.show();
|
||||
container.css("background","");
|
||||
container.css("background", "");
|
||||
that.input.val("__PWRD__");
|
||||
that.element.val("__PWRD__");
|
||||
that.elementDiv.hide();
|
||||
editButton.show();
|
||||
cancelButton.hide();
|
||||
eyeButton.hide();
|
||||
that.input.attr("type","password");
|
||||
that.input.attr("type", "password");
|
||||
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
|
||||
|
||||
}).hide();
|
||||
} else {
|
||||
container.css("background","none");
|
||||
container.css("pointer-events","none");
|
||||
container.css("background", "none");
|
||||
container.css("pointer-events", "none");
|
||||
this.elementDiv.show();
|
||||
eyeButton.show();
|
||||
}
|
||||
@@ -1538,26 +1543,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
validate: function() {
|
||||
var result;
|
||||
var value = this.value();
|
||||
var type = this.type();
|
||||
validate: function(options) {
|
||||
let valid = true;
|
||||
const value = this.value();
|
||||
const type = this.type();
|
||||
if (this.typeMap[type] && this.typeMap[type].validate) {
|
||||
var val = this.typeMap[type].validate;
|
||||
if (typeof val === 'function') {
|
||||
result = val(value);
|
||||
const validate = this.typeMap[type].validate;
|
||||
if (typeof validate === 'function') {
|
||||
valid = validate(value, {});
|
||||
} else {
|
||||
result = val.test(value);
|
||||
// Regex
|
||||
valid = validate.test(value);
|
||||
if (!valid) {
|
||||
valid = RED._("validator.errors.invalid-regexp");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((typeof valid === "string") || !valid) {
|
||||
this.element.addClass("input-error");
|
||||
this.uiSelect.addClass("input-error");
|
||||
if (typeof valid === "string") {
|
||||
let tooltip = this.element.data("tooltip");
|
||||
if (tooltip) {
|
||||
tooltip.setContent(valid);
|
||||
} else {
|
||||
tooltip = RED.popover.tooltip(this.elementDiv, valid);
|
||||
this.element.data("tooltip", tooltip);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
this.element.removeClass("input-error");
|
||||
this.uiSelect.removeClass("input-error");
|
||||
const tooltip = this.element.data("tooltip");
|
||||
if (tooltip) {
|
||||
this.element.data("tooltip", null);
|
||||
tooltip.delete();
|
||||
}
|
||||
}
|
||||
if (result) {
|
||||
this.uiSelect.removeClass('input-error');
|
||||
} else {
|
||||
this.uiSelect.addClass('input-error');
|
||||
if (options?.returnErrorMessage === true) {
|
||||
return valid;
|
||||
}
|
||||
return result;
|
||||
// Must return a boolean for no 3.x validator
|
||||
return (typeof valid === "string") ? false : valid;
|
||||
},
|
||||
show: function() {
|
||||
this.uiSelect.show();
|
||||
|
@@ -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()
|
||||
|
@@ -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";
|
||||
@@ -190,7 +201,10 @@ RED.editor = (function() {
|
||||
const input = $("#"+prefix+"-"+property);
|
||||
const isTypedInput = input.length > 0 && input.next(".red-ui-typedInput-container").length > 0;
|
||||
if (isTypedInput) {
|
||||
valid = input.typedInput("validate");
|
||||
valid = input.typedInput("validate", { returnErrorMessage: true });
|
||||
if (typeof valid === "string") {
|
||||
return label ? label + ": " + valid : valid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,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 {
|
||||
@@ -423,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_');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -931,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);
|
||||
|
@@ -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() {
|
||||
|
@@ -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)
|
||||
|
@@ -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":
|
||||
|
@@ -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) {
|
||||
|
@@ -901,11 +901,25 @@ RED.utils = (function() {
|
||||
return parts;
|
||||
}
|
||||
|
||||
function validatePropertyExpression(str) {
|
||||
/**
|
||||
* Validate a property expression
|
||||
* @param {*} str - the property value
|
||||
* @returns {boolean|string} whether the node proprty is valid. `true`: valid `false|String`: invalid
|
||||
*/
|
||||
function validatePropertyExpression(str, opt) {
|
||||
try {
|
||||
var parts = normalisePropertyExpression(str);
|
||||
const parts = normalisePropertyExpression(str);
|
||||
return true;
|
||||
} catch(err) {
|
||||
// If the validator has opt, it is a 3.x validator that
|
||||
// can return a String to mean 'invalid' and provide a reason
|
||||
if (opt) {
|
||||
if (opt.label) {
|
||||
return opt.label + ': ' + err.message;
|
||||
}
|
||||
return err.message;
|
||||
}
|
||||
// Otherwise, a 2.x returns a false value
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -923,22 +937,24 @@ RED.utils = (function() {
|
||||
// Allow ${ENV_VAR} value
|
||||
return true
|
||||
}
|
||||
let error
|
||||
let error;
|
||||
if (propertyType === 'json') {
|
||||
try {
|
||||
JSON.parse(propertyValue);
|
||||
} catch(err) {
|
||||
error = RED._("validator.errors.invalid-json", {
|
||||
error: err.message
|
||||
})
|
||||
});
|
||||
}
|
||||
} else if (propertyType === 'msg' || propertyType === 'flow' || propertyType === 'global' ) {
|
||||
if (!RED.utils.validatePropertyExpression(propertyValue)) {
|
||||
error = RED._("validator.errors.invalid-prop")
|
||||
// To avoid double label
|
||||
const valid = RED.utils.validatePropertyExpression(propertyValue, opt ? {} : null);
|
||||
if (valid !== true) {
|
||||
error = opt ? valid : RED._("validator.errors.invalid-prop");
|
||||
}
|
||||
} else if (propertyType === 'num') {
|
||||
if (!/^NaN$|^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$|^[+-]?(0b|0B)[01]+$|^[+-]?(0o|0O)[0-7]+$|^[+-]?(0x|0X)[0-9a-fA-F]+$/.test(propertyValue)) {
|
||||
error = RED._("validator.errors.invalid-num")
|
||||
error = RED._("validator.errors.invalid-num");
|
||||
}
|
||||
} else if (propertyType === 'jsonata') {
|
||||
try {
|
||||
@@ -946,16 +962,16 @@ RED.utils = (function() {
|
||||
} catch(err) {
|
||||
error = RED._("validator.errors.invalid-expr", {
|
||||
error: err.message
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
if (opt && opt.label) {
|
||||
return opt.label+': '+error
|
||||
return opt.label + ': ' + error;
|
||||
}
|
||||
return error
|
||||
return error;
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
function getMessageProperty(msg,expr) {
|
||||
|
@@ -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;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
version: "4.0.0-beta.4",
|
||||
version: "4.0.0",
|
||||
steps: [
|
||||
{
|
||||
titleIcon: "fa fa-map-o",
|
||||
@@ -91,37 +91,6 @@ export default {
|
||||
<p>C'est un petit changement, mais cela devrait faciliter le travail avec vos noeuds de configuration.</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Remembering palette state",
|
||||
"ja": "パレットの状態を維持",
|
||||
"fr": "Mémorisation de l'état de la palette"
|
||||
},
|
||||
description: {
|
||||
"en-US": `<p>The palette now remembers what categories you have hidden between reloads - as well as any
|
||||
filter you have applied.</p>`,
|
||||
"ja": `<p>パレット上で非表示にしたカテゴリや適用したフィルタが、リロードしても記憶されるようになりました。</p>`,
|
||||
"fr": `<p>La palette se souvient désormais des catégories que vous avez masquées entre les rechargements,
|
||||
ainsi que le filtre que vous avez appliqué.</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Plugins shown in the Palette Manager",
|
||||
"ja": "パレット管理にプラグインを表示",
|
||||
"fr": "Affichage des Plugins dans le gestionnaire de palettes"
|
||||
},
|
||||
image: 'images/nr4-plugins.png',
|
||||
description: {
|
||||
"en-US": `<p>The palette manager now shows any plugin modules you have installed, such as
|
||||
<code>node-red-debugger</code>. Previously they would only be shown if the plugins include
|
||||
nodes for the palette.</p>`,
|
||||
"ja": `<p>パレットの管理に <code>node-red-debugger</code> の様なインストールしたプラグインが表示されます。以前はプラグインにパレット向けのノードが含まれている時のみ表示されていました。</p>`,
|
||||
"fr": `<p>Le gestionnaire de palettes affiche désormais tous les plugins que vous avez installés,
|
||||
tels que <code>node-red-debugger</code>. Auparavant, ils n'étaient affichés que s'ils contenaient
|
||||
des noeuds pour la palette.</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Timestamp formatting options",
|
||||
@@ -194,6 +163,37 @@ export default {
|
||||
`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Remembering palette state",
|
||||
"ja": "パレットの状態を維持",
|
||||
"fr": "Mémorisation de l'état de la palette"
|
||||
},
|
||||
description: {
|
||||
"en-US": `<p>The palette now remembers what categories you have hidden between reloads - as well as any
|
||||
filter you have applied.</p>`,
|
||||
"ja": `<p>パレット上で非表示にしたカテゴリや適用したフィルタが、リロードしても記憶されるようになりました。</p>`,
|
||||
"fr": `<p>La palette se souvient désormais des catégories que vous avez masquées entre les rechargements,
|
||||
ainsi que le filtre que vous avez appliqué.</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Plugins shown in the Palette Manager",
|
||||
"ja": "パレット管理にプラグインを表示",
|
||||
"fr": "Affichage des Plugins dans le gestionnaire de palettes"
|
||||
},
|
||||
image: 'images/nr4-plugins.png',
|
||||
description: {
|
||||
"en-US": `<p>The palette manager now shows any plugin modules you have installed, such as
|
||||
<code>node-red-debugger</code>. Previously they would only be shown if the plugins include
|
||||
nodes for the palette.</p>`,
|
||||
"ja": `<p>パレットの管理に <code>node-red-debugger</code> の様なインストールしたプラグインが表示されます。以前はプラグインにパレット向けのノードが含まれている時のみ表示されていました。</p>`,
|
||||
"fr": `<p>Le gestionnaire de palettes affiche désormais tous les plugins que vous avez installés,
|
||||
tels que <code>node-red-debugger</code>. Auparavant, ils n'étaient affichés que s'ils contenaient
|
||||
des noeuds pour la palette.</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Node Updates",
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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;"/>
|
||||
<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;">
|
||||
<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;"/>
|
||||
<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;">
|
||||
<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">
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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 {
|
||||
|
@@ -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">
|
||||
|
@@ -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> <input type="text" id="node-input-skip" style="width:40px; height:25px;"/> <span data-i18n="csv.label.skip-e"></span><br/>
|
||||
<span data-i18n="csv.label.skip-s"></span> <input type="text" id="node-input-skip" style="width:40px; height:25px;"> <span data-i18n="csv.label.skip-e"></span><br>
|
||||
<label> </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> </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> </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> </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>
|
||||
|
@@ -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">
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -17,8 +17,12 @@
|
||||
<script type="text/html" data-template-name="split">
|
||||
<!-- <div class="form-row"><span data-i18n="[html]split.intro"></span></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%;"/>
|
||||
<label for="node-input-name"><i class="fa fa-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-row">
|
||||
<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">
|
||||
@@ -43,10 +47,6 @@
|
||||
<label for="node-input-addname-cb" style="width:auto;" data-i18n="split.addname"></label>
|
||||
<input type="text" id="node-input-addname" style="width:70%">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-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>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -122,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;">
|
||||
@@ -157,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>
|
||||
@@ -192,13 +202,9 @@
|
||||
<div class="form-row">
|
||||
<label> </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>
|
||||
|
||||
@@ -234,6 +240,7 @@
|
||||
},
|
||||
joiner: { value:"\\n"},
|
||||
joinerType: { value:"str"},
|
||||
useparts: { value:false },
|
||||
accumulate: { value:"false" },
|
||||
timeout: {value:""},
|
||||
count: {value:""},
|
||||
@@ -259,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();
|
||||
|
@@ -444,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) {
|
||||
@@ -611,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 };
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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)) {
|
||||
|
@@ -20,12 +20,26 @@
|
||||
<dt class="optional">delay <span class="property-type">number</span></dt>
|
||||
<dd>Legt die Verzögerung in Millisekunden fest, die auf die Nachricht angewendet werden soll.
|
||||
Zur Nutzung dieser Option muss <i>Verzög. mit msg.delay überschreibbar</i> aktiviert sein.</dd>
|
||||
<dt class="optional">rate <span class="property-type">number</span></dt>
|
||||
<dd>Setzt die Verzögerung in Millisekunden zwischen den Nachrichten. Diese Node überschreibt die
|
||||
bestehende Verzögerung die in der Node konfiguration, wenn die empfangende Nachricht <code>msg.rate</code>
|
||||
in Millisekunden enthält. Dies trifft nur zu, wenn in der Node konfiguriert ist, das empfangene
|
||||
Nachrichten den konfigurierten Wert überschreiben können.</dd>
|
||||
<dt class="optional">reset</dt>
|
||||
<dd>Wenn bei der empfangenen Nachricht diese Eigenschaft auf einen beliebigen Wert gesetzt ist,
|
||||
werden alle im Node gepufferten Nachrichten gelöscht.</dd>
|
||||
<dt class="optional">flush</dt>
|
||||
<dd>Wenn bei der empfangenen Nachricht diese Eigenschaft auf einen beliebigen Wert gesetzt ist,
|
||||
werden alle im Node gepufferten Nachrichten sofort gesendet.</dd>
|
||||
<dt class="optional">flush</dt>
|
||||
<dd>Wenn bei der empfangenen Nachricht diese Eigenschaft auf einen numerischen Wert gesetzt ist,
|
||||
wird diese Anzahl an Nachrichten sofort gesendet. Wenn ein anderer Typ gesetzt ist (z.B. Boolean),
|
||||
werden alle in der Node gepufferten Nachrichten gesendet.</dd>
|
||||
<dt class="optional">toFront</dt>
|
||||
<dd>Wenn diese Eigenschaft im Ratenbegrenzungsmodus für die empfangene Nachricht auf den booleschen Wert
|
||||
<code>true</code> gesetzt ist, Anschließend wird die Nachricht an den Anfang der Warteschlange verschoben
|
||||
und als nächstes freigegeben. Dies kann in Kombination mit <code>msg.flush=1</code> verwendet werden, um sofort erneut zu senden.
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Details</h3>
|
||||
<p>Wenn Verzögerung als Nachrichtenaktion eingestellt ist, kann die Verzögerungszeit ein fixer Wert,
|
||||
|
@@ -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)",
|
||||
|
@@ -1011,12 +1011,13 @@
|
||||
"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",
|
||||
@@ -1046,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",
|
||||
@@ -1112,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",
|
||||
|
@@ -1017,6 +1017,7 @@
|
||||
"objectSend": "各key/valueペアのメッセージを送信",
|
||||
"strBuff": "<b>文字列</b> / <b>バッファ</b>",
|
||||
"array": "<b>配列</b>",
|
||||
"splitThe": "に基づく",
|
||||
"splitUsing": "分割",
|
||||
"splitLength": "固定長",
|
||||
"stream": "メッセージのストリームとして処理",
|
||||
|
@@ -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": {
|
||||
|
@@ -874,6 +874,7 @@
|
||||
"objectSend":"Отправлять сообщение для каждой пары ключ/значение",
|
||||
"strBuff":"<b>Строка</b> / <b>Буфер</b>",
|
||||
"array":"<b>Массив</b>",
|
||||
"splitThe": "Pазделить",
|
||||
"splitUsing":"С помощью",
|
||||
"splitLength":"Фикс. длина",
|
||||
"stream":"Обрабатывать как поток сообщений",
|
||||
|
@@ -997,6 +997,7 @@
|
||||
"objectSend": "每个键值对作为单个消息发送",
|
||||
"strBuff": "<b>字符串</b> / <b>Buffer</b>",
|
||||
"array": "<b>数组</b>",
|
||||
"splitThe": "Split",
|
||||
"splitUsing": "拆分使用",
|
||||
"splitLength": "固定长度",
|
||||
"stream": "作为消息流处理",
|
||||
|
@@ -866,6 +866,7 @@
|
||||
"objectSend": "每個鍵值對作為單個消息發送",
|
||||
"strBuff": "<b>字串</b> / <b>Buffer</b>",
|
||||
"array": "<b>陣列</b>",
|
||||
"splitThe": "Split",
|
||||
"splitUsing": "拆分使用",
|
||||
"splitLength": "固定長度",
|
||||
"stream": "作為消息流處理",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/nodes",
|
||||
"version": "4.0.0-beta.4",
|
||||
"version": "4.1.0-beta.0",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -43,7 +43,7 @@
|
||||
"raw-body": "2.5.2",
|
||||
"tough-cookie": "4.1.4",
|
||||
"uuid": "9.0.1",
|
||||
"ws": "7.5.6",
|
||||
"ws": "7.5.10",
|
||||
"xml2js": "0.6.2",
|
||||
"iconv-lite": "0.6.3"
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/registry",
|
||||
"version": "4.0.0-beta.4",
|
||||
"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-beta.4",
|
||||
"@node-red/util": "4.1.0-beta.0",
|
||||
"clone": "2.1.2",
|
||||
"fs-extra": "11.2.0",
|
||||
"semver": "7.5.4",
|
||||
|
@@ -1,5 +1,6 @@
|
||||
const flowUtil = require("./util");
|
||||
const credentials = require("../nodes/credentials");
|
||||
const clone = require("clone");
|
||||
|
||||
/**
|
||||
* This class represents a group within the runtime.
|
||||
|
@@ -462,9 +462,8 @@ function stop(type,diff,muteLog,isDeploy) {
|
||||
if (type === 'nodes') {
|
||||
stopList = diff.changed.concat(diff.removed);
|
||||
} else if (type === 'flows') {
|
||||
stopList = diff.changed.concat(diff.removed).concat(diff.linked);
|
||||
stopList = diff.changed.concat(diff.removed).concat(diff.linked).concat(diff.rewired);
|
||||
}
|
||||
|
||||
events.emit("flows:stopping",{config: activeConfig, type: type, diff: diff})
|
||||
|
||||
// Stop the global flow object last
|
||||
@@ -646,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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -681,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) {
|
||||
|
@@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/runtime",
|
||||
"version": "4.0.0-beta.4",
|
||||
"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-beta.4",
|
||||
"@node-red/util": "4.0.0-beta.4",
|
||||
"@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",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/util",
|
||||
"version": "4.0.0-beta.4",
|
||||
"version": "4.1.0-beta.0",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
13
packages/node_modules/node-red/package.json
vendored
13
packages/node_modules/node-red/package.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "4.0.0-beta.4",
|
||||
"version": "4.1.0-beta.0",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "https://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -31,15 +31,16 @@
|
||||
"flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/editor-api": "4.0.0-beta.4",
|
||||
"@node-red/runtime": "4.0.0-beta.4",
|
||||
"@node-red/util": "4.0.0-beta.4",
|
||||
"@node-red/nodes": "4.0.0-beta.4",
|
||||
"@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",
|
||||
"express": "4.19.2",
|
||||
"fs-extra": "11.2.0",
|
||||
"node-red-admin": "^3.1.3",
|
||||
"node-red-admin": "^4.0.0",
|
||||
"nopt": "5.0.0",
|
||||
"semver": "7.5.4"
|
||||
},
|
||||
|
8
packages/node_modules/node-red/red.js
vendored
8
packages/node_modules/node-red/red.js
vendored
@@ -44,6 +44,8 @@ var nopt = require("nopt");
|
||||
var path = require("path");
|
||||
const os = require("os")
|
||||
var fs = require("fs-extra");
|
||||
const cors = require('cors');
|
||||
|
||||
var RED = require("./lib/red.js");
|
||||
|
||||
var server;
|
||||
@@ -441,10 +443,16 @@ httpsPromise.then(function(startupHttps) {
|
||||
const thisRoot = sp.root || "/";
|
||||
const options = sp.options;
|
||||
const middleware = sp.middleware;
|
||||
const corsOptions = sp.cors || settings.httpStaticCors;
|
||||
if(appUseMem[filePath + "::" + thisRoot]) {
|
||||
continue;// this path and root already registered!
|
||||
}
|
||||
appUseMem[filePath + "::" + thisRoot] = true;
|
||||
if (corsOptions) {
|
||||
const corsHandler = cors(corsOptions);
|
||||
app.options(thisRoot, corsHandler)
|
||||
app.use(thisRoot, corsHandler)
|
||||
}
|
||||
if (settings.httpStaticAuth) {
|
||||
app.use(thisRoot, basicAuthMiddleware(settings.httpStaticAuth.user, settings.httpStaticAuth.pass));
|
||||
}
|
||||
|
14
packages/node_modules/node-red/settings.js
vendored
14
packages/node_modules/node-red/settings.js
vendored
@@ -139,6 +139,7 @@ module.exports = {
|
||||
* - httpNodeMiddleware
|
||||
* - httpStatic
|
||||
* - httpStaticRoot
|
||||
* - httpStaticCors
|
||||
******************************************************************************/
|
||||
|
||||
/** the tcp port that the Node-RED web server is listening on */
|
||||
@@ -233,6 +234,9 @@ module.exports = {
|
||||
* OR multiple static sources can be created using an array of objects...
|
||||
* Each object can also contain an options object for further configuration.
|
||||
* See https://expressjs.com/en/api.html#express.static for available options.
|
||||
* They can also contain an option `cors` object to set specific Cross-Origin
|
||||
* Resource Sharing rules for the source. `httpStaticCors` can be used to
|
||||
* set a default cors policy across all static routes.
|
||||
*/
|
||||
//httpStatic: [
|
||||
// {path: '/home/nol/pics/', root: "/img/"},
|
||||
@@ -250,6 +254,16 @@ module.exports = {
|
||||
*/
|
||||
//httpStaticRoot: '/static/',
|
||||
|
||||
/** The following property can be used to configure cross-origin resource sharing
|
||||
* in the http static routes.
|
||||
* See https://github.com/troygoode/node-cors#configuration-options for
|
||||
* details on its contents. The following is a basic permissive set of options:
|
||||
*/
|
||||
//httpStaticCors: {
|
||||
// origin: "*",
|
||||
// methods: "GET,PUT,POST,DELETE"
|
||||
//},
|
||||
|
||||
/** The following property can be used to modify proxy options */
|
||||
// proxyOptions: {
|
||||
// mode: "legacy", // legacy mode is for non-strict previous proxy determination logic (node-red < v4 compatible)
|
||||
|
@@ -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() {
|
||||
|
||||
|
@@ -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}
|
||||
]);
|
||||
|
@@ -16,6 +16,31 @@ describe('Group', function () {
|
||||
group.getSetting("NR_GROUP_NAME").should.equal("g1")
|
||||
group.getSetting("NR_GROUP_ID").should.equal("group1")
|
||||
})
|
||||
it("returns cloned env var property", async function () {
|
||||
const group = new Group({
|
||||
getSetting: v => v+v
|
||||
}, {
|
||||
name: "g1",
|
||||
id: "group1",
|
||||
env: [
|
||||
{
|
||||
name: 'jsonEnvVar',
|
||||
type: 'json',
|
||||
value: '{"a":1}'
|
||||
}
|
||||
]
|
||||
})
|
||||
await group.start()
|
||||
const result = group.getSetting('jsonEnvVar')
|
||||
result.should.have.property('a', 1)
|
||||
result.a = 2
|
||||
result.b = 'hello'
|
||||
|
||||
const result2 = group.getSetting('jsonEnvVar')
|
||||
result2.should.have.property('a', 1)
|
||||
result2.should.not.have.property('b')
|
||||
|
||||
})
|
||||
it("delegates to parent if not found", async function () {
|
||||
const group = new Group({
|
||||
getSetting: v => v+v
|
||||
|
Reference in New Issue
Block a user