@gregoriusrippenstein/node-red-contrib-introspection 0.12.8 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/nodes/60-client-code.html +1 -1
- package/nodes/60-client-code.js +1 -0
- package/package.json +1 -1
- package/plugins/sidebar.html +17 -13
package/README.md
CHANGED
|
@@ -136,6 +136,16 @@ This is something that I can not say strenuously enough!!! It is all too easy, i
|
|
|
136
136
|
|
|
137
137
|
That's also another thing that I've noticed: sometimes I would leavethe message tracing on for too long, and only I realize that once the editor got flooded. That is also simpler in Node-RED 5.x because you can keep that panel open while switching between the debug nodes. This is what always happened to me because I switched between the debug and the message tracer panel and then forgot that I left the tracing on, as I had headed off to the debug panel. So in Node-RED 5, that's a little bit nicer because you can keep that panel with the message tracing open and then switch off tracing as needed.
|
|
138
138
|
|
|
139
|
+
### Message Tracing Fail Safe
|
|
140
|
+
|
|
141
|
+

|
|
142
|
+
|
|
143
|
+
Because of the risk to freezing the editor, a fail safe in message generation has been added.
|
|
144
|
+
|
|
145
|
+
If the message tracing detects more than 100 messages per second, then the message tracing is deactivated automatically. One hundred messages per second is a default value and can be modifed in the browser console using `window.msgTracer_MAX_MSG_PER_SECOND = 100`.
|
|
146
|
+
|
|
147
|
+
Message counts include the debug message generated and the message tracing shown in the sidebar.
|
|
148
|
+
|
|
139
149
|
## Palette Nodes
|
|
140
150
|
|
|
141
151
|
### Sink & Seeker
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
2
|
|
|
3
|
-
function handleMsgTracePacket(e,
|
|
3
|
+
function handleMsgTracePacket(e,a){if(a.nodeid){let t=RED.nodes.node(a.nodeid);if(t){var s,a=$("#node-input-msgtracer-trace-treelist").treeList("data");let r=!1;a.forEach(e=>{e.id==t.id&&(r=!0,e.seenCounter+=1,e.sublabel=e.seenCounter+" @ "+e.workspaceLabel)}),r?$("#node-input-msgtracer-trace-treelist").treeList("data",a):(s={id:t.id,label:"",_label:RED.utils.getNodeLabel(t),seenCounter:1,workspaceLabel:(RED.nodes.workspace(t.z)||{label:t.z}).label,sublabel:"1 @ "+(RED.nodes.workspace(t.z)||{label:t.z}).label,selected:!1,checkbox:!1,node:t,icon:RED.utils.createNodeIcon(t,!0)},$("#node-input-msgtracer-trace-treelist").treeList("data",[s,...a]))}}}RED.comms.subscribe("msgtracer:node-received",(e,r)=>{try{handleMsgTracePacket(e,r)}catch(e){console.error(e)}}),RED.comms.subscribe("msgtracer:node-received",(e,r)=>{window.msgTracerCounter||=0,window.msgTracerCounter+=1}),RED.comms.subscribe("msgtracer:debug-sent",(e,r)=>{window.msgTracerCounter||=0,window.msgTracerCounter+=1});
|
|
4
4
|
|
|
5
5
|
function handleClientCodeFrontend(event,data){if("execfunc"==data.msg){let doSend=(e,d,t)=>{"object"==typeof e&&(e={...t,...e}),$.ajax({url:"ClientCode/"+d,type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify(e),success:function(e){},error:function(e,t,o){RED.notify("ClientCode Communcation Failure: "+d+": "+t,{type:"error",id:d,timeout:3e3})}})},doStatus=(e,d,t)=>{$.ajax({url:"ClientCode/"+d+"/status",type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify(e),success:function(e){},error:function(e,t,o){RED.notify("ClientCode Communcation Failure: "+d+": "+t,{type:"error",id:d,timeout:3e3})}})},doError=(e,t,o)=>{RED.notify("ClientCode Failed: "+t+": "+e,{type:"error",id:t,timeout:3e3}),console.log("ClientCode: Error with node: "+t+": "+e)},nodeid=data.nodeid,_msg=data._msg,msg=data._msg??{},cfg=data._cfg,node={id:data.nodeid,sendMsg:e=>{doSend(e,nodeid,{_msgid:RED.nodes.id()})},send:e=>{doSend(e,nodeid,_msg)},error:e=>{doError(e,nodeid,_msg)},status:e=>{doStatus(e,nodeid,_msg)}},payload=data.payload,topic=data.topic;eval(data.func)}}RED.comms.subscribe("introspect:client-code-perform",(e,t)=>{try{handleClientCodeFrontend(e,t)}catch(e){console.error(e)}});
|
|
6
6
|
|
package/nodes/60-client-code.js
CHANGED
package/package.json
CHANGED
package/plugins/sidebar.html
CHANGED
|
@@ -14,14 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
function setupTreelistAllLinksForLinkIn(e){e=collectAllLinksNodes(e);if(0==e.length){RED.notify("No links found",{type:"error",timeout:2e3});try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){}}else{try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){$("#node-input-orphan-target-container-div").css({width:"100%",height:"calc(100%)"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){}).on("treelistitemmouseout",function(e,t){}).on("treelistselect",function(e,t){t.node&&(RED.workspaces.show(t.node.z,!1,!1,!0),RED.view.reveal(t.node.id,!0),RED.view.redraw())}).on("treelistconfirm",function(e,t){var n;t.node&&(n=t.node.id,setTimeout(()=>{var e=RED.nodes.node(n);e&&(RED.view.reveal(e.id),RED.view.select(e.id),RED.editor.edit(e,"editor-tab-description")),n==RED.workspaces.active()&&RED.workspaces.edit()},50))}),$("#node-input-orphan-target-filter").show();var n=$("#node-input-orphan-target-filter").searchBox({style:"compact",delay:300,change:function(){var e,t=$(this).val().trim().toLowerCase();""===t?($("#node-input-orphan-target-container-div").treeList("filter",null),n.searchBox("count","")):(e=$("#node-input-orphan-target-container-div").treeList("filter",function(e){return-1<e.label.toLowerCase().indexOf(t)||-1<e.node.type.toLowerCase().indexOf(t)}),n.searchBox("count",e+" / "+$("#node-input-orphan-target-container-div").treeList("data").length))}})}$("#node-input-orphan-target-container-div").treeList("data",e.sort((e,t)=>e.node.z>t.node.z))}}function collectAllLinksNodes(t){let n=[];RED.nodes.eachNode(e=>{"link call"!=e.type&&"link out"!=e.type||-1<e.links.indexOf(t)&&n.push(e)});var i=[],r={};return n.forEach(function(e){var t=RED.nodes.getType(e.type);if(t){var n,o=t.label,o=("function"==typeof o?o.call(e):o)||"";if(0===(n=e.type).indexOf("subflow:"))return}t&&o||(o=e.type),r[e.id]={node:e,label:o,sublabel:n,selected:!1,checkbox:!1},i.push(r[e.id])}),i}function collectNodeStats(){let t={},n=[],o={workspaces:0,junctions:0,subflows:0,configs:0,groups:0,wires:0,nodes:0},i=(RED.nodes.eachConfig(e=>{o.configs+=1}),RED.nodes.eachLink(e=>{o.wires+=1}),RED.nodes.eachJunction(e=>{o.junctions+=1}),RED.nodes.eachGroup(e=>{o.groups+=1}),RED.nodes.eachWorkspace(e=>{o.workspaces+=1}),RED.nodes.eachSubflow(e=>{o.subflows+=1}),RED.nodes.eachNode(e=>{o.nodes+=1,t[e.type]=(t[e.type]||0)+1}),Object.keys(t).forEach(e=>{n.push({label:e,sublabel:t[e],selected:!1,checkbox:!1})}),[]),r=0;return Object.keys(o).forEach(e=>{r+=o[e],i.push({label:e,sublabel:o[e],selected:!1,checkbox:!1})}),[{label:"__ TOTALS",sublabel:r,selected:!1,checkbox:!1,children:i}].concat(n.sort((e,t)=>e.label>t.label))}function setupTreeListForNodeStats(){var e=collectNodeStats();try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){$("#node-input-orphan-target-container-div").css({width:"100%",height:"calc(100%)"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){}).on("treelistitemmouseout",function(e,t){}).on("treelistselect",function(e,t){}).on("treelistconfirm",function(e,t){}),$("#node-input-orphan-target-filter").show();var n=$("#node-input-orphan-target-filter").searchBox({style:"compact",delay:300,change:function(){var e,t=$(this).val().trim().toLowerCase();""===t?($("#node-input-orphan-target-container-div").treeList("filter",null),n.searchBox("count","")):(e=$("#node-input-orphan-target-container-div").treeList("filter",function(e){return-1<e.label.toLowerCase().indexOf(t)}),n.searchBox("count",e+" / "+$("#node-input-orphan-target-container-div").treeList("data").length))}})}$("#node-input-orphan-target-container-div").treeList("data",e)}
|
|
16
16
|
|
|
17
|
-
function setupTreeMsgTracerlist(){try{$("#node-input-msgtracer-trace-treelist").treeList("empty"),$("#node-input-msgtracer-trace-treelist").treeList("data",[])}catch(e){$("#node-input-msgtracer-trace-treelist").css({width:"100%",height:"calc(100%)"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){t.node&&RED.workspaces.active()==t.node.z&&(RED.view.reveal(t.node.id,!0),RED.view.redraw())}).on("treelistitemmouseout",function(e,t){}).on("treelistselect",function(e,t){t.node&&(RED.workspaces.show(t.node.z,!1,!1,!0),RED.view.reveal(t.node.id,!0),RED.view.redraw(),$(".red-ui-debug-msg").css("background-color",""),$(".red-ui-debug-msg-node-"+t.node.id).css("background-color","red"))}).on("treelistconfirm",function(e,t){var
|
|
17
|
+
function setupTreeMsgTracerlist(){try{$("#node-input-msgtracer-trace-treelist").treeList("empty"),$("#node-input-msgtracer-trace-treelist").treeList("data",[])}catch(e){$("#node-input-msgtracer-trace-treelist").css({width:"100%",height:"calc(100%)"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){t.node&&RED.workspaces.active()==t.node.z&&(RED.view.reveal(t.node.id,!0),RED.view.redraw())}).on("treelistitemmouseout",function(e,t){}).on("treelistselect",function(e,t){t.node&&(RED.workspaces.show(t.node.z,!1,!1,!0),RED.view.reveal(t.node.id,!0),RED.view.redraw(),$(".red-ui-debug-msg").css("background-color",""),$(".red-ui-debug-msg-node-"+t.node.id).css("background-color","red"))}).on("treelistconfirm",function(e,t){var r;t.node&&(r=t.node.id,setTimeout(()=>{var e=RED.nodes.node(r);e&&(RED.view.reveal(e.id),RED.view.select(e.id),RED.editor.edit(e)),r==RED.workspaces.active()&&RED.workspaces.edit()},50))}),$("#node-input-msgtracer-trace-treelist-filter").show();var r=$("#node-input-msgtracer-trace-treelist-filter").searchBox({style:"compact",delay:300,change:function(){var e,t=$(this).val().trim().toLowerCase();""===t?($("#node-input-msgtracer-trace-treelist").treeList("filter",null),r.searchBox("count","")):(e=$("#node-input-msgtracer-trace-treelist").treeList("filter",function(e){return-1<e._label.toLowerCase().indexOf(t)||-1<e.node.type.toLowerCase().indexOf(t)||-1<e.node.id.toLowerCase().indexOf(t)||-1<e.sublabel.toLowerCase().indexOf(t)}),r.searchBox("count",e+" / "+$("#node-input-msgtracer-trace-treelist").treeList("data").length))}})}}function msgTracerDebugOff(){$.ajax({url:"MsgTracer/debug/off",type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify({}),success:function(e){},error:function(e,t,r){}})}function msgTracerTracingOff(){$.ajax({url:"MsgTracer/msgtracing/off",type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify({}),success:function(e){},error:function(e,t,r){}})}function uncheckAllTracing(){$("#node-input-msgtracer-toggle-all").prop("checked",!1),$("#node-input-msgtracer-toggle").prop("checked",!1),$("#node-input-msgtracer-msgtodebug").prop("checked",!1),$("#node-input-msgtracer-msgtodebug-all").prop("checked",!1)}function setupFailsafe(){clearInterval(window.msgTracerInterval),window.msgTracerCounter=0,window.msgTracerInterval=setInterval(()=>{window.msgTracerCounter>window.msgTracer_MAX_MSG_PER_SECOND&&(RED.notify("Message tracing flooding detected, deactivating tracing automatically.",{type:"error",timeout:3e3}),uncheckAllTracing(),msgTracerDebugOff(),msgTracerTracingOff(),clearInterval(window.msgTracerInterval)),window.msgTracerCounter=0},1e3)}function setupFailsafeForClampMode(){clearInterval(window.msgTracerInterval),window.msgTracerCounter=0,window.msgTracerInterval=setInterval(()=>{window.msgTracerCounter>window.msgTracer_MAX_MSG_PER_SECOND&&(RED.notify("Message tracing flooding detected, deactivating tracing automatically.",{type:"error",timeout:3e3}),RED.view.select([]),uncheckAllTracing(),$("#node-input-msgtracer-clamp-mode-with-debug").prop("checked",!1),$("#node-input-msgtracer-clamp-mode-with-debug").prop("disabled",!1),$("#node-input-msgtracer-clamp-mode-status").hide(),$("#node-input-msgtracer-clamp-mode").prop("checked",!1),msgTracerDebugOff(),msgTracerTracingOff(),clearInterval(window.msgTracerInterval)),window.msgTracerCounter=0},1e3)}function toggleClampWithDebug(e){$("#node-input-msgtracer-clamp-mode-with-debug").is(":checked")||(msgTracerDebugOff(),$("#node-input-msgtracer-clamp-mode").is(":checked")&&$("#node-input-msgtracer-clamp-mode-with-debug").prop("disabled",!0))}function toggleClampWireTappingInBackend(e){if($("#node-input-msgtracer-clamp-mode").is(":checked")){$("#node-input-msgtracer-clamp-mode-with-debug").is("checked")||$("#node-input-msgtracer-clamp-mode-with-debug").prop("disabled",!0);let o=e=>{var t,r,n=t=>{uncheckAllTracing(),$.ajax({url:"MsgTracer/msgtracing/off",type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify({}),error:function(e,t,r){},success:function(e){$.ajax({url:"MsgTracer/msgtracing/on",type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify({nodesSelected:t,allIsOn:!1}),error:function(e,t,r){},success:function(e){setupFailsafeForClampMode(),$("#node-input-msgtracer-clamp-mode-with-debug").is(":checked")?$.ajax({url:"MsgTracer/debug/on",type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify({nodesSelected:t,allIsOn:!1}),success:function(e){},error:function(e,t,r){}}):($("#node-input-msgtracer-clamp-mode-with-debug").prop("disabled",!0),msgTracerDebugOff())}})}})};$("#node-input-msgtracer-clamp-mode").is(":checked")?(t=RED.view.selection()).links&&1==t.links.length?(r=[(r=RED.view.selection().links[0]).source.id,r.target.id],setupTreeMsgTracerlist(),n(r),$("#node-input-msgtracer-clamp-mode-status").show()):t.nodes&&0<t.nodes.length?(setupTreeMsgTracerlist(),n(t.nodes.map(e=>e.id)),$("#node-input-msgtracer-clamp-mode-status").show()):($("#node-input-msgtracer-clamp-mode-status").hide(),msgTracerTracingOff(),msgTracerDebugOff()):($("#node-input-msgtracer-clamp-mode-with-debug").prop("disabled",!1),clearInterval(window.msgTracerInterval),RED.events.off("view:selection-changed",o),$("#node-input-msgtracer-clamp-mode-status").hide())};RED.events.on("view:selection-changed",o)}else $("#node-input-msgtracer-clamp-mode-status").hide(),$("#node-input-msgtracer-clamp-mode-with-debug").prop("disabled",!1),msgTracerTracingOff(),msgTracerDebugOff()}function toggleDebugTracingInBackend(e){let t=$("#node-input-msgtracer-msgtodebug").is(":checked");var r=$("#node-input-msgtracer-msgtodebug-all").is(":checked"),n=RED.view.selection().nodes;clearInterval(window.msgTracerInterval),n||r||!t?n&&0<n.length&&r&&t?($("#node-input-msgtracer-msgtodebug").prop("checked",!1),$("#node-input-msgtracer-msgtodebug-all").prop("checked",!1),msgTracerDebugOff(),RED.notify("Debug Messages not toggled, both nodes selected and all nodes is on.",{type:"error",timeout:3e3})):(t?($("#node-input-msgtracer-clamp-mode-with-debug").prop("checked",!1),$("#node-input-msgtracer-clamp-mode").prop("checked",!1),toggleClampWireTappingInBackend()):$("#node-input-msgtracer-msgtodebug-all").prop("checked",!1),$.ajax({url:"MsgTracer/debug/"+(t?"on":"off"),type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify({nodesSelected:selectionToNodeIdList(RED.view.selection().nodes),allIsOn:r}),success:function(e){RED.notify("Message debugging is <b>"+(t?"on":"off")+"</b>",{type:"success",timeout:3e3}),t&&setupFailsafe()},error:function(e,t,r){RED.notify("Message tracing failed: "+t,{type:"error",timeout:3e3})}})):($("#node-input-msgtracer-msgtodebug").prop("checked",!1),msgTracerDebugOff(),RED.notify("Debug Messages not toggled, first select nodes to be debugged <b>or</b> toggle 'all nodes' to on.",{type:"error",timeout:3e3}))}function toggleMsgTracingInBackend(e){let t=$("#node-input-msgtracer-toggle").is(":checked");var r=$("#node-input-msgtracer-toggle-all").is(":checked"),n=RED.view.selection().nodes;clearInterval(window.msgTracerInterval),n||r||!t?n&&0<n.length&&r&&t?($("#node-input-msgtracer-toggle-all").prop("checked",!1),$("#node-input-msgtracer-toggle").prop("checked",!1),msgTracerTracingOff(),RED.notify("Message tracing not toggled, nodes are selected <b>and</b> all nodes is checked.",{type:"error",timeout:3e3})):(t?($("#node-input-msgtracer-clamp-mode-with-debug").prop("checked",!1),$("#node-input-msgtracer-clamp-mode").prop("checked",!1),toggleClampWireTappingInBackend()):$("#node-input-msgtracer-toggle-all").prop("checked",!1),t&&setupTreeMsgTracerlist(),$.ajax({url:"MsgTracer/msgtracing/"+(t?"on":"off"),type:"POST",contentType:"application/json; charset=utf-8",data:JSON.stringify({nodesSelected:selectionToNodeIdList(RED.view.selection().nodes),allIsOn:r}),success:function(e){RED.notify("Message tracing is <b>"+(t?"on":"off")+"</b>",{type:"success",timeout:3e3}),t&&setupFailsafe()},error:function(e,t,r){RED.notify("Message tracing failed: "+t,{type:"error",timeout:3e3})}})):($("#node-input-msgtracer-toggle").prop("checked",!1),msgTracerTracingOff(),RED.notify("Message tracing not toggled, first select nodes to be traced <b>or</b> toggle 'all nodes' to on.",{type:"error",timeout:3e3}))}function selectionToNodeIdList(e){return!e||Array.isArray(e)&&0==e.length?[]:e.map(e=>"group"==e.type?selectionToNodeIdList(RED.nodes.group(e.id).nodes):e.id).flat(1/0)}window.msgTracer_MAX_MSG_PER_SECOND=100;
|
|
18
18
|
|
|
19
19
|
function transformInjectToChangeNode(t){t&&t.preventDefault();t=[...(RED.view.selection().nodes||[]).filter(e=>"inject"==e.type),...(RED.view.selection().nodes||[]).filter(e=>"group"==e.type).map(e=>e.nodes.filter(e=>"inject"==e.type))].flat();if(0==(t=RED.nodes.createExportableNodeSet(t)).length)RED.notify("No inject nodes found in node selection",{type:"warning"});else{let e=t.map(o=>{var e={id:RED.nodes.id(),type:"change",name:o.name,action:"",property:"",from:"",to:"",reg:!1,x:0,y:0,wires:[[]]};return e.rules=o.props.map(e=>{var t={t:"set",p:e.p,pt:"msg",to:e.v,tot:e.vt};return"payload"==e.p?(t.to=o.payload,t.tot=o.payloadType):"topic"==e.p&&(t.to=o.topic),t}),e});setTimeout(()=>{RED.clipboard.import(),setTimeout(()=>{$("#red-ui-clipboard-dialog-import-text").val(JSON.stringify(e)).trigger("paste")},300)},400)}}
|
|
20
20
|
|
|
21
21
|
// Add your plugin as a new tabsheet in the right sidebar AFTER the flow editor is completely started
|
|
22
|
-
var
|
|
23
|
-
RED.events.off('runtime-state', initialiseConfigNodeOnce);
|
|
24
|
-
|
|
22
|
+
var setupSidebarForIntrospection = () => {
|
|
25
23
|
// The html content of the sidebar has been specified below as a data-template, from where it can be loaded:
|
|
26
24
|
var content = $($('script[type="text/x-red"][data-template-name="Screenshot"]').i18n().html());
|
|
27
25
|
|
|
@@ -291,14 +289,6 @@
|
|
|
291
289
|
msgTracerDebugOff()
|
|
292
290
|
})
|
|
293
291
|
|
|
294
|
-
// NR 5.x has a debug pausing feature that others don't. If so, then the convenience
|
|
295
|
-
// button in the tracer is activated else hidden.
|
|
296
|
-
if (isNodeRedFive()) {
|
|
297
|
-
$('#node-input-msgtrace-pause-debug-btn').on('click', RED.debug.toggleDebugMessages)
|
|
298
|
-
} else {
|
|
299
|
-
$('#node-input-msgtrace-pause-debug-btn').hide()
|
|
300
|
-
}
|
|
301
|
-
|
|
302
292
|
RED.actions.add( "introspection:show-link-call-links", highlightAllLinkCalls)
|
|
303
293
|
|
|
304
294
|
// event is triggered by the unittesting node package, has no affect otherwise.
|
|
@@ -336,8 +326,22 @@
|
|
|
336
326
|
$('#node-input-orphan-target-container-div').parent().css('height', `calc(${topSze}px - 15vh)`)
|
|
337
327
|
}
|
|
338
328
|
})
|
|
329
|
+
|
|
330
|
+
// RED.debug isn't yet available
|
|
331
|
+
let fillInSiderBarForm = () => {
|
|
332
|
+
RED.events.off('flows:loaded', fillInSiderBarForm);
|
|
333
|
+
// NR 5.x has a debug pausing feature that others don't. If so, then the convenience
|
|
334
|
+
// button in the tracer is activated else hidden.
|
|
335
|
+
if (isNodeRedFive()) {
|
|
336
|
+
$('#node-input-msgtrace-pause-debug-btn').on('click', RED.debug.toggleDebugMessages)
|
|
337
|
+
} else {
|
|
338
|
+
$('#node-input-msgtrace-pause-debug-btn').hide()
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
RED.events.on('flows:loaded', fillInSiderBarForm);
|
|
339
342
|
};
|
|
340
|
-
|
|
343
|
+
|
|
344
|
+
RED.plugins.registerPlugin("intro-spection-sidebar-plugin", { onadd: setupSidebarForIntrospection });
|
|
341
345
|
})();
|
|
342
346
|
</script>
|
|
343
347
|
|