Compare commits
No commits in common. "6eb67407d94f3df92d2dd31f9f7e60aed45f3909" and "0b6e4661cba199bb478c9fca52124bf91eda7b9f" have entirely different histories.
6eb67407d9
...
0b6e4661cb
5 changed files with 164 additions and 1446 deletions
69
README.md
69
README.md
|
@ -4,7 +4,6 @@ This repository contains a script to pull semantic data out of Semantic Mediawik
|
||||||
|
|
||||||
Built for [Security Vision](https://securityvision.io).
|
Built for [Security Vision](https://securityvision.io).
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -42,72 +41,4 @@ _Ask_ SMW with the following query:
|
||||||
?Software Deployed.Developped by (institutions)=Software Developer
|
?Software Deployed.Developped by (institutions)=Software Developer
|
||||||
?Datasets Used
|
?Datasets Used
|
||||||
?Datasets Used.Developed by Institution=Dataset Developer
|
?Datasets Used.Developed by Institution=Dataset Developer
|
||||||
```
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
({{#ask: [[Category:Deployments||Institution]]
|
|
||||||
OR [[Category:Technologies]] [[Developed by (institutions)::+]] // TODO: + should give a subquery <q></q> with all institutions in EU
|
|
||||||
OR [[Category:Technologies]] [[-Software Deployed::+]] // TODO: + should give a subquery <q></q> with all deployments in EU
|
|
||||||
|?Category
|
|
||||||
|?Geolocation
|
|
||||||
|?City
|
|
||||||
|?City.Has Coordinates=Has Coordinates
|
|
||||||
|?City.Is in Country=City Country
|
|
||||||
|?City Country.Has Coordinates=Country Coordinates
|
|
||||||
|?Clients
|
|
||||||
|?Managed by
|
|
||||||
|?Used by
|
|
||||||
|?Funded by
|
|
||||||
|?Provided by // TODO: technology/product provided by
|
|
||||||
|?Software Deployed
|
|
||||||
|?Developped by (institutions)
|
|
||||||
|format=broadtable
|
|
||||||
|limit=500
|
|
||||||
|offset=0
|
|
||||||
|link=all
|
|
||||||
|sort=
|
|
||||||
|order=asc
|
|
||||||
|headers=show
|
|
||||||
|searchlabel=... further results
|
|
||||||
|class=sortable wikitable smwtable
|
|
||||||
}}
|
|
||||||
|
|
||||||
|
|
||||||
{{#ask: [[Category:Deployments||Institution]] [[Not in graph::!Greens Report 2021]]
|
|
||||||
OR [[Category:Technologies]] [[Developed by (institutions)::+]]
|
|
||||||
OR [[Category:Technologies]] [[-Software Deployed::+]]
|
|
||||||
|?Category
|
|
||||||
|?Geolocation
|
|
||||||
|?City
|
|
||||||
|?City.Has Coordinates=City Coordinates
|
|
||||||
|?City.Is in Country=City Country
|
|
||||||
|?City Country.Has Coordinates=Country Coordinates
|
|
||||||
|?Clients
|
|
||||||
|?Managed by
|
|
||||||
|?Used by
|
|
||||||
|?Funded by
|
|
||||||
|?Provided by
|
|
||||||
|?Software Deployed
|
|
||||||
|?Software Deployed.Developped by (institutions)=Software Developer
|
|
||||||
|?Datasets Used
|
|
||||||
|?Datasets Used.Developed by Institution=Dataset Developer
|
|
||||||
|format=broadtable
|
|
||||||
|limit=50
|
|
||||||
|offset=0
|
|
||||||
|link=all
|
|
||||||
|sort=
|
|
||||||
|order=asc
|
|
||||||
|headers=show
|
|
||||||
|searchlabel=... further results
|
|
||||||
|class=sortable wikitable smwtable
|
|
||||||
}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Fetch using CURL:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl 'https://www.securityvision.io/wiki/index.php?title=Special:Ask&#search' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Content-Type: application/x-www-form-urlencoded' -H 'Origin: https://www.securityvision.io' -H 'Connection: keep-alive' -H 'Referer: https://www.securityvision.io/wiki/index.php/Special:Ask' -H 'Upgrade-Insecure-Requests: 1' --data-raw 'title=Special%3AAsk&_action=submit&q=%5B%5BCategory%3ADeployments%7C%7CInstitution%5D%5D++&po=%0D%0A+%7C%3FCategory%0D%0A+%7C%3FGeolocation%0D%0A+%7C%3FCity%0D%0A+%7C%3FCity.Has+Coordinates%3DCity+Coordinates%0D%0A+%7C%3FCity.Is+in+Country%3DCity+Country%0D%0A+%7C%3FCity+Country.Has+Coordinates%3DCountry+Coordinates%0D%0A+%7C%3FClients%0D%0A+%7C%3FManaged+by%0D%0A+%7C%3FUsed+by%0D%0A+%7C%3FFunded+by%0D%0A+%7C%3FProvided+by%0D%0A+%7C%3FSoftware+Deployed%0D%0A+%7C%3FSoftware+Deployed.Developped+by+%28institutions%29%3DSoftware+Developer%0D%0A+%7C%3FDatasets+Used%0D%0A+%7C%3FDatasets+Used.Developed+by+Institution%3DDataset+Developer&eq=yes&p%5Bformat%5D=json&p%5Blimit%5D=500&p%5Boffset%5D=0&p%5Blink%5D=all&p%5Bheaders%5D=show&p%5Bmainlabel%5D=&p%5Bintro%5D=&p%5Boutro%5D=&p%5Bsearchlabel%5D=JSON&p%5Bdefault%5D=&p%5Btype%5D=full&p%5Bfilename%5D=result2.json&sort_num%5B%5D=&order_num%5B%5D=asc&sort_num%5B%5D=&order_num%5B%5D=asc&eq=yes'
|
|
||||||
```
|
```
|
383
www/graph.css
383
www/graph.css
|
@ -1,12 +1,6 @@
|
||||||
@font-face {
|
|
||||||
font-family: 'Lexend Mega Regular';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
src: local('Lexend Mega Regular'), url('LexendMega-Regular.woff') format('woff');
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
:root{
|
||||||
--color1: #9741f9;
|
--color1: #f94144;
|
||||||
--color2: #f3722c;
|
--color2: #f3722c;
|
||||||
--color3: #f8961e;
|
--color3: #f8961e;
|
||||||
/* --color4: #f9844a; */
|
/* --color4: #f9844a; */
|
||||||
|
@ -16,25 +10,18 @@
|
||||||
--color8: #4d908e;
|
--color8: #4d908e;
|
||||||
--color9: #577590;
|
--color9: #577590;
|
||||||
--color10: #277da1;
|
--color10: #277da1;
|
||||||
|
|
||||||
--hover-color: var(--color1);
|
--hover-color: var(--color1);
|
||||||
--hover-related-color: #d1bce9;
|
|
||||||
/* --hover-color: var(darkblue); */
|
/* --hover-color: var(darkblue); */
|
||||||
--selected-color: var(--color1);
|
--selected-color: var(--color1);
|
||||||
--link-color: rgba(255,255,255,0.5);
|
--selected-color: var(--color1);
|
||||||
--link-hover-color: var(--hover-color);
|
|
||||||
--link-hover-related-color: var(--hover-related-color);
|
|
||||||
--link-focus-color: var(--hover-color);
|
|
||||||
|
|
||||||
--body-back: #96a7b7; /*#9cb3c9; /*#8195a7; /*#9cb3c9; #b9cada*/
|
|
||||||
--title-color: #1c1c1c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
/* overflow: hidden; */
|
/* overflow: hidden; */
|
||||||
/* background: linear-gradient(to top, #040308, #AD4A28, #DD723C, #fc7001, #dcb697, #9ba5ae, #3e5879, #020b1a); */
|
/* background: linear-gradient(to top, #040308, #AD4A28, #DD723C, #fc7001, #dcb697, #9ba5ae, #3e5879, #020b1a); */
|
||||||
background: var(--body-back);
|
background:linear-gradient(to top, #414141, #99a6b8);
|
||||||
/* background: #9cb3c9; */
|
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
@ -48,215 +35,112 @@ svg.dragging {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
#arrowHead {
|
svg .links line,svg .links path{
|
||||||
fill: var(--link-color);
|
stroke: #f3722c;
|
||||||
}
|
|
||||||
|
|
||||||
#arrowHeadSelected {
|
|
||||||
fill: var(--link-focus-color);;
|
|
||||||
}
|
|
||||||
#arrowHeadSelectedRelated {
|
|
||||||
fill: var(--link-hover-related-color);;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg .links line, svg .links path {
|
|
||||||
/* stroke: #f3722c; */
|
|
||||||
/* stroke: #9df32c; */
|
|
||||||
stroke: var(--link-color);
|
|
||||||
stroke-width: 6;
|
stroke-width: 6;
|
||||||
fill: none;
|
fill:none;
|
||||||
transition: stroke-width 1s;
|
transition: stroke-width 1s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .links line.hover, svg .links path.hover {
|
svg .links line.hover, svg .links path.hover{
|
||||||
stroke: var(--link-hover-color);
|
stroke:red;
|
||||||
/* stroke-width: 12; */
|
|
||||||
marker-end: url(#arrowHeadSelected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .links .linkedHover path{
|
svg.zoomed .links line, svg.zoomed .links path{
|
||||||
stroke: var(--link-hover-related-color);
|
|
||||||
stroke-width: 12;
|
|
||||||
marker-end: url(#arrowHeadSelectedRelated);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg .links .linkedSelected path{
|
|
||||||
stroke: var(--link-focus-color);
|
|
||||||
stroke-width: 12;
|
|
||||||
marker-end: url(#arrowHeadSelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg.zoomed .links line, svg.zoomed .links path {
|
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* svg.zoomed .links line, svg.zoomed .links path.hover {
|
.links text{
|
||||||
stroke-width: 4;
|
display:none;
|
||||||
} */
|
font-size:5pt;
|
||||||
|
|
||||||
svg .title {
|
|
||||||
font-size: 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg .subtitle {
|
|
||||||
font-size: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #countries .country {
|
|
||||||
fill: #f7f5f5;
|
|
||||||
fill: #f0eeee;
|
|
||||||
/* stroke:rgb(230, 230, 230); */
|
|
||||||
stroke:rgb(230, 230, 230);
|
|
||||||
stroke-width: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #countries .country.eu_country {
|
|
||||||
/* fill:#eae5e3; */
|
|
||||||
fill:#e9e1e4;
|
|
||||||
/* fill:rgb(234, 229, 235); */
|
|
||||||
stroke:rgb(217, 209, 218);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #header #titlePath, svg #header #title2Path, svg #header #subtitlePath {
|
|
||||||
stroke: none;
|
|
||||||
fill: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #header text {
|
|
||||||
font-size: 180px;
|
|
||||||
font-family: "Lexend Mega Regular";
|
|
||||||
/*Comfortaa*/
|
|
||||||
opacity: .8;
|
|
||||||
fill: var(--title-color);
|
|
||||||
text-shadow: rgba(0, 0, 0, .5) 5px 5px 10px;
|
|
||||||
/* text-transform: uppercase; */
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #header text:nth-of-type(2) {
|
|
||||||
/* dominant-baseline: hanging; */
|
|
||||||
/* transform: translate(10px, 25px); */
|
|
||||||
}
|
|
||||||
|
|
||||||
svg #header text#subtitle {
|
|
||||||
font-size: 70px;
|
|
||||||
fill: var(--body-back);
|
|
||||||
}
|
|
||||||
|
|
||||||
.links text {
|
|
||||||
display: none;
|
|
||||||
font-size: 5pt;
|
|
||||||
text-anchor: middle;
|
text-anchor: middle;
|
||||||
fill: whitesmoke;
|
fill: whitesmoke;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node {
|
.node{
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node text.nodeTitle {
|
.node text.nodeTitle{
|
||||||
text-anchor: start;
|
text-anchor: start;
|
||||||
dominant-baseline: hanging;
|
dominant-baseline: hanging; /*achieves a 'text-anchor: top'*/
|
||||||
/*achieves a 'text-anchor: top'*/
|
font-size:16pt;
|
||||||
/* font-size: 16pt; */
|
|
||||||
/*Set this in JS*/
|
|
||||||
transition: font-size .4s, opacity 1s;
|
transition: font-size .4s, opacity 1s;
|
||||||
fill: #5d5d5f; /*also when hovering node*/
|
fill: white;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: none;
|
|
||||||
/*prevent mouse glitches*/
|
|
||||||
}
|
}
|
||||||
|
.node:not(:hover):not(.linkHover) text.nodeTitle.overlapping{
|
||||||
/* .node:not(:hover):not(.linkHover) text.nodeTitle.overlapping { */
|
|
||||||
.node text.nodeTitle.overlapping {
|
|
||||||
/* used to be shown on hover, but disabled now that we have a tooltip */
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.zoomed .node text.nodeTitle {
|
|
||||||
/* font-size: 6pt; */
|
svg.zoomed .node text.nodeTitle{
|
||||||
|
font-size:6pt;
|
||||||
|
}
|
||||||
|
svg.zoomed.zoomed2 .node text.nodeTitle{
|
||||||
|
font-size:3pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.zoomed.zoomed2 .node text.nodeTitle {
|
.node circle{
|
||||||
font-size: 3pt;
|
fill: white;
|
||||||
}
|
|
||||||
|
|
||||||
.node circle, .node path {
|
|
||||||
fill: lightgray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Whenever a connected link is hovered */
|
/* Whenever a connected link is hovered */
|
||||||
|
.node.linkHover circle{
|
||||||
.node.linkHover circle, .node.linkHover path, .node.linkedHover path, label:hover .node path {
|
|
||||||
fill: var(--hover-related-color) !important;
|
|
||||||
stroke: var(--hover-related-color);
|
|
||||||
stroke-width: 5px;
|
|
||||||
}
|
|
||||||
.node.linkedSelected path {
|
|
||||||
fill: var(--hover-related-color) !important;
|
|
||||||
/* same as linkHover/linkedHover but without border */
|
|
||||||
}
|
|
||||||
|
|
||||||
.node.linkHover text.nodeTitle.overlapping {
|
|
||||||
transition: opacity 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node:hover circle, .node:hover path, .node.selected path {
|
|
||||||
fill: var(--hover-color) !important;
|
|
||||||
stroke: var(--hover-color);
|
stroke: var(--hover-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
/*
|
.node.linkHover text.nodeTitle.overlapping{
|
||||||
.node:hover text {
|
transition: opacity 0s;
|
||||||
transition: none;
|
}
|
||||||
fill: var(--hover-color);
|
|
||||||
} */
|
|
||||||
|
|
||||||
.node.selected circle, .node.selected path {
|
.node:hover circle{
|
||||||
fill: var(--selected-color) !important;
|
stroke: var(--hover-color);
|
||||||
|
stroke-width: 5px;
|
||||||
|
}
|
||||||
|
.node:hover text{
|
||||||
|
transition: none;
|
||||||
|
fill: var(--hover-color);
|
||||||
|
}
|
||||||
|
.node.selected circle{
|
||||||
stroke: var(--selected-color);
|
stroke: var(--selected-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.City circle, .node.City path {
|
.node.City circle{
|
||||||
display: none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
.node.City{
|
||||||
.node.City {
|
fill:white;
|
||||||
fill: white;
|
|
||||||
stroke: black;
|
stroke: black;
|
||||||
stroke-width: .5px;
|
stroke-width: .5px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
.node.Person circle {
|
||||||
.node.Person circle, .node.Person path {
|
|
||||||
fill: lightgreen
|
fill: lightgreen
|
||||||
}
|
}
|
||||||
|
.node.Technology circle {
|
||||||
.node.Technology circle, .node.Technology path {
|
fill: lightcoral;
|
||||||
fill: plum;
|
|
||||||
}
|
}
|
||||||
|
.node.Deployment circle {
|
||||||
.node.Deployments circle, .node.Deployments path {
|
fill: lightblue;
|
||||||
/* fill: lightblue; */
|
|
||||||
/* fill: rgb(75, 97, 190); */
|
|
||||||
fill: rgb(8, 8, 8);
|
|
||||||
}
|
}
|
||||||
|
.node.Institution circle {
|
||||||
.node.Institution circle, .node.Institution path {
|
|
||||||
/* fill: lightcoral; */
|
|
||||||
fill: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node.Dataset circle, .node.Dataset path {
|
|
||||||
fill: lightgoldenrodyellow
|
fill: lightgoldenrodyellow
|
||||||
}
|
}
|
||||||
|
.node.Dataset circle {
|
||||||
.labels .label text {
|
fill: plum
|
||||||
fill: yellow;
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.labels .label text{
|
||||||
|
fill:yellow;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* .node.Person circle {
|
/* .node.Person circle {
|
||||||
fill: var(--color2)
|
fill: var(--color2)
|
||||||
}
|
}
|
||||||
|
@ -273,164 +157,69 @@ svg.zoomed.zoomed2 .node text.nodeTitle {
|
||||||
fill: var(--color7)
|
fill: var(--color7)
|
||||||
} */
|
} */
|
||||||
|
|
||||||
#nodeInfo {
|
|
||||||
|
#nodeInfo{
|
||||||
position: fixed;
|
position: fixed;
|
||||||
display: block;
|
display:block;
|
||||||
right: 20px;
|
right:20px;
|
||||||
bottom: 20px;
|
bottom:20px;
|
||||||
background: white;
|
background:white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: solid 1px #ccc;
|
border: solid 1px #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nodeInfo.hidden {
|
#nodeInfo.hidden{
|
||||||
display: none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nodeInfo h2{
|
||||||
#nodeInfo h2 {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nodeInfo iframe {
|
#nodeInfo iframe{
|
||||||
width: 50vw;
|
width: 50vw;
|
||||||
height: calc(100vh - 40px - 20px - 30px);
|
height: calc(100vh - 40px - 20px - 30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#tooltip{
|
#closeInfo{
|
||||||
position:absolute;
|
|
||||||
z-index: 100;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity .3s;
|
|
||||||
background:white;
|
|
||||||
padding: 20px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 2px 2px 5px rgba(0, 0, 0, .5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#tooltip:not(.visible){
|
|
||||||
position:absolute;
|
|
||||||
z-index: 100;
|
|
||||||
opacity:0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tooltip h3{
|
|
||||||
margin: 5px 0;
|
|
||||||
text-align: center;;
|
|
||||||
}
|
|
||||||
#tooltip .category{
|
|
||||||
display: block;
|
|
||||||
color: black;
|
|
||||||
text-align: center;;
|
|
||||||
}
|
|
||||||
#tooltip .category::before{
|
|
||||||
content:'· '
|
|
||||||
}
|
|
||||||
#tooltip .category::after{
|
|
||||||
content:' ·'
|
|
||||||
}
|
|
||||||
#tooltip .clickForMore{
|
|
||||||
display: block;
|
|
||||||
color: gray;
|
|
||||||
text-align: center;;
|
|
||||||
}
|
|
||||||
|
|
||||||
#closeInfo {
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#closeInfo:hover {
|
#closeInfo:hover{
|
||||||
color: var(--hover-color);
|
color: var(--hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a, a:link {
|
a, a:link{
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
a:hover{
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
#filters,#menu{
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
left: 0;
|
||||||
right: 0;
|
top: 0;
|
||||||
background: white;
|
background: white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-top-left-radius: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
#map .borders{
|
||||||
/* color:var(--color9); */
|
|
||||||
margin: 0;
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.subtitle {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--color10);
|
|
||||||
/* text-transform: uppercase;; */
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filters h3 {
|
|
||||||
text-align: center;
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filters label {
|
|
||||||
cursor: pointer;
|
|
||||||
display: block;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filters label svg {
|
|
||||||
display: inline;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filters span:hover {
|
|
||||||
color: var(--hover-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#filters input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filters input+span {
|
|
||||||
display: inline-block;
|
|
||||||
padding-left: 10px;
|
|
||||||
/* background: var(--color9); */
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filters input:checked+span {
|
|
||||||
/* color: var(--selected-color); */
|
|
||||||
text-decoration: none;
|
|
||||||
/* box-shadow: inset 0 0 5px black; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#map .borders {
|
|
||||||
stroke-width: 6px;
|
stroke-width: 6px;
|
||||||
stroke: rgb(221, 210, 210);
|
stroke: white;
|
||||||
fill: none;
|
fill:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#alluvial {
|
#alluvial{
|
||||||
/* position:fixed; */
|
/* position:fixed; */
|
||||||
top: 0;
|
top:0;
|
||||||
left: 0;
|
left:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#alluvial .flow_label text {
|
#alluvial .flow_label text{
|
||||||
font-size: 30;
|
font-size: 30;
|
||||||
}
|
}
|
||||||
|
|
612
www/graph.js
612
www/graph.js
|
@ -1,12 +1,8 @@
|
||||||
|
|
||||||
const CONFIG = {
|
const CONFIG = {
|
||||||
'title': "Remote Biometric Identification",
|
'nodeSize': 8,
|
||||||
'subtitle': "A survey of the European Union",
|
|
||||||
// 'nodeSize': 8,
|
|
||||||
'nodeRadius': 5,
|
|
||||||
'nodeRepositionPadding': 10,
|
|
||||||
'baseUrl': 'https://www.securityvision.io/wiki/index.php/',
|
'baseUrl': 'https://www.securityvision.io/wiki/index.php/',
|
||||||
'dataUrl': 'result2.json',
|
'dataUrl': 'result.json',
|
||||||
'preSimulate': false, // run simulation before starting, so we don't start with lines jumping around
|
'preSimulate': false, // run simulation before starting, so we don't start with lines jumping around
|
||||||
'labels': {
|
'labels': {
|
||||||
'rotate': true,
|
'rotate': true,
|
||||||
|
@ -51,66 +47,13 @@ const CONFIG = {
|
||||||
"Company": ["Managed by", "Provided by", "Developped by (institutions)"],
|
"Company": ["Managed by", "Provided by", "Developped by (institutions)"],
|
||||||
"Tech": ["Technologies Used", "Software Deployed"],
|
"Tech": ["Technologies Used", "Software Deployed"],
|
||||||
"Funding": ["Funded by"],
|
"Funding": ["Funded by"],
|
||||||
},
|
}
|
||||||
"zoom": {
|
|
||||||
"scale_min": .2,
|
|
||||||
"scale_max": 20,
|
|
||||||
},
|
|
||||||
|
|
||||||
"cases": [
|
|
||||||
"Data-lab Burglary-Free Neighbourhood",
|
|
||||||
"Dragonfly Project",
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// let width = window.innerWidth;
|
// let width = window.innerWidth;
|
||||||
// let height = window.innerHeight;
|
// let height = window.innerHeight;
|
||||||
|
|
||||||
|
|
||||||
function getSymbolForCategories(classes) {
|
|
||||||
if (!Array.isArray(classes)) {
|
|
||||||
classes = [classes];
|
|
||||||
}
|
|
||||||
if (classes.includes('Institution')) {
|
|
||||||
return d3.symbol()
|
|
||||||
.type(d3.symbolTriangle)
|
|
||||||
.size(CONFIG.nodeRadius * 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
return d3.symbol()
|
|
||||||
.type(d3.symbolCircle)
|
|
||||||
.size(CONFIG.nodeRadius * 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns a symbol function
|
|
||||||
function getSymbolForNode(n) {
|
|
||||||
const classes = getCategories(n);
|
|
||||||
return getSymbolForCategories(classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slugify a string, by https://lucidar.me/en/web-dev/how-to-slugify-a-string-in-javascript/
|
|
||||||
function slugify(str) {
|
|
||||||
str = str.replace(/^\s+|\s+$/g, '');
|
|
||||||
|
|
||||||
// Make the string lowercase
|
|
||||||
str = str.toLowerCase();
|
|
||||||
|
|
||||||
// Remove accents, swap ñ for n, etc
|
|
||||||
var from = "ÁÄÂÀÃÅČÇĆĎÉĚËÈÊẼĔȆÍÌÎÏŇÑÓÖÒÔÕØŘŔŠŤÚŮÜÙÛÝŸŽáäâàãåčçćďéěëèêẽĕȇíìîïňñóöòôõøðřŕšťúůüùûýÿžþÞĐđßÆa·/_,:;";
|
|
||||||
var to = "AAAAAACCCDEEEEEEEEIIIINNOOOOOORRSTUUUUUYYZaaaaaacccdeeeeeeeeiiiinnooooooorrstuuuuuyyzbBDdBAa------";
|
|
||||||
for (var i = 0, l = from.length; i < l; i++) {
|
|
||||||
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove invalid chars
|
|
||||||
str = str.replace(/[^a-z0-9 -]/g, '')
|
|
||||||
// Collapse whitespace and replace by -
|
|
||||||
.replace(/\s+/g, '-')
|
|
||||||
// Collapse dashes
|
|
||||||
.replace(/-+/g, '-');
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -203,19 +146,12 @@ function getClasses(obj) {
|
||||||
return 'node ' + classes.join(' ');
|
return 'node ' + classes.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLinkId(link) {
|
|
||||||
return "link_" + link.nr;
|
|
||||||
// return "link-" + link.source.id + '-' + link.target.id + '-' + slugify(link.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NodeMap {
|
class NodeMap {
|
||||||
constructor(parent) {
|
constructor(parent) {
|
||||||
this.root = d3.select(parent);
|
this.root = d3.select(parent);
|
||||||
this.resizeEvent = window.addEventListener('resize', this.resize.bind(this));
|
this.resizeEvent = window.addEventListener('resize', this.resize.bind(this));
|
||||||
this.tooltipEl = document.getElementById('tooltip');
|
|
||||||
this.selectedNode = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resize() {
|
resize() {
|
||||||
|
@ -263,47 +199,7 @@ class NodeMap {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.svg = this.root.append('svg')
|
this.svg = this.root.append('svg')
|
||||||
this.svg.append('defs').html(`<marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHead" ><path d="M0,-3L8,0L0,3"></path></marker><marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHeadSelected"><path d="M0,-3L8,0L0,3"></path></marker><marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHeadSelectedRelated"><path d="M0,-3L8,0L0,3"></path></marker>
|
this.svg.append('defs').html('<marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHead" fill="#f3722c"><path d="M0,-3L8,0L0,3"></path></marker><marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHeadSelected"><path d="M0,-3L8,0L0,3" fill="white"></path></marker>');
|
||||||
<!--Sketching:-->
|
|
||||||
<defs>
|
|
||||||
<filter id="tint">
|
|
||||||
<feColorMatrix values="1.1 0 0 0 0 0 1.1 0 0 0 0 0 0.9 0 0 0 0 0 1 0" />
|
|
||||||
</filter>
|
|
||||||
<filter id="splotch">
|
|
||||||
<feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="4" />
|
|
||||||
<feColorMatrix values="0 0 0 0 0, 0 0 0 0 0, 0 0 0 0 0, 0 0 0 -0.9 1.2" result="texture" />
|
|
||||||
<feComposite in="SourceGraphic" in2="texture" operator="in" />
|
|
||||||
<feGaussianBlur stdDeviation="3.5" />
|
|
||||||
</filter>
|
|
||||||
<filter id="pencil">
|
|
||||||
<feTurbulence baseFrequency="0.03" numOctaves="6" type="fractalNoise" />
|
|
||||||
<feDisplacementMap scale="4" in="SourceGraphic" xChannelSelector="R" yChannelSelector="G" />
|
|
||||||
<feGaussianBlur stdDeviation="0.5" />
|
|
||||||
</filter>
|
|
||||||
</defs>`);
|
|
||||||
|
|
||||||
this.svg.on('click', (e) => { console.log(e); this.deselectNode()})
|
|
||||||
|
|
||||||
// const noise = 0.001;
|
|
||||||
// this.svg.append('defs').append('filter').attr('id', 'splotch').html( `${
|
|
||||||
// noise
|
|
||||||
// ? `<feTurbulence
|
|
||||||
// type="fractalNoise"
|
|
||||||
// baseFrequency="${noise}"
|
|
||||||
// numOctaves="4"
|
|
||||||
// ></feTurbulence>
|
|
||||||
// <feColorMatrix
|
|
||||||
// values="0 0 0 0 0, 0 0 0 0 0, 0 0 0 0 0, 0 0 0 -0.9 0.2"
|
|
||||||
// result="texture"
|
|
||||||
// ></feColorMatrix>
|
|
||||||
// <feComposite
|
|
||||||
// in="SourceGraphic"
|
|
||||||
// in2="texture"
|
|
||||||
// operator="in"
|
|
||||||
// ></feComposite>`
|
|
||||||
// : ``
|
|
||||||
// }`)
|
|
||||||
|
|
||||||
this.resize();
|
this.resize();
|
||||||
|
|
||||||
this.projection = d3.geoHill()
|
this.projection = d3.geoHill()
|
||||||
|
@ -338,104 +234,45 @@ class NodeMap {
|
||||||
.data(this.countries)
|
.data(this.countries)
|
||||||
.enter()
|
.enter()
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("class", (n) => {
|
.attr("class", "countries")
|
||||||
if (CONFIG.countries.indexOf(n.properties.name) !== -1) {
|
|
||||||
return 'country eu_country';
|
|
||||||
}
|
|
||||||
return "country";
|
|
||||||
})
|
|
||||||
.attr("d", this.proj)
|
.attr("d", this.proj)
|
||||||
// .attr("filter", 'url(#splotch)')
|
.attr("fill", (n) => {
|
||||||
// .attr("fill", );
|
if (CONFIG.countries.indexOf(n.properties.name) !== -1) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return "rgba(200,200,200,.7)";
|
||||||
|
});
|
||||||
|
|
||||||
this.g_borders
|
this.g_borders
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("class", "borders")
|
.attr("class", "borders")
|
||||||
.attr("d", this.proj(this.borders))
|
.attr("d", this.proj(this.borders))
|
||||||
|
|
||||||
let zoomTimeout = null;
|
const zoom = d3.zoom().scaleExtent([0.2, 10]).on("start", () => {
|
||||||
this.zoom = d3.zoom()
|
this.svg.node().classList.add("dragging");
|
||||||
.scaleExtent([CONFIG.zoom.scale_min, CONFIG.zoom.scale_max])
|
}).on("end", () => {
|
||||||
.on("start", () => {
|
this.svg.node().classList.remove("dragging");
|
||||||
this.svg.node().classList.add("dragging");
|
}).on("zoom", ({ transform }) => {
|
||||||
}).on("end", () => {
|
this.container.attr("transform", transform);
|
||||||
this.svg.node().classList.remove("dragging");
|
const oldZoom = this.svg.classed('zoomed');
|
||||||
}).on("zoom", (evt) => {
|
const newZoom = transform.k > 2.0;
|
||||||
this.container.attr("transform", evt.transform);
|
if (oldZoom != newZoom) {
|
||||||
const oldZoom = this.svg.classed('zoomed');
|
this.svg.classed('zoomed', newZoom);
|
||||||
const newZoom = evt.transform.k > 2.0;
|
|
||||||
if (zoomTimeout) {
|
|
||||||
clearTimeout(zoomTimeout)
|
|
||||||
}
|
|
||||||
zoomTimeout = setTimeout(() => {
|
|
||||||
this.g_nodes.attr('style', `font-size:${22000 / this.height / evt.transform.k}pt`)
|
|
||||||
setTimeout(() => {
|
|
||||||
this.calculateLabels();
|
|
||||||
}, 500);
|
|
||||||
}, 250);
|
|
||||||
if (oldZoom != newZoom) {
|
|
||||||
this.svg.classed('zoomed', newZoom);
|
|
||||||
|
|
||||||
}
|
setTimeout(() => {
|
||||||
});
|
this.calculateLabels();
|
||||||
this.title = this.container.append('g').attr('id', 'header');
|
}, 500);
|
||||||
|
}
|
||||||
const titleFeature = {
|
});
|
||||||
"type": "LineString",
|
this.svg
|
||||||
"coordinates": []
|
.call(zoom)
|
||||||
};
|
.call(zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
||||||
const title2Feature = {
|
|
||||||
"type": "LineString",
|
|
||||||
"coordinates": []
|
|
||||||
};
|
|
||||||
const subtitleFeature = {
|
|
||||||
"type": "LineString",
|
|
||||||
"coordinates": []
|
|
||||||
};
|
|
||||||
for (let index = 26; index < 70; index++) {
|
|
||||||
// projection apparently tries to find the shortest path between two points
|
|
||||||
// which is NOT following a lat/lon line on the globe
|
|
||||||
titleFeature.coordinates.push([index, 52]);
|
|
||||||
title2Feature.coordinates.push([index, 50.5]);
|
|
||||||
subtitleFeature.coordinates.push([index, 49]);
|
|
||||||
}
|
|
||||||
this.title.append("path")
|
|
||||||
.attr("id", "titlePath")
|
|
||||||
.attr("d", this.proj(titleFeature))
|
|
||||||
;
|
|
||||||
this.title.append("path")
|
|
||||||
.attr("id", "title2Path")
|
|
||||||
.attr("d", this.proj(title2Feature))
|
|
||||||
;
|
|
||||||
this.title.append("path")
|
|
||||||
.attr("id", "subtitlePath")
|
|
||||||
.attr("d", this.proj(subtitleFeature))
|
|
||||||
;
|
|
||||||
this.title.append("text")
|
|
||||||
.html('<textPath xlink:href="#titlePath">Remote Biometric</textPath>')
|
|
||||||
this.title.append("text")
|
|
||||||
.html('<textPath xlink:href="#title2Path">Identification</textPath>')
|
|
||||||
this.title.append("text")
|
|
||||||
.attr("id", "subtitle")
|
|
||||||
.html('<textPath xlink:href="#subtitlePath">' + CONFIG.subtitle + '</textPath>')
|
|
||||||
|
|
||||||
// this.title.append('text')
|
|
||||||
// .attr('class', 'title')
|
|
||||||
// .attr('x', 1000)
|
|
||||||
// .attr('y', 1000)
|
|
||||||
// .text(CONFIG.title);
|
|
||||||
// this.title.append('text')
|
|
||||||
// .attr('class', 'subtitle')
|
|
||||||
// .attr('x', 1000)
|
|
||||||
// .attr('y', 1200)
|
|
||||||
// .text(CONFIG.subtitle);
|
|
||||||
|
|
||||||
this.link = this.container.append("g")
|
this.link = this.container.append("g")
|
||||||
.attr('class', 'links')
|
.attr('class', 'links')
|
||||||
.selectAll(".link");
|
.selectAll(".link");
|
||||||
this.g_nodes = this.container.append("g")
|
this.node = this.container.append("g")
|
||||||
.attr('class', 'nodes');
|
.attr('class', 'nodes')
|
||||||
this.node = this.g_nodes
|
|
||||||
.selectAll(".node");
|
.selectAll(".node");
|
||||||
|
|
||||||
|
|
||||||
|
@ -446,16 +283,10 @@ class NodeMap {
|
||||||
if (d.printouts[prop].length) {
|
if (d.printouts[prop].length) {
|
||||||
// console.log("fix node", d);
|
// console.log("fix node", d);
|
||||||
var p = this.projection([d.printouts[prop][0].lon, d.printouts[prop][0].lat]);
|
var p = this.projection([d.printouts[prop][0].lon, d.printouts[prop][0].lat]);
|
||||||
|
|
||||||
// initial positions:
|
|
||||||
d.x = p[0];
|
d.x = p[0];
|
||||||
d.y = p[1];
|
d.y = p[1];
|
||||||
|
// d.targetX = p[0];
|
||||||
//These are used when we need to move overlapping points:
|
// d.targetY = p[1];
|
||||||
d.originalX = p[0];
|
|
||||||
d.originalY = p[1];
|
|
||||||
|
|
||||||
// target pos for the force layout
|
|
||||||
d.fx = p[0];
|
d.fx = p[0];
|
||||||
d.fy = p[1];
|
d.fy = p[1];
|
||||||
// d.targetLat = d.printouts[prop][0].lat;
|
// d.targetLat = d.printouts[prop][0].lat;
|
||||||
|
@ -465,8 +296,6 @@ class NodeMap {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.store.configureTree();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this.nodeMap = Object.fromEntries(this.graph.nodes.map(d => [d['id'], d]));
|
// this.nodeMap = Object.fromEntries(this.graph.nodes.map(d => [d['id'], d]));
|
||||||
|
@ -493,216 +322,28 @@ class NodeMap {
|
||||||
.force("collision", d3.forceCollide(this.nodeSize))
|
.force("collision", d3.forceCollide(this.nodeSize))
|
||||||
// TODO look at simpler labels https://github.com/d3fc/d3fc/tree/master/packages/d3fc-label-layout
|
// TODO look at simpler labels https://github.com/d3fc/d3fc/tree/master/packages/d3fc-label-layout
|
||||||
// TODO look at rects https://github.com/emeeks/d3-bboxCollide
|
// TODO look at rects https://github.com/emeeks/d3-bboxCollide
|
||||||
// .force("posX", d3.forceX(n => n.targetX || 0).strength(n => n.targetX ? 1 : 0)) // TODO: should not be or 0
|
.force("posX", d3.forceX(n => n.targetX || 0).strength(n => n.targetX ? 1 : 0)) // TODO: should not be or 0
|
||||||
// .force("posY", d3.forceY(n => n.targetY || 0).strength(n => n.targetY ? 1 : 0))
|
.force("posY", d3.forceY(n => n.targetY || 0).strength(n => n.targetY ? 1 : 0))
|
||||||
;
|
;
|
||||||
|
|
||||||
this.svg
|
|
||||||
.call(this.zoom)
|
|
||||||
.call(this.zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
|
||||||
|
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
|
|
||||||
setTimeout(() => this.calculateLabels(), 1000);
|
setTimeout(() => this.calculateLabels(), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetZoom() {
|
|
||||||
this.deselectNode();
|
|
||||||
this.svg
|
|
||||||
.transition()
|
|
||||||
.duration(2000) // milliseconds
|
|
||||||
.call(this.zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
|
||||||
}
|
|
||||||
|
|
||||||
getSizeForNode(node) {
|
getSizeForNode(node) {
|
||||||
return this.nodeSize;
|
return this.nodeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeRadius(node) {
|
|
||||||
return CONFIG.nodeRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolveOverlaps() {
|
|
||||||
// reset:
|
|
||||||
this.graph.nodes.forEach((n) => {
|
|
||||||
if (n.hasOwnProperty('originalX')) {
|
|
||||||
n.x = n.originalX;
|
|
||||||
n.y = n.originalY;
|
|
||||||
n.fx = n.originalX;
|
|
||||||
n.fy = n.originalY;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.store.configureTree();
|
|
||||||
|
|
||||||
let moved = 0;
|
|
||||||
|
|
||||||
// resolve overlapping points by repositioning
|
|
||||||
this.graph.nodes.forEach((n) => {
|
|
||||||
// only for fixed points:
|
|
||||||
if (!n.hasOwnProperty('originalX')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const startX = n.originalX;
|
|
||||||
const startY = n.originalY;
|
|
||||||
let alpha = 0; // angle
|
|
||||||
let step = 1;
|
|
||||||
const d_alpha = Math.PI / 4;
|
|
||||||
const r = this.getNodeRadius(n) + CONFIG.nodeRepositionPadding / 2;
|
|
||||||
let foundNodes;
|
|
||||||
let i = 0;
|
|
||||||
// find a new pos until it's not overlapping anymore...
|
|
||||||
while ((foundNodes = this.store.findVisibleInCircle(n.x, n.y, r)).length > 1) {
|
|
||||||
this.store.quadtree.remove(n); // remove uses the current x,y so we need to do this before reconfiguring these
|
|
||||||
n.x = startX + Math.cos(alpha) * r * 2 * step;
|
|
||||||
n.y = startY + Math.sin(alpha) * r * 2 * step;
|
|
||||||
this.store.quadtree.add(n);
|
|
||||||
alpha += d_alpha;
|
|
||||||
// on to the next round:
|
|
||||||
if (alpha > Math.PI * 2) {
|
|
||||||
step++;
|
|
||||||
alpha -= Math.PI * 2;
|
|
||||||
alpha += d_alpha - 2; // little offset
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
|
|
||||||
// if(n.fulltext == 'Control Room (Venice)') {
|
|
||||||
// console.log(startX, startY, n.x, n.y, r, foundNodes);
|
|
||||||
// // this.store.configureTree();
|
|
||||||
// // this.store.findVisibleInCircle(n.x, n.y, r).forEach((found) => console.log(found.x, found.y, found));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
n.fx = n.x;
|
|
||||||
n.fy = n.y;
|
|
||||||
|
|
||||||
if (i > 0) {
|
|
||||||
// we moved something, update tree
|
|
||||||
|
|
||||||
console.debug('resolved for', n.fulltext, i);
|
|
||||||
moved++;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`moved ${moved} nodes`);
|
|
||||||
}
|
|
||||||
|
|
||||||
showTooltip(el, node, links) {
|
|
||||||
// TODO: make links optional (otherwise collect links here)
|
|
||||||
|
|
||||||
this.tooltipEl.innerHTML = `
|
|
||||||
<span class='category'>${getCategories(node)[0]}</span>
|
|
||||||
<h3>${node.fulltext}</h3>
|
|
||||||
`;
|
|
||||||
if (links.length) {
|
|
||||||
const rels = links.length === 1 ? 'relationship' : 'relationships';
|
|
||||||
this.tooltipEl.innerHTML += `
|
|
||||||
<span class='clickForMore'>Click to examine ${links.length} ${rels}</span>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
const rectTT = this.tooltipEl.getBoundingClientRect();
|
|
||||||
this.tooltipEl.style.top = (rect.top - rectTT.height) + 'px';
|
|
||||||
this.tooltipEl.style.left = (rect.left + rect.width / 2 - rectTT.width / 2) + 'px';
|
|
||||||
// console.log(el, node, rect.top);
|
|
||||||
|
|
||||||
this.tooltipEl.classList.add('visible');
|
|
||||||
}
|
|
||||||
|
|
||||||
hideTooltip() {
|
|
||||||
this.tooltipEl.classList.remove('visible');
|
|
||||||
}
|
|
||||||
|
|
||||||
selectNode(node) {
|
|
||||||
this.deselectNode(); // remove potential old selection
|
|
||||||
|
|
||||||
this.selectedNode = node;
|
|
||||||
let links = [];
|
|
||||||
let connectedNodes = [];
|
|
||||||
for (let link of this.graph.links) {
|
|
||||||
if (link.source == node || link.target == node) {
|
|
||||||
links.push(link);
|
|
||||||
const otherNode = node == link.target ? link.source : link.target;
|
|
||||||
connectedNodes.push(otherNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let allNodes = [...connectedNodes, node];
|
|
||||||
|
|
||||||
this.zoomFit(allNodes);
|
|
||||||
|
|
||||||
document.getElementById(node.id).classList.add('selected');
|
|
||||||
connectedNodes.forEach(n => document.getElementById(n.id).classList.add('linkedSelected'));
|
|
||||||
links.forEach(l => document.getElementById(getLinkId(l)).classList.add('linkedSelected'));
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: show details;
|
|
||||||
|
|
||||||
// alert('not yet implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
deselectNode() {
|
|
||||||
this.selectedNode = null;
|
|
||||||
let nodeEls = document.getElementsByClassName('selected');
|
|
||||||
while (nodeEls.length) {
|
|
||||||
nodeEls[0].classList.remove('selected');
|
|
||||||
}
|
|
||||||
let els = document.getElementsByClassName('linkedSelected');
|
|
||||||
while (els.length) {
|
|
||||||
els[0].classList.remove('linkedSelected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
// console.log(this.graph)
|
console.log(this.graph)
|
||||||
|
|
||||||
this.resolveOverlaps();
|
|
||||||
|
|
||||||
// see also: https://www.createwithdata.com/enter-exit-with-d3-join/
|
// see also: https://www.createwithdata.com/enter-exit-with-d3-join/
|
||||||
this.node = this.node.data(this.graph.nodes, d => d.id)
|
this.node = this.node.data(this.graph.nodes, d => d.id)
|
||||||
.join((enter) => {
|
.join((enter) => {
|
||||||
let group = enter.append("g")
|
let group = enter.append("g").attr("class", getClasses);
|
||||||
.attr("class", getClasses)
|
|
||||||
.attr("id", (n) => getIdForTitle(n.fulltext));
|
|
||||||
// group.call(drag(simulation));
|
// group.call(drag(simulation));
|
||||||
group.on("click", (evt, n) => {
|
group.on("click", (evt, n) => selectNode(evt, n, node));
|
||||||
evt.stopPropagation(); this.selectNode(n);
|
group.append('circle').attr("r", 5 /*this.nodeSize*/);
|
||||||
});
|
|
||||||
group.on("mouseover", (evt, n) => {
|
|
||||||
// d3.select(this).classed('hover', true);
|
|
||||||
const links = document.getElementsByClassName('link');
|
|
||||||
const linkedLinks = [];
|
|
||||||
for (let link of links) {
|
|
||||||
const l = d3.select(link).datum();
|
|
||||||
if (n == l.target || n == l.source) {
|
|
||||||
link.classList.add('linkedHover');
|
|
||||||
// make sure it's the last element, so it's drawn on top
|
|
||||||
// link.parentNode.appendChild(link); .. causes gliches
|
|
||||||
// find related related node:
|
|
||||||
const otherNode = n == l.target ? l.source : l.target;
|
|
||||||
const otherNodeEl = document.getElementById(otherNode.id);
|
|
||||||
otherNodeEl.classList.add('linkedHover');
|
|
||||||
linkedLinks.push(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.showTooltip(evt.target, n, linkedLinks);
|
|
||||||
|
|
||||||
});
|
|
||||||
group.on("mouseout", (evt, n) => {
|
|
||||||
this.hideTooltip();
|
|
||||||
const links = document.getElementsByClassName('linkedHover');
|
|
||||||
while (links.length) {
|
|
||||||
links[0].classList.remove('linkedHover');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// group.append('circle').attr("r", 5 /*this.nodeSize*/);
|
|
||||||
group.append('path')
|
|
||||||
.attr('d', (n) => {
|
|
||||||
return getSymbolForNode(n)(n);
|
|
||||||
})
|
|
||||||
var nodeTitle = group.append('text').attr("class", "nodeTitle").attr("y", "3").attr('x', 5);
|
var nodeTitle = group.append('text').attr("class", "nodeTitle").attr("y", "3").attr('x', 5);
|
||||||
nodeTitle
|
nodeTitle
|
||||||
.each(function (node, i, nodes) {
|
.each(function (node, i, nodes) {
|
||||||
|
@ -791,12 +432,10 @@ class NodeMap {
|
||||||
.data(this.graph.links)
|
.data(this.graph.links)
|
||||||
.join(
|
.join(
|
||||||
enter => {
|
enter => {
|
||||||
let group = enter.append("g")
|
let group = enter.append("g").attr("class", "link");
|
||||||
.attr("class", (l) => "link " + slugify(l.name))
|
|
||||||
.attr("id", getLinkId);
|
|
||||||
group.append("path")
|
group.append("path")
|
||||||
.attr("marker-end", "url(#arrowHead)")
|
.attr("marker-end", "url(#arrowHead)")
|
||||||
.attr('id', (d, i) => 'linkpath_' + i)
|
.attr('id', (d, i) => 'linkid_' + i)
|
||||||
.on("mouseover", function (ev, link) {
|
.on("mouseover", function (ev, link) {
|
||||||
d3.select(this).classed('hover', true);
|
d3.select(this).classed('hover', true);
|
||||||
const nodes = document.getElementsByClassName('node');
|
const nodes = document.getElementsByClassName('node');
|
||||||
|
@ -810,18 +449,14 @@ class NodeMap {
|
||||||
}).on("mouseout", function (ev, link) {
|
}).on("mouseout", function (ev, link) {
|
||||||
d3.select(this).classed('hover', false);
|
d3.select(this).classed('hover', false);
|
||||||
const nodes = document.getElementsByClassName('linkHover');
|
const nodes = document.getElementsByClassName('linkHover');
|
||||||
while (nodes.length) {
|
for (let n of nodes) {
|
||||||
nodes[0].classList.remove('linkHover');
|
n.classList.remove('linkHover');
|
||||||
}
|
}
|
||||||
// l.classed('hover',false);
|
// l.classed('hover',false);
|
||||||
// l.target.classed('hover',false);
|
// l.target.classed('hover',false);
|
||||||
// l.source.classed('hover',false);
|
// l.source.classed('hover',false);
|
||||||
// console.log(l,'l');
|
// console.log(l,'l');
|
||||||
}).on("click", (ev, link) => {
|
});
|
||||||
ev.stopPropagation();
|
|
||||||
this.selectNode(link.source);
|
|
||||||
})
|
|
||||||
;
|
|
||||||
group.filter((l) => l.name != "City").append("text").attr("class", "labelText").text(function (l) {
|
group.filter((l) => l.name != "City").append("text").attr("class", "labelText").text(function (l) {
|
||||||
return l.name;
|
return l.name;
|
||||||
});
|
});
|
||||||
|
@ -955,60 +590,6 @@ class NodeMap {
|
||||||
redraw() {
|
redraw() {
|
||||||
this.update()
|
this.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
// viewBox + preserveAspectRatio can lead to a visible area that is larger than
|
|
||||||
// the viewBox. Try to get this
|
|
||||||
getVisibleBox() {
|
|
||||||
const svgEl = this.svg.node()
|
|
||||||
const vbRatio = svgEl.viewBox.baseVal.width / svgEl.viewBox.baseVal.height;
|
|
||||||
const wRatio = this.width / this.height;
|
|
||||||
if (wRatio > vbRatio) {
|
|
||||||
// wider
|
|
||||||
return {
|
|
||||||
width: (wRatio / vbRatio) * svgEl.viewBox.baseVal.width,
|
|
||||||
height: svgEl.viewBox.baseVal.height
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// taller
|
|
||||||
return {
|
|
||||||
width: svgEl.viewBox.baseVal.width,
|
|
||||||
height: (vbRatio / wRatio) * svgEl.viewBox.baseVal.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zoomFit(nodes, paddingPercent = 0.8, transitionDuration = 2000) {
|
|
||||||
// var bounds = root.node().getBBox();
|
|
||||||
const x0 = Math.min(...nodes.map(n => n.x - CONFIG.nodeRadius));
|
|
||||||
const x1 = Math.max(...nodes.map(n => n.x + CONFIG.nodeRadius));
|
|
||||||
const y0 = Math.min(...nodes.map(n => n.y - CONFIG.nodeRadius));
|
|
||||||
const y1 = Math.max(...nodes.map(n => n.y + CONFIG.nodeRadius));
|
|
||||||
|
|
||||||
const width = x1 - x0;
|
|
||||||
const height = y1 - y0;
|
|
||||||
|
|
||||||
const visibleBox = this.getVisibleBox();
|
|
||||||
const fullWidth = visibleBox.width, //2000, //this.width, TODO: use viewbox now, but consider the overflowing of the vbox
|
|
||||||
fullHeight = visibleBox.height; //2000; //this.height;
|
|
||||||
const viewBox = this.svg.node().viewBox.baseVal;
|
|
||||||
const midX = x0 + width / 2,
|
|
||||||
midY = y0 + height / 2;
|
|
||||||
if (width == 0 || height == 0) return; // nothing to fit
|
|
||||||
let scale = paddingPercent / Math.max(width / fullWidth, height / fullHeight);
|
|
||||||
scale = Math.min(CONFIG.zoom.scale_max, scale);
|
|
||||||
const translate = [fullWidth / 2 - midX, fullHeight / 2 - midY];
|
|
||||||
const transform = d3.zoomIdentity.translate(
|
|
||||||
-midX * scale + viewBox.width / 2,
|
|
||||||
-midY * scale + viewBox.height / 2)
|
|
||||||
.scale(scale);
|
|
||||||
// console.log("zoomFit", x0, x1, y0, y1, translate, scale, transform);
|
|
||||||
this.svg
|
|
||||||
.transition()
|
|
||||||
.duration(transitionDuration || 0) // milliseconds
|
|
||||||
.call(this.zoom.transform, transform);
|
|
||||||
// .call(this.zoom.translateTo, midX, midY)
|
|
||||||
// .call(this.zoom.scaleTo, scale);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlluvialMap {
|
class AlluvialMap {
|
||||||
|
@ -1044,7 +625,6 @@ class AlluvialMap {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.svg = this.root.append('svg')
|
this.svg = this.root.append('svg')
|
||||||
|
|
||||||
this.resize();
|
this.resize();
|
||||||
|
|
||||||
this.sankey = d3.sankey()
|
this.sankey = d3.sankey()
|
||||||
|
@ -1053,7 +633,7 @@ class AlluvialMap {
|
||||||
.nodeWidth(40) // height
|
.nodeWidth(40) // height
|
||||||
.nodePadding(10)
|
.nodePadding(10)
|
||||||
.extent([[1, 5], [2000 - 1, 2000 - 5]]);
|
.extent([[1, 5], [2000 - 1, 2000 - 5]]);
|
||||||
// console.log(this.graph);
|
console.log(this.graph);
|
||||||
let s = this.sankey({
|
let s = this.sankey({
|
||||||
nodes: this.graph.nodes.map(d => Object.assign({}, d)),
|
nodes: this.graph.nodes.map(d => Object.assign({}, d)),
|
||||||
links: this.graph.links.map(d => Object.assign({}, d))
|
links: this.graph.links.map(d => Object.assign({}, d))
|
||||||
|
@ -1063,7 +643,7 @@ class AlluvialMap {
|
||||||
this.links = s.links;
|
this.links = s.links;
|
||||||
|
|
||||||
const scale = d3.scaleOrdinal(d3.schemeCategory10);
|
const scale = d3.scaleOrdinal(d3.schemeCategory10);
|
||||||
const color = (c) => c == "Unknown" ? "#333" : scale(c);
|
const color = (c) => c == "Unknown" ? "#333": scale(c);
|
||||||
|
|
||||||
|
|
||||||
this.svg.append("g")
|
this.svg.append("g")
|
||||||
|
@ -1089,12 +669,12 @@ class AlluvialMap {
|
||||||
.join("g")
|
.join("g")
|
||||||
.style("mix-blend-mode", "multiply");
|
.style("mix-blend-mode", "multiply");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const edgeColor = 'path'; // either: path, none, input, output
|
const edgeColor = 'path'; // either: path, none, input, output
|
||||||
if (edgeColor === "path") {
|
if (edgeColor === "path") {
|
||||||
const gradient = link.append("linearGradient")
|
const gradient = link.append("linearGradient")
|
||||||
.attr("id", (d, i) => {
|
.attr("id", (d,i) => {
|
||||||
const id = `link-${i}`; // thanks https://talk.observablehq.com/t/how-do-i-work-with-the-d3-sankey-example/1696/3
|
const id = `link-${i}`; // thanks https://talk.observablehq.com/t/how-do-i-work-with-the-d3-sankey-example/1696/3
|
||||||
d.uid = `url(#${id})`;
|
d.uid = `url(#${id})`;
|
||||||
return id;
|
return id;
|
||||||
|
@ -1102,12 +682,12 @@ class AlluvialMap {
|
||||||
.attr("gradientUnits", "userSpaceOnUse")
|
.attr("gradientUnits", "userSpaceOnUse")
|
||||||
.attr("y1", d => d.source.x1)
|
.attr("y1", d => d.source.x1)
|
||||||
.attr("y2", d => d.target.x0)
|
.attr("y2", d => d.target.x0)
|
||||||
.attr("x1", 0)
|
.attr("x1",0)
|
||||||
.attr("x2", 0);
|
.attr("x2", 0);
|
||||||
// .attr("y1", "0%")
|
// .attr("y1", "0%")
|
||||||
// .attr("y2", "100%")
|
// .attr("y2", "100%")
|
||||||
// .attr("x1", "0%")
|
// .attr("x1", "0%")
|
||||||
// .attr("x2", "0%");
|
// .attr("x2", "0%");
|
||||||
|
|
||||||
gradient.append("stop")
|
gradient.append("stop")
|
||||||
.attr("offset", "0%")
|
.attr("offset", "0%")
|
||||||
|
@ -1153,13 +733,7 @@ class AlluvialMap {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
titleIdMap = {};
|
|
||||||
function getIdForTitle(title) {
|
|
||||||
if (!titleIdMap.hasOwnProperty(title)) {
|
|
||||||
titleIdMap[title] = slugify(title) + `-${Object.keys(titleIdMap).length}`
|
|
||||||
}
|
|
||||||
return titleIdMap[title];
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonToGraph = function (data) {
|
JsonToGraph = function (data) {
|
||||||
let nodes = [];
|
let nodes = [];
|
||||||
|
@ -1167,13 +741,10 @@ JsonToGraph = function (data) {
|
||||||
|
|
||||||
let smwBugFixLocationMaps = {};
|
let smwBugFixLocationMaps = {};
|
||||||
console.log(data)
|
console.log(data)
|
||||||
let i = 0;
|
|
||||||
let linkI = 0;
|
|
||||||
for (const node_id in data.results) {
|
for (const node_id in data.results) {
|
||||||
if (Object.hasOwnProperty.call(data.results, node_id)) {
|
if (Object.hasOwnProperty.call(data.results, node_id)) {
|
||||||
i++;
|
|
||||||
let node = data.results[node_id];
|
let node = data.results[node_id];
|
||||||
node.id = getIdForTitle(node.fulltext); //node_id;
|
node.id = node.fulltext; //node_id;
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
// console.log(node_id, node);
|
// console.log(node_id, node);
|
||||||
|
|
||||||
|
@ -1193,9 +764,9 @@ JsonToGraph = function (data) {
|
||||||
}
|
}
|
||||||
for (const target_node of node.printouts[prop]) {
|
for (const target_node of node.printouts[prop]) {
|
||||||
links.push({
|
links.push({
|
||||||
"source": node.id,
|
"source": node_id,
|
||||||
"target": getIdForTitle(target_node.fulltext),
|
"target": target_node.fulltext,
|
||||||
"name": prop,
|
"name": prop
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1224,18 +795,14 @@ JsonToGraph = function (data) {
|
||||||
|
|
||||||
console.debug(`Fixed location for ${fixes} nodes`);
|
console.debug(`Fixed location for ${fixes} nodes`);
|
||||||
|
|
||||||
|
console.log(links.length);
|
||||||
|
|
||||||
nodeMap = Object.fromEntries(nodes.map(d => [d['id'], d]));
|
nodeMap = Object.fromEntries(nodes.map(d => [d['id'], d]));
|
||||||
links = links.filter(l => nodeMap[l.source] && nodeMap[l.target]).map(l => {
|
links = links.filter(l => nodeMap[l.source] && nodeMap[l.target]).map(l => {
|
||||||
l.source = nodeMap[l.source];
|
l.source = nodeMap[l.source];
|
||||||
l.target = nodeMap[l.target];
|
l.target = nodeMap[l.target];
|
||||||
l.nr = linkI++;
|
|
||||||
return l;
|
return l;
|
||||||
}).filter((link, index, self) =>
|
});
|
||||||
// remove incidental duplicates
|
|
||||||
index === self.findIndex((l) => (
|
|
||||||
l.source.id === link.source.id && l.target.id === link.target.id && l.name === link.name
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1432,7 +999,6 @@ class Store {
|
||||||
constructor(graph, parent) {
|
constructor(graph, parent) {
|
||||||
this.nodes = graph.nodes;
|
this.nodes = graph.nodes;
|
||||||
this.links = graph.links;
|
this.links = graph.links;
|
||||||
// graph is a filtered version of this.nodes & this.links
|
|
||||||
this.graph = {
|
this.graph = {
|
||||||
'nodes': [],
|
'nodes': [],
|
||||||
'links': []
|
'links': []
|
||||||
|
@ -1446,52 +1012,10 @@ class Store {
|
||||||
'categories': [],
|
'categories': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.filter();
|
this.filter();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configureTree() {
|
|
||||||
// set up the tree, we do this only after all points are configured.
|
|
||||||
this.quadtree = d3.quadtree(
|
|
||||||
this.nodes,
|
|
||||||
(n) => n.x,
|
|
||||||
(n) => n.y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// from: https://observablehq.com/@d3/quadtree-findincircle
|
|
||||||
findInCircle(x, y, radius, filter) {
|
|
||||||
if (typeof this.quadtree === 'undefined') {
|
|
||||||
this.configureTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = [],
|
|
||||||
radius2 = radius * radius,
|
|
||||||
accept = filter
|
|
||||||
? d => filter(d) && result.push(d)
|
|
||||||
: d => result.push(d);
|
|
||||||
|
|
||||||
this.quadtree.visit((node, x1, y1, x2, y2) => {
|
|
||||||
if (node.length) {
|
|
||||||
return x1 >= x + radius || y1 >= y + radius || x2 < x - radius || y2 < y - radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dx = +this.quadtree._x.call(null, node.data) - x,
|
|
||||||
dy = +this.quadtree._y.call(null, node.data) - y;
|
|
||||||
if (dx * dx + dy * dy < radius2) {
|
|
||||||
do { accept(node.data); } while (node = node.next);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
findVisibleInCircle(x, y, radius) {
|
|
||||||
return this.findInCircle(x, y, radius, (n) => !n.filtered);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerMap(map) {
|
registerMap(map) {
|
||||||
this._maps.push(map);
|
this._maps.push(map);
|
||||||
return this;
|
return this;
|
||||||
|
@ -1543,16 +1067,9 @@ class Store {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
CONFIG.filters.forEach(f => {
|
CONFIG.filters.forEach(f => {
|
||||||
|
|
||||||
let labelEl = document.createElement('label')
|
let labelEl = document.createElement('label')
|
||||||
let inputEl = document.createElement('input')
|
let inputEl = document.createElement('input')
|
||||||
let textEl = document.createElement('span');
|
let textEl = document.createElement('span');
|
||||||
let svg = d3.select(labelEl).append('svg')
|
|
||||||
.attr("viewBox", [-12, -12, 24, 24]);
|
|
||||||
svg.append('g')
|
|
||||||
.attr("class", "node " + f)
|
|
||||||
.append('path')
|
|
||||||
.attr('d', getSymbolForCategories(f)());
|
|
||||||
inputEl.type = "checkbox";
|
inputEl.type = "checkbox";
|
||||||
textEl.innerText = f;
|
textEl.innerText = f;
|
||||||
labelEl.appendChild(inputEl);
|
labelEl.appendChild(inputEl);
|
||||||
|
@ -1586,7 +1103,7 @@ class Store {
|
||||||
|
|
||||||
|
|
||||||
var mapGraph = new NodeMap('#map')
|
var mapGraph = new NodeMap('#map')
|
||||||
// var alluvialGraph = new AlluvialMap('#alluvial')
|
var alluvialGraph = new AlluvialMap('#alluvial')
|
||||||
|
|
||||||
// REQUEST ATLAS & GRAPH
|
// REQUEST ATLAS & GRAPH
|
||||||
const req_data = new Request(CONFIG.dataUrl, { method: 'GET' });
|
const req_data = new Request(CONFIG.dataUrl, { method: 'GET' });
|
||||||
|
@ -1601,11 +1118,12 @@ Promise.all([fetch(req_data), fetch(req_world)])
|
||||||
|
|
||||||
mapGraph.setWorld(world);
|
mapGraph.setWorld(world);
|
||||||
mapGraph.setStore(store);
|
mapGraph.setStore(store);
|
||||||
// alluvialGraph.setData(data.results);
|
// console.log();
|
||||||
|
alluvialGraph.setData(data.results);
|
||||||
|
|
||||||
store.render()
|
store.render()
|
||||||
mapGraph.render()
|
mapGraph.render()
|
||||||
// alluvialGraph.render()
|
alluvialGraph.render()
|
||||||
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
|
@ -3,41 +3,27 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" href="graph.css">
|
<link rel="stylesheet" href="graph.css">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id='tooltip'></div>
|
<div id='map'></div>
|
||||||
<div id='map'></div>
|
<div id='alluvial'></div>
|
||||||
<!-- <div id='alluvial'></div> -->
|
|
||||||
|
|
||||||
<header>
|
<div id="filters">
|
||||||
<h1>Remote Biometric Identification</h1>
|
|
||||||
<p class='subtitle'>A survey of the European Union</p>
|
</div>
|
||||||
|
|
||||||
|
<script src="https://d3js.org/d3.v6.js"></script>
|
||||||
|
|
||||||
<aside id="filters">
|
<script src="https://d3js.org/d3-geo-projection.v3.min.js"></script>
|
||||||
<h3>Legend</h3>
|
<script src="https://d3js.org/topojson.v3.min.js"></script>
|
||||||
</aside>
|
<script src="//unpkg.com/d3-geo-zoom"></script>
|
||||||
|
|
||||||
</header>
|
<script src="https://unpkg.com/d3-sankey@0"></script>
|
||||||
|
<!-- <script src="//unpkg.com/d3fc@14.0.1"></script> -->
|
||||||
<script src="https://d3js.org/d3.v6.js"></script>
|
<script src="graph.js"></script>
|
||||||
|
|
||||||
<script src="https://d3js.org/d3-geo-projection.v3.min.js"></script>
|
|
||||||
<script src="https://d3js.org/topojson.v3.min.js"></script>
|
|
||||||
<script src="//unpkg.com/d3-geo-zoom"></script>
|
|
||||||
|
|
||||||
<script src="https://unpkg.com/d3-sankey@0"></script>
|
|
||||||
<!-- <script src="//unpkg.com/d3fc@14.0.1"></script> -->
|
|
||||||
<script src="graph.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
if(window.location.hash == '#light') {
|
|
||||||
document.body.classList.add('light');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
506
www/story.html
506
www/story.html
|
@ -1,506 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Biometric Mass Surveillance</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 50vw;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
content {
|
|
||||||
margin-left: 55vw;
|
|
||||||
max-width: 800px;
|
|
||||||
margin-right: 5vw;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<iframe id='frame' src="index.html"></iframe>
|
|
||||||
|
|
||||||
<content>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi risus augue, pellentesque quis iaculis
|
|
||||||
vitae, condimentum a leo. Nunc scelerisque, odio a tristique molestie, massa mi sollicitudin odio, ac
|
|
||||||
fermentum orci nisi a ipsum. Curabitur pellentesque eleifend risus, eget porta tellus malesuada ut. Nunc
|
|
||||||
dignissim, ipsum pellentesque accumsan faucibus, mi leo mattis ante, a rhoncus massa leo vel augue.
|
|
||||||
Mauris eros nunc, vehicula et faucibus at, convallis non odio. Ut gravida, lorem non sodales maximus,
|
|
||||||
purus justo blandit felis, at sollicitudin dolor erat eget est. Ut euismod auctor lorem, iaculis
|
|
||||||
pulvinar odio tempus et. Proin ullamcorper cursus posuere. Donec venenatis diam nulla, sed egestas ipsum
|
|
||||||
vestibulum ultrices. Proin lobortis elementum semper. Morbi sollicitudin aliquam tortor, ac maximus urna
|
|
||||||
finibus vel. Vivamus ultricies, nibh non hendrerit rutrum, sapien neque eleifend leo, nec lobortis
|
|
||||||
libero orci ut sapien.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nulla porttitor dolor laoreet elit ornare, eu pellentesque nulla tempus. Duis et suscipit massa. Etiam
|
|
||||||
velit lorem, ornare at odio vel, malesuada imperdiet massa. Pellentesque mauris justo, sollicitudin quis
|
|
||||||
varius vel, dignissim ut turpis. Maecenas vel odio mauris. Aliquam maximus maximus erat vel fermentum.
|
|
||||||
Aenean vel venenatis diam. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur risus
|
|
||||||
risus, facilisis sed velit a, feugiat eleifend quam. Donec rutrum molestie sem.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Ut metus elit, porta et turpis in, sodales imperdiet ligula. Nam vulputate ultricies urna non facilisis.
|
|
||||||
Vivamus sit amet pulvinar risus. Nullam id odio tempus, malesuada metus et, lobortis nibh. Suspendisse
|
|
||||||
viverra nulla magna, id tincidunt turpis pretium rhoncus. Pellentesque vel aliquet dui. Mauris eleifend
|
|
||||||
efficitur libero eu porttitor. Aenean congue interdum dui eget aliquam. Maecenas volutpat porta quam,
|
|
||||||
vitae congue justo cursus in. Fusce ac congue lorem. Nulla pretium odio ac quam aliquam convallis.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Fusce a urna ac lorem eleifend eleifend. Etiam mollis quam nisl, et scelerisque felis lacinia quis.
|
|
||||||
Mauris ullamcorper mi et feugiat auctor. Sed placerat tincidunt tincidunt. Vestibulum dictum vestibulum
|
|
||||||
orci dignissim ornare. Nullam sed congue sapien. Donec rhoncus nibh quis malesuada egestas. Cras non est
|
|
||||||
quam. Curabitur vulputate, metus non faucibus rutrum, ex lorem viverra ligula, sodales vehicula sapien
|
|
||||||
diam quis justo. Quisque tempor maximus mauris sit amet eleifend. Cras auctor feugiat ullamcorper.
|
|
||||||
Mauris ultricies elit vel euismod volutpat. Praesent in elit dignissim, dignissim urna varius,
|
|
||||||
consectetur lacus. Sed ligula tellus, consectetur a ligula a, tempus hendrerit urna. Maecenas volutpat
|
|
||||||
sapien risus, ac egestas ex porta et.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nullam velit quam, efficitur vitae sem nec, sodales porttitor orci. Sed egestas lacus sit amet ex
|
|
||||||
dictum, sit amet tincidunt ex hendrerit. Morbi pharetra, ipsum sed gravida fermentum, felis quam rhoncus
|
|
||||||
lacus, vitae auctor enim ligula pulvinar tellus. Cras sapien augue, ultricies pulvinar arcu et,
|
|
||||||
convallis porttitor augue. Vestibulum accumsan lorem eros, id volutpat orci tincidunt at. Vivamus porta
|
|
||||||
magna id finibus tristique. Quisque sodales augue in lacus tristique ornare. Curabitur cursus hendrerit
|
|
||||||
neque rutrum blandit. Maecenas posuere ex eget eleifend fringilla.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Integer est dui, elementum in nisl nec, blandit auctor libero. Cras in nulla nunc. Curabitur fringilla
|
|
||||||
dolor sagittis felis fringilla, non euismod felis tempus. Aenean scelerisque euismod nisl vitae
|
|
||||||
tincidunt. Nam nec purus eleifend, fringilla diam quis, faucibus ex. Maecenas nunc felis, rhoncus sed
|
|
||||||
sollicitudin non, luctus sed dolor. Phasellus elit neque, rutrum a est venenatis, convallis imperdiet
|
|
||||||
dui. Vestibulum at ultrices eros, at vulputate sem. Ut vestibulum libero sed sapien sagittis, id laoreet
|
|
||||||
velit eleifend. Phasellus eleifend nec odio non bibendum. Suspendisse nec lectus nec ex blandit rutrum
|
|
||||||
nec et felis. Cras libero justo, sagittis sed lacinia quis, pharetra quis nibh. Maecenas vitae lacus ac
|
|
||||||
augue condimentum suscipit.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Pellentesque eu tempor orci. Aenean pulvinar, justo ac maximus molestie, nibh nunc rutrum ex, varius
|
|
||||||
finibus mi nulla lobortis tellus. Nam dictum leo maximus ultrices maximus. Aliquam bibendum libero sem,
|
|
||||||
mattis pulvinar erat vulputate quis. Duis venenatis nisi ac sem iaculis, nec feugiat orci elementum.
|
|
||||||
Curabitur accumsan lacinia justo id suscipit. In lacinia ornare justo quis egestas. Nullam nec lacus a
|
|
||||||
ligula pulvinar tempus eget id nunc. Morbi tincidunt placerat placerat.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" data-title="Dragonfly Project">
|
|
||||||
<h2>Project Dragonfly</h2>
|
|
||||||
<p>
|
|
||||||
Morbi vitae nulla erat. Etiam eros ipsum, tristique in maximus sed, rutrum a mi. Integer molestie
|
|
||||||
imperdiet mauris sit amet lacinia. Aenean dignissim posuere lacus, ut vulputate tellus venenatis
|
|
||||||
posuere. Nullam suscipit tempor massa at viverra. Sed volutpat justo ut lacinia luctus. Sed egestas
|
|
||||||
hendrerit tellus, non bibendum quam gravida non. Aenean suscipit ligula tristique venenatis molestie.
|
|
||||||
Nunc aliquet orci mi.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nunc vel dolor lorem. Sed venenatis lacinia sapien ut aliquam. Nulla vitae tincidunt sem. Aliquam eu
|
|
||||||
nisl pharetra, ornare justo nec, egestas nisi. Nam et metus vitae enim tempus vestibulum. Fusce ipsum
|
|
||||||
quam, maximus et mattis nec, consectetur vitae urna. Vivamus rhoncus augue sed justo bibendum, in
|
|
||||||
imperdiet metus vestibulum. Nam egestas eleifend enim ultricies lobortis. Pellentesque sed nibh eros.
|
|
||||||
Nulla mattis facilisis felis, eu lacinia neque condimentum ut. Suspendisse fermentum ultrices faucibus.
|
|
||||||
Aliquam ac ultrices lectus, in pharetra lectus. Curabitur in molestie felis. Proin ac ante eget sem
|
|
||||||
fringilla hendrerit sit amet a leo. Sed at nunc vitae dolor vestibulum luctus quis ac nunc. Suspendisse
|
|
||||||
nunc justo, gravida id pharetra et, convallis nec velit.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nunc dui leo, varius ac venenatis nec, vehicula et dui. Pellentesque quis enim dolor. Vestibulum
|
|
||||||
tincidunt egestas porta. Suspendisse ac nisl nisl. Pellentesque auctor convallis euismod. In pharetra
|
|
||||||
iaculis accumsan. Maecenas scelerisque molestie dui eu gravida. Aliquam hendrerit ipsum vitae laoreet
|
|
||||||
laoreet. Phasellus nec mattis leo, at rhoncus sem. Nam maximus, dolor eget consequat mattis, tortor leo
|
|
||||||
blandit mi, in lacinia est ligula et erat. Fusce at nisi eleifend, dapibus leo eget, porttitor ipsum.
|
|
||||||
Duis tristique velit vitae dui fermentum, quis gravida lorem tempor. Phasellus sed risus diam. Aenean
|
|
||||||
fringilla justo ac felis lacinia, quis placerat tellus pretium.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Vestibulum ante nisl, porta vitae ultrices in, fermentum eget tellus. Quisque ornare, leo aliquet tempor
|
|
||||||
aliquet, ligula diam vehicula nisi, nec faucibus mauris ex at dui. Phasellus vel ligula suscipit lacus
|
|
||||||
eleifend egestas. Suspendisse ut lacus suscipit, gravida dui at, volutpat dolor. Nunc suscipit erat ac
|
|
||||||
risus tempus tempor. Nulla quis mi blandit, hendrerit mauris eget, commodo nisl. Aliquam ultricies purus
|
|
||||||
metus, quis gravida ante condimentum quis. Mauris porta vestibulum ornare. Vivamus elementum pharetra
|
|
||||||
nisl, a vehicula nibh. Etiam ac turpis tortor.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nunc non elit ac diam interdum pretium ac sed velit. Etiam non metus elementum, consectetur ante vel,
|
|
||||||
iaculis lorem. Pellentesque convallis finibus ipsum ut fermentum. Morbi at eros vel felis pharetra
|
|
||||||
dapibus faucibus et nisl. Nunc sit amet felis nec leo malesuada aliquam quis in risus. Sed quis leo
|
|
||||||
felis. Curabitur in porta urna. Nulla dignissim, tortor et sodales auctor, ante odio porttitor nunc, ut
|
|
||||||
tincidunt ligula dolor non sapien. Suspendisse vitae iaculis urna, id commodo sem.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nulla facilisi. Sed at augue sit amet erat posuere consectetur. Aliquam pretium lectus ipsum, nec ornare
|
|
||||||
enim tincidunt sit amet. Praesent sit amet iaculis odio. Etiam nibh risus, pulvinar vel arcu quis,
|
|
||||||
elementum faucibus mauris. Curabitur posuere leo a consectetur hendrerit. Proin finibus lobortis turpis,
|
|
||||||
ac feugiat est elementum at. Suspendisse vitae neque id nisi ultricies viverra. Curabitur ut libero sed
|
|
||||||
arcu accumsan accumsan a sed ex. Sed suscipit non sapien non eleifend. Vivamus condimentum scelerisque
|
|
||||||
est, sit amet sollicitudin nisi lacinia nec. Nunc in lacus consectetur neque ultrices laoreet ut et est.
|
|
||||||
Quisque vulputate odio neque, eget laoreet felis molestie sit amet.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class='section' data-title="Data-lab Burglary-free Neighbourhood">
|
|
||||||
<h2>Burglary-free Neighbourhood</h2>
|
|
||||||
<p>
|
|
||||||
Aenean fringilla nisl sed ante congue, in commodo tellus ullamcorper. Nullam sollicitudin, lacus id
|
|
||||||
interdum facilisis, erat augue feugiat justo, ac finibus nunc tellus ac odio. Cras a tempor nulla.
|
|
||||||
Nullam consequat convallis eros, sed interdum orci. Etiam justo lectus, ornare nec risus ut,
|
|
||||||
pellentesque mattis ante. Integer tristique sapien quis lacus cursus luctus. Pellentesque risus lacus,
|
|
||||||
auctor eget lacus eu, dapibus pretium ligula. Sed eget tortor ut lacus elementum facilisis. Pellentesque
|
|
||||||
velit ante, sagittis vel odio nec, rhoncus sagittis sem. Duis sodales lectus eget suscipit scelerisque.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Donec suscipit, nulla at congue scelerisque, eros odio consectetur quam, a ornare ex leo a sapien.
|
|
||||||
Curabitur gravida massa at posuere aliquet. Maecenas consequat, dui quis posuere hendrerit, nunc ipsum
|
|
||||||
interdum erat, sit amet ultricies justo nibh vel nulla. Integer venenatis semper ipsum quis congue.
|
|
||||||
Nulla facilisi. Proin dapibus diam nec erat viverra imperdiet. Proin quis rhoncus sem. Maecenas
|
|
||||||
ultricies elementum mauris, sit amet rutrum libero laoreet cursus. Maecenas quam velit, consectetur sit
|
|
||||||
amet sapien vitae, tempor egestas nisi. Suspendisse pulvinar ultricies erat sit amet pretium. In luctus,
|
|
||||||
diam vitae ornare feugiat, risus eros tincidunt orci, at convallis lacus lacus quis felis.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Praesent et facilisis elit, id ultricies magna. Donec interdum sed dolor vitae blandit. Vestibulum
|
|
||||||
hendrerit eros et magna finibus, nec pharetra est aliquam. Suspendisse tempor sagittis maximus. Ut
|
|
||||||
porttitor tellus ipsum, non malesuada lectus vulputate a. Fusce vitae bibendum justo, non aliquam elit.
|
|
||||||
Mauris fringilla purus sit amet aliquet laoreet. Nulla tempus ac metus luctus porta. Donec quis tellus
|
|
||||||
luctus, dignissim nisl vitae, iaculis eros. Duis nisl nulla, rhoncus eu iaculis non, faucibus vitae
|
|
||||||
risus. Pellentesque consectetur faucibus metus. Nulla facilisi.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Mauris maximus ullamcorper mattis. Morbi consectetur mollis sapien, sed ultricies ipsum interdum vitae.
|
|
||||||
Proin feugiat vitae elit eget pharetra. Sed in maximus nisl, non ultrices massa. In nec ipsum placerat,
|
|
||||||
tempor neque vitae, consequat neque. Nam in tempor libero. Sed feugiat massa pellentesque malesuada
|
|
||||||
volutpat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec
|
|
||||||
vel odio vel orci auctor aliquam convallis id lacus. Sed euismod ultricies ullamcorper. Morbi lobortis
|
|
||||||
luctus interdum. Quisque ac nisl lobortis, elementum tortor et, euismod erat.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Etiam at dolor mauris. Morbi in mauris scelerisque, suscipit lacus id, condimentum est. Vestibulum ante
|
|
||||||
ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed rutrum lobortis ullamcorper.
|
|
||||||
Vivamus ac mi non libero rutrum tempus. Donec ante ipsum, gravida eu magna auctor, efficitur molestie
|
|
||||||
augue. Aenean a justo eros. Vivamus aliquet pretium eros, tempus feugiat odio molestie vitae. Aenean
|
|
||||||
mattis gravida erat, eget venenatis dolor faucibus nec. Nullam in risus feugiat, aliquam nisi vel,
|
|
||||||
tincidunt tortor. Donec quis imperdiet felis. Curabitur suscipit auctor purus quis faucibus. Cras nec
|
|
||||||
diam rutrum, pharetra erat ut, pellentesque augue. Morbi mattis massa ut ante congue blandit. Orci
|
|
||||||
varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Integer tincidunt odio non mauris egestas viverra. Nulla blandit vulputate enim vel vestibulum. Donec
|
|
||||||
suscipit dignissim nisi a scelerisque. Class aptent taciti sociosqu ad litora torquent per conubia
|
|
||||||
nostra, per inceptos himenaeos. Donec egestas mauris posuere diam tempor, vel consectetur enim rhoncus.
|
|
||||||
Phasellus accumsan feugiat dui, sit amet viverra neque facilisis in. Pellentesque fermentum ipsum
|
|
||||||
porttitor, luctus felis eu, ornare quam. Integer ut pharetra nisi. Phasellus sed facilisis quam, nec
|
|
||||||
porta metus. Sed consequat ac nisl eget faucibus. Proin blandit nisi id pharetra vestibulum. Vestibulum
|
|
||||||
posuere nibh vitae elit pharetra rhoncus at a ligula. Duis at tincidunt dolor. Quisque a diam varius,
|
|
||||||
facilisis libero a, volutpat dolor. Nulla vel accumsan diam. Praesent eu neque nibh.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Suspendisse ac libero mi. In hac habitasse platea dictumst. Ut bibendum a dui eu congue. Sed et lacus
|
|
||||||
congue, faucibus nulla cursus, sagittis odio. In ac risus vehicula, interdum mauris quis, porttitor
|
|
||||||
massa. Vivamus non iaculis tortor. Mauris ac pharetra urna. Integer ac mattis enim. Integer egestas
|
|
||||||
velit purus. In hac habitasse platea dictumst. Ut interdum felis ut libero pellentesque, ac vulputate
|
|
||||||
felis faucibus. Sed facilisis fringilla leo, et condimentum quam fermentum ac. Maecenas blandit
|
|
||||||
sollicitudin libero in pretium.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Donec sodales convallis sapien eget rutrum. Nullam fermentum nisi nec auctor convallis. Phasellus
|
|
||||||
euismod ante erat, ut vulputate sem rhoncus et. Quisque ac eros sit amet metus aliquet blandit. Integer
|
|
||||||
sagittis ligula vitae orci gravida rhoncus. Vestibulum eu ante id ipsum ultricies tempor. Proin viverra,
|
|
||||||
felis vel suscipit laoreet, lorem nisl condimentum ipsum, id convallis ipsum ante ac sem. Maecenas vitae
|
|
||||||
ultricies augue. Morbi lectus dolor, tempus ac scelerisque sit amet, condimentum eget tortor. Fusce id
|
|
||||||
rutrum ligula.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
In suscipit purus at leo convallis sodales. Sed a placerat arcu. Fusce id mi quis nisl ullamcorper
|
|
||||||
imperdiet. Donec eu leo sit amet lacus euismod accumsan. Fusce facilisis congue leo eu commodo. Sed vel
|
|
||||||
libero sem. Nulla nec volutpat eros. Pellentesque habitant morbi tristique senectus et netus et
|
|
||||||
malesuada fames ac turpis egestas. In lacinia massa sed justo tincidunt, sed aliquam erat tristique. Nam
|
|
||||||
porta pharetra nisi eu aliquet.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
In in neque vel ligula dictum suscipit. Cras cursus a felis nec mattis. Praesent rhoncus orci nec nulla
|
|
||||||
rutrum, sit amet iaculis massa varius. Mauris sit amet finibus ipsum. Nam sit amet augue sed ipsum
|
|
||||||
laoreet venenatis. Praesent feugiat faucibus congue. Nunc vulputate ante at neque suscipit auctor. In
|
|
||||||
nec sagittis lacus. Suspendisse ornare ut eros a lobortis. Donec et pharetra urna, sit amet dictum enim.
|
|
||||||
Morbi fringilla ligula tristique nibh tempus, quis pharetra sem imperdiet. Suspendisse auctor fringilla
|
|
||||||
purus, ultricies feugiat augue consectetur et. Integer neque mauris, elementum imperdiet finibus vel,
|
|
||||||
suscipit ac metus. In hac habitasse platea dictumst. Mauris eu nibh porttitor, tempor enim vel,
|
|
||||||
tristique libero.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Suspendisse commodo in justo a hendrerit. Nunc nec laoreet ante. Nam ac tincidunt libero, sed rhoncus
|
|
||||||
libero. Nunc et arcu a ex consequat malesuada eget vel nunc. Mauris felis lectus, sodales sed lacus id,
|
|
||||||
congue blandit diam. Curabitur eu quam accumsan augue accumsan lacinia. Nulla id justo elementum,
|
|
||||||
placerat lectus et, varius purus. Nam congue, erat at congue posuere, diam felis consequat mi, a tempor
|
|
||||||
erat odio id dolor. Nunc pulvinar libero sit amet neque posuere, sit amet placerat metus facilisis. In
|
|
||||||
justo libero, facilisis condimentum molestie eu, tristique in sapien. Interdum et malesuada fames ac
|
|
||||||
ante ipsum primis in faucibus. In mattis, nisl eu mollis ultricies, justo lacus commodo nisl, ut
|
|
||||||
pulvinar augue odio nec leo. Pellentesque ornare tellus nec massa pellentesque, id euismod odio mattis.
|
|
||||||
Aliquam at massa a nisi maximus varius. Donec interdum odio id nibh commodo, in ultricies lacus lacinia.
|
|
||||||
Ut et ultricies libero.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nunc pretium vehicula leo, ut vehicula dolor ultricies quis. Nunc ultricies elit lobortis lorem tempus
|
|
||||||
placerat. Vestibulum vitae velit ut libero pretium dignissim sed vitae sapien. Ut ornare nunc nec tortor
|
|
||||||
sodales, nec volutpat risus ornare. Morbi ac iaculis arcu. Fusce viverra feugiat porta. Nam aliquam
|
|
||||||
aliquet urna quis consequat. Sed suscipit arcu ut eros imperdiet posuere non at nunc. Quisque ac
|
|
||||||
convallis lacus, non efficitur nulla. Interdum et malesuada fames ac ante ipsum primis in faucibus.
|
|
||||||
Fusce suscipit tincidunt pharetra. Sed eget egestas enim.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Quisque tellus nulla, facilisis eget ante ac, pharetra pretium mauris. Nam arcu urna, condimentum sed
|
|
||||||
urna sed, mattis dictum nisi. Maecenas eget ante facilisis, dignissim nisl non, venenatis odio. Integer
|
|
||||||
turpis enim, sagittis eu commodo nec, mollis at ex. In non dui dictum, porttitor mauris in, rutrum
|
|
||||||
dolor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin vel
|
|
||||||
nisi eros. Aliquam vel turpis est. Class aptent taciti sociosqu ad litora torquent per conubia nostra,
|
|
||||||
per inceptos himenaeos. In libero risus, feugiat non tristique quis, egestas vel tortor. Praesent cursus
|
|
||||||
lorem nec euismod placerat. Fusce tincidunt non risus at luctus. Nullam hendrerit mi diam, ut rhoncus
|
|
||||||
neque accumsan vel. Sed cursus dolor id velit ullamcorper rhoncus. Duis volutpat sodales aliquam.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Curabitur dignissim posuere ex sit amet porta. Sed viverra arcu et sapien tristique, ullamcorper
|
|
||||||
eleifend elit posuere. Aenean nec augue dui. Proin semper efficitur vehicula. Praesent vestibulum tortor
|
|
||||||
fringilla dolor congue maximus. Morbi sagittis mollis blandit. Lorem ipsum dolor sit amet, consectetur
|
|
||||||
adipiscing elit. Cras pulvinar laoreet tincidunt. Mauris hendrerit mollis neque nec vehicula. Mauris
|
|
||||||
auctor pharetra nisl, at ornare ante accumsan feugiat. Maecenas felis risus, posuere vel nunc et,
|
|
||||||
fringilla viverra dolor. Cras consectetur rutrum ipsum vel tempus. Etiam mattis ante in lacus mollis,
|
|
||||||
non porta quam pulvinar. Sed rutrum tempor placerat. Sed id dui eleifend, interdum tellus ut, sagittis
|
|
||||||
odio. Ut iaculis at enim a dignissim.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Proin tempor mi sed mattis mollis. Quisque nulla odio, rutrum vitae ligula eget, interdum faucibus
|
|
||||||
tellus. Aliquam et augue vel justo molestie vestibulum eu nec risus. Maecenas aliquet, risus eget
|
|
||||||
feugiat consequat, diam magna bibendum diam, sed pharetra tellus purus ut est. Aenean dictum pulvinar
|
|
||||||
vestibulum. Sed non neque eros. Etiam vitae aliquam nisl, quis congue velit. Ut gravida libero vel
|
|
||||||
vestibulum tincidunt. Sed vel nibh ac velit aliquam porttitor eget et nulla.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Suspendisse ut odio molestie odio auctor sollicitudin. Vestibulum viverra ornare nibh, vel lobortis odio
|
|
||||||
dapibus ac. Cras dapibus porta libero, nec congue nisi sagittis sit amet. Class aptent taciti sociosqu
|
|
||||||
ad litora torquent per conubia nostra, per inceptos himenaeos. Donec ullamcorper leo vitae urna
|
|
||||||
sollicitudin sodales. Suspendisse quis nulla vitae quam ornare lobortis sodales sit amet lectus.
|
|
||||||
Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut eleifend gravida laoreet. Cras id purus
|
|
||||||
vehicula, egestas velit sit amet, pretium magna. In in pulvinar quam.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Aliquam fermentum a tellus a sodales. Pellentesque habitant morbi tristique senectus et netus et
|
|
||||||
malesuada fames ac turpis egestas. Etiam facilisis molestie ipsum ac viverra. Vestibulum lacus nunc,
|
|
||||||
egestas at lorem vel, pulvinar consequat lacus. Sed feugiat nisl vel risus vehicula, sed imperdiet elit
|
|
||||||
ornare. Maecenas non magna vitae erat feugiat scelerisque. Nam vulputate lacus porta porttitor
|
|
||||||
sollicitudin.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Quisque auctor tellus lorem, et ornare tellus efficitur id. Cras dictum tortor quis lorem facilisis, in
|
|
||||||
molestie erat porta. Sed vel porttitor libero. Etiam pulvinar augue a semper finibus. Mauris tellus
|
|
||||||
metus, lobortis non viverra eu, commodo quis nunc. Maecenas vitae pellentesque lectus. Nunc ut felis
|
|
||||||
tellus. Phasellus a mi in neque placerat egestas eget vitae tellus. Vivamus ut metus tellus.
|
|
||||||
Pellentesque mollis tempus tempus.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Ut at pretium lacus, non sollicitudin velit. Vivamus sagittis fermentum purus, nec suscipit lectus
|
|
||||||
imperdiet eu. Cras velit risus, porttitor vitae lectus id, condimentum pulvinar sem. Ut lacinia metus eu
|
|
||||||
rhoncus finibus. Vivamus accumsan, ex suscipit porttitor dictum, tellus lectus luctus lorem, sed lacinia
|
|
||||||
lectus sem et ligula. Duis vitae turpis nunc. Curabitur non sapien felis. Maecenas a egestas nunc, eget
|
|
||||||
luctus quam. Duis vestibulum metus eu turpis vulputate, et lacinia ante maximus. Vivamus molestie leo
|
|
||||||
eget sem dictum, nec posuere sapien euismod.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Cras et risus venenatis nisi dapibus feugiat non at dui. Pellentesque faucibus nulla aliquam nunc
|
|
||||||
congue, ut fringilla est viverra. Mauris nulla elit, ornare non augue a, cursus elementum diam. Proin
|
|
||||||
pulvinar justo nulla, et semper tortor dapibus ac. Maecenas ac libero ut eros hendrerit auctor at nec
|
|
||||||
augue. Aliquam dictum, ligula fermentum lobortis egestas, risus neque pulvinar felis, id lobortis tellus
|
|
||||||
nunc eget massa. Fusce vel mollis arcu. Suspendisse dapibus, tortor in blandit pulvinar, nulla dolor
|
|
||||||
euismod justo, vel ullamcorper dui ante ac dolor. Duis congue urna et purus posuere semper. Phasellus ut
|
|
||||||
feugiat justo, in commodo turpis. Donec venenatis et urna ut vestibulum.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nam tristique odio a ultricies suscipit. Cras sollicitudin vulputate euismod. Sed elementum congue
|
|
||||||
tortor, ut pretium lacus dapibus accumsan. Sed eget aliquam tortor. Donec blandit tempor tellus, sit
|
|
||||||
amet pharetra mauris iaculis vel. Quisque pharetra nisi eget orci egestas dignissim. Mauris sed laoreet
|
|
||||||
ligula, vel placerat ante. Curabitur vel libero ornare, suscipit ante sed, feugiat orci. Sed malesuada
|
|
||||||
mattis augue. Sed auctor mattis diam, bibendum pretium arcu tempus quis.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Praesent id finibus purus, sed congue turpis. Maecenas fringilla, nisi non elementum congue, diam nunc
|
|
||||||
vestibulum urna, non elementum quam risus ut tellus. Donec imperdiet, turpis vel elementum sollicitudin,
|
|
||||||
ipsum odio faucibus lectus, quis facilisis urna nibh non ipsum. Sed sed massa sed orci cursus viverra eu
|
|
||||||
quis velit. Donec volutpat pretium erat vitae sodales. Suspendisse posuere nisi at orci rhoncus,
|
|
||||||
fermentum sodales lacus scelerisque. Suspendisse consectetur enim eget faucibus eleifend. Nullam magna
|
|
||||||
eros, elementum id aliquam non, egestas eu nunc. Sed tincidunt scelerisque nibh, id venenatis dolor
|
|
||||||
ultricies vitae. Morbi sapien nisi, tempor ut odio non, varius varius ipsum.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Pellentesque sit amet maximus nibh, et placerat orci. Duis accumsan libero a dolor dignissim viverra.
|
|
||||||
Sed purus nibh, egestas eu nisi at, commodo elementum sem. Nunc id dui a ex condimentum venenatis.
|
|
||||||
Maecenas id mattis nunc. Fusce accumsan nibh nec lacus congue consequat. Donec cursus laoreet leo in
|
|
||||||
egestas. Proin sit amet dui tristique, rhoncus tortor eget, aliquet tellus. Aenean vitae pellentesque
|
|
||||||
ex, in congue odio. Etiam lectus velit, fermentum ultricies consequat eget, pellentesque quis tellus.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Aenean id libero et turpis fringilla elementum. Orci varius natoque penatibus et magnis dis parturient
|
|
||||||
montes, nascetur ridiculus mus. Sed vel ex euismod, dignissim nunc ac, consequat ipsum. Donec et tortor
|
|
||||||
nisi. Nulla tempor turpis nec elit aliquam, id rutrum nulla blandit. Maecenas ullamcorper turpis
|
|
||||||
placerat lectus imperdiet tincidunt. Vestibulum tempor posuere risus tincidunt porttitor. Mauris sit
|
|
||||||
amet hendrerit diam, non porta sem. Proin finibus quam eu metus vulputate, quis vehicula lectus
|
|
||||||
lobortis. Aenean libero felis, ullamcorper sit amet sagittis et, scelerisque et leo. Sed vehicula sem
|
|
||||||
justo, eget pulvinar leo laoreet nec. In hac habitasse platea dictumst.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Donec ac felis sem. Morbi lorem felis, mollis ac elit ut, luctus congue augue. Donec leo purus, accumsan
|
|
||||||
non pulvinar vel, hendrerit vitae diam. Etiam bibendum ut quam vehicula cursus. Mauris tristique
|
|
||||||
volutpat ligula. Ut quis augue sollicitudin, tincidunt nisl ac, egestas turpis. Morbi gravida in nisi
|
|
||||||
non interdum. Nullam pharetra accumsan tellus in laoreet. Mauris auctor auctor odio. Nunc cursus at
|
|
||||||
metus a fermentum. Interdum et malesuada fames ac ante ipsum primis in faucibus.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Mauris hendrerit vel mi non mollis. Morbi nibh felis, convallis non lacinia sed, accumsan in nulla.
|
|
||||||
Fusce non erat in massa venenatis euismod. Fusce justo felis, varius nec dolor vel, lobortis fringilla
|
|
||||||
enim. Pellentesque ac fringilla mi. Aliquam ultrices interdum pellentesque. Vivamus purus mi, porta ut
|
|
||||||
dui malesuada, interdum lobortis leo. Sed in sem in dolor hendrerit posuere ut eget tortor. Duis id
|
|
||||||
dolor vitae risus accumsan tincidunt.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Sed consectetur ornare risus porta rutrum. Sed vel leo at neque rutrum lobortis eu at lorem. Curabitur
|
|
||||||
ultrices congue lacinia. Proin non bibendum augue. Maecenas et volutpat leo, in rutrum neque. In
|
|
||||||
efficitur, purus quis pharetra pretium, nibh metus pharetra metus, vitae euismod mi metus a nibh.
|
|
||||||
Praesent condimentum velit felis, sollicitudin pretium elit lacinia nec. Integer non arcu dignissim,
|
|
||||||
suscipit nunc nec, rutrum tellus. Mauris lobortis, lorem lacinia sagittis tincidunt, mi lacus viverra
|
|
||||||
orci, id ultricies ipsum dui at quam.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Maecenas ornare sagittis vestibulum. Phasellus egestas a tellus sit amet ullamcorper. Sed eu vestibulum
|
|
||||||
sapien. Ut et ipsum augue. Sed tincidunt nibh magna, non accumsan dolor aliquet eu. Cras vitae pharetra
|
|
||||||
elit. Donec pretium dui nec pellentesque vehicula. Cras eu sem elementum mi posuere fringilla. Duis
|
|
||||||
porta, purus a sodales rhoncus, eros eros iaculis turpis, nec ornare magna nisl a ex. Pellentesque
|
|
||||||
dictum eu mi non pretium. Donec pharetra elementum dui. Curabitur et nunc ut nunc euismod aliquet eu
|
|
||||||
quis libero. Quisque sollicitudin metus maximus elit lacinia bibendum a a velit. Sed commodo est at nisl
|
|
||||||
varius, et egestas libero mattis. Vivamus sit amet rutrum turpis, nec vulputate purus. Morbi in feugiat
|
|
||||||
massa.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Pellentesque vel justo congue, scelerisque ex at, vehicula diam. Donec elementum tincidunt nulla, et
|
|
||||||
pharetra ex varius et. Suspendisse iaculis ligula a dolor bibendum, quis eleifend velit tristique. Fusce
|
|
||||||
vitae leo vitae dolor porta lobortis. Pellentesque consectetur sem quis dolor rhoncus volutpat.
|
|
||||||
Pellentesque ullamcorper volutpat condimentum. Cras venenatis elit a vehicula feugiat. Maecenas interdum
|
|
||||||
enim justo, eu tincidunt nibh pharetra at. Cras porta dui ut accumsan aliquam. Nam ut ultricies dolor.
|
|
||||||
Ut in libero vitae nibh accumsan maximus. Duis hendrerit ligula ligula, vehicula ullamcorper purus
|
|
||||||
cursus vel. Mauris efficitur vulputate elit id fermentum. Nam turpis lorem, fermentum et ullamcorper ut,
|
|
||||||
ultrices aliquam felis. Morbi et faucibus justo.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Morbi a nisi nibh. Aenean ut ex ut justo cursus venenatis. Aliquam erat volutpat. Phasellus posuere
|
|
||||||
libero sed enim pretium maximus. Curabitur risus nunc, feugiat nec libero sed, fermentum semper turpis.
|
|
||||||
Pellentesque vestibulum scelerisque ipsum quis porttitor. Proin dapibus tortor sed odio dignissim, a
|
|
||||||
maximus turpis fringilla.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Aenean blandit nulla lorem, sodales rutrum leo dignissim ac. Aliquam ornare nulla mi, eget mollis enim
|
|
||||||
rhoncus eu. Donec quis erat sem. Sed in orci quis lectus interdum efficitur. Fusce non fringilla orci.
|
|
||||||
Pellentesque elementum ut leo at aliquet. Mauris fringilla, nisi in imperdiet consectetur, lorem tellus
|
|
||||||
iaculis eros, quis ullamcorper justo est sit amet felis. In felis justo, mattis nec elit vel, convallis
|
|
||||||
accumsan libero. Nulla facilisi. Suspendisse faucibus, magna a hendrerit tempor, elit elit posuere nibh,
|
|
||||||
non maximus mi enim ac justo. Mauris ut tempor leo. Etiam finibus imperdiet pellentesque. Vestibulum
|
|
||||||
massa arcu, iaculis vitae leo eu, fringilla semper metus. Etiam nunc dolor, consectetur eu justo non,
|
|
||||||
feugiat viverra est. Aliquam tristique molestie purus, quis ultrices est ullamcorper eu. Etiam cursus
|
|
||||||
elit et rhoncus viverra.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Donec et porttitor lacus. Suspendisse venenatis sapien ut ultrices ornare. Quisque dignissim, tortor ut
|
|
||||||
fringilla dignissim, est orci lobortis odio, ac egestas sem odio dapibus lorem. Vestibulum in magna
|
|
||||||
fermentum, laoreet metus sit amet, molestie lacus. Ut quam felis, dignissim non gravida non, vulputate
|
|
||||||
quis sapien. Sed sit amet tellus sem. Fusce eget ligula et enim feugiat vulputate. Duis nec orci id
|
|
||||||
lectus accumsan fermentum vel ac dolor. Aliquam vel accumsan augue, at consequat nibh. Ut vulputate
|
|
||||||
sapien augue, ullamcorper accumsan magna luctus id. In congue et dui sed tempus. Sed et venenatis dui,
|
|
||||||
et viverra sem. Nam elementum, libero sed lobortis pellentesque, nulla dolor tincidunt odio, in
|
|
||||||
ullamcorper orci magna eu nulla.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Etiam vitae pharetra erat. Pellentesque ullamcorper a urna vel gravida. Maecenas dapibus erat sed metus
|
|
||||||
ultrices, venenatis pulvinar felis bibendum. Morbi sollicitudin pretium rhoncus. Proin tempor pretium
|
|
||||||
feugiat. Ut sed ornare eros. Etiam sed ullamcorper justo. Integer tristique ornare lobortis. Aenean et
|
|
||||||
vestibulum sapien. Aliquam non interdum diam. Pellentesque egestas sagittis nibh, eu feugiat leo
|
|
||||||
vestibulum eget. Proin bibendum ligula sapien, ut dapibus turpis sodales sed. Sed vitae nunc feugiat,
|
|
||||||
porta diam non, fringilla arcu. Suspendisse eleifend arcu vitae mi auctor suscipit. Lorem ipsum dolor
|
|
||||||
sit amet, consectetur adipiscing elit. Integer ut odio vel ipsum luctus maximus.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nunc rhoncus dui nibh, ac pulvinar risus vulputate in. Quisque gravida convallis ultricies. In vitae
|
|
||||||
felis dolor. Aenean congue vitae libero ac mattis. Quisque tincidunt arcu id mi bibendum, non viverra mi
|
|
||||||
vulputate. Sed mollis commodo elementum. Vivamus iaculis urna at ante tempor, eu aliquet elit bibendum.
|
|
||||||
Praesent feugiat lacinia dolor, sit amet semper enim auctor nec.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Vivamus tincidunt turpis id nulla maximus commodo. Nullam a ligula ut risus congue tempus in et ipsum.
|
|
||||||
Fusce dapibus a elit vitae sagittis. Nam metus quam, pretium a tortor eu, molestie hendrerit lacus. Cras
|
|
||||||
volutpat lacinia sem, non volutpat tellus pretium sagittis. Morbi a viverra diam, vel feugiat nisi. Orci
|
|
||||||
varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras varius nibh ut
|
|
||||||
tempor ullamcorper. Cras convallis, massa sit amet mollis laoreet, elit erat scelerisque arcu, ac
|
|
||||||
bibendum augue dolor vel nunc. Aliquam sit amet orci nec dui venenatis imperdiet.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Nulla facilisi. Aliquam vehicula velit in aliquam auctor. Cras ut nibh diam. Donec vitae vulputate
|
|
||||||
lacus. Suspendisse potenti. Morbi quis leo quis sapien sagittis finibus in id purus. Sed congue
|
|
||||||
fringilla purus, vel finibus leo aliquet eu. Cras volutpat sapien imperdiet erat consectetur interdum.
|
|
||||||
Nam eu pretium diam, quis mattis lacus.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Donec sed ligula nunc. Donec fermentum tempor diam, malesuada cursus ante iaculis at. Aliquam sodales
|
|
||||||
lectus in lorem molestie commodo. Ut aliquet faucibus commodo. Curabitur ut nisi sit amet orci molestie
|
|
||||||
ultrices at ac diam. Morbi nec tellus nec enim lobortis faucibus quis non tellus. Nulla varius ligula in
|
|
||||||
dolor fringilla vulputate. Phasellus sed finibus justo, nec faucibus ipsum. Cras at tempor mi. Donec
|
|
||||||
ipsum ligula, ultrices in suscipit non, cursus vel est. In vitae neque condimentum, euismod nunc at,
|
|
||||||
pulvinar est. Donec rhoncus luctus ornare.
|
|
||||||
</p>
|
|
||||||
</content>
|
|
||||||
<script>
|
|
||||||
const frameEl = document.getElementById('frame');
|
|
||||||
frameEl.addEventListener('load', function () {
|
|
||||||
|
|
||||||
const caseEls = document.getElementsByClassName('section');
|
|
||||||
for (let caseEl of caseEls) {
|
|
||||||
console.log(caseEl.dataset.title);
|
|
||||||
const toSelect = typeof caseEl.dataset.title == 'undefined' ? null : frameEl.contentWindow.getIdForTitle(caseEl.dataset.title);
|
|
||||||
// navItemEl.hash url-encodes
|
|
||||||
// let targetEl = document.getElementById(navItemEl.attributes.href.value.substr(1));
|
|
||||||
// let wrapperEl = targetEl.parentNode;
|
|
||||||
let intersectionObserver = new IntersectionObserver(function (entries) {
|
|
||||||
console.log(entries);
|
|
||||||
// If intersectionRatio is 0, the target is out of view
|
|
||||||
// and we do not need to do anything.
|
|
||||||
if (entries[0].intersectionRatio <= 0) {
|
|
||||||
// navItemEl.classList.remove('active');
|
|
||||||
} else {
|
|
||||||
if(toSelect === null) {
|
|
||||||
frameEl.contentWindow.mapGraph.deselectNode();
|
|
||||||
frameEl.contentWindow.mapGraph.resetZoom();
|
|
||||||
} else {
|
|
||||||
const node = frameEl.contentWindow.mapGraph.graph.nodes.filter(n => n.id == toSelect)[0]
|
|
||||||
frameEl.contentWindow.mapGraph.selectNode(node);
|
|
||||||
}
|
|
||||||
// navItemEl.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
// start observing
|
|
||||||
intersectionObserver.observe(caseEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
// frame.contentWindow;
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
Loading…
Reference in a new issue