adva-zakai.overbooked
clone your own copy | download snapshot

Snapshots | iceberg

Inside this repository

projection.js
application/javascript

Download raw (8.8 KB)

(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;
        
        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],
                anchor = paragraph.getElementsByClassName('anchor')[0],
                rect = anchor.getBoundingClientRect(),
                offsetLeft = positions[p][0] - anchor.offsetLeft,
                offsetTop = positions[p][1] - anchor.offsetTop,
                style = window.getComputedStyle(paragraph),
                scale = getScale(style.transform),
                centerDistanceX = (anchor.offsetLeft + ((rect.width * .5) / scale[0])) - parseInt(paragraph.clientWidth) / 2,
                scaleCorrectionX = Math.floor(centerDistanceX * (parseFloat(scale[0]) - 1)),
                offsetLeft = offsetLeft - scaleCorrectionX, 
                centerDistanceY = (anchor.offsetTop + ((rect.height * .5) / scale[1])) - parseInt(paragraph.clientHeight) / 2,
                scaleCorrectionY = Math.floor(centerDistanceY * (parseFloat(scale[1]) - 1)),
                offsetTop = offsetTop - scaleCorrectionY;
                    
                paragraph.style.setProperty('left', Math.floor(offsetLeft) + 'px');
                paragraph.style.setProperty('top', Math.floor(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);