﻿// Function to recursively look for a classname in parentnodes. 
// Returns the noe if found. Returns NULL if it reaches the beforeClassname or body-tag.
function FindParentByClass(node, classname, beforeClassname) {
    // if you do not pass a beforeClassname we'll put an unlikly class name in that parameter for you.
    beforeClassname = beforeClassname == "" ? "quite unlikely name for a css class" : beforeClassname;
    var parent = node.parentNode;
    
    // debugging //if(!confirm(parent.tagName + ", " + parent.className +"\n\nContinue?")) return "TERMINATE";

    // loock for classname
    var re = new RegExp('\\b' + classname + '\\b');
    if(re.test(parent.className)) {
        return parent; // good boy!
    } else {
        if(parent.className == beforeClassname) return null;
        if(parent.tagName == "BODY") return null;
        // if not found, lets take a loock at grandpa..
        return FindParentByClass(parent, classname, beforeClassname);
    }
}


// The function that loops trough all tags with the fittext-text classname
function Fixall() {
    // Get all tags with the fittext-text classname
    var divsToFix = getElementsByClassName("fittext-text-node",document);
    
    // Fixing all the collected boxes, one by one.
    for(i=0;i<divsToFix.length;i++) {

        // The div to fix
        var contentNode = divsToFix[i]; 
        
        // The tag whith the height-spesifications (cannot cotinue without it)
        var outerNode = FindParentByClass(contentNode, "fittext-outer-node")
        if(outerNode == null) break;
        
        // The tag to compare with grandpa, if not found it is the content-node it self
        var innerNode = FindParentByClass(contentNode, "fittext-inner-node", "fittext-outer-node")
        if(innerNode == null) innerNode = contentNode;
        
        // First we check if the content is at all lager then the sized parent - cuz else there's just no point, is there..
        if(innerNode.offsetHeight > outerNode.offsetHeight) {
            // Remove all leading and trailing whitespace from the content div, it might throw the math off.
            contentNode.innerHTML = contentNode.innerHTML.replace(/^\s+|\s+$/g,""); 

            // We don't want to split the text inside tags! So we make a copy of the text one with no tags - easy peasy..
            var contentNoTags = contentNode.innerHTML.replace(/<[^>]*>/g, function(s){return s.replace(/./g,"_")});

            // For better performance we do some crude cuts first before we start looking for the exact fit
            // Lets split the block in half until it is undersized.
            var lastOversizedContent; // We'll want one "undo-level"
            while(innerNode.offsetHeight > outerNode.offsetHeight) {
                lastOversizedContent = contentNode.innerHTML;
                contentNode.innerHTML = contentNode.innerHTML.substr(0, contentNode.innerHTML.length/2);
            }
            
            // Now the block is too small goshdarnit! Let's take one step back and do the final trimming from there.
            contentNode.innerHTML = lastOversizedContent + "&nbsp;...";
            contentNoTags = contentNoTags.substring(0, lastOversizedContent.length);

            // Now here comes the final filleting.
            while(innerNode.offsetHeight > outerNode.offsetHeight) {
                // Slice off a word, and than slice up close to a word
                contentNoTags = String(contentNoTags.match(/.*[ ,.!?]/));
                contentNoTags = String(contentNoTags.match(/.*[^ ,.!?]/));
                
                // Lets not be stuck here if the box is simply to small and there is no good way to resolving the issue.
                if(contentNoTags == "null") {
                    contentNode.innerHTML = ""; // no room - no text!
                    break; // I QUIT!! (I'll be back for the next div though)
                }

                lastOversizedContent = lastOversizedContent.substr(0, contentNoTags.length);
                contentNode.innerHTML = lastOversizedContent + "&nbsp;...";
                
                // Safety feature; Give the user a chance to stop a loop of death at every 200 steps.. Mainly for debugging use.
                // if((this.step == undefined ? this.step = 1 : ++step) % 200 == 0 && confirm(step + " steps!\n\nBreak now?")) return;
                // if (!confirm("Continue?")) return; // Debug one step at a time
            }
        }
    }
}

// Function for getting elements by class name
function getElementsByClassName(classname, node)  {
    if(!node) node = document.getElementsByTagName("body")[0];
    var a = [];
    var re = new RegExp('\\b' + classname + '\\b');
    var els = node.getElementsByTagName("*");
    for(var i=0,j=els.length; i<j; i++)
        if(re.test(els[i].className))a.push(els[i]);
    return a;
}

// The function that does the adding without overwriting
function addLoadEvent(func) {
    var oldonload = window.onload;
    if (typeof window.onload != 'function') {
        window.onload = func;
    } else {
        window.onload = function() {
            if (oldonload) {
                oldonload();
            }
            func();
        }
    }
}

// Add the Fixall-function to the pageload event
addLoadEvent(Fixall); //Remember not to CALL the function but just name it (just "functionToRun" not "functionToRun()")
