(function (window, document) { 'use strict'; Node.prototype.insertAfter = function (node, referenceNode) { if (referenceNode.nextSibling) { return this.insertBefore(node, referenceNode.nextSibling); } else { return this.appendChild(node); } } function getScale (transform) { var patt = /matrix\(((?:\d+\.)?\d+),\s*((?:\d+\.)?\d+),\s*((?:\d+\.)?\d+),\s*((?:\d+\.)?\d+),\s*((?:\d+\.)?\d+),\s*((?:\d+\.)?\d+)\)/ if (patt.test(transform)) { var matches = patt.exec(transform); return [ parseFloat(matches[1]), parseFloat(matches[4]) ]; } return [ 1, 1 ]; } function positionTexts (positions) { var sections = document.getElementsByTagName('section'), section, paragraph, paragraphs, anchor, rect, style, scale, centerDistanceX, centerDistanceY, scaleCorrectionX, scaleCorrectionY, offsetLeft, offsetTop, sectionRect; for (var i=0; i < sections.length; i++) { section = sections[i]; paragraphs = section.getElementsByTagName('p'); for (var p=0; p < paragraphs.length;p++) { paragraph = paragraphs[p]; sectionRect = section.getBoundingClientRect(); anchor = paragraph.getElementsByClassName('anchor')[0]; rect = anchor.getBoundingClientRect(); offsetLeft = positions[p][0] - ((rect.left + rect.width *.5) - sectionRect.left); offsetTop = positions[p][1] - ((rect.top + rect.height *.5) - sectionRect.top); paragraph.style.setProperty('left', (offsetLeft) + 'px'); paragraph.style.setProperty('top', (offsetTop) + 'px'); } } } function buildFrames () { var section, duration, frames, key, frames = [], frame, frameCount, referenceNode, paragraphs, p, frameFilters, filters, keyFrames = document.getElementsByTagName('section'), frameLength = .5; for (var i=0; i < keyFrames.length; i++) { key = keyFrames[i], duration = key.getAttribute('duration'), frameCount = Math.ceil(duration / frameLength), filters = { 'random': function (p, frameNum, frameCount) { return revealWords(p, Math.max(0, randInt(0,2)-1), p); }, 'reveal-o-l': function (p, frameNum, frameCount) { return revealOL(p, frameNum, frameCount); }, 'zoom': function (p, frameNum, frameCount) { return zoom(p, frameNum, frameCount); } } for (var f=0; f < frameCount; f++) { frame = key.cloneNode(true); frameFilters = (frame.getAttribute('data-filter') || '').split(' '); paragraphs = frame.getElementsByTagName('p'); for (var j=0; j < paragraphs.length; j++) { p = paragraphs[j]; for (key in filters) { if (frameFilters.indexOf(key) > -1) { frame.replaceChild(filters[key](p, f, frameCount), p); } } } frames.push(frame); key = frame.cloneNode(true); } } while (document.getElementsByTagName('section').length > 0) { section = document.getElementsByTagName('section')[0]; section.parentElement.removeChild(section); } for (var i=0; i < frames.length; i++) { document.getElementsByTagName('body')[0].appendChild(frames[i]); } } function createSpan (content) { var span = createTag('span', content); span.className = 'visible'; span.appendChild(text); return span; } function createTag (tagName, content) { var tag = document.createElement('span'); tag.appendChild(createTextNode(content)); return tag; } function createTextNode (content) { return document.createTextNode(content); } function reveal (textNode, regex) { var nodes = [], span, match, index = 0, text = textNode.textContent; while((match = regex.exec(text)) !== null) { nodes.push(createTextNode(text.substr(index, (match.index - index)))); var span = createTag('span', match[0]); span.className = 'visible'; nodes.push(span); index = match.index + match[0].length; } nodes.push(createTextNode(text.substr(index))); return nodes; } function revealOL (paragraph) { var newChildNodes, regex = /[lo]/g; newChildNodes = []; while (paragraph.hasChildNodes()) { var node = paragraph.firstChild; if (node.nodeType == Node.TEXT_NODE) { newChildNodes.push.apply(newChildNodes, reveal(node, regex)); } else { newChildNodes.push(node); } paragraph.removeChild(node); } for (var i=0; i < newChildNodes.length; i++) { paragraph.appendChild(newChildNodes[i]); } return paragraph } function zoom (paragraph, frameNum, frameCount) { if (! paragraph.classList.contains('big')) { var scale = 1 + ((frameNum + 1) / frameCount); paragraph.style.transform = 'scale(' + scale + ')'; } return paragraph; } function randInt (min, max) { return Math.round((Math.random() * max) + min); } /** * Returns a list of textNodes indexes which match * given query */ function eligibleTextNodes(nodes, patt) { var eligibleNodes = []; for (var i=0; i < nodes.length; i++) { if (nodes[i].nodeType == Node.TEXT_NODE && patt.test(nodes[i].textContent)) { eligibleNodes.push(i); } } return eligibleNodes; } /** * Reveals given words on node in a random fashion. * * Reveals them by wrapping them in a span with the class 'visible' */ function revealWords (node, amount) { var textNode, text, eligibleNodes, picked, revealed = 0, patt = /\S+/g, match, matches, head, body, tail, // Prepared nodes headNode, bodyNode, tailNode; // AttachedNodes eligibleNodes = eligibleTextNodes(node.childNodes, patt); while (revealed < amount && eligibleNodes.length > 0) { textNode = node.childNodes[eligibleNodes[randInt(0, eligibleNodes.length - 1)]], text = textNode.textContent, matches = []; while ((match = patt.exec(text)) !== null) { matches.push(match); } if (matches.length > 0) { picked = matches[randInt(0, matches.length - 1)]; head = text.substr(0, picked.index); body = picked[0]; tail = text.substr(picked.index + body.length, text.length); headNode = createTextNode(head); bodyNode = createTag('span', body); bodyNode.className = 'visible'; tailNode = createTextNode(tail); node.replaceChild(headNode, textNode); node.insertAfter(bodyNode, headNode); node.insertAfter(tailNode, bodyNode); revealed += 1; eligibleNodes = eligibleTextNodes(node.childNodes, patt); } } return node; } window.projection = { positionTexts: positionTexts, buildFrames: buildFrames, reveal: reveal, revealOL: revealOL }; })(window, document);