Browse code

initial import

devnewton authored on 06/09/2017 08:30:38
Showing 7 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,3 @@
1
+/node_modules/
2
+/nbproject/project.xml
3
+/nbproject/project.properties
0 4
\ No newline at end of file
1 5
new file mode 100644
... ...
@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2017 devnewton <devnewton@bci.im>
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+THE SOFTWARE.
0 22
\ No newline at end of file
1 23
new file mode 100644
... ...
@@ -0,0 +1,6 @@
1
+Node Red node to parse xml/html and map to javascript object.
2
+
3
+Derived from
4
+
5
+- Node Red html node.
6
+- https://github.com/njh/node-red-contrib-mapper
0 7
\ No newline at end of file
1 8
new file mode 100644
... ...
@@ -0,0 +1,145 @@
1
+<script type="text/x-red" data-template-name="cheerio">
2
+    <div class="form-row">
3
+        <label for="node-input-tag"><i class="fa fa-filter"></i> <span data-i18n="cheerio.label.select"></span></label>
4
+        <input type="text" id="node-input-tag" placeholder="h1">
5
+    </div>
6
+    <div class="form-row">
7
+        <label for="node-input-ret"><i class="fa fa-sign-out"></i> <span data-i18n="cheerio.label.output"></span></label>
8
+        <select id="node-input-ret" style="width:70%">
9
+            <option value="html" data-i18n="cheerio.output.html"></option>
10
+            <option value="text" data-i18n="cheerio.output.text"></option>
11
+            <option value="object" data-i18n="cheerio.output.object"></option>
12
+        </select>
13
+    </div>
14
+    <div class="form-row">
15
+        <label for="node-input-as">&nbsp;</label>
16
+        <select id="node-input-as" style="width:70%">
17
+            <option value="single" data-i18n="cheerio.format.single"></option>
18
+            <option value="multi" data-i18n="cheerio.format.multi"></option>
19
+        </select>
20
+    </div>
21
+    <div class="form-row">
22
+        <div id="node-input-mapping-container-div" style="border-radius: 5px; height: 320px; padding: 5px; border: 1px solid #ccc; overflow-y:scroll;">
23
+        <ol id="node-input-mapping-container" style=" list-style-type:none; margin: 0;">
24
+        </ol>
25
+        </div>
26
+        <a href="#" class="btn btn-mini" id="node-input-add-mapping" style="margin-top: 4px;"><i class="fa fa-plus"></i> Add</a>
27
+    </div>
28
+    <br/>
29
+    <div class="form-row">
30
+        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
31
+        <input type="text" id="node-input-name" style="width:70%" data-i18n="[placeholder]common.label.name">
32
+    </div>
33
+</script>
34
+
35
+<script type="text/x-red" data-help-name="cheerio">
36
+    <p>Extracts elements from an xml/html document held in <code>msg.payload</code> using CSS selectors.</p>
37
+    <h3>Inputs</h3>
38
+    <dl class="message-properties">
39
+        <dt>payload <span class="property-type">string</span></dt>
40
+        <dd>the html string from which to extract elements.</dd>
41
+        <dt class="optional">select <span class="property-type">string</span></dt>
42
+        <dd>if not configured in the edit panel the selector can be set as a property of msg.</dd>
43
+    </dl>
44
+    <h3>Output</h3>
45
+    <dl class="message-properties">
46
+        <dt>payload <span class="property-type">array | string</span></dt>
47
+        <dd>the result can be either a single message with a payload containing an array of the matched elements, or multiple
48
+           messages that each contain a matched element.</dd>
49
+    </dl>
50
+    <h3>Details</h3>
51
+    <p>This node supports a combination of CSS and jQuery selectors. See the
52
+    <a href="https://github.com/fb55/CSSselect#user-content-supported-selectors" target="_blank">css-select documentation</a> for more information
53
+    on the supported syntax.</p>
54
+</script>
55
+
56
+<script type="text/javascript">
57
+    RED.nodes.registerType('cheerio', {
58
+        category: 'function',
59
+        color: "#DEBD5C",
60
+        defaults: {
61
+            name: {value: ""},
62
+            tag: {value: ""},
63
+            ret: {value: "html"},
64
+            as: {value: "single"},
65
+            map: {value:[{search:"",replace:"", ret: "html", attr: ""}]}
66
+        },
67
+        inputs: 1,
68
+        outputs: 1,
69
+        icon: "parser-html.png",
70
+        label: function () {
71
+            return this.name || this.tag || "cheerio";
72
+        },
73
+        labelStyle: function () {
74
+            return this.name ? "node_label_italic" : "";
75
+        },
76
+        oneditprepare: function() {
77
+
78
+            function generateRow(mapping) {
79
+                var container = $('<li/>',{style:"margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;"});
80
+                var row = $('<div/>').appendTo(container);
81
+                var searchField = $('<input/>',{class:"node-input-mapping-search",type:"text",style:"margin-left: 5px; width: 25%;",placeholder:"Search", title:"Selector"}).appendTo(row);
82
+                var retField = $('<select></select>',{class:"node-input-mapping-ret",style:"margin-left: 5px; width: 20%;", title:"Return"}).appendTo(row);
83
+                $('<option value="text">text</option><option value="html">html</option><option value="attribute">attribute</option>').appendTo(retField);
84
+                var attrField = $('<input/>',{class:"node-input-mapping-attr",type:"text",style:"margin-left: 5px; width: 10%;",placeholder:"attribute", title:"Attribute"}).appendTo(row);
85
+                $('<span/>',{class:"fa fa-arrow-right",style:"margin: 5px"}).appendTo(row);
86
+                var replaceField = $('<input/>',{class:"node-input-mapping-replace",type:"text",style:"width: 25%;",placeholder:"Replace", title: 'Output field'}).appendTo(row);
87
+                searchField.val(mapping.search);
88
+                retField.val(mapping.ret);
89
+                attrField.val(mapping.attr);
90
+                replaceField.val(mapping.replace);
91
+
92
+                var deleteButton = $('<a/>',{href:"#",class:"btn btn-mini", style:"margin-left: 5px;"}).appendTo(row);
93
+                $('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
94
+
95
+                deleteButton.click(function() {
96
+                    container.css({"background":"#fee"});
97
+                    container.fadeOut(300, function() {
98
+                        $(this).remove();
99
+                    });
100
+                });
101
+
102
+                $("#node-input-mapping-container").append(container);
103
+            }
104
+
105
+            $("#node-input-add-mapping").click(function() {
106
+                generateRow({search:"",replace:"", ret: "html", attr:""});
107
+                $("#node-input-mapping-container-div").scrollTop($("#node-input-mapping-container-div").get(0).scrollHeight);
108
+            });
109
+
110
+            for(var i=0; i<this.map.length; i++) {
111
+                var mapping = this.map[i];
112
+                generateRow(mapping);
113
+            }
114
+
115
+            function mapperDialogResize(ev,ui) {
116
+                $("#node-input-mapping-container-div").css("height",(ui.size.height-300)+"px");
117
+            };
118
+
119
+            $( "#dialog" ).on("dialogresize", mapperDialogResize);
120
+            $( "#dialog" ).one("dialogopen", function(ev) {
121
+                var size = $( "#dialog" ).dialog('option','sizeCache-mapper');
122
+                if (size) {
123
+                    mapperDialogResize(null,{size:size});
124
+                }
125
+            });
126
+            $( "#dialog" ).one("dialogclose", function(ev,ui) {
127
+                $( "#dialog" ).off("dialogresize",mapperDialogResize);
128
+            });
129
+        },
130
+        oneditsave: function() {
131
+            var map = $("#node-input-mapping-container").children();
132
+            var node = this;
133
+            node.map = [];
134
+            map.each(function() {
135
+                var mapping = $(this);
136
+                node.map.push({
137
+                    search: mapping.find(".node-input-mapping-search").val(),
138
+                    ret: mapping.find(".node-input-mapping-ret").val(),
139
+                    replace: mapping.find(".node-input-mapping-replace").val(),
140
+                    attr:  mapping.find(".node-input-mapping-attr").val()
141
+                });
142
+            });
143
+        }
144
+    });
145
+</script>
0 146
\ No newline at end of file
1 147
new file mode 100644
... ...
@@ -0,0 +1,72 @@
1
+module.exports = function (RED) {
2
+    "use strict";
3
+    var cheerio = require('cheerio');
4
+    function CheerioNode(n) {
5
+        RED.nodes.createNode(this, n);
6
+        this.tag = n.tag;
7
+        this.ret = n.ret || "html";
8
+        this.as = n.as || "single";
9
+        this.map = n.map || [];
10
+        var node = this;
11
+        this.on("input", function (msg) {
12
+            if (msg.hasOwnProperty("payload")) {
13
+                var tag = node.tag;
14
+                if (msg.hasOwnProperty("select")) {
15
+                    tag = node.tag || msg.select;
16
+                }
17
+                try {
18
+                    var $ = cheerio.load(msg.payload);
19
+                    var payloads = [];
20
+                    $(tag).each(function () {
21
+                        var payload = null;
22
+                        switch (node.ret) {
23
+                            case "html":
24
+                                payload = $(this).html();
25
+                                break;
26
+                            case "text":
27
+                                payload = $(this).text();
28
+                                break;
29
+                            case "object":
30
+                                payload = {};
31
+                                node.map.forEach(function(mapping) {
32
+                                    var element = mapping.search ? this.find(mapping.search) : this;
33
+                                    switch(mapping.ret) {
34
+                                        case "html":
35
+                                            payload[mapping.replace] = element.html();
36
+                                            break;
37
+                                        case "text":
38
+                                            payload[mapping.replace] = element.text();
39
+                                            break;
40
+                                        case "attribute":
41
+                                            payload[mapping.replace] = element.attr(mapping.attr);
42
+                                    }
43
+                                    
44
+                                }, $(this));
45
+                                break;
46
+                        }
47
+                        if (payload) {
48
+                            switch (node.as) {
49
+                                case "multi":
50
+                                    msg.payload = payload;
51
+                                    node.send(msg);
52
+                                    break;
53
+                                case "single":
54
+                                    payloads.push(payload);
55
+                                    break;
56
+                            }
57
+                        }
58
+                    });
59
+                    if ((node.as === "single") && (payloads.length !== 0)) {
60
+                        msg.payload = payloads;
61
+                        node.send(msg);
62
+                    }
63
+                } catch (error) {
64
+                    node.error(error.message, msg);
65
+                }
66
+            } else {
67
+                node.send(msg);
68
+            } // If no payload - just pass it on.
69
+        });
70
+    }
71
+    RED.nodes.registerType("cheerio", CheerioNode);
72
+};
0 73
\ No newline at end of file
1 74
new file mode 100644
... ...
@@ -0,0 +1,17 @@
1
+{
2
+    "cheerio": {
3
+        "label": {
4
+            "select": "Root selector",
5
+            "output": "Output"
6
+        },
7
+        "output": {
8
+            "html": "html content of the elements",
9
+            "text": "text content of the elements",
10
+            "object": "map to objects"
11
+        },
12
+        "format": {
13
+            "single": "as a single message containing an array",
14
+            "multi": "as multiple messages, one for each element"
15
+        }
16
+    }
17
+}
0 18
new file mode 100644
... ...
@@ -0,0 +1,21 @@
1
+{
2
+  "name": "node-red-contrib-cheerio",
3
+  "version": "1.0.0",
4
+  "keywords": [
5
+    "util",
6
+    "functional",
7
+    "server",
8
+    "client",
9
+    "browser"
10
+  ],
11
+  "author": "devnewton",
12
+  "contributors": [],
13
+  "dependencies": {
14
+    "cheerio": "^1.0.0-rc.2"
15
+  },
16
+  "node-red": {
17
+    "nodes": {
18
+      "cheerio": "cheerio.js"
19
+    }
20
+  }
21
+}