2 * Copyright 2014 IBM Corp.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 module.exports = function(RED) {
20 RED.nodes.createNode(this,n);
21 this.template = n.temp.split(",");
22 this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r");
24 this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r");
25 this.winflag = (this.ret === "\r\n");
27 this.multi = n.multi || "one";
28 this.hdrin = n.hdrin || false;
29 this.hdrout = n.hdrout || false;
33 // pass in an array of column names to be trimed, de-quoted and retrimed
34 var clean = function(col) {
35 for (var t = 0; t < col.length; t++) {
36 col[t] = col[t].trim(); // remove leading and trailing whitespace
37 if (col[t].charAt(0) === '"' && col[t].charAt(col[t].length -1) === '"') {
38 // remove leading and trailing quotes (if they exist) - and remove whitepace again.
39 col[t] = col[t].substr(1,col[t].length -2).trim();
42 if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
43 else { node.goodtmpl = true; }
46 node.template = clean(node.template);
48 this.on("input", function(msg) {
49 if (msg.hasOwnProperty("payload")) {
50 if (typeof msg.payload == "object") { // convert object to CSV string
54 ou += node.template.join(node.sep) + node.ret;
56 if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
57 for (var s = 0; s < msg.payload.length; s++) {
58 for (var t=0; t < node.template.length; t++) {
60 // aaargh - resorting to eval here - but fairly contained front and back.
61 var p = RED.util.ensureString(eval("msg.payload[s]."+node.template[t]));
63 if (p === "undefined") { p = ""; }
64 if (p.indexOf(node.sep) != -1) { // add quotes if any "commas"
65 ou += node.quo + p + node.quo + node.sep;
67 else if (p.indexOf(node.quo) != -1) { // add double quotes if any quotes
68 p = p.replace(/"/g, '""');
69 ou += node.quo + p + node.quo + node.sep;
71 else { ou += p + node.sep; } // otherwise just add
73 ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
75 node.send({payload:ou});
77 catch(e) { node.log(e); }
79 else if (typeof msg.payload == "string") { // convert CSV string to object
81 var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
82 var j = 0; // pointer into array of template items
83 var k = [""]; // array of data for each of the template items
84 var o = {}; // output object to build up
85 var a = []; // output array is needed for multiline option
86 var first = true; // is this the first line
89 // For now we are just going to assume that any \r or \n means an end of line...
90 // got to be a weird csv that has singleton \r \n in it for another reason...
92 // Now process the whole file/line
93 for (var i = 0; i < msg.payload.length; i++) {
94 if ((node.hdrin === true) && first) { // if the template is in the first line
95 if ((msg.payload[i] === "\n")||(msg.payload[i] === "\r")) { // look for first line break
96 node.template = clean(tmp.split(node.sep));
99 else { tmp += msg.payload[i]; }
102 if (msg.payload[i] === node.quo) { // if it's a quote toggle inside or outside
104 if (msg.payload[i-1] === node.quo) { k[j] += '\"'; } // if it's a quotequote then it's actually a quote
106 else if ((msg.payload[i] === node.sep) && f) { // if we are outside of quote (ie valid separator
107 if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
108 if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "" ) ) {
109 if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); }
110 o[node.template[j]] = k[j];
115 else if (f && ((msg.payload[i] === "\n") || (msg.payload[i] === "\r"))) { // handle multiple lines
116 //console.log(j,k,o,k[j]);
117 if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
118 if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); }
119 else { k[j].replace(/\r$/,''); }
120 o[node.template[j]] = k[j];
122 if (JSON.stringify(o) !== "{}") { // don't send empty objects
123 if (node.multi === "one") { node.send({payload:o}); } // either send
124 else { a.push(o); } // or add to the array
130 else { // just add to the part of the message
131 k[j] += msg.payload[i];
135 // Finished so finalize and send anything left
136 //console.log(j,k,o,k[j]);
137 if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
138 if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
139 if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); }
140 else { k[j].replace(/\r$/,''); }
141 o[node.template[j]] = k[j];
144 if (JSON.stringify(o) !== "{}") { // don't send empty objects
145 if (node.multi === "one") { node.send({payload:o}); } // either send
146 else { a.push(o); } // or add to the aray
148 if (node.multi !== "one") { node.send({payload:a}); } // finally send the array
150 catch(e) { node.log(e); }
152 else { node.log("This node only handles csv strings or js objects."); }
156 RED.nodes.registerType("csv",CSVNode);