/**
 * Copyright 2006-2011 by Jeremy March.  All rights reserved.
 */

/* global variables */
var wts = new Array();
var cache = new Array();
var cacheLength = 0;

/* global settings */
var keyDelay = 350; //to prevent each key press from triggering a query for fast typers
var cacheLimit = 500;
var debug = false;
var mouseWheelSpeedFactor = 10; //between 4 and 12 seem to work best
var keyScrollAccel = 4;

var browser;
if (window.navigator.userAgent.toLowerCase().indexOf("ie") != -1)
    browser = "ie";
else if (window.navigator.userAgent.toLowerCase().indexOf("webkit/312") != -1) //Safari 1.3.2
    browser = "safari312";
else if (window.navigator.userAgent.toLowerCase().indexOf("webkit") != -1) 
    browser = "safari";
else if (window.navigator.userAgent.toLowerCase().indexOf("opera") != -1) 
    browser = "opera";
else 
    browser = "firefox";
    
var platform;
if (window.navigator.userAgent.indexOf("iPhone") != -1)
	platform = "iphone";
else if (window.navigator.userAgent.indexOf("iPad") != -1)
	platform = "ipad";
else if (window.navigator.platform.toLowerCase().indexOf("mac") != -1)
    platform = "mac";
else if (window.navigator.platform.toLowerCase().indexOf("linux") != -1)    
    platform = "linux";
else
    platform = "windows";

function lookupWT(idPrefix)
{
	for (var i = 0; i < wts.length; i++)
		if (wts[i][0] == idPrefix)
			return wts[i][1];

	return null;
}

function destroyWT()
{
    var p = this.div.parentNode;
    p.removeChild(this.div);
    var w = lookupWT(this.idPrefix);

    w = null;

    for (var i = 0; i < wts.length; i++)
    {
		if (wts[i][0] == this.idPrefix)
        {
			wts.splice(i,1);
            break;
        }
    }
    
	return null;
}

function onSelect(ev)
{
    var match = /([0-9]+)(.*)((Word)|(Root))/.exec(this.id);
    if (!match)
        return;
            
    var selected_id = match[1];
    var idPrefix = match[2];
    var word_or_root = match[3];
        
    var w = lookupWT(idPrefix);

    if (w.selectedRow)
    {
        w.selectedRow.style.border = "1px solid white";
        w.selectedRow.style.backgroundColor = "white";
        w.selectedRow.style.color = "black";
    }
    this.style.backgroundColor = "#8899EE";
    this.style.color = "white";
    this.style.border = "1px solid blue"; 
    
    w.hidden.value = selected_id;
    w.selectedRow = this;        
        
    if (typeof w.onClickActivate == "function")
    {
        w.onClickActivate(selected_id, w.lexicon, nodeGetDisplayValue(this), word_or_root);
    }        

    if (w.entry && w.autofocus)
    	w.entry.focus();
}

function getAssocArrayLength(obj) 
{
    var size = 0, key;
    for (key in obj) 
    {
        if (obj.hasOwnProperty(key)) 
            size++;
    }
    return size;
}

function procResponse(str)
{
    var returnObj;

    try
    {
        if (typeof JSON != "undefined")
        {
            returnObj = JSON.parse(str);
        }
        else
        {
            returnObj = eval("(" + str + ")");
        }
    }
    catch(e) { if (debug) alert(e.message + "\n" + str); return; };
    
    if (!returnObj)
        return;
    
    var wt = lookupWT(returnObj.wtprefix);
    if (!wt)
        return;

    wt.blockScroll = true;

    //if caching is activated, add result to cache
    if (wt.useCache)
        wtAddResultToCache(wt, returnObj.query, str);

    if (returnObj.error)
    {
        wt.con.innerHTML = returnObj.error;
        wt.blockScroll = false;
        return;
    }
    
    //save original height here, for use if we're paging up.  See below.
    var saveHeight = wt.con.scrollHeight;

    wt.loading.style.display = "none";
    
    //block result sets which come out of sequence
    if (wt.lastRequestTime > parseInt(returnObj.requestTime))
    {
        if (debug)
            alert("out of seq!");
            
        wt.blockScroll = false;    
        return;
    }
    else
    {
        //set lastRequestTime to that of the last result set received
        wt.lastRequestTime = parseInt(returnObj.requestTime);
    }
    
    var returnedPage = parseInt(returnObj.page);
    
    //block pages which are repeats or out of order
    if (returnedPage < 0 && returnedPage >= wt.pageUp)
    {
        if (debug)
            alert("wt.pageUp: " + wt.pageUp + "; returnObj.page: " + returnObj.page);
            
        wt.blockScroll = false;
        return;
    }
    else if (returnedPage > 0 && returnedPage <= wt.pageUp)
    {
        if (debug)
            alert("wt.pageDown: " + wt.page + "; returnObj.pageDown: " + returnObj.page);
            
        wt.blockScroll = false;
        return;
    }   

	var con = document.getElementById(returnObj.container);

    arrOptions = returnObj.arrOptions;
    
    if (returnObj.lastPage == 1)
        wt.lastPage = true;
    else
        wt.lastPage = false; //need to remove this
        
    if (returnObj.lastPageUp == 1)
        wt.lastPageUp = true;
    else
        wt.lastPageUp = false; //need to remove this
        
    if (returnedPage < 0)
    {
        wt.pageUp = returnedPage;
        wt.prevPageRequestPending = false;
    }
    else if (returnedPage == 0)
    {
        //reset these if we just refreshed a page 0
        wt.page = 0;
        wt.pageUp = 0;
    }    
    else
    {
        wt.page = returnedPage;
        wt.nextPageRequestPending = false;
    }

	//delete old rows if this is page 0 and this isn't a tree
    if (returnedPage == 0 && !returnObj.treeOptions)
    {
        var a = con.firstChild;
        while (a)
        {
            var b = a.nextSibling;
            con.removeChild(a);
            a = b;
        }
        wt.selectedRow = null;
        wt.hidden.value = "";
    }

    if (returnObj.treeOptions && returnObj.treeOptions.length > 0)
    {
    /*
    this is if there isn't already a list of roots/ i.e. when you want one root per page
		var node = document.createElement('div');

        node.style.paddingLeft = "1px";
        node.style.paddingTop = "1px";
        node.style.paddingBottom = "1px";

        node.style.margin = "0px";
        node.style.border = "1px solid white";
		node.setAttribute('class','treerow');
		node.id = 0;
        
		var text = document.createTextNode(returnObj.root);
		
        //node.onclick = onSelect; 
        node.appendChild(text);
		con.appendChild(node);    
    */
    
        dotree(returnObj.treeOptions, con, returnObj.wtprefix);
        
        if (returnObj.selectId)
        {
            var node = document.getElementById(returnObj.selectId);
            if (node)
            {
                node.style.backgroundColor = "#8899EE";
                node.style.color = "white";
                node.style.border = "1px solid blue";
                wt.selectedRow = node;
                
                var match = /([0-9]+)(.*)((Word)|(Root))/.exec(node.id);
                if (match)
                    wt.hidden.value = match[1];
            }
        }
        if (wt.entry && wt.autofocus)
        	wt.entry.focus();
        wt.blockScroll = false;
        return;
    }

    var len = arrOptions.length;
    var before = wt.con.firstChild;
    
    if (debug)
    {
        var node = document.createElement('div');
        var text = document.createTextNode("Start Page " + returnObj.page);
        node.appendChild(text);
        node.style.border = "1px solid white";
        node.id = "10blahWord";
        node.setAttribute('class','treerow');
        node.style.whiteSpace = "nowrap";
        node.style.color = "red";
        node.style.fontWeight = "bold";
        if (returnObj.page < 0 && before)
            con.insertBefore(node, before);
        else
            con.appendChild(node);
    }
    
	for (var i = 0; i < len; i++)
	{
		var node = document.createElement('div');
        
        node.style.padding = "1px";
        
        if (returnObj.roots == 1)
        {
            if (arrOptions[i][2])//temp
            {
                var img = document.createElement('img');
                img.src = "images/plus.png";
                img.style.display = "inline";
                img.style.paddingRight = "4px";
                img.style.cursor = "pointer";
                img.id = arrOptions[i][0] + wt.idPrefix + "Img";
                img.onclick = onClickTopLevelNode;
                node.appendChild(img);
            }
            else
            {
                node.style.paddingLeft = "14px";
            }
        }
        else
        {
            node.style.margin = "0px";
        }
        //node.style.paddingLeft = "4px"; //need to consider how this looks more
        //node.style.height = "15px";
        node.style.border = "1px solid white";
		node.setAttribute('class','treerow');
        node.style.whiteSpace = "nowrap";
        
		node.id = arrOptions[i][0] + wt.idPrefix + (returnObj.roots ? "Root" : "Word");
        
        if (arrOptions[i][2] && returnObj.roots != 1)//temp
        {
            node.setAttribute("seq", arrOptions[i][2]);
        }

		var text = document.createTextNode(arrOptions[i][1]);

		node.appendChild(text);
        if (returnedPage < 0 && before)
            con.insertBefore(node, before);
        else
            con.appendChild(node);
        node.onclick = onSelect;
	}
    
    if (wt && returnObj.scroll == "top")
    {
        wt.con.scrollTop = "0px";
    }
    else if (wt && returnObj.scroll == "bottom")
    {
        wt.con.scrollTop = wt.con.scrollHeight;
    }
    else if (wt && returnObj.selectId && returnedPage == 0)
    {
        //select middle word and scroll there
        var worr = (returnObj.roots == 1) ? "Root" : "Word";

        var s = document.getElementById(returnObj.selectId + wt.idPrefix + worr);
        if (s)
        {
            if (wt.selectedRow)
            {
                wt.selectedRow.style.backgroundColor = "white";
                wt.selectedRow.style.color = "black";
                wt.selectedRow.style.border = "1px solid white";
            }
            s.style.backgroundColor = "#8899EE";
            s.style.color = "white";
            s.style.border = "1px solid blue";
            wt.selectedRow = s;
            //scroll to middle
            wt.con.scrollTop = s.offsetTop - (wt.con.offsetHeight / 2) + 60;
        }
    }
    
    if (debug)
    {
        var node = document.createElement('div');
        var text = document.createTextNode("End Page " + returnObj.page);
        node.appendChild(text);
        node.id = "10blahWord";
        node.style.border = "1px solid white";
        node.setAttribute('class','treerow');
        node.style.whiteSpace = "nowrap";
        node.style.color = "red";
        node.style.fontWeight = "bold";
        if (returnedPage < 0 && before)
            con.insertBefore(node, before);
        else
            con.appendChild(node);
    }

    if (returnedPage < 0)
    {
        //to keep scrollTop in same place as before when paging up.
        wt.con.scrollTop += (wt.con.scrollHeight - saveHeight);
    }
    
    wt.blockScroll = false; //fixes bug in webkit where next page was requested in middle of this function

    //request def
    if (wt.selectedRow && returnedPage == 0 && typeof wt.onSelectionChanged == "function")
        wt.onSelectionChanged(parseInt(wt.selectedRow.id), wt.lexicon, "blah", "Word");//getDef(parseInt(wt.selectedRow.id), wt.lexicon, "");
}

function printTree(tree, con, treeArray, level, wtprefix)
{
    var node = document.createElement('div');
    if (level == 0)
        node.style.paddingLeft = "1px";
    else if (tree.length < 3)
        node.style.paddingLeft = ((level * 25) + 12) + "px";
    else
        node.style.paddingLeft = level * 25 + "px";
        
    node.style.paddingTop = "1px";
    node.style.paddingBottom = "1px";

    node.style.margin = "0px";
    node.style.border = "1px solid white";
    node.setAttribute('class','treerow');
    node.id = tree[0] + wtprefix + "Word";
    node.style.whiteSpace = "nowrap";
        
    if (tree[2]) //if has children
    {
        var img = document.createElement('img');
        img.src = "images/plus.png";
        img.style.display = "inline";
        img.style.paddingRight = "4px";
        img.style.cursor = "pointer";
        img.id = tree[0] + wtprefix + "Img";
        img.onclick = openCloseCon;
        node.appendChild(img);
    }
    var text = document.createTextNode(tree[1]);
	
    node.onclick = onSelect;    
    node.appendChild(text);
    con.appendChild(node);
        
    if (tree[2] && tree[2][0] && tree[2][0].length > 1)
    {
        var node2 = document.createElement('div');
        node2.id = tree[0] + wtprefix + "Children";
        node2.style.display = "none";
        con.appendChild(node2);
            
        for (var i = 0; i < tree[2].length; i++)
        {
            printTree(tree[2][i], node2, tree, level + 1, wtprefix);
        }
    }
}

function onClickTopLevelNode(ev)
{
    /*
    wtprefix
    rowscon = wtprefix + "Container"
    rowid = word_id + wtprefix + "Word" || "Root"
    imageid = word/root_id + wtprefix + "Img"
    rowchidren = word/rootid + wtprefix + "Children"
    */
	if (!ev) var ev = window.event;
	ev.cancelBubble = true;
	if (ev.stopPropagation) ev.stopPropagation();
    
    //"this" is a reference to the img node
    var wtmatch = /([0-9]+)(.+)Img/.exec(this.id);
    var rootid = wtmatch[1];
    var wtprefix = wtmatch[2];
    var wt = lookupWT(wtprefix);
    if (wt)
        toggleNode(wt, rootid);
}

function openCloseCon(ev)
{
	if (!ev) var ev = window.event;
	ev.cancelBubble = true;
	if (ev.stopPropagation) ev.stopPropagation();
    
    //"this" is a reference to the img node
    var match = /([0-9]+)(.+)Img/.exec(this.id);
    if (!match)
        return;
        
    var wtprefix = match[2];
    var wordid = match[1];
    var wt = lookupWT(wtprefix);
    if (wt)
        toggleNode(wt, wordid);
}

function toggleNode(wt, rootid)
{
    var childrenConId = rootid + wt.idPrefix + "Children";
    var imgId = rootid + wt.idPrefix + "Img";
    var n = document.getElementById(childrenConId);
    var imgNode = document.getElementById(imgId);

    if (!imgNode) //its ok for n to be null
        return;

    if (imgNode.src.indexOf("images/plus.png") != -1)
    {
        imgNode.src = "images/minus.png";
        
        if (n)
        {
            n.style.display = "block";
        }
        else
        {
            var requestTime = new Date().getTime();
            var childrenCon = document.createElement("div");
        
            childrenCon.id = childrenConId;
            
            if (wt.selectedRow)
                var selected_id = '&selectedid=' + wt.selectedRow.id;
            else
                var selected_id = "";
        
            if (imgNode.parentNode.nextSibling)
                wt.con.insertBefore(childrenCon, imgNode.parentNode.nextSibling);
            else
                wt.con.appendChild(childrenCon); 
                
            var url = wt.url + '?n=' + (wt.maxWords + 1) + "&idprefix=" + wt.idPrefix + '&lexicon=' + wt.lexicon + '&x=' + Math.random() + '&requestTime=' + requestTime + '&page=' + wt.page + '&con=' + childrenConId + '&rootid=' + rootid + selected_id + '&mode=' + wt.mode;

            loadXMLDocCOM(url);
        }
    }
    else
    {
        if (n) 
            n.style.display = "none";
        imgNode.src = "images/plus.png";
    }
    if (wt.entry && wt.autofocus)
    	wt.entry.focus();
}

function dotree(tree, con, wtprefix)
{
    for (var i = 0; i < tree.length; i++)
    {
        printTree(tree[i], con, tree, 1, wtprefix);
    }
}

function loadXMLDocCOM(url) 
{
    new Ajax.Request(url,
    {
        method:'get',
        onSuccess: function(transport) 
        {
            var response = transport.responseText;// || "no response text";
            procResponse(response);
        },
        onFailure: function() { if (debug) alert('Something went wrong...'); }
    });
}

function stepDown(n)
{
    //now with hierarchies the upper node could the start of a Child container or we could be leaving a child container
    var p = n.parentNode;
    n = n.nextSibling;

    if (n && n.id.indexOf("Children") != -1)
    {
        n = n.firstChild;
    }
            
    if (!n)
    {
        while (p && p.id.indexOf("Children") != -1)
        {
            if (p.nextSibling)
            {
                n = p.nextSibling;
                break;
            }
            p = p.parentNode;
        }
    }
    return n;
}

function stepUp(n1)
{
    var n = n1.previousSibling;
    while (n && n.id.indexOf("Children") != -1)
    {
        n = n.lastChild;
    }
            
    if (!n && n1.parentNode.id.indexOf("Container") == -1)
    {
        n = n1.parentNode.previousSibling;
    }
    return n;
}

function move(upDown, wt, step)
{
	var con = wt.con;
	var n;
    var lastGoodn = null;

	if (!wt.selectedRow)
	{
		n = con.firstChild;
	}
	else if (upDown > 0) //up
	{
		if (wt.selectedRow != con.firstChild)
        {
            n = wt.selectedRow;
            do
            {
                n = stepUp(n);
            } while (n && n.parentNode.style.display == "none");
            
            for (var i = 1; n && i < step; i++)
            {
                lastGoodn = n;
                n = stepUp(n);
            }
            
            if (!n)
                n = lastGoodn;
        }
	}
	else //down
	{
		if (wt.selectedRow != con.lastChild)
        {
            n = wt.selectedRow;
            do 
            {
                n = stepDown(n);
            } while (n && n.parentNode.style.display == "none");
            
            for (var i = 1; n && i < step; i++)
            {
                lastGoodn = n;
                n = stepDown(n);
            }
            
            if (!n)
                n = lastGoodn;
        }
	}
    
	if (!n)
    {
        return;
    }
    
	if (wt.selectedRow)
	{
		wt.selectedRow.style.backgroundColor='white';
		wt.selectedRow.style.color='black';
        wt.selectedRow.style.border = "1px solid white";
	}

    n.style.border = "1px solid blue";
	n.style.backgroundColor='#8899EE';
	n.style.color='white';
    
    //scroll as you go down
    if (n.offsetTop > wt.con.scrollTop + wt.con.offsetHeight - 30)
    {
        wt.con.scrollTop = n.offsetTop - wt.con.offsetHeight + 40;
    }            
    //scroll as you go up
    if (n.offsetTop < wt.con.scrollTop + 15)
    {
        wt.con.scrollTop = n.offsetTop - 18;                       
    }    
    
    var match = /([0-9]+).*((Word)|(Root))/.exec(n.id);
    if (!match)
        return;
    var selectedid = match[1];
    var word_or_root = match[2];

    wt.hidden.value = selectedid;
	wt.selectedRow = n;
}

function wordtree_ondown_firefox_mac(ev)
{
    if (!ev)
        ev = window.event;
        
    var key = ev.keyCode;
    
    var match = /(.*)Entry/.exec(this.id);
    if (!match)
        return;
    var idPrefix = match[1];
    
    var wt = lookupWT(idPrefix);
    if (!wt)
        return;
    
    if (key == 38)
    {
        move(1, wt, wt.step);
        //accelerate
        if (!wt.downkey)
        {
            wt.accelTimeout = setTimeout("var a = lookupWT('" + wt.idPrefix + "'); a.step = 2; a.accelTimeout = setTimeout(\"lookupWT('test1').step = keyScrollAccel\", 2000)", 2000);
        }
    }
    else if (key == 40)
    {
        move(-1, wt, wt.step);
        //accelerate
        if (!wt.downkey)
        {
            wt.accelTimeout = setTimeout("var a = lookupWT('" + wt.idPrefix + "'); a.step = 2; a.accelTimeout = setTimeout(\"lookupWT('test1').step = keyScrollAccel\", 2000)", 2000);
        }
    }
    wt.downkey = true;
}

function wordtree_ondown(ev)
{
    if (!ev)
        ev = window.event;
        
    var key = ev.keyCode;
    
    if (key == 17)
        return;
    
    var match = /(.*)Entry/.exec(this.id);
    if (!match)
        return;
    var idPrefix = match[1];
    
    var wt = lookupWT(idPrefix);
    if (!wt)
        return;
        
    if ((browser != "firefox" && browser != "opera") || (platform == "windows" && browser != "opera"))
    {         
        if (key == 38)
        {
            move(1, wt, wt.step);
            //accelerate
            if (!wt.downkey)
                wt.accelTimeout = setTimeout("var a = lookupWT('" + wt.idPrefix + "'); a.step = 2; a.accelTimeout = setTimeout(\"lookupWT('test1').step = keyScrollAccel\", 2000)", 2000);
        }
        else if (key == 40)
        {
            move(-1, wt, wt.step);
            //accelerate
            if (!wt.downkey)
                wt.accelTimeout = setTimeout("var a = lookupWT('" + wt.idPrefix + "'); a.step = 2; a.accelTimeout = setTimeout(\"lookupWT('test1').step = keyScrollAccel\", 2000)", 2000);
        }
    }
        
    if (!wt.downkey)
    {
        //put non-repeating downkey stuff here
        if (ev.ctrlKey)
        {
            if (key == 65) //a
            {
                if (typeof wt.onAddWord == "function" && wt.hidden.value != "" && wt.selectedRow)
                {
                    wt.onAddWord(wt.hidden.value);
                }
            }
            if (key == 68) //d
            {
                if (typeof wt.onDeleteWord == "function" && wt.hidden.value != "" && wt.selectedRow)
                {
                    wt.onDeleteWord(wt.hidden.value, wt.selectedRow.getAttribute("seq"), wt.tag_id);
                }
            }
            if (key == 69) //e
            {
                if (wt.hidden.value)
                    toggleNode(wt, wt.hidden.value);
            }                
            //block default hot keys, like bookmark, etc
            ev.returnValue = false;
            if (typeof ev.preventDefault == "function")
                ev.preventDefault();
        }
    
        switch (key)
        {
            case 17: //control
                ev.returnValue = false;
                if (typeof ev.preventDefault == "function")
                    ev.preventDefault();
                return false;
                break;
            case 13: //enter
                if (typeof wt.onEnterActivate == "function" && wt.selectedRow)
                {
                    var match = /([0-9]+).*((Word)|(Root))/.exec(wt.selectedRow.id);
                    if (!match)
                        return;
                    var selectedid = match[1];
                    var word_or_root = match[2];     
                           
                    wt.onEnterActivate(wt.hidden.value, wt.lexicon, nodeGetDisplayValue(wt.selectedRow), word_or_root);
                }        
                break;
            case 27: //esc
                
                wt.entry.value = "";
                wt.page = 0;
                wt.selectedRow = null;
                wt.hidden.value = 0;            
                
                //block fast typers from making requests for every keystroke
                if (wt.lastKeyTimeout)
                    clearTimeout(wt.lastKeyTimeout);
                wt.lastKeyTimeout = setTimeout("var a = lookupWT('" + wt.idPrefix + "'); a.refresh(); if (a.entry && a.autofocus) a.entry.focus()", keyDelay); 
                      
                break;
            default:
                break;
        }
    }
    if (browser != "firefox" || platform == "windows")
        wt.downkey = true;
}
 
function wordtree_onup(ev)
{
    if (!ev)
        ev = window.event;
        
    var key = ev.keyCode;
    
    var match = /(.*)Entry/.exec(this.id);
    if (!match)
        return;
    var idPrefix = match[1];
    
    var wt = lookupWT(idPrefix);
    if (!wt)
        return;
        
    clearTimeout(wt.accelTimeout);
    wt.accelTimeout = null;
    wt.step = 1;
    wt.downkey = false;
/*
    //temp for testing paging without actually paging
    if (key == 78)
    {
        wt.prevPageRequestPending = true;
        wt.requestPrevPage();
    }
*/  
    if (key == 40 || key == 38)
    {
        if (typeof wt.onSelectionChanged == "function" && wt.selectedRow)
        {
            var match = /([0-9]+).*((Word)|(Root))/.exec(wt.selectedRow.id);
            if (!match)
                return;
            var selectedid = match[1];
            var word_or_root = match[2];
            wt.onSelectionChanged(wt.hidden.value, wt.lexicon, nodeGetDisplayValue(wt.selectedRow), word_or_root);
        }
    }
    else if (!ev.ctrlKey && (((key >= 48 && key <= 90) || key == 8 || key == 46) || key == 0))
    {
        //block fast typers from making requests for every keystroke
        wt.page = 0;
        wt.selectedRow = null;
        wt.hidden.value = 0;
        
        if (wt.lastKeyTimeout)
            clearTimeout(wt.lastKeyTimeout);
        wt.lastKeyTimeout = setTimeout("lookupWT('" + wt.idPrefix + "').refresh();", keyDelay);
    }
}

function nodeGetDisplayValue(node)
{
    //hack for now
    if (node.firstChild.nodeType != "3")
        return node.firstChild.nextSibling.nodeValue;
    else
        return node.firstChild.nodeValue;
}
 
function dontsubmit()
{
	return false;
}

function conOnScroll(e)
{
    //increase this number to fetch next page earlier (for slower connections if there is a lag when you hit the end of the last page), 
    //decrease if it's being fetched to soon
    var whenToGetNextPage = 400; 
    
    //this = the container element
    var match = /(.*)Container/.exec(this.id);
    if (!match)
        return;

    var wt = lookupWT(match[1]);
    if (!wt)
        return;
    //don't request another page if we're on the last page OR if another nextPageRequest is pending.
    //don't increase page until it is received and appropriate
    if (wt.con.scrollTop > wt.con.scrollHeight - wt.con.offsetHeight - whenToGetNextPage && !wt.nextPageRequestPending && !wt.lastPage && !wt.blockScroll)
    {
        wt.nextPageRequestPending = true;
        wt.requestNextPage();
    }
    else if (wt.mode == "context" && wt.con.scrollTop < whenToGetNextPage && !wt.prevPageRequestPending && !wt.lastPageUp && !wt.blockScroll)
    {
        wt.prevPageRequestPending = true;
        wt.requestPrevPage();
    }
}

function cancelEvent(e)
{
  e = e ? e : window.event;
  if(e.stopPropagation)
    e.stopPropagation();
  if(e.preventDefault)
    e.preventDefault();
  e.cancelBubble = true;
  e.cancel = true;
  e.returnValue = false;
  return false;
}

function more(am)
{
    var wt = lookupWT("test1");
    wt.con.scrollTop += (am / 2);
}

function fadeIn(am)
{
    var duration = 200;     /* 1000 millisecond fade = 1 sec */
    var steps = 15;         /* number of opacity intervals   */

    for (var i = 0; i <= 1; i += (1 / steps)) 
    {
        setTimeout("more(" + (am / am * 10)  + ")", i * duration);
    }
}

function onMouseWheel(e)
{
    // v. http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel

    var e = e ? e : window.event;
    
    //hack!
    if (browser == "ie")
    {
        //var wt = lookupWT("test1");
        //var el = wt.con;
        var el = e.srcElement.parentNode;
    }
    else
    {
        var el = this;
    }
    var amount = e.detail ? e.detail * mouseWheelSpeedFactor : e.wheelDelta / 40 * mouseWheelSpeedFactor * -1;
        
    el.scrollTop += amount;
    
    //fadeIn(amount);
    //setTimeout("more(" + amount + ")", 50);
    
    return cancelEvent(e);
}

function wordtree (idPrefix, lexicon, width, height)
{
    this.mode = "context";
	this.width = width;
	this.bgcolor = "#ffffff";
	this.margin = "10px";
	this.selectedRow = null;
    this.accelTimeout = null;
    this.lastRequestTime = null;
    this.lastKeyTimeout = null;

	this.maxWords = 100;
	this.url = "wtgreekserv.php";
	this.idPrefix = idPrefix;
	this.lexicon = lexicon;
    this.tag_id = 0;
    this.root_id = 0;
    this.step = 1;
    
    this.pageUp = 0; //for scrolling up
    this.page = 0; //for scrolling down
    this.nextPageRequestPending = false;
    this.prevPageRequestPending = false;
    this.lastPage = false;
    this.lastPageUp = false;
    this.blockScroll = false;  //fixes a bug in webkit where next page was requested while loading 0 page

	var d = document.createElement("div");
	this.div = d;
	
    //whether to automatically focus the entry when wt "has focus", disable for iphones, ipads, etc.
    if (platform == "ipad" || platform == "iphone")
    	this.autofocus = false;
    else
		this.autofocus = true;	
    
	d.style.margin = this.margin;
    d.style.textAlign = "left";
    //d.style.zIndex = "100";
    d.style.position = "relative";

    d.style.width = this.width + 10 + "px";
	d.style.backgroundColor = this.bgcolor;
    d.className = "t";
    
    var bottom = document.createElement("div");
    bottom.className = "b";
    bottom.style.width = this.width + 10 + "px";
    d.appendChild(bottom);

    var left = document.createElement("div");
    left.className = "l";
    bottom.appendChild(left);
    
    var right = document.createElement("div");
    right.className = "r";
    right.style.width = this.width + 10 + "px";
    left.appendChild(right);
    
    var botleft = document.createElement("div");
    botleft.className = "bl";
    right.appendChild(botleft);
    
    var botright = document.createElement("div");
    botright.className = "br";
    botleft.appendChild(botright);                            

    var topleft = document.createElement("div");
    topleft.className = "tl";
    botright.appendChild(topleft);
    
    var topright = document.createElement("div");
    topright.className = "tr";
    topleft.appendChild(topright); 
    
    this.topright = topright;
        
    var ti = document.createElement("div");
    ti.style.position = "absolute";
    ti.style.top = "12px";
    ti.style.left = "6px";
    ti.style.width = this.width - 3 + "px";
    ti.innerHTML = "Title";
    this.title = ti;
        
    topright.appendChild(ti);
        
	var form = document.createElement("form");
	form.style.display = "inline";
    form.onsubmit = dontsubmit;
	topright.appendChild(form);

	var input = document.createElement("input");
	input.style.width = this.width - 46 + "px";
    input.style.position = "absolute";
    input.style.top = "40px";
    input.style.left = "14px";
	input.setAttribute("autocomplete", "off");
    input.id = idPrefix + "Entry";
    this.entry = input;
    
    var loading = document.createElement("img");
    loading.id = this.idPrefix + "Loading";
    loading.src = "images/loading.gif";
    loading.style.position = "absolute";
    loading.style.top = "44px";
    loading.style.right = "16px";
    loading.style.display = "none";
    loading.style.height = "15px";
    loading.style.width = "15px";
    this.loading = loading;
    
    //make another case here for safari 1.3.2 where we block it from being called twice somehow
    if ((browser == "firefox" && (platform == "mac" || platform == "linux")) || browser == "opera")
    {
        input.onkeypress = wordtree_ondown_firefox_mac;
    }
    
    input.onkeydown = wordtree_ondown;
    
    input.onkeyup = wordtree_onup;

	form.appendChild(input);
    form.appendChild(loading);

	var hidden = document.createElement("input");
    hidden.type = "hidden";
    hidden.id = idPrefix + "hidden";
    this.hidden = hidden;
	form.appendChild(hidden);

	var con = document.createElement("div");
	con.id = idPrefix + "Container";

    con.style.position = "relative";
    con.style.top = "60px";
    con.style.left = "4px"; 
	con.style.width = this.width - 20 + "px";

    con.style.overflow = "hidden"; //for Safari 1.3.2
    con.style.overflowY = "hidden";
    con.style.overflowX = "hidden";
    
    if (con.addEventListener)
    {
        con.addEventListener('DOMMouseScroll', onMouseWheel, false);  
        con.addEventListener("mousewheel", onMouseWheel, false);
    }
    else if (con.attachEvent)
        con.attachEvent("onmousewheel", onMouseWheel);
        
	con.style.paddingTop = "2px";
    con.style.fontSize = "10pt";
    
    con.onscroll = conOnScroll;
    var wt = this;
    d.onclick = function () { if (wt.entry && wt.autofocus) wt.entry.focus() };
    //d.oncontextmenu = function () { if (typeof wt.onContextMenu == "function") wt.onContextMenu(); return false; };
	topright.appendChild(con);

	this.con = con;

    wts.push([idPrefix, this]);

	this.show = wordtree_show;
    this.close = destroyWT;    
    this.setHeight = setHeight;
    this.refresh = refreshWordTree;
    this.requestNextPage = requestNextPage;
    this.requestPrevPage = requestPrevPage;
    this.checkCache = checkCache;
    this.useCache = true;
    
	this.onEnterActivate = null;
    this.onClickActivate = null;
    this.onSelectionChanged = null;    
    this.onAddWord = null;
    this.onDeleteWord = null;
    this.onContextMenu = context;

    this.setHeight(height);
}

function context()
{
    alert("context menu");
}

function setHeight(height)
{
    this.height = height;
    this.topright.style.height = height - 40 + "px";
    this.con.style.height = (height - 110) + "px";
}

function wordtree_show(parent)
{
    parent.appendChild(this.div);
}

function requestNextPage()
{
    var requestTime = new Date().getTime();
    this.loading.style.display = "block";
    
	var input = this.entry.value;
	// encodeURI... only required for IE--Mozilla did it automatically
	var url = this.url + '?w=' + encodeURIComponent(input) + '&n=' + (this.maxWords + 1) + "&idprefix=" + this.idPrefix + '&lexicon=' + this.lexicon + '&tagid=' + this.tag_id + '&x=' + Math.random() + '&requestTime=' + requestTime + '&page=' + (parseInt(this.page) + 1) + '&rootid=' + this.root_id + '&mode=' + this.mode;
    
	loadXMLDocCOM(url);
}

function requestPrevPage()
{ 
    var requestTime = new Date().getTime();
    this.loading.style.display = "block";
    
	var input = this.entry.value;
	// encodeURI... only required for IE--Mozilla did it automatically
	var url = this.url + '?w=' + encodeURIComponent(input) + '&n=' + (this.maxWords + 1) + "&idprefix=" + this.idPrefix + '&lexicon=' + this.lexicon + '&tagid=' + this.tag_id + '&x=' + Math.random() + '&requestTime=' + requestTime + '&page=' + (parseInt(this.pageUp) - 1) + '&rootid=' + this.root_id + '&mode=' + this.mode;
    
	loadXMLDocCOM(url);
}

function refreshWordTree()
{
    var requestTime = new Date().getTime();
    this.loading.style.display = "block";
    
    if (this.checkCache())
        return;
    
	var input = this.entry.value;
	// encodeURI... only required for IE--Mozilla did it automatically
	var url = this.url + '?w=' + encodeURIComponent(input) + '&n=' + (this.maxWords + 1) + "&idprefix=" + this.idPrefix + '&lexicon=' + this.lexicon + '&tagid=' + this.tag_id + '&x=' + Math.random() + '&requestTime=' + requestTime + '&page=' + 0 + '&rootid=' + this.root_id + '&mode=' + this.mode;

	loadXMLDocCOM(url);
}

function checkCache()
{
    if (this.entry.value == "")
        var queryKey = this.lexicon + "a"; //because wtgreekserv changes "" to "a"
    else
        var queryKey = this.lexicon + this.entry.value;
        
    if (this.useCache && cache && cache[queryKey])
    {
        this.lastRequestTime = 0; //defeat sequence check
        procResponse(cache[queryKey].str);
        //procResponse("");
        return true;
    }
    else
    {
        return false; //not cached, request it
    }
}

function wtAddResultToCache(wt, queryKey, str)
{
    //the queryKey is the lexicon + the query word
    //wtgreekserv changes an empty query to be "a", so an empty query has a key of "a" not ""
    queryKey = wt.lexicon + queryKey;
   
    //if this query isn't in the cache
    if (!cache[queryKey])
    {
        //if we're at the cacheLimit remove the oldest item 
        //(shorter keys are more useful results to have so maybe we should give them added importance?)
        //use cacheLength because assoc arrays have no length property and we don't want to have to count them each time
        if (cacheLimit && cacheLength >= cacheLimit)
        {
            var prev = null;
            for (var x in cache)
            {
                if (!cache.hasOwnProperty(x))
                    continue;
                    
                if (prev == null || cache[x].time < cache[prev].time)
                    prev = x;
            }
            if (prev)
            {
                cacheLength--;
                delete cache[prev];
            }
        }
        cacheLength++;
        cache[queryKey] = new Array();
        cache[queryKey].str = str;
        cache[queryKey].time = new Date().getTime();
    }
    else
    {
        //if it is in the cache, update the timestamp
        cache[queryKey].time = new Date().getTime();
    }
}

