Toggle navigation
Toggle navigation
This project
Loading...
Sign in
홍길동
/
onos
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
Simon Hunt
2014-11-04 20:13:09 -0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
142d003abf071778a15a177127caf2d26709b72c
142d003a
1 parent
af92d127
GUI -- Starting migration of topology view to the updated framework. WIP.
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1294 additions
and
1111 deletions
web/gui/src/main/webapp/index2.html
web/gui/src/main/webapp/onos2.js
web/gui/src/main/webapp/topo2-OLD.js
web/gui/src/main/webapp/topo2.css
web/gui/src/main/webapp/topo2.js
web/gui/src/main/webapp/index2.html
View file @
142d003
...
...
@@ -70,6 +70,7 @@
<script
type=
"text/javascript"
>
var
ONOS
=
$
.
onos
({
comment
:
"configuration options"
,
startVid
:
'topo'
,
trace
:
false
});
</script>
...
...
@@ -77,12 +78,15 @@
<!-- Framework module files included here -->
<script
src=
"mast2.js"
></script>
<!-- Contributed (application) views injected here -->
<!-- TODO: replace with template marker and inject refs server-side -->
<!-- Sample views; can be dispensed with eventually -->
<script
src=
"sample2.js"
></script>
<script
src=
"sampleAlt2.js"
></script>
<script
src=
"sampleRadio.js"
></script>
<!-- Contributed (application) views injected here -->
<!-- TODO: replace with template marker and inject refs server-side -->
<script
src=
"topo2.js"
></script>
<!-- finally, build the UI-->
<script
type=
"text/javascript"
>
$
(
ONOS
.
buildUi
);
...
...
web/gui/src/main/webapp/onos2.js
View file @
142d003
...
...
@@ -25,7 +25,7 @@
var
tsI
=
new
Date
().
getTime
(),
// initialize time stamp
tsB
,
// build time stamp
mastHeight
=
36
,
// see mast2.css
default
Hash
=
'sample'
;
default
Vid
=
'sample'
;
// attach our main function to the jQuery object
...
...
@@ -35,7 +35,8 @@
navApi
;
var
defaultOptions
=
{
trace
:
false
trace
:
false
,
startVid
:
defaultVid
};
// compute runtime settings
...
...
@@ -91,7 +92,7 @@
traceFn
(
'hash'
,
hash
);
if
(
!
hash
)
{
hash
=
defaultHash
;
hash
=
settings
.
startVid
;
redo
=
true
;
}
...
...
@@ -336,10 +337,6 @@
}
var
viewInstanceMethods
=
{
toString
:
function
()
{
return
'[View: id="'
+
this
.
vid
+
'"]'
;
},
token
:
function
()
{
return
{
// attributes
...
...
@@ -350,6 +347,7 @@
// functions
width
:
this
.
width
,
height
:
this
.
height
,
uid
:
this
.
uid
,
setRadio
:
this
.
setRadio
}
},
...
...
@@ -433,6 +431,10 @@
setRadio
:
function
(
btnSet
,
cb
)
{
setRadioButtons
(
this
.
vid
,
btnSet
,
cb
);
},
uid
:
function
(
id
)
{
return
uid
(
this
,
id
);
}
// TODO: consider schedule, clearTimer, etc.
...
...
web/gui/src/main/webapp/topo2-OLD.js
0 → 100644
View file @
142d003
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
ONOS network topology viewer - PoC version 1.0
@author Simon Hunt
*/
(
function
(
onos
)
{
'use strict'
;
// configuration data
var
config
=
{
useLiveData
:
true
,
debugOn
:
false
,
debug
:
{
showNodeXY
:
false
,
showKeyHandler
:
true
},
options
:
{
layering
:
true
,
collisionPrevention
:
true
,
loadBackground
:
true
},
backgroundUrl
:
'img/us-map.png'
,
data
:
{
live
:
{
jsonUrl
:
'rs/topology/graph'
,
detailPrefix
:
'rs/topology/graph/'
,
detailSuffix
:
''
},
fake
:
{
jsonUrl
:
'json/network2.json'
,
detailPrefix
:
'json/'
,
detailSuffix
:
'.json'
}
},
iconUrl
:
{
device
:
'img/device.png'
,
host
:
'img/host.png'
,
pkt
:
'img/pkt.png'
,
opt
:
'img/opt.png'
},
mastHeight
:
36
,
force
:
{
note
:
'node.class or link.class is used to differentiate'
,
linkDistance
:
{
infra
:
200
,
host
:
40
},
linkStrength
:
{
infra
:
1.0
,
host
:
1.0
},
charge
:
{
device
:
-
800
,
host
:
-
1000
},
ticksWithoutCollisions
:
50
,
marginLR
:
20
,
marginTB
:
20
,
translate
:
function
()
{
return
'translate('
+
config
.
force
.
marginLR
+
','
+
config
.
force
.
marginTB
+
')'
;
}
},
labels
:
{
imgPad
:
16
,
padLR
:
8
,
padTB
:
6
,
marginLR
:
3
,
marginTB
:
2
,
port
:
{
gap
:
3
,
width
:
18
,
height
:
14
}
},
icons
:
{
w
:
32
,
h
:
32
,
xoff
:
-
12
,
yoff
:
-
8
},
constraints
:
{
ypos
:
{
host
:
0.05
,
switch
:
0.3
,
roadm
:
0.7
}
},
hostLinkWidth
:
1.0
,
hostRadius
:
7
,
mouseOutTimerDelayMs
:
120
};
// state variables
var
netView
=
{},
network
=
{},
selected
=
{},
highlighted
=
null
,
hovered
=
null
,
viewMode
=
'showAll'
,
portLabelsOn
=
false
;
function
debug
(
what
)
{
return
config
.
debugOn
&&
config
.
debug
[
what
];
}
function
urlData
()
{
return
config
.
data
[
config
.
useLiveData
?
'live'
:
'fake'
];
}
function
networkJsonUrl
()
{
return
urlData
().
jsonUrl
;
}
function
safeId
(
id
)
{
return
id
.
replace
(
/
[^
a-z0-9
]
/gi
,
'_'
);
}
function
detailJsonUrl
(
id
)
{
var
u
=
urlData
(),
encId
=
config
.
useLiveData
?
encodeURIComponent
(
id
)
:
safeId
(
id
);
return
u
.
detailPrefix
+
encId
+
u
.
detailSuffix
;
}
// load the topology view of the network
function
loadNetworkView
()
{
// Hey, here I am, calling something on the ONOS api:
api
.
printTime
();
resize
();
// go get our network data from the server...
var
url
=
networkJsonUrl
();
d3
.
json
(
url
,
function
(
err
,
data
)
{
if
(
err
)
{
alert
(
'Oops! Error reading JSON...\n\n'
+
'URL: '
+
url
+
'\n\n'
+
'Error: '
+
err
.
message
);
return
;
}
// console.log("here is the JSON data...");
// console.log(data);
network
.
data
=
data
;
drawNetwork
();
});
// while we wait for the data, set up the handlers...
setUpClickHandler
();
setUpRadioButtonHandler
();
setUpKeyHandler
();
$
(
window
).
on
(
'resize'
,
resize
);
}
function
setUpClickHandler
()
{
// click handler for "selectable" objects
$
(
document
).
on
(
'click'
,
'.select-object'
,
function
()
{
// when any object of class "select-object" is clicked...
var
obj
=
network
.
lookup
[
$
(
this
).
data
(
'id'
)];
if
(
obj
)
{
selectObject
(
obj
);
}
// stop propagation of event (I think) ...
return
false
;
});
}
function
setUpRadioButtonHandler
()
{
d3
.
selectAll
(
'#displayModes .radio'
).
on
(
'click'
,
function
()
{
var
id
=
d3
.
select
(
this
).
attr
(
'id'
);
if
(
id
!==
viewMode
)
{
radioButton
(
'displayModes'
,
id
);
viewMode
=
id
;
doRadioAction
(
id
);
}
});
}
function
doRadioAction
(
id
)
{
showAllLayers
();
if
(
id
===
'showPkt'
)
{
showPacketLayer
();
}
else
if
(
id
===
'showOpt'
)
{
showOpticalLayer
();
}
}
function
showAllLayers
()
{
network
.
node
.
classed
(
'inactive'
,
false
);
network
.
link
.
classed
(
'inactive'
,
false
);
d3
.
selectAll
(
'svg .port'
).
classed
(
'inactive'
,
false
);
d3
.
selectAll
(
'svg .portText'
).
classed
(
'inactive'
,
false
);
}
function
showPacketLayer
()
{
network
.
node
.
each
(
function
(
d
)
{
// deactivate nodes that are not hosts or switches
if
(
d
.
class
===
'device'
&&
d
.
type
!==
'switch'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
network
.
link
.
each
(
function
(
lnk
)
{
// deactivate infrastructure links that have opt's as endpoints
if
(
lnk
.
source
.
type
===
'roadm'
||
lnk
.
target
.
type
===
'roadm'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
// deactivate non-packet ports
d3
.
selectAll
(
'svg .optPort'
).
classed
(
'inactive'
,
true
)
}
function
showOpticalLayer
()
{
network
.
node
.
each
(
function
(
d
)
{
// deactivate nodes that are not optical devices
if
(
d
.
type
!==
'roadm'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
network
.
link
.
each
(
function
(
lnk
)
{
// deactivate infrastructure links that have opt's as endpoints
if
(
lnk
.
source
.
type
!==
'roadm'
||
lnk
.
target
.
type
!==
'roadm'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
// deactivate non-packet ports
d3
.
selectAll
(
'svg .pktPort'
).
classed
(
'inactive'
,
true
)
}
function
setUpKeyHandler
()
{
d3
.
select
(
'body'
)
.
on
(
'keydown'
,
function
()
{
processKeyEvent
();
if
(
debug
(
'showKeyHandler'
))
{
network
.
svg
.
append
(
'text'
)
.
attr
(
'x'
,
5
)
.
attr
(
'y'
,
15
)
.
style
(
'font-size'
,
'20pt'
)
.
text
(
'keyCode: '
+
d3
.
event
.
keyCode
+
' applied to : '
+
contextLabel
())
.
transition
().
duration
(
2000
)
.
style
(
'font-size'
,
'2pt'
)
.
style
(
'fill-opacity'
,
0.01
)
.
remove
();
}
});
}
function
contextLabel
()
{
return
hovered
===
null
?
"(nothing)"
:
hovered
.
id
;
}
function
radioButton
(
group
,
id
)
{
d3
.
selectAll
(
"#"
+
group
+
" .radio"
).
classed
(
"active"
,
false
);
d3
.
select
(
"#"
+
group
+
" #"
+
id
).
classed
(
"active"
,
true
);
}
function
processKeyEvent
()
{
var
code
=
d3
.
event
.
keyCode
;
switch
(
code
)
{
case
66
:
// B
toggleBackground
();
break
;
case
71
:
// G
cycleLayout
();
break
;
case
76
:
// L
cycleLabels
();
break
;
case
80
:
// P
togglePorts
();
break
;
case
85
:
// U
unpin
();
break
;
}
}
function
toggleBackground
()
{
var
bg
=
d3
.
select
(
'#bg'
),
vis
=
bg
.
style
(
'visibility'
),
newvis
=
(
vis
===
'hidden'
)
?
'visible'
:
'hidden'
;
bg
.
style
(
'visibility'
,
newvis
);
}
function
cycleLayout
()
{
config
.
options
.
layering
=
!
config
.
options
.
layering
;
network
.
force
.
resume
();
}
function
cycleLabels
()
{
console
.
log
(
'Cycle Labels - context = '
+
contextLabel
());
}
function
togglePorts
()
{
portLabelsOn
=
!
portLabelsOn
;
var
portVis
=
portLabelsOn
?
'visible'
:
'hidden'
;
d3
.
selectAll
(
'.port'
).
style
(
'visibility'
,
portVis
);
d3
.
selectAll
(
'.portText'
).
style
(
'visibility'
,
portVis
);
}
function
unpin
()
{
if
(
hovered
)
{
hovered
.
fixed
=
false
;
findNodeFromData
(
hovered
).
classed
(
'fixed'
,
false
);
network
.
force
.
resume
();
}
console
.
log
(
'Unpin - context = '
+
contextLabel
());
}
// ========================================================
function
drawNetwork
()
{
$
(
'#view'
).
empty
();
prepareNodesAndLinks
();
createLayout
();
console
.
log
(
"\n\nHere is the augmented network object..."
);
console
.
log
(
network
);
}
function
prepareNodesAndLinks
()
{
network
.
lookup
=
{};
network
.
nodes
=
[];
network
.
links
=
[];
var
nw
=
network
.
forceWidth
,
nh
=
network
.
forceHeight
;
function
yPosConstraintForNode
(
n
)
{
return
config
.
constraints
.
ypos
[
n
.
type
||
'host'
];
}
// Note that both 'devices' and 'hosts' get mapped into the nodes array
// first, the devices...
network
.
data
.
devices
.
forEach
(
function
(
n
)
{
var
ypc
=
yPosConstraintForNode
(
n
),
ix
=
Math
.
random
()
*
0.6
*
nw
+
0.2
*
nw
,
iy
=
ypc
*
nh
,
node
=
{
id
:
n
.
id
,
labels
:
n
.
labels
,
class
:
'device'
,
icon
:
'device'
,
type
:
n
.
type
,
x
:
ix
,
y
:
iy
,
constraint
:
{
weight
:
0.7
,
y
:
iy
}
};
network
.
lookup
[
n
.
id
]
=
node
;
network
.
nodes
.
push
(
node
);
});
// then, the hosts...
network
.
data
.
hosts
.
forEach
(
function
(
n
)
{
var
ypc
=
yPosConstraintForNode
(
n
),
ix
=
Math
.
random
()
*
0.6
*
nw
+
0.2
*
nw
,
iy
=
ypc
*
nh
,
node
=
{
id
:
n
.
id
,
labels
:
n
.
labels
,
class
:
'host'
,
icon
:
'host'
,
type
:
n
.
type
,
x
:
ix
,
y
:
iy
,
constraint
:
{
weight
:
0.7
,
y
:
iy
}
};
network
.
lookup
[
n
.
id
]
=
node
;
network
.
nodes
.
push
(
node
);
});
// now, process the explicit links...
network
.
data
.
links
.
forEach
(
function
(
lnk
)
{
var
src
=
network
.
lookup
[
lnk
.
src
],
dst
=
network
.
lookup
[
lnk
.
dst
],
id
=
src
.
id
+
"-"
+
dst
.
id
;
var
link
=
{
class
:
'infra'
,
id
:
id
,
type
:
lnk
.
type
,
width
:
lnk
.
linkWidth
,
source
:
src
,
srcPort
:
lnk
.
srcPort
,
target
:
dst
,
tgtPort
:
lnk
.
dstPort
,
strength
:
config
.
force
.
linkStrength
.
infra
};
network
.
links
.
push
(
link
);
});
// finally, infer host links...
network
.
data
.
hosts
.
forEach
(
function
(
n
)
{
var
src
=
network
.
lookup
[
n
.
id
],
dst
=
network
.
lookup
[
n
.
cp
.
device
],
id
=
src
.
id
+
"-"
+
dst
.
id
;
var
link
=
{
class
:
'host'
,
id
:
id
,
type
:
'hostLink'
,
width
:
config
.
hostLinkWidth
,
source
:
src
,
target
:
dst
,
strength
:
config
.
force
.
linkStrength
.
host
};
network
.
links
.
push
(
link
);
});
}
function
createLayout
()
{
var
cfg
=
config
.
force
;
network
.
force
=
d3
.
layout
.
force
()
.
size
([
network
.
forceWidth
,
network
.
forceHeight
])
.
nodes
(
network
.
nodes
)
.
links
(
network
.
links
)
.
linkStrength
(
function
(
d
)
{
return
cfg
.
linkStrength
[
d
.
class
];
})
.
linkDistance
(
function
(
d
)
{
return
cfg
.
linkDistance
[
d
.
class
];
})
.
charge
(
function
(
d
)
{
return
cfg
.
charge
[
d
.
class
];
})
.
on
(
'tick'
,
tick
);
network
.
svg
=
d3
.
select
(
'#view'
).
append
(
'svg'
)
.
attr
(
'width'
,
netView
.
width
)
.
attr
(
'height'
,
netView
.
height
)
.
append
(
'g'
)
.
attr
(
'transform'
,
config
.
force
.
translate
());
// .attr('id', 'zoomable')
// .call(d3.behavior.zoom().on("zoom", zoomRedraw));
network
.
svg
.
append
(
'svg:image'
)
.
attr
({
id
:
'bg'
,
width
:
netView
.
width
,
height
:
netView
.
height
,
'xlink:href'
:
config
.
backgroundUrl
})
.
style
(
'visibility'
,
config
.
options
.
loadBackground
?
'visible'
:
'hidden'
);
// function zoomRedraw() {
// d3.select("#zoomable").attr("transform",
// "translate(" + d3.event.translate + ")"
// + " scale(" + d3.event.scale + ")");
// }
// TODO: move glow/blur stuff to util script
var
glow
=
network
.
svg
.
append
(
'filter'
)
.
attr
(
'x'
,
'-50%'
)
.
attr
(
'y'
,
'-50%'
)
.
attr
(
'width'
,
'200%'
)
.
attr
(
'height'
,
'200%'
)
.
attr
(
'id'
,
'blue-glow'
);
glow
.
append
(
'feColorMatrix'
)
.
attr
(
'type'
,
'matrix'
)
.
attr
(
'values'
,
'0 0 0 0 0 '
+
'0 0 0 0 0 '
+
'0 0 0 0 .7 '
+
'0 0 0 1 0 '
);
glow
.
append
(
'feGaussianBlur'
)
.
attr
(
'stdDeviation'
,
3
)
.
attr
(
'result'
,
'coloredBlur'
);
glow
.
append
(
'feMerge'
).
selectAll
(
'feMergeNode'
)
.
data
([
'coloredBlur'
,
'SourceGraphic'
])
.
enter
().
append
(
'feMergeNode'
)
.
attr
(
'in'
,
String
);
// TODO: legend (and auto adjust on scroll)
// $('#view').on('scroll', function() {
//
// });
// TODO: move drag behavior into separate method.
// == define node drag behavior...
network
.
draggedThreshold
=
d3
.
scale
.
linear
()
.
domain
([
0
,
0.1
])
.
range
([
5
,
20
])
.
clamp
(
true
);
function
dragged
(
d
)
{
var
threshold
=
network
.
draggedThreshold
(
network
.
force
.
alpha
()),
dx
=
d
.
oldX
-
d
.
px
,
dy
=
d
.
oldY
-
d
.
py
;
if
(
Math
.
abs
(
dx
)
>=
threshold
||
Math
.
abs
(
dy
)
>=
threshold
)
{
d
.
dragged
=
true
;
}
return
d
.
dragged
;
}
network
.
drag
=
d3
.
behavior
.
drag
()
.
origin
(
function
(
d
)
{
return
d
;
})
.
on
(
'dragstart'
,
function
(
d
)
{
d
.
oldX
=
d
.
x
;
d
.
oldY
=
d
.
y
;
d
.
dragged
=
false
;
d
.
fixed
|=
2
;
})
.
on
(
'drag'
,
function
(
d
)
{
d
.
px
=
d3
.
event
.
x
;
d
.
py
=
d3
.
event
.
y
;
if
(
dragged
(
d
))
{
if
(
!
network
.
force
.
alpha
())
{
network
.
force
.
alpha
(.
025
);
}
}
})
.
on
(
'dragend'
,
function
(
d
)
{
if
(
!
dragged
(
d
))
{
selectObject
(
d
,
this
);
}
d
.
fixed
&=
~
6
;
// once we've finished moving, pin the node in position,
// if it is a device (not a host)
if
(
d
.
class
===
'device'
)
{
d
.
fixed
=
true
;
d3
.
select
(
this
).
classed
(
'fixed'
,
true
)
}
});
$
(
'#view'
).
on
(
'click'
,
function
(
e
)
{
if
(
!
$
(
e
.
target
).
closest
(
'.node'
).
length
)
{
deselectObject
();
}
});
// ...............................................................
// add links to the display
network
.
link
=
network
.
svg
.
append
(
'g'
).
attr
(
'id'
,
'links'
)
.
selectAll
(
'.link'
)
.
data
(
network
.
force
.
links
(),
function
(
d
)
{
return
d
.
id
})
.
enter
().
append
(
'line'
)
.
attr
(
'class'
,
function
(
d
)
{
return
'link '
+
d
.
class
});
network
.
linkSrcPort
=
network
.
svg
.
append
(
'g'
)
.
attr
({
id
:
'srcPorts'
,
class
:
'portLayer'
});
network
.
linkTgtPort
=
network
.
svg
.
append
(
'g'
)
.
attr
({
id
:
'tgtPorts'
,
class
:
'portLayer'
});
var
portVis
=
portLabelsOn
?
'visible'
:
'hidden'
,
pw
=
config
.
labels
.
port
.
width
,
ph
=
config
.
labels
.
port
.
height
;
network
.
link
.
filter
(
'.infra'
).
each
(
function
(
d
)
{
var
srcType
=
d
.
source
.
type
===
'roadm'
?
'optPort'
:
'pktPort'
,
tgtType
=
d
.
target
.
type
===
'roadm'
?
'optPort'
:
'pktPort'
;
if
(
d
.
source
.
type
)
network
.
linkSrcPort
.
append
(
'rect'
).
attr
({
id
:
'srcPort-'
+
safeId
(
d
.
id
),
class
:
'port '
+
srcType
,
width
:
pw
,
height
:
ph
,
rx
:
4
,
ry
:
4
}).
style
(
'visibility'
,
portVis
);
network
.
linkTgtPort
.
append
(
'rect'
).
attr
({
id
:
'tgtPort-'
+
safeId
(
d
.
id
),
class
:
'port '
+
tgtType
,
width
:
pw
,
height
:
ph
,
rx
:
4
,
ry
:
4
}).
style
(
'visibility'
,
portVis
);
network
.
linkSrcPort
.
append
(
'text'
).
attr
({
id
:
'srcText-'
+
safeId
(
d
.
id
),
class
:
'portText '
+
srcType
}).
text
(
d
.
srcPort
)
.
style
(
'visibility'
,
portVis
);
network
.
linkTgtPort
.
append
(
'text'
).
attr
({
id
:
'tgtText-'
+
safeId
(
d
.
id
),
class
:
'portText '
+
tgtType
}).
text
(
d
.
tgtPort
)
.
style
(
'visibility'
,
portVis
);
});
// ...............................................................
// add nodes to the display
network
.
node
=
network
.
svg
.
selectAll
(
'.node'
)
.
data
(
network
.
force
.
nodes
(),
function
(
d
)
{
return
d
.
id
})
.
enter
().
append
(
'g'
)
.
attr
(
'class'
,
function
(
d
)
{
var
cls
=
'node '
+
d
.
class
;
if
(
d
.
type
)
{
cls
+=
' '
+
d
.
type
;
}
return
cls
;
})
.
attr
(
'transform'
,
function
(
d
)
{
return
translate
(
d
.
x
,
d
.
y
);
})
.
call
(
network
.
drag
)
.
on
(
'mouseover'
,
function
(
d
)
{
// TODO: show tooltip
if
(
network
.
mouseoutTimeout
)
{
clearTimeout
(
network
.
mouseoutTimeout
);
network
.
mouseoutTimeout
=
null
;
}
hoverObject
(
d
);
})
.
on
(
'mouseout'
,
function
(
d
)
{
// TODO: hide tooltip
if
(
network
.
mouseoutTimeout
)
{
clearTimeout
(
network
.
mouseoutTimeout
);
network
.
mouseoutTimeout
=
null
;
}
network
.
mouseoutTimeout
=
setTimeout
(
function
()
{
hoverObject
(
null
);
},
config
.
mouseOutTimerDelayMs
);
});
// deal with device nodes first
network
.
nodeRect
=
network
.
node
.
filter
(
'.device'
)
.
append
(
'rect'
)
.
attr
({
rx
:
5
,
ry
:
5
,
width
:
100
,
height
:
12
});
// note that width/height are adjusted to fit the label text
// then padded, and space made for the icon.
network
.
node
.
filter
(
'.device'
).
each
(
function
(
d
)
{
var
node
=
d3
.
select
(
this
),
icon
=
iconUrl
(
d
);
node
.
append
(
'text'
)
// TODO: add label cycle behavior
.
text
(
d
.
id
)
.
attr
(
'dy'
,
'1.1em'
);
if
(
icon
)
{
var
cfg
=
config
.
icons
;
node
.
append
(
'svg:image'
)
.
attr
({
width
:
cfg
.
w
,
height
:
cfg
.
h
,
'xlink:href'
:
icon
});
// note, icon relative positioning (x,y) is done after we have
// adjusted the bounds of the rectangle...
}
// debug function to show the modelled x,y coordinates of nodes...
if
(
debug
(
'showNodeXY'
))
{
node
.
select
(
'rect'
).
attr
(
'fill-opacity'
,
0.5
);
node
.
append
(
'circle'
)
.
attr
({
class
:
'debug'
,
cx
:
0
,
cy
:
0
,
r
:
'3px'
});
}
});
// now process host nodes
network
.
nodeCircle
=
network
.
node
.
filter
(
'.host'
)
.
append
(
'circle'
)
.
attr
({
r
:
config
.
hostRadius
});
network
.
node
.
filter
(
'.host'
).
each
(
function
(
d
)
{
var
node
=
d3
.
select
(
this
),
icon
=
iconUrl
(
d
);
// debug function to show the modelled x,y coordinates of nodes...
if
(
debug
(
'showNodeXY'
))
{
node
.
select
(
'circle'
).
attr
(
'fill-opacity'
,
0.5
);
node
.
append
(
'circle'
)
.
attr
({
class
:
'debug'
,
cx
:
0
,
cy
:
0
,
r
:
'3px'
});
}
});
// this function is scheduled to happen soon after the given thread ends
setTimeout
(
function
()
{
var
lab
=
config
.
labels
,
portGap
=
lab
.
port
.
gap
,
midW
=
portGap
+
lab
.
port
.
width
/
2
,
midH
=
portGap
+
lab
.
port
.
height
/
2
;
// post process the device nodes, to pad their size to fit the
// label text and attach the icon to the right location.
network
.
node
.
filter
(
'.device'
).
each
(
function
(
d
)
{
// for every node, recompute size, padding, etc. so text fits
var
node
=
d3
.
select
(
this
),
text
=
node
.
select
(
'text'
),
box
=
adjustRectToFitText
(
node
);
// now make the computed adjustment
node
.
select
(
'rect'
)
.
attr
(
box
);
node
.
select
(
'image'
)
.
attr
(
'x'
,
box
.
x
+
config
.
icons
.
xoff
)
.
attr
(
'y'
,
box
.
y
+
config
.
icons
.
yoff
);
var
bounds
=
boundsFromBox
(
box
),
portBounds
=
{
x1
:
bounds
.
x1
-
midW
,
x2
:
bounds
.
x2
+
midW
,
y1
:
bounds
.
y1
-
midH
,
y2
:
bounds
.
y2
+
midH
};
// todo: clean up extent and edge work..
d
.
extent
=
{
left
:
bounds
.
x1
-
lab
.
marginLR
,
right
:
bounds
.
x2
+
lab
.
marginLR
,
top
:
bounds
.
y1
-
lab
.
marginTB
,
bottom
:
bounds
.
y2
+
lab
.
marginTB
};
d
.
edge
=
{
left
:
new
geo
.
LineSegment
(
bounds
.
x1
,
bounds
.
y1
,
bounds
.
x1
,
bounds
.
y2
),
right
:
new
geo
.
LineSegment
(
bounds
.
x2
,
bounds
.
y1
,
bounds
.
x2
,
bounds
.
y2
),
top
:
new
geo
.
LineSegment
(
bounds
.
x1
,
bounds
.
y1
,
bounds
.
x2
,
bounds
.
y1
),
bottom
:
new
geo
.
LineSegment
(
bounds
.
x1
,
bounds
.
y2
,
bounds
.
x2
,
bounds
.
y2
)
};
d
.
portEdge
=
{
left
:
new
geo
.
LineSegment
(
portBounds
.
x1
,
portBounds
.
y1
,
portBounds
.
x1
,
portBounds
.
y2
),
right
:
new
geo
.
LineSegment
(
portBounds
.
x2
,
portBounds
.
y1
,
portBounds
.
x2
,
portBounds
.
y2
),
top
:
new
geo
.
LineSegment
(
portBounds
.
x1
,
portBounds
.
y1
,
portBounds
.
x2
,
portBounds
.
y1
),
bottom
:
new
geo
.
LineSegment
(
portBounds
.
x1
,
portBounds
.
y2
,
portBounds
.
x2
,
portBounds
.
y2
)
};
});
network
.
numTicks
=
0
;
network
.
preventCollisions
=
false
;
network
.
force
.
start
();
for
(
var
i
=
0
;
i
<
config
.
force
.
ticksWithoutCollisions
;
i
++
)
{
network
.
force
.
tick
();
}
network
.
preventCollisions
=
true
;
$
(
'#view'
).
css
(
'visibility'
,
'visible'
);
});
// returns the newly computed bounding box of the rectangle
function
adjustRectToFitText
(
n
)
{
var
text
=
n
.
select
(
'text'
),
box
=
text
.
node
().
getBBox
(),
lab
=
config
.
labels
;
// not sure why n.data() returns an array of 1 element...
var
data
=
n
.
data
()[
0
];
text
.
attr
(
'text-anchor'
,
'middle'
)
.
attr
(
'y'
,
'-0.8em'
)
.
attr
(
'x'
,
lab
.
imgPad
/
2
)
;
// translate the bbox so that it is centered on [x,y]
box
.
x
=
-
box
.
width
/
2
;
box
.
y
=
-
box
.
height
/
2
;
// add padding
box
.
x
-=
(
lab
.
padLR
+
lab
.
imgPad
/
2
);
box
.
width
+=
lab
.
padLR
*
2
+
lab
.
imgPad
;
box
.
y
-=
lab
.
padTB
;
box
.
height
+=
lab
.
padTB
*
2
;
return
box
;
}
function
boundsFromBox
(
box
)
{
return
{
x1
:
box
.
x
,
y1
:
box
.
y
,
x2
:
box
.
x
+
box
.
width
,
y2
:
box
.
y
+
box
.
height
};
}
}
function
iconUrl
(
d
)
{
return
'img/'
+
d
.
type
+
'.png'
;
// return config.iconUrl[d.icon];
}
function
translate
(
x
,
y
)
{
return
'translate('
+
x
+
','
+
y
+
')'
;
}
// prevents collisions amongst device nodes
function
preventCollisions
()
{
var
quadtree
=
d3
.
geom
.
quadtree
(
network
.
nodes
),
hrad
=
config
.
hostRadius
;
network
.
nodes
.
forEach
(
function
(
n
)
{
var
nx1
,
nx2
,
ny1
,
ny2
;
if
(
n
.
class
===
'device'
)
{
nx1
=
n
.
x
+
n
.
extent
.
left
;
nx2
=
n
.
x
+
n
.
extent
.
right
;
ny1
=
n
.
y
+
n
.
extent
.
top
;
ny2
=
n
.
y
+
n
.
extent
.
bottom
;
}
else
{
nx1
=
n
.
x
-
hrad
;
nx2
=
n
.
x
+
hrad
;
ny1
=
n
.
y
-
hrad
;
ny2
=
n
.
y
+
hrad
;
}
quadtree
.
visit
(
function
(
quad
,
x1
,
y1
,
x2
,
y2
)
{
if
(
quad
.
point
&&
quad
.
point
!==
n
)
{
// check if the rectangles/circles intersect
var
p
=
quad
.
point
,
px1
,
px2
,
py1
,
py2
,
ix
;
if
(
p
.
class
===
'device'
)
{
px1
=
p
.
x
+
p
.
extent
.
left
;
px2
=
p
.
x
+
p
.
extent
.
right
;
py1
=
p
.
y
+
p
.
extent
.
top
;
py2
=
p
.
y
+
p
.
extent
.
bottom
;
}
else
{
px1
=
p
.
x
-
hrad
;
px2
=
p
.
x
+
hrad
;
py1
=
p
.
y
-
hrad
;
py2
=
p
.
y
+
hrad
;
}
ix
=
(
px1
<=
nx2
&&
nx1
<=
px2
&&
py1
<=
ny2
&&
ny1
<=
py2
);
if
(
ix
)
{
var
xa1
=
nx2
-
px1
,
// shift n left , p right
xa2
=
px2
-
nx1
,
// shift n right, p left
ya1
=
ny2
-
py1
,
// shift n up , p down
ya2
=
py2
-
ny1
,
// shift n down , p up
adj
=
Math
.
min
(
xa1
,
xa2
,
ya1
,
ya2
);
if
(
adj
==
xa1
)
{
n
.
x
-=
adj
/
2
;
p
.
x
+=
adj
/
2
;
}
else
if
(
adj
==
xa2
)
{
n
.
x
+=
adj
/
2
;
p
.
x
-=
adj
/
2
;
}
else
if
(
adj
==
ya1
)
{
n
.
y
-=
adj
/
2
;
p
.
y
+=
adj
/
2
;
}
else
if
(
adj
==
ya2
)
{
n
.
y
+=
adj
/
2
;
p
.
y
-=
adj
/
2
;
}
}
return
ix
;
}
});
});
}
function
tick
(
e
)
{
network
.
numTicks
++
;
if
(
config
.
options
.
layering
)
{
// adjust the y-coord of each node, based on y-pos constraints
network
.
nodes
.
forEach
(
function
(
n
)
{
var
z
=
e
.
alpha
*
n
.
constraint
.
weight
;
if
(
!
isNaN
(
n
.
constraint
.
y
))
{
n
.
y
=
(
n
.
constraint
.
y
*
z
+
n
.
y
*
(
1
-
z
));
}
});
}
if
(
config
.
options
.
collisionPrevention
&&
network
.
preventCollisions
)
{
preventCollisions
();
}
var
portHalfW
=
config
.
labels
.
port
.
width
/
2
,
portHalfH
=
config
.
labels
.
port
.
height
/
2
;
// clip visualization of links at bounds of nodes...
network
.
link
.
each
(
function
(
d
)
{
var
xs
=
d
.
source
.
x
,
ys
=
d
.
source
.
y
,
xt
=
d
.
target
.
x
,
yt
=
d
.
target
.
y
,
line
=
new
geo
.
LineSegment
(
xs
,
ys
,
xt
,
yt
),
e
,
ix
,
exs
,
eys
,
ext
,
eyt
,
pxs
,
pys
,
pxt
,
pyt
;
if
(
d
.
class
===
'host'
)
{
// no adjustment for source end of link, since hosts are dots
exs
=
xs
;
eys
=
ys
;
}
else
{
for
(
e
in
d
.
source
.
edge
)
{
ix
=
line
.
intersect
(
d
.
source
.
edge
[
e
].
offset
(
xs
,
ys
));
if
(
ix
.
in1
&&
ix
.
in2
)
{
exs
=
ix
.
x
;
eys
=
ix
.
y
;
// also pick off the port label intersection
ix
=
line
.
intersect
(
d
.
source
.
portEdge
[
e
].
offset
(
xs
,
ys
));
pxs
=
ix
.
x
;
pys
=
ix
.
y
;
break
;
}
}
}
for
(
e
in
d
.
target
.
edge
)
{
ix
=
line
.
intersect
(
d
.
target
.
edge
[
e
].
offset
(
xt
,
yt
));
if
(
ix
.
in1
&&
ix
.
in2
)
{
ext
=
ix
.
x
;
eyt
=
ix
.
y
;
// also pick off the port label intersection
ix
=
line
.
intersect
(
d
.
target
.
portEdge
[
e
].
offset
(
xt
,
yt
));
pxt
=
ix
.
x
;
pyt
=
ix
.
y
;
break
;
}
}
// adjust the endpoints of the link's line to match rectangles
var
sid
=
safeId
(
d
.
id
);
d3
.
select
(
this
)
.
attr
(
'x1'
,
exs
)
.
attr
(
'y1'
,
eys
)
.
attr
(
'x2'
,
ext
)
.
attr
(
'y2'
,
eyt
);
d3
.
select
(
'#srcPort-'
+
sid
)
.
attr
(
'x'
,
pxs
-
portHalfW
)
.
attr
(
'y'
,
pys
-
portHalfH
);
d3
.
select
(
'#tgtPort-'
+
sid
)
.
attr
(
'x'
,
pxt
-
portHalfW
)
.
attr
(
'y'
,
pyt
-
portHalfH
);
// TODO: fit label rect to size of port number.
d3
.
select
(
'#srcText-'
+
sid
)
.
attr
(
'x'
,
pxs
-
5
)
.
attr
(
'y'
,
pys
+
3
);
d3
.
select
(
'#tgtText-'
+
sid
)
.
attr
(
'x'
,
pxt
-
5
)
.
attr
(
'y'
,
pyt
+
3
);
});
// position each node by translating the node (group) by x,y
network
.
node
.
attr
(
'transform'
,
function
(
d
)
{
return
translate
(
d
.
x
,
d
.
y
);
});
}
// $('#docs-close').on('click', function() {
// deselectObject();
// return false;
// });
// $(document).on('click', '.select-object', function() {
// var obj = graph.data[$(this).data('name')];
// if (obj) {
// selectObject(obj);
// }
// return false;
// });
function
findNodeFromData
(
d
)
{
var
el
=
null
;
network
.
node
.
filter
(
'.'
+
d
.
class
).
each
(
function
(
n
)
{
if
(
n
.
id
===
d
.
id
)
{
el
=
d3
.
select
(
this
);
}
});
return
el
;
}
function
selectObject
(
obj
,
el
)
{
var
node
;
if
(
el
)
{
node
=
d3
.
select
(
el
);
}
else
{
network
.
node
.
each
(
function
(
d
)
{
if
(
d
==
obj
)
{
node
=
d3
.
select
(
el
=
this
);
}
});
}
if
(
!
node
)
return
;
if
(
node
.
classed
(
'selected'
))
{
deselectObject
();
flyinPane
(
null
);
return
;
}
deselectObject
(
false
);
selected
=
{
obj
:
obj
,
el
:
el
};
node
.
classed
(
'selected'
,
true
);
flyinPane
(
obj
);
}
function
deselectObject
(
doResize
)
{
// Review: logic of 'resize(...)' function.
if
(
doResize
||
typeof
doResize
==
'undefined'
)
{
resize
(
false
);
}
// deselect all nodes in the network...
network
.
node
.
classed
(
'selected'
,
false
);
selected
=
{};
flyinPane
(
null
);
}
function
flyinPane
(
obj
)
{
var
pane
=
d3
.
select
(
'#flyout'
),
url
;
if
(
obj
)
{
// go get details of the selected object from the server...
url
=
detailJsonUrl
(
obj
.
id
);
d3
.
json
(
url
,
function
(
err
,
data
)
{
if
(
err
)
{
alert
(
'Oops! Error reading JSON...\n\n'
+
'URL: '
+
url
+
'\n\n'
+
'Error: '
+
err
.
message
);
return
;
}
// console.log("JSON data... " + url);
// console.log(data);
displayDetails
(
data
,
pane
);
});
}
else
{
// hide pane
pane
.
transition
().
duration
(
750
)
.
style
(
'right'
,
'-320px'
)
.
style
(
'opacity'
,
0.0
);
}
}
function
displayDetails
(
data
,
pane
)
{
$
(
'#flyout'
).
empty
();
var
title
=
pane
.
append
(
"h2"
),
table
=
pane
.
append
(
"table"
),
tbody
=
table
.
append
(
"tbody"
);
$
(
'<img src="img/'
+
data
.
type
+
'.png">'
).
appendTo
(
title
);
$
(
'<span>'
).
attr
(
'class'
,
'icon'
).
text
(
data
.
id
).
appendTo
(
title
);
// TODO: consider using d3 data bind to TR/TD
data
.
propOrder
.
forEach
(
function
(
p
)
{
if
(
p
===
'-'
)
{
addSep
(
tbody
);
}
else
{
addProp
(
tbody
,
p
,
data
.
props
[
p
]);
}
});
function
addSep
(
tbody
)
{
var
tr
=
tbody
.
append
(
'tr'
);
$
(
'<hr>'
).
appendTo
(
tr
.
append
(
'td'
).
attr
(
'colspan'
,
2
));
}
function
addProp
(
tbody
,
label
,
value
)
{
var
tr
=
tbody
.
append
(
'tr'
);
tr
.
append
(
'td'
)
.
attr
(
'class'
,
'label'
)
.
text
(
label
+
' :'
);
tr
.
append
(
'td'
)
.
attr
(
'class'
,
'value'
)
.
text
(
value
);
}
// show pane
pane
.
transition
().
duration
(
750
)
.
style
(
'right'
,
'20px'
)
.
style
(
'opacity'
,
1.0
);
}
function
highlightObject
(
obj
)
{
if
(
obj
)
{
if
(
obj
!=
highlighted
)
{
// TODO set or clear "inactive" class on nodes, based on criteria
network
.
node
.
classed
(
'inactive'
,
function
(
d
)
{
// return (obj !== d &&
// d.relation(obj.id));
return
(
obj
!==
d
);
});
// TODO: same with links
network
.
link
.
classed
(
'inactive'
,
function
(
d
)
{
return
(
obj
!==
d
.
source
&&
obj
!==
d
.
target
);
});
}
highlighted
=
obj
;
}
else
{
if
(
highlighted
)
{
// clear the inactive flag (no longer suppressed visually)
network
.
node
.
classed
(
'inactive'
,
false
);
network
.
link
.
classed
(
'inactive'
,
false
);
}
highlighted
=
null
;
}
}
function
hoverObject
(
obj
)
{
if
(
obj
)
{
hovered
=
obj
;
}
else
{
if
(
hovered
)
{
hovered
=
null
;
}
}
}
function
resize
()
{
netView
.
height
=
window
.
innerHeight
-
config
.
mastHeight
;
netView
.
width
=
window
.
innerWidth
;
$
(
'#view'
)
.
css
(
'height'
,
netView
.
height
+
'px'
)
.
css
(
'width'
,
netView
.
width
+
'px'
);
network
.
forceWidth
=
netView
.
width
-
config
.
force
.
marginLR
;
network
.
forceHeight
=
netView
.
height
-
config
.
force
.
marginTB
;
}
// ======================================================================
// register with the UI framework
onos
.
ui
.
addView
(
'topo'
,
{
load
:
loadNetworkView
});
}(
ONOS
));
web/gui/src/main/webapp/topo2.css
View file @
142d003
...
...
@@ -20,3 +20,7 @@
@author Simon Hunt
*/
svg
#topo-bg
{
opacity
:
0.5
;
}
...
...
web/gui/src/main/webapp/topo2.js
View file @
142d003
...
...
@@ -15,7 +15,7 @@
*/
/*
ONOS network topology viewer -
PoC version 1.0
ONOS network topology viewer -
version 1.1
@author Simon Hunt
*/
...
...
@@ -25,7 +25,7 @@
// configuration data
var
config
=
{
useLiveData
:
tru
e
,
useLiveData
:
fals
e
,
debugOn
:
false
,
debug
:
{
showNodeXY
:
false
,
...
...
@@ -34,7 +34,7 @@
options
:
{
layering
:
true
,
collisionPrevention
:
true
,
load
Background
:
true
show
Background
:
true
},
backgroundUrl
:
'img/us-map.png'
,
data
:
{
...
...
@@ -55,22 +55,7 @@
pkt
:
'img/pkt.png'
,
opt
:
'img/opt.png'
},
mastHeight
:
36
,
force
:
{
note
:
'node.class or link.class is used to differentiate'
,
linkDistance
:
{
infra
:
200
,
host
:
40
},
linkStrength
:
{
infra
:
1.0
,
host
:
1.0
},
charge
:
{
device
:
-
800
,
host
:
-
1000
},
ticksWithoutCollisions
:
50
,
marginLR
:
20
,
marginTB
:
20
,
translate
:
function
()
{
...
...
@@ -78,39 +63,19 @@
config
.
force
.
marginLR
+
','
+
config
.
force
.
marginTB
+
')'
;
}
},
labels
:
{
imgPad
:
16
,
padLR
:
8
,
padTB
:
6
,
marginLR
:
3
,
marginTB
:
2
,
port
:
{
gap
:
3
,
width
:
18
,
height
:
14
}
},
icons
:
{
w
:
32
,
h
:
32
,
xoff
:
-
12
,
yoff
:
-
8
},
constraints
:
{
ypos
:
{
host
:
0.05
,
switch
:
0.3
,
roadm
:
0.7
}
},
hostLinkWidth
:
1.0
,
hostRadius
:
7
,
mouseOutTimerDelayMs
:
120
};
// radio buttons
var
btnSet
=
[
{
id
:
'showAll'
,
text
:
'All Layers'
},
{
id
:
'showPkt'
,
text
:
'Packet Only'
},
{
id
:
'showOpt'
,
text
:
'Optical Only'
}
];
// state variables
var
view
=
{},
var
svg
,
bgImg
,
network
=
{},
selected
=
{},
highlighted
=
null
,
...
...
@@ -119,84 +84,19 @@
portLabelsOn
=
false
;
function
debug
(
what
)
{
return
config
.
debugOn
&&
config
.
debug
[
what
];
}
function
urlData
()
{
return
config
.
data
[
config
.
useLiveData
?
'live'
:
'fake'
];
}
function
networkJsonUrl
()
{
return
urlData
().
jsonUrl
;
}
function
safeId
(
id
)
{
return
id
.
replace
(
/
[^
a-z0-9
]
/gi
,
'_'
);
}
function
detailJsonUrl
(
id
)
{
var
u
=
urlData
(),
encId
=
config
.
useLiveData
?
encodeURIComponent
(
id
)
:
safeId
(
id
);
return
u
.
detailPrefix
+
encId
+
u
.
detailSuffix
;
}
// load the topology view of the network
function
loadNetworkView
()
{
// Hey, here I am, calling something on the ONOS api:
api
.
printTime
();
resize
();
// go get our network data from the server...
var
url
=
networkJsonUrl
();
d3
.
json
(
url
,
function
(
err
,
data
)
{
if
(
err
)
{
alert
(
'Oops! Error reading JSON...\n\n'
+
'URL: '
+
url
+
'\n\n'
+
'Error: '
+
err
.
message
);
return
;
}
// console.log("here is the JSON data...");
// console.log(data);
network
.
data
=
data
;
drawNetwork
();
});
// while we wait for the data, set up the handlers...
setUpClickHandler
();
setUpRadioButtonHandler
();
setUpKeyHandler
();
$
(
window
).
on
(
'resize'
,
resize
);
}
// ==============================
// Private functions
function
setUpClickHandler
()
{
// click handler for "selectable" objects
$
(
document
).
on
(
'click'
,
'.select-object'
,
function
()
{
// when any object of class "select-object" is clicked...
var
obj
=
network
.
lookup
[
$
(
this
).
data
(
'id'
)];
if
(
obj
)
{
selectObject
(
obj
);
}
// stop propagation of event (I think) ...
return
false
;
// set the size of the SVG layer (or other element) to that of the view
function
setSize
(
view
,
el
)
{
var
thing
=
el
||
svg
;
thing
.
attr
({
width
:
view
.
width
(),
height
:
view
.
height
()
});
}
function
setUpRadioButtonHandler
()
{
d3
.
selectAll
(
'#displayModes .radio'
).
on
(
'click'
,
function
()
{
var
id
=
d3
.
select
(
this
).
attr
(
'id'
);
if
(
id
!==
viewMode
)
{
radioButton
(
'displayModes'
,
id
);
viewMode
=
id
;
doRadioAction
(
id
);
}
});
}
function
doRadioAction
(
id
)
{
function
doRadio
(
view
,
id
)
{
showAllLayers
();
if
(
id
===
'showPkt'
)
{
showPacketLayer
();
...
...
@@ -206,1014 +106,68 @@
}
function
showAllLayers
()
{
network
.
node
.
classed
(
'inactive'
,
false
);
network
.
link
.
classed
(
'inactive'
,
false
);
d3
.
selectAll
(
'svg .port'
).
classed
(
'inactive'
,
false
)
d3
.
selectAll
(
'svg .portText'
).
classed
(
'inactive'
,
false
)
// network.node.classed('inactive', false);
// network.link.classed('inactive', false);
// d3.selectAll('svg .port').classed('inactive', false);
// d3.selectAll('svg .portText').classed('inactive', false);
alert
(
'show all layers'
);
}
function
showPacketLayer
()
{
network
.
node
.
each
(
function
(
d
)
{
// deactivate nodes that are not hosts or switches
if
(
d
.
class
===
'device'
&&
d
.
type
!==
'switch'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
network
.
link
.
each
(
function
(
lnk
)
{
// deactivate infrastructure links that have opt's as endpoints
if
(
lnk
.
source
.
type
===
'roadm'
||
lnk
.
target
.
type
===
'roadm'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
// deactivate non-packet ports
d3
.
selectAll
(
'svg .optPort'
).
classed
(
'inactive'
,
true
)
alert
(
'show packet layer'
);
}
function
showOpticalLayer
()
{
network
.
node
.
each
(
function
(
d
)
{
// deactivate nodes that are not optical devices
if
(
d
.
type
!==
'roadm'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
network
.
link
.
each
(
function
(
lnk
)
{
// deactivate infrastructure links that have opt's as endpoints
if
(
lnk
.
source
.
type
!==
'roadm'
||
lnk
.
target
.
type
!==
'roadm'
)
{
d3
.
select
(
this
).
classed
(
'inactive'
,
true
);
}
});
// deactivate non-packet ports
d3
.
selectAll
(
'svg .pktPort'
).
classed
(
'inactive'
,
true
)
}
function
setUpKeyHandler
()
{
d3
.
select
(
'body'
)
.
on
(
'keydown'
,
function
()
{
processKeyEvent
();
if
(
debug
(
'showKeyHandler'
))
{
network
.
svg
.
append
(
'text'
)
.
attr
(
'x'
,
5
)
.
attr
(
'y'
,
15
)
.
style
(
'font-size'
,
'20pt'
)
.
text
(
'keyCode: '
+
d3
.
event
.
keyCode
+
' applied to : '
+
contextLabel
())
.
transition
().
duration
(
2000
)
.
style
(
'font-size'
,
'2pt'
)
.
style
(
'fill-opacity'
,
0.01
)
.
remove
();
}
});
}
function
contextLabel
()
{
return
hovered
===
null
?
"(nothing)"
:
hovered
.
id
;
}
function
radioButton
(
group
,
id
)
{
d3
.
selectAll
(
"#"
+
group
+
" .radio"
).
classed
(
"active"
,
false
);
d3
.
select
(
"#"
+
group
+
" #"
+
id
).
classed
(
"active"
,
true
);
}
function
processKeyEvent
()
{
var
code
=
d3
.
event
.
keyCode
;
switch
(
code
)
{
case
66
:
// B
toggleBackground
();
break
;
case
71
:
// G
cycleLayout
();
break
;
case
76
:
// L
cycleLabels
();
break
;
case
80
:
// P
togglePorts
();
break
;
case
85
:
// U
unpin
();
break
;
}
}
function
toggleBackground
()
{
var
bg
=
d3
.
select
(
'#bg'
),
vis
=
bg
.
style
(
'visibility'
),
newvis
=
(
vis
===
'hidden'
)
?
'visible'
:
'hidden'
;
bg
.
style
(
'visibility'
,
newvis
);
}
function
cycleLayout
()
{
config
.
options
.
layering
=
!
config
.
options
.
layering
;
network
.
force
.
resume
();
}
function
cycleLabels
()
{
console
.
log
(
'Cycle Labels - context = '
+
contextLabel
());
}
function
togglePorts
()
{
portLabelsOn
=
!
portLabelsOn
;
var
portVis
=
portLabelsOn
?
'visible'
:
'hidden'
;
d3
.
selectAll
(
'.port'
).
style
(
'visibility'
,
portVis
);
d3
.
selectAll
(
'.portText'
).
style
(
'visibility'
,
portVis
);
}
function
unpin
()
{
if
(
hovered
)
{
hovered
.
fixed
=
false
;
findNodeFromData
(
hovered
).
classed
(
'fixed'
,
false
);
network
.
force
.
resume
();
}
console
.
log
(
'Unpin - context = '
+
contextLabel
());
}
// ========================================================
function
drawNetwork
()
{
$
(
'#view'
).
empty
();
prepareNodesAndLinks
();
createLayout
();
console
.
log
(
"\n\nHere is the augmented network object..."
);
console
.
log
(
network
);
}
function
prepareNodesAndLinks
()
{
network
.
lookup
=
{};
network
.
nodes
=
[];
network
.
links
=
[];
var
nw
=
network
.
forceWidth
,
nh
=
network
.
forceHeight
;
function
yPosConstraintForNode
(
n
)
{
return
config
.
constraints
.
ypos
[
n
.
type
||
'host'
];
alert
(
'show optical layer'
);
}
// Note that both 'devices' and 'hosts' get mapped into the nodes array
// ==============================
// View life-cycle callbacks
// first, the devices...
network
.
data
.
devices
.
forEach
(
function
(
n
)
{
var
ypc
=
yPosConstraintForNode
(
n
),
ix
=
Math
.
random
()
*
0.6
*
nw
+
0.2
*
nw
,
iy
=
ypc
*
nh
,
node
=
{
id
:
n
.
id
,
labels
:
n
.
labels
,
class
:
'device'
,
icon
:
'device'
,
type
:
n
.
type
,
x
:
ix
,
y
:
iy
,
constraint
:
{
weight
:
0.7
,
y
:
iy
}
};
network
.
lookup
[
n
.
id
]
=
node
;
network
.
nodes
.
push
(
node
);
});
// then, the hosts...
network
.
data
.
hosts
.
forEach
(
function
(
n
)
{
var
ypc
=
yPosConstraintForNode
(
n
),
ix
=
Math
.
random
()
*
0.6
*
nw
+
0.2
*
nw
,
iy
=
ypc
*
nh
,
node
=
{
id
:
n
.
id
,
labels
:
n
.
labels
,
class
:
'host'
,
icon
:
'host'
,
type
:
n
.
type
,
x
:
ix
,
y
:
iy
,
constraint
:
{
weight
:
0.7
,
y
:
iy
}
};
network
.
lookup
[
n
.
id
]
=
node
;
network
.
nodes
.
push
(
node
);
});
// now, process the explicit links...
network
.
data
.
links
.
forEach
(
function
(
lnk
)
{
var
src
=
network
.
lookup
[
lnk
.
src
],
dst
=
network
.
lookup
[
lnk
.
dst
],
id
=
src
.
id
+
"-"
+
dst
.
id
;
var
link
=
{
class
:
'infra'
,
id
:
id
,
type
:
lnk
.
type
,
width
:
lnk
.
linkWidth
,
source
:
src
,
srcPort
:
lnk
.
srcPort
,
target
:
dst
,
tgtPort
:
lnk
.
dstPort
,
strength
:
config
.
force
.
linkStrength
.
infra
};
network
.
links
.
push
(
link
);
});
// finally, infer host links...
network
.
data
.
hosts
.
forEach
(
function
(
n
)
{
var
src
=
network
.
lookup
[
n
.
id
],
dst
=
network
.
lookup
[
n
.
cp
.
device
],
id
=
src
.
id
+
"-"
+
dst
.
id
;
var
link
=
{
class
:
'host'
,
id
:
id
,
type
:
'hostLink'
,
width
:
config
.
hostLinkWidth
,
source
:
src
,
target
:
dst
,
strength
:
config
.
force
.
linkStrength
.
host
};
network
.
links
.
push
(
link
);
});
}
function
createLayout
()
{
var
cfg
=
config
.
force
;
function
preload
(
view
,
ctx
)
{
var
w
=
view
.
width
(),
h
=
view
.
height
(),
idBg
=
view
.
uid
(
'bg'
),
showBg
=
config
.
options
.
showBackground
?
'visible'
:
'hidden'
;
network
.
force
=
d3
.
layout
.
force
()
.
size
([
network
.
forceWidth
,
network
.
forceHeight
])
.
nodes
(
network
.
nodes
)
.
links
(
network
.
links
)
.
linkStrength
(
function
(
d
)
{
return
cfg
.
linkStrength
[
d
.
class
];
})
.
linkDistance
(
function
(
d
)
{
return
cfg
.
linkDistance
[
d
.
class
];
})
.
charge
(
function
(
d
)
{
return
cfg
.
charge
[
d
.
class
];
})
.
on
(
'tick'
,
tick
);
network
.
svg
=
d3
.
select
(
'#view'
).
append
(
'svg'
)
.
attr
(
'width'
,
view
.
width
)
.
attr
(
'height'
,
view
.
height
)
.
append
(
'g'
)
// NOTE: view.$div is a D3 selection of the view's div
svg
=
view
.
$div
.
append
(
'svg'
);
setSize
(
view
);
svg
.
append
(
'g'
)
.
attr
(
'transform'
,
config
.
force
.
translate
());
// .attr('id', 'zoomable')
// .call(d3.behavior.zoom().on("zoom", zoomRedraw));
network
.
svg
.
append
(
'svg:image'
)
// load the background image
bgImg
=
svg
.
append
(
'svg:image'
)
.
attr
({
id
:
'bg'
,
width
:
view
.
width
,
height
:
view
.
height
,
id
:
idBg
,
width
:
w
,
height
:
h
,
'xlink:href'
:
config
.
backgroundUrl
})
.
style
(
'visibility'
,
config
.
options
.
loadBackground
?
'visible'
:
'hidden'
);
// function zoomRedraw() {
// d3.select("#zoomable").attr("transform",
// "translate(" + d3.event.translate + ")"
// + " scale(" + d3.event.scale + ")");
// }
// TODO: move glow/blur stuff to util script
var
glow
=
network
.
svg
.
append
(
'filter'
)
.
attr
(
'x'
,
'-50%'
)
.
attr
(
'y'
,
'-50%'
)
.
attr
(
'width'
,
'200%'
)
.
attr
(
'height'
,
'200%'
)
.
attr
(
'id'
,
'blue-glow'
);
glow
.
append
(
'feColorMatrix'
)
.
attr
(
'type'
,
'matrix'
)
.
attr
(
'values'
,
'0 0 0 0 0 '
+
'0 0 0 0 0 '
+
'0 0 0 0 .7 '
+
'0 0 0 1 0 '
);
glow
.
append
(
'feGaussianBlur'
)
.
attr
(
'stdDeviation'
,
3
)
.
attr
(
'result'
,
'coloredBlur'
);
glow
.
append
(
'feMerge'
).
selectAll
(
'feMergeNode'
)
.
data
([
'coloredBlur'
,
'SourceGraphic'
])
.
enter
().
append
(
'feMergeNode'
)
.
attr
(
'in'
,
String
);
// TODO: legend (and auto adjust on scroll)
// $('#view').on('scroll', function() {
//
// });
// TODO: move drag behavior into separate method.
// == define node drag behavior...
network
.
draggedThreshold
=
d3
.
scale
.
linear
()
.
domain
([
0
,
0.1
])
.
range
([
5
,
20
])
.
clamp
(
true
);
function
dragged
(
d
)
{
var
threshold
=
network
.
draggedThreshold
(
network
.
force
.
alpha
()),
dx
=
d
.
oldX
-
d
.
px
,
dy
=
d
.
oldY
-
d
.
py
;
if
(
Math
.
abs
(
dx
)
>=
threshold
||
Math
.
abs
(
dy
)
>=
threshold
)
{
d
.
dragged
=
true
;
}
return
d
.
dragged
;
}
network
.
drag
=
d3
.
behavior
.
drag
()
.
origin
(
function
(
d
)
{
return
d
;
})
.
on
(
'dragstart'
,
function
(
d
)
{
d
.
oldX
=
d
.
x
;
d
.
oldY
=
d
.
y
;
d
.
dragged
=
false
;
d
.
fixed
|=
2
;
})
.
on
(
'drag'
,
function
(
d
)
{
d
.
px
=
d3
.
event
.
x
;
d
.
py
=
d3
.
event
.
y
;
if
(
dragged
(
d
))
{
if
(
!
network
.
force
.
alpha
())
{
network
.
force
.
alpha
(.
025
);
}
}
})
.
on
(
'dragend'
,
function
(
d
)
{
if
(
!
dragged
(
d
))
{
selectObject
(
d
,
this
);
}
d
.
fixed
&=
~
6
;
// once we've finished moving, pin the node in position,
// if it is a device (not a host)
if
(
d
.
class
===
'device'
)
{
d
.
fixed
=
true
;
d3
.
select
(
this
).
classed
(
'fixed'
,
true
)
}
});
$
(
'#view'
).
on
(
'click'
,
function
(
e
)
{
if
(
!
$
(
e
.
target
).
closest
(
'.node'
).
length
)
{
deselectObject
();
}
});
// ...............................................................
// add links to the display
network
.
link
=
network
.
svg
.
append
(
'g'
).
attr
(
'id'
,
'links'
)
.
selectAll
(
'.link'
)
.
data
(
network
.
force
.
links
(),
function
(
d
)
{
return
d
.
id
})
.
enter
().
append
(
'line'
)
.
attr
(
'class'
,
function
(
d
)
{
return
'link '
+
d
.
class
});
network
.
linkSrcPort
=
network
.
svg
.
append
(
'g'
)
.
attr
({
id
:
'srcPorts'
,
class
:
'portLayer'
});
network
.
linkTgtPort
=
network
.
svg
.
append
(
'g'
)
.
attr
({
id
:
'tgtPorts'
,
class
:
'portLayer'
});
var
portVis
=
portLabelsOn
?
'visible'
:
'hidden'
,
pw
=
config
.
labels
.
port
.
width
,
ph
=
config
.
labels
.
port
.
height
;
network
.
link
.
filter
(
'.infra'
).
each
(
function
(
d
)
{
var
srcType
=
d
.
source
.
type
===
'roadm'
?
'optPort'
:
'pktPort'
,
tgtType
=
d
.
target
.
type
===
'roadm'
?
'optPort'
:
'pktPort'
;
if
(
d
.
source
.
type
)
network
.
linkSrcPort
.
append
(
'rect'
).
attr
({
id
:
'srcPort-'
+
safeId
(
d
.
id
),
class
:
'port '
+
srcType
,
width
:
pw
,
height
:
ph
,
rx
:
4
,
ry
:
4
}).
style
(
'visibility'
,
portVis
);
network
.
linkTgtPort
.
append
(
'rect'
).
attr
({
id
:
'tgtPort-'
+
safeId
(
d
.
id
),
class
:
'port '
+
tgtType
,
width
:
pw
,
height
:
ph
,
rx
:
4
,
ry
:
4
}).
style
(
'visibility'
,
portVis
);
network
.
linkSrcPort
.
append
(
'text'
).
attr
({
id
:
'srcText-'
+
safeId
(
d
.
id
),
class
:
'portText '
+
srcType
}).
text
(
d
.
srcPort
)
.
style
(
'visibility'
,
portVis
);
network
.
linkTgtPort
.
append
(
'text'
).
attr
({
id
:
'tgtText-'
+
safeId
(
d
.
id
),
class
:
'portText '
+
tgtType
}).
text
(
d
.
tgtPort
)
.
style
(
'visibility'
,
portVis
);
.
style
({
visibility
:
showBg
});
// ...............................................................
// add nodes to the display
network
.
node
=
network
.
svg
.
selectAll
(
'.node'
)
.
data
(
network
.
force
.
nodes
(),
function
(
d
)
{
return
d
.
id
})
.
enter
().
append
(
'g'
)
.
attr
(
'class'
,
function
(
d
)
{
var
cls
=
'node '
+
d
.
class
;
if
(
d
.
type
)
{
cls
+=
' '
+
d
.
type
;
}
return
cls
;
})
.
attr
(
'transform'
,
function
(
d
)
{
return
translate
(
d
.
x
,
d
.
y
);
})
.
call
(
network
.
drag
)
.
on
(
'mouseover'
,
function
(
d
)
{
// TODO: show tooltip
if
(
network
.
mouseoutTimeout
)
{
clearTimeout
(
network
.
mouseoutTimeout
);
network
.
mouseoutTimeout
=
null
;
}
hoverObject
(
d
);
})
.
on
(
'mouseout'
,
function
(
d
)
{
// TODO: hide tooltip
if
(
network
.
mouseoutTimeout
)
{
clearTimeout
(
network
.
mouseoutTimeout
);
network
.
mouseoutTimeout
=
null
;
}
network
.
mouseoutTimeout
=
setTimeout
(
function
()
{
hoverObject
(
null
);
},
config
.
mouseOutTimerDelayMs
);
});
// deal with device nodes first
network
.
nodeRect
=
network
.
node
.
filter
(
'.device'
)
.
append
(
'rect'
)
.
attr
({
rx
:
5
,
ry
:
5
,
width
:
100
,
height
:
12
});
// note that width/height are adjusted to fit the label text
// then padded, and space made for the icon.
function
load
(
view
,
ctx
)
{
view
.
setRadio
(
btnSet
,
doRadio
);
network
.
node
.
filter
(
'.device'
).
each
(
function
(
d
)
{
var
node
=
d3
.
select
(
this
),
icon
=
iconUrl
(
d
);
node
.
append
(
'text'
)
// TODO: add label cycle behavior
.
text
(
d
.
id
)
.
attr
(
'dy'
,
'1.1em'
);
if
(
icon
)
{
var
cfg
=
config
.
icons
;
node
.
append
(
'svg:image'
)
.
attr
({
width
:
cfg
.
w
,
height
:
cfg
.
h
,
'xlink:href'
:
icon
});
// note, icon relative positioning (x,y) is done after we have
// adjusted the bounds of the rectangle...
}
// debug function to show the modelled x,y coordinates of nodes...
if
(
debug
(
'showNodeXY'
))
{
node
.
select
(
'rect'
).
attr
(
'fill-opacity'
,
0.5
);
node
.
append
(
'circle'
)
.
attr
({
class
:
'debug'
,
cx
:
0
,
cy
:
0
,
r
:
'3px'
});
function
resize
(
view
,
ctx
)
{
setSize
(
view
);
setSize
(
view
,
bgImg
);
}
});
// now process host nodes
network
.
nodeCircle
=
network
.
node
.
filter
(
'.host'
)
.
append
(
'circle'
)
.
attr
({
r
:
config
.
hostRadius
});
network
.
node
.
filter
(
'.host'
).
each
(
function
(
d
)
{
var
node
=
d3
.
select
(
this
),
icon
=
iconUrl
(
d
);
// debug function to show the modelled x,y coordinates of nodes...
if
(
debug
(
'showNodeXY'
))
{
node
.
select
(
'circle'
).
attr
(
'fill-opacity'
,
0.5
);
node
.
append
(
'circle'
)
.
attr
({
class
:
'debug'
,
cx
:
0
,
cy
:
0
,
r
:
'3px'
});
}
});
// this function is scheduled to happen soon after the given thread ends
setTimeout
(
function
()
{
var
lab
=
config
.
labels
,
portGap
=
lab
.
port
.
gap
,
midW
=
portGap
+
lab
.
port
.
width
/
2
,
midH
=
portGap
+
lab
.
port
.
height
/
2
;
// post process the device nodes, to pad their size to fit the
// label text and attach the icon to the right location.
network
.
node
.
filter
(
'.device'
).
each
(
function
(
d
)
{
// for every node, recompute size, padding, etc. so text fits
var
node
=
d3
.
select
(
this
),
text
=
node
.
select
(
'text'
),
box
=
adjustRectToFitText
(
node
);
// now make the computed adjustment
node
.
select
(
'rect'
)
.
attr
(
box
);
node
.
select
(
'image'
)
.
attr
(
'x'
,
box
.
x
+
config
.
icons
.
xoff
)
.
attr
(
'y'
,
box
.
y
+
config
.
icons
.
yoff
);
var
bounds
=
boundsFromBox
(
box
),
portBounds
=
{
x1
:
bounds
.
x1
-
midW
,
x2
:
bounds
.
x2
+
midW
,
y1
:
bounds
.
y1
-
midH
,
y2
:
bounds
.
y2
+
midH
};
// todo: clean up extent and edge work..
d
.
extent
=
{
left
:
bounds
.
x1
-
lab
.
marginLR
,
right
:
bounds
.
x2
+
lab
.
marginLR
,
top
:
bounds
.
y1
-
lab
.
marginTB
,
bottom
:
bounds
.
y2
+
lab
.
marginTB
};
d
.
edge
=
{
left
:
new
geo
.
LineSegment
(
bounds
.
x1
,
bounds
.
y1
,
bounds
.
x1
,
bounds
.
y2
),
right
:
new
geo
.
LineSegment
(
bounds
.
x2
,
bounds
.
y1
,
bounds
.
x2
,
bounds
.
y2
),
top
:
new
geo
.
LineSegment
(
bounds
.
x1
,
bounds
.
y1
,
bounds
.
x2
,
bounds
.
y1
),
bottom
:
new
geo
.
LineSegment
(
bounds
.
x1
,
bounds
.
y2
,
bounds
.
x2
,
bounds
.
y2
)
};
d
.
portEdge
=
{
left
:
new
geo
.
LineSegment
(
portBounds
.
x1
,
portBounds
.
y1
,
portBounds
.
x1
,
portBounds
.
y2
),
right
:
new
geo
.
LineSegment
(
portBounds
.
x2
,
portBounds
.
y1
,
portBounds
.
x2
,
portBounds
.
y2
),
top
:
new
geo
.
LineSegment
(
portBounds
.
x1
,
portBounds
.
y1
,
portBounds
.
x2
,
portBounds
.
y1
),
bottom
:
new
geo
.
LineSegment
(
portBounds
.
x1
,
portBounds
.
y2
,
portBounds
.
x2
,
portBounds
.
y2
)
};
});
network
.
numTicks
=
0
;
network
.
preventCollisions
=
false
;
network
.
force
.
start
();
for
(
var
i
=
0
;
i
<
config
.
force
.
ticksWithoutCollisions
;
i
++
)
{
network
.
force
.
tick
();
}
network
.
preventCollisions
=
true
;
$
(
'#view'
).
css
(
'visibility'
,
'visible'
);
});
// returns the newly computed bounding box of the rectangle
function
adjustRectToFitText
(
n
)
{
var
text
=
n
.
select
(
'text'
),
box
=
text
.
node
().
getBBox
(),
lab
=
config
.
labels
;
// not sure why n.data() returns an array of 1 element...
var
data
=
n
.
data
()[
0
];
text
.
attr
(
'text-anchor'
,
'middle'
)
.
attr
(
'y'
,
'-0.8em'
)
.
attr
(
'x'
,
lab
.
imgPad
/
2
)
;
// translate the bbox so that it is centered on [x,y]
box
.
x
=
-
box
.
width
/
2
;
box
.
y
=
-
box
.
height
/
2
;
// add padding
box
.
x
-=
(
lab
.
padLR
+
lab
.
imgPad
/
2
);
box
.
width
+=
lab
.
padLR
*
2
+
lab
.
imgPad
;
box
.
y
-=
lab
.
padTB
;
box
.
height
+=
lab
.
padTB
*
2
;
return
box
;
}
function
boundsFromBox
(
box
)
{
return
{
x1
:
box
.
x
,
y1
:
box
.
y
,
x2
:
box
.
x
+
box
.
width
,
y2
:
box
.
y
+
box
.
height
};
}
}
function
iconUrl
(
d
)
{
return
'img/'
+
d
.
type
+
'.png'
;
// return config.iconUrl[d.icon];
}
function
translate
(
x
,
y
)
{
return
'translate('
+
x
+
','
+
y
+
')'
;
}
// prevents collisions amongst device nodes
function
preventCollisions
()
{
var
quadtree
=
d3
.
geom
.
quadtree
(
network
.
nodes
),
hrad
=
config
.
hostRadius
;
network
.
nodes
.
forEach
(
function
(
n
)
{
var
nx1
,
nx2
,
ny1
,
ny2
;
if
(
n
.
class
===
'device'
)
{
nx1
=
n
.
x
+
n
.
extent
.
left
;
nx2
=
n
.
x
+
n
.
extent
.
right
;
ny1
=
n
.
y
+
n
.
extent
.
top
;
ny2
=
n
.
y
+
n
.
extent
.
bottom
;
}
else
{
nx1
=
n
.
x
-
hrad
;
nx2
=
n
.
x
+
hrad
;
ny1
=
n
.
y
-
hrad
;
ny2
=
n
.
y
+
hrad
;
}
quadtree
.
visit
(
function
(
quad
,
x1
,
y1
,
x2
,
y2
)
{
if
(
quad
.
point
&&
quad
.
point
!==
n
)
{
// check if the rectangles/circles intersect
var
p
=
quad
.
point
,
px1
,
px2
,
py1
,
py2
,
ix
;
if
(
p
.
class
===
'device'
)
{
px1
=
p
.
x
+
p
.
extent
.
left
;
px2
=
p
.
x
+
p
.
extent
.
right
;
py1
=
p
.
y
+
p
.
extent
.
top
;
py2
=
p
.
y
+
p
.
extent
.
bottom
;
}
else
{
px1
=
p
.
x
-
hrad
;
px2
=
p
.
x
+
hrad
;
py1
=
p
.
y
-
hrad
;
py2
=
p
.
y
+
hrad
;
}
ix
=
(
px1
<=
nx2
&&
nx1
<=
px2
&&
py1
<=
ny2
&&
ny1
<=
py2
);
if
(
ix
)
{
var
xa1
=
nx2
-
px1
,
// shift n left , p right
xa2
=
px2
-
nx1
,
// shift n right, p left
ya1
=
ny2
-
py1
,
// shift n up , p down
ya2
=
py2
-
ny1
,
// shift n down , p up
adj
=
Math
.
min
(
xa1
,
xa2
,
ya1
,
ya2
);
if
(
adj
==
xa1
)
{
n
.
x
-=
adj
/
2
;
p
.
x
+=
adj
/
2
;
}
else
if
(
adj
==
xa2
)
{
n
.
x
+=
adj
/
2
;
p
.
x
-=
adj
/
2
;
}
else
if
(
adj
==
ya1
)
{
n
.
y
-=
adj
/
2
;
p
.
y
+=
adj
/
2
;
}
else
if
(
adj
==
ya2
)
{
n
.
y
+=
adj
/
2
;
p
.
y
-=
adj
/
2
;
}
}
return
ix
;
}
});
});
}
function
tick
(
e
)
{
network
.
numTicks
++
;
if
(
config
.
options
.
layering
)
{
// adjust the y-coord of each node, based on y-pos constraints
network
.
nodes
.
forEach
(
function
(
n
)
{
var
z
=
e
.
alpha
*
n
.
constraint
.
weight
;
if
(
!
isNaN
(
n
.
constraint
.
y
))
{
n
.
y
=
(
n
.
constraint
.
y
*
z
+
n
.
y
*
(
1
-
z
));
}
});
}
if
(
config
.
options
.
collisionPrevention
&&
network
.
preventCollisions
)
{
preventCollisions
();
}
var
portHalfW
=
config
.
labels
.
port
.
width
/
2
,
portHalfH
=
config
.
labels
.
port
.
height
/
2
;
// clip visualization of links at bounds of nodes...
network
.
link
.
each
(
function
(
d
)
{
var
xs
=
d
.
source
.
x
,
ys
=
d
.
source
.
y
,
xt
=
d
.
target
.
x
,
yt
=
d
.
target
.
y
,
line
=
new
geo
.
LineSegment
(
xs
,
ys
,
xt
,
yt
),
e
,
ix
,
exs
,
eys
,
ext
,
eyt
,
pxs
,
pys
,
pxt
,
pyt
;
if
(
d
.
class
===
'host'
)
{
// no adjustment for source end of link, since hosts are dots
exs
=
xs
;
eys
=
ys
;
}
else
{
for
(
e
in
d
.
source
.
edge
)
{
ix
=
line
.
intersect
(
d
.
source
.
edge
[
e
].
offset
(
xs
,
ys
));
if
(
ix
.
in1
&&
ix
.
in2
)
{
exs
=
ix
.
x
;
eys
=
ix
.
y
;
// also pick off the port label intersection
ix
=
line
.
intersect
(
d
.
source
.
portEdge
[
e
].
offset
(
xs
,
ys
));
pxs
=
ix
.
x
;
pys
=
ix
.
y
;
break
;
}
}
}
for
(
e
in
d
.
target
.
edge
)
{
ix
=
line
.
intersect
(
d
.
target
.
edge
[
e
].
offset
(
xt
,
yt
));
if
(
ix
.
in1
&&
ix
.
in2
)
{
ext
=
ix
.
x
;
eyt
=
ix
.
y
;
// also pick off the port label intersection
ix
=
line
.
intersect
(
d
.
target
.
portEdge
[
e
].
offset
(
xt
,
yt
));
pxt
=
ix
.
x
;
pyt
=
ix
.
y
;
break
;
}
}
// adjust the endpoints of the link's line to match rectangles
var
sid
=
safeId
(
d
.
id
);
d3
.
select
(
this
)
.
attr
(
'x1'
,
exs
)
.
attr
(
'y1'
,
eys
)
.
attr
(
'x2'
,
ext
)
.
attr
(
'y2'
,
eyt
);
d3
.
select
(
'#srcPort-'
+
sid
)
.
attr
(
'x'
,
pxs
-
portHalfW
)
.
attr
(
'y'
,
pys
-
portHalfH
);
d3
.
select
(
'#tgtPort-'
+
sid
)
.
attr
(
'x'
,
pxt
-
portHalfW
)
.
attr
(
'y'
,
pyt
-
portHalfH
);
// TODO: fit label rect to size of port number.
d3
.
select
(
'#srcText-'
+
sid
)
.
attr
(
'x'
,
pxs
-
5
)
.
attr
(
'y'
,
pys
+
3
);
d3
.
select
(
'#tgtText-'
+
sid
)
.
attr
(
'x'
,
pxt
-
5
)
.
attr
(
'y'
,
pyt
+
3
);
});
// position each node by translating the node (group) by x,y
network
.
node
.
attr
(
'transform'
,
function
(
d
)
{
return
translate
(
d
.
x
,
d
.
y
);
});
}
// $('#docs-close').on('click', function() {
// deselectObject();
// return false;
// });
// $(document).on('click', '.select-object', function() {
// var obj = graph.data[$(this).data('name')];
// if (obj) {
// selectObject(obj);
// }
// return false;
// });
function
findNodeFromData
(
d
)
{
var
el
=
null
;
network
.
node
.
filter
(
'.'
+
d
.
class
).
each
(
function
(
n
)
{
if
(
n
.
id
===
d
.
id
)
{
el
=
d3
.
select
(
this
);
}
});
return
el
;
}
function
selectObject
(
obj
,
el
)
{
var
node
;
if
(
el
)
{
node
=
d3
.
select
(
el
);
}
else
{
network
.
node
.
each
(
function
(
d
)
{
if
(
d
==
obj
)
{
node
=
d3
.
select
(
el
=
this
);
}
});
}
if
(
!
node
)
return
;
if
(
node
.
classed
(
'selected'
))
{
deselectObject
();
flyinPane
(
null
);
return
;
}
deselectObject
(
false
);
selected
=
{
obj
:
obj
,
el
:
el
};
node
.
classed
(
'selected'
,
true
);
flyinPane
(
obj
);
}
function
deselectObject
(
doResize
)
{
// Review: logic of 'resize(...)' function.
if
(
doResize
||
typeof
doResize
==
'undefined'
)
{
resize
(
false
);
}
// deselect all nodes in the network...
network
.
node
.
classed
(
'selected'
,
false
);
selected
=
{};
flyinPane
(
null
);
}
function
flyinPane
(
obj
)
{
var
pane
=
d3
.
select
(
'#flyout'
),
url
;
if
(
obj
)
{
// go get details of the selected object from the server...
url
=
detailJsonUrl
(
obj
.
id
);
d3
.
json
(
url
,
function
(
err
,
data
)
{
if
(
err
)
{
alert
(
'Oops! Error reading JSON...\n\n'
+
'URL: '
+
url
+
'\n\n'
+
'Error: '
+
err
.
message
);
return
;
}
// console.log("JSON data... " + url);
// console.log(data);
displayDetails
(
data
,
pane
);
});
}
else
{
// hide pane
pane
.
transition
().
duration
(
750
)
.
style
(
'right'
,
'-320px'
)
.
style
(
'opacity'
,
0.0
);
}
}
function
displayDetails
(
data
,
pane
)
{
$
(
'#flyout'
).
empty
();
var
title
=
pane
.
append
(
"h2"
),
table
=
pane
.
append
(
"table"
),
tbody
=
table
.
append
(
"tbody"
);
$
(
'<img src="img/'
+
data
.
type
+
'.png">'
).
appendTo
(
title
);
$
(
'<span>'
).
attr
(
'class'
,
'icon'
).
text
(
data
.
id
).
appendTo
(
title
);
// TODO: consider using d3 data bind to TR/TD
data
.
propOrder
.
forEach
(
function
(
p
)
{
if
(
p
===
'-'
)
{
addSep
(
tbody
);
}
else
{
addProp
(
tbody
,
p
,
data
.
props
[
p
]);
}
});
function
addSep
(
tbody
)
{
var
tr
=
tbody
.
append
(
'tr'
);
$
(
'<hr>'
).
appendTo
(
tr
.
append
(
'td'
).
attr
(
'colspan'
,
2
));
}
function
addProp
(
tbody
,
label
,
value
)
{
var
tr
=
tbody
.
append
(
'tr'
);
tr
.
append
(
'td'
)
.
attr
(
'class'
,
'label'
)
.
text
(
label
+
' :'
);
tr
.
append
(
'td'
)
.
attr
(
'class'
,
'value'
)
.
text
(
value
);
}
// show pane
pane
.
transition
().
duration
(
750
)
.
style
(
'right'
,
'20px'
)
.
style
(
'opacity'
,
1.0
);
}
function
highlightObject
(
obj
)
{
if
(
obj
)
{
if
(
obj
!=
highlighted
)
{
// TODO set or clear "inactive" class on nodes, based on criteria
network
.
node
.
classed
(
'inactive'
,
function
(
d
)
{
// return (obj !== d &&
// d.relation(obj.id));
return
(
obj
!==
d
);
});
// TODO: same with links
network
.
link
.
classed
(
'inactive'
,
function
(
d
)
{
return
(
obj
!==
d
.
source
&&
obj
!==
d
.
target
);
});
}
highlighted
=
obj
;
}
else
{
if
(
highlighted
)
{
// clear the inactive flag (no longer suppressed visually)
network
.
node
.
classed
(
'inactive'
,
false
);
network
.
link
.
classed
(
'inactive'
,
false
);
}
highlighted
=
null
;
}
}
function
hoverObject
(
obj
)
{
if
(
obj
)
{
hovered
=
obj
;
}
else
{
if
(
hovered
)
{
hovered
=
null
;
}
}
}
function
resize
()
{
view
.
height
=
window
.
innerHeight
-
config
.
mastHeight
;
view
.
width
=
window
.
innerWidth
;
$
(
'#view'
)
.
css
(
'height'
,
view
.
height
+
'px'
)
.
css
(
'width'
,
view
.
width
+
'px'
);
network
.
forceWidth
=
view
.
width
-
config
.
force
.
marginLR
;
network
.
forceHeight
=
view
.
height
-
config
.
force
.
marginTB
;
}
// ======================================================================
// register with the UI framework
// ==============================
// View registration
onos
.
ui
.
addView
(
'topo'
,
{
load
:
loadNetworkView
preload
:
preload
,
load
:
load
,
resize
:
resize
});
}(
ONOS
));
...
...
Please
register
or
login
to post a comment