[CCSDK-28] populated the seed code for dgbuilder
[ccsdk/distribution.git] / dgbuilder / public / red / ui / view.js
1 /**
2  * Copyright 2013, 2014 IBM Corp.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  **/
16
17
18 RED.view = (function() {
19         /* increasing the width and height from 5000 to 7500*/
20     var space_width = 7500,
21         space_height = 7500,
22         lineCurveScale = 0.75,
23         scaleFactor = 1,
24         node_width = 100,
25         node_height = 30;
26     
27     var touchLongPressTimeout = 1000,
28         startTouchDistance = 0,
29         startTouchCenter = [],
30         moveTouchCenter = [],
31         touchStartTime = 0;
32
33
34     var activeWorkspace = 0;
35     var workspaceScrollPositions = {};
36
37     var selected_link = null,
38         mousedown_link = null,
39         mousedown_node = null,
40         mousedown_port_type = null,
41         mousedown_port_index = 0,
42         mouseup_node = null,
43         mouse_offset = [0,0],
44         mouse_position = null,
45         mouse_mode = 0,
46         moving_set = [],
47         dirty = false,
48         lasso = null,
49         showStatus = false,
50         showNumbers = false,
51         showNodePalette = true,
52         lastClickNode = null,
53         dblClickPrimed = null,
54         clickTime = 0,
55         clickElapsed = 0;
56
57     var clipboard = "";
58
59     var status_colours = {
60         "red":    "#c00",
61         "green":  "#5a8",
62         "yellow": "#F9DF31",
63         "blue":   "#53A3F3",
64         "grey":   "#d3d3d3"
65     }
66
67     var outer = d3.select("#chart")
68         .append("svg:svg")
69         .attr("width", space_width)
70         .attr("height", space_height)
71         .attr("pointer-events", "all")
72         .style("cursor","crosshair");
73
74      var vis = outer
75         .append('svg:g')
76         .on("dblclick.zoom", null)
77         .append('svg:g')
78         .on("mousemove", canvasMouseMove)
79         .on("mousedown", canvasMouseDown)
80         .on("mouseup", canvasMouseUp)
81         .on("touchend", function() {
82             clearTimeout(touchStartTime);
83             touchStartTime = null;
84             if  (RED.touch.radialMenu.active()) {
85                 return;
86             }
87             if (lasso) {
88                 outer_background.attr("fill","#fff");
89             }
90             canvasMouseUp.call(this);
91         })
92         .on("touchcancel", canvasMouseUp)
93         .on("touchstart", function() {
94             var touch0;
95             if (d3.event.touches.length>1) {
96                 clearTimeout(touchStartTime);
97                 touchStartTime = null;
98                 d3.event.preventDefault();
99                 touch0 = d3.event.touches.item(0);
100                 var touch1 = d3.event.touches.item(1);
101                 var a = touch0['pageY']-touch1['pageY'];
102                 var b = touch0['pageX']-touch1['pageX'];
103
104                 var offset = $("#chart").offset();
105                 var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()];
106                 startTouchCenter = [
107                     (touch1['pageX']+(b/2)-offset.left+scrollPos[0])/scaleFactor,
108                     (touch1['pageY']+(a/2)-offset.top+scrollPos[1])/scaleFactor
109                 ];
110                 moveTouchCenter = [
111                     touch1['pageX']+(b/2),
112                     touch1['pageY']+(a/2)
113                 ]
114                 startTouchDistance = Math.sqrt((a*a)+(b*b));
115             } else {
116                 var obj = d3.select(document.body);
117                 touch0 = d3.event.touches.item(0);
118                 var pos = [touch0.pageX,touch0.pageY];
119                 startTouchCenter = [touch0.pageX,touch0.pageY];
120                 startTouchDistance = 0;
121                 var point = d3.touches(this)[0];
122                 touchStartTime = setTimeout(function() {
123                     touchStartTime = null;
124                     showTouchMenu(obj,pos);
125                     //lasso = vis.append('rect')
126                     //    .attr("ox",point[0])
127                     //    .attr("oy",point[1])
128                     //    .attr("rx",2)
129                     //    .attr("ry",2)
130                     //    .attr("x",point[0])
131                     //    .attr("y",point[1])
132                     //    .attr("width",0)
133                     //    .attr("height",0)
134                     //    .attr("class","lasso");
135                     //outer_background.attr("fill","#e3e3f3");
136                 },touchLongPressTimeout);
137             }
138         })
139         .on("touchmove", function(){
140                 if  (RED.touch.radialMenu.active()) {
141                     d3.event.preventDefault();
142                     return;
143                 }
144                 var touch0;
145                 if (d3.event.touches.length<2) {
146                     if (touchStartTime) {
147                         touch0 = d3.event.touches.item(0);
148                         var dx = (touch0.pageX-startTouchCenter[0]);
149                         var dy = (touch0.pageY-startTouchCenter[1]);
150                         var d = Math.abs(dx*dx+dy*dy);
151                         if (d > 64) {
152                             clearTimeout(touchStartTime);
153                             touchStartTime = null;
154                         }
155                     } else if (lasso) {
156                         d3.event.preventDefault();
157                     }
158                     canvasMouseMove.call(this);
159                 } else {
160                     touch0 = d3.event.touches.item(0);
161                     var touch1 = d3.event.touches.item(1);
162                     var a = touch0['pageY']-touch1['pageY'];
163                     var b = touch0['pageX']-touch1['pageX'];
164                     var offset = $("#chart").offset();
165                     var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()];
166                     var moveTouchDistance = Math.sqrt((a*a)+(b*b));
167                     var touchCenter = [
168                         touch1['pageX']+(b/2),
169                         touch1['pageY']+(a/2)
170                     ];
171
172                     if (!isNaN(moveTouchDistance)) {
173                         oldScaleFactor = scaleFactor;
174                         scaleFactor = Math.min(2,Math.max(0.3, scaleFactor + (Math.floor(((moveTouchDistance*100)-(startTouchDistance*100)))/10000)));
175
176                         var deltaTouchCenter = [                             // Try to pan whilst zooming - not 100%
177                             startTouchCenter[0]*(scaleFactor-oldScaleFactor),//-(touchCenter[0]-moveTouchCenter[0]),
178                             startTouchCenter[1]*(scaleFactor-oldScaleFactor) //-(touchCenter[1]-moveTouchCenter[1])
179                         ];
180
181                         startTouchDistance = moveTouchDistance;
182                         moveTouchCenter = touchCenter;
183
184                         $("#chart").scrollLeft(scrollPos[0]+deltaTouchCenter[0]);
185                         $("#chart").scrollTop(scrollPos[1]+deltaTouchCenter[1]);
186                         redraw();
187                     }
188                 }
189         });
190
191     var outer_background = vis.append('svg:rect')
192         .attr('width', space_width)
193         .attr('height', space_height)
194         .attr('fill','#fff');
195
196     //var gridScale = d3.scale.linear().range([0,2000]).domain([0,2000]);
197     //var grid = vis.append('g');
198     //
199     //grid.selectAll("line.horizontal").data(gridScale.ticks(100)).enter()
200     //    .append("line")
201     //        .attr(
202     //        {
203     //            "class":"horizontal",
204     //            "x1" : 0,
205     //            "x2" : 2000,
206     //            "y1" : function(d){ return gridScale(d);},
207     //            "y2" : function(d){ return gridScale(d);},
208     //            "fill" : "none",
209     //            "shape-rendering" : "crispEdges",
210     //            "stroke" : "#eee",
211     //            "stroke-width" : "1px"
212     //        });
213     //grid.selectAll("line.vertical").data(gridScale.ticks(100)).enter()
214     //    .append("line")
215     //        .attr(
216     //        {
217     //            "class":"vertical",
218     //            "y1" : 0,
219     //            "y2" : 2000,
220     //            "x1" : function(d){ return gridScale(d);},
221     //            "x2" : function(d){ return gridScale(d);},
222     //            "fill" : "none",
223     //            "shape-rendering" : "crispEdges",
224     //            "stroke" : "#eee",
225     //            "stroke-width" : "1px"
226     //        });
227
228
229     var drag_line = vis.append("svg:path").attr("class", "drag_line");
230
231     var workspace_tabs = RED.tabs.create({
232         id: "workspace-tabs",
233         onchange: function(tab) {
234             if (tab.type == "subflow") {
235                 $("#workspace-toolbar").show();
236             } else {
237                 $("#workspace-toolbar").hide();
238             }
239             var chart = $("#chart");
240             if (activeWorkspace !== 0) {
241                 workspaceScrollPositions[activeWorkspace] = {
242                     left:chart.scrollLeft(),
243                     top:chart.scrollTop()
244                 };
245             }
246             var scrollStartLeft = chart.scrollLeft();
247             var scrollStartTop = chart.scrollTop();
248
249             activeWorkspace = tab.id;
250             if (workspaceScrollPositions[activeWorkspace]) {
251                 chart.scrollLeft(workspaceScrollPositions[activeWorkspace].left);
252                 chart.scrollTop(workspaceScrollPositions[activeWorkspace].top);
253             } else {
254                 chart.scrollLeft(0);
255                 chart.scrollTop(0);
256             }
257             var scrollDeltaLeft = chart.scrollLeft() - scrollStartLeft;
258             var scrollDeltaTop = chart.scrollTop() - scrollStartTop;
259             if (mouse_position != null) {
260                 mouse_position[0] += scrollDeltaLeft;
261                 mouse_position[1] += scrollDeltaTop;
262             }
263
264             clearSelection();
265             RED.nodes.eachNode(function(n) {
266                     n.dirty = true;
267             });
268             redraw();
269         },
270         ondblclick: function(tab) {
271             showRenameWorkspaceDialog(tab.id);
272         },
273         onadd: function(tab) {
274             RED.menu.addItem("btn-workspace-menu",{
275                 id:"btn-workspace-menu-"+tab.id.replace(".","-"),
276                 label:tab.label,
277                 onselect:function() {
278                     workspace_tabs.activateTab(tab.id);
279                 }
280             });
281             RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
282         },
283         onremove: function(tab) {
284             RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
285             RED.menu.removeItem("btn-workspace-menu-"+tab.id.replace(".","-"));
286         }
287     });
288
289     var workspaceIndex = 0;
290
291     function addWorkspace() {
292         var tabId = RED.nodes.id();
293         do {
294             workspaceIndex += 1;
295         } while($("#workspace-tabs a[title='Sheet "+workspaceIndex+"']").size() !== 0);
296
297         var ws = {type:"tab",id:tabId,label:"Sheet "+workspaceIndex};
298         RED.nodes.addWorkspace(ws);
299         workspace_tabs.addTab(ws);
300         workspace_tabs.activateTab(tabId);
301         RED.history.push({t:'add',workspaces:[ws],dirty:dirty});
302         RED.view.dirty(true);
303     }
304     $(function() {
305         $('#btn-workspace-add-tab').on("click",addWorkspace);
306         $('#btn-workspace-add').on("click",addWorkspace);
307         $('#btn-workspace-edit').on("click",function() {
308             showRenameWorkspaceDialog(activeWorkspace);
309         });
310         $('#btn-workspace-delete').on("click",function() {
311             deleteWorkspace(activeWorkspace);
312         });
313     });
314
315     function deleteWorkspace(id) {
316         if (workspace_tabs.count() == 1) {
317             return;
318         }
319         var ws = RED.nodes.workspace(id);
320         $( "#node-dialog-delete-workspace" ).dialog('option','workspace',ws);
321         $( "#node-dialog-delete-workspace-name" ).text(ws.label);
322         $( "#node-dialog-delete-workspace" ).dialog('open');
323     }
324
325     function canvasMouseDown() {
326         if (!mousedown_node && !mousedown_link) {
327             selected_link = null;
328             updateSelection();
329         }
330         if (mouse_mode === 0) {
331             if (lasso) {
332                 lasso.remove();
333                 lasso = null;
334             }
335             
336             if (!touchStartTime) {
337                 var point = d3.mouse(this);
338                 lasso = vis.append('rect')
339                     .attr("ox",point[0])
340                     .attr("oy",point[1])
341                     .attr("rx",2)
342                     .attr("ry",2)
343                     .attr("x",point[0])
344                     .attr("y",point[1])
345                     .attr("width",0)
346                     .attr("height",0)
347                     .attr("class","lasso");
348                 d3.event.preventDefault();
349             }
350         }
351     }
352
353     function canvasMouseMove() {
354         mouse_position = d3.touches(this)[0]||d3.mouse(this);
355
356         // Prevent touch scrolling...
357         //if (d3.touches(this)[0]) {
358         //    d3.event.preventDefault();
359         //}
360
361         // TODO: auto scroll the container
362         //var point = d3.mouse(this);
363         //if (point[0]-container.scrollLeft < 30 && container.scrollLeft > 0) { container.scrollLeft -= 15; }
364         //console.log(d3.mouse(this),container.offsetWidth,container.offsetHeight,container.scrollLeft,container.scrollTop);
365
366         if (lasso) {
367             var ox = parseInt(lasso.attr("ox"));
368             var oy = parseInt(lasso.attr("oy"));
369             var x = parseInt(lasso.attr("x"));
370             var y = parseInt(lasso.attr("y"));
371             var w;
372             var h;
373             if (mouse_position[0] < ox) {
374                 x = mouse_position[0];
375                 w = ox-x;
376             } else {
377                 w = mouse_position[0]-x;
378             }
379             if (mouse_position[1] < oy) {
380                 y = mouse_position[1];
381                 h = oy-y;
382             } else {
383                 h = mouse_position[1]-y;
384             }
385             lasso
386                 .attr("x",x)
387                 .attr("y",y)
388                 .attr("width",w)
389                 .attr("height",h)
390             ;
391             return;
392         }
393
394         if (mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && selected_link == null) {
395             return;
396         }
397
398         var mousePos;
399         if (mouse_mode == RED.state.JOINING) {
400             // update drag line
401             drag_line.attr("class", "drag_line");
402             mousePos = mouse_position;
403             var numOutputs = (mousedown_port_type === 0)?(mousedown_node.outputs || 1):1;
404             var sourcePort = mousedown_port_index;
405             var portY = -((numOutputs-1)/2)*13 +13*sourcePort;
406
407             var sc = (mousedown_port_type === 0)?1:-1;
408
409             var dy = mousePos[1]-(mousedown_node.y+portY);
410             var dx = mousePos[0]-(mousedown_node.x+sc*mousedown_node.w/2);
411             var delta = Math.sqrt(dy*dy+dx*dx);
412             var scale = lineCurveScale;
413             var scaleY = 0;
414
415             if (delta < node_width) {
416                 scale = 0.75-0.75*((node_width-delta)/node_width);
417             }
418             if (dx*sc < 0) {
419                 scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width));
420                 if (Math.abs(dy) < 3*node_height) {
421                     scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ;
422                 }
423             }
424
425             drag_line.attr("d",
426                 "M "+(mousedown_node.x+sc*mousedown_node.w/2)+" "+(mousedown_node.y+portY)+
427                 " C "+(mousedown_node.x+sc*(mousedown_node.w/2+node_width*scale))+" "+(mousedown_node.y+portY+scaleY*node_height)+" "+
428                 (mousePos[0]-sc*(scale)*node_width)+" "+(mousePos[1]-scaleY*node_height)+" "+
429                 mousePos[0]+" "+mousePos[1]
430                 );
431             d3.event.preventDefault();
432         } else if (mouse_mode == RED.state.MOVING) {
433                 //console.log("node mouse moving");
434             mousePos = mouse_position;
435             var d = (mouse_offset[0]-mousePos[0])*(mouse_offset[0]-mousePos[0]) + (mouse_offset[1]-mousePos[1])*(mouse_offset[1]-mousePos[1]);
436             if (d > 2) {
437                 mouse_mode = RED.state.MOVING_ACTIVE;
438                 clickElapsed = 0;
439             }
440         } else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING) {
441                 //console.log("node mouse moving active or IMPORT_DRAGGING");
442             mousePos = mouse_position;
443             var node;
444             var i;
445             var minX = 0;
446             var minY = 0;
447             for (var n = 0; n<moving_set.length; n++) {
448                 node = moving_set[n];
449                 if (d3.event.shiftKey) {
450                     node.n.ox = node.n.x;
451                     node.n.oy = node.n.y;
452                 }
453                 node.n.x = mousePos[0]+node.dx;
454                 node.n.y = mousePos[1]+node.dy;
455                 node.n.dirty = true;
456                 minX = Math.min(node.n.x-node.n.w/2-5,minX);
457                 minY = Math.min(node.n.y-node.n.h/2-5,minY);
458             }
459             if (minX !== 0 || minY !== 0) {
460                 for (i = 0; i<moving_set.length; i++) {
461                     node = moving_set[i];
462                     node.n.x -= minX;
463                     node.n.y -= minY;
464                 }
465             }
466             if (d3.event.shiftKey && moving_set.length > 0) {
467                 var gridOffset =  [0,0];
468                 node = moving_set[0];
469                 gridOffset[0] = node.n.x-(20*Math.floor((node.n.x-node.n.w/2)/20)+node.n.w/2);
470                 gridOffset[1] = node.n.y-(20*Math.floor(node.n.y/20));
471                 if (gridOffset[0] !== 0 || gridOffset[1] !== 0) {
472                     for (i = 0; i<moving_set.length; i++) {
473                         node = moving_set[i];
474                         node.n.x -= gridOffset[0];
475                         node.n.y -= gridOffset[1];
476                         if (node.n.x == node.n.ox && node.n.y == node.n.oy) {
477                             node.dirty = false;
478                         }
479                     }
480                 }
481             }
482         }
483         redraw();
484     }
485
486     function canvasMouseUp() {
487         if (mousedown_node && mouse_mode == RED.state.JOINING) {
488             drag_line.attr("class", "drag_line_hidden");
489         }
490         if (lasso) {
491             var x = parseInt(lasso.attr("x"));
492             var y = parseInt(lasso.attr("y"));
493             var x2 = x+parseInt(lasso.attr("width"));
494             var y2 = y+parseInt(lasso.attr("height"));
495             if (!d3.event.ctrlKey) {
496                 clearSelection();
497             }
498             RED.nodes.eachNode(function(n) {
499                 if (n.z == activeWorkspace && !n.selected) {
500                     n.selected = (n.x > x && n.x < x2 && n.y > y && n.y < y2);
501                     if (n.selected) {
502                         n.dirty = true;
503                         moving_set.push({n:n});
504                     }
505                 }
506             });
507             updateSelection();
508             lasso.remove();
509             lasso = null;
510         } else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey ) {
511             clearSelection();
512             updateSelection();
513         }
514         if (mouse_mode == RED.state.MOVING_ACTIVE) {
515                 //console.log("node moved active.");
516                 //CSS setting view dirty if the node was moved 
517                 //RED.view.dirty(true);
518             if (moving_set.length > 0) {
519                 var ns = [];
520                 for (var j=0;j<moving_set.length;j++) {
521                     ns.push({n:moving_set[j].n,ox:moving_set[j].ox,oy:moving_set[j].oy});
522                 }
523                 RED.history.push({t:'move',nodes:ns,dirty:dirty});
524             }
525         }
526         if (mouse_mode == RED.state.MOVING || mouse_mode == RED.state.MOVING_ACTIVE) {
527                 //console.log("node moving or MOVING_ACTIVE.");
528             for (var i=0;i<moving_set.length;i++) {
529                 delete moving_set[i].ox;
530                 delete moving_set[i].oy;
531             }
532         }
533         if (mouse_mode == RED.state.IMPORT_DRAGGING) {
534             RED.keyboard.remove(/* ESCAPE */ 27);
535             setDirty(true);
536         }
537         redraw();
538         // clear mouse event vars
539         resetMouseVars();
540     }
541
542     $('#btn-zoom-out').click(function() {zoomOut();});
543     $('#btn-zoom-zero').click(function() {zoomZero();});
544     $('#btn-zoom-in').click(function() {zoomIn();});
545     $("#chart").on('DOMMouseScroll mousewheel', function (evt) {
546         if ( evt.altKey ) {
547             evt.preventDefault();
548             evt.stopPropagation();
549             var move = -(evt.originalEvent.detail) || evt.originalEvent.wheelDelta;
550             if (move <= 0) { zoomOut(); }
551             else { zoomIn(); }
552         }
553     });
554     $("#chart").droppable({
555             accept:".palette_node",
556             drop: function( event, ui ) {
557                 d3.event = event;
558                 var selected_tool = ui.draggable[0].type;
559                 var mousePos = d3.touches(this)[0]||d3.mouse(this);
560                 mousePos[1] += this.scrollTop;
561                 mousePos[0] += this.scrollLeft;
562                 mousePos[1] /= scaleFactor;
563                 mousePos[0] /= scaleFactor;
564
565                 var nn = { id:(1+Math.random()*4294967295).toString(16),x: mousePos[0],y:mousePos[1],w:node_width,z:activeWorkspace};
566
567                 nn.type = selected_tool;
568                 nn._def = RED.nodes.getType(nn.type);
569                 nn.outputs = nn._def.outputs;
570                 nn.changed = true;
571
572                 for (var d in nn._def.defaults) {
573                     if (nn._def.defaults.hasOwnProperty(d)) {
574                         nn[d] = nn._def.defaults[d].value;
575                     }
576                 }
577
578                 if (nn._def.onadd) {
579                     nn._def.onadd.call(nn);
580                 }
581
582                 nn.h = Math.max(node_height,(nn.outputs||0) * 15);
583                 RED.history.push({t:'add',nodes:[nn.id],dirty:dirty});
584                 RED.nodes.add(nn);
585                 RED.editor.validateNode(nn);
586                 setDirty(true);
587                 // auto select dropped node - so info shows (if visible)
588                 clearSelection();
589                 nn.selected = true;
590                 moving_set.push({n:nn});
591                 updateSelection();
592                 redraw();
593
594                 if (nn._def.autoedit) {
595                     RED.editor.edit(nn);
596                 }
597             }
598     });
599
600     function zoomIn() {
601         if (scaleFactor < 2) {
602             scaleFactor += 0.1;
603             redraw();
604         }
605     }
606     function zoomOut() {
607         if (scaleFactor > 0.3) {
608             scaleFactor -= 0.1;
609             redraw();
610         }
611     }
612     function zoomZero() {
613         scaleFactor = 1;
614         redraw();
615     }
616
617     function selectAll() {
618         RED.nodes.eachNode(function(n) {
619             if (n.z == activeWorkspace) {
620                 if (!n.selected) {
621                     n.selected = true;
622                     n.dirty = true;
623                     moving_set.push({n:n});
624                 }
625             }
626         });
627         selected_link = null;
628         updateSelection();
629         redraw();
630     }
631
632     function clearSelection() {
633         for (var i=0;i<moving_set.length;i++) {
634             var n = moving_set[i];
635             n.n.dirty = true;
636             n.n.selected = false;
637         }
638         moving_set = [];
639         selected_link = null;
640     }
641
642     function updateSelection() {
643         if (moving_set.length === 0) {
644             RED.menu.setDisabled("btn-export-menu",true);
645             RED.menu.setDisabled("btn-export-clipboard",true);
646             RED.menu.setDisabled("btn-export-library",true);
647         } else {
648             RED.menu.setDisabled("btn-export-menu",false);
649             RED.menu.setDisabled("btn-export-clipboard",false);
650             RED.menu.setDisabled("btn-export-library",false);
651         }
652         if (moving_set.length === 0 && selected_link == null) {
653             //RED.keyboard.remove(/* backspace */ 8);
654             RED.keyboard.remove(/* delete */ 46);
655             RED.keyboard.remove(/* c */ 67);
656             RED.keyboard.remove(/* x */ 88);
657         } else {
658             //RED.keyboard.add(/* backspace */ 8,function(){deleteSelection();d3.event.preventDefault();});
659             RED.keyboard.add(/* delete */ 46,function(){deleteSelection();d3.event.preventDefault();});
660             RED.keyboard.add(/* c */ 67,{ctrl:true},function(){copySelection();d3.event.preventDefault();});
661             RED.keyboard.add(/* x */ 88,{ctrl:true},function(){copySelection();deleteSelection();d3.event.preventDefault();});
662         }
663         if (moving_set.length === 0) {
664             RED.keyboard.remove(/* up   */ 38);
665             RED.keyboard.remove(/* down */ 40);
666             RED.keyboard.remove(/* left */ 37);
667             RED.keyboard.remove(/* right*/ 39);
668         } else {
669             RED.keyboard.add(/* up   */ 38, function() { if(d3.event.shiftKey){moveSelection(  0,-20)}else{moveSelection( 0,-1);}d3.event.preventDefault();},endKeyboardMove);
670             RED.keyboard.add(/* down */ 40, function() { if(d3.event.shiftKey){moveSelection(  0, 20)}else{moveSelection( 0, 1);}d3.event.preventDefault();},endKeyboardMove);
671             RED.keyboard.add(/* left */ 37, function() { if(d3.event.shiftKey){moveSelection(-20,  0)}else{moveSelection(-1, 0);}d3.event.preventDefault();},endKeyboardMove);
672             RED.keyboard.add(/* right*/ 39, function() { if(d3.event.shiftKey){moveSelection( 20,  0)}else{moveSelection( 1, 0);}d3.event.preventDefault();},endKeyboardMove);
673         }
674         if (moving_set.length == 1) {
675             RED.sidebar.info.refresh(moving_set[0].n);
676         } else {
677             RED.sidebar.info.clear();
678         }
679     }
680     function endKeyboardMove() {
681         //console.log("end keyboard move.");
682         //CSS setting view dirty if the node was moved 
683         //RED.view.dirty(true);
684         var ns = [];
685         for (var i=0;i<moving_set.length;i++) {
686             ns.push({n:moving_set[i].n,ox:moving_set[i].ox,oy:moving_set[i].oy});
687             delete moving_set[i].ox;
688             delete moving_set[i].oy;
689         }
690         RED.history.push({t:'move',nodes:ns,dirty:dirty});
691     }
692     function moveSelection(dx,dy) {
693         var minX = 0;
694         var minY = 0;
695         var node;
696         
697         for (var i=0;i<moving_set.length;i++) {
698             node = moving_set[i];
699             if (node.ox == null && node.oy == null) {
700                 node.ox = node.n.x;
701                 node.oy = node.n.y;
702             }
703             node.n.x += dx;
704             node.n.y += dy;
705             node.n.dirty = true;
706             minX = Math.min(node.n.x-node.n.w/2-5,minX);
707             minY = Math.min(node.n.y-node.n.h/2-5,minY);
708         }
709
710         if (minX !== 0 || minY !== 0) {
711             for (var n = 0; n<moving_set.length; n++) {
712                 node = moving_set[n];
713                 node.n.x -= minX;
714                 node.n.y -= minY;
715             }
716         }
717
718         redraw();
719     }
720     function deleteSelection() {
721         var removedNodes = [];
722         var removedLinks = [];
723         var startDirty = dirty;
724         if (moving_set.length > 0) {
725             for (var i=0;i<moving_set.length;i++) {
726                 var node = moving_set[i].n;
727                 node.selected = false;
728                 if (node.x < 0) {
729                     node.x = 25
730                 }
731                 var rmlinks = RED.nodes.remove(node.id);
732                 removedNodes.push(node);
733                 removedLinks = removedLinks.concat(rmlinks);
734             }
735             moving_set = [];
736             setDirty(true);
737         }
738         if (selected_link) {
739             RED.nodes.removeLink(selected_link);
740             removedLinks.push(selected_link);
741             setDirty(true);
742         }
743         RED.history.push({t:'delete',nodes:removedNodes,links:removedLinks,dirty:startDirty});
744
745         selected_link = null;
746         updateSelection();
747         redraw();
748     }
749
750     function copySelection() {
751         if (moving_set.length > 0) {
752             var nns = [];
753             for (var n=0;n<moving_set.length;n++) {
754                 var node = moving_set[n].n;
755                 nns.push(RED.nodes.convertNode(node));
756             }
757             clipboard = JSON.stringify(nns);
758             RED.notify(moving_set.length+" node"+(moving_set.length>1?"s":"")+" copied");
759         }
760     }
761
762
763     function calculateTextWidth(str) {
764         var sp = document.createElement("span");
765         sp.className = "node_label";
766         sp.style.position = "absolute";
767         sp.style.top = "-1000px";
768         sp.innerHTML = (str||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
769         document.body.appendChild(sp);
770         var w = sp.offsetWidth;
771         document.body.removeChild(sp);
772         return 50+w;
773     }
774
775     function resetMouseVars() {
776         mousedown_node = null;
777         mouseup_node = null;
778         mousedown_link = null;
779         mouse_mode = 0;
780         mousedown_port_type = 0;
781     }
782
783     function portMouseDown(d,portType,portIndex) {
784         // disable zoom
785         //vis.call(d3.behavior.zoom().on("zoom"), null);
786         mousedown_node = d;
787         selected_link = null;
788         mouse_mode = RED.state.JOINING;
789         mousedown_port_type = portType;
790         mousedown_port_index = portIndex || 0;
791         document.body.style.cursor = "crosshair";
792         d3.event.preventDefault();
793     }
794
795     function portMouseUp(d,portType,portIndex) {
796         document.body.style.cursor = "";
797         if (mouse_mode == RED.state.JOINING && mousedown_node) {
798             if (typeof TouchEvent != "undefined" && d3.event instanceof TouchEvent) {
799                 RED.nodes.eachNode(function(n) {
800                         if (n.z == activeWorkspace) {
801                             var hw = n.w/2;
802                             var hh = n.h/2;
803                             if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
804                                 n.y-hh<mouse_position[1] && n.y+hh>mouse_position[1]) {
805                                     mouseup_node = n;
806                                     portType = mouseup_node._def.inputs>0?1:0;
807                                     portIndex = 0;
808                             }
809                         }
810                 });
811             } else {
812                 mouseup_node = d;
813             }
814             if (portType == mousedown_port_type || mouseup_node === mousedown_node) {
815                 drag_line.attr("class", "drag_line_hidden");
816                 resetMouseVars();
817                 return;
818             }
819             var src,dst,src_port;
820             if (mousedown_port_type === 0) {
821                 src = mousedown_node;
822                 src_port = mousedown_port_index;
823                 dst = mouseup_node;
824             } else if (mousedown_port_type == 1) {
825                 src = mouseup_node;
826                 dst = mousedown_node;
827                 src_port = portIndex;
828             }
829
830             var existingLink = false;
831             RED.nodes.eachLink(function(d) {
832                     existingLink = existingLink || (d.source === src && d.target === dst && d.sourcePort == src_port);
833             });
834             if (!existingLink) {
835                 var link = {source: src, sourcePort:src_port, target: dst};
836                 RED.nodes.addLink(link);
837                 RED.history.push({t:'add',links:[link],dirty:dirty});
838                 setDirty(true);
839             }
840             selected_link = null;
841             redraw();
842         }
843     }
844
845     function nodeMouseUp(d) {
846         if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < 750) {
847             RED.editor.edit(d);
848             clickElapsed = 0;
849             d3.event.stopPropagation();
850             return;
851         }
852         portMouseUp(d, d._def.inputs > 0 ? 1 : 0, 0);
853     }
854
855     function nodeMouseDown(d) {
856         //var touch0 = d3.event;
857         //var pos = [touch0.pageX,touch0.pageY];
858         //RED.touch.radialMenu.show(d3.select(this),pos);
859         if (mouse_mode == RED.state.IMPORT_DRAGGING) {
860             RED.keyboard.remove(/* ESCAPE */ 27);
861             updateSelection();
862             setDirty(true);
863             redraw();
864             resetMouseVars();
865             d3.event.stopPropagation();
866             return;
867         }
868         mousedown_node = d;
869         var now = Date.now();
870         clickElapsed = now-clickTime;
871         clickTime = now;
872
873         dblClickPrimed = (lastClickNode == mousedown_node);
874         lastClickNode = mousedown_node;
875         
876         var i;
877         
878         if (d.selected && d3.event.ctrlKey) {
879             d.selected = false;
880             for (i=0;i<moving_set.length;i+=1) {
881                 if (moving_set[i].n === d) {
882                     moving_set.splice(i,1);
883                     break;
884                 }
885             }
886         } else {
887             if (d3.event.shiftKey) {
888                 clearSelection();
889                 var cnodes = RED.nodes.getAllFlowNodes(mousedown_node);
890                 for (var n=0;n<cnodes.length;n++) {
891                     cnodes[n].selected = true;
892                     cnodes[n].dirty = true;
893                     moving_set.push({n:cnodes[n]});
894                 }
895             } else if (!d.selected) {
896                 if (!d3.event.ctrlKey) {
897                     clearSelection();
898                 }
899                 mousedown_node.selected = true;
900                 moving_set.push({n:mousedown_node});
901             }
902             selected_link = null;
903             if (d3.event.button != 2) {
904                 mouse_mode = RED.state.MOVING;
905                 var mouse = d3.touches(this)[0]||d3.mouse(this);
906                 mouse[0] += d.x-d.w/2;
907                 mouse[1] += d.y-d.h/2;
908                 for (i=0;i<moving_set.length;i++) {
909                     moving_set[i].ox = moving_set[i].n.x;
910                     moving_set[i].oy = moving_set[i].n.y;
911                     moving_set[i].dx = moving_set[i].n.x-mouse[0];
912                     moving_set[i].dy = moving_set[i].n.y-mouse[1];
913                 }
914                 mouse_offset = d3.mouse(document.body);
915                 if (isNaN(mouse_offset[0])) {
916                     mouse_offset = d3.touches(document.body)[0];
917                 }
918             }
919         }
920         d.dirty = true;
921         updateSelection();
922         redraw();
923         d3.event.stopPropagation();
924     }
925
926     function nodeButtonClicked(d) {
927         if (d._def.button.toggle) {
928             d[d._def.button.toggle] = !d[d._def.button.toggle];
929             d.dirty = true;
930         }
931         if (d._def.button.onclick) {
932             d._def.button.onclick.call(d);
933         }
934         if (d.dirty) {
935             redraw();
936         }
937         d3.event.preventDefault();
938     }
939
940     function showTouchMenu(obj,pos) {
941         var mdn = mousedown_node;
942         var options = [];
943         options.push({name:"delete",disabled:(moving_set.length===0),onselect:function() {deleteSelection();}});
944         options.push({name:"cut",disabled:(moving_set.length===0),onselect:function() {copySelection();deleteSelection();}});
945         options.push({name:"copy",disabled:(moving_set.length===0),onselect:function() {copySelection();}});
946         options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard,true);}});
947         options.push({name:"edit",disabled:(moving_set.length != 1),onselect:function() { RED.editor.edit(mdn);}});
948         options.push({name:"select",onselect:function() {selectAll();}});
949         options.push({name:"undo",disabled:(RED.history.depth() === 0),onselect:function() {RED.history.pop();}});
950         
951         RED.touch.radialMenu.show(obj,pos,options);
952         resetMouseVars();
953     }
954     function redraw() {
955         vis.attr("transform","scale("+scaleFactor+")");
956         outer.attr("width", space_width*scaleFactor).attr("height", space_height*scaleFactor);
957
958         if (mouse_mode != RED.state.JOINING) {
959             // Don't bother redrawing nodes if we're drawing links
960
961             var node = vis.selectAll(".nodegroup").data(RED.nodes.nodes.filter(function(d) { return d.z == activeWorkspace }),function(d){return d.id});
962             node.exit().remove();
963
964             var nodeEnter = node.enter().insert("svg:g").attr("class", "node nodegroup");
965             nodeEnter.each(function(d,i) {
966                     var node = d3.select(this);
967                     node.attr("id",d.id);
968                     var l = d._def.label;
969                     l = (typeof l === "function" ? l.call(d) : l)||"";
970                     d.w = Math.max(node_width,calculateTextWidth(l)+(d._def.inputs>0?7:0) );
971                     d.h = Math.max(node_height,(d.outputs||0) * 15);
972
973                     if (d._def.badge) {
974                         var badge = node.append("svg:g").attr("class","node_badge_group");
975                         var badgeRect = badge.append("rect").attr("class","node_badge").attr("rx",5).attr("ry",5).attr("width",40).attr("height",15);
976                         badge.append("svg:text").attr("class","node_badge_label").attr("x",35).attr("y",11).attr('text-anchor','end').text(d._def.badge());
977                         if (d._def.onbadgeclick) {
978                             badgeRect.attr("cursor","pointer")
979                                 .on("click",function(d) { d._def.onbadgeclick.call(d);d3.event.preventDefault();});
980                         }
981                     }
982
983                     if (d._def.button) {
984                         var nodeButtonGroup = node.append('svg:g')
985                             .attr("transform",function(d) { return "translate("+((d._def.align == "right") ? 94 : -25)+",2)"; })
986                             .attr("class",function(d) { return "node_button "+((d._def.align == "right") ? "node_right_button" : "node_left_button"); });
987                         nodeButtonGroup.append('rect')
988                             .attr("rx",8)
989                             .attr("ry",8)
990                             .attr("width",32)
991                             .attr("height",node_height-4)
992                             .attr("fill","#eee");//function(d) { return d._def.color;})
993                         nodeButtonGroup.append('rect')
994                             .attr("x",function(d) { return d._def.align == "right"? 10:5})
995                             .attr("y",4)
996                             .attr("rx",5)
997                             .attr("ry",5)
998                             .attr("width",16)
999                             .attr("height",node_height-12)
1000                             .attr("fill",function(d) { return d._def.color;})
1001                             .attr("cursor","pointer")
1002                             .on("mousedown",function(d) {if (!lasso) { d3.select(this).attr("fill-opacity",0.2);d3.event.preventDefault(); d3.event.stopPropagation();}})
1003                             .on("mouseup",function(d) {if (!lasso) { d3.select(this).attr("fill-opacity",0.4);d3.event.preventDefault();d3.event.stopPropagation();}})
1004                             .on("mouseover",function(d) {if (!lasso) { d3.select(this).attr("fill-opacity",0.4);}})
1005                             .on("mouseout",function(d) {if (!lasso) {
1006                                 var op = 1;
1007                                 if (d._def.button.toggle) {
1008                                     op = d[d._def.button.toggle]?1:0.2;
1009                                 }
1010                                 d3.select(this).attr("fill-opacity",op);
1011                             }})
1012                             .on("click",nodeButtonClicked)
1013                             .on("touchstart",nodeButtonClicked)
1014                     }
1015
1016                     var mainRect = node.append("rect")
1017                         .attr("class", "node")
1018                         .classed("node_unknown",function(d) { return d.type == "unknown"; })
1019                         .attr("rx", 6)
1020                         .attr("ry", 6)
1021                         .attr("fill",function(d) { return d._def.color;})
1022                         .on("mouseup",nodeMouseUp)
1023                         .on("mousedown",nodeMouseDown)
1024                         .on("touchstart",function(d) {
1025                             var obj = d3.select(this);
1026                             var touch0 = d3.event.touches.item(0);
1027                             var pos = [touch0.pageX,touch0.pageY];
1028                             startTouchCenter = [touch0.pageX,touch0.pageY];
1029                             startTouchDistance = 0;
1030                             touchStartTime = setTimeout(function() {
1031                                 showTouchMenu(obj,pos);
1032                             },touchLongPressTimeout);
1033                             nodeMouseDown.call(this,d)       
1034                         })
1035                         .on("touchend", function(d) {
1036                             clearTimeout(touchStartTime);
1037                             touchStartTime = null;
1038                             if  (RED.touch.radialMenu.active()) {
1039                                 d3.event.stopPropagation();
1040                                 return;
1041                             }
1042                             nodeMouseUp.call(this,d);
1043                         })
1044                         .on("mouseover",function(d) {
1045                                 if (mouse_mode === 0) {
1046                                     var node = d3.select(this);
1047                                     node.classed("node_hovered",true);
1048                                 }
1049                         })
1050                         .on("mouseout",function(d) {
1051                                 var node = d3.select(this);
1052                                 node.classed("node_hovered",false);
1053                         });
1054
1055                    //node.append("rect").attr("class", "node-gradient-top").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-top)").style("pointer-events","none");
1056                    //node.append("rect").attr("class", "node-gradient-bottom").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-bottom)").style("pointer-events","none");
1057
1058                     if (d._def.icon) {
1059                         
1060                         var icon_group = node.append("g")
1061                             .attr("class","node_icon_group")
1062                             .attr("x",0).attr("y",0);
1063                         
1064                         var icon_shade = icon_group.append("rect")
1065                             .attr("x",0).attr("y",0)
1066                             .attr("class","node_icon_shade")
1067                             .attr("width","30")
1068                             .attr("stroke","none")
1069                             .attr("fill","#000")
1070                             .attr("fill-opacity","0.05")
1071                             .attr("height",function(d){return Math.min(50,d.h-4);});
1072                             
1073                         var icon = icon_group.append("image")
1074                             .attr("xlink:href","icons/"+d._def.icon)
1075                             .attr("class","node_icon")
1076                             .attr("x",0)
1077                             .attr("width","30")
1078                             .attr("height","30");
1079                             
1080                         var icon_shade_border = icon_group.append("path")
1081                             .attr("d",function(d) { return "M 30 1 l 0 "+(d.h-2)})
1082                             .attr("class","node_icon_shade_border")
1083                             .attr("stroke-opacity","0.1")
1084                             .attr("stroke","#000")
1085                             .attr("stroke-width","2");
1086
1087                         if ("right" == d._def.align) {
1088                             icon_group.attr('class','node_icon_group node_icon_group_'+d._def.align);
1089                             icon_shade_border.attr("d",function(d) { return "M 0 1 l 0 "+(d.h-2)})
1090                             //icon.attr('class','node_icon node_icon_'+d._def.align);
1091                             //icon.attr('class','node_icon_shade node_icon_shade_'+d._def.align);
1092                             //icon.attr('class','node_icon_shade_border node_icon_shade_border_'+d._def.align);
1093                         }
1094                         
1095                         //if (d._def.inputs > 0 && d._def.align == null) {
1096                         //    icon_shade.attr("width",35);
1097                         //    icon.attr("transform","translate(5,0)");
1098                         //    icon_shade_border.attr("transform","translate(5,0)");
1099                         //}
1100                         //if (d._def.outputs > 0 && "right" == d._def.align) {
1101                         //    icon_shade.attr("width",35); //icon.attr("x",5);
1102                         //}
1103                         
1104                         var img = new Image();
1105                         img.src = "icons/"+d._def.icon;
1106                         img.onload = function() {
1107                             icon.attr("width",Math.min(img.width,30));
1108                             icon.attr("height",Math.min(img.height,30));
1109                             icon.attr("x",15-Math.min(img.width,30)/2);
1110                             //if ("right" == d._def.align) {
1111                             //    icon.attr("x",function(d){return d.w-img.width-1-(d.outputs>0?5:0);});
1112                             //    icon_shade.attr("x",function(d){return d.w-30});
1113                             //    icon_shade_border.attr("d",function(d){return "M "+(d.w-30)+" 1 l 0 "+(d.h-2);});
1114                             //}
1115                         }
1116                         
1117                         //icon.style("pointer-events","none");
1118                         icon_group.style("pointer-events","none");
1119                     }
1120                     var text = node.append('svg:text').attr('class','node_label').attr('x', 38).attr('dy', '.35em').attr('text-anchor','start');
1121                     if (d._def.align) {
1122                         text.attr('class','node_label node_label_'+d._def.align);
1123                         text.attr('text-anchor','end');
1124                     }
1125
1126                     var status = node.append("svg:g").attr("class","node_status_group").style("display","none");
1127
1128                     var statusRect = status.append("rect").attr("class","node_status")
1129                                         .attr("x",6).attr("y",1).attr("width",9).attr("height",9)
1130                                         .attr("rx",2).attr("ry",2).attr("stroke-width","3");
1131
1132                     var statusLabel = status.append("svg:text")
1133                         .attr("class","node_status_label")
1134                         .attr('x',20).attr('y',9)
1135                         .style({
1136                                 'stroke-width': 0,
1137                                 'fill': '#888',
1138                                 'font-size':'9pt',
1139                                 'stroke':'#000',
1140                                 'text-anchor':'start'
1141                         });
1142
1143                     var dgNumber = node.append("svg:g").attr("class","node_dgnumber_group").style("display","none");
1144
1145                     /*var dgNumberRect = dgNumber.append("rect").attr("class","node_dgnumber")
1146                                         .attr("x",6).attr("y",-49).attr("width",9).attr("height",9)
1147                                         .attr("rx",2).attr("ry",2).attr("stroke-width","3");
1148                         */
1149
1150                     var dgNumberLabel = dgNumber.append("svg:text")
1151                         .attr("class","node_dgnumber_label")
1152                         .attr('x',1).attr('y',-43)
1153                         .style({
1154                                 'stroke-width': 0,
1155                                 /*'fill': '#2E4F83',*/
1156                                 'fill': 'blue',
1157                                 'font-size':'12pt',
1158                                 'stroke':'#000',
1159                                 'text-anchor':'start'
1160                         });
1161
1162                     var dgNumberTitle = dgNumber.append("title")
1163                                         .attr("class","node_dgnumber_title");
1164
1165                     //node.append("circle").attr({"class":"centerDot","cx":0,"cy":0,"r":5});
1166
1167                     if (d._def.inputs > 0) {
1168                         text.attr("x",38);
1169                         node.append("rect").attr("class","port port_input").attr("rx",3).attr("ry",3).attr("x",-5).attr("width",10).attr("height",10)
1170                             .on("mousedown",function(d){portMouseDown(d,1,0);})
1171                             .on("touchstart",function(d){portMouseDown(d,1,0);})
1172                             .on("mouseup",function(d){portMouseUp(d,1,0);} )
1173                             .on("touchend",function(d){portMouseUp(d,1,0);} )
1174                             .on("mouseover",function(d) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || mousedown_port_type != 1 ));})
1175                             .on("mouseout",function(d) { var port = d3.select(this); port.classed("port_hovered",false);})
1176                     }
1177
1178                     //node.append("path").attr("class","node_error").attr("d","M 3,-3 l 10,0 l -5,-8 z");
1179                     node.append("image").attr("class","node_error hidden").attr("xlink:href","icons/node-error.png").attr("x",0).attr("y",-6).attr("width",10).attr("height",9);
1180                     node.append("image").attr("class","node_changed hidden").attr("xlink:href","icons/node-changed.png").attr("x",12).attr("y",-6).attr("width",10).attr("height",10);
1181             });
1182
1183             node.each(function(d,i) {
1184                     if (d.dirty) {
1185                         //if (d.x < -50) deleteSelection();  // Delete nodes if dragged back to palette
1186                         if (d.resize) {
1187                             var l = d._def.label;
1188                             l = (typeof l === "function" ? l.call(d) : l)||"";
1189                             d.w = Math.max(node_width,calculateTextWidth(l)+(d._def.inputs>0?7:0) );
1190                             d.h = Math.max(node_height,(d.outputs||0) * 15);
1191                         }
1192                         var thisNode = d3.select(this);
1193                         //thisNode.selectAll(".centerDot").attr({"cx":function(d) { return d.w/2;},"cy":function(d){return d.h/2}});
1194                         thisNode.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
1195                         thisNode.selectAll(".node")
1196                             .attr("width",function(d){return d.w})
1197                             .attr("height",function(d){return d.h})
1198                             .classed("node_selected",function(d) { return d.selected; })
1199                             .classed("node_highlighted",function(d) { return d.highlighted; })
1200                         ;
1201                         //thisNode.selectAll(".node-gradient-top").attr("width",function(d){return d.w});
1202                         //thisNode.selectAll(".node-gradient-bottom").attr("width",function(d){return d.w}).attr("y",function(d){return d.h-30});
1203
1204                         thisNode.selectAll(".node_icon_group_right").attr('transform', function(d){return "translate("+(d.w-30)+",0)"});
1205                         thisNode.selectAll(".node_label_right").attr('x', function(d){return d.w-38});
1206                         //thisNode.selectAll(".node_icon_right").attr("x",function(d){return d.w-d3.select(this).attr("width")-1-(d.outputs>0?5:0);});
1207                         //thisNode.selectAll(".node_icon_shade_right").attr("x",function(d){return d.w-30;});
1208                         //thisNode.selectAll(".node_icon_shade_border_right").attr("d",function(d){return "M "+(d.w-30)+" 1 l 0 "+(d.h-2)});
1209
1210                         
1211                         var numOutputs = d.outputs;
1212                         var y = (d.h/2)-((numOutputs-1)/2)*13;
1213                         d.ports = d.ports || d3.range(numOutputs);
1214                         d._ports = thisNode.selectAll(".port_output").data(d.ports);
1215                         d._ports.enter().append("rect").attr("class","port port_output").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
1216                             .on("mousedown",(function(){var node = d; return function(d,i){portMouseDown(node,0,i);}})() )
1217                             .on("touchstart",(function(){var node = d; return function(d,i){portMouseDown(node,0,i);}})() )
1218                             .on("mouseup",(function(){var node = d; return function(d,i){portMouseUp(node,0,i);}})() )
1219                             .on("touchend",(function(){var node = d; return function(d,i){portMouseUp(node,0,i);}})() )
1220                             .on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || mousedown_port_type !== 0 ));})
1221                             .on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
1222                         d._ports.exit().remove();
1223                         if (d._ports) {
1224                             numOutputs = d.outputs || 1;
1225                             y = (d.h/2)-((numOutputs-1)/2)*13;
1226                             var x = d.w - 5;
1227                             d._ports.each(function(d,i) {
1228                                     var port = d3.select(this);
1229                                     port.attr("y",(y+13*i)-5).attr("x",x);
1230                             });
1231                         }
1232                         thisNode.selectAll('text.node_label').text(function(d,i){
1233                                 if (d._def.label) {
1234                                     if (typeof d._def.label == "function") {
1235                                         return d._def.label.call(d);
1236                                     } else {
1237                                         return d._def.label;
1238                                     }
1239                                 }
1240                                 return "";
1241                         })
1242                             .attr('y', function(d){return (d.h/2)-1;})
1243                             .attr('class',function(d){
1244                                 return 'node_label'+
1245                                 (d._def.align?' node_label_'+d._def.align:'')+
1246                                 (d._def.labelStyle?' '+(typeof d._def.labelStyle == "function" ? d._def.labelStyle.call(d):d._def.labelStyle):'') ;
1247                         });
1248                         thisNode.selectAll(".node_tools").attr("x",function(d){return d.w-35;}).attr("y",function(d){return d.h-20;});
1249
1250                         thisNode.selectAll(".node_changed")
1251                             .attr("x",function(d){return d.w-10})
1252                             .classed("hidden",function(d) { return !d.changed; });
1253
1254                         thisNode.selectAll(".node_error")
1255                             .attr("x",function(d){return d.w-10-(d.changed?13:0)})
1256                             .classed("hidden",function(d) { return d.valid; });
1257
1258                         thisNode.selectAll(".port_input").each(function(d,i) {
1259                                 var port = d3.select(this);
1260                                 port.attr("y",function(d){return (d.h/2)-5;})
1261                         });
1262
1263                         thisNode.selectAll(".node_icon").attr("y",function(d){return (d.h-d3.select(this).attr("height"))/2;});
1264                         thisNode.selectAll(".node_icon_shade").attr("height",function(d){return d.h;});
1265                         thisNode.selectAll(".node_icon_shade_border").attr("d",function(d){ return "M "+(("right" == d._def.align) ?0:30)+" 1 l 0 "+(d.h-2)});
1266
1267                         
1268                         thisNode.selectAll('.node_right_button').attr("transform",function(d){
1269                                 var x = d.w-6;
1270                                 if (d._def.button.toggle && !d[d._def.button.toggle]) {
1271                                     x = x - 8;
1272                                 }
1273                                 return "translate("+x+",2)";
1274                         });
1275                         thisNode.selectAll('.node_right_button rect').attr("fill-opacity",function(d){
1276                                 if (d._def.button.toggle) {
1277                                     return d[d._def.button.toggle]?1:0.2;
1278                                 }
1279                                 return 1;
1280                         });
1281
1282                         //thisNode.selectAll('.node_right_button').attr("transform",function(d){return "translate("+(d.w - d._def.button.width.call(d))+","+0+")";}).attr("fill",function(d) {
1283                         //         return typeof d._def.button.color  === "function" ? d._def.button.color.call(d):(d._def.button.color != null ? d._def.button.color : d._def.color)
1284                         //});
1285
1286                         thisNode.selectAll('.node_badge_group').attr("transform",function(d){return "translate("+(d.w-40)+","+(d.h+3)+")";});
1287                         thisNode.selectAll('text.node_badge_label').text(function(d,i) {
1288                             if (d._def.badge) {
1289                                 if (typeof d._def.badge == "function") {
1290                                     return d._def.badge.call(d);
1291                                 } else {
1292                                     return d._def.badge;
1293                                 }
1294                             }
1295                             return "";
1296                         });
1297                         if (!showStatus || !d.status) {
1298                             thisNode.selectAll('.node_status_group').style("display","none");
1299                         } else {
1300                             thisNode.selectAll('.node_status_group').style("display","inline").attr("transform","translate(3,"+(d.h+3)+")");
1301                             var fill = status_colours[d.status.fill]; // Only allow our colours for now
1302                             if (d.status.shape == null && fill == null) {
1303                                 thisNode.selectAll('.node_status').style("display","none");
1304                             } else {
1305                                 var style;
1306                                 if (d.status.shape == null || d.status.shape == "dot") {
1307                                     style = {
1308                                         display: "inline",
1309                                         fill: fill,
1310                                         stroke: fill
1311                                     };
1312                                 } else if (d.status.shape == "ring" ){
1313                                     style = {
1314                                         display: "inline",
1315                                         fill: '#fff',
1316                                         stroke: fill
1317                                     }
1318                                 }
1319                                 thisNode.selectAll('.node_status').style(style);
1320                             }
1321                             if (d.status.text) {
1322                                 thisNode.selectAll('.node_status_label').text(d.status.text);
1323                             } else {
1324                                 thisNode.selectAll('.node_status_label').text("");
1325                             }
1326                         }
1327                                 //console.dir("d value");
1328                                 //console.dir(d);
1329                             if (showNumbers && d.dgnumber != null && d.dgnumber != undefined  && d.dgnumber.length >0) {
1330                                         thisNode.selectAll('.node_dgnumber_group').style("display","inline").attr("transform","translate(9,"+(d.h+9)+")");
1331                                         thisNode.selectAll('.node_dgnumber_label').text(d.dgnumber.toString());
1332                                         var dgnumberList = d.dgnumber;
1333                                         var dgnum = "";
1334                                         if(dgnumberList != null && dgnumberList.length >=1){
1335                                                 dgnum = dgnumberList[0];
1336                                                 thisNode.select('.node_dgnumber_label').text(dgnum);
1337                                                 //console.log(dgnumberList);
1338                                                 thisNode.select('.node_dgnumber_title').text(dgnumberList);
1339                                         }
1340                                 /*
1341                                 if(d.dgnumber.length > 1){
1342                                         thisNode.selectAll('.node_dgnumber_group').style("display","inline").attr("transform","translate(9,"+(d.h-15)+")");
1343                                         thisNode.selectAll('.node_dgnumber_label').text(d.dgnumber.toString());
1344                                 }else{
1345                                         thisNode.selectAll('.node_dgnumber_group').style("display","inline").attr("transform","translate(9,"+(d.h+9)+")");
1346                                         thisNode.selectAll('.node_dgnumber_label').text(d.dgnumber.toString());
1347                                 }
1348                                 */
1349                             } else {
1350                                 //console.log("fhfjhfjh ");
1351                                 thisNode.select('.node_dgnumber').style("display","none");
1352                                 thisNode.select('.node_dgnumber_label').text("");
1353                                 thisNode.select('.node_dgnumber_title').text("");
1354                             }
1355
1356                         d.dirty = false;
1357                     }
1358             });
1359         }
1360
1361         var link = vis.selectAll(".link").data(RED.nodes.links.filter(function(d) { return d.source.z == activeWorkspace && d.target.z == activeWorkspace }),function(d) { return d.source.id+":"+d.sourcePort+":"+d.target.id;});
1362
1363         var linkEnter = link.enter().insert("g",".node").attr("class","link");
1364         
1365         linkEnter.each(function(d,i) {
1366             var l = d3.select(this);
1367             l.append("svg:path").attr("class","link_background link_path")
1368                .on("mousedown",function(d) {
1369                     mousedown_link = d;
1370                     clearSelection();
1371                     selected_link = mousedown_link;
1372                     updateSelection();
1373                     redraw();
1374                     d3.event.stopPropagation();
1375                 })
1376                 .on("touchstart",function(d) {
1377                     mousedown_link = d;
1378                     clearSelection();
1379                     selected_link = mousedown_link;
1380                     updateSelection();
1381                     redraw();
1382                     d3.event.stopPropagation();
1383                 });
1384             l.append("svg:path").attr("class","link_outline link_path");
1385             l.append("svg:path").attr("class","link_line link_path");
1386         });
1387
1388         link.exit().remove();
1389
1390         var links = vis.selectAll(".link_path")
1391         links.attr("d",function(d){
1392                 var numOutputs = d.source.outputs || 1;
1393                 var sourcePort = d.sourcePort || 0;
1394                 var y = -((numOutputs-1)/2)*13 +13*sourcePort;
1395
1396                 var dy = d.target.y-(d.source.y+y);
1397                 var dx = (d.target.x-d.target.w/2)-(d.source.x+d.source.w/2);
1398                 var delta = Math.sqrt(dy*dy+dx*dx);
1399                 var scale = lineCurveScale;
1400                 var scaleY = 0;
1401                 if (delta < node_width) {
1402                     scale = 0.75-0.75*((node_width-delta)/node_width);
1403                 }
1404
1405                 if (dx < 0) {
1406                     scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width));
1407                     if (Math.abs(dy) < 3*node_height) {
1408                         scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ;
1409                     }
1410                 }
1411
1412                 d.x1 = d.source.x+d.source.w/2;
1413                 d.y1 = d.source.y+y;
1414                 d.x2 = d.target.x-d.target.w/2;
1415                 d.y2 = d.target.y;
1416
1417                 return "M "+(d.source.x+d.source.w/2)+" "+(d.source.y+y)+
1418                     " C "+(d.source.x+d.source.w/2+scale*node_width)+" "+(d.source.y+y+scaleY*node_height)+" "+
1419                     (d.target.x-d.target.w/2-scale*node_width)+" "+(d.target.y-scaleY*node_height)+" "+
1420                     (d.target.x-d.target.w/2)+" "+d.target.y;
1421         })
1422
1423         link.classed("link_selected", function(d) { return d === selected_link || d.selected; });
1424         link.classed("link_unknown",function(d) { return d.target.type == "unknown" || d.source.type == "unknown"});
1425
1426         if (d3.event) {
1427             d3.event.preventDefault();
1428         }
1429     }
1430
1431     RED.keyboard.add(/* z */ 90,{ctrl:true},function(){RED.history.pop();});
1432     RED.keyboard.add(/* a */ 65,{ctrl:true},function(){selectAll();d3.event.preventDefault();});
1433     RED.keyboard.add(/* = */ 187,{ctrl:true},function(){zoomIn();d3.event.preventDefault();});
1434     RED.keyboard.add(/* - */ 189,{ctrl:true},function(){zoomOut();d3.event.preventDefault();});
1435     RED.keyboard.add(/* 0 */ 48,{ctrl:true},function(){zoomZero();d3.event.preventDefault();});
1436     RED.keyboard.add(/* v */ 86,{ctrl:true},function(){importNodes(clipboard);d3.event.preventDefault();});
1437     RED.keyboard.add(/* e */ 69,{ctrl:true},function(){showExportNodesDialog();d3.event.preventDefault();});
1438     RED.keyboard.add(/* i */ 73,{ctrl:true},function(){showImportNodesDialog();d3.event.preventDefault();});
1439     RED.keyboard.add(/* B */ 66,{ctrl:true},function(){RED.view.showDgNumberDialog();d3.event.preventDefault();});
1440     RED.keyboard.add(/* [ */ 219,{ctrl:true},function(){RED.view.showSearchTextDialog();d3.event.preventDefault();});
1441     RED.keyboard.add(/* O */ 79,{ctrl:true},function(){RED.view.showRequestTemplateDialog();d3.event.preventDefault();});
1442
1443
1444     // TODO: 'dirty' should be a property of RED.nodes - with an event callback for ui hooks
1445     function setDirty(d) {
1446         dirty = d;
1447         if (dirty) {
1448             $("#btn-deploy").removeClass("disabled");
1449         } else {
1450             $("#btn-deploy").addClass("disabled");
1451         }
1452     }
1453
1454     /**
1455      * Imports a new collection of nodes from a JSON String.
1456      *  - all get new IDs assigned
1457      *  - all 'selected'
1458      *  - attached to mouse for placing - 'IMPORT_DRAGGING'
1459      */
1460     function importNodes(newNodesStr,touchImport) {
1461         try {
1462             var result = RED.nodes.import(newNodesStr,true);
1463             if (result) {
1464                 var new_nodes = result[0];
1465                 var new_links = result[1];
1466                 var new_workspaces = result[2];
1467                 
1468                 var new_ms = new_nodes.filter(function(n) { return n.z == activeWorkspace }).map(function(n) { return {n:n};});
1469                 var new_node_ids = new_nodes.map(function(n){ return n.id; });
1470                 
1471                 // TODO: pick a more sensible root node
1472                 if (new_ms.length > 0) {
1473                     var root_node = new_ms[0].n;
1474                     var dx = root_node.x;
1475                     var dy = root_node.y;
1476     
1477                     if (mouse_position == null) {
1478                         mouse_position = [0,0];
1479                     }
1480     
1481                     var minX = 0;
1482                     var minY = 0;
1483                     var i;
1484                     var node;
1485                     
1486                     for (i=0;i<new_ms.length;i++) {
1487                         node = new_ms[i];
1488                         node.n.selected = true;
1489                         node.n.changed = true;
1490                         node.n.x -= dx - mouse_position[0];
1491                         node.n.y -= dy - mouse_position[1];
1492                         node.dx = node.n.x - mouse_position[0];
1493                         node.dy = node.n.y - mouse_position[1];
1494                         minX = Math.min(node.n.x-node_width/2-5,minX);
1495                         minY = Math.min(node.n.y-node_height/2-5,minY);
1496                     }
1497                     for (i=0;i<new_ms.length;i++) {
1498                         node = new_ms[i];
1499                         node.n.x -= minX;
1500                         node.n.y -= minY;
1501                         node.dx -= minX;
1502                         node.dy -= minY;
1503                     }
1504                     if (!touchImport) {
1505                         mouse_mode = RED.state.IMPORT_DRAGGING;
1506                     }
1507     
1508                     RED.keyboard.add(/* ESCAPE */ 27,function(){
1509                             RED.keyboard.remove(/* ESCAPE */ 27);
1510                             clearSelection();
1511                             RED.history.pop();
1512                             mouse_mode = 0;
1513                     });
1514                     clearSelection();
1515                     moving_set = new_ms;
1516                 }
1517
1518                 RED.history.push({t:'add',nodes:new_node_ids,links:new_links,workspaces:new_workspaces,dirty:RED.view.dirty()});
1519
1520
1521                 redraw();
1522             }
1523         } catch(error) {
1524             console.log(error.stack);
1525             RED.notify("<strong>Error</strong>: "+error,"error");
1526         }
1527     }
1528
1529     function showExportNodesDialog() {
1530         mouse_mode = RED.state.EXPORT;
1531         var nns = RED.nodes.createExportableNodeSet(moving_set);
1532         $("#dialog-form").html($("script[data-template-name='export-clipboard-dialog']").html());
1533         $("#node-input-export").val(JSON.stringify(nns));
1534         $("#node-input-export").focus(function() {
1535                 var textarea = $(this);
1536                 textarea.select();
1537                 textarea.mouseup(function() {
1538                         textarea.unbind("mouseup");
1539                         return false;
1540                 });
1541         });
1542         $( "#dialog" ).dialog("option","title","Export nodes to clipboard").dialog( "open" );
1543         $("#node-input-export").focus();
1544     }
1545
1546     function showExportNodesLibraryDialog() {
1547         mouse_mode = RED.state.EXPORT;
1548         var nns = RED.nodes.createExportableNodeSet(moving_set);
1549         $("#dialog-form").html($("script[data-template-name='export-library-dialog']").html());
1550         $("#node-input-filename").attr('nodes',JSON.stringify(nns));
1551         $( "#dialog" ).dialog("option","title","Export nodes to library").dialog( "open" );
1552     }
1553
1554     function showImportNodesDialog() {
1555         mouse_mode = RED.state.IMPORT;
1556         $("#dialog-form").html($("script[data-template-name='import-dialog']").html());
1557         $("#node-input-import").val("");
1558         $( "#dialog" ).dialog("option","title","Import nodes").dialog( "open" );
1559     }
1560
1561     function showRenameWorkspaceDialog(id) {
1562         var ws = RED.nodes.workspace(id);
1563         $( "#node-dialog-rename-workspace" ).dialog("option","workspace",ws);
1564
1565         if (workspace_tabs.count() == 1) {
1566             $( "#node-dialog-rename-workspace").next().find(".leftButton")
1567                 .prop('disabled',true)
1568                 .addClass("ui-state-disabled");
1569         } else {
1570             $( "#node-dialog-rename-workspace").next().find(".leftButton")
1571                 .prop('disabled',false)
1572                 .removeClass("ui-state-disabled");
1573         }
1574
1575         $( "#node-input-workspace-name" ).val(ws.label);
1576         $( "#node-dialog-rename-workspace" ).dialog("open");
1577     }
1578
1579     $("#node-dialog-rename-workspace form" ).submit(function(e) { e.preventDefault();});
1580     $( "#node-dialog-rename-workspace" ).dialog({
1581         modal: true,
1582         autoOpen: false,
1583         width: 500,
1584         title: "Rename sheet",
1585         buttons: [
1586             {
1587                 class: 'leftButton',
1588                 text: "Delete",
1589                 click: function() {
1590                     var workspace = $(this).dialog('option','workspace');
1591                     $( this ).dialog( "close" );
1592                     deleteWorkspace(workspace.id);
1593                 }
1594             },
1595             {
1596                 text: "Ok",
1597                 click: function() {
1598                     var workspace = $(this).dialog('option','workspace');
1599                     var label = $( "#node-input-workspace-name" ).val();
1600                     if (workspace.label != label) {
1601                         workspace.label = label;
1602                         var link = $("#workspace-tabs a[href='#"+workspace.id+"']");
1603                         link.attr("title",label);
1604                         link.text(label);
1605                         RED.view.dirty(true);
1606                     }
1607                     $( this ).dialog( "close" );
1608                 }
1609             },
1610             {
1611                 text: "Cancel",
1612                 click: function() {
1613                     $( this ).dialog( "close" );
1614                 }
1615             }
1616         ],
1617         open: function(e) {
1618             RED.keyboard.disable();
1619         },
1620         close: function(e) {
1621             RED.keyboard.enable();
1622         }
1623     });
1624     $( "#node-dialog-delete-workspace" ).dialog({
1625         modal: true,
1626         autoOpen: false,
1627         width: 500,
1628         title: "Confirm delete",
1629         buttons: [
1630             {
1631                 text: "Ok",
1632                 click: function() {
1633                     var workspace = $(this).dialog('option','workspace');
1634                     RED.view.removeWorkspace(workspace);
1635                     var historyEvent = RED.nodes.removeWorkspace(workspace.id);
1636                     historyEvent.t = 'delete';
1637                     historyEvent.dirty = dirty;
1638                     historyEvent.workspaces = [workspace];
1639                     RED.history.push(historyEvent);
1640                     RED.view.dirty(true);
1641                     $( this ).dialog( "close" );
1642                 }
1643             },
1644             {
1645                 text: "Cancel",
1646                 click: function() {
1647                     $( this ).dialog( "close" );
1648                 }
1649             }
1650         ],
1651         open: function(e) {
1652             RED.keyboard.disable();
1653         },
1654         close: function(e) {
1655             RED.keyboard.enable();
1656         }
1657
1658     });
1659     return {
1660         state:function(state) {
1661             if (state == null) {
1662                 return mouse_mode
1663             } else {
1664                 mouse_mode = state;
1665             }
1666         },
1667         addWorkspace: function(ws) {
1668             workspace_tabs.addTab(ws);
1669             workspace_tabs.resize();
1670         },
1671         removeWorkspace: function(ws) {
1672             workspace_tabs.removeTab(ws.id);
1673         },
1674         getWorkspace: function() {
1675             return activeWorkspace;
1676         },
1677         showWorkspace: function(id) {
1678             workspace_tabs.activateTab(id);
1679         },
1680         redraw:redraw,
1681         dirty: function(d) {
1682             if (d == null) {
1683                 return dirty;
1684             } else {
1685                 setDirty(d);
1686             }
1687         },
1688         importNodes: importNodes,
1689         resize: function() {
1690             workspace_tabs.resize();
1691         },
1692         status: function(s) {
1693                 validateEachNodeXml();
1694             showStatus = s;
1695             RED.nodes.eachNode(function(n) { n.dirty = true;});
1696             //TODO: subscribe/unsubscribe here
1697             redraw();
1698         },
1699         showYangUploadDialog:function showYangUploadDialog(){
1700                 $(function() {
1701                         var htmlStr= "<div id='yang-upload-div' style='width:375;height:225'>" +
1702                         '<form id="uploadForm" name="uploadForm" enctype="multipart/form-data" action="/api/uploadyang" method="post" >' + 
1703                         "<input id='yang-file-id' name='yangFile' type='file' accept='.yang,.zip'><p style='font-size:0.7em'><i>For Module depending on multiple yang files, zip them and upload the zip file. The zip file name should match the exact name of the module with .zip extension</i</p><br><br><br><br><br><p id='yang-upload-status'></p>" +
1704                         //'<input id="upload-yang-button-id"  style="font-size:1em;font-weight:bold" type="button" value="Upload Yang" name="upload-yang-button">' +
1705                         "</form></div>";
1706
1707                         $("#yang-upload-dialog").dialog({
1708                                 modal:true,     
1709                                 autoOpen :false,
1710                                 title: "Upload Yang",
1711                                 width: 500,
1712                                 height: 260,
1713                                 minWidth : 300, 
1714                                 minHeight :260, 
1715                                 buttons :[
1716                                         {
1717                                                 text: "Upload Yang",
1718                                                 click: function() {
1719                                                         if( document.getElementById("yang-file-id").files.length == 0 ){
1720                                                                 $("#yang-upload-status").html("<span>No files selected.</span>");
1721                                                                 return ;
1722                                                         }       
1723                                                         $('#yang-upload-dialog').parent().find('.ui-dialog-buttonpane button:first').button("disable");
1724                                                         //$("#yang-upload-status").empty().text("File is uploading...");
1725                                                         $("#yang-upload-status").html("<span>Processing...Please wait</span><img src='images/page-loading.gif'>");
1726                                                         $.ajax({
1727                                                                 url: "/api/uploadyang", 
1728                                                                 type: "POST",             
1729                                                                 data: new FormData(document.forms['uploadForm']),
1730                                                                 contentType: false,       
1731                                                                 cache: false,             
1732                                                                 processData:false, 
1733                                                                 success: function(data) {
1734                                                                         $("#yang-upload-status").html("");
1735                                                                         $("#yang-upload-status").text(data.msg);
1736                                                                         $('#yang-upload-dialog').parent().find('.ui-dialog-buttonpane button:first').button("enable");
1737                                                                 /*
1738                                                                         sliValuesObj = {};
1739                                                                         rpcValues = {};
1740                                                                         reqInputValues = {};
1741                                                                         for(var i=0;i<data.sliValuesObj.length;i++){
1742                                                                                 var moduleName = data.sliValuesObj[i].moduleName;
1743                                                                                 sliValuesObj[moduleName] = data.sliValuesObj[i][moduleName + '_PROPS'];
1744                                                                                 rpcValues[moduleName] = data.sliValuesObj[i][ moduleName +'_RPCS'];
1745                                                                                 for(var k=0;rpcValues[moduleName] != undefined && k<rpcValues[moduleName].length;k++){
1746                                                                                         var rpcName = rpcValues[moduleName][k];
1747                                                                                         reqInputValues[moduleName + "_" + rpcName] = data.sliValuesObj[i][rpcName +"-input"];
1748                                                                                 }
1749                                                                         }
1750                                                                  */
1751                                                                         //close the yang upload dialogog box and open the load dialogbox
1752                                                                         $('#yang-upload-dialog').dialog("close");
1753                                                                         $("#btn-available-yang-modules").trigger("click");
1754                                                                 },
1755                                                                 error:function (xhr, desc, err){
1756                                                                         $("#yang-upload-status").html(err);
1757                                                                         $('#yang-upload-dialog').parent().find('.ui-dialog-buttonpane button:first').button("enable");
1758                                                                 }
1759                                                         });
1760                                                 }
1761                                         },
1762                                         {
1763                                                 text: "Close",
1764                                                 click: function() {
1765                                                         $("#yang-upload-dialog").dialog("close");
1766                                                 }
1767                                         }
1768                                         ]
1769                         }).dialog("open").html(htmlStr);
1770                 });
1771         },
1772         showDgNumberDialog: function showDgNumberDialog(){
1773         $(function() {
1774                 var isLoopDetected = detectLoop();
1775                 console.log("isLoopDetected:" + isLoopDetected);
1776                 if(isLoopDetected){
1777                         return false;
1778                 }
1779                 updateDgNumbers();
1780         var htmlStr="<div id='find-dgnumber-div' style='width:375;height:225'><label>DG Number</label><input id='dgnumber-val-id' type='text' value=''><p id='find-dgnumber-status' style='color:red'></p></div>";
1781         $("#dgnumber-find-dialog").dialog({
1782                 modal:true,     
1783                 autoOpen :false,
1784                 title: "Find Node By DGNumber",
1785                 width: 300,
1786                 height: 215,
1787                 minWidth : 300, 
1788                 minHeight :215, 
1789                 buttons :[
1790                         {
1791                         text: "Find",
1792                         click: function() {
1793                                 var dgnumVal = $("#dgnumber-val-id").val();
1794                                 $("#find-dgnumber-status").text("");
1795                                 if(dgnumVal != undefined &&  dgnumVal != '' && dgnumVal != ''){
1796                                         dgnumVal = dgnumVal.trim();
1797                                 }else{
1798                                         dgnumVal ="";
1799                                 }
1800                                         
1801                                 var dgNumberFound = false;
1802                                 var node = vis.selectAll(".nodegroup").data(RED.nodes.nodes.filter(function(d) { return d.z == activeWorkspace }),function(d){return d.id});
1803                                 node.each(function(d,i) {
1804                                         var thisNode = d3.select(this);
1805                                         var dgn = d.dgnumber;
1806                                         
1807                                         if(dgn != undefined && typeof dgn == 'object'){
1808                                                 var found = false;
1809                                                 for(var k=0;k<dgn.length;k++){
1810                                                         if(dgn[k] == dgnumVal){
1811                                                                 found = true;
1812                                                                 break;
1813                                                         }
1814                                                 }
1815                                                 if(found){
1816                                                         //thisNode.select("rect").style({"stroke":"blue","stroke-width" :"3","stroke-dasharray":"5,1"});
1817                                                         //$("#" + d.id).find("rect").attr("class","node-found-selected");
1818                                                         //$("#" + d.id).find("rect").attr("class","node node_selected");
1819                                                         //thisNode.select("rect").attr("class","node node_selected");
1820                                                         thisNode.select("rect").attr("class","node node_found");
1821                                                         document.getElementById( d.id ).scrollIntoView();       
1822                                                         $("#dgnumber-find-dialog").dialog("close");
1823
1824                                                         //display the node edit dialogbox
1825                                                         RED.editor.edit(d);
1826                                                         dgNumberFound = true;
1827                                                 }else{
1828                                                         //thisNode.select("rect").style({"stroke":"#999","stroke-width" :"2","stroke-dasharray":"none"});
1829                                                         //$("#" + d.id ).find("rect").attr("class","node-found-clear");
1830                                                         thisNode.select("rect").attr("class","node");
1831                                                         //$("#" + d.id ).find("rect").attr("class","node");
1832                                                         //$("#find-dgnumber-status").text("DGNumber :" + dgnumVal + " Not found");
1833                                                         
1834                                                 }
1835                                         }
1836                                 });
1837                                 if(!dgNumberFound){
1838                                         $("#find-dgnumber-status").text("DGNumber :" + dgnumVal + " Not found");
1839                                 }
1840                         }
1841                         },
1842                         {
1843                         text: "Close",
1844                         click: function() {
1845                                 $("#dgnumber-find-dialog").dialog("close");
1846                         }
1847                         }
1848                 ],
1849                 open:function(){
1850                         //Bind the Enter key to Find button
1851                         $('#dgnumber-find-dialog').keypress(function(e) {
1852                                if (e.keyCode == $.ui.keyCode.ENTER) {
1853                                         $('#dgnumber-find-dialog').parent().find('.ui-dialog-buttonpane button:first').click();
1854                                                 return false;
1855                                         }
1856                         });
1857                         $(function(){
1858                                 //set focus on the input box
1859                                 $("#dgnumber-val-id").focus();
1860                         });
1861                 }
1862                 }).dialog("open").html(htmlStr);
1863         });
1864         },      
1865         showSearchTextDialog: function showSearchTextDialog(){
1866         $(function() {
1867                 var isLoopDetected = detectLoop();
1868                 console.log("isLoopDetected:" + isLoopDetected);
1869                 if(isLoopDetected){
1870                         return false;
1871                 }
1872                 updateDgNumbers();
1873         //console.log("In the showSearchTextDialog.");  
1874         var htmlStr="<div id='search-text-div' style='width:675;height:525'><label>Search Text</label><input style='width:500px;' id='search-text-val-id' type='text' value=''><br><input id='ignore-case-id' type='checkbox' name='ignorecase' value='1' >Ignore Case<br><p id='search-text-status' style='color:red'></p></div>";
1875         //console.log("setting up search-text-dialog.");        
1876         $("#search-text-dialog").dialog({
1877                 modal:true,     
1878                 autoOpen :false,
1879                 title: "Search text in DG",
1880                 width: 600,
1881                 height: 515,
1882                 minWidth : 500, 
1883                 minHeight :415, 
1884                 buttons :[
1885                         {
1886                         text: "Search",
1887                         click: function() {
1888         
1889                                 var node = vis.selectAll(".nodegroup").data(RED.nodes.nodes.filter(function(d) { return d.z == activeWorkspace }),function(d){return d.id});
1890                                 var searchText = $("#search-text-val-id").val();
1891                                 $("#search-text-status").text("");
1892                                 if(searchText != undefined &&  searchText != '' && searchText != ''){
1893                                         searchText = searchText.trim();
1894                                 }else{
1895                                         searchText ="";
1896                                         node.each(function(d,i) {
1897                                                 var thisNode = d3.select(this);
1898                                                 thisNode.select("rect").attr("class","node");
1899                                         });
1900                                         return;
1901                                 }
1902                                         
1903                                 var foundSearchText = false;
1904                                 var foundInDgNumArr = [];
1905                                 //console.log("In search function");
1906                                 node.each(function(d,i) {
1907                                         var thisNode = d3.select(this);
1908                                         var dgn = d.dgnumber;
1909                                         var xml = d.xml;
1910                                         var nName = d.name;
1911                         
1912                                         var ignoreCase = $('#ignore-case-id').prop('checked') 
1913                                         var options = 'g';
1914                                         if(ignoreCase){
1915                                                 options='gi';
1916                                         }
1917                                         var searchPattern = new RegExp(searchText, options );
1918                                         if(xml == undefined || xml == null){
1919                                                 xml = "";
1920                                         }
1921                                         if(nName == undefined || nName == null){
1922                                                 nName = "";
1923                                         }
1924                                         //console.log(searchPattern);
1925                                         var count1 = (xml.match(searchPattern) || []).length;
1926                                         //console.log(count1);  
1927                                         var count2 = (nName.match(searchPattern) || []).length;
1928                                         //console.log(count2);  
1929                                                 
1930                                         if(count1 >0 || count2 > 0){
1931                                                 thisNode.select("rect").attr("class","node text_found");
1932                                                 var dgn = d.dgnumber;
1933                                         
1934                                                 var dgNumber =  dgn;
1935                                                 if(dgn != undefined && typeof dgn == 'object'){
1936                                                         console.log("DGNUMBERS:"  + dgn);
1937                                                         dgNumber =  dgn[0];
1938                                                 }
1939                                                 if(dgn != undefined ){
1940                                                         foundInDgNumArr.push(dgNumber);
1941                                                 }else{
1942                                                         foundInDgNumArr.push(d.type);
1943                                                 }
1944                                                 foundSearchText=true;
1945                                         }else{
1946                                                 thisNode.select("rect").attr("class","node");
1947                                         }
1948                                 });
1949                                 if(!foundSearchText){
1950                                         $("#search-text-status").text("Search Text :" + searchText  + " Not found");
1951                                 }else{
1952                                         //console.log("closing dialog");
1953                                         //$("#search-text-dialog").dialog("close");
1954                                         console.log(foundInDgNumArr);
1955                                         $("#search-text-status").text("Found in DG numbers :" + foundInDgNumArr);
1956                                 }
1957                         }
1958                         },
1959                         {
1960                         text: "Close",
1961                         click: function() {
1962                                 //console.log("closing dialog");
1963                                 $("#search-text-dialog").dialog("close");
1964                         }
1965                         }
1966                 ],
1967                 open:function(){
1968                         //console.log("called open.");
1969                         //Bind the Enter key to Find button
1970                         $('#search-text-dialog').keypress(function(e) {
1971                                if (e.keyCode == $.ui.keyCode.ENTER) {
1972                                         $('#search-text-dialog').parent().find('.ui-dialog-buttonpane button:first').click();
1973                                                 return false;
1974                                         }
1975                         });
1976                         $(function(){
1977                                 //set focus on the input box
1978                                 $("#search-text-id").focus();
1979                         });
1980                         //console.log("done open call.");
1981                 }
1982                 }).dialog('open').html(htmlStr);
1983         });
1984         },      
1985         showRequestTemplateDialog: function showRequestTemplateDialog(){
1986         $(function() {
1987                 var currNodes =  RED.nodes.nodes.filter(function(d) { return d.z == activeWorkspace })
1988                 var moduleName = "";
1989                 var rpcName = "";
1990                 if(currNodes != null && currNodes.length > 1){
1991                         currNodes.forEach(function(n){
1992                                 if(n.type == 'service-logic'){
1993                                         moduleName = getAttributeValue(n.xml,"module");
1994                                 }else if(n.type == 'method'){
1995                                         rpcName = getAttributeValue(n.xml,"rpc");
1996                                 }
1997                         });
1998                 }
1999                 console.log("moduleName:" + moduleName);
2000                 console.log("rpcName:" + rpcName);
2001                 var inputValObj = reqInputValues[moduleName + "_" + rpcName];
2002                 var inputValStr = "Not found. Please make sure that the Module is loaded and the rpc has input.";
2003                 if(inputValObj != undefined && inputValObj != null){
2004                         inputValStr = "{\n\"input\" : " + JSON.stringify(inputValObj,null,4)+ "\n}";
2005                 }       
2006                 
2007                 //var htmlStr="<div id='request-template-div' style='width:875px;height:575px'><textarea style='width:875px;height:575px'>" + inputValStr + "</textarea></div>"
2008                 //var htmlStr="<div id='request-template-div' style='width:750px;height:550px;font-weight:bold;font-size:1em'><pre>" + inputValStr + "</pre></div>"
2009                 var htmlStr="<textarea readonly='1' id='request-template-textarea' style='width:750px;height:550px;font-weight:bold;font-size:1em'>" + inputValStr + "</textarea>"
2010                 $("#request-input-dialog").dialog({
2011                         dialogClass :"no-close",
2012                         modal:true,     
2013                         autoOpen :false,
2014                         title: "Request Template for Module:" + moduleName + "    RPC:" + rpcName,
2015                         width: 800,
2016                         height: "auto",
2017                         buttons :[
2018                         {
2019                                 text: "Close",
2020                                 click: function() {
2021                                         $("#request-input-dialog").dialog("close");
2022                                 }
2023                         }
2024                         ],
2025                         open:function(){
2026                                  $('#request-input-dialog').css('overflow', 'hidden'); 
2027                         }
2028                 }).dialog("open").html(htmlStr);
2029         });
2030         },
2031         showNumbers: function(s) {
2032                 console.log("showNumbers:" + s);
2033                 showNumbers = s;
2034             RED.nodes.eachNode(function(n) { n.dirty = true;});
2035             redraw();
2036         },
2037         showNodePalette: function(s) {
2038                 showNodePalette=s;
2039                 if(!s){
2040                         $("#main-container").addClass("palette-bar-closed");
2041                         //RED.menu.setSelected("btn-node-panel",true);
2042                 }else{
2043                         $("#main-container").removeClass("palette-bar-closed");
2044                 }
2045                 //console.log("showNodePalette:" + showNodePalette);
2046         },
2047         
2048         //TODO: should these move to an import/export module?
2049         showImportNodesDialog: showImportNodesDialog,
2050         showExportNodesDialog: showExportNodesDialog,
2051         showExportNodesLibraryDialog: showExportNodesLibraryDialog
2052     };
2053 })();