//
// Copyright (c) Joseph Tate - 2010
// License: MIT
// jtate@dragonstrider.com
//
// A simple plugin that truncates text in a fixed height
// element using an optimal search algorithm.  A fixed height
// element will use a height attr or style and use some sort of
// overflow setting.
//
// For basic use, add a "vtruncated" class to the element whose
// contents you wish to truncate.  Otherwise, use it like
// jQuery(...).vtruncate().

;(function($) {
    $.fn.vtruncate = function(repl) {
        if (repl == undefined) {
            repl = '...';
        }
        this.each(
            function (index) {
                //Undo any previous runs by showing the original element
                //and removing any previously truncated siblings
                $(this).show().siblings('.vtruncated-done').remove();
                goal = this.offsetHeight;
                if (this.offsetHeight >= this.scrollHeight)
                    return; //we're already there

                //Hide the original and add the clone after it
                var nthis = $(this).clone().empty().addClass('vtruncated-done')[0];
                $(this).hide().after(nthis);

                function truncText(node) {
                    if (this.offsetHeight >= this.scrollHeight)
                        return; //we're already there
                    otext = node.nodeValue;
                    start = 0;
                    end = otext.length;
                    while (start < end)
                    {
                        mid = start + Math.max(Math.floor((end-start)/2), 1);
                        node.nodeValue = otext.slice(0, mid) + repl;
                        if (nthis.scrollHeight > goal)
                            end = mid - 1;
                        else
                            start = mid
                    }
                    node.nodeValue = otext.slice(0, start) + repl;
                }

                function inject_continuance(node) {
                    //add the indicator, hopefully it doesn't kick us over the edge
                    cont = $('<span>' + repl + '</span>');
                    var jnode = $(node);
                    jnode.append(cont);
                    while (nthis.scrollHeight > goal)
                    {
                        cont.remove();
                        //the last childNode
                        lastnode = $(jnode.find(':last-child'));
                        if (lastnode[0].nodeType == 3) {
                            return lastnode.text( lastnode.text().slice(0,0-repl.length) + repl);
                        }
                        else {
                            if (lastnode[0].childNodes.length == 0) {
                                lastnode.replaceWith(cont);
                            }
                            else {
                                return inject_continuance(lastnode[0]);
                            }
                        }
                    }
                }

                function truncNode(onode, node) {
                    function try_or_backout(p, item) {
                        if (nthis.scrollHeight > goal) {
                            //Just the addition of this element is too much
                            item.remove()
                            inject_continuance(p);
                            return true;
                        }
                    }
                    for (idx = 0; idx < onode.childNodes.length; idx = idx + 1) {
                        //Insert the new child with all contents
                        var inserted = $(onode.childNodes[idx]).clone();
                        $(node).append(inserted);
                        if (nthis.scrollHeight > goal) {
                            //The contents are too much, clear them and check again
                            if (inserted[0].nodeType!=3) {
                                inserted.empty();
                                if (try_or_backout(node, inserted))
                                    return;
                                else
                                    return truncNode(onode.childNodes[idx], inserted[0]);
                            }
                            else {
                                otxt = inserted[0].nodeValue;
                                //Try with only the repl text.
                                inserted[0].nodeValue = repl;
                                if (try_or_backout(node, inserted)) return;
                                else {
                                    inserted[0].nodeValue = otxt; //Back out the text change
                                    truncText(inserted[0]);
                                    return;
                                }
                            }
                        }
                    }
                }
                //Kick it all off
                truncNode(this, nthis);
            }
        );
    }

})(jQuery);

// Automatic handling of class="vtruncated" elements
jQuery(document).ready(function() { jQuery('.vtruncated').vtruncate() } );

/*
            function (index) {
                if (this.origHTML == undefined) {
                    this.origHTML = this.innerHTML;
                }
                var orig = this.origHTML;
                var start = 0;
                var end = orig.length;
                while (start < end)
                {
                    //Truncate in the middle of start/end
                    var newpos = start + Math.max(Math.floor((end-start)/2), 1);
                    this.innerHTML = orig.slice(0, newpos) + repl;
                    if (this.scrollHeight > this.offsetHeight)
                    {
                        end = newpos-1;
                    }
                    else
                    {
                        start = newpos; //We don't know if this is the optimum yet, so we don't increment
                    }
                }
                this.innerHTML = orig.slice(0, start) + repl;
            }
*/

