Paul Greyson | e15c439 | 2013-04-09 15:05:31 -0700 | [diff] [blame] | 1 | function mouseOverSwitch(data) { |
| 2 | |
| 3 | d3.event.preventDefault(); |
| 4 | |
| 5 | d3.select(document.getElementById(data.dpid + '-label')).classed('nolabel', false); |
| 6 | |
| 7 | if (data.highlighted) { |
| 8 | return; |
| 9 | } |
| 10 | |
| 11 | // only highlight valid link or flow destination by checking for class of existing highlighted circle |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 12 | var highlighted = topology.selectAll('.highlight')[0]; |
Paul Greyson | e15c439 | 2013-04-09 15:05:31 -0700 | [diff] [blame] | 13 | if (highlighted.length == 1) { |
| 14 | var s = d3.select(highlighted[0]).select('circle'); |
| 15 | // only allow links |
| 16 | // edge->edge (flow) |
| 17 | // aggregation->core |
| 18 | // core->core |
| 19 | if (data.className == 'edge' && !s.classed('edge') || |
| 20 | data.className == 'core' && !s.classed('core') && !s.classed('aggregation') || |
| 21 | data.className == 'aggregation' && !s.classed('core')) { |
| 22 | return; |
| 23 | } |
| 24 | |
| 25 | // the second highlighted switch is the target for a link or flow |
| 26 | data.target = true; |
| 27 | } |
| 28 | |
| 29 | var node = d3.select(document.getElementById(data.dpid)); |
| 30 | node.classed('highlight', true).select('circle').transition().duration(100).attr("r", widths.core); |
| 31 | data.highlighted = true; |
| 32 | node.moveToFront(); |
| 33 | } |
| 34 | |
| 35 | function mouseOutSwitch(data) { |
| 36 | d3.select(document.getElementById(data.dpid + '-label')).classed('nolabel', true); |
| 37 | |
| 38 | if (data.mouseDown) |
| 39 | return; |
| 40 | |
| 41 | var node = d3.select(document.getElementById(data.dpid)); |
| 42 | node.classed('highlight', false).select('circle').transition().duration(100).attr("r", widths[data.className]); |
| 43 | data.highlighted = false; |
| 44 | data.target = false; |
| 45 | } |
| 46 | |
| 47 | function mouseDownSwitch(data) { |
| 48 | mouseOverSwitch(data); |
| 49 | data.mouseDown = true; |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 50 | d3.select('#topologyArea').classed('linking', true); |
Paul Greyson | e15c439 | 2013-04-09 15:05:31 -0700 | [diff] [blame] | 51 | |
| 52 | d3.select('svg') |
| 53 | .append('svg:path') |
| 54 | .attr('id', 'linkVector') |
| 55 | .attr('d', function () { |
| 56 | var s = d3.select(document.getElementById(data.dpid)); |
| 57 | |
| 58 | var pt = document.querySelector('svg').createSVGPoint(); |
| 59 | pt.x = s.attr('x'); |
| 60 | pt.y = s.attr('y'); |
| 61 | pt = pt.matrixTransform(s[0][0].getCTM()); |
| 62 | |
| 63 | return line([pt, pt]); |
| 64 | }); |
| 65 | |
| 66 | |
| 67 | if (data.className === 'core') { |
| 68 | d3.selectAll('.edge').classed('nodrop', true); |
| 69 | } |
| 70 | if (data.className === 'edge') { |
| 71 | d3.selectAll('.core').classed('nodrop', true); |
| 72 | d3.selectAll('.aggregation').classed('nodrop', true); |
| 73 | } |
| 74 | if (data.className === 'aggregation') { |
| 75 | d3.selectAll('.edge').classed('nodrop', true); |
| 76 | d3.selectAll('.aggregation').classed('nodrop', true); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | function mouseUpSwitch(data) { |
| 81 | if (data.mouseDown) { |
| 82 | data.mouseDown = false; |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 83 | d3.select('#topologyArea').classed('linking', false); |
Paul Greyson | e15c439 | 2013-04-09 15:05:31 -0700 | [diff] [blame] | 84 | d3.event.stopPropagation(); |
| 85 | d3.selectAll('.nodrop').classed('nodrop', false); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | function doubleClickSwitch(data) { |
| 90 | var circle = d3.select(document.getElementById(data.dpid)).select('circle'); |
| 91 | if (data.state == 'ACTIVE') { |
| 92 | var prompt = 'Deactivate ' + data.dpid + '?'; |
| 93 | if (confirm(prompt)) { |
| 94 | switchDown(data); |
| 95 | setPending(circle); |
| 96 | } |
| 97 | } else { |
| 98 | var prompt = 'Activate ' + data.dpid + '?'; |
| 99 | if (confirm(prompt)) { |
| 100 | switchUp(data); |
| 101 | setPending(circle); |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | d3.select(document.body).on('mouseup', function () { |
| 107 | function clearHighlight() { |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 108 | topology.selectAll('circle').each(function (data) { |
Paul Greyson | e15c439 | 2013-04-09 15:05:31 -0700 | [diff] [blame] | 109 | data.mouseDown = false; |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 110 | d3.select('#topologyArea').classed('linking', false); |
Paul Greyson | e15c439 | 2013-04-09 15:05:31 -0700 | [diff] [blame] | 111 | mouseOutSwitch(data); |
| 112 | }); |
| 113 | d3.select('#linkVector').remove(); |
| 114 | }; |
| 115 | |
| 116 | d3.selectAll('.nodrop').classed('nodrop', false); |
| 117 | |
| 118 | function removeLink(link) { |
| 119 | var path1 = document.getElementById(link['src-switch'] + '=>' + link['dst-switch']); |
| 120 | var path2 = document.getElementById(link['dst-switch'] + '=>' + link['src-switch']); |
| 121 | |
| 122 | if (path1) { |
| 123 | setPending(d3.select(path1)); |
| 124 | } |
| 125 | if (path2) { |
| 126 | setPending(d3.select(path2)); |
| 127 | } |
| 128 | |
| 129 | linkDown(link); |
| 130 | } |
| 131 | |
| 132 | |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 133 | var highlighted = topology.selectAll('.highlight')[0]; |
Paul Greyson | e15c439 | 2013-04-09 15:05:31 -0700 | [diff] [blame] | 134 | if (highlighted.length == 2) { |
| 135 | var s1Data = highlighted[0].__data__; |
| 136 | var s2Data = highlighted[1].__data__; |
| 137 | |
| 138 | var srcData, dstData; |
| 139 | if (s1Data.target) { |
| 140 | dstData = s1Data; |
| 141 | srcData = s2Data; |
| 142 | } else { |
| 143 | dstData = s2Data; |
| 144 | srcData = s1Data; |
| 145 | } |
| 146 | |
| 147 | if (s1Data.className == 'edge' && s2Data.className == 'edge') { |
| 148 | var prompt = 'Create flow from ' + srcData.dpid + ' to ' + dstData.dpid + '?'; |
| 149 | if (confirm(prompt)) { |
| 150 | addFlow(srcData, dstData); |
| 151 | |
| 152 | var flow = { |
| 153 | dataPath: { |
| 154 | srcPort: { |
| 155 | dpid: { |
| 156 | value: srcData.dpid |
| 157 | } |
| 158 | }, |
| 159 | dstPort: { |
| 160 | dpid: { |
| 161 | value: dstData.dpid |
| 162 | } |
| 163 | } |
| 164 | }, |
| 165 | srcDpid: srcData.dpid, |
| 166 | dstDpid: dstData.dpid, |
| 167 | createPending: true |
| 168 | }; |
| 169 | |
| 170 | selectFlow(flow); |
| 171 | |
| 172 | setTimeout(function () { |
| 173 | deselectFlowIfCreatePending(flow); |
| 174 | }, pendingTimeout); |
| 175 | } |
| 176 | } else { |
| 177 | var map = linkMap[srcData.dpid]; |
| 178 | if (map && map[dstData.dpid]) { |
| 179 | var prompt = 'Remove link between ' + srcData.dpid + ' and ' + dstData.dpid + '?'; |
| 180 | if (confirm(prompt)) { |
| 181 | removeLink(map[dstData.dpid]); |
| 182 | } |
| 183 | } else { |
| 184 | map = linkMap[dstData.dpid]; |
| 185 | if (map && map[srcData.dpid]) { |
| 186 | var prompt = 'Remove link between ' + dstData.dpid + ' and ' + srcData.dpid + '?'; |
| 187 | if (confirm(prompt)) { |
| 188 | removeLink(map[srcData.dpid]); |
| 189 | } |
| 190 | } else { |
| 191 | var prompt = 'Create link between ' + srcData.dpid + ' and ' + dstData.dpid + '?'; |
| 192 | if (confirm(prompt)) { |
| 193 | var link1 = { |
| 194 | 'src-switch': srcData.dpid, |
| 195 | 'src-port': 1, |
| 196 | 'dst-switch': dstData.dpid, |
| 197 | 'dst-port': 1, |
| 198 | pending: true |
| 199 | }; |
| 200 | pendingLinks[makeLinkKey(link1)] = link1; |
| 201 | var link2 = { |
| 202 | 'src-switch': dstData.dpid, |
| 203 | 'src-port': 1, |
| 204 | 'dst-switch': srcData.dpid, |
| 205 | 'dst-port': 1, |
| 206 | pending: true |
| 207 | }; |
| 208 | pendingLinks[makeLinkKey(link2)] = link2; |
| 209 | updateTopology(); |
| 210 | |
| 211 | linkUp(link1); |
| 212 | |
| 213 | // remove the pending links after 10s |
| 214 | setTimeout(function () { |
| 215 | delete pendingLinks[makeLinkKey(link1)]; |
| 216 | delete pendingLinks[makeLinkKey(link2)]; |
| 217 | |
| 218 | updateTopology(); |
| 219 | }, pendingTimeout); |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | clearHighlight(); |
| 226 | } else { |
| 227 | clearHighlight(); |
| 228 | } |
Paul Greyson | 3063625 | 2013-04-09 15:22:04 -0700 | [diff] [blame] | 229 | }); |
| 230 | |
| 231 | d3.select(document.body).on('mousemove', function () { |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 232 | if (!d3.select('#topologyArea').classed('linking')) { |
Paul Greyson | 3063625 | 2013-04-09 15:22:04 -0700 | [diff] [blame] | 233 | return; |
| 234 | } |
| 235 | var linkVector = document.getElementById('linkVector'); |
| 236 | if (!linkVector) { |
| 237 | return; |
| 238 | } |
| 239 | linkVector = d3.select(linkVector); |
| 240 | |
Paul Greyson | c090d14 | 2013-04-09 16:59:03 -0700 | [diff] [blame] | 241 | var highlighted = topology.selectAll('.highlight')[0]; |
Paul Greyson | 3063625 | 2013-04-09 15:22:04 -0700 | [diff] [blame] | 242 | var s1 = null, s2 = null; |
| 243 | if (highlighted.length > 1) { |
| 244 | var s1 = d3.select(highlighted[0]); |
| 245 | var s2 = d3.select(highlighted[1]); |
| 246 | |
| 247 | } else if (highlighted.length > 0) { |
| 248 | var s1 = d3.select(highlighted[0]); |
| 249 | } |
| 250 | var src = s1; |
| 251 | if (s2 && !s2.data()[0].target) { |
| 252 | src = s2; |
| 253 | } |
| 254 | if (src) { |
| 255 | linkVector.attr('d', function () { |
| 256 | var srcPt = document.querySelector('svg').createSVGPoint(); |
| 257 | srcPt.x = src.attr('x'); |
| 258 | srcPt.y = src.attr('y'); |
| 259 | srcPt = srcPt.matrixTransform(src[0][0].getCTM()); |
| 260 | |
| 261 | var svg = document.getElementById('topology'); |
| 262 | var mouse = d3.mouse(viewbox); |
| 263 | var dstPt = document.querySelector('svg').createSVGPoint(); |
| 264 | dstPt.x = mouse[0]; |
| 265 | dstPt.y = mouse[1]; |
| 266 | dstPt = dstPt.matrixTransform(viewbox.getCTM()); |
| 267 | |
| 268 | return line([srcPt, dstPt]); |
| 269 | }); |
| 270 | } |
| 271 | }); |