Compare commits
7 commits
0b6e4661cb
...
6eb67407d9
Author | SHA1 | Date | |
---|---|---|---|
|
6eb67407d9 | ||
|
40b2251dbd | ||
|
07503883d3 | ||
|
4dec76a325 | ||
|
7751fc05a8 | ||
|
2dda26ff77 | ||
|
9a79cea9d7 |
5 changed files with 1439 additions and 157 deletions
69
README.md
69
README.md
|
@ -4,6 +4,7 @@ This repository contains a script to pull semantic data out of Semantic Mediawik
|
|||
|
||||
Built for [Security Vision](https://securityvision.io).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
|
@ -42,3 +43,71 @@ _Ask_ SMW with the following query:
|
|||
?Datasets Used
|
||||
?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'
|
||||
```
|
365
www/graph.css
365
www/graph.css
|
@ -1,6 +1,12 @@
|
|||
@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{
|
||||
--color1: #f94144;
|
||||
:root {
|
||||
--color1: #9741f9;
|
||||
--color2: #f3722c;
|
||||
--color3: #f8961e;
|
||||
/* --color4: #f9844a; */
|
||||
|
@ -10,18 +16,25 @@
|
|||
--color8: #4d908e;
|
||||
--color9: #577590;
|
||||
--color10: #277da1;
|
||||
|
||||
--hover-color: var(--color1);
|
||||
--hover-related-color: #d1bce9;
|
||||
/* --hover-color: var(darkblue); */
|
||||
--selected-color: var(--color1);
|
||||
--selected-color: var(--color1);
|
||||
--link-color: rgba(255,255,255,0.5);
|
||||
--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 {
|
||||
margin: 0;
|
||||
/* overflow: hidden; */
|
||||
/* background: linear-gradient(to top, #040308, #AD4A28, #DD723C, #fc7001, #dcb697, #9ba5ae, #3e5879, #020b1a); */
|
||||
background:linear-gradient(to top, #414141, #99a6b8);
|
||||
background: var(--body-back);
|
||||
/* background: #9cb3c9; */
|
||||
font-family: sans-serif;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
@ -35,109 +48,212 @@ svg.dragging {
|
|||
cursor: grabbing;
|
||||
}
|
||||
|
||||
svg .links line,svg .links path{
|
||||
stroke: #f3722c;
|
||||
#arrowHead {
|
||||
fill: var(--link-color);
|
||||
}
|
||||
|
||||
#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;
|
||||
fill:none;
|
||||
fill: none;
|
||||
transition: stroke-width 1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
svg .links line.hover, svg .links path.hover{
|
||||
stroke:red;
|
||||
svg .links line.hover, svg .links path.hover {
|
||||
stroke: var(--link-hover-color);
|
||||
/* stroke-width: 12; */
|
||||
marker-end: url(#arrowHeadSelected);
|
||||
}
|
||||
|
||||
svg.zoomed .links line, svg.zoomed .links path{
|
||||
svg .links .linkedHover 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;
|
||||
}
|
||||
|
||||
.links text{
|
||||
display:none;
|
||||
font-size:5pt;
|
||||
/* svg.zoomed .links line, svg.zoomed .links path.hover {
|
||||
stroke-width: 4;
|
||||
} */
|
||||
|
||||
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;
|
||||
fill: whitesmoke;
|
||||
}
|
||||
|
||||
.node{
|
||||
|
||||
.node {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.node text.nodeTitle{
|
||||
.node text.nodeTitle {
|
||||
text-anchor: start;
|
||||
dominant-baseline: hanging; /*achieves a 'text-anchor: top'*/
|
||||
font-size:16pt;
|
||||
dominant-baseline: hanging;
|
||||
/*achieves a 'text-anchor: top'*/
|
||||
/* font-size: 16pt; */
|
||||
/*Set this in JS*/
|
||||
transition: font-size .4s, opacity 1s;
|
||||
fill: white;
|
||||
fill: #5d5d5f; /*also when hovering node*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
svg.zoomed .node text.nodeTitle{
|
||||
font-size:6pt;
|
||||
}
|
||||
svg.zoomed.zoomed2 .node text.nodeTitle{
|
||||
font-size:3pt;
|
||||
svg.zoomed .node text.nodeTitle {
|
||||
/* font-size: 6pt; */
|
||||
}
|
||||
|
||||
.node circle{
|
||||
fill: white;
|
||||
svg.zoomed.zoomed2 .node text.nodeTitle {
|
||||
font-size: 3pt;
|
||||
}
|
||||
|
||||
.node circle, .node path {
|
||||
fill: lightgray;
|
||||
}
|
||||
|
||||
/* Whenever a connected link is hovered */
|
||||
.node.linkHover circle{
|
||||
stroke: var(--hover-color);
|
||||
|
||||
.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.linkHover text.nodeTitle.overlapping{
|
||||
.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 circle, .node:hover path, .node.selected path {
|
||||
fill: var(--hover-color) !important;
|
||||
stroke: var(--hover-color);
|
||||
stroke-width: 5px;
|
||||
}
|
||||
.node:hover text{
|
||||
/*
|
||||
.node:hover text {
|
||||
transition: none;
|
||||
fill: var(--hover-color);
|
||||
}
|
||||
.node.selected circle{
|
||||
} */
|
||||
|
||||
.node.selected circle, .node.selected path {
|
||||
fill: var(--selected-color) !important;
|
||||
stroke: var(--selected-color);
|
||||
stroke-width: 5px;
|
||||
}
|
||||
|
||||
.node.City circle{
|
||||
display:none;
|
||||
.node.City circle, .node.City path {
|
||||
display: none;
|
||||
}
|
||||
.node.City{
|
||||
fill:white;
|
||||
|
||||
.node.City {
|
||||
fill: white;
|
||||
stroke: black;
|
||||
stroke-width: .5px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.node.Person circle {
|
||||
|
||||
.node.Person circle, .node.Person path {
|
||||
fill: lightgreen
|
||||
}
|
||||
.node.Technology circle {
|
||||
fill: lightcoral;
|
||||
|
||||
.node.Technology circle, .node.Technology path {
|
||||
fill: plum;
|
||||
}
|
||||
.node.Deployment circle {
|
||||
fill: lightblue;
|
||||
|
||||
.node.Deployments circle, .node.Deployments path {
|
||||
/* 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
|
||||
}
|
||||
.node.Dataset circle {
|
||||
fill: plum
|
||||
}
|
||||
|
||||
|
||||
|
||||
.labels .label text{
|
||||
fill:yellow;
|
||||
.labels .label text {
|
||||
fill: yellow;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
|
@ -157,69 +273,164 @@ svg.zoomed.zoomed2 .node text.nodeTitle{
|
|||
fill: var(--color7)
|
||||
} */
|
||||
|
||||
|
||||
#nodeInfo{
|
||||
#nodeInfo {
|
||||
position: fixed;
|
||||
display:block;
|
||||
right:20px;
|
||||
bottom:20px;
|
||||
background:white;
|
||||
display: block;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
background: white;
|
||||
padding: 10px;
|
||||
border: solid 1px #ccc;
|
||||
}
|
||||
|
||||
#nodeInfo.hidden{
|
||||
display:none;
|
||||
#nodeInfo.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#nodeInfo h2{
|
||||
|
||||
#nodeInfo h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#nodeInfo iframe{
|
||||
#nodeInfo iframe {
|
||||
width: 50vw;
|
||||
height: calc(100vh - 40px - 20px - 30px);
|
||||
}
|
||||
|
||||
#closeInfo{
|
||||
#tooltip{
|
||||
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;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
#closeInfo:hover{
|
||||
#closeInfo:hover {
|
||||
color: var(--hover-color);
|
||||
}
|
||||
|
||||
a, a:link{
|
||||
a, a:link {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover{
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#filters,#menu{
|
||||
header {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
padding: 10px;
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
#map .borders{
|
||||
#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: white;
|
||||
fill:none;
|
||||
stroke: rgb(221, 210, 210);
|
||||
fill: none;
|
||||
}
|
||||
|
||||
#alluvial{
|
||||
#alluvial {
|
||||
/* position:fixed; */
|
||||
top:0;
|
||||
left:0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#alluvial .flow_label text{
|
||||
#alluvial .flow_label text {
|
||||
font-size: 30;
|
||||
}
|
||||
|
||||
|
|
584
www/graph.js
584
www/graph.js
|
@ -1,8 +1,12 @@
|
|||
|
||||
const CONFIG = {
|
||||
'nodeSize': 8,
|
||||
'title': "Remote Biometric Identification",
|
||||
'subtitle': "A survey of the European Union",
|
||||
// 'nodeSize': 8,
|
||||
'nodeRadius': 5,
|
||||
'nodeRepositionPadding': 10,
|
||||
'baseUrl': 'https://www.securityvision.io/wiki/index.php/',
|
||||
'dataUrl': 'result.json',
|
||||
'dataUrl': 'result2.json',
|
||||
'preSimulate': false, // run simulation before starting, so we don't start with lines jumping around
|
||||
'labels': {
|
||||
'rotate': true,
|
||||
|
@ -47,13 +51,66 @@ const CONFIG = {
|
|||
"Company": ["Managed by", "Provided by", "Developped by (institutions)"],
|
||||
"Tech": ["Technologies Used", "Software Deployed"],
|
||||
"Funding": ["Funded by"],
|
||||
}
|
||||
},
|
||||
"zoom": {
|
||||
"scale_min": .2,
|
||||
"scale_max": 20,
|
||||
},
|
||||
|
||||
"cases": [
|
||||
"Data-lab Burglary-Free Neighbourhood",
|
||||
"Dragonfly Project",
|
||||
]
|
||||
};
|
||||
|
||||
// let width = window.innerWidth;
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -146,12 +203,19 @@ function getClasses(obj) {
|
|||
return 'node ' + classes.join(' ');
|
||||
}
|
||||
|
||||
function getLinkId(link) {
|
||||
return "link_" + link.nr;
|
||||
// return "link-" + link.source.id + '-' + link.target.id + '-' + slugify(link.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class NodeMap {
|
||||
constructor(parent) {
|
||||
this.root = d3.select(parent);
|
||||
this.resizeEvent = window.addEventListener('resize', this.resize.bind(this));
|
||||
this.tooltipEl = document.getElementById('tooltip');
|
||||
this.selectedNode = null;
|
||||
}
|
||||
|
||||
resize() {
|
||||
|
@ -199,7 +263,47 @@ class NodeMap {
|
|||
|
||||
render() {
|
||||
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" 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>');
|
||||
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>
|
||||
<!--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.projection = d3.geoHill()
|
||||
|
@ -234,45 +338,104 @@ class NodeMap {
|
|||
.data(this.countries)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", "countries")
|
||||
.attr("d", this.proj)
|
||||
.attr("fill", (n) => {
|
||||
.attr("class", (n) => {
|
||||
if (CONFIG.countries.indexOf(n.properties.name) !== -1) {
|
||||
return '';
|
||||
return 'country eu_country';
|
||||
}
|
||||
return "rgba(200,200,200,.7)";
|
||||
});
|
||||
return "country";
|
||||
})
|
||||
.attr("d", this.proj)
|
||||
// .attr("filter", 'url(#splotch)')
|
||||
// .attr("fill", );
|
||||
|
||||
this.g_borders
|
||||
.append("path")
|
||||
.attr("class", "borders")
|
||||
.attr("d", this.proj(this.borders))
|
||||
|
||||
const zoom = d3.zoom().scaleExtent([0.2, 10]).on("start", () => {
|
||||
let zoomTimeout = null;
|
||||
this.zoom = d3.zoom()
|
||||
.scaleExtent([CONFIG.zoom.scale_min, CONFIG.zoom.scale_max])
|
||||
.on("start", () => {
|
||||
this.svg.node().classList.add("dragging");
|
||||
}).on("end", () => {
|
||||
this.svg.node().classList.remove("dragging");
|
||||
}).on("zoom", ({ transform }) => {
|
||||
this.container.attr("transform", transform);
|
||||
}).on("zoom", (evt) => {
|
||||
this.container.attr("transform", evt.transform);
|
||||
const oldZoom = this.svg.classed('zoomed');
|
||||
const newZoom = transform.k > 2.0;
|
||||
if (oldZoom != newZoom) {
|
||||
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);
|
||||
|
||||
}
|
||||
});
|
||||
this.svg
|
||||
.call(zoom)
|
||||
.call(zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
||||
this.title = this.container.append('g').attr('id', 'header');
|
||||
|
||||
const titleFeature = {
|
||||
"type": "LineString",
|
||||
"coordinates": []
|
||||
};
|
||||
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")
|
||||
.attr('class', 'links')
|
||||
.selectAll(".link");
|
||||
this.node = this.container.append("g")
|
||||
.attr('class', 'nodes')
|
||||
this.g_nodes = this.container.append("g")
|
||||
.attr('class', 'nodes');
|
||||
this.node = this.g_nodes
|
||||
.selectAll(".node");
|
||||
|
||||
|
||||
|
@ -283,10 +446,16 @@ class NodeMap {
|
|||
if (d.printouts[prop].length) {
|
||||
// console.log("fix node", d);
|
||||
var p = this.projection([d.printouts[prop][0].lon, d.printouts[prop][0].lat]);
|
||||
|
||||
// initial positions:
|
||||
d.x = p[0];
|
||||
d.y = p[1];
|
||||
// d.targetX = p[0];
|
||||
// d.targetY = p[1];
|
||||
|
||||
//These are used when we need to move overlapping points:
|
||||
d.originalX = p[0];
|
||||
d.originalY = p[1];
|
||||
|
||||
// target pos for the force layout
|
||||
d.fx = p[0];
|
||||
d.fy = p[1];
|
||||
// d.targetLat = d.printouts[prop][0].lat;
|
||||
|
@ -296,6 +465,8 @@ class NodeMap {
|
|||
}
|
||||
})
|
||||
|
||||
this.store.configureTree();
|
||||
|
||||
|
||||
|
||||
// this.nodeMap = Object.fromEntries(this.graph.nodes.map(d => [d['id'], d]));
|
||||
|
@ -322,28 +493,216 @@ class NodeMap {
|
|||
.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 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("posY", d3.forceY(n => n.targetY || 0).strength(n => n.targetY ? 1 : 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))
|
||||
;
|
||||
|
||||
this.svg
|
||||
.call(this.zoom)
|
||||
.call(this.zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
||||
|
||||
|
||||
this.update();
|
||||
|
||||
setTimeout(() => this.calculateLabels(), 1000);
|
||||
}
|
||||
|
||||
resetZoom() {
|
||||
this.deselectNode();
|
||||
this.svg
|
||||
.transition()
|
||||
.duration(2000) // milliseconds
|
||||
.call(this.zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
||||
}
|
||||
|
||||
getSizeForNode(node) {
|
||||
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() {
|
||||
console.log(this.graph)
|
||||
// console.log(this.graph)
|
||||
|
||||
this.resolveOverlaps();
|
||||
|
||||
// see also: https://www.createwithdata.com/enter-exit-with-d3-join/
|
||||
this.node = this.node.data(this.graph.nodes, d => d.id)
|
||||
.join((enter) => {
|
||||
let group = enter.append("g").attr("class", getClasses);
|
||||
let group = enter.append("g")
|
||||
.attr("class", getClasses)
|
||||
.attr("id", (n) => getIdForTitle(n.fulltext));
|
||||
// group.call(drag(simulation));
|
||||
group.on("click", (evt, n) => selectNode(evt, n, node));
|
||||
group.append('circle').attr("r", 5 /*this.nodeSize*/);
|
||||
group.on("click", (evt, n) => {
|
||||
evt.stopPropagation(); this.selectNode(n);
|
||||
});
|
||||
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);
|
||||
nodeTitle
|
||||
.each(function (node, i, nodes) {
|
||||
|
@ -432,10 +791,12 @@ class NodeMap {
|
|||
.data(this.graph.links)
|
||||
.join(
|
||||
enter => {
|
||||
let group = enter.append("g").attr("class", "link");
|
||||
let group = enter.append("g")
|
||||
.attr("class", (l) => "link " + slugify(l.name))
|
||||
.attr("id", getLinkId);
|
||||
group.append("path")
|
||||
.attr("marker-end", "url(#arrowHead)")
|
||||
.attr('id', (d, i) => 'linkid_' + i)
|
||||
.attr('id', (d, i) => 'linkpath_' + i)
|
||||
.on("mouseover", function (ev, link) {
|
||||
d3.select(this).classed('hover', true);
|
||||
const nodes = document.getElementsByClassName('node');
|
||||
|
@ -449,14 +810,18 @@ class NodeMap {
|
|||
}).on("mouseout", function (ev, link) {
|
||||
d3.select(this).classed('hover', false);
|
||||
const nodes = document.getElementsByClassName('linkHover');
|
||||
for (let n of nodes) {
|
||||
n.classList.remove('linkHover');
|
||||
while (nodes.length) {
|
||||
nodes[0].classList.remove('linkHover');
|
||||
}
|
||||
// l.classed('hover',false);
|
||||
// l.target.classed('hover',false);
|
||||
// l.source.classed('hover',false);
|
||||
// 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) {
|
||||
return l.name;
|
||||
});
|
||||
|
@ -590,6 +955,60 @@ class NodeMap {
|
|||
redraw() {
|
||||
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 {
|
||||
|
@ -625,6 +1044,7 @@ class AlluvialMap {
|
|||
|
||||
render() {
|
||||
this.svg = this.root.append('svg')
|
||||
|
||||
this.resize();
|
||||
|
||||
this.sankey = d3.sankey()
|
||||
|
@ -633,7 +1053,7 @@ class AlluvialMap {
|
|||
.nodeWidth(40) // height
|
||||
.nodePadding(10)
|
||||
.extent([[1, 5], [2000 - 1, 2000 - 5]]);
|
||||
console.log(this.graph);
|
||||
// console.log(this.graph);
|
||||
let s = this.sankey({
|
||||
nodes: this.graph.nodes.map(d => Object.assign({}, d)),
|
||||
links: this.graph.links.map(d => Object.assign({}, d))
|
||||
|
@ -643,7 +1063,7 @@ class AlluvialMap {
|
|||
this.links = s.links;
|
||||
|
||||
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")
|
||||
|
@ -674,7 +1094,7 @@ class AlluvialMap {
|
|||
const edgeColor = 'path'; // either: path, none, input, output
|
||||
if (edgeColor === "path") {
|
||||
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
|
||||
d.uid = `url(#${id})`;
|
||||
return id;
|
||||
|
@ -682,7 +1102,7 @@ class AlluvialMap {
|
|||
.attr("gradientUnits", "userSpaceOnUse")
|
||||
.attr("y1", d => d.source.x1)
|
||||
.attr("y2", d => d.target.x0)
|
||||
.attr("x1",0)
|
||||
.attr("x1", 0)
|
||||
.attr("x2", 0);
|
||||
// .attr("y1", "0%")
|
||||
// .attr("y2", "100%")
|
||||
|
@ -733,7 +1153,13 @@ class AlluvialMap {
|
|||
|
||||
}
|
||||
|
||||
|
||||
titleIdMap = {};
|
||||
function getIdForTitle(title) {
|
||||
if (!titleIdMap.hasOwnProperty(title)) {
|
||||
titleIdMap[title] = slugify(title) + `-${Object.keys(titleIdMap).length}`
|
||||
}
|
||||
return titleIdMap[title];
|
||||
}
|
||||
|
||||
JsonToGraph = function (data) {
|
||||
let nodes = [];
|
||||
|
@ -741,10 +1167,13 @@ JsonToGraph = function (data) {
|
|||
|
||||
let smwBugFixLocationMaps = {};
|
||||
console.log(data)
|
||||
let i = 0;
|
||||
let linkI = 0;
|
||||
for (const node_id in data.results) {
|
||||
if (Object.hasOwnProperty.call(data.results, node_id)) {
|
||||
i++;
|
||||
let node = data.results[node_id];
|
||||
node.id = node.fulltext; //node_id;
|
||||
node.id = getIdForTitle(node.fulltext); //node_id;
|
||||
nodes.push(node);
|
||||
// console.log(node_id, node);
|
||||
|
||||
|
@ -764,9 +1193,9 @@ JsonToGraph = function (data) {
|
|||
}
|
||||
for (const target_node of node.printouts[prop]) {
|
||||
links.push({
|
||||
"source": node_id,
|
||||
"target": target_node.fulltext,
|
||||
"name": prop
|
||||
"source": node.id,
|
||||
"target": getIdForTitle(target_node.fulltext),
|
||||
"name": prop,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -795,14 +1224,18 @@ JsonToGraph = function (data) {
|
|||
|
||||
console.debug(`Fixed location for ${fixes} nodes`);
|
||||
|
||||
console.log(links.length);
|
||||
|
||||
nodeMap = Object.fromEntries(nodes.map(d => [d['id'], d]));
|
||||
links = links.filter(l => nodeMap[l.source] && nodeMap[l.target]).map(l => {
|
||||
l.source = nodeMap[l.source];
|
||||
l.target = nodeMap[l.target];
|
||||
l.nr = linkI++;
|
||||
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
|
||||
))
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
@ -999,6 +1432,7 @@ class Store {
|
|||
constructor(graph, parent) {
|
||||
this.nodes = graph.nodes;
|
||||
this.links = graph.links;
|
||||
// graph is a filtered version of this.nodes & this.links
|
||||
this.graph = {
|
||||
'nodes': [],
|
||||
'links': []
|
||||
|
@ -1012,10 +1446,52 @@ class Store {
|
|||
'categories': [],
|
||||
}
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
this._maps.push(map);
|
||||
return this;
|
||||
|
@ -1067,9 +1543,16 @@ class Store {
|
|||
|
||||
render() {
|
||||
CONFIG.filters.forEach(f => {
|
||||
|
||||
let labelEl = document.createElement('label')
|
||||
let inputEl = document.createElement('input')
|
||||
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";
|
||||
textEl.innerText = f;
|
||||
labelEl.appendChild(inputEl);
|
||||
|
@ -1103,7 +1586,7 @@ class Store {
|
|||
|
||||
|
||||
var mapGraph = new NodeMap('#map')
|
||||
var alluvialGraph = new AlluvialMap('#alluvial')
|
||||
// var alluvialGraph = new AlluvialMap('#alluvial')
|
||||
|
||||
// REQUEST ATLAS & GRAPH
|
||||
const req_data = new Request(CONFIG.dataUrl, { method: 'GET' });
|
||||
|
@ -1118,12 +1601,11 @@ Promise.all([fetch(req_data), fetch(req_world)])
|
|||
|
||||
mapGraph.setWorld(world);
|
||||
mapGraph.setStore(store);
|
||||
// console.log();
|
||||
alluvialGraph.setData(data.results);
|
||||
// alluvialGraph.setData(data.results);
|
||||
|
||||
store.render()
|
||||
mapGraph.render()
|
||||
alluvialGraph.render()
|
||||
// alluvialGraph.render()
|
||||
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
|
|
|
@ -8,22 +8,36 @@
|
|||
|
||||
<body>
|
||||
|
||||
<div id='map'></div>
|
||||
<div id='alluvial'></div>
|
||||
<div id='tooltip'></div>
|
||||
<div id='map'></div>
|
||||
<!-- <div id='alluvial'></div> -->
|
||||
|
||||
<div id="filters">
|
||||
<header>
|
||||
<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">
|
||||
<h3>Legend</h3>
|
||||
</aside>
|
||||
|
||||
<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>
|
||||
</header>
|
||||
|
||||
<script src="https://unpkg.com/d3-sankey@0"></script>
|
||||
<!-- <script src="//unpkg.com/d3fc@14.0.1"></script> -->
|
||||
<script src="graph.js"></script>
|
||||
<script src="https://d3js.org/d3.v6.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>
|
||||
|
||||
</html>
|
506
www/story.html
Normal file
506
www/story.html
Normal file
|
@ -0,0 +1,506 @@
|
|||
<!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