export const modal = content => { const wrapper = document.createElement('a') wrapper.href = '/' wrapper.className = 'fullview' const fullview = document.createElement('div') fullview.innerHTML = content wrapper.appendChild(fullview) return wrapper } export class Display { constructor(dispatch, target) { this.target = target const findA = target => { while (target.nodeName !== 'A') { target = target.parentNode if (!target) return } return target } target.addEventListener('click', e => { let target = findA(e.target) if (!target) return window.history.pushState(null, "", target.href) dispatch(target.getAttribute('href')) e.preventDefault() }) target.addEventListener('dragstart', e => { let target = findA(e.target) if (!target || !target.draggable) return // https://github.com/Bernardo-Castilho/dragdroptouch/pull/37 // e.dataTransfer.clearData() for (const x of e.dataTransfer.types) { e.dataTransfer.clearData(x) } e.dataTransfer.setData('application/prs.x', target.dataset['id']) e.dataTransfer.effectAllowed = 'link' }) target.addEventListener('dragenter', e => { let target = findA(e.target) if (!target) return e.preventDefault() }) target.addEventListener('dragover', e => { let target = findA(e.target) if (!target) return e.preventDefault() }) target.addEventListener('drop', e => { let target = findA(e.target) if (!target || !target.draggable) return dispatch({ action: 'link', from: e.dataTransfer.getData("application/prs.x"), to: target.dataset['id']}) && e.preventDefault() }) this.graph = new Springy.Graph() this.layout = new Springy.Layout.ForceDirected( this.graph, 15, 1000.0, // Node repulsion 0.5 // Damping ) } renderIntro(intro) { this.target.appendChild(modal(intro)) } render(state) { const target = document.createElement('div') target.className = 'wrapper' const field = document.createElement('ul') field.className = 'items' const graph = this.graph for (const item of state.items) { graph.addNode(new Springy.Node(item.id, item)) } for (const item of state.links) { graph.addEdge(new Springy.Edge(item.id, graph.nodeSet[item.from], graph.nodeSet[item.to], { text: state.items[item.from].linkable.filter(i => state.items[item.to].linkable.includes(i)).join(', ') })) } let currentBB = this.layout.getBoundingBox() let width = this.target.scrollWidth || 100 let height = this.target.scrollHeight || 100 const ballRadius = Math.min(document.body.clientWidth, document.body.clientHeight) * 0.06 var toScreen = function(p) { var size = currentBB.topright.subtract(currentBB.bottomleft); var sx = p.subtract(currentBB.bottomleft).divide(size.x).x * (width - ballRadius * 2) + ballRadius; var sy = p.subtract(currentBB.bottomleft).divide(size.y).y * (height - ballRadius * 2) + ballRadius; return new Springy.Vector(sx, sy); }; var renderer = new Springy.Renderer( this.layout, () => { currentBB = this.layout.getBoundingBox() width = this.target.scrollWidth height = this.target.scrollHeight field.innerHTML = '' }, function drawEdge(edge, p1, p2) { const dom = document.createElement('span') dom.innerText = edge.data.text p1 = toScreen(p1) p2 = toScreen(p2) if (p1.y > p2.y) [p1, p2] = [p2, p1] const a = p2.x - p1.x const b = p2.y - p1.y const negative = (a < 0) != (b < 0) let rad = Math.atan(b / a) if (negative) rad = rad + Math.PI if (rad > Math.PI / 2) { rad += Math.PI ;[p2, p1] = [p1, p2] } dom.className = 'line' dom.style.transform = 'rotate(' + rad + 'rad) translateY(-1em)' dom.style.left = p1.x + 'px' dom.style.top = p1.y + 'px' dom.style.right = p2.x + 'px' dom.style.bottom = p2.y + 'px' dom.style.width = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)) + 'px' // FIXME eigentlich falsches parent field.appendChild(dom) }, function drawNode(item, p) { const target = toScreen(p) const dom = document.createElement('li') const a = document.createElement('a') const action = item.data.state == 'face-down' ? 'flip' : 'show' a.href = `/${item.id}/${action}` a.draggable = false if (item.data.state !== 'face-down') { a.style.backgroundImage = `url("/img/${item.data.icon}")` a.dataset['id'] = item.id a.draggable = true } dom.className = item.data.state dom.style.position = 'absolute' dom.style.left = (target.x - ballRadius) + 'px' dom.style.top = (target.y - ballRadius) + 'px' dom.appendChild(a) field.appendChild(dom) }, undefined, undefined, () => { const dd = new diffDOM.DiffDOM() const diff = dd.diff(this.target.children[0], field) dd.apply(this.target.children[0], diff) } ); target.appendChild(field) if (state.items.filter(i => i.state != 'face-up').length == 0) { const a = document.createElement('a') a.className = 'continue' a.innerText = 'fertig' a.href = '/end' target.appendChild(a) } if (state.show !== null) { const data = state.items[state.show] const name = data.name const desc = data.desc const img = '/img/' + data.img const text = (data.text || []).map(v => '

' + v + '

').join('') const wrapper = modal(`

${name}

${desc}

${text}
${data.quote || ''}
`) target.appendChild(wrapper) } if (state.special) { target.appendChild(modal(state.special)) } const dd = new diffDOM.DiffDOM() const diff = dd.diff(this.target, target) dd.apply(this.target, diff) renderer.start(); } }