File: /var/www/nowruzgan.com/vis/cafe-baladieh/js/script.js
var dataset = [];
var spanDocs = new Map();
var center = {x: 0, y: -5, r: 0};
var spread = {x: 80, y: 50, r: 60}
var hammering = false;
var fullscreen = false;
fa = num => {
return `${num}`
.replace(/0/g, '۰')
.replace(/1/g, '۱')
.replace(/2/g, '۲')
.replace(/3/g, '۳')
.replace(/4/g, '۴')
.replace(/5/g, '۵')
.replace(/6/g, '۶')
.replace(/7/g, '۷')
.replace(/8/g, '۸')
.replace(/9/g, '۹');
}
$(async () =>{
await initDocs(raw);
initScrapboard();
initTimeline();
rearrange();
$('.fullscreen').click(event => openFullscreen(document.documentElement));
document.documentElement.addEventListener('fullscreenchange', event => {
fullscreen = !!document.fullscreenElement;
$('.fullscreen').toggleClass('hidden', document.fullscreenElement);
});
});
function openFullscreen(elem) {
if(fullscreen)
document.exitFullscreen();
else{
if (elem.requestFullscreen) elem.requestFullscreen();
else if (elem.mozRequestFullScreen) elem.mozRequestFullScreen();
else if (elem.webkitRequestFullscreen) elem.webkitRequestFullscreen();
else if (elem.msRequestFullscreen) elem.msRequestFullscreen();
}
}
function initTimeline(){
let spans = Array.from(spanDocs.entries()).map(entry => ({year: entry[1].year, span: entry[1].span})).reverse();
let lastYear = 1357;
let oneYearFlag = false;
for(let span of spans) {
let endYear = span.year+span.span;
if(lastYear>endYear){
let spanLength = lastYear-endYear;
lastYear = endYear;
if(spanLength>1) oneYearFlag = false;
else oneYearFlag = !oneYearFlag;
if(oneYearFlag) $('.time-span:first-child .date').remove();
let spanEl = $(`
<div class="time-span empty" data-year="${endYear}" style="--year-factor: ${spanLength};">
<div class="line"></div>
<div class="date">${fa(endYear)}</div>
</div>`);
$('.time-spans').prepend(spanEl);
}
let spanLength = lastYear-span.year;
lastYear = span.year;
if(spanLength>1) oneYearFlag = false;
else oneYearFlag = !oneYearFlag;
if(oneYearFlag) $('.time-span:first-child .date').remove();
let spanEl = $(`
<div class="time-span" data-year="${span.year}" style="--year-factor: ${spanLength};">
<div class="line"></div>
<div class="date">${fa(span.year)}</div>
</div>`);
$('.time-spans').prepend(spanEl);
}
$('.time-spans .time-span:last').addClass('last');
$('.time-span').click(event => {
if(hammering) return;
$this = $(event.target).closest('.time-span');
if($this.hasClass('empty')) return;
let selectedYear = $this.data().year;
$sts = $('.selected-time-span');
if($this.hasClass('active')){
selectedYear = 0;
$this.toggleClass('active', false);
$sts.toggleClass('visible', false);
$('body').removeClass('in-lightbox');
}else{
$this
.toggleClass('active', true)
.siblings()
.toggleClass('active', false);
$sts
.toggleClass('visible', true)
.css('left', $this.position().left)
.css('width', $this.width());
$('body').addClass('in-lightbox');
}
rearrange(selectedYear);
});
let timeEl = $('.timeline')[0];
let mc = new Hammer(timeEl);
mc.on('panstart', event => {
hammering = true;
let timeEl = $('.timeline');
timeEl.data('pose', timeEl.position().left);
// timeEl.data('panning', true);
});
mc.on('pan', event => {
event.srcEvent.preventDefault();
let timeEl = $('.timeline');
let left = timeEl.data('pose')+event.deltaX;
let width = $('body').width();
left = Math.min(width*.1, left);
left = Math.max(-width*1.1, left);
timeEl.css('left', `${left}px`);
});
mc.on('panend', event => {
setTimeout(() => hammering = false, 300);
});
$('.timeline-section:first-child').click(event => {
let width = $('body').width();
// $('.timeline').css('left', `${width*.1}px`);
$('.timeline').animate({left: width*.1}, 1000);
});
$('.timeline-section:last-child').click(event => {
let width = $('body').width();
// $('.timeline').css('left', `${-width*1.1}px`);
$('.timeline').animate({left: -width*1.1}, 1000);
});
}
function rearrange(year) {
reorderZ();
if(!year)
$('.scrapboard .doc').each((i, doc) => {
doc.style.setProperty('--x', `${(Math.random() - .5)*spread.x + center.x}vw`);
doc.style.setProperty('--y', `${(Math.random() - .5)*spread.y + center.y}vh`);
doc.style.setProperty('--r', `${(Math.random() - .5)*spread.r + center.r}deg`);
doc.style.setProperty('--s', .5);
});
else
updateLightbox(spanDocs.get(`_${year}`).docs);
}
function updateLightbox(lightbox){
let $docs = $('.scrapboard .doc');
let lightboxIds = lightbox.map(record => record.index);
for(let doc of $docs) {
if(lightboxIds.includes(parseInt(doc.id.substr(1)))) {
doc.style.setProperty('--x', `${(Math.random() - .5)*spread.x*.7 + center.x}vw`);
doc.style.setProperty('--y', `${(Math.random() - .5)*spread.y*.7 + center.y}vh`);
doc.style.setProperty('--r', `${(Math.random() - .5)*spread.r + center.r}deg`);
doc.style.setProperty('--s', .5);
doc.classList.add('lightbox');
}else{
let docx = parseFloat(doc.style.getPropertyValue('--x').replace('vw', ''));
if(docx == center.x) docx = center.x + Math.random()*10-5;
let docy = parseFloat(doc.style.getPropertyValue('--y').replace('vh', ''));
if(docy == center.y) docy = center.y + Math.random()*10-5;
let distance = Math.sqrt((docx - center.x)**2 + (docy - center.y)**2);
let maxDistance = Math.sqrt((spread.x/2)**2 + (spread.y/2)**2)*.9;
let minDistance = maxDistance*.7;
if(distance<minDistance || distance>maxDistance) {
let factor = (minDistance + Math.random()*(maxDistance - minDistance))/distance;
doc.style.setProperty('--x', `${factor*(docx - center.x) + center.x}vw`);
doc.style.setProperty('--y', `${factor*(docy - center.y) + center.y}vh`);
doc.style.setProperty('--r', `${(Math.random() - .5)*spread.r + center.r}deg`);
}
doc.style.setProperty('--s', .3);
doc.classList.remove('lightbox');
}
}
}
function initDocs(raw) {
let map = {};
raw
.split('\n')
.map(line => line.split(','))
.filter((record, i) => record.length==6)
.forEach((record, i) => {
if(!/^\d+$/.exec(record[0])) return;
record[4] = record[4].trim();
let key = `${record[2]}_${record[4]}`;
if(!map[key]) {
let yearFrom = parseInt(record[2].split('-')[0]);
let yearTo = parseInt(record[2].split('-')[1] || '0');
if(!yearTo) yearTo = yearFrom;
yearTo++;
map[key] = {
type: record[1],
year: yearFrom,
span: yearTo - yearFrom,
docset: fa(record[3]),
caption: record[4],
files: []
};
let span = spanDocs.get(`_${map[key].year}`);
if(!span){
span = {year: map[key].year, span: map[key].span, docs: []};
spanDocs.set(`_${map[key].year}`, span);
}
span.docs.push(map[key]);
}
map[key].files.push({
file: record[0],
cite: fa(record[5])
});
});
dataset = Object
.keys(map)
.map(key => map[key]);
return new Promise((resolve, reject) => {
// return resolve(); /* ------------ */
let setImage = i => {
i--;
if(i<0) return setTimeout(() => resolve(), 1000);
let doc = dataset[i];
doc.index = i;
let $doc = $(`<div class="doc invisible" id="_${i}"></div>`);
// for(let fileConf of doc.files)
$doc.append(`<img class="thumb" src="docs-thumb/${doc.files[0].file}.jpg"></div>`);
$('.scrapboard').append($doc);
$doc.data('conf', doc);
$doc.find('img:first-child')/*.addClass('visible')*/.on('load', event => {
let $doc = $(event.target).closest('.doc');
let w = event.target.naturalWidth; let h = event.target.naturalHeight;
if(w>h) {
h = 400*h/w;
w = 400;
}else{
w = 400*w/h;
h = 400;
}
$doc
.css('width', `${w}px`)
.css('height', `${h}px`)
.css('margin-left', `${-w/2}px`)
.css('margin-top', `${-h/2}px`)
.toggleClass('invisible', false);
$doc[0].style.setProperty('--ss', Math.min($('body').width()*.8/w, ($('body').height() - 230)*.8/h));
});
$doc.click(event => {
if(hammering) return;
if($doc.hasClass('spotlight'))
hideSpotlight($doc);
else
showSpotlight($doc);
});
$('.spotlight-bg').click(event => {
$('.doc.spotlight').click();
});
$('.lightbox-bg').click(event => {
$('.time-span.active').click();
});
setTimeout(() => setImage(i), 0);
};
setImage(dataset.length);
});
}
function hideSpotlight($doc) {
$('body').toggleClass('in-spotlight', false);
$doc
.toggleClass('spotlight', false)
.find('img.hires')
.remove();
$doc[0].style.setProperty('--z', ++maxz);
}
function showSpotlight($doc) {
$('body').toggleClass('in-spotlight', true);
for(let fileConf of $doc.data('conf').files)
$doc.append(`<img class="hires" src="docs/${fileConf.file}.jpg"></div>`);
$doc.find('img.hires:nth-child(2)').addClass('visible');
$doc.toggleClass('spotlight', true);
$('.spotlight-desc .doc-text').text($doc.data('conf').caption);
$('.spotlight-desc .img-text').text($doc.data('conf').files[0].cite);
let numBullets = $doc.data('conf').files.length;
$('.spotlight-pagination .bullet').remove();
$('.spotlight-pagination .bullet-index').remove();
if(numBullets>1){
$('.spotlight-pagination .bullets').removeClass('hidden').append($('<div class="bullet-index"></div>'));
for(let i=0; i<numBullets; i++)
$('.spotlight-pagination .bullets').append($('<div class="bullet"></div>').click(event => gotoPage(i)));
alignBullet(0);
}else
$('.spotlight-pagination .bullets').addClass('hidden');
}
function nextPage() {
let $doc = $('.doc.spotlight');
let visible = $doc.find('img.visible').index() - 1;
let next = (visible + 1) % $doc.data('conf').files.length;
gotoPage(next);
}
function prevPage() {
let $doc = $('.doc.spotlight');
let visible = $doc.find('img.visible').index() - 1;
let prev = visible ? visible - 1 : $doc.data('conf').files.length - 1;
gotoPage(prev);
}
function gotoPage(i) {
let $doc = $('.doc.spotlight');
if($doc.data('conf').files.length<2) return;
let $old = $doc.find('img.visible');
let $new = $doc.find(`img:nth-child(${i+2})`);
$('.spotlight-desc .img-text').text($doc.data('conf').files[i].cite);
$old
.removeClass('visible')
.css('animation', `to-${$old.index()>i ? 'left' : 'right'} 1s`);
$new
.addClass('visible')
.css('animation', `from-${$old.index()>i ? 'right' : 'left'} 1s`);
alignBullet(i);
}
function alignBullet(i) {
let pos = $(`.bullet:nth-child(${i+2})`).position();
$('.bullets .bullet-index').css('left', pos.left+'px');
$('.bullets .bullet-index').css('top', pos.top+'px');
}
maxz = 1;
function initScrapboard() {
$('.scrapboard .doc').each((index, el) => {
let $doc = $(el);
var mc = new Hammer(el);
var pinch = new Hammer.Pinch();
var rotate = new Hammer.Rotate();
pinch.recognizeWith(rotate);
mc.add([pinch, rotate]);
mc.get('pan').set({ direction: Hammer.DIRECTION_ALL });
mc.on("panstart", event => {
$doc.toggleClass('panning', true);
let style = $doc[0].style;
$doc[0].style.setProperty('--z', ++maxz);
$doc.data('offset', {
x: event.srcEvent.clientX - $('body').width()*(.5 + style.getPropertyValue('--x').replace('vw', '')/100),
y: event.srcEvent.clientY - $('body').height()*(.5 + style.getPropertyValue('--y').replace('vh', '')/100)
});
if(maxz>1000) reorderZ();
hammering = true;
});
mc.on("pan pinch", event => {
if($doc.hasClass('spotlight')) return;
$doc[0].style.setProperty('--x', (100*(event.srcEvent.clientX-$doc.data('offset').x)/$('body').width() - 50)+'vw');
$doc[0].style.setProperty('--y', (100*(event.srcEvent.clientY-$doc.data('offset').y)/$('body').height() - 50)+'vh');
});
mc.on("panend pinchend", event => {
$doc.toggleClass('panning', false);
setTimeout(() => hammering = false, 300);
});
mc.on('swipeleft swiperight', event => {
if(!$doc.hasClass('spotlight')) return;
if($doc.find('img').length<2) return;
if(event.type=='swiperight')
nextPage($doc);
else
prevPage($doc);
})
});
}
function reorderZ() {
maxz = 1;
$('.scrapboard .doc')
.map((i, el) => ({el: el, z: parseInt(el.style.getPropertyValue('--z')) || 1}))
.sort((a, b) => a.z-b.z)
.map((i, record) => record.el.style.setProperty('--z', maxz++));
}