This commit is contained in:
Ruben van de Ven 2018-08-26 13:15:02 +02:00
parent d0b3be2209
commit e1072f9899
12 changed files with 18799 additions and 111 deletions

9
.babelrc Normal file
View File

@ -0,0 +1,9 @@
{
"presets": [
["env", {
"targets": {
"browsers": ["> 0.5%", "last 2 versions", "Firefox ESR", "not dead"]
}
}]
]
}

8
d3.min.js vendored

File diff suppressed because one or more lines are too long

72
gulpfile.js Normal file
View File

@ -0,0 +1,72 @@
var gulp = require('gulp');
var sass = require('gulp-sass');
var babel = require('gulp-babel');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var browserSync = require('browser-sync');
// todo: rollup for d3 & possibly jsonld
var paths = {
"styles": {
"src": "./src/scss/*.scss",
"dest": "./assets/css/"
},
"scripts": {
"src": "./src/js/*.js",
"dest": "./assets/js/"
},
"data": {
"src": "./rubenvandeven.jsonld",
"dest": "./assets/rubenvandeven.jsonld"
}
};
gulp.task('styles', function() {
gulp.src(paths.styles.src, { sourcemaps: true })
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.styles.dest))
.pipe(browserSync.reload({ stream: true }));
});
gulp.task('scripts', function() {
return gulp.src(paths.scripts.src)
.pipe(sourcemaps.init())
.pipe(babel({
ignore: [
'./src/js/d3.v5.js',
]
}))
.pipe(concat('portfolio.js'))
.pipe(gulp.dest(paths.scripts.dest)) // save .js
.pipe(uglify())
.pipe(rename({ extname: '.min.js' }))
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest(paths.scripts.dest)) // save .min.js
});
var watchStylesAndScripts = function() {
gulp.watch(paths.styles.src,['styles']);
gulp.watch(paths.scripts.src,['scripts', browserSync.reload]);
}
gulp.task('watch', watchStylesAndScripts);
// watch files for changes and reload
gulp.task('serve', function() {
browserSync({
server: {
baseDir: '.'
}
});
gulp.watch(['index.html','rubenvandeven.jsonld'], browserSync.reload);
watchStylesAndScripts();
});
gulp.task('default', function() {
gulp.parallel('styles', 'scripts');
});

View File

@ -3,7 +3,7 @@
<head>
<title></title>
<link href="rubenvandeven.jsonld" rel="alternate" type="application/ld+json" />
<link rel="stylesheet" href="/portfolio.css">
<link rel="stylesheet" href="/assets/css/portfolio.css">
</head>
<body>
<svg id="portfolioGraph" width="800" height="600" reserveAspectRatio="xMidYMid" viewBox="-400 -300 800 600" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
@ -50,11 +50,12 @@
</div>
</svg>
<div id="graphControls">
<span class="typeJump">Show:</span>
<ul id='typeLinks'></ul>
<ul id='relLinks'></ul>
<!-- <ul id='relLinks'></ul> -->
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsonld@0.5.21/dist/jsonld.js"></script>
<script type="text/javascript" src="d3.min.js"></script>
<script type="text/javascript" src="/portfolio.js"></script>
<!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsonld@0.5.21/dist/jsonld.js"></script> -->
<!-- <script type="text/javascript" src="/assets/js/d3.min.js"></script> -->
<script type="text/javascript" src="/assets/js/portfolio.js"></script>
</body>
</html>

1
js/jsonld.min.js vendored

File diff suppressed because one or more lines are too long

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "portfolio",
"version": "1.0.0",
"description": "",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Ruben van de Ven",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-preset-env": "^1.7.0",
"browser-sync": "^2.24.6",
"gulp": "^3.9.1",
"gulp-babel": "^7.0.1",
"gulp-concat": "^2.6.1",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.1",
"gulp-sourcemaps": "^2.6.4",
"gulp-uglify": "^3.0.1"
},
"dependencies": {
"jsonld": "^1.0.4"
}
}

View File

@ -7,43 +7,77 @@
"@type": "Person",
"name": "Ruben van de Ven",
"email": "info@rubenvandeven.com",
"nationality": {
"@type": "Country",
"@id": "Netherlands",
"name": "The Netherlands"
},
"nationality": "The Netherlands",
"@reverse": {
"author": [
{
"@type": "MediaObject",
"name": "Sustaining Gazes",
"dateCreated": "2018",
"description": "...",
"@reverse": {
"workFeatured": [
{
"@type": "ExhibitionEvent",
"name": "Route du Nord",
"url": "http://routedunord.nl/portfolio-item/ruben-van-de-ven-2/",
"location": {
"@type": "Place",
"name": "Rotterdam",
"address": "MuseumsQuartier /Vienna"
},
"startDate": "2018-05",
"endDate": "2018-05",
"workFeatured": []
}
]
},
"image": [
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/sustaining-gazes-2.jpg"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/sustaining-gazes-1.jpg"
}
]
},
{
"@type": "Series",
"@type": "CreativeWorkSeries",
"name": "MVP's",
"about": "lorem ipsum etc.",
"hasPart": [
{
"@type": "MediaObject",
"name": "MVP#1",
"dateCreated": "2018",
"description": "....",
"@reverse": {
"workFeatured": [
{
"@id": "blabla",
"@type": "PublicationEvent",
"@id": "kick2018exhibit",
"@type": "ExhibitionEvent",
"name": "KickstART",
"location": {
"@type": "Place",
"name": "In4Art Gallery",
"address": {
"@type": "PostalAddress",
"addressLocality": "Amsterdam",
"addressCountry": {
"@type": "Country",
"@id": "Netherlands",
"name": "The Netherlands"
}
}
"name": "In4Art Project Space",
"address": "Amsterdam"
},
"startDate": "2018-03-28",
"endDate": "2018-05-28"
"endDate": "2018-03-28"
},
{
"@id": "in4artSalon",
"@type": "ExhibitionEvent",
"name": "Salon VI - Innovatism",
"location": {
"@type": "Place",
"name": "In4Art Project Space",
"address": "Amsterdam"
},
"startDate": "2018-05-18",
"endDate": "2018-05-27"
}
]
},
@ -53,11 +87,12 @@
{
"@type": "MediaObject",
"name": "MVP#2",
"dateCreated": "2018",
"description": "....",
"@reverse": {
"workFeatured":
{
"@id": "blabla"
"@id": "kick2018exhibit"
}
},
"image": [
@ -66,11 +101,12 @@
{
"@type": "MediaObject",
"name": "MVP#3",
"dateCreated": "2018",
"description": "....",
"@reverse": {
"workFeatured":
{
"@id": "blabla"
"@id": "kick2018exhibit"
}
},
"image": [
@ -78,6 +114,29 @@
}
]
},
{
"@type": "MediaObject",
"name": "Spectacular Spectator Mood Meter",
"dateCreated": "2017",
"description": "...",
"@reverse": {
"workFeatured": [
{
"@type": "ExhibitionEvent",
"name": "The Black Box Concern",
"url": "....",
"location": {
"@id": "v2_"
},
"startDate": "2017-12",
"workFeatured": []
}
]
},
"image": [
]
},
{
"@type": "MediaObject",
"name": "Emotion Hero",
@ -86,24 +145,107 @@
"@reverse": {
"workFeatured": [
{
"@type": "PublicationEvent",
"@type": "ExhibitionEvent",
"name": "Mood Swings",
"location": {
"@type": "Place",
"name": "Q21",
"address": {
"@type": "PostalAddress",
"name": "Museums Quartier",
"addressCountry": {
"@type": "Country",
"name": "Austria"
},
"addressLocality": "Vienna"
}
"name": "frei_raum / Q21",
"address": "MuseumsQuartier /Vienna"
},
"startDate": "2017-03-28",
"endDate": "2017-05-28",
"workFeatured": []
},
{
"@type": "ExhibitionEvent",
"name": "Open Codes",
"url": "https://open-codes.zkm.de/",
"location": {
"@type": "Place",
"name": "ZKM",
"address": "Karlsrue"
},
"startDate": "2017-10-20",
"endDate": "2018-08-05",
"workFeatured": []
},
{
"@type": "ExhibitionEvent",
"name": "Plan D",
"url": "...",
"location": "Zagreb",
"startDate": "2017-10",
"endDate": "2018-10",
"awarded": "honorable mention",
"workFeatured": []
},
{
"@type": "ExhibitionEvent",
"name": "Ars Electronica",
"url": "...",
"location": {
"@type": "Place",
"name": "Ars Electronica",
"address": "Linz"
},
"startDate": "2017-09",
"endDate": "2017-09",
"organiser": { "@id":"v2_" },
"funded": "Creative Industries Fund",
"workFeatured": []
},
{
"@type": "ExhibitionEvent",
"name": "Microbites of Creativity",
"url": "...",
"organiser": "ACM Creativity & Cognition",
"location": {
"@type": "Place",
"name": "Museum of Arts & Sciences",
"address": "Singapore"
},
"startDate": "2017-06",
"endDate": "2017-06",
"workFeatured": []
},
{
"@id": "codesandmodes",
"@type": "ExhibitionEvent",
"name": "Codes & Modes II",
"organiser": "Integrated Media Arts MFA",
"location": {
"@type": "Place",
"name": "Hunter College",
"address": "New York"
},
"startDate": "2017-03",
"endDate": "2017-03",
"workFeatured": []
},
{
"@id": "stateofemotion",
"@type": "ExhibitionEvent",
"name": "STATE of Emotion",
"location": {
"@type": "Place",
"name": "STATE Festival",
"address": "Berlin"
},
"startDate": "2016-11",
"endDate": "2016-11",
"workFeatured": []
},
{
"@type": "ExhibitionEvent",
"name": "Manipulation: Emotion Hero",
"location": {
"@type": "Place",
"name": "Arquivo237",
"address": "Lisbon"
},
"startDate": "2016-09",
"endDate": "2016-09",
"workFeatured": []
}
]
},
@ -116,44 +258,177 @@
"@type": "ImageObject",
"contentUrl": "assets\/image\/emotionhero2.jpg"
}
],
"subjectOf": [
{
"@type": "BlogPosting",
"url": "http://blog.affectiva.com\/sdk-on-the-spot-emotion-hero-app-encourages-play-with-facial-expressions",
"dateCreated": "2017-01-1-18",
"publisher": "Affectiva",
"name": "SDK On the Spot: Emotion Hero Project Encourages Play with Facial Expressions"
}
]
},
{
"@type": "MediaObject",
"name": "Choose How You Feel; You Have Seven Options",
"dateCreated": "2016",
"description": "...",
"@reverse": {
"workFeatured": [
{
"@type": "ExhibitionEvent",
"name": "Big Stories Need Human Stakes",
"location": {
"@type": "Place",
"name": "Nieuwe Vide",
"address": "Haarlem"
},
"startDate": "2018-02",
"endDate": "2018-03",
"workFeatured": []
},
{
"@type": "ExhibitionEvent",
"name": "Test_Lab the Graduation Edition",
"url": "...",
"location": {
"@id": "v2_",
"@type": "Place",
"name": "V2_",
"address": "Rotterdam"
},
"startDate": "2016-07",
"endDate": "2016-07",
"workFeatured": []
},
{
"@type": "ExhibitionEvent",
"name": "Fuzzy Logic - Graduation Show",
"url": "...",
"location": {
"@type": "Place",
"name": "Piet Zwart Institute",
"address": "Rotterdam"
},
"startDate": "2016-06",
"endDate": "2016-06",
"workFeatured": []
}
]
},
"image": [
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/choosehowyoufeel2-2.jpg"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/choosehowyoufeel.jpg"
}
]
},
{
"@type": "MediaObject",
"name": "EYE Without A Face",
"dateCreated": "2015-01-01",
"description": "lorem ipsum.",
"dateCreated": "2016",
"description": "...",
"author": {
"@type": "Person",
"name": "Cristina Cochior",
"url": "http://randomizer.info"
},
"image": [
]
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/eye_0-2.jpg"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/eye_3-2.jpg"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/eye_6-2.jpg"
}
],
"@reverse": {
"workFeatured": [
{
"@type": "ExhibitionEvent",
"name": "Video Vortex",
"location": {
"@type": "Place",
"name": "...",
"address": "Kochi (India)"
},
"startDate": "2017-04",
"endDate": "2017-04"
},
{
"@type": "ExhibitionEvent",
"name": "Grand Instant Fiction",
"location": {
"@type": "Place",
"name": "Umakart Gallery",
"address": "Brno"
},
"startDate": "2016-12",
"endDate": "2016-12"
},
{
"@type": "ExhibitionEvent",
"name": "Boundaries of the Archive",
"location": {
"@type": "Place",
"name": "EYE Film Museum",
"address": "Amsterdam"
},
"startDate": "2016-04",
"endDate": "2014-04"
}
]
}
},
{
"@type": "Report",
"name": "Choose How You Feel; You Have Seven Options",
"url": "http://networkcultures.org/longform/2017/01/25/choose-how-you-feel-you-have-seven-options/",
"datePublished": "2017-01-27",
"publisher": "Institute of Network Cultures"
},
{
"@type": "MediaObject",
"name": "We know how you feel",
"dateCreated": "2015-01-01",
"dateCreated": "2015",
"description": "A two-part artwork and my first work on software that derives emotional parameters from facial expressions. The first part displays the <em>Mind Reading Emotions Library<\/em>, an interactive collection of videos, audio fragments and scenes depicting 412 distinct emotions &mdash;ranging from `angry' to `unsure'&mdash; grouped in 24 categories. The second part of the project is a tablet with a modified demo app by Affectiva, a major player in the field of emotion analysis software. This app acts as an interactive mirror which displays the various parameters that the Affectiva software derives from someone's facial expression, while a voice over reads a text extracted from the Dutch classic <em>Beyond Sleep<\/em> by W.F. Hermans (1966), concerning the impact of the mirror, photography and video on the human self-image.\n\nIt was part of the exhibition <em><a href='http://v2.nl\/events\/an-encyclopedia-of-media-objects'>Encyclopedia Of Media Objects<\/a><\/em>, organised by the Piet Zwart Institute and held at the V2_ Institute for unstable media in June 2015.",
"image": [
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/fragments-6-2.jpg",
"caption": "Fragments of reality"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/fragments-9-2.jpg",
"caption": "Fragments of reality"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/fragments-7-2.jpg",
"contentUrl": "assets\/image\/we_know_how_you_feel.jpg",
"caption": "Fragments of reality"
}
]
],
"@reverse": {
"workFeatured": [
{
"@id": "codesandmodes"
},
{
"@id": "stateofemotion"
},
{
"@type": "ExhibitionEvent",
"name": "Encyclopedia of Media Objects",
"url": "...",
"startDate": "2015-06",
"endDate": "2015-06",
"location": {
"@id": "v2_"
}
}
]
}
},
{
@ -178,6 +453,98 @@
"caption": "Fragments of reality"
}
]
},
{
"@type": "MediaObject",
"name": "Waterdagen",
"dateCreated": "2013",
"description": "Adam is security guard at a nursing home with comatose patients. During his night shifts he has unsettling visions of someone drowning. He seeks his comfort in the company of his pregnant girlfriend. Days of Water is a story on guilt and shame; and on a longing for hope.\nI wrote and directed this film for my graduation as film director for fiction at the Utrecht School of the Arts.\nIt was accompanied by my MA research on spectatorial emotion and the influence emotions on the spectator's sympathy for the protagonist.",
"image": [
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/waterdagen-teaser.jpg"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/waterdagen1.jpg"
}
],
"@reverse": {
"workFeatured": [
{
"@type": "ExhibitionEvent",
"name": "High Desert International Film Festival ",
"url": "http://routedunord.nl/portfolio-item/ruben-van-de-ven-2/",
"location": {
"@type": "Place",
"name": "High Desert International Film Festival ",
"address": "Pahrump, Nevada"
},
"startDate": "2014-05",
"endDate": "2014-05",
"workFeatured": []
}
]
}
},
{
"@type": "MediaObject",
"name": "Co-Poet",
"dateCreated": "2012",
"url": "http://projectkaleido.nl/#copoet",
"description": "For the visitors of the Crossing Border festival in The Hague we desgined an installation to collaboratively contribute to a poem.\nBased on the input of the user, a sentence is added to the poem, originating from digitalised texts of artists participating in the festival.\nThe text was combined with an audiovisual presentation that changed depending on the user's input.",
"image": [
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/copoet1.jpg"
}
],
"@reverse": {
"workFeatured": [
{
"@type": "ExhibitionEvent",
"name": "Crossing Border",
"location": {
"@type": "Place",
"name": "Leidse Schouwburg",
"address": "Leiden"
},
"startDate": "2012",
"endDate": "2012",
"workFeatured": []
}
]
}
},
{
"@type": "MediaObject",
"name": "Rangi Ya Samawati",
"dateCreated": "2012",
"description": "The Colour of Blue. A diptych about two Tanzanians: a miner and a lobster fisher. How do they live, work and survive; and how do they see their own future? Each of the two men is in his own way depending on nature, everyday gambling for a good catch.\nThis documentary was shot complementary to our work at the Kilimanjaro Film Institute in Arusha.\nDocumentary, 1442″. The full film is available on Youtube.",
"image": [
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/samawati-prev2.jpg"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/samawati-prev1.jpg"
}
],
"video": "http://www.youtube.com/watch?v=D8pMy7xOZnI"
},
{
"@type": "MediaObject",
"name": "Virtual Afterlife",
"dateCreated": "2010",
"description": "A short HKU project on the future of social media, when it was still an upcomming phenomenon. It is made in two weeks for a series of shorts with a given start and ending image.\nWe asked ourselves: what happens if our online identity lives on after we die? Do we still live on if a part of our online identity does? How can this virtual identity be shaped?\n210″",
"image": [
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/guerilla2.jpg"
}
],
"video": "http://www.youtube.com/watch?v=7Nk24Mh6uMc"
}
],
"contributor": [
@ -197,6 +564,11 @@
"@type": "ImageObject",
"contentUrl": "assets\/image\/times1.jpg",
"caption": "The Spectacular Times"
},
{
"@type": "ImageObject",
"contentUrl": "assets\/image\/times2.jpg",
"caption": "The Spectacular Times"
}
]
}

View File

@ -26,11 +26,24 @@ background: -moz-linear-gradient(45deg, #d2dfed 0%, #afc1d8 13%, #d5e0ef 28%, #b
background: -webkit-linear-gradient(45deg, #d2dfed 0%,#afc1d8 13%,#d5e0ef 28%,#bed0ea 51%,#a8c0dd 51%,#c0d0e5 63%,#bad0ef 75%,#a2bad8 88%,#799bc8 100%);
background: linear-gradient(45deg, #d2dfed 0%,#afc1d8 13%,#d5e0ef 28%,#bed0ea 51%,#a8c0dd 51%,#c0d0e5 63%,#bad0ef 75%,#a2bad8 88%,#799bc8 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d2dfed', endColorstr='#799bc8',GradientType=1 );
height: 100vh;
/*-moz-animation: bgShift 5s infinite;
background-size: 400% 400%;*/
}
a, a:link, a:visited{
color:blue;
text-decoration: none;
cursor:pointer; /* Not all links have a href, so force pointer for those too */
}
a:hover{
text-decoration: underline;
}
a:active{
color:red;
}
/*@-moz-keyframes bgShift{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
@ -39,7 +52,7 @@ background-size: 400% 400%;*/
svg{
width:100vw;
height: calc(100vh - 40px);
cursor: dragging;
cursor: grab;
}
svg.dragging{
cursor: grabbing;
@ -60,18 +73,25 @@ g.node.visibleNode/* , g.node.ImageObject */
}
g.node circle.highlightCircle{
fill: none;
stroke-width:0px;
stroke-dasharray: 3 2;
}
g.node:hover circle.highlightCircle{
stroke-width: 2px;
stroke-width: 1px;
stroke: blue;
}
g.node:active circle.highlightCircle{
stroke-width:1px;
stroke: red;
}
g.node.centeredNode circle.highlightCircle{
stroke-width:1px;
stroke: green;
stroke-dasharray: 3 2;
}
g.node.selectedNode circle.highlightCircle{
color: red;
stroke-width:1px;
stroke: red;
}
/* g.node.selectedNode circle.highlightCircle{
stroke-width:1px;
stroke: red;
} */
g.node.drag{
cursor:grabbing;
}
@ -146,14 +166,13 @@ text{
#nodeDetails{
position: absolute;
top: 0;
right: -720px;
bottom: 10px;
right: -740px;
width: 700px;
background: white;
padding: 20px;
/* opacity: 0; */
transition: opacity 1s, right 1s;
max-height: 100%;
height: 100%;
overflow-y: auto;
}
body.detailsOpen #nodeDetails{
@ -163,7 +182,8 @@ body.detailsOpen #nodeDetails{
svg#portfolioGraph {
position: relative;
right: 0;
transition: right 1s;
top: 0;
transition: right 1s, top 1s;
}
body.detailsOpen svg#portfolioGraph{
right: calc(720px / 2);
@ -201,21 +221,58 @@ body.detailsOpen svg#portfolioGraph{
#nodeDetails dd.dd-description{
margin-left:0;
}
#nodeDetails dd.dd-contentobject{
margin-left:0;
}
#nodeDetails dd.dd-contentobject object{
width: 100%;
}
#graphControls{
position:fixed;
left:0;
bottom:0;
right:0;
height:40px;
background:white;
position: fixed;
left: 10px;
top: 10px;
height: auto;
background: white;
padding: 10px;
}
#graphControls .typeJump{
font-weight:bold;
}
#graphControls ul{
margin:0;
padding: 0;
display:inline-block;
}
#graphControls li{
list-style:none;
display: inline-block;
margin: 9px 10px 0;
margin: 10px 10px;
cursor: pointer;
}
@media (max-width: 1000px) {
body{
overflow:auto;
}
#nodeDetails{
/* display:none; */
position: static;
width: 100%;
background: white
;
padding: 20px;
/* opacity: 0; */
height:auto;
margin-top:0;
transition: margin 1s;
}
body.detailsOpen #nodeDetails{
displaY:block;
margin-top: -33vh;
}
body.detailsOpen svg#portfolioGraph{
right: 0;
top: -33vh;
}
}

17858
src/js/d3.v5.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ function getTitleAttribute(node) {
break;
case "ImageObject":
if(typeof node['caption'] !== "undefined") {return 'caption';}
break;
if(typeof node['contentUrl'] !== "undefined") {return 'contentUrl';}
break;
case "PostalAddress":
if(typeof node['addressLocality'] !== "undefined") {return 'addressLocality';}
@ -29,25 +29,25 @@ Transform a flattened jsonld into a d3 compatible graph
function jsonLdToGraph(data){
let nodes = {};
let links = [];
// collect all nodes
for(nodeId in data){
data[nodeId]["type"][0] = data[nodeId]["type"][0];
for(let nodeId in data){
// data[nodeId]["type"][0] = data[nodeId]["type"][0];
nodes[data[nodeId]["id"]] = data[nodeId];
}
// collect all links (separate loop as we need to check nodes)
for(nodeId in data) {
for(let nodeId in data) {
let node = data[nodeId];
currentId = node["id"];
for(key in node){
let currentId = node["id"];
for(let key in node){
let nodeAttr = Array.isArray(node[key]) ? node[key] : [node[key]];
// // relations should always be lists (eases assumptions)
// if(typeof node[key] !== "Array" && typeof node[key]['id'] !== "undefined") {
// node[key] = [node[key]];
// }
// every attribute is an Array after flatten(), loop them
for(i in nodeAttr) {
for(let i in nodeAttr) {
if(key !== "id" && typeof nodeAttr[i] === "string" && nodes[nodeAttr[i]]) {
links[links.length] = {
"source": currentId,
@ -77,6 +77,7 @@ function jsonLdToGraph(data){
"links": links
};
}
var graph;
var nodeMap = {};
jsonld.flatten(window.location.protocol + "//" + window.location.host + "/rubenvandeven.jsonld", {"@context": "https://schema.org/"},(err, flattened)=> {
@ -108,6 +109,7 @@ function inCircle(dx, dy, r) {
}
}
var nodePositions = {};
function startGraph(graph){
@ -118,9 +120,8 @@ var selectedNodeSize = 140;
// set some vars
var currentNodeIdx = 0;
var currentNodePositionRadius = 0;
var nodePositions = {};
var types = {};
for (nodeIdx in graph['nodes']) {
for (let nodeIdx in graph['nodes']) {
let type = graph['nodes'][nodeIdx]["type"];
if(typeof types[type] == 'undefined') {
types[type] = [];
@ -134,11 +135,13 @@ var relLinksEl = document.getElementById('relLinks');
// make controls
for (let typeName in types) {
let typeLinkEl = document.createElement("li");
typeLinkEl.innerHTML = typeName;
typeLinkEl.addEventListener('click', function(){
let typeLinkAEl = document.createElement("a");
typeLinkAEl.innerHTML = typeName;
typeLinkAEl.addEventListener('click', function(){
centerByType(typeName);
// positionNodesInCenter(types[typeName]);
});
typeLinkEl.append(typeLinkAEl);
typeLinksEl.appendChild(typeLinkEl);
}
@ -167,11 +170,11 @@ var link = svg.append("g")
.enter().append("g")
.attr("class", function(d){return "relationship "+d.name;})
;
linkLine = link
var linkLine = link
// .append("line");
.append("line").attr("marker-end", "url(#arrowHead)")
;
linkText = link
var linkText = link
.append("text")
.text(function(d){
return d.name;
@ -239,6 +242,7 @@ var positionNodesInCenter = function(idxs) {
// restart animation (they call that 'alpha' in d3 force)
simulation.alpha(1);
simulation.restart();
}
var positionNodesInCircle = function(idxs, r) {
let viewBox = getViewbox();
@ -253,7 +257,7 @@ var positionNodesInCircle = function(idxs, r) {
let cx = viewBox[0] + viewBox[2]/2;
let cy = viewBox[1] + viewBox[3]/2;
stepSize = 2*Math.PI / idxs.length;
let stepSize = 2*Math.PI / idxs.length;
for (var i = 0; i < idxs.length; i++) {
nodePositions[idxs[i]] = [
cx + Math.sin(stepSize * i) * r,
@ -262,7 +266,9 @@ var positionNodesInCircle = function(idxs, r) {
}
// restart animation (they call that 'alpha' in d3 force)
console.log("reset alpha");
simulation.alpha(1);
simulation.restart();
}
var centerByType = function(types) {
if(!Array.isArray(types)) {
@ -278,7 +284,6 @@ var centerByType = function(types) {
positionNodesInCenter(idxs.length ? idxs : null);
}
d3Selection="";
var selectedNodeTransition = d3.transition()
.duration(750)
.ease(d3.easeLinear);
@ -288,7 +293,7 @@ var nodeDetailEl = document.getElementById("nodeDetails");
var createRelationshipEl = function(relNode, i) {
let el = document.createElement("dd");
el.classList.add('relLink');
let titleEl = document.createElement('span');
let titleEl = document.createElement('a');
titleEl.innerHTML = getNodeTitle(relNode);
titleEl.classList.add('nodeTitle');
titleEl.classList.add('nodeTitleNr'+i);
@ -296,7 +301,7 @@ var createRelationshipEl = function(relNode, i) {
let idx = graph.nodes.indexOf(relNode);
selectNode(idx);
});
let typeEl = document.createElement('span');
let typeEl = document.createElement('a');
typeEl.classList.add('nodeType')
typeEl.innerHTML = relNode['type']
typeEl.addEventListener('click',function(e){
@ -351,7 +356,8 @@ var setDetails = function(nodeDatum, nodeIdx) {
if(attr == 'url') {
listEl.innerHTML += `<dt class='dt-${attr}'>${attr}</dt><dd class='dd-${attr}'><a href='${nodeAttr[i]}'>${nodeAttr[i]}</a></dd>`;
} else if(attr == 'contentUrl') {
listEl.innerHTML += `<dt class='dt-${attr}'>${attr}</dt><dd class='dd-${attr}'><object data='${nodeAttr[i]}'>${nodeAttr[i]}</object></dd>`;
listEl.innerHTML += `<dt class='dt-${attr}'>${attr}</dt><dd class='dd-${attr}'><a href='${nodeAttr[i]}'>${nodeAttr[i]}</a></dd>`;
listEl.innerHTML += `<dd class='dd-contentobject'><object data='${nodeAttr[i]}'></object></dd>`;
} else {
listEl.innerHTML += `<dt class='dt-${attr}'>${attr}</dt><dd class='dd-${attr}'>${nodeAttr[i]}</dd>`;
}
@ -390,7 +396,12 @@ var setDetails = function(nodeDatum, nodeIdx) {
for(let i in relDown[attr]) {
let rel = relDown[attr][i];
relsEl.appendChild(createRelationshipEl(rel));
// html += `<dd>${rel['type']}: ${rel['id']}</dd>`;
if(typeof rel['contentUrl'] != 'undefined') {
let ddEl = document.createElement('dd')
ddEl.classList.add('dd-contentobject');
ddEl.innerHTML = `<object data='${rel['contentUrl']}'></object>`
relsEl.appendChild(ddEl);
}
}
}
@ -401,7 +412,12 @@ var setDetails = function(nodeDatum, nodeIdx) {
for(let i in relUp[attr]) {
let rel = relUp[attr][i];
relsEl.appendChild(createRelationshipEl(rel, i));
// html += `<dd>${rel['type']}: ${rel['id']}</dd>`;
if(typeof rel['contentUrl'] != 'undefined') {
let ddEl = document.createElement('dd')
ddEl.classList.add('dd-contentobject');
ddEl.innerHTML = `<object data='${rel['contentUrl']}'></object>`
relsEl.appendChild(ddEl);
}
}
}
@ -463,7 +479,7 @@ var selectNode = function(idx){
}
});
*/
linkedIdxs = [];
let linkedIdxs = [];
link.each(function(d,idx,linkEls,q){
if(d.source == nodeDatum || d.target == nodeDatum) {
linkEls[idx].classList.add('activeLink','visibleLink');
@ -482,7 +498,7 @@ var selectNode = function(idx){
}
});
i = linkedIdxs.indexOf(idx);
let i = linkedIdxs.indexOf(idx);
if(i !== -1) {
linkedIdxs.splice(i, 1);
@ -533,7 +549,7 @@ simulation.force('centerActive', function force(alpha) {
});
//path to curve the tile
nodePath = node.append("path")
var nodePath = node.append("path")
.attr("id", function(d,idx){return "nodePath"+idx;})
.attr("d", function(){
var r = nodeSize * 0.9;
@ -641,7 +657,7 @@ function ticked() {
});
linkLine.each(function (d) {
var sourceX, targetX, midX, dy, dy, angle;
var sourceX, targetX, midX, dx, dy, angle;
// This mess makes the arrows exactly perfect.
// thanks to http://bl.ocks.org/curran/9b73eb564c1c8a3d8f3ab207de364bf4
@ -677,8 +693,8 @@ function ticked() {
srcSize = (typeof nodePositions[d.source.index] != 'undefined') ? selectedNodeSize : nodeSize;
tgtSize = (typeof nodePositions[d.target.index] != 'undefined') ? selectedNodeSize : nodeSize;
*/
srcSize = nodeSize;
tgtSize = nodeSize;
let srcSize = nodeSize;
let tgtSize = nodeSize;
// Compute the line endpoint such that the arrow
// is touching the edge of the node rectangle perfectly.
@ -781,6 +797,7 @@ function moveViewboxPx(dx, dy){
// start by selecting the first node :-)
// selectNode(currentNodeIdx+1);
// positionNodesInCenter(currentNodeIdx);
selectNode(currentNodeIdx); deselectNode();
selectNode(currentNodeIdx); //deselectNode();
closeDetails();
// positionNodesInCenter(currentNodeIdx+1);
}

285
src/scss/portfolio.scss Normal file
View File

@ -0,0 +1,285 @@
$detailsPadding: 20px;
$detailsWidth: 700px;
$detailSlide: -1 * ($detailsWidth + 2 * $detailsPadding);
$detailSlideMobile: -30vh;
body{
margin:0;overflow: hidden;
font-family: sans-serif;
/* background: #fceabb;
background: -moz-linear-gradient(-45deg, #fceabb 0%, #fccd4d 50%, #f8b500 51%, #fbdf93 100%);
background: -webkit-linear-gradient(-45deg, #fceabb 0%,#fccd4d 50%,#f8b500 51%,#fbdf93 100%);
background: linear-gradient(135deg, #fceabb 0%,#fccd4d 50%,#f8b500 51%,#fbdf93 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fceabb', endColorstr='#fbdf93',GradientType=1 ); */
/*background: #f7fbfc;
background: -moz-linear-gradient(45deg, #f7fbfc 0%, #d9edf2 40%, #add9e4 100%);
background: -webkit-linear-gradient(45deg, #f7fbfc 0%,#d9edf2 40%,#add9e4 100%);
background: linear-gradient(45deg, #f7fbfc 0%,#d9edf2 40%,#add9e4 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7fbfc', endColorstr='#add9e4',GradientType=1 );*/
/*background: #e2e2e2;
background: -moz-linear-gradient(45deg, #e2e2e2 0%, #dbdbdb 50%, #d1d1d1 51%, #fefefe 100%);
background: -webkit-linear-gradient(45deg, #e2e2e2 0%,#dbdbdb 50%,#d1d1d1 51%,#fefefe 100%);
background: linear-gradient(45deg, #e2e2e2 0%,#dbdbdb 50%,#d1d1d1 51%,#fefefe 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e2e2e2', endColorstr='#fefefe',GradientType=1 );*/
/*background: #d2dfed;
background: -moz-linear-gradient(45deg, #d2dfed 0%, #c8d7eb 26%, #bed0ea 51%, #a6c0e3 51%, #afc7e8 62%, #bad0ef 75%, #99b5db 88%, #799bc8 100%);
background: -webkit-linear-gradient(45deg, #d2dfed 0%,#c8d7eb 26%,#bed0ea 51%,#a6c0e3 51%,#afc7e8 62%,#bad0ef 75%,#99b5db 88%,#799bc8 100%);
background: linear-gradient(45deg, #d2dfed 0%,#c8d7eb 26%,#bed0ea 51%,#a6c0e3 51%,#afc7e8 62%,#bad0ef 75%,#99b5db 88%,#799bc8 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d2dfed', endColorstr='#799bc8',GradientType=1 );*/
background: #d2dfed;
background: -moz-linear-gradient(45deg, #d2dfed 0%, #afc1d8 13%, #d5e0ef 28%, #bed0ea 51%, #a8c0dd 51%, #c0d0e5 63%, #bad0ef 75%, #a2bad8 88%, #799bc8 100%);
background: -webkit-linear-gradient(45deg, #d2dfed 0%,#afc1d8 13%,#d5e0ef 28%,#bed0ea 51%,#a8c0dd 51%,#c0d0e5 63%,#bad0ef 75%,#a2bad8 88%,#799bc8 100%);
background: linear-gradient(45deg, #d2dfed 0%,#afc1d8 13%,#d5e0ef 28%,#bed0ea 51%,#a8c0dd 51%,#c0d0e5 63%,#bad0ef 75%,#a2bad8 88%,#799bc8 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d2dfed', endColorstr='#799bc8',GradientType=1 );
height: 100vh;
/*-moz-animation: bgShift 5s infinite;
background-size: 400% 400%;*/
// font-size:18px;
}
a, a:link, a:visited{
color:blue;
text-decoration: none;
cursor:pointer; /* Not all links have a href, so force pointer for those too */
}
a:hover{
text-decoration: underline;
}
a:active{
color:red;
}
/*@-moz-keyframes bgShift{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}*/
svg{
width:100vw;
height: calc(100vh - 40px);
cursor: grab;
}
svg.dragging{
cursor: grabbing;
}
g.node{
cursor: pointer;
stroke: blue;
stroke-width: 0;
transition: stroke-width .5s;
opacity: 0;
pointer-events: none;
transition: opacity 1s;
}
g.node.visibleNode/* , g.node.ImageObject */
{
opacity: 1;
pointer-events: auto;
}
g.node circle.highlightCircle{
fill: none;
stroke-width:0px;
stroke-dasharray: 3 2;
}
g.node:hover circle.highlightCircle{
stroke-width: 1px;
stroke: blue;
}
g.node:active circle.highlightCircle{
stroke-width:1px;
stroke: red;
}
g.node.centeredNode circle.highlightCircle{
stroke-width:1px;
stroke: red;
}
/* g.node.selectedNode circle.highlightCircle{
stroke-width:1px;
stroke: red;
} */
g.node.drag{
cursor:grabbing;
}
.node text{
text-anchor: start;
}
.relationship{
display:none;
}
.relationship.visibleLink{
display:block;
}
.relationship line{
fill:none;
stroke: #999;
stroke-width: 2px;
}
.relationship text{
fill:black;
/* text-transform: lowercase; */
font-size: 75%;
display:none;
}
.relationship.activeLink text{
fill:white;
display:block;
}
.relationship.activeLink line{
stroke: white;
}
circle.nodeBg{
fill: white;
/*stroke-width:.2em;*/
fill:url(#blueGrad);
.MediaObject & {
fill:url(#orangeGrad);
}
.Person & {
fill:url(#redGrad);
}
.PublicationEvent & {
fill:url(#limeGrad);
}
}
text{
text-anchor: middle;
}
.drag{
fill:#00f;
}
.relationship.address line{
/* stroke:#90F7FE; */
}
.relationship.location line{
/* stroke:darkgreen; */
}
.relationship.contributor line{
/* stroke:orange; */
/* stroke-width:.4em; */
}
#nodeDetails{
position: absolute;
top: 0;
right: $detailSlide;
width: $detailsWidth;
background: white;
padding: $detailsPadding;
/* opacity: 0; */
transition: opacity 1s, right 1s;
height: 100%;
overflow-y: auto;
.nodeTitle{
}
.nodeType{
font-size:80%;
text-transform: uppercase;
color: #999;
margin-left:10px;
&:hover{
cursor:pointer;
color:blue;
}
}
dt{
float:left;
width: 120px;
font-weight:bold;
}
dd:not(.nodeTitleNr1) {
margin-left: 130px;
}
dt.dt-description{
float:none;
}
dd.dd-description{
margin-left:0;
}
dd.dd-contentobject{
margin-left:0;
}
dd.dd-contentobject object{
width: 100%;
}
}
body.detailsOpen{
#nodeDetails{
/* opacity:1; */
right: 0px;
}
svg#portfolioGraph{
right: -1 * $detailSlide / 2;
}
}
svg#portfolioGraph {
position: relative;
right: 0;
top: 0;
transition: right 1s, top 1s;
}
#graphControls{
position: fixed;
left: 10px;
top: 10px;
height: auto;
background: white;
padding: 10px;
}
#graphControls .typeJump{
font-weight:bold;
}
#graphControls ul{
margin:0;
padding: 0;
display:inline-block;
}
#graphControls li{
list-style:none;
display: inline-block;
margin: 10px 10px;
cursor: pointer;
}
@media (max-width: 1000px) {
body{
overflow:auto;
}
#nodeDetails{
/* display:none; */
position: static;
width: 100%;
box-sizing: border-box;
background: white;
padding: $detailsPadding;
/* opacity: 0; */
height:auto;
margin-top:0;
transition: margin 1s;
}
body.detailsOpen{
#nodeDetails{
displaY:block;
margin-top: $detailSlideMobile;
position: relative;
z-index: 1000;
min-height:$detailSlideMobile * -1;
}
svg#portfolioGraph{
right: 0;
top: $detailSlideMobile / 2;
}
}
}