2020-01-15 03:40:48 +01:00
|
|
|
<script type="text/html" data-template-name="function">
|
2013-09-05 16:02:48 +02:00
|
|
|
<div class="form-row">
|
2015-05-10 22:47:22 +02:00
|
|
|
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
2018-10-27 10:56:54 +02:00
|
|
|
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>
|
2013-09-05 16:02:48 +02:00
|
|
|
</div>
|
2020-03-06 17:55:45 +01:00
|
|
|
|
|
|
|
<div class="form-row">
|
|
|
|
<ul style="min-width: 600px; margin-bottom: 20px;" id="func-tabs"></ul>
|
2013-09-05 16:02:48 +02:00
|
|
|
</div>
|
2020-03-06 17:55:45 +01:00
|
|
|
|
2020-05-11 07:37:14 +02:00
|
|
|
<div id="func-tabs-content" style="min-height: calc(100% - 95px);">
|
2020-03-06 17:55:45 +01:00
|
|
|
|
|
|
|
<div id="func-tab-init" style="display:none">
|
|
|
|
<div class="form-row" style="margin-bottom: 0px;">
|
|
|
|
<input type="hidden" id="node-input-initialize" autofocus="autofocus">
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="form-row node-text-editor-row" style="position:relative">
|
2020-05-11 07:37:14 +02:00
|
|
|
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
|
|
|
<div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-init-editor" ></div>
|
2020-03-06 17:55:45 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2020-04-06 09:34:41 +02:00
|
|
|
<div id="func-tab-body" style="display:none">
|
2020-03-06 17:55:45 +01:00
|
|
|
<div class="form-row" style="margin-bottom: 0px;">
|
|
|
|
<input type="hidden" id="node-input-func" autofocus="autofocus">
|
|
|
|
<input type="hidden" id="node-input-noerr">
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="form-row node-text-editor-row" style="position:relative">
|
2020-05-11 07:37:14 +02:00
|
|
|
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
|
|
|
<div style="height: 220px; min-height:120px; margin-top: 30px;" class="node-text-editor" id="node-input-func-editor" ></div>
|
2020-03-06 17:55:45 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="form-row" style="margin-bottom: 0px">
|
|
|
|
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
|
|
|
|
<input id="node-input-outputs" style="width: 60px;" value="1">
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div id="func-tab-finalize" style="display:none">
|
|
|
|
<div class="form-row" style="margin-bottom: 0px;">
|
|
|
|
<input type="hidden" id="node-input-finalize" autofocus="autofocus">
|
|
|
|
</div>
|
|
|
|
<div class="form-row node-text-editor-row" style="position:relative">
|
2020-05-11 07:37:14 +02:00
|
|
|
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
|
|
|
|
<div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
|
2020-03-06 17:55:45 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2021-01-11 11:32:16 +01:00
|
|
|
<div id="func-tab-config" style="display:none">
|
2021-01-27 14:27:54 +01:00
|
|
|
<div class="form-row" style="margin-bottom: 0px;">
|
2021-01-11 11:32:16 +01:00
|
|
|
<label><i class="fa fa-wrench"></i> <span>Use Library</span></label>
|
|
|
|
</div>
|
|
|
|
<div class="form-row" style="height: 250px; min-height: 150px">
|
2021-01-27 14:27:54 +01:00
|
|
|
<ol id="node-input-libs-container"></ol>
|
2021-01-11 11:32:16 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
2013-09-05 16:02:48 +02:00
|
|
|
</div>
|
2020-03-06 17:55:45 +01:00
|
|
|
|
2013-09-05 16:02:48 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
2021-01-27 14:27:54 +01:00
|
|
|
// object that maps from library name to its descriptor
|
|
|
|
var allLibs = [];
|
|
|
|
|
|
|
|
function moduleName(module) {
|
|
|
|
var match = /^([^@]+)@(.+)/.exec(module);
|
|
|
|
if (match) {
|
|
|
|
return [match[1], match[2]];
|
|
|
|
}
|
|
|
|
return [module, undefined];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add library descriptor
|
|
|
|
* @param {string} name - library name
|
|
|
|
*/
|
|
|
|
function addLib(info) {
|
|
|
|
allLibs.push(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validate library spec including conflicts with other specs
|
|
|
|
* @param {string} name - library name
|
|
|
|
*/
|
|
|
|
function checkLib(spec) {
|
|
|
|
var [name, ver] = moduleName(spec);
|
|
|
|
var libs = $("#node-input-libs-container").editableList("items");
|
|
|
|
var count = 0;
|
|
|
|
libs.each(function(i) {
|
|
|
|
if (count > 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var item = $(this);
|
|
|
|
var n = item.find(".node-input-libs-val").val();
|
|
|
|
if (n && (n !== "")) {
|
|
|
|
var [name1, ver1] = moduleName(n);
|
|
|
|
if (name1 === name) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return (count <= 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get library list
|
|
|
|
*/
|
|
|
|
function getModules() {
|
|
|
|
var root = RED.settings.apiRootUrl || "";
|
|
|
|
var promise = $.ajax({
|
|
|
|
url: root + "/modules",
|
|
|
|
type: "GET",
|
|
|
|
cache: false
|
|
|
|
});
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check and update in progress state of modules
|
|
|
|
*/
|
|
|
|
function checkInProgress(allLibs) {
|
|
|
|
var waitTime = 1000;
|
|
|
|
|
|
|
|
function update() {
|
|
|
|
map = {};
|
|
|
|
allLibs.forEach(function (lib) {
|
|
|
|
if (lib.inProgress) {
|
|
|
|
map[lib.name] = lib;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
getModules().then(function (data,textStatus,xhr) {
|
|
|
|
var count = 0;
|
|
|
|
data.forEach(lib => {
|
|
|
|
var name = lib.name;
|
|
|
|
if (lib.inProgress) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
else if (name in map) {
|
|
|
|
var lib2 = map[name];
|
|
|
|
if (lib2) {
|
|
|
|
delete map[name];
|
|
|
|
var cb = lib2.onReady;
|
|
|
|
if (cb) {
|
|
|
|
lib2.version = lib.version;
|
|
|
|
lib2.status["installed"] = true;
|
|
|
|
lib2.inProgress = false;
|
|
|
|
cb();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (count > 0) {
|
|
|
|
setTimeout(update, waitTime);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
setTimeout(update, waitTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update library list
|
|
|
|
*/
|
|
|
|
function updateLib(node, done) {
|
|
|
|
var names = {};
|
|
|
|
var specs = {};
|
|
|
|
var libs = node.libs || [];
|
|
|
|
allLibs = [];
|
|
|
|
libs.forEach(lib => {
|
|
|
|
var vname = lib.vname;
|
|
|
|
var spec = lib.spec;
|
|
|
|
var [name, ver] = moduleName(spec);
|
|
|
|
var item = {
|
|
|
|
vname: vname,
|
|
|
|
name: name,
|
|
|
|
spec: spec,
|
|
|
|
status: { "edit":true }
|
|
|
|
};
|
|
|
|
addLib(item);
|
|
|
|
specs[spec] = true;
|
|
|
|
names[name] = true;
|
|
|
|
});
|
|
|
|
RED.nodes.eachNode(function(n) {
|
|
|
|
if ((n.type === "function") && (n !== node)) {
|
|
|
|
var libs = n.libs || [];
|
|
|
|
libs.forEach(lib => {
|
|
|
|
var spec = lib.spec;
|
|
|
|
var [name, ver] = moduleName(spec);
|
|
|
|
if (name in names) {
|
|
|
|
var items = allLibs.filter(item => (item.name === name));
|
|
|
|
items.forEach(item => {
|
|
|
|
item.status["used"] = true;
|
|
|
|
});
|
|
|
|
if (spec in specs) {
|
|
|
|
// olready found
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// spec conflict
|
|
|
|
}
|
|
|
|
var item = {
|
|
|
|
name: name,
|
|
|
|
spec: spec,
|
|
|
|
status: { "used": true }
|
|
|
|
};
|
|
|
|
addLib(item);
|
|
|
|
names[name] = true;
|
|
|
|
specs[spec] = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
var inProgressCount = 0;
|
|
|
|
getModules().then(function(data,textStatus,xhr) {
|
|
|
|
data.forEach(lib => {
|
|
|
|
var name = lib.name;
|
|
|
|
var version = lib.version;
|
|
|
|
var preinstalled = (lib.status == "preinstalled");
|
|
|
|
var spec = lib.spec;
|
|
|
|
var items = allLibs.filter(item => (item.name === name));
|
|
|
|
if (items.length > 0) {
|
|
|
|
items.forEach(item => {
|
|
|
|
item.version = version;
|
|
|
|
item.status["installed"] = true;
|
|
|
|
item.status["preinstalled"] = preinstalled;
|
|
|
|
item.inProgress = lib.inProgress;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
var item = {
|
|
|
|
name: name,
|
|
|
|
spec: spec,
|
|
|
|
version: version,
|
|
|
|
status: {
|
|
|
|
"installed": true,
|
|
|
|
"preinstalled": preinstalled
|
|
|
|
},
|
|
|
|
inProgress: lib.inProgress
|
|
|
|
};
|
|
|
|
addLib(item);
|
|
|
|
names[name] = true;
|
|
|
|
specs[spec] = true;
|
|
|
|
}
|
|
|
|
if (lib.inProgress) {
|
|
|
|
inProgressCount++;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
$("#node-input-libs-container").editableList("empty");
|
|
|
|
allLibs.forEach(function(lib) {
|
|
|
|
$("#node-input-libs-container").editableList("addItem", lib);
|
|
|
|
});
|
|
|
|
if (inProgressCount > 0) {
|
|
|
|
checkInProgress(allLibs);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (done) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Uninstall NPM module
|
|
|
|
*/
|
|
|
|
function uninstallModule(node, spec, name, version,
|
|
|
|
moduleInfo, installButton,
|
|
|
|
installInfoRow, installedInfo,
|
|
|
|
removeButton,
|
|
|
|
progressRow,
|
|
|
|
done) {
|
|
|
|
var root = RED.settings.apiRootUrl || "";
|
|
|
|
progressRow.show();
|
|
|
|
$.ajax({
|
|
|
|
url: root + "/modules/" +spec,
|
|
|
|
type: "DELETE",
|
|
|
|
}).then(function(data,textStatus,xhr) {
|
|
|
|
moduleInfo.attr("disabled", false);
|
|
|
|
installButton.attr("disabled", false);
|
|
|
|
installInfoRow.hide();
|
|
|
|
installedInfo.val("");
|
|
|
|
removeButton.show();
|
|
|
|
progressRow.hide();
|
|
|
|
|
|
|
|
RED.notify("Successfully uninstalled:" +name);
|
|
|
|
if(done) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
}).fail(function(xhr,textStatus,err) {
|
|
|
|
progressRow.hide();
|
|
|
|
RED.notify("Failed to uninstall: " +name);
|
|
|
|
if (done) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updaate or Install NPM module
|
|
|
|
*/
|
|
|
|
function updateModule(node, spec, update,
|
|
|
|
moduleInfo, installButton,
|
|
|
|
installInfoRow, installedInfo, removeButton,
|
|
|
|
progressRow,
|
|
|
|
done) {
|
|
|
|
var [name, version] = moduleName(spec);
|
|
|
|
var root = RED.settings.apiRootUrl || "";
|
|
|
|
var errorMessage = "failed to "+(update ? "update": "install") +": " +name;
|
|
|
|
progressRow.show();
|
|
|
|
$.ajax({
|
|
|
|
url: root + "/modules",
|
|
|
|
type: "POST",
|
|
|
|
contentType: "application/json; charset=utf-8",
|
|
|
|
cache: false,
|
|
|
|
data: JSON.stringify({
|
|
|
|
spec: spec,
|
|
|
|
update: update
|
|
|
|
})
|
|
|
|
}).then(function(data) {
|
|
|
|
getModules().then(function(data,textStatus,xhr) {
|
|
|
|
var item = data.find(lib => (lib.name === name));
|
|
|
|
progressRow.hide();
|
|
|
|
if (item) {
|
|
|
|
moduleInfo.attr("disabled", true);
|
|
|
|
installButton.attr("disabled", true);
|
|
|
|
installInfoRow.show();
|
|
|
|
installedInfo.val(item.name+"@"+item.version);
|
|
|
|
removeButton.hide();
|
|
|
|
|
|
|
|
var msg = "Successfully "
|
|
|
|
+(update ? "updated": "installed")
|
|
|
|
+": " +name;
|
|
|
|
|
|
|
|
RED.notify(msg);
|
|
|
|
}
|
|
|
|
if (done) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
}).fail(function(xhr,textStatus,err) {
|
|
|
|
progressRow.hide();
|
|
|
|
RED.notify(errorMessage);
|
|
|
|
if (done) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}).fail(function(xhr,textStatus,err) {
|
|
|
|
progressRow.hide();
|
|
|
|
RED.notify(errorMessage);
|
|
|
|
if (done) {
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
function withNotification(msg, text, cb) {
|
|
|
|
var notification = RED.notify(msg, {
|
|
|
|
modal: true,
|
|
|
|
fixed: true,
|
|
|
|
buttons: [
|
|
|
|
{
|
|
|
|
text: RED._("common.label.cancel"),
|
|
|
|
click: function() {
|
|
|
|
notification.close();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: text,
|
|
|
|
class: "primary red-ui-palette-module-install-confirm-button-update",
|
|
|
|
click: function() {
|
|
|
|
notification.close();
|
|
|
|
cb();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function prepareLibraryConfig(node) {
|
|
|
|
$("#node-input-libs-container").css('min-height','250px').css('min-width','450px').editableList({
|
|
|
|
addItem: function(container,i,opt) {
|
|
|
|
var parent = container.parent();
|
|
|
|
var removeButton = parent.find(".red-ui-editableList-item-remove");
|
|
|
|
var disabled = (opt && !!opt.unused);
|
|
|
|
var row0 = $("<div/>").appendTo(container);
|
|
|
|
var fvar = $("<input/>", {
|
|
|
|
class: "node-input-libs-var",
|
|
|
|
placeholder: RED._("node-red:function.require.var"),
|
|
|
|
type: "text",
|
|
|
|
disabled: disabled
|
|
|
|
}).css({
|
|
|
|
width: "90px",
|
|
|
|
"margin-left": "5px"
|
|
|
|
}).appendTo(row0);
|
|
|
|
var fieldWidth = "260px";
|
|
|
|
var fmodule = $("<input/>", {
|
|
|
|
class: "node-input-libs-val",
|
|
|
|
placeholder: RED._("node-red:function.require.module"),
|
|
|
|
type: "text",
|
|
|
|
disabled: disabled
|
|
|
|
}).css({
|
|
|
|
width: fieldWidth,
|
|
|
|
"margin-left": "5px"
|
|
|
|
}).appendTo(row0);
|
|
|
|
|
|
|
|
var buttonWidth = "70px";
|
|
|
|
var install = $("<button/>", {
|
|
|
|
class: "red-ui-button red-ui-button-small"
|
|
|
|
}).css({
|
|
|
|
"margin-left": "10px",
|
|
|
|
width: buttonWidth
|
|
|
|
}).appendTo(row0);;
|
|
|
|
$("<i/>", {
|
|
|
|
class: "fa fa-cube"
|
|
|
|
}).appendTo(install);
|
|
|
|
$("<span/>").css({
|
|
|
|
"margin-left": "5px"
|
|
|
|
}).text("Install").appendTo(install);
|
|
|
|
|
|
|
|
var warn = $("<span/>").css({
|
|
|
|
"margin-left": "10px"
|
|
|
|
}).appendTo(row0);
|
|
|
|
$("<i/>", {
|
|
|
|
class: "fa fa-warning"
|
|
|
|
}).appendTo(warn);
|
|
|
|
RED.popover.tooltip(warn, "module spec conflict");
|
|
|
|
warn.hide();
|
|
|
|
|
|
|
|
var row1 = $("<div/>").css({
|
|
|
|
"margin-top": "5px"
|
|
|
|
}).appendTo(container);
|
|
|
|
|
|
|
|
var linstalled = $("<label/>").css({
|
|
|
|
width: "90px",
|
|
|
|
"text-align": "right",
|
|
|
|
"margin-right": "10px"
|
|
|
|
}).appendTo(row1);
|
|
|
|
$("<i/>", {
|
|
|
|
class: "fa fa-cube"
|
|
|
|
}).appendTo(linstalled);
|
|
|
|
var status = "Installed";
|
|
|
|
$("<span/>").css({
|
|
|
|
"margin-left": "5px"
|
|
|
|
}).text(status).appendTo(linstalled);
|
|
|
|
|
|
|
|
var finstalled = $("<input/>", {
|
|
|
|
type: "text",
|
|
|
|
disabled: true
|
|
|
|
}).css({
|
|
|
|
width: fieldWidth
|
|
|
|
}).appendTo(row1);
|
|
|
|
|
|
|
|
var uninstall = $("<button/>", {
|
|
|
|
class: "red-ui-button red-ui-button-small"
|
|
|
|
}).css({
|
|
|
|
"margin-left": "10px",
|
|
|
|
width: buttonWidth
|
|
|
|
}).appendTo(row1);
|
|
|
|
$("<i/>", {
|
|
|
|
class: "fa fa-trash"
|
|
|
|
}).appendTo(uninstall);
|
|
|
|
$("<span/>").css({
|
|
|
|
"margin-left": "5px"
|
|
|
|
}).text("Uninstall").appendTo(uninstall);
|
|
|
|
|
|
|
|
var update = $("<button/>", {
|
|
|
|
class: "red-ui-button red-ui-button-small"
|
|
|
|
}).css({
|
|
|
|
"margin-left": "10px",
|
|
|
|
width: buttonWidth
|
|
|
|
}).appendTo(row1);
|
|
|
|
$("<i/>", {
|
|
|
|
class: "fa fa-refresh"
|
|
|
|
}).appendTo(update);
|
|
|
|
$("<span/>").css({
|
|
|
|
"margin-left": "5px"
|
|
|
|
}).text("Update").appendTo(update);
|
|
|
|
|
|
|
|
var row2 = $("<div/>", {
|
|
|
|
class: "red-ui-palette-module-shade"
|
|
|
|
}).appendTo(container);
|
|
|
|
$("<img/>", {
|
|
|
|
src: "red/images/spin.svg",
|
|
|
|
class: "red-ui-palette-spinner"
|
|
|
|
}).appendTo(row2);
|
|
|
|
row2.hide();
|
|
|
|
|
|
|
|
fvar.on("change", function (e) {
|
|
|
|
opt.vname = $(this).val();
|
|
|
|
});
|
|
|
|
|
|
|
|
fmodule.on("change", function (e) {
|
|
|
|
var val = $(this).val();
|
|
|
|
if (!checkLib(val, opt.id)) {
|
|
|
|
$(this).addClass("input-error");
|
|
|
|
warn.show();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$(this).removeClass("input-error");
|
|
|
|
warn.hide();
|
|
|
|
if (val && (val !== "") &&
|
|
|
|
(!opt.version || (opt.version === ""))) {
|
|
|
|
install.attr("disabled", false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
opt.spec = val;
|
|
|
|
});
|
|
|
|
|
|
|
|
install.on("click", function (e) {
|
|
|
|
withNotification(
|
|
|
|
"Install NPM Module " +opt.spec,
|
|
|
|
"Install",
|
|
|
|
function (cb) {
|
|
|
|
updateModule(
|
|
|
|
node, opt.spec, false,
|
|
|
|
fmodule, install,
|
|
|
|
row1, finstalled, removeButton,
|
|
|
|
row2,
|
|
|
|
cb);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
uninstall.on("click", function (e) {
|
|
|
|
withNotification(
|
|
|
|
"Uninstall NPM Module "+opt.spec,
|
|
|
|
"Uninstall",
|
|
|
|
function (cb) {
|
|
|
|
uninstallModule(
|
|
|
|
node, opt.spec, opt.name, opt.version,
|
|
|
|
fmodule, install,
|
|
|
|
row1, finstalled,
|
|
|
|
removeButton,
|
|
|
|
row2,
|
|
|
|
cb);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
update.on("click", function (e) {
|
|
|
|
withNotification(
|
|
|
|
"Update NPM Module " +opt.spec,
|
|
|
|
"Update",
|
|
|
|
function (cb) {
|
|
|
|
updateModule(
|
|
|
|
node, opt.spec, true,
|
|
|
|
fmodule, install,
|
|
|
|
row1, finstalled, removeButton,
|
|
|
|
row2,
|
|
|
|
cb);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
install.attr("disabled", true);
|
|
|
|
if (opt) {
|
|
|
|
function updateData(data) {
|
|
|
|
if (data.vname && (data.vname !== "")) {
|
|
|
|
fvar.val(data.vname);
|
|
|
|
}
|
|
|
|
if (data.spec && (data.spec !== "")) {
|
|
|
|
fmodule.val(data.spec);
|
|
|
|
}
|
|
|
|
if (data.version && (data.version !== "")) {
|
|
|
|
fmodule.prop("disabled", true);
|
|
|
|
row1.show();
|
|
|
|
removeButton.hide();
|
|
|
|
finstalled.val(data.name+"@"+data.version)
|
|
|
|
if (data.status["preinstalled"]) {
|
|
|
|
uninstall.attr("disabled", true);
|
|
|
|
update.attr("disabled", true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
install.attr("disabled", false);
|
|
|
|
row1.hide();
|
|
|
|
removeButton.show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
updateData(opt);
|
|
|
|
if (opt.inProgress) {
|
|
|
|
row2.show();
|
|
|
|
opt.onReady = function () {
|
|
|
|
row2.hide();
|
|
|
|
updateData(opt);
|
|
|
|
opt.onReady = null;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
removable: true,
|
|
|
|
sortable: true
|
|
|
|
});
|
|
|
|
updateLib(node, function () {
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-09-05 16:02:48 +02:00
|
|
|
RED.nodes.registerType('function',{
|
|
|
|
color:"#fdd0a2",
|
|
|
|
category: 'function',
|
|
|
|
defaults: {
|
|
|
|
name: {value:""},
|
2014-04-02 22:20:58 +02:00
|
|
|
func: {value:"\nreturn msg;"},
|
2015-02-13 23:07:31 +01:00
|
|
|
outputs: {value:1},
|
2020-03-06 17:55:45 +01:00
|
|
|
noerr: {value:0,required:true,validate:function(v) { return !v; }},
|
|
|
|
initialize: {value:""},
|
2021-01-11 11:32:16 +01:00
|
|
|
finalize: {value:""},
|
2021-01-27 14:27:54 +01:00
|
|
|
libs: {value: []}
|
2013-09-05 16:02:48 +02:00
|
|
|
},
|
|
|
|
inputs:1,
|
|
|
|
outputs:1,
|
2019-06-21 13:36:20 +02:00
|
|
|
icon: "function.svg",
|
2013-09-05 16:02:48 +02:00
|
|
|
label: function() {
|
2018-04-16 12:12:21 +02:00
|
|
|
return this.name||this._("function.function");
|
2013-09-05 16:02:48 +02:00
|
|
|
},
|
2017-12-06 23:47:13 +01:00
|
|
|
labelStyle: function() {
|
|
|
|
return this.name?"node_label_italic":"";
|
|
|
|
},
|
2013-09-05 16:02:48 +02:00
|
|
|
oneditprepare: function() {
|
2015-02-13 01:15:07 +01:00
|
|
|
var that = this;
|
2020-03-06 17:55:45 +01:00
|
|
|
|
|
|
|
var tabs = RED.tabs.create({
|
|
|
|
id: "func-tabs",
|
|
|
|
onchange: function(tab) {
|
|
|
|
$("#func-tabs-content").children().hide();
|
|
|
|
$("#" + tab.id).show();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
tabs.addTab({
|
|
|
|
id: "func-tab-init",
|
|
|
|
label: that._("function.label.initialize")
|
|
|
|
});
|
|
|
|
tabs.addTab({
|
2020-04-06 09:34:41 +02:00
|
|
|
id: "func-tab-body",
|
2020-03-06 17:55:45 +01:00
|
|
|
label: that._("function.label.function")
|
|
|
|
});
|
|
|
|
tabs.addTab({
|
|
|
|
id: "func-tab-finalize",
|
|
|
|
label: that._("function.label.finalize")
|
|
|
|
});
|
2021-01-11 11:32:16 +01:00
|
|
|
tabs.addTab({
|
|
|
|
id: "func-tab-config",
|
|
|
|
label: "Config"
|
|
|
|
});
|
|
|
|
|
2020-04-06 09:34:41 +02:00
|
|
|
tabs.activateTab("func-tab-body");
|
2020-03-06 17:55:45 +01:00
|
|
|
|
2013-09-05 16:02:48 +02:00
|
|
|
$( "#node-input-outputs" ).spinner({
|
2019-09-30 10:54:05 +02:00
|
|
|
min:0,
|
2017-08-21 23:00:23 +02:00
|
|
|
change: function(event, ui) {
|
|
|
|
var value = this.value;
|
|
|
|
if (!value.match(/^\d+$/)) { value = 1; }
|
|
|
|
else if (value < this.min) { value = this.min; }
|
|
|
|
if (value !== this.value) { $(this).spinner("value", value); }
|
|
|
|
}
|
2013-09-05 16:02:48 +02:00
|
|
|
});
|
|
|
|
|
2020-05-22 15:57:28 +02:00
|
|
|
var buildEditor = function(id, value, defaultValue) {
|
|
|
|
var editor = RED.editor.createEditor({
|
|
|
|
id: id,
|
|
|
|
mode: 'ace/mode/nrjavascript',
|
|
|
|
value: value || defaultValue || "",
|
|
|
|
globals: {
|
|
|
|
msg:true,
|
|
|
|
context:true,
|
|
|
|
RED: true,
|
|
|
|
util: true,
|
|
|
|
flow: true,
|
|
|
|
global: true,
|
|
|
|
console: true,
|
|
|
|
Buffer: true,
|
|
|
|
setTimeout: true,
|
|
|
|
clearTimeout: true,
|
|
|
|
setInterval: true,
|
|
|
|
clearInterval: true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (defaultValue && value === "") {
|
|
|
|
editor.moveCursorTo(defaultValue.split("\n").length - 1, 0);
|
2016-07-28 18:14:55 +02:00
|
|
|
}
|
2020-05-22 15:57:28 +02:00
|
|
|
return editor;
|
2020-05-11 07:37:14 +02:00
|
|
|
}
|
2020-05-22 15:57:28 +02:00
|
|
|
this.initEditor = buildEditor('node-input-init-editor',$("#node-input-initialize").val(),RED._("node-red:function.text.initialize"))
|
|
|
|
this.editor = buildEditor('node-input-func-editor',$("#node-input-func").val())
|
|
|
|
this.finalizeEditor = buildEditor('node-input-finalize-editor',$("#node-input-finalize").val(),RED._("node-red:function.text.finalize"))
|
2015-05-07 23:02:46 +02:00
|
|
|
|
2015-02-13 01:15:07 +01:00
|
|
|
RED.library.create({
|
|
|
|
url:"functions", // where to get the data from
|
|
|
|
type:"function", // the type of object the library is for
|
2015-02-26 22:29:56 +01:00
|
|
|
editor:this.editor, // the field name the main text body goes to
|
2018-10-03 00:01:34 +02:00
|
|
|
mode:"ace/mode/nrjavascript",
|
2020-04-06 09:34:41 +02:00
|
|
|
fields:[
|
|
|
|
'name', 'outputs',
|
|
|
|
{
|
|
|
|
name: 'initialize',
|
|
|
|
get: function() {
|
|
|
|
return that.initEditor.getValue();
|
|
|
|
},
|
|
|
|
set: function(v) {
|
2020-05-22 15:57:28 +02:00
|
|
|
that.initEditor.setValue(v||RED._("node-red:function.text.initialize"), -1);
|
2020-04-06 09:34:41 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'finalize',
|
|
|
|
get: function() {
|
|
|
|
return that.finalizeEditor.getValue();
|
|
|
|
},
|
|
|
|
set: function(v) {
|
2020-05-22 15:57:28 +02:00
|
|
|
that.finalizeEditor.setValue(v||RED._("node-red:function.text.finalize"), -1);
|
2020-04-06 09:34:41 +02:00
|
|
|
}
|
2020-05-22 16:43:17 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'info',
|
|
|
|
get: function() {
|
|
|
|
return that.infoEditor.getValue();
|
|
|
|
},
|
|
|
|
set: function(v) {
|
|
|
|
that.infoEditor.setValue(v||"", -1);
|
|
|
|
}
|
2020-04-06 09:34:41 +02:00
|
|
|
}
|
|
|
|
],
|
2019-05-21 23:58:56 +02:00
|
|
|
ext:"js"
|
2013-09-05 16:02:48 +02:00
|
|
|
});
|
2015-02-26 22:29:56 +01:00
|
|
|
this.editor.focus();
|
2018-07-15 22:06:51 +02:00
|
|
|
|
2019-05-27 16:48:06 +02:00
|
|
|
|
2020-05-22 15:57:28 +02:00
|
|
|
var expandButtonClickHandler = function(editor) {
|
|
|
|
return function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
var value = editor.getValue();
|
|
|
|
RED.editor.editJavaScript({
|
|
|
|
value: value,
|
|
|
|
width: "Infinity",
|
|
|
|
cursor: editor.getCursorPosition(),
|
|
|
|
mode: "ace/mode/nrjavascript",
|
|
|
|
complete: function(v,cursor) {
|
|
|
|
editor.setValue(v, -1);
|
|
|
|
editor.gotoLine(cursor.row+1,cursor.column,false);
|
|
|
|
setTimeout(function() {
|
|
|
|
editor.focus();
|
|
|
|
},300);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$("#node-init-expand-js").on("click", expandButtonClickHandler(this.initEditor));
|
|
|
|
$("#node-function-expand-js").on("click", expandButtonClickHandler(this.editor));
|
|
|
|
$("#node-finalize-expand-js").on("click", expandButtonClickHandler(this.finalizeEditor));
|
|
|
|
|
|
|
|
RED.popover.tooltip($("#node-init-expand-js"), RED._("node-red:common.label.expand"));
|
2019-05-27 16:48:06 +02:00
|
|
|
RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand"));
|
2020-05-22 15:57:28 +02:00
|
|
|
RED.popover.tooltip($("#node-finalize-expand-js"), RED._("node-red:common.label.expand"));
|
2020-03-06 17:55:45 +01:00
|
|
|
|
2021-01-27 14:27:54 +01:00
|
|
|
prepareLibraryConfig(that);
|
2013-09-05 16:02:48 +02:00
|
|
|
},
|
|
|
|
oneditsave: function() {
|
2020-03-06 17:55:45 +01:00
|
|
|
var node = this;
|
|
|
|
var noerr = 0;
|
2015-05-08 16:31:48 +02:00
|
|
|
$("#node-input-noerr").val(0);
|
2020-05-22 15:57:28 +02:00
|
|
|
|
|
|
|
var disposeEditor = function(editorName,targetName,defaultValue) {
|
|
|
|
var editor = node[editorName];
|
|
|
|
var annot = editor.getSession().getAnnotations();
|
|
|
|
for (var k=0; k < annot.length; k++) {
|
|
|
|
if (annot[k].type === "error") {
|
|
|
|
noerr += annot.length;
|
|
|
|
break;
|
|
|
|
}
|
2015-02-13 23:07:31 +01:00
|
|
|
}
|
2020-05-22 15:57:28 +02:00
|
|
|
var val = editor.getValue();
|
2020-06-05 11:36:49 +02:00
|
|
|
if (defaultValue) {
|
|
|
|
if (val.trim() == defaultValue.trim()) {
|
|
|
|
val = "";
|
|
|
|
}
|
2020-03-06 17:55:45 +01:00
|
|
|
}
|
2020-05-22 15:57:28 +02:00
|
|
|
editor.destroy();
|
|
|
|
delete node[editorName];
|
|
|
|
$("#"+targetName).val(val);
|
2015-02-13 23:07:31 +01:00
|
|
|
}
|
2020-05-22 15:57:28 +02:00
|
|
|
disposeEditor("editor","node-input-func");
|
|
|
|
disposeEditor("initEditor","node-input-initialize", RED._("node-red:function.text.initialize"));
|
|
|
|
disposeEditor("finalizeEditor","node-input-finalize", RED._("node-red:function.text.finalize"));
|
|
|
|
|
2020-03-06 17:55:45 +01:00
|
|
|
$("#node-input-noerr").val(noerr);
|
|
|
|
this.noerr = noerr;
|
|
|
|
|
2021-01-27 14:27:54 +01:00
|
|
|
var libs = $("#node-input-libs-container").editableList("items");
|
|
|
|
var oldLibs = node.libs || [];
|
|
|
|
var newLibs = [];
|
|
|
|
node.libs = newLibs;
|
|
|
|
libs.each(function(i) {
|
|
|
|
var item = $(this);
|
|
|
|
var v = item.find(".node-input-libs-var").val();
|
|
|
|
var n = item.find(".node-input-libs-val").val();
|
|
|
|
if ((!v || (v === "")) ||
|
|
|
|
(!n || (n === ""))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
newLibs.push({
|
|
|
|
vname: v,
|
|
|
|
spec: n
|
|
|
|
});
|
|
|
|
});
|
2016-01-04 17:53:32 +01:00
|
|
|
},
|
2017-04-21 10:03:41 +02:00
|
|
|
oneditcancel: function() {
|
2020-03-06 17:55:45 +01:00
|
|
|
var node = this;
|
|
|
|
|
|
|
|
node.editor.destroy();
|
|
|
|
delete node.editor;
|
|
|
|
|
|
|
|
node.initEditor.destroy();
|
|
|
|
delete node.initEditor;
|
|
|
|
|
|
|
|
node.finalizeEditor.destroy();
|
|
|
|
delete node.finalizeEditor;
|
2017-04-21 10:03:41 +02:00
|
|
|
},
|
2016-01-04 17:53:32 +01:00
|
|
|
oneditresize: function(size) {
|
|
|
|
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
|
|
|
var height = $("#dialog-form").height();
|
2019-04-29 12:50:15 +02:00
|
|
|
for (var i=0; i<rows.length; i++) {
|
2016-01-04 17:53:32 +01:00
|
|
|
height -= $(rows[i]).outerHeight(true);
|
|
|
|
}
|
|
|
|
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
|
|
|
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
|
|
|
$(".node-text-editor").css("height",height+"px");
|
|
|
|
this.editor.resize();
|
2020-03-06 17:55:45 +01:00
|
|
|
|
|
|
|
var height = size.height;
|
2020-05-11 07:37:14 +02:00
|
|
|
$("#node-input-init-editor").css("height", (height -105)+"px");
|
2020-05-22 15:57:28 +02:00
|
|
|
$("#node-input-func-editor").css("height", (height -145)+"px");
|
2020-05-11 07:37:14 +02:00
|
|
|
$("#node-input-finalize-editor").css("height", (height -105)+"px");
|
2020-05-22 15:57:28 +02:00
|
|
|
|
|
|
|
this.initEditor.resize();
|
|
|
|
this.editor.resize();
|
|
|
|
this.finalizeEditor.resize();
|
|
|
|
|
2021-01-27 14:27:54 +01:00
|
|
|
$("#node-input-libs-container").css("height", (height -155)+"px");
|
2013-09-05 16:02:48 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|