From d9dc171c28633bd124724001244a150393d9343c Mon Sep 17 00:00:00 2001
From: Nick O'Leary
Date: Sun, 11 Jun 2017 21:19:46 +0100
Subject: [PATCH] Add buffer mode to typedInput
---
editor/js/ui/common/panels.js | 2 -
editor/js/ui/common/typedInput.js | 14 ++
editor/js/ui/editor.js | 197 +++++++++++++++++++++++++-
editor/sass/editor.scss | 19 ++-
editor/sass/ui/common/typedInput.scss | 2 +-
editor/templates/index.mst | 18 ++-
nodes/core/core/20-inject.html | 2 +-
nodes/core/logic/15-change.html | 4 +-
nodes/core/logic/15-change.js | 4 +-
red/api/locales/en-US/editor.json | 6 +
red/runtime/util.js | 3 +
11 files changed, 256 insertions(+), 15 deletions(-)
diff --git a/editor/js/ui/common/panels.js b/editor/js/ui/common/panels.js
index fbe595662..84e1d4c7e 100644
--- a/editor/js/ui/common/panels.js
+++ b/editor/js/ui/common/panels.js
@@ -39,7 +39,6 @@ RED.panels = (function() {
var height = container.height();
startPosition = ui.position.top;
panelHeights = [$(children[0]).height(),$(children[1]).height()];
- console.log("START",height,panelHeights,panelHeights[0]+panelHeights[1],height-(panelHeights[0]+panelHeights[1]));
},
drag: function(event,ui) {
var height = container.height();
@@ -68,7 +67,6 @@ RED.panels = (function() {
panelHeights = [topPanelHeight,bottomPanelHeight];
$(children[0]).height(panelHeights[0]);
$(children[1]).height(panelHeights[1]);
- console.log("SET",height,panelHeights,panelHeights[0]+panelHeights[1],height-(panelHeights[0]+panelHeights[1]));
}
if (options.resize) {
options.resize(panelHeights[0],panelHeights[1]);
diff --git a/editor/js/ui/common/typedInput.js b/editor/js/ui/common/typedInput.js
index 3b0a56753..e481c6ef4 100644
--- a/editor/js/ui/common/typedInput.js
+++ b/editor/js/ui/common/typedInput.js
@@ -62,6 +62,20 @@
}
})
}
+ },
+ bin: {
+ value: "bin",
+ label: "buffer",
+ icon: "red/images/typedInput/bin.png",
+ expand: function() {
+ var that = this;
+ RED.editor.editBuffer({
+ value: this.value(),
+ complete: function(v) {
+ that.value(v);
+ }
+ })
+ }
}
};
var nlsd = false;
diff --git a/editor/js/ui/editor.js b/editor/js/ui/editor.js
index 622861ab5..bdeadca43 100644
--- a/editor/js/ui/editor.js
+++ b/editor/js/ui/editor.js
@@ -496,6 +496,8 @@ RED.editor = (function() {
label = RED._("expressionEditor.title");
} else if (node.type === '_json') {
label = RED._("jsonEditor.title");
+ } else if (node.type === '_buffer') {
+ label = RED._("bufferEditor.title");
} else if (node.type === 'subflow') {
label = RED._("subflow.editSubflow",{name:node.name})
} else if (node.type.indexOf("subflow:")===0) {
@@ -1874,7 +1876,7 @@ RED.editor = (function() {
});
legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
} catch(err) {
- testResultEditor.setValue(RED._("expressionEditor.errors.invalid-expr",{message:err.message}));
+ testResultEditor.setValue(RED._("expressionEditor.errors.invalid-expr",{message:err.message}),-1);
return;
}
$(".node-input-expression-legacy").toggle(legacyMode);
@@ -1888,7 +1890,7 @@ RED.editor = (function() {
try {
var result = expr.evaluate(legacyMode?{msg:parsedData}:parsedData);
if (usesContext) {
- testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"));
+ testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
return;
}
@@ -1898,9 +1900,9 @@ RED.editor = (function() {
} else {
formattedResult = RED._("expressionEditor.noMatch");
}
- testResultEditor.setValue(formattedResult);
+ testResultEditor.setValue(formattedResult,-1);
} catch(err) {
- testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}));
+ testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
}
}
@@ -2028,6 +2030,191 @@ RED.editor = (function() {
RED.tray.show(trayOptions);
}
+ function stringToUTF8Array(str) {
+ var data = [];
+ var i=0, l = str.length;
+ for (i=0; i> 6));
+ data.push(0x80 | (char & 0x3f));
+ } else if (char < 0xd800 || char >= 0xe000) {
+ data.push(0xe0 | (char >> 12));
+ data.push(0x80 | ((char>>6) & 0x3f));
+ data.push(0x80 | (char & 0x3f));
+ } else {
+ i++;
+ char = 0x10000 + (((char & 0x3ff)<<10) | (str.charAt(i) & 0x3ff));
+ data.push(0xf0 | (char >>18));
+ data.push(0x80 | ((char>>12) & 0x3f));
+ data.push(0x80 | ((char>>6) & 0x3f));
+ data.push(0x80 | (char & 0x3f));
+ }
+ }
+ return data;
+ }
+
+ function editBuffer(options) {
+ var value = options.value;
+ var onComplete = options.complete;
+ var type = "_buffer"
+ editStack.push({type:type});
+ RED.view.state(RED.state.EDITING);
+ var bufferStringEditor = [];
+ var bufferBinValue;
+
+ var panels;
+
+ var trayOptions = {
+ title: getEditStackTitle(),
+ buttons: [
+ {
+ id: "node-dialog-cancel",
+ text: RED._("common.label.cancel"),
+ click: function() {
+ RED.tray.close();
+ }
+ },
+ {
+ id: "node-dialog-ok",
+ text: RED._("common.label.done"),
+ class: "primary",
+ click: function() {
+ onComplete(JSON.stringify(bufferBinValue));
+ RED.tray.close();
+ }
+ }
+ ],
+ resize: function(dimensions) {
+ if (dimensions) {
+ editTrayWidthCache[type] = dimensions.width;
+ }
+ var height = $("#dialog-form").height();
+ if (panels) {
+ panels.resize(height);
+ }
+ },
+ open: function(tray) {
+ var trayBody = tray.find('.editor-tray-body');
+ var dialogForm = buildEditForm(tray.find('.editor-tray-body'),'dialog-form',type,'editor');
+
+ bufferStringEditor = RED.editor.createEditor({
+ id: 'node-input-buffer-str',
+ value: "",
+ mode:"ace/mode/text"
+ });
+ bufferStringEditor.getSession().setValue(value||"",-1);
+
+ bufferBinEditor = RED.editor.createEditor({
+ id: 'node-input-buffer-bin',
+ value: "",
+ mode:"ace/mode/text",
+ readOnly: true
+ });
+
+ var changeTimer;
+ var buildBuffer = function(data) {
+ var valid = true;
+ var isString = typeof data === 'string';
+ var binBuffer = [];
+ if (isString) {
+ bufferBinValue = stringToUTF8Array(data);
+ } else {
+ bufferBinValue = data;
+ }
+ var i=0,l=bufferBinValue.length;
+ var c = 0;
+ for(i=0;i 255)) {
+ valid = false;
+ break;
+ }
+ if (i>0) {
+ if (i%8 === 0) {
+ if (i%16 === 0) {
+ binBuffer.push("\n");
+ } else {
+ binBuffer.push(" ");
+ }
+ } else {
+ binBuffer.push(" ");
+ }
+ }
+ binBuffer.push((d<16?"0":"")+d.toString(16).toUpperCase());
+ }
+ if (valid) {
+ $("#node-input-buffer-type-string").toggle(isString);
+ $("#node-input-buffer-type-array").toggle(!isString);
+ bufferBinEditor.setValue(binBuffer.join(""),1);
+ }
+ return valid;
+ }
+ var bufferStringUpdate = function() {
+ var value = bufferStringEditor.getValue();
+ var isValidArray = false;
+ if (/^[\s]*\[[\s\S]*\][\s]*$/.test(value)) {
+ isValidArray = true;
+ try {
+ var data = JSON.parse(value);
+ isValidArray = buildBuffer(data);
+ } catch(err) {
+ isValidArray = false;
+ }
+ }
+ if (!isValidArray) {
+ buildBuffer(value);
+ }
+
+ }
+ bufferStringEditor.getSession().on('change', function() {
+ clearTimeout(changeTimer);
+ changeTimer = setTimeout(bufferStringUpdate,200);
+ });
+
+ bufferStringUpdate();
+
+ dialogForm.i18n();
+
+ panels = RED.panels.create({
+ id:"node-input-buffer-panels",
+ resize: function(p1Height,p2Height) {
+ var p1 = $("#node-input-buffer-panel-str");
+ p1Height -= $(p1.children()[0]).outerHeight(true);
+ var editorRow = $(p1.children()[1]);
+ p1Height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
+ $("#node-input-buffer-str").css("height",(p1Height-5)+"px");
+ bufferStringEditor.resize();
+
+ var p2 = $("#node-input-buffer-panel-bin");
+ editorRow = $(p2.children()[0]);
+ p2Height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
+ $("#node-input-buffer-bin").css("height",(p2Height-5)+"px");
+ bufferBinEditor.resize();
+ }
+ });
+
+ $(".node-input-buffer-type").click(function(e) {
+ e.preventDefault();
+ RED.sidebar.info.set(RED._("bufferEditor.modeDesc"));
+ RED.sidebar.info.show();
+ })
+
+
+ },
+ close: function() {
+ editStack.pop();
+ },
+ show: function() {}
+ }
+ if (editTrayWidthCache.hasOwnProperty(type)) {
+ trayOptions.width = editTrayWidthCache[type];
+ }
+ RED.tray.show(trayOptions);
+ }
+
return {
init: function() {
RED.tray.init();
@@ -2045,6 +2232,7 @@ RED.editor = (function() {
editSubflow: showEditSubflowDialog,
editExpression: editExpression,
editJSON: editJSON,
+ editBuffer: editBuffer,
validateNode: validateNode,
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
@@ -2071,6 +2259,7 @@ RED.editor = (function() {
}
if (options.readOnly) {
editor.setOption('readOnly',options.readOnly);
+ editor.container.classList.add("ace_read-only");
}
if (options.hasOwnProperty('lineNumbers')) {
editor.renderer.setOption('showGutter',options.lineNumbers);
diff --git a/editor/sass/editor.scss b/editor/sass/editor.scss
index 8590095d7..a68eeedb8 100644
--- a/editor/sass/editor.scss
+++ b/editor/sass/editor.scss
@@ -282,16 +282,21 @@
}
}
}
-.node-input-expression-legacy {
+.node-input-expression-legacy, .node-input-buffer-type {
+ font-size: 0.8em;
float: left;
cursor: pointer;
border: 1px solid white;
- padding: 0 5px;
+ padding: 2px 5px;
border-radius: 2px;
&:hover {
- border-color: $primary-border-color;
+ border-color: $form-input-border-color;
}
}
+.node-input-buffer-type {
+ float: none;
+ text-align: right;
+}
#clipboard-hidden {
position: absolute;
@@ -318,3 +323,11 @@
color: #999;
}
}
+
+.ace_read-only {
+ background: #eee !important;
+ .ace_cursor {
+ color: transparent !important;
+ }
+
+}
diff --git a/editor/sass/ui/common/typedInput.scss b/editor/sass/ui/common/typedInput.scss
index b4e6cdfc9..e664cd8ec 100644
--- a/editor/sass/ui/common/typedInput.scss
+++ b/editor/sass/ui/common/typedInput.scss
@@ -35,7 +35,7 @@
}
input {
width: 100%;
- padding: 0 0 0 1px;
+ padding: 0 0 0 3px;
margin:0;
height: 32px;
border:none;
diff --git a/editor/templates/index.mst b/editor/templates/index.mst
index cc11bcd0f..ebf26947f 100644
--- a/editor/templates/index.mst
+++ b/editor/templates/index.mst
@@ -206,7 +206,23 @@
-
+
diff --git a/nodes/core/core/20-inject.html b/nodes/core/core/20-inject.html
index c5654f490..fbc180c47 100644
--- a/nodes/core/core/20-inject.html
+++ b/nodes/core/core/20-inject.html
@@ -256,7 +256,7 @@ If you want every 20 minutes from now - use the "interval" option.
$("#node-input-payload").typedInput({
default: 'str',
typeField: $("#node-input-payloadType"),
- types:['flow','global','str','num','bool','json','date']
+ types:['flow','global','str','num','bool','json','bin','date']
});
$("#inject-time-type-select").change(function() {
diff --git a/nodes/core/logic/15-change.html b/nodes/core/logic/15-change.html
index f2f5c1edd..c7d941568 100644
--- a/nodes/core/logic/15-change.html
+++ b/nodes/core/logic/15-change.html
@@ -143,7 +143,7 @@
.appendTo(row2);
var propertyValue = $(' ',{class:"node-input-rule-property-value",type:"text"})
.appendTo(row2)
- .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','date','jsonata']});
+ .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata']});
var row3_1 = $('
').appendTo(row3);
$('
',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
@@ -159,7 +159,7 @@
.appendTo(row3_2);
var toValue = $(' ',{class:"node-input-rule-property-replace-value",type:"text"})
.appendTo(row3_2)
- .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json']});
+ .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin']});
$('
',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(to)
diff --git a/nodes/core/logic/15-change.js b/nodes/core/logic/15-change.js
index dda8bfb97..a831ac379 100644
--- a/nodes/core/logic/15-change.js
+++ b/nodes/core/logic/15-change.js
@@ -76,7 +76,7 @@ module.exports = function(RED) {
}
if (rule.tot === 'num') {
rule.to = Number(rule.to);
- } else if (rule.tot === 'json') {
+ } else if (rule.tot === 'json' || rule.tot === 'bin') {
try {
// check this is parsable JSON
JSON.parse(rule.to);
@@ -102,6 +102,8 @@ module.exports = function(RED) {
var value = rule.to;
if (rule.tot === 'json') {
value = JSON.parse(rule.to);
+ } else if (rule.tot === 'bin') {
+ value = Buffer.from(JSON.parse(rule.to))
}
var current;
var fromValue;
diff --git a/red/api/locales/en-US/editor.json b/red/api/locales/en-US/editor.json
index a0788cd28..8ee3be8b2 100644
--- a/red/api/locales/en-US/editor.json
+++ b/red/api/locales/en-US/editor.json
@@ -419,5 +419,11 @@
"jsonEditor": {
"title": "JSON editor",
"format": "format JSON"
+ },
+ "bufferEditor": {
+ "title": "Buffer editor",
+ "modeString": "Handle as UTF-8 String",
+ "modeArray": "Handle as JSON array",
+ "modeDesc":"Buffer editor The Buffer type is stored as a JSON array of byte values. The editor will attempt to parse the entered value as a JSON array. If it is not valid JSON, it will be treated as a UTF-8 String and converted to an array of the individual character code points.
For example, a value of Hello World
will be converted to the JSON array:
[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100] "
}
}
diff --git a/red/runtime/util.js b/red/runtime/util.js
index 30bb2e259..08a68d60e 100644
--- a/red/runtime/util.js
+++ b/red/runtime/util.js
@@ -314,6 +314,9 @@ function evaluateNodeProperty(value, type, node, msg) {
return new RegExp(value);
} else if (type === 'date') {
return Date.now();
+ } else if (type === 'bin') {
+ var data = JSON.parse(value);
+ return Buffer.from(data);
} else if (type === 'msg' && msg) {
return getMessageProperty(msg,value);
} else if (type === 'flow' && node) {