@inteli.city/node-red-contrib-http-plus 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * http.auth.config+ — Authentication config node for http.in+
3
+ * Supports: none, Basic Auth (simple + multi-user), AWS Cognito JWT (JWKS)
4
+ */
5
+
6
+ module.exports = function(RED) {
7
+ 'use strict';
8
+
9
+ function HttpAuthConfigNode(n) {
10
+ RED.nodes.createNode(this, n);
11
+ this.name = n.name;
12
+ this.authType = n.authType || 'none';
13
+ this.useJsonUsers = n.useJsonUsers || false;
14
+ this.usersJson = n.usersJson || '';
15
+ this.jwksUrl = n.jwksUrl || '';
16
+ this.audience = n.audience || '';
17
+ this.issuer = n.issuer || '';
18
+ this.exposeUser = n.exposeUser || false;
19
+ this.userMapping = n.userMapping || '';
20
+ // credentials.username / credentials.password managed by Node-RED
21
+ }
22
+
23
+ RED.nodes.registerType('http.auth.config+', HttpAuthConfigNode, {
24
+ credentials: {
25
+ username: { type: 'text' },
26
+ password: { type: 'password' }
27
+ }
28
+ });
29
+ };
package/httpin+.html ADDED
@@ -0,0 +1,494 @@
1
+ <!--
2
+ Copyright JS Foundation and other contributors, http://js.foundation
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ -->
16
+
17
+ <style>
18
+ #node-input-auth-help code,
19
+ #node-input-upload-help code,
20
+ #node-input-zod-help code {
21
+ font-size: 12px;
22
+ background: none;
23
+ padding: 0;
24
+ }
25
+ </style>
26
+
27
+ <script type="text/html" data-help-name="http.in+">
28
+ <p>Receives an HTTP request and starts a flow. Supports built-in authentication and Zod request validation.</p>
29
+
30
+ <h3>Outputs</h3>
31
+ <dl class="message-properties">
32
+ <dt>payload <span class="property-type">object | string</span></dt>
33
+ <dd>Request body (POST/PUT/PATCH) or query object (GET).</dd>
34
+ <dt>req <span class="property-type">object</span></dt>
35
+ <dd>Express request object. Key sub-properties:
36
+ <ul>
37
+ <li><code>req.query</code> — query string as an object</li>
38
+ <li><code>req.params</code> — route parameters (e.g. <code>{ id: "42" }</code>)</li>
39
+ <li><code>req.headers</code> — request headers</li>
40
+ </ul>
41
+ </dd>
42
+ <dt>res <span class="property-type">object</span></dt>
43
+ <dd>Response handle. Must be passed unmodified to a connected <b>http.response+</b> node.</dd>
44
+ <dt>files <span class="property-type">array</span></dt>
45
+ <dd>Uploaded files. Only present when file upload is enabled. Each entry contains <code>fieldname</code>, <code>originalname</code>, <code>mimetype</code>, <code>size</code>, <code>storage</code>, and either <code>buffer</code> (memory) or <code>path</code> (disk).</dd>
46
+ <dt>validated <span class="property-type">object</span></dt>
47
+ <dd>Parsed and validated request object from Zod. Only present when validation is enabled and passes. <code>msg.payload</code> is never modified.</dd>
48
+ <dt>user <span class="property-type">string | object</span></dt>
49
+ <dd>Authenticated user identity. For Basic Auth: the username string. For Cognito: an object with fields mapped from the JWT payload — only present when "Expose user to flow" is enabled in the auth config node.</dd>
50
+ </dl>
51
+
52
+ <h3>Request Validation</h3>
53
+ <p>Enable <b>Zod validation</b> in the node editor to validate every incoming request before it enters the flow.
54
+ Full Zod documentation at <a href="https://zod.dev/api" target="_blank">zod.dev/api</a>.</p>
55
+ <p>The schema receives:</p>
56
+ <pre>{ body, query, params, files }</pre>
57
+ <p>If validation passes, <code>msg.validated</code> contains the parsed result. <code>msg.payload</code> is never modified.</p>
58
+ <p>If validation fails, the node automatically returns <code>400 invalid_request</code> — no downstream node runs.</p>
59
+ <p><b>Example — validate a POST body:</b></p>
60
+ <pre>z.object({
61
+ body: z.object({
62
+ name: z.string().min(1),
63
+ age: z.number().int().min(0)
64
+ })
65
+ })</pre>
66
+ <p><b>Query and param values are always strings.</b> Use <code>z.coerce</code> to convert them, or use transforms to normalise values:</p>
67
+ <pre>z.object({
68
+ query: z.object({
69
+ term: z.string().trim().toLowerCase(),
70
+ page: z.coerce.number().int().min(1)
71
+ })
72
+ })</pre>
73
+
74
+ <h3>Authentication</h3>
75
+ <p>Attach an <b>http.auth.config+</b> config node to enable authentication (None / Basic Auth / AWS Cognito JWT).
76
+ Failed authentication returns <code>401</code> and stops the flow.</p>
77
+ <p><b>Basic Auth — single-user:</b> credentials stored securely via Node-RED's credentials system. <code>msg.user</code> is set to the authenticated username.</p>
78
+ <p><b>Basic Auth — multiple users:</b> provide a JSON map of <code>username: password</code> pairs.
79
+ Passwords are stored in plain text in the flow — use only in trusted environments.</p>
80
+ <p><b>Cognito JWT:</b> validates a Bearer token against a JWKS endpoint. Enable <b>"Expose user to flow"</b> in the config node to populate <code>msg.user</code> with mapped JWT claims. When disabled, no JWT data enters the flow.</p>
81
+ <p>After successful authentication, <code>Authorization</code> headers and <code>?token=</code> query parameters are always removed before the message is dispatched.</p>
82
+
83
+ <h3>Details</h3>
84
+ <p>The URL field supports Express route patterns, including named parameters such as <code>/users/:id</code>.</p>
85
+ <p>For more examples see the package <a href="https://github.com/inteli-city/node-red-contrib-http-plus">README</a>.</p>
86
+ </script>
87
+
88
+ <script type="text/html" data-help-name="http.out+">
89
+ <p>Sends an HTTP response back to the caller. Must be used with a flow started by <b>http.in+</b>.</p>
90
+
91
+ <h3>Inputs</h3>
92
+ <dl class="message-properties">
93
+ <dt>payload <span class="property-type">string | object | buffer</span></dt>
94
+ <dd>Response body. Objects are serialised to JSON automatically.</dd>
95
+ <dt>stream <span class="property-type">readable stream</span></dt>
96
+ <dd>If set, the stream is piped directly to the response instead of sending <code>msg.payload</code>. Useful for large files or proxied streams.</dd>
97
+ <dt>statusCode <span class="property-type">number</span></dt>
98
+ <dd>HTTP status code. Defaults to <code>200</code>.</dd>
99
+ <dt>headers <span class="property-type">object</span></dt>
100
+ <dd>Additional response headers. Applied in both normal and streaming modes.</dd>
101
+ <dt>cookies <span class="property-type">object</span></dt>
102
+ <dd>Cookies to set or clear in the response.</dd>
103
+ </dl>
104
+
105
+ <h3>Details</h3>
106
+ <p>This node is the standard response pair for <b>http.in+</b>.</p>
107
+ <p>It is fully compatible with the built-in Node-RED <i>http response</i> node, with one key addition: support for streaming responses.</p>
108
+ <p>The <code>msg.res</code> object from the originating <b>http.in+</b> must reach this node unchanged.</p>
109
+
110
+ <h3>Difference from the standard node</h3>
111
+ <p>The only behavioral difference from the built-in <i>http response</i> node is support for streaming.</p>
112
+ <ul>
113
+ <li>If <code>msg.stream</code> is set, the stream is piped directly to the HTTP response.</li>
114
+ <li>If not, the node behaves exactly like the standard <i>http response</i> node.</li>
115
+ </ul>
116
+
117
+ <h3>Streaming</h3>
118
+ <p>Set <code>msg.stream</code> to a readable stream (e.g. <code>fs.createReadStream("/tmp/file.zip")</code>) to stream the response.</p>
119
+ <p>When <code>msg.stream</code> is not provided, the node falls back to standard behavior using <code>msg.payload</code>.</p>
120
+ <p><code>msg.statusCode</code> and <code>msg.headers</code> work the same in both modes.</p>
121
+ </script>
122
+
123
+ <script type="text/html" data-template-name="http.in+">
124
+ <div class="form-row">
125
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
126
+ <input type="text" id="node-input-name" placeholder="Name">
127
+ </div>
128
+ <div class="form-row">
129
+ <label for="node-input-method"><i class="fa fa-exchange"></i> Method</label>
130
+ <select type="text" id="node-input-method" style="width:70%;">
131
+ <option value="get">GET</option>
132
+ <option value="post">POST</option>
133
+ <option value="put">PUT</option>
134
+ <option value="delete">DELETE</option>
135
+ <option value="patch">PATCH</option>
136
+ </select>
137
+ </div>
138
+ <div class="form-row">
139
+ <label for="node-input-url"><i class="fa fa-globe"></i> URL</label>
140
+ <input id="node-input-url" type="text" placeholder="/url">
141
+ </div>
142
+ <div class="form-row">
143
+ <label for="node-input-authConfig"><i class="fa fa-shield"></i> Auth</label>
144
+ <input type="text" id="node-input-authConfig">
145
+ </div>
146
+ <div id="node-input-auth-help" style="font-size:12px; color:#666; margin: -4px 0 8px 110px; line-height:1.5;"></div>
147
+ <div class="form-row row-swagger-doc">
148
+ <label for="node-input-swaggerDoc"><i class="fa fa-file-text-o"></i> <span data-i18n="httpin.label.doc"></span></label>
149
+ <input type="text" id="node-input-swaggerDoc">
150
+ </div>
151
+ <div class="form-row upload-section">
152
+ <input type="checkbox" id="node-input-enableUpload" style="display:inline-block; width:auto; vertical-align:top;">
153
+ <label for="node-input-enableUpload" style="width:auto;"><i class="fa fa-upload"></i> Enable file uploads</label>
154
+ </div>
155
+ <div class="form-row upload-settings-row">
156
+ <label for="node-input-uploadStorage"><i class="fa fa-database"></i> Storage</label>
157
+ <select id="node-input-uploadStorage" style="width:70%;">
158
+ <option value="memory">Memory (buffer)</option>
159
+ <option value="disk">Disk (temp files)</option>
160
+ </select>
161
+ </div>
162
+ <div class="form-row upload-settings-row">
163
+ <label for="node-input-maxFileSize"><i class="fa fa-hdd-o"></i> Max size (MB)</label>
164
+ <input type="number" id="node-input-maxFileSize" style="width:70%;" min="1" placeholder="5">
165
+ </div>
166
+ <div class="form-row upload-settings-row upload-disk-row">
167
+ <label for="node-input-uploadDir"><i class="fa fa-folder-open-o"></i> Temp dir</label>
168
+ <input type="text" id="node-input-uploadDir" style="width:70%;" placeholder="OS temp dir (default)">
169
+ </div>
170
+ <div class="upload-settings-row" id="node-input-upload-help" style="font-size:12px; color:#666; margin: -4px 0 8px 110px; line-height:1.5;">
171
+ Files are available in <code>msg.files</code>. Other fields remain in <code>msg.payload</code>.<br>
172
+ Memory: stored as buffers. Disk: saved to a temporary path.<br>
173
+ <span class="upload-disk-help"><em>Note: disk files must be cleaned up manually after use.</em></span>
174
+ </div>
175
+ <div class="form-row">
176
+ <input type="checkbox" id="node-input-enableZod" style="display:inline-block; width:auto; vertical-align:top;">
177
+ <label for="node-input-enableZod" style="width:auto;"> Enable Zod validation</label>
178
+ </div>
179
+ <div class="form-row zod-schema-row">
180
+ <label for="node-input-zodSchema" style="vertical-align:top; margin-top:4px;"><i class="fa fa-code"></i> Schema</label>
181
+ <textarea id="node-input-zodSchema" rows="7"
182
+ style="width:70%; font-family:monospace; font-size:12px; resize:vertical;"
183
+ placeholder="z.object({&#10; body: z.object({ name: z.string() }),&#10; query: z.object({}).optional(),&#10; params: z.object({}).optional()&#10;})"></textarea>
184
+ </div>
185
+ <div class="zod-schema-row" id="node-input-zod-help" style="font-size:12px; color:#666; margin: -4px 0 8px 110px; line-height:1.5;">
186
+ <b>Schema structure</b><br>
187
+ <code>{ body, query, params, files }</code><br><br>
188
+
189
+ <b>HTTP method usage</b><br>
190
+ <table style="font-size:12px; border-collapse:collapse;">
191
+ <tr><td style="padding-right:8px;"><b>GET</b></td><td>query, params</td></tr>
192
+ <tr><td style="padding-right:8px;"><b>DELETE</b></td><td>query, params</td></tr>
193
+ <tr><td style="padding-right:8px;"><b>POST</b></td><td>body, query, params</td></tr>
194
+ <tr><td style="padding-right:8px;"><b>PUT</b></td><td>body, query, params</td></tr>
195
+ <tr><td style="padding-right:8px;"><b>PATCH</b></td><td>body, query, params</td></tr>
196
+ </table><br>
197
+
198
+ <span style="color:#a33;">GET and DELETE do not support body. Defining body will be ignored.</span><br><br>
199
+
200
+ <b>Type handling</b><br>
201
+ Query and params are always strings → use <code>z.coerce</code><br><br>
202
+
203
+ <b>Validation</b><br>
204
+ Invalid requests return HTTP 400 automatically<br><br>
205
+
206
+ <b>Swagger / OpenAPI</b><br>
207
+ Only nodes with a valid Zod schema appear in docs<br>
208
+ <span id="node-input-zod-swagger-link"></span><br><br>
209
+
210
+ <b>Zod docs</b><br>
211
+ <a href="https://zod.dev/api" target="_blank">https://zod.dev/api</a>
212
+ </div>
213
+ <div id="node-input-tip" class="form-tips"><span data-i18n="httpin.tip.in"></span><code><span id="node-input-path"></span></code>.</div>
214
+ </script>
215
+
216
+ <script type="text/html" data-template-name="http.out+">
217
+ <div class="form-row">
218
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
219
+ <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
220
+ </div>
221
+ <div class="form-row">
222
+ <label for="node-input-statusCode"><i class="fa fa-long-arrow-left"></i> <span data-i18n="httpin.label.status"></span></label>
223
+ <input type="text" id="node-input-statusCode" placeholder="msg.statusCode">
224
+ </div>
225
+ <div class="form-row" style="margin-bottom:0;">
226
+ <label><i class="fa fa-list"></i> <span data-i18n="httpin.label.headers"></span></label>
227
+ </div>
228
+ <div class="form-row node-input-headers-container-row">
229
+ <ol id="node-input-headers-container"></ol>
230
+ </div>
231
+ <div class="form-tips"><span data-i18n="[html]httpin.tip.res"></span></div>
232
+ </script>
233
+
234
+ <script type="text/javascript">
235
+ (function() {
236
+ RED.nodes.registerType('http.in+',{
237
+ category: 'network',
238
+ color: "#9575CD",
239
+ defaults: {
240
+ name: {value:""},
241
+ url: {value:"", required:true,
242
+ label:RED._("node-red:httpin.label.url")},
243
+ method: {value:"get",required:true},
244
+ swaggerDoc: {type:"swagger-doc", required:false},
245
+ enableUpload: {value: false},
246
+ uploadStorage: {value: 'memory'},
247
+ maxFileSize: {value: 5},
248
+ uploadDir: {value: ''},
249
+ authConfig: {type:"http.auth.config+", required:false},
250
+ enableZod: {value: false},
251
+ zodSchema: {value: ""}
252
+ },
253
+ inputs:0,
254
+ outputs:1,
255
+ icon: "white-globe.svg",
256
+ label: function() {
257
+ if (this.name) {
258
+ return this.name;
259
+ } else if (this.url) {
260
+ var root = RED.settings.httpNodeRoot;
261
+ if (root.slice(-1) != "/") {
262
+ root = root+"/";
263
+ }
264
+ if (this.url.charAt(0) == "/") {
265
+ root += this.url.slice(1);
266
+ } else {
267
+ root += this.url;
268
+ }
269
+ return "["+this.method+"] "+root;
270
+ } else {
271
+ return "http.in+";
272
+ }
273
+ },
274
+ labelStyle: function() {
275
+ return this.name?"node_label_italic":"";
276
+ },
277
+ oneditprepare: function() {
278
+ var root = RED.settings.httpNodeRoot;
279
+ if (root.slice(-1) == "/") {
280
+ root = root.slice(0,-1);
281
+ }
282
+ if (root == "") {
283
+ $("#node-input-tip").hide();
284
+ } else {
285
+ $("#node-input-path").html(root);
286
+ $("#node-input-tip").show();
287
+ }
288
+ if(!RED.nodes.getType("swagger-doc")){
289
+ $('.row-swagger-doc').hide();
290
+ }
291
+ var uploadMethods = ["post"];
292
+ function toggleUploadSection() {
293
+ var method = $("#node-input-method").val();
294
+ var show = uploadMethods.indexOf(method) !== -1;
295
+ $(".upload-section").toggle(show);
296
+ if (show) { toggleUploadSettings(); } else { $(".upload-settings-row").hide(); }
297
+ }
298
+ function toggleUploadSettings() {
299
+ var enabled = $("#node-input-enableUpload").is(":checked");
300
+ $(".upload-settings-row").toggle(enabled);
301
+ if (enabled) { toggleDiskDir(); }
302
+ }
303
+ function toggleDiskDir() {
304
+ var isDisk = $("#node-input-uploadStorage").val() === "disk";
305
+ $(".upload-disk-row").toggle(isDisk);
306
+ $(".upload-disk-help").toggle(isDisk);
307
+ }
308
+ $("#node-input-method").on("change", toggleUploadSection);
309
+ $("#node-input-enableUpload").on("change", toggleUploadSettings);
310
+ $("#node-input-uploadStorage").on("change", toggleDiskDir);
311
+ toggleUploadSection();
312
+
313
+ function toggleZodSchema() {
314
+ $(".zod-schema-row").toggle($("#node-input-enableZod").is(":checked"));
315
+ }
316
+ $("#node-input-enableZod").on("change", toggleZodSchema);
317
+ toggleZodSchema();
318
+
319
+ var httpRoot = (RED.settings.httpNodeRoot || "").replace(/\/$/, "");
320
+ var docsUrl = window.location.origin + httpRoot + "/docs";
321
+ var specUrl = window.location.origin + httpRoot + "/openapi.json";
322
+ $("#node-input-zod-swagger-link").html(
323
+ '<a href="' + docsUrl + '" target="_blank">/docs</a>' +
324
+ ' &nbsp;·&nbsp; ' +
325
+ '<a href="' + specUrl + '" target="_blank">/openapi.json</a>'
326
+ );
327
+
328
+ var authHelpContent = {
329
+ "none": "No authentication. This endpoint is public.",
330
+ "basic": "Credentials sent via <code>Authorization: Basic base64(username:password)</code>.",
331
+ "cognito": "JWT validated against the configured JWKS endpoint.<br>Token accepted via <code>Authorization: Bearer &lt;token&gt;</code> or <code>?token=&lt;token&gt;</code>.<br><em>Note: token must be valid and issued by the configured Cognito pool.</em>"
332
+ };
333
+ function updateAuthHelp() {
334
+ var nodeId = $("#node-input-authConfig").val();
335
+ var authNode = RED.nodes.node(nodeId);
336
+ var type = (authNode && authNode.authType) ? authNode.authType : "none";
337
+ var content = authHelpContent[type] || authHelpContent["none"];
338
+ $("#node-input-auth-help").html(content).show();
339
+ }
340
+ $("#node-input-authConfig").on("change", updateAuthHelp);
341
+ updateAuthHelp();
342
+ }
343
+
344
+ });
345
+ var headerTypes = [
346
+ {value:"content-type",label:"Content-Type",hasValue: false},
347
+ {value:"location",label:"Location",hasValue: false},
348
+ {value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.svg"}
349
+ ]
350
+ var contentTypes = [
351
+ {value:"application/json",label:"application/json",hasValue: false},
352
+ {value:"application/xml",label:"application/xml",hasValue: false},
353
+ {value:"text/css",label:"text/css",hasValue: false},
354
+ {value:"text/html",label:"text/html",hasValue: false},
355
+ {value:"text/plain",label:"text/plain",hasValue: false},
356
+ {value:"image/gif",label:"image/gif",hasValue: false},
357
+ {value:"image/png",label:"image/png",hasValue: false},
358
+ {value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.svg"}
359
+ ];
360
+
361
+ RED.nodes.registerType('http.out+',{
362
+ category: 'network',
363
+ color: "#9575CD",
364
+ defaults: {
365
+ name: {value:""},
366
+ statusCode: {
367
+ value:"",
368
+ label: RED._("node-red:httpin.label.status"),
369
+ validate: RED.validators.number(true)},
370
+ headers: {value:{}}
371
+ },
372
+ inputs:1,
373
+ outputs:0,
374
+ align: "right",
375
+ icon: "white-globe.svg",
376
+ label: function() {
377
+ return this.name||("http.out+"+(this.statusCode?" ("+this.statusCode+")":""));
378
+ },
379
+ labelStyle: function() {
380
+ return this.name?"node_label_italic":"";
381
+ },
382
+ oneditprepare: function() {
383
+ var headerList = $("#node-input-headers-container").css('min-height','150px').css('min-width','450px').editableList({
384
+ addItem: function(container,i,header) {
385
+ var row = $('<div/>').css({
386
+ overflow: 'hidden',
387
+ whiteSpace: 'nowrap',
388
+ display: 'flex'
389
+ }).appendTo(container);
390
+ var propertNameCell = $('<div/>').css({'flex-grow':1}).appendTo(row);
391
+ var propertyName = $('<input/>',{class:"node-input-header-name",type:"text", style:"width: 100%"})
392
+ .appendTo(propertNameCell)
393
+ .typedInput({types:headerTypes});
394
+
395
+ var propertyValueCell = $('<div/>').css({'flex-grow':1,'margin-left':'10px'}).appendTo(row);
396
+ var propertyValue = $('<input/>',{class:"node-input-header-value",type:"text",style:"width: 100%"})
397
+ .appendTo(propertyValueCell)
398
+ .typedInput({types:
399
+ header.h === 'content-type'?contentTypes:[{value:"other",label:"other",icon:"red/images/typedInput/az.svg"}]
400
+ });
401
+
402
+ var matchedType = headerTypes.filter(function(ht) {
403
+ return ht.value === header.h
404
+ });
405
+ if (matchedType.length === 0) {
406
+ propertyName.typedInput('type','other');
407
+ propertyName.typedInput('value',header.h);
408
+ propertyValue.typedInput('value',header.v);
409
+ } else {
410
+ propertyName.typedInput('type',header.h);
411
+
412
+ if (header.h === "content-type") {
413
+ matchedType = contentTypes.filter(function(ct) {
414
+ return ct.value === header.v;
415
+ });
416
+ if (matchedType.length === 0) {
417
+ propertyValue.typedInput('type','other');
418
+ propertyValue.typedInput('value',header.v);
419
+ } else {
420
+ propertyValue.typedInput('type',header.v);
421
+ }
422
+ } else {
423
+ propertyValue.typedInput('value',header.v);
424
+ }
425
+ }
426
+
427
+ matchedType = headerTypes.filter(function(ht) {
428
+ return ht.value === header.h
429
+ });
430
+ if (matchedType.length === 0) {
431
+ propertyName.typedInput('type','other');
432
+ propertyName.typedInput('value',header.h);
433
+ } else {
434
+ propertyName.typedInput('type',header.h);
435
+ }
436
+
437
+ propertyName.on('change',function(event) {
438
+ var type = propertyName.typedInput('type');
439
+ if (type === 'content-type') {
440
+ propertyValue.typedInput('types',contentTypes);
441
+ } else {
442
+ propertyValue.typedInput('types',[{value:"other",label:"other",icon:"red/images/typedInput/az.svg"}]);
443
+ }
444
+ });
445
+ },
446
+ sortable: true,
447
+ removable: true
448
+ });
449
+
450
+ if (this.headers) {
451
+ for (var key in this.headers) {
452
+ if (this.headers.hasOwnProperty(key)) {
453
+ headerList.editableList('addItem',{h:key,v:this.headers[key]});
454
+ }
455
+ }
456
+ }
457
+ },
458
+ oneditsave: function() {
459
+ var headers = $("#node-input-headers-container").editableList('items');
460
+ var node = this;
461
+ node.headers = {};
462
+ headers.each(function(i) {
463
+ var header = $(this);
464
+ var keyType = header.find(".node-input-header-name").typedInput('type');
465
+ var keyValue = header.find(".node-input-header-name").typedInput('value');
466
+ var valueType = header.find(".node-input-header-value").typedInput('type');
467
+ var valueValue = header.find(".node-input-header-value").typedInput('value');
468
+ var key = keyType;
469
+ var value = valueType;
470
+ if (keyType === 'other') {
471
+ key = keyValue;
472
+ }
473
+ if (valueType === 'other') {
474
+ value = valueValue;
475
+ }
476
+ if (key !== '') {
477
+ node.headers[key] = value;
478
+ }
479
+ });
480
+ },
481
+ oneditresize: function(size) {
482
+ var rows = $("#dialog-form>div:not(.node-input-headers-container-row)");
483
+ var height = size.height;
484
+ for (var i=0; i<rows.length; i++) {
485
+ height -= $(rows[i]).outerHeight(true);
486
+ }
487
+ var editorRow = $("#dialog-form>div.node-input-headers-container-row");
488
+ height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
489
+
490
+ $("#node-input-headers-container").editableList('height',height);
491
+ }
492
+ });
493
+ })();
494
+ </script>