123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- module.exports = function(RED) {
- var ui = require('../ui')(RED);
-
- function DropdownNode(config) {
- RED.nodes.createNode(this, config);
- this.pt = config.passthru;
- this.multiple = config.multiple || false;
- this.state = [" "," "];
- var node = this;
- node.status({});
-
- var group = RED.nodes.getNode(config.group);
- if (!group) { return; }
- var tab = RED.nodes.getNode(group.config.tab);
- if (!tab) { return; }
-
- var control = {
- type: 'dropdown',
- multiple: config.multiple,
- label: config.label,
- tooltip: config.tooltip,
- place: config.place,
- order: config.order,
- value: config.payload || node.id,
- width: config.width || group.config.width || 6,
- height: config.height || 1,
- className: config.className || '',
- };
-
- for (var o=0; o<config.options.length; o++) {
- config.options[o].label = config.options[o].label || config.options[o].value;
- }
- control.options = config.options;
-
- var emitOptions = { value:undefined };
-
- node.on("input", function(msg) {
- node.topi = msg.topic;
- });
-
- var done = ui.add({
- node: node,
- tab: tab,
- group: group,
- forwardInputMessages: config.passthru,
- control: control,
-
- convert: function (payload, oldValue, msg) {
- // convert msg
- // as of now, only allow a full replacement of options
- // beforeEmit is only called when a node linked to us sends a msg
- // we are expecting to receive an "update options" msg
- // which we expect to be an array of new options
-
- // for convenience, we pass an indication to the node connected to this dropdown
- // that this is an "update options" message coming from the input sender
- // 'beforeEmit' is called before 'beforeSend', so we may pass in that info
- // otherwise that convenience info would not be sent (would not cause any problems)...
-
- emitOptions = {isOptionsValid:false, value:undefined, newOptions:undefined};
- do {
- if (!msg.options) { break; }
- if (typeof msg.options === "string" ) { msg.options = [ msg.options ]; }
- if (!Array.isArray(msg.options)) { break; }
- emitOptions.newOptions = [];
- if (msg.options.length === 0) {
- emitOptions.isOptionsValid = true;
- break;
- }
- // could check whether or not all members have same type
- for (var i = 0; i < msg.options.length; i++) {
- var opt = msg.options[i];
- if (opt === undefined || opt === null) { continue; }
- switch (typeof opt) {
- case 'number': {
- opt = "" + opt;
- emitOptions.newOptions.push({label:opt, value:opt, type:"number"});
- break;
- }
- case 'string': {
- emitOptions.newOptions.push({label:opt, value:opt, type:"string"});
- break;
- }
- case 'object': {
- // assuming object of {label:value}
- for (var m in opt) {
- if (opt.hasOwnProperty(m)) {
- emitOptions.newOptions.push({label:m, value:opt[m], type:typeof(opt[m])});
- }
- }
- break;
- }
- default:
- // do nothing, just continue with next option
- }
- }
- // send null object on change of menu list
- if (emitOptions.newOptions.length > 0) { emitOptions.value = null; }
- // or send the preselected value
- if (msg.payload) { emitOptions.value = msg.payload; }
- emitOptions.isOptionsValid = true;
- } while (false);
- // finally adjust msg to reflect the input
- msg._dontSend = true;
- if (emitOptions.isOptionsValid) {
- control.options = emitOptions.newOptions;
- control.value = emitOptions.value;
- }
- else {
- if (msg.options) {
- node.error("ERR: Invalid Options", msg);
- }
- }
-
- if (msg.hasOwnProperty("payload")) {
- if (node.multiple) {
- if (typeof msg.payload === "string") {
- msg.payload = msg.payload.split(',');
- }
- }
- emitOptions.value = msg.payload;
- control.value = emitOptions.value;
- delete msg._dontSend;
- return emitOptions;
- }
- // we do not overide payload here due to 'opt.emitOnlyNewValues' in ui.js
- // when undefined is returned, msg will not be forwarded
- return emitOptions.isOptionsValid ? emitOptions : undefined; // always pass entire object (newValue == oldValue)
- },
-
- beforeEmit: function (msg, newValue) {
- if (msg.socketid) { emitOptions.socketid = msg.socketid; }
- return emitOptions;
- },
-
- convertBack: function (msg) {
- var val = node.multiple ? [] : "";
- var m = RED.util.cloneMessage(msg);
- var mm = (m.hasOwnProperty("id") && m.hasOwnProperty("value")) ? m.value : m;
- for (var i=0; i<control.options.length; i++) {
- if (!node.multiple) {
- delete m["$$mdSelectId"];
- if (JSON.stringify(control.options[i].value) == JSON.stringify(mm)) {
- val = control.options[i].value;
- if (typeof val === "string" && control.options[i].type.indexOf("str") !== 0) {
- try { val = JSON.parse(val); }
- catch(e) {}
- }
- break;
- }
- }
- else if (node.multiple && mm !== null) {
- if (!Array.isArray(mm)) {
- if (mm.hasOwnProperty("value")) { mm = mm.value; }
- // if (typeof m === "string") { m = [ m ]; }
- if (mm == null) { mm = []; }
- else { mm = [ mm ]; }
- }
- mm.map(x => delete x["$$mdSelectId"])
- for (var j = 0; j < mm.length; j++) {
- if (JSON.stringify(control.options[i].value) === JSON.stringify(mm[j])) {
- var v = control.options[i].value;
- if (typeof v === "string" && control.options[i].type !== "string") {
- try { v = JSON.parse(v); }
- catch(e) {}
- }
- val.push(v);
- break;
- }
- }
- }
- }
- return val;
- },
-
- beforeSend: function (msg) {
- if (msg.payload === undefined) { msg.payload = []; }
- if (msg.payload === "") { msg._dontSend = true; }
- if (msg._dontSend) {
- delete msg.options;
- msg.payload = emitOptions.value;
- }
- var t = RED.util.evaluateNodeProperty(config.topic,config.topicType || "str",node,msg) || node.topi;
- if (t) { msg.topic = t; }
- if (msg.payload === null || msg._dontSend) { node.status({}); }
- else {
- var stat = "";
- if (Array.isArray(msg.payload)) { stat = msg.payload.length + " items"; }
- else {
- if (typeof msg.payload === "object") { stat = JSON.stringify(msg.payload); }
- else { stat = msg.payload.toString(); }
- if (stat.length > 32) { stat = stat.substr(0,31)+"..."; }
- }
- if (node.pt) {
- node.status({shape:"dot",fill:"grey",text:stat});
- }
- else {
- node.state[1] = stat;
- node.status({shape:"dot",fill:"grey",text:node.state[1] + " | " + node.state[1]});
- }
- }
- }
- });
-
- if (!node.pt) {
- node.on("input", function(msg) {
- node.state[0] = msg.payload;
- node.status({shape:"dot",fill:"grey",text:node.state[0] + " | " + node.state[1]});
- });
- }
-
- node.on("close", done);
- }
- RED.nodes.registerType("ui_dropdown", DropdownNode);
- };
|