﻿var suggestBoxes = [];
var currentSuggest;
var checkBoxCollections = [];
var MAX_KOMBAT_DOMAINS_SUBSCRIBER = 3;
var MAX_KOMBAT_DOMAINS_NON_SUBSCRIBER = 2;
var ORGANIC_COMPETITOR_TYPE_NAME = 'Organic';
var AD_COMPETITOR_TYPE_NAME = 'Ad';

var loggedIn;

try
{
    document.domain = 'spyfu.com';
    //document.domain = 'localhost';
}
catch (e) {}

var countryCode = '';
if ( document.URL.indexOf('/uk/', 0) >= 0 )
{
    countryCode = 'UK'
}


document.onclick = suggestHideAll;

function getTarget(e)
{
    return (e && e.target) || (event && event.srcElement);
}

function getInnerText(obj)
{
    if(obj.innerText != undefined)
    {
        return obj.innerText;
    } 
    else
    {
        return obj.textContent;
    }
}

function trim(stringToTrim) {
	return stringToTrim.replace(/^\s+|\s+$/g,"");
}
function ltrim(stringToTrim) {
	return stringToTrim.replace(/^\s+/,"");
}
function rtrim(stringToTrim) {
	return stringToTrim.replace(/\s+$/,"");
}

//find x of top left of obj
function findPosX(obj)
{
     var curleft = 0;
     if (obj.offsetParent)
     {
          while (obj.offsetParent)
          {
               curleft += obj.offsetLeft
               obj = obj.offsetParent;
          }
     }
     else if (obj.x)
          curleft += obj.x;
     return curleft;
}

//find y of the top left of obj
function findPosY(obj)
{
     var curtop = 0;
     if (obj.offsetParent)
     {
          while (obj.offsetParent)
          {
               curtop += obj.offsetTop
               obj = obj.offsetParent;
          }
     }
     else if (obj.y)
          curtop += obj.y;
     return curtop;
}

function createNewFormElement(inputForm, elementName, elementValue)
{
    var newElement = document.createElement('input');
    newElement.setAttribute('name', elementName);
    newElement.setAttribute('type', 'hidden');
    inputForm.appendChild(newElement);
    newElement.value = elementValue;
    return newElement;
}

function cursorToHand()
{
    document.body.style.cursor = 'hand';
}

function cursorToDefault()
{
    document.body.style.cursor = 'default';
}

function getStyle(obj)
{
    if(document.defaultView)
    {
        return document.defaultView.getComputedStyle(obj, null);
    }
    else if(obj.currentStyle)
    {
        return obj.currentStyle;
    }
    return null;
}

function pixelsToNum(pixels)
{
    return (+pixels.replace('px', ''));
}

function getScrollTop()
{
    var scrollTop = 0;
    if (document.documentElement && !document.documentElement.scrollTop)
    {
        // IE6 +4.01 but no scrolling going on
    }
    else if (document.documentElement && document.documentElement.scrollTop)
    {
        // IE6 +4.01 and user has scrolled
        scrollTop = document.documentElement.scrollTop;
    }
    else if (document.body && document.body.scrollTop)
    {
        // IE5 or DTD 3.2
        scrollTop = document.body.scrollTop;
    }
    return scrollTop;
}

function getNewMarginTop(originalMarginTop)
{
    var scrollTop = getScrollTop();
    return originalMarginTop + scrollTop + 'px';
}

function nullOrEmptyString(value)
{
    return (!value || value == '');
}

function formatNumber(data, formatString, threeDigitSep, decimalSeperator)
{
    var a = getMask(formatString, decimalSeperator, threeDigitSep);
	var c = (parseFloat(data) < 0 ? "-" : "")+a[2];
	data=Math.abs(Math.round(parseFloat(data)*Math.pow(10, a[0] > 0 ? a[0] : 0))).toString();
	data=(data.length
		< a[0]
			? Math.pow(10, a[0]+1-data.length).toString().substr(1, a[0]+1)+data.toString()
			: data).split("").reverse();
	data[a[0]]=(data[a[0]]||"0")+a[4];
	if (a[1] > 0)
		for (var j = (a[0] > 0 ? 0 : 1)+a[0]+a[1]; j < data.length; j+=a[1])data[j]+=a[5];
	return c+data.reverse().join("")+a[3];
}

function getMask(mask, p_sep, d_sep)
{
	var nmask = mask.replace(/[^0\,\.]*/g, "");
	var pfix = nmask.indexOf(".");
	if (pfix > -1)
		pfix=nmask.length-pfix-1;
	var dfix = nmask.indexOf(",");

	if (dfix > -1)
		dfix=nmask.length-pfix-2-dfix;
	var pref = mask.split(nmask)[0];
	var postf = mask.split(nmask)[1];
	return [
		pfix,
		dfix,
		pref,
		postf,
		p_sep,
		d_sep
	];
}

function customAlert(html, hide_ok)
{
    if(hide_ok === undefined) {
        hide_ok = false;
    }

    if(hide_ok === true) {
        $('#inptCstmAlertOk').hide();
    }
    if (navigator.userAgent.indexOf("MSIE") !== -1) {
        $('#spnCustomAlert').html(html);
    }
    else {
        elm('spnCustomAlert').innerHTML = html;
    }
    elm('cstmAlert').style.display = 'block';
    elm('cstmAlert').style.visibility = 'visible';
    $('#inptCstmAlertOk').focus();
    $('#cstmAlert').centerInClient();
}

// Custom confirm dialog box ----------------------------
var customConfirmCallBackFunction;
function customConfirm(html, callBackFunction)
{
    elm('spnCstmConfirm').innerHTML = html;
    elm('cstmConfirm').style.display = 'block';
    $('#inptCstmConfirmOk').focus();
    $('#cstmConfirm').centerInClient();
    customConfirmCallBackFunction = callBackFunction;
}

function cstmConfirmClickOk()
{
    customConfirmCallBackFunction(true);
    customConfirmHide();
}

function cstmConfirmClickCancel()
{
    customConfirmCallBackFunction(false);
    customConfirmHide();
}

function customConfirmHide()
{
    elm('cstmConfirm').style.display = 'none'; 
}
// End custom confirm dialog box --------------------------



function addOptionToDropDwn(selectbox, text, value)
{
    var optn = document.createElement("OPTION");
    optn.text = text;
    optn.value = value;
    selectbox.options.add(optn);
}

function removeAllOptionsFromDropDwn(selectbox)
{
    var i;
    for(i=selectbox.options.length-1;i>=0;i--)
    {
        selectbox.remove(i);
    }
}

// Suggest Class -------------------------------------------
Suggest.prototype.inputBox;
Suggest.prototype.divBox;
Suggest.prototype.iframe;
Suggest.prototype.intSelected = 0;
Suggest.prototype.intItemCount; // includes search box
Suggest.prototype.strSearchTyped = ''
Suggest.prototype.items = [];
Suggest.prototype.nonSelectedItemClass = 'DivSuggestItem';
Suggest.prototype.selectedItemClass = 'DivSuggestItemSelected';
Suggest.prototype.versus_class = 'suggest_vs';
Suggest.prototype.versus_arrow_class = 'suggest_vs_arrow';
Suggest.prototype.terms = '';
Suggest.prototype.domains = '';
Suggest.prototype.searchOnPick = true;
Suggest.prototype.index;
Suggest.prototype.regex_domain = /(\w+\.)(com|org|edu|gov|uk|net|ca|de|jp|fr|au|us|ru|ch|it|nl|se|no|es|mil|biz)$/;
Suggest.prototype.search_button;
Suggest.prototype.selected_versus = false;

Suggest.prototype.resetCss = function()
{
    var child;
    for(var i = 0; i < this.divBox.childNodes.length; i++) {
        child = this.divBox.childNodes[i];
        if(child) {
            if (this.item_is_versus(child)) {
                child.className = this.versus_class;
            }
            else {
                child.className = this.nonSelectedItemClass;
            }
                
        }
    }
}

Suggest.prototype.select = function (keyNum)
{
    this.resetCss();
    var item = this.get_selected();
    if(item !== undefined) {
        var content = $(item).text();
        if(this.intSelected == 0) // reset to what was originally typed in box
        {
            this.inputBox.value = this.strSearchTyped;
        }
        else
        {
            if(item)
            {
                $(item).addClass(this.selectedItemClass);
            }
            this.inputBox.value = content.replace('▼', '');
        }
    }
}

Suggest.prototype.show = function ()
{
    if(this.items.length == 0)
    {
        this.hide();
    }
    else
    {
        this.divBox.style.visibility = "visible";
    }
}

Suggest.prototype.hide = function ()
{
    this.divBox.style.visibility = "hidden";
}

Suggest.prototype.item_is_versus = function(item) {
    if(item === undefined) {
        item = this.get_selected();
    }
    if(item !== undefined && item.innerHTML.match('▼')) {
        return true;
    }
    return false;
}

Suggest.prototype.get_selected = function() {
    return this.items[this.intSelected];
}

Suggest.prototype.search_is_versus = function() {
    var input = spyfu_ns.trim(this.strSearchTyped);
    var index = input.indexOf(' vs');

    if(index >= 0 && (index === input.length - 3 || index === input.length - 4)) { // "* vs" or "* vs." 
        return true;
    }
    else if(index !== -1 && (input.indexOf(' vs ') !== -1 || input.indexOf(' vs. ') !== -1)) {// "* vs *" or "* vs. *"

        return true;
    }
    return false;
}

Suggest.prototype.add_kombat_suggestions = function (suggestions) {
    for(var i = 0; i < suggestions.length; i++) {
        if(this.regex_domain.test(suggestions[i]) === true) {
            suggestions.splice(i + 1, 0, suggestions[i] +  ' vs. <span class="suggest_vs_arrow">&#9660;</span>');
            i++;
        }
    }
    return suggestions;
}

Suggest.prototype.fill = function (text, currObj)
{
    currObj.clear();
    var is_vs = currObj.search_is_versus();
    if(is_vs === true && this.selected_versus === true && spyfu_ns.trim(text) === '') {
        currObj.search_button.click();
    }
    var suggest_vs = (! is_vs && (currObj.terms === '' || currObj.terms > 0));// having terms makes sure we're not in Kombat
    if(text && text != '')
    {
        text = text.replace('<HR>', '<BR>');
        var suggestions = text.split('<BR>');
        if(suggest_vs) {
            suggestions = currObj.add_kombat_suggestions(suggestions);
        }
        for(var i = 1; i < suggestions.length; i++) // this.items[0] is the search box
        {
            var content = suggestions[i - 1];
            var newDiv = document.createElement('div');
            newDiv.setAttribute('id', currObj.divBox.id + '_i-' + i);
            
			if(document.addEventListener)
			{
				newDiv.addEventListener('mouseover', function(event) {currObj.mouseOver(event); }, false);
				newDiv.addEventListener('click', function(event) {currObj.click(event); }, false);
			}
			else if(document.attachEvent) // IE
			{
				newDiv.attachEvent('onmouseover', function(event) {currObj.mouseOver(event); }, false);
				newDiv.attachEvent('onclick', function(event) {currObj.click(event); }, false);
			}
            
            if(is_vs === true)
            {
                content = spyfu_ns.trim(currObj.strSearchTyped) + ' ' + content;
            }  
            newDiv.innerHTML = content + newDiv.innerHTML; // this.items[0] is the search box so shift suggestions over one

            currObj.divBox.appendChild(newDiv);
            currObj.items[i] = newDiv;
        }
    }
    currObj.resetCss();
    currObj.show();
}

Suggest.prototype.clear = function ()
{
    for(var i = this.divBox.childNodes.length -1; i >= 0; i--)
    {
        this.divBox.removeChild(this.divBox.childNodes[i]);
    }
}

Suggest.prototype.mouseOver = function (e)
{
    var target = (e && e.target) || (event && event.srcElement);
    if(target.className !== this.versus_arrow_class) {
        this.resetCss();
        $(target).addClass(this.selectedItemClass);   
    }

    this.intSelected = parseInt(target.id.substring(target.id.indexOf('-', 0) + 1, target.id.length));
}

Suggest.prototype.click = function (e)
{
    var target = (e && e.target) || (event && event.srcElement);
    var selection = spyfu_ns.trim($(target).text()).replace('▼', '');
    if(selection === '') { // clicked arrow inside div
        selection = spyfu_ns.trim($(target).parent().text()).replace('▼', '');
    }
    this.inputBox.value = selection;
    if(this.item_is_versus(target) && this.divBox.style.visibility == 'visible') {
        // show the competitor suggestions
        this.request_items(true);
    }
    else if(this.searchOnPick)
    {
        window.location = 'search?q=' + selection;
    }
}

Suggest.prototype.keyUp = function (e)
{
    var keynum = GetKeyNum(e);
    currentSuggest = this;
    switch(keynum) {
        case 40: // down
            this.intSelected = this.intSelected + 1;
            if(this.intSelected == this.items.length)
            {
                this.intSelected = 0;
            }
            this.select(keynum);
            break;
        case 38: // up
            this.intSelected = this.intSelected - 1;
            if(this.intSelected < 0)
            {
                this.intSelected = this.items.length - 1;
            }
            this.select(keynum);
            break;
        case 27: // escape
            this.inputBox.value = this.strSearchTyped;
            this.hide();
            break;
        case 9: // tab
            //suggestHideAll();
            break;
        case 13: // enter
            try {
                if(this.item_is_versus() === true) {
                    // show the competitor suggestions
                    this.request_items(true);
                    return spyfu_ns.stifle(e);
                }
                else if(suggestBoxes.length > 1) {
                    if(this.is_visible() && suggestIncrementFocus() ===  false){
                        this.search_button.click();
                    }
                    suggestHideAll();
                    return spyfu_ns.stifle(e);
                }
                else {
                    suggestHideAll();
                    this.search_button.click();
                }
            }
            catch (e) {
                this.search_button.click(); 
            }
            break;
        default:
            if(this.strSearchTyped != this.inputBox.value)
            {
                this.request_items();
            }
            break;
    }
}

Suggest.prototype.key_down = function (e)
{
    var keynum = GetKeyNum(e);
    switch(keynum) {
        case 9: // tab
            suggestHideAll();
            if(suggestBoxes.length === 1) {
                if(this.item_is_versus() === true) {
                    this.request_items(true);
                }
                else {
                    this.search_button.focus();
                }
                return spyfu_ns.stifle(e);
            }
            break;
        default:
            break;
    }
    return true;
}

Suggest.prototype.request_items = function(selected_versus) {
    if(selected_versus === true) {
        this.selected_versus = true;
    }
    this.strSearchTyped = this.inputBox.value;
    this.intSelected = 0;
    this.items = [];
    try
    {
        this.iframe.GetItems(this);
    }
    catch (e) {}
}

//Suggest.prototype.inputClick = function (e)
//{
    //this.inputBox.select();
//}

Suggest.prototype.is_visible = function() {
    return (this.divBox.style.visibility === 'visible')
}

function Suggest(table, type)
{
    this.inputBox = table.getElementsByTagName('input')[0];
    this.divBox = table.getElementsByTagName('div')[0];
    this.iframe = window.frames['SuggestReqFrame'];
    if(type.terms !== undefined) {
        this.terms = type.terms;
    }
    if(type.domains !== undefined) {
        this.domains = type.domains;
    }
    if(type.search_button_id !== undefined) {
        this.search_button = $('#' + type.search_button_id);
    }
    function assignEventHandlers(currObj) // Creates a new closure for each Suggest object so its event handler has its own version of "this". (Since "this" accesses "currObj" in the closure.)
    {
        //$(currObj.inputBox).keyup(function(event) {currObj.keyUp(event);});
        if(document.addEventListener)
        {
            currObj.inputBox.addEventListener('keyup', function(event) {currObj.keyUp(event); }, false);
            currObj.inputBox.addEventListener('keydown', function(event) {currObj.key_down(event); }, false);
            currObj.inputBox.addEventListener('select', suggestHideAll, false);
            //currObj.inputBox.addEventListener('click', function(event) {currObj.inputClick(event); }, false);
        }
        else if(document.attachEvent) // IE
        {
            currObj.inputBox.attachEvent('onkeyup', function(event) {currObj.keyUp(event); }, false);
            currObj.inputBox.attachEvent('onkeydown', function(event) {currObj.key_down(event); }, false);
            currObj.inputBox.attachEvent('onselect', suggestHideAll, false);
            //currObj.inputBox.attachEvent('onclick', function(event) {currObj.inputClick(event); }, false);
        }
    };
    assignEventHandlers(this);
}

// End Suggest Class --------------------------------------------------

function suggestInitAll()
{

    var tables = document.getElementsByTagName('table');
    var j = 0;
    for (var i = 0; i < tables.length; i++)
    {
        if(tables[i].getAttribute('suggest') != null)
        {
            suggestBoxes[j++] = new Suggest(tables[i], eval('(' + tables[i].getAttribute('suggest') + ')')); // type is JSON
        }
    }
    if(suggestBoxes.length > 1)
    {
        for (var k = 0; k < suggestBoxes.length; k++)
        {
            suggestBoxes[k].searchOnPick = false;
            suggestBoxes[k].inputBox.tabIndex = k + 999;
            suggestBoxes[k].index = k;
        }        
    }
    if(suggestBoxes.length === 1) {
       $(suggestBoxes[0].inputBox).attr("tabindex", 0);
       $(suggestBoxes[0].search_button).attr("tabindex", 1);
    }
}

function suggestHideAll()
{
    for (var i = 0; i < suggestBoxes.length; i++)
    {
        suggestBoxes[i].divBox.style.visibility = 'hidden';
    }
}

function suggestIncrementFocus()
{
    var index = suggestGetFocusedIndex() + 1;
    if(index < suggestBoxes.length) {
        var box = suggestBoxes[index].inputBox;
        //alert($(box).attr('disabled'));
        if($(box).attr('disabled') === false){
            box.focus();
            box.select();
            //alert(3);
            return true;
        }
    }
    return false;
}

function suggestGetFocusedIndex()
{
    for (var i = 0; i < suggestBoxes.length; i++)
    {
        if(currentSuggest == suggestBoxes[i])
        {
            return i;
        }
    } 
}

function Ajax(url, vars, callbackFunction)
{
    request = GetXmlHttpObject();
    request.open('GET', url, true);
    request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    request.onreadystatechange = function() {
        var done = 4, ok = 200;
        if (request.readyState == done && request.status == ok)
        {
            callbackFunction(request.responseText);
        }
    };
    request.send(vars);
}

function GetKeyNum(event)
{
    if(event !== undefined) {
        if(window.event) // IE
        {
            return event.keyCode;
        }
        else if(event.which) // Netscape/Firefox/Opera
        {
            return event.which;
        }
    }
}

function GetXmlHttpObject()
{
    var xmlHttp=null;
    try
    {
        // Firefox, Opera 8.0+, Safari
        xmlHttp=new XMLHttpRequest();
    }
    catch (e)
    {
        // Internet Explorer
        try
        {
            xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (e)
        {
            xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
        }
    }
    return xmlHttp;
}

$(document).ready(function() {
    suggestInitAll(); // adds this to functions that wait for DOM to load before calling
    if(elm('isAuth')) {
        loggedIn = true;
    }
    else {
        loggedIn = false;
    }
});

function suggestBoxOpen()
{
    for (var i = 0; i < suggestBoxes.length; i++)
    {
        if(suggestBoxes[i].divBox.style.visibility == 'visible')
        {
            return true;            
        }
    }
    return false;
}

// CheckBoxCollection Class -------------------------------------------
// ** Assumes check boxes in same collection appear sequentially in DOM. **
CheckBoxCollection.prototype.numChecked;
CheckBoxCollection.prototype.name;
CheckBoxCollection.prototype.count;
CheckBoxCollection.prototype.checkBoxes;
CheckBoxCollection.prototype.compareButton;

CheckBoxCollection.prototype.validate = function()
{
    if( loggedIn && this.numChecked > MAX_KOMBAT_DOMAINS_SUBSCRIBER )
    {
        customAlert('Only three domains can be compared at a time.');
        return false;
    }
    else if( !loggedIn && this.numChecked > MAX_KOMBAT_DOMAINS_NON_SUBSCRIBER)
    {
        customAlert('Only two domains can be compared at a time, however ' +
            'three domains can be compared with a <a href="Store/SignUp.aspx">SpyFu subscription.</a>');
        return false;
    }
    return true;
}

CheckBoxCollection.prototype.click = function (e)
{
	var target = getTarget(e);
    if(target.checked)
    {
   	    this.numChecked++;
    }
    else
    {
   	    this.numChecked--;
    }
    
    if( ! this.validate())
    {
        target.checked = false;
        this.numChecked--;
    }
    this.showCompareButton();
}

CheckBoxCollection.prototype.setCompareButtonToolTip = function ()
{
    var domains = this.getDomainsToCompare();
    if(domains && domains.length > 0)
    {
        var toolTip = domains[0].toUpperCase();
        for(var i = 1; i < domains.length; i++)
        {
            toolTip = toolTip + ' vs ' + domains[i].toUpperCase();
        }
        document.getElementById('img' + this.name).setAttribute('title', toolTip);
        document.getElementById('img' + this.name).setAttribute('alt', toolTip);
    }
}

CheckBoxCollection.prototype.showCompareButton = function()
{
    if(this.numChecked > 0)
    {
        this.compareButton.style.display = '';
        this.setCompareButtonToolTip();
    }
    else
    {
        this.compareButton.style.display = 'none';
    }
}

CheckBoxCollection.prototype.add = function (checkBox)
{
    this.checkBoxes[this.count] = checkBox;
    this.count++;
    // Looked for already checked boxes. (Back button pressed)
    if(checkBox.checked && checkBox.checked == true)
    {
        if (this.numChecked == NaN || this.numChecked == undefined)
        {
            this.numChecked = 0;
        }
        this.numChecked = this.numChecked + 1;
        this.showCompareButton();
    } 
    
    function assignEventHandlers(currObj) // Creates a new closure for each collection object so its event handler has its own internal version of "this" (Since "this" accesses "currObj" in the closure.)
    {
        //Attach the same onclick event handler to every check box in the the collection.
        if(document.addEventListener)
        {
	        checkBox.addEventListener('click', function(event) { currObj.click(event) }, false);
        }
        else if(document.attachEvent) // IE
        {
            checkBox.attachEvent('onclick', function(event) { currObj.click(event) }, false);
        }
    }
    assignEventHandlers(this);   
}

CheckBoxCollection.prototype.getNamesOfChecked = function ()
{
    var names = [];
    var count = 0;
    for(var i = 0; i < this.checkBoxes.length; i++)
    {
        if(this.checkBoxes[i].checked)
        {
            names[count] = this.checkBoxes[i].getAttribute('name');
            count++;
        }
    }
    return names;
}

CheckBoxCollection.prototype.getDomainsToCompare = function ()
{
    var domains = this.getNamesOfChecked();
    if( ( !loggedIn && domains.length < MAX_KOMBAT_DOMAINS_NON_SUBSCRIBER) || (loggedIn && domains.length < MAX_KOMBAT_DOMAINS_SUBSCRIBER))
    {
        domains.unshift(getCurrentDomainName());
    }
    return domains;
}

function CheckBoxCollection(name)
{
    this.name = name;
    this.numChecked = 0;
    this.count = 0;
    this.checkBoxes = [];
    this.compareButton = document.getElementById('link' + name);
}

// End CheckBoxCollection Class --------------------------------------------------

function checkBoxCollectionInitAll(e)
{
    var inputs = document.getElementsByTagName('input');
    var count = 0;
    var currCollectionName = '';
    var currCollection;
    var newObj;
    
    for (var i = 0; i < inputs.length; i++)
    {
        currCollectionName = inputs[i].getAttribute('checkBoxCollection');
        
        if(currCollectionName)
        {    
            // Create new check box collection. ** Assumes check boxes in same collection appear sequentially in DOM. **
            if(!checkBoxCollections[0] || currCollectionName != checkBoxCollections[count - 1].name)
            {   
                newObj = new CheckBoxCollection(currCollectionName);
                checkBoxCollections[count] = newObj;
                count++;
            }
            
            // Add check box to collection.
            newObj.add(inputs[i]);
        }
    }
}

function getCheckBoxCollection(name)
{
    for(var i = 0; i < checkBoxCollections.length; i++)
    {
        if(checkBoxCollections[i].name == name)
        {
            return checkBoxCollections[i];
        }
    }
}

function getCheckBoxCollectionName(type)
{
    switch (type)
    {
        case 'Organic':
            return 'orgCompetitors';
        case 'Ad':
            return 'adCompetitors';
    }   
}

function compareClick(type)
{    
    var coll = getCheckBoxCollection(getCheckBoxCollectionName(type));
    var domains = coll.getDomainsToCompare();
    window.location = buildKombatUrl(domains, type);
}

function getCurrentDomainName()
{
    return document.getElementById('currentDomainName').getAttribute('value');
}


function buildKombatUrl(domains, type)
{    
    var queryString = "d1=" + domains[0];
    var prefix = '/Kombat/Results.aspx?';
    for( var i = 1; i < domains.length; i++)
    {
        queryString = queryString + '&d' + (i + 1) + '=' + domains[i]
    }
    
    queryString = queryString + '&type=' + type;
    
    if(countryCode != '')
    {
        prefix = '/' + countryCode + prefix;   
    }
    
    return prefix + queryString;
}

var atLeastOneGridObjectExists = false;
//// Grid Class -------------------------------------------
/*
Note that I tested an in-memory sqlite db against a regular db and actually found the in-memory and temp db's to be slower
at sorting and counting 85,000 rows. This prob changes with bigger tables, but then you have to keep all that data in-RAM.
*/
Grid.prototype.globalName;
Grid.prototype.dataUrl;
Grid.prototype.rowsPerChunk;
Grid.prototype.tableName;
Grid.prototype.tableId;
Grid.prototype.dhtmlxGrid; // encapsulated dhtmlxgrid (third pary)
Grid.prototype.db;  // Javascript interface to SQLite/Gears
Grid.prototype.dbUpdated = 0; // Fixes Ajax bug in Chrome where two requests seem to be getting made. (This was because duplicate references to the js file existed :).)
Grid.prototype.div;
Grid.prototype.processingRequestDiv;
Grid.prototype.resultCountSpanTop;
Grid.prototype.resultCountSpanBottom;
Grid.prototype.showMoreDropDwn;
Grid.prototype.userWaiting = false;
Grid.prototype.loadingSpanTop;
Grid.prototype.loadingSpanBottom;
Grid.prototype.menuBarDiv;
Grid.prototype.statusBarRightDiv;
Grid.prototype.pagingTopDiv;
Grid.prototype.pagingBottomDiv;
Grid.prototype.hasCheckBoxColumn = false;
Grid.prototype.rowsPerPage = 100;
Grid.prototype.pageNumber = 0;
Grid.prototype.gridColumns = [];
Grid.prototype.defaultColFilterText  = 'Start typing to narrow your results';
Grid.prototype.busy = false;
Grid.prototype.statusBarTopDiv;
Grid.prototype.statusBarBottomDiv;
Grid.prototype.pageDropDownTop;
Grid.prototype.pageDropDownBottom;
Grid.prototype.nextPageTopDiv;
Grid.prototype.nextPageBottomDiv;
Grid.prototype.prevPageTopDiv;
Grid.prototype.prevPageBottomDiv;
Grid.prototype.pageDropDwnTop;
Grid.prototype.pageDropDwnBottom;
Grid.prototype.resultCount = 0;
Grid.prototype.numPages = 0;
Grid.prototype.removeFiltersLinkTop;
Grid.prototype.removeFiltersLinkBottom;
Grid.prototype.resetSortsLinkTop;
Grid.prototype.resetSortsLinkBottom;
Grid.prototype.defaultOrderByClause;
Grid.prototype.topAnchor;
Grid.prototype.askAfterApiTimeout;
Grid.prototype.parentDiv;
Grid.prototype.navigatedToAnchor = false;
Grid.prototype.chunkNumber = 0;
Grid.prototype.jQueryHelper;
Grid.prototype.allowSortByRelevance;
Grid.prototype.failedDbUpdate = false;
Grid.prototype.size_to_window;
Grid.prototype.EXPORT_LIMIT = 100000;
Grid.prototype.export_filename = '';
Grid.prototype.export_all_url = '';

Grid.prototype.load = function ()
{
    try { 
        var version = this.getVersion();
        this.openDb();
        
        // Put "this" in closure since it gets overloaded in event handler below.
        var thisGrid = this;
        var request = google.gears.factory.create('beta.httprequest');
        request.open('GET', '/LocalDbUpdate/?v=' + version);
        request.onreadystatechange = function()
        {
	        if (request.readyState === 4 && thisGrid.dbUpdated === 0)
	        {
                thisGrid.finishLoad(request.responseText);
	        }
        };
        request.send();
    }
    catch (e) {
        this.show_error(e);
    }
}

Grid.prototype.finishLoad = function(updateScript)
{
    try {
        this.runUpdateScript(updateScript);
        // DB READY -- Do not access or modify the database before this point.
        //throw 'I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. I am possibly a very long javascript error. ';
        this.configureDhtmlx();
        this.tableId = this.getTableId(this.tableName);
        this.defaultOrderByClause = this.getSingleValueFromDb("SELECT DefaultOrderByClause FROM DGridTable WHERE TableId = " + this.tableId);
        this.resetSortsAndFilters();          
        this.setupColumns();
        if(this.size_to_window === true) {
            this.resize(undefined);
        }
        // Used instead of recursion to keep stack size down
        this.jQueryHelper = $(this).bind('fetchNextChunk', this.fetchTableData);
        this.jQueryHelper.trigger('fetchNextChunk');
    }
    catch (e) {
        this.show_error(e);
    }
}

Grid.prototype.resetSortsAndFilters = function()
{
    // Reset filters and sorts until things are done on a per search basis
    this.resetSorts();
    this.removeFilters();
}

Grid.prototype.removeFilters = function()
{
    this.executeSql("UPDATE DGridTable SET WhereClause = '' WHERE TableName = '" + this.tableName + "'");
    this.executeSql("UPDATE DGridColumn SET FullTextSearchBoxContents = '' WHERE TableName = '" + this.tableName + "'");
    this.executeSql("UPDATE DGridColumn SET IncludeValues = '' WHERE TableName = '" + this.tableName + "'");
    this.executeSql("UPDATE DGridColumn SET ExcludeValues = '' WHERE TableName = '" + this.tableName + "'");
}

Grid.prototype.resetSorts = function()
{
    this.executeSql("UPDATE DGridTable SET OrderByClause = '" + this.defaultOrderByClause + "' WHERE TableName = '" + this.tableName + "'");  
}

Grid.prototype.resetSortsClick = function()
{
    this.resetSorts();
    this.clearColHeaderArrows();
    this.resetSortsLinkTop.style.display = 'none';
    this.resetSortsLinkBottom.style.display = 'none';
    this.pageNumber = 0;
    this.fill();
}

Grid.prototype.removeFiltersClick = function()
{
    this.removeFilters();
    this.removeFiltersLinkTop.style.display = 'none';
    this.removeFiltersLinkBottom.style.display = 'none';
    this.pageNumber = 0;
    this.fill();
}

Grid.prototype.runUpdateScript = function(sqlScript)
{
    if (sqlScript != '')
    {
        try
        {
	        // Do all the updating in one transaction so we can rollback to the previous version on 
            // any errors.
            this.db.execute('BEGIN');

	        var statements = sqlScript.split(';');
            for (var i = 0; i < statements.length - 1; i++) // Minus 1 to ignore last empty item
            {
                if(statements[i] != '')
                {
                    if (statements[i].substring(0,2) == '\r\n')
                    {
                        statements[i] = statements[i].substring(2, statements[i].length)
                    }
                    this.db.execute(statements[i] + ';');
                }
            }
            this.db.execute('COMMIT');
        }
        catch(e)
        {
            this.db.execute('ROLLBACK');
            if(this.failedDbUpdate === false) {
                this.failedDbUpdate = true;
                this.db.remove(); // delete database and try again. Maybe there was schema inconsistency
                this.db.close();
                this.openDb();
                this.runUpdateScript(sqlScript);
            }
            else {
                this.db.close();
                throw e;
            }
        }
        
        this.db.close();
    }
    this.dbUpdated = 1;
}

Grid.prototype.openDb = function ()
{
    try {this.db.open('SpyFu');} catch(e){}
}

Grid.prototype.getVersion = function ()
{
    var version = 0;
    try
    {
        this.openDb();
        var rs = this.db.execute('select version from version');
        version = rs.field(0);
    }
    catch (e)
    {
        version = 0;
        this.db.close();
    }
    finally
    {
        this.db.close();
        return version;
    }
}

Grid.prototype.startCommands = function ()
{
    var setFunctions = this.db.execute('select SetFunction from DGridColumnMapping order by ExecutionOrder');
    var commands = [];
    
    while (setFunctions.isValidRow())
	{
	    // Note that ExecutionOrder equals the command array index, i, because of the order clause above.
	    commands.push("this." + setFunctions.field(0) + "('" );
	    setFunctions.next();
	}
    setFunctions.close();
    return commands;
}

Grid.prototype.dhtmlxSetSchema = function ()
{	
	// This function executes the SetFunctions in DGridColumnMapping by mapping to the values in DGridColumn
	this.openDb();
	try
	{
	    var values = this.db.execute("select * from DGridColumn where TableName = '" + this.tableName + "' order by DisplayOrder");
	    var commands = 	this.startCommands();
	    var commandIndexRs;
	    var commandIndex;

	    while (values.isValidRow())
	    {
            // Append one value to the end of each command
            for( var i = 0; i < values.fieldCount(); i++)
            {
                commandIndexRs = this.db.execute("select ExecutionOrder from DGridColumnMapping where ColumnName = '" + values.fieldName(i) + "'");
                commandIndex = commandIndexRs.field(0);
                commands[commandIndex] = commands[commandIndex] + values.field(i) + ',';
            }
	        values.next();
	    }
	    values.close();
	    
	    if(commandIndexRs)
	    {
	    	commandIndexRs.close();
	    }

	    this.db.close();
	    this.closeAndExecCommands(commands);
	}
	catch (e) 
	{
	    // Don't worry about closing the row sets cuz execution just stops anyway after throwing the error below.
	    // This helps make sure another error isn't thrown here for an unopened database.
	    this.db.close();
	    throw e;
	}
}

Grid.prototype.configureDhtmlx = function()
{
    this.dhtmlxSetSchema();
    this.dhtmlxNumberFormatting(); 
    this.dhtmlxGrid.enableKeyboardSupport();
	this.dhtmlxGrid.setActive();
    this.dhtmlxGrid.imgURL = '/UserControls/DGrid/dhtmlx/imgs/';
    this.dhtmlxGrid.init();
	this.dhtmlxGrid.enableCSVAutoID(false);
	this.dhtmlxGrid.csv.cell = ",";
	this.dhtmlxGrid.setSkin("light");
	//this.dhtmlxGrid.enableBlockSelection();
	//this.dhtmlxGrid.enableDistributedParsing(true, 25, 100);
	//this.dhtmlxGrid.rowsToShow = this.rowsPerPage;
	                //enablePaging(enable,rowsPerPage,pagesInGroup,pagingArea,recInfoEnabled,recinfoArea);
	//this.dhtmlxGrid.enablePaging(true, this.rowsPerPage, 10, this.pagingTopDiv, true, undefined);
	                //insertColumn(ind,header,type,width,sort,align,valign,hidden,columnColor)
	//this.dhtmlxGrid.insertColumn(0, '#master_checkbox' , 'ch', 45, 'int', 'middle', undefined, false, undefined);
}

Grid.prototype.dhtmlxNumberFormatting = function()
{
	this.openDb();
	try
	{
	    var colProps = this.db.execute("select * from DGridColumn where TableName = '" + this.tableName + "' order by DisplayOrder");
        var i = 0;
	    while (colProps.isValidRow())
	    {
            var numberFormatString = colProps.fieldByName('NumberFormatString');
            if( !nullOrEmptyString(numberFormatString) )
            {
                this.dhtmlxGrid.setNumberFormat(numberFormatString, 
                    i,
                    colProps.fieldByName('NumberFormatDecimalPoint'),
                    colProps.fieldByName('NumberFormat3digitSeperator')
                    );
            }
            i++;
	        colProps.next();
	    }
	    colProps.close();

	    this.db.close();
	}
	catch (e) 
	{
	    // Don't worry about closing the row sets cuz execution just stops anyway after throwing the error below.
	    // This helps make sure another error isn't thrown here for an unopened database.
	    this.db.close();
	    throw e;
	}    
}

Grid.prototype.setupColumns = function()
{
    this.openDb();
	try
	{
	    var cols = this.db.execute("select Name, DbColName, FullTextSearchBox, CalculatedColumnFormula from DGridColumn where TableName = '" + this.tableName + "' order by DisplayOrder");
	    var i = 0;
	    var j = 0;
	    var colName;
	    var dbColName;
	    var hasSearchBox;
	    var formula;
	    var div;
	    var gridColumn;
	    this.gridColumns = [];
	    while (cols.isValidRow())
	    {
	        colName = cols.field(0);
	        dbColName = cols.field(1);
	        hasSearchBox = cols.field(2);
	        formula = cols.field(3);
	        if(colName != '#master_checkbox')
	        {
		        div = this.createHeaderDiv(colName, dbColName, j, hasSearchBox);	        
		        this.dhtmlxGrid.setColumnLabel(i, undefined, 0, div);
		        this.gridColumns.push(new GridColumn(div, colName, dbColName, hasSearchBox, formula));
		        j++;
	        }
	        i++;
	        cols.next();
	    }
	    cols.close();
	}
	catch (e)
	{
	    // Don't worry about closing the row sets cuz execution just stops anyway after throwing the error below.
	    // This helps make sure another error isn't thrown here for an unopened database.
	    this.db.close();
	    throw e;
	}
}

Grid.prototype.createHeaderDiv = function(colName, dbColName, index, hasSearchBox)
{
    div = document.createElement('div');
    div.className = 'DivDGridColHeader';
    div.innerHTML = '&nbsp;<u>' + colName + '</u>';
    
    // Disable this until it's faster. They can use include filter instead
    if(hasSearchBox == 1)
    {
        var searchBox = document.createElement('input');
        searchBox.setAttribute('type', 'text');
        searchBox.setAttribute('value', this.defaultColFilterText);
        searchBox.className = 'InptDGridColFilter';
        $(searchBox).keydown(this.keydown);
        div.innerHTML = div.innerHTML + '&nbsp;';
        div.appendChild(searchBox);
    }
   
    function assignEventHandlers(currObj) // Creates a new closure for each object so its event handler has its own version of "this". (Since "this" accesses "currObj" in the closure.)
    {
        if(document.addEventListener)
        {
            div.addEventListener('click', function(event) {currObj.colHeaderClick(dbColName, index); }, false);
            if(searchBox)
            {
                div.addEventListener('keyup', function(event) {event.cancelBubble = true; currObj.colFilterKeyUp(event, dbColName); }, false);
                searchBox.addEventListener('click', function(event) {event.cancelBubble = true; currObj.colFilterClick(event, dbColName);}, false);}
        }
        else if(document.attachEvent) // IE
        {
            div.attachEvent('onclick', function(event) {currObj.colHeaderClick(dbColName, index); }, false);
            if(searchBox)
            {
                div.attachEvent('onkeyup', function(event) {event.cancelBubble = true; currObj.colFilterKeyUp(event, dbColName);}, false);
                searchBox.attachEvent('onclick', function(event) {event.cancelBubble = true; currObj.colFilterClick(event, dbColName);}, false);
            }            
        }
    };
    assignEventHandlers(this);
    return div;
}

Grid.prototype.colFilterClick = function(e, colName)
{
    target = getTarget(e);
    if(target.value == this.defaultColFilterText)
    {
        target.value = '';        
    }
}

// Shows busy for at least one second cuz gif doesn't load fast enough.
Grid.prototype.showBusy = function()
{
    this.busy = true;
    var currObj = this;
    var timer = google.gears.factory.create('beta.timer').setTimeout(function () {currObj.afterBusy1second()}, 1000);
    this.showLoadingIcon();
    this.loadingSpanTop.style.visibility = 'visible';
    this.loadingSpanBottom.style.visibility = 'visible';
}

Grid.prototype.showLoadingIcon = function()
{
    this.loadingSpanTop.style.visibility = 'visible';
    this.loadingSpanBottom.style.visibility = 'visible';
}

Grid.prototype.hideLoadingIcon = function()
{
    this.loadingSpanTop.style.visibility = 'hidden';
    this.loadingSpanBottom.style.visibility = 'hidden';
}

Grid.prototype.show_error = function(err) {
        this.hide_loading_splash();
        this.hideLoadingIcon();
        var message = (err.message ? err.message : err.toString());
        elm('dgrid_error_details').innerHTML = message;
        customAlert(elm('dgrid_error').innerHTML, true);
        throw err;
}

Grid.prototype.colFilterKeyUp = function(e, colName)
{
    this.showBusy();
    target = getTarget(e);
    if(target.value == this.defaultColFilterText)
    {
        target.value = '';        
    }
    else
    {
        // Set FullTextSearchBoxContents column on DGridTable based of searchBox input text
        this.setColProp(colName, "FullTextSearchBoxContents", target.value);
        this.filter(false);
    }
    this.busy = false;
}

Grid.prototype.getSingleValueFromDb = function(sqlQuery)
{
    var returnValue;
    this.openDb();
    try
    {
        var rs = this.db.execute(sqlQuery);
        var returnValue = rs.field(0);
    }
    catch(e)
    {
        throw e;
    }
    finally
    {
        this.db.close();
    }
    rs.close();
    return returnValue;
}

Grid.prototype.executeSql = function(sqlQuery)
{
    this.openDb();
    this.db.execute('BEGIN').close();
    try
    {
        var rs = this.db.execute(sqlQuery);
        this.db.execute('COMMIT').close();
    }
    catch(e)
    {
        try
        {
            this.db.execute('ROLLBACK').close();   
        } 
        catch (e) {}
        throw e;
    }
    finally
    {
        this.db.close();
    }
    rs.close();
}

Grid.prototype.getColProp = function(colName, propName)
{
    return this.getSingleValueFromDb("select " + propName + " from DGridColumn where TableName = '" + this.tableName + "' and DbColName = '" + colName + "'");
}

Grid.prototype.setColProp = function(colName, propName, newPropValue)
{
    this.executeSql("update DGridColumn set " + propName + " = '" + newPropValue + "' where TableName = '" + this.tableName + "' and DbColName = '" + colName + "'");
}

Grid.prototype.colHeaderClick = function(colName, index)
{
    this.showLoadingIcon();

    // Get current sort direction
    var sortDirection = this.getColProp(colName, 'SortDirection');
    var newSortDirection;
    var asc;
    
    // Reverse it and store it
    if (sortDirection == null)
    {
        newSortDirection = 1; // desc
        asc = false;
    }
    else if(sortDirection == 1)
    {
        newSortDirection = 0;
        asc = true;
    }
    else
    {
        newSortDirection = 1;
        asc = false;
    }
    this.setColProp(colName, 'SortDirection', newSortDirection);
    this.clearColHeaderArrows();
    this.gridColumns[index].setDirection(asc);
    
    // Delete the sort arrows from the other headers
    // Add the correct sort arrow to the header
    
    // Erase other sortDirections
    this.executeSql("update DGridColumn set SortDirection = null where TableName = '" + this.tableName + "' and DbColName != '" + colName + "'");
    this.sort();
    this.hideLoadingIcon();
}

Grid.prototype.clearColHeaderArrows = function()
{
    for(var i = 0; i < this.gridColumns.length; i++)
    {
        this.gridColumns[i].clearDirectionArrow();
    }
}

// This is a helper functionn for setting up the dhtmlx stuff
Grid.prototype.closeAndExecCommands = function(commands)
{
    var cmd;
    for(var i = 0; i < commands.length; i++)
    {
        cmd = commands[i];
        if(cmd.substring(cmd.length - 1, cmd.length) == ',')
        {
            cmd = cmd.substring(0, cmd.length - 1); // remove last comma from list
        }
        cmd = cmd + "')" + ";"; // close the function call

        eval(cmd);
    }
}

Grid.prototype.afterTimeOut = function()
{
    if( !this.userWaiting && this.askAfterApiTimeout)
    {
        this.userWaiting = true;
        customConfirm('This search may take a few minutes. Click OK to keep waiting, or click Cancel to run this search in the background.',
            function(answer)
            {
                if(answer)
                {
                   this.userWaiting = true;
                }
                else
                {
         
                    this.userWaiting = false;
                    window.location = window.location.pathname;
                }
            });
    }
}

Grid.prototype.afterBusy1second = function()
{
    if(this.busy)
    {
        var currObj = this;
        var timer = google.gears.factory.create('beta.timer').setTimeout(function(){currObj.afterBusy1second();}, 1000);
    }
    else 
    {
        this.hideLoadingIcon();
    }
}

Grid.prototype.hide_loading_splash = function() {
    this.processingRequestDiv.style.display = 'none';
}

Grid.prototype.fetchTableData = function ()
{            
    try {
        // Put "this" in closure since it gets overloaded in event handler below.
        var thisGrid = this;
        var request = google.gears.factory.create('beta.httprequest');
        request.open('GET', thisGrid.dataUrl + '&chunk=' + this.chunkNumber + '&chunkSize=' + thisGrid.rowsPerChunk + '&junk=' + 
            guid()); // Guid protects against stack overflow and makes sure a new request is made
        request.onreadystatechange = function()
        {
	        if (request.readyState === 4)
	        {
                if ( request.responseText == 'NotReady')
                {
                    if(thisGrid.chunkNumber === 0)
                    {
                        thisGrid.afterTimeOut();
                        var timer = google.gears.factory.create('beta.timer');
                        timer.setTimeout(thisGrid.afterTimeOut, 10); // Protects against stack overflow                 
                    }
                    thisGrid.jQueryHelper.trigger('fetchNextChunk');
                }
                // Empty response signifies last chunk was sent
                else if( request.responseText === '')
                {
                    thisGrid.processingRequestDiv.style.display = 'none';
                    thisGrid.hideLoadingIcon();

                    if ( parseInt(thisGrid.resultCountSpanTop.innerHTML) === 0)
                    {
                        customAlert('No results found.');
                    }
                    else
                    {
                        thisGrid.menuBarDiv.style.visibility = 'visible';
                        thisGrid.fill();
                    }
                }
                else
                {   
                    thisGrid.insertDbTable(request.responseText);
                    
                    if (thisGrid.chunkNumber === 0)
                    {
                        thisGrid.hide_loading_splash();
                        if(elm('gridTitle')) // This should be done with observer pattern, but it's tested so next time
                        {
                            elm('gridTitle').style.display = '';
                        }
                        if(elm('gridTable'))
                        {
                            elm('gridTable').style.borderWidth = '2px';
                        }                        
                        thisGrid.fill(); // Show first chunk
                    }
                    else if(thisGrid.chunkNumber === 1)
                    {
                        thisGrid.fill(false);
                    }
                    else
                    {
                        thisGrid.updatePaging();
                    }
                    
                    thisGrid.chunkNumber = thisGrid.chunkNumber + 1;
                    thisGrid.jQueryHelper.trigger('fetchNextChunk');
                }
	        }
        };
        request.send();
    }
    catch (e) {
        this.show_error(e);
    }
}

Grid.prototype.show = function() {
    this.div.style.display = 'block';
    if(elm('gridTitle')) // This should be done with observer pattern, but it's tested so next time
    {
        elm('gridTitle').style.display = '';
    }
    if(elm('gridTable'))
    {
        elm('gridTable').style.borderWidth = '2px';
    }
}

Grid.prototype.updatePaging = function()
{
    var upperRange;
    var lowerRange;
    if(this.resultCount == 0)
    {
        upperRange = 0;
        lowerRange = 0;
    }
    else
    {
        var currentResultNum = this.pageNumber * this.rowsPerPage;
        lowerRange = currentResultNum + 1;
        
        if(currentResultNum + this.rowsPerPage > this.resultCount)
        {
            upperRange = this.resultCount;
        }
        else
        {
            upperRange = currentResultNum + this.rowsPerPage;
        }
    }
    
    var newPagingInfo = 'Results ' + String(lowerRange) + ' - ' + String(upperRange) + ' of ';
    this.pagingTopDiv.innerHTML = newPagingInfo;
    this.pagingBottomDiv.innerHTML = newPagingInfo;
    
    if(this.pageNumber === this.numPages - 1)
    {
        this.nextPageTopDiv.style.display = 'none';
        this.nextPageBottomDiv.style.display = 'none';
    }
    else
    {
        this.nextPageTopDiv.style.display = 'inline';
        this.nextPageBottomDiv.style.display = 'inline';
    }
    
    if(this.pageNumber === 0)
    {
        this.prevPageTopDiv.style.display = 'none';
        this.prevPageBottomDiv.style.display = 'none';
    }
    else
    {
        this.prevPageTopDiv.style.display = 'inline';
        this.prevPageBottomDiv.style.display = 'inline';
    }
    
    this.fillPageDropDwns();
}

Grid.prototype.truncateDbTable = function()
{

    if (this.tableStoredLocally(this.tableName))
    {
        this.openDb();
        try {
            this.db.execute("delete from " + this.tableName + ";").close();
        }
        finally {
            this.db.close();        
        }

    }

}

Grid.prototype.insertDbTable = function(csv)
{
    this.openDb();
    // Bulk the inserts into one database transaction for performance.
    this.db.execute('BEGIN').close();
    try
    {        
        var data = csv.split("\r\n");
        var fields;
        for (var i=0; i < data.length; i++)
        {
            if ( data[i] != '')
            {
                // CSV -> SQL (I'm crazy......I know....... :D)
                fields = "'" + data[i].replace(/\'/g, "''").replace(/,/g, "','").replace(/<comma>/g, ",") + "'";
                this.db.execute("insert into " + this.tableName + " values(" + fields + ")").close()
                this.setResultCount(this.resultCount + 1);
            }
        }
        this.db.execute('COMMIT').close();
    }
    catch(e)
    {
      this.db.execute('ROLLBACK').close();
      this.db.close();
      throw e;
    }
    this.db.close();
}

Grid.prototype.sort = function()
{
    var orderByClause;
    this.openDb();
	try
	{
	    var rs = this.db.execute("select * from DGridColumn where TableName = '" + this.tableName + "' and (SortDirection = 0 or SortDirection = 1)");
    	var sortByColName = rs.fieldByName('DbColName');
        var sortByDirection = rs.fieldByName('SortDirection');
        
	    if(sortByColName != null)
	    {
	        var sortDirectionSql;
    	    
            if( sortByDirection == 1)
            {
                sortDirectionSql = 'desc';
            }
            else
            {
                sortDirectionSql = 'asc';
            }
	    }
        else
        {
            orderByClause =  this.defaultOrderByClause; // Order by first column which is usually rank.
        }
	    
	    rs.close();
	    this.db.close();
	}
	catch (e)
	{
	    rs.close();
	    this.db.close();
	    orderByClause = ' order by 1 '; // Order by first column which is usually rank.
	}
    
    orderByClause = " order by case when " + sortByColName + " = '' then -1  else " + sortByColName + " end " + sortDirectionSql;
    orderByClause = orderByClause.replace(/\'/g, "''"); //'escape quotes
    this.executeSql("UPDATE DGridTable SET OrderByClause = '" + orderByClause + "' WHERE TableName = '" + this.tableName + "'");
    this.pageNumber = 0;
    if (this.allowSortByRelevance === true)
    {
        this.resetSortsLinkTop.style.display = '';
        this.resetSortsLinkBottom.style.display = '';
    }
    this.fill();
}

Grid.prototype.filter = function(goToTop)
{
    var whereClause = 
        'WHERE (' +
        this.getColFilterSql() +
        ') AND (' + 
        this.getIncludeExcludeFilterSql('IncludeValues') + 
        ') AND (' + 
        this.getIncludeExcludeFilterSql('ExcludeValues') +
        ')';
    whereClause = whereClause.replace(/\'/g, "''"); //'escape quotes
    this.executeSql("UPDATE DGridTable SET WhereClause = '" + whereClause + "' WHERE TableName = '" + this.tableName + "'");
    this.pageNumber = 0;
    this.removeFiltersLinkTop.style.display = '';
    this.removeFiltersLinkBottom.style.display = '';
    this.fill(goToTop);
}

Grid.prototype.getIncludeExcludeFilterSql = function(propName)
{
    var i = 0;
    var filterText = ' 0 = 0 ';
    this.openDb();
	try
	{
	    var rs = this.db.execute("select * from DGridColumn where TableName = '" + this.tableName + "' and " + propName + " <> ''");
    	var colName;
                
        while (rs.isValidRow())
        {
            var trimmedValue = trim(rs.fieldByName(propName).replace('\n', ''));
            if(trimmedValue != '') //don't do anything if just a bunch of whitespace
            {
            colName = rs.fieldByName('DbColName');
            if(i > 0)
            {
                filterText = filterText + ' AND ';
            }
            else
            {
                filterText = '';
            }
            filterText = filterText + ' (' + this.buildIncludeExcludeForColumn(colName, rs.fieldByName(propName), propName) + ') ';
            }
            rs.next();
        }

	    rs.close();
	    this.db.close();
	}
	catch (e)
	{
	    this.db.close();
	    throw e;
	}
    finally
    {
        return filterText;   
    }

    return filterText;

}

Grid.prototype.buildIncludeExcludeForColumn = function(colName, values, type)
{
    var comparator;
    var compareEnd;
    var combiner;
    if(type == "IncludeValues")
    {
        comparator = " LIKE '%";
        compareEnd =  "%'";
        combiner = " OR ";
    }
    else
    {
        comparator = " NOT LIKE '%";;
        compareEnd = "%'";
        combiner = " AND ";
    }
    var lines = values.split('\n');
    var cleanedLines = new Array();
    for (var i=0; i < lines.length; i++)
    {
        lines[i] = trim(lines[i]);
        if(lines[i] != '')
        {
            cleanedLines.push(lines[i]);
        }
    }

    var output = ' ';
    for(var i = 0; i < cleanedLines.length; i++)
    {
        output = output + " " + colName + comparator + cleanedLines[i] + compareEnd;
        if(i < cleanedLines.length - 1)
        {
            output = output + combiner;
        }
    }
    return output;
}

Grid.prototype.getColFilterSql = function()
{
    var i = 0;
    var filterText = ' 0 = 0 ';
    this.openDb();
	try
	{
	    var rs = this.db.execute("select * from DGridColumn where TableName = '" + this.tableName + "' and FullTextSearchBoxContents <> ''");
    	var colName;
                
        while (rs.isValidRow())
        {
            colName = rs.fieldByName('DbColName');
            if(i > 0)
            {
                filterText = filterText + ' AND ';
            }
            else
            {
                filterText = '';
            }
            filterText = filterText + ' '  + colName + " LIKE '%" + rs.fieldByName('FullTextSearchBoxContents') + "%' ";
            rs.next();    
        }
	    
	    rs.close();
	    this.db.close();
	}
	catch (e)
	{
	    this.db.close();
	}
	finally
	{
        return filterText;
    }
    return filterText;
}

Grid.prototype.fillPageDropDwns = function()
{
    var numPages = parseInt(this.resultCount / this.rowsPerPage);
    if(numPages * this.rowsPerPage != this.resultCount)
    {
        numPages++;
    }
    this.numPages = numPages;
    this.fillPageDropDwn(this.pageDropDwnTop, numPages);
    this.fillPageDropDwn(this.pageDropDwnBottom, numPages);
}

Grid.prototype.fillPageDropDwn = function(dropDwn, numPages)
{
    removeAllOptionsFromDropDwn(dropDwn);
    for(var i = 0; i < numPages; i++)
    {
        addOptionToDropDwn(dropDwn, i + 1, i + 1);
    }
    dropDwn.selectedIndex = this.pageNumber;
}

Grid.prototype.setResultCount = function(count)
{
    this.resultCount = count;
    this.resultCountSpanTop.innerHTML = count;
    this.resultCountSpanBottom.innerHTML = count;
}

Grid.prototype.get_order_by_clause = function() {
    return this.getSingleValueFromDb("SELECT OrderByClause FROM DGridTable WHERE TableId = '" + this.tableId + "'");
}

Grid.prototype.get_where_clause = function() {
    return this.getSingleValueFromDb("SELECT WhereClause FROM DGridTable WHERE TableId = '" + this.tableId + "'");
}

Grid.prototype.get_data_as_csv = function(column_names, where_clause, order_by_clause, offset, limit) {
    if (navigator.userAgent.indexOf("MSIE") !== -1) {
        return this.get_data_as_csv_ie.apply(this, arguments);
    }
    else {
        var data = '';
        this.openDb();
        try {
            var rs = this.db.execute("select " + column_names + " from " + this.tableName + " " + where_clause + " " + order_by_clause + " limit " + offset + ", " + limit); // uses limit myOffset, myLimit notation since offset keyword wasn't working.   
            var i = 0;
            var field = '';
            var row = '';
            while (rs.isValidRow()) {
                row = '';
                for(var j = 0; j < rs.fieldCount(); j++) {
                    field = '' + rs.field(j);
                    field = '"' + spyfu_ns.remove_html(field).replace('"', '""') + '"';
                    if(j === 0) {
                        row = field;
                    }
                    else {
                        row = row + ',' + field;
                    }
                }
                if(i == 0) {
                    data = row;
                }
                else {
                    data = data + '\n' + row;
                }
                rs.next();
                i++;
            }
        }
        catch (e) {
           this.db.close();
           throw e;
        }
        
        rs.close();
        this.db.close();
            
        return data;    
    }
}

Grid.prototype.get_data_as_csv_ie = function(column_names, where_clause, order_by_clause, offset, limit) {
    var data = '';
    this.openDb();
    try {
        var rs = this.db.execute("select " + column_names + " from " + this.tableName + " " + where_clause + " " + order_by_clause + " limit " + offset + ", " + limit); // uses limit myOffset, myLimit notation since offset keyword wasn't working.   
        var i = 0;
        var field = '';
        var field_sb;
        var row_sb;
        var data_sb = new spyfu_ns.string_builder();
        while (rs.isValidRow()) {
            row_sb = new spyfu_ns.string_builder();
            for(var j = 0; j < rs.fieldCount(); j++) {
                field_sb = new spyfu_ns.string_builder();
                field_sb.append('"');
                field_sb.append(spyfu_ns.remove_html(String(rs.field(j))).replace('"', '""'));
                field_sb.append('"');
                if(j !== 0) {
                    row_sb.append(',');
                }
                row_sb.append(field_sb.string());
            }
            if(i !== 0) {
                data_sb.append('\n');
            }
            data_sb.append(row_sb.string());
            rs.next();
            i++;
        }
    }
    catch (e) {
       this.db.close();
       throw e;
    }
    
    rs.close();
    this.db.close();

    return data_sb.string();
}

Grid.prototype.get_data = function(column_names, where_clause, order_by_clause, offset, limit) {
    var data=[];
    this.openDb();
    try
    {
        var rs = this.db.execute("select " + column_names + " from " + this.tableName + " " + where_clause + " " + order_by_clause + " limit " + offset + ", " + limit); // uses limit myOffset, myLimit notation since offset keyword wasn't working.   
        var i = 0;
        while (rs.isValidRow())
        {
            var row = [];
            for(var j = 0; j < rs.fieldCount(); j++)
            {
                row.push(rs.field(j));
            }
            data[i] = row;
            rs.next();
            i++;
        }
    }
    catch (e)
    {
       this.db.close();
       throw e;
    }
    
    rs.close();
    this.db.close();
    return data;
}

Grid.prototype.export_csv = function() {
    var curr_obj = this;
    if(this.resultCount > 2500) {
        spyfu_ns.dgrid_export_dialog(function(){curr_obj.export_all();}, function(){curr_obj.export_edited();});
    }
    else {
        this.export_edited();
    }
}

Grid.prototype.export_all = function() {
    window.location = this.export_all_url;
}

Grid.prototype.export_edited = function() {
    var columnNames = this.getColumnList(false);
    var data = this.get_data_as_csv(columnNames, this.get_where_clause(), this.get_order_by_clause(), 0, this.EXPORT_LIMIT);
    elm('grid_export_file_name').value = this.export_filename;
    elm('grid_export_data').value = this.get_export_columns() + '\n' + data.replace('"', '\"');
    elm('grid_export_form').submit();
}

Grid.prototype.fill = function(goToTop)
{
    try {
        var orderByClause = this.get_order_by_clause();
        var whereClause = this.get_where_clause();
        
        var checkBoxColumn = '';
        if( this.hasCheckBoxColumn )
        {
            checkBoxColumn = "'0',";
        }

        var columnNames = this.getColumnList();
        var data = this.get_data(checkBoxColumn + " " + columnNames, whereClause, orderByClause, String(this.pageNumber * this.rowsPerPage), this.rowsPerPage);


        this.dhtmlxGrid.clearAll();
        this.dhtmlxGrid.parse(data,"jsarray");
        this.loadSpecialCells();
         this.setResultCount(this.getSingleValueFromDb("select count(*) from " + this.tableName + " " + whereClause));
        this.updatePaging();
        
        if(goToTop == undefined || goToTop) // goToTop is an optional parameter
        {
            this.goToTopAnchor();
        }
        //grid.parse({rows:data}, "json");
    }
    catch (e) {
        this.show_error(e);
    }
}

Grid.prototype.loadSpecialCells = function()
{
    var thisGrid = this;
    // Query DOM for all elements with dgridLoad attribute and call a function on the value of the attribute
    $('div').filter(function() { return $(this).attr('dgridLoad'); }).each(function() {
        if(this.getAttribute('dgridLoad').indexOf('{') === -1) { // Legacy, this was the first type of special cell and so it gets special treatment
            $(this).html(thisGrid.fillAdHistory(this));
        }
        else { // We have type information in JSON format for extensibility
            var dgridLoad = eval('(' + this.getAttribute('dgridLoad').replace(',}', '}') + ')'); // expects JSON format
            switch (dgridLoad.type) {
                case 'stacked_chart':
                    thisGrid.fillAdHistoryStacked(this, dgridLoad.history);
                    break;
            }
        }
    });    
}

// fillAdHistoryStacked
// static variables this function uses
var maxValue = 9.0;
var twelveZeroes = '000000000000';
Grid.prototype.fillAdHistoryStacked = function(div, history) {
    var num_levels = history.length;
    var maxPxlHeight = 17.0 / num_levels;
    var normalizationCoefficient = maxPxlHeight / maxValue;
    var twelveCharHistory = [];
    var background_color = '';
    var heights = [0,0,0,0,0,0,0,0,0,0,0,0];
    var html = '';
    for(var i = 0; i < num_levels; i++) {
        if(history[i] !== undefined) {
            switch (i) {
                case 0:
                    background_color = '#c77';
                    break;
                case 1:
                    background_color = '#dd7';
                    break;
                case 2:
                    background_color = '#7c7';
                    break;
            }
            var historyAsString = "" + history[i];
            twelveCharHistory[i] = twelveZeroes.substring(0, 12 - historyAsString.length) + historyAsString;
            for(var j = 0; j < twelveCharHistory[i].length; j++) {
                var height = Math.round(normalizationCoefficient * parseFloat(twelveCharHistory[i].charAt(j)));
                var bottom_offset = heights[j];
                heights[j] = heights[j] + height;
                var left = String(j * 4);
                var opacity = '';
                if(height === 0) {
                    opacity = ' opacity:.1; filter:alpha(opacity=1);';
                }
                html = html + '<div class="gridBar" style="left: ' + left + 'px; bottom: ' + bottom_offset + 'px; height: ' + String(height) + 'px; background-color: ' + background_color + ';' + opacity + '"></div>';
            }    
        }
    }
//    $(div).append(history);
    div.innerHTML = html;
}

Grid.prototype.fillAdHistory = function(div) {
    //$(tr).html($(tr).attr('dgridLoad'));
    var maxPctHeight = 100.0;
    var maxValue = 9.0;
    var normalizationCoefficient = maxPctHeight / maxValue;
    var history = div.getAttribute('dgridLoad');
    var twelveZeroes = '000000000000';
    var twelveCharHistory = twelveZeroes.substring(0, 12 - history.length) + history;
    var html = '';
    if (twelveCharHistory === twelveZeroes) {
        $(div).append('> 1 yr old');
        $(div).addClass('dgridGray');
        $(div).removeClass('outerGridBarDiv');
    }
    else {
        for(var i = 0; i < twelveCharHistory.length; i++) {
            var height = Math.round(normalizationCoefficient * parseFloat(twelveCharHistory.charAt(i))) + 1;
            var left = String(i * 4);
            var opacity = '';
            if(height === 0)
            {
                opacity = ' opacity:.1; filter:alpha(opacity=1);';
            }
            html = html + '<div class="gridBar" style="left:' + left + 'px; height: ' + String(height) + '%;' + opacity + '"></div>';
        }
        div.innerHTML = html;
    }
//    $(div).append(history);
}

Grid.prototype.goToTopAnchor = function()
{
    if(this.navigatedToAnchor)
    {
        window.history.back();
    }
    else
    {
        window.location.hash = this.topAnchor;
    }
}

Grid.prototype.getColumnList = function (return_id_columns)
{
    if(return_id_columns === undefined) {
        return_id_columns = true;
    }
    var colNames = '';
    var delim = '';
    for(var i = 0; i < this.gridColumns.length; i++) {
        if(return_id_columns || ! this.is_id_column_name(this.gridColumns[i].dbName))
        {
            if(i !== 0) {
                colNames = colNames + ',';
            }
            
            if(this.gridColumns[i].formula !== '') {
                colNames = colNames + this.gridColumns[i].formula;
            }
            else {
                colNames = colNames + this.gridColumns[i].dbName;
            }
        }
    }
    return colNames;
}

Grid.prototype.is_id_column_name = function (col_name) {
    if(col_name.substring(col_name.length - 3, col_name.length).toLowerCase() === '_id' || col_name.toLowerCase() === 'termid') {
        return true;
    }
    else {
        return false;
    }
}

Grid.prototype.get_export_columns = function () {
    var colNames = '';
    for(var i = 0; i < this.gridColumns.length; i++) {
        if( ! this.is_id_column_name(this.gridColumns[i].name) ) {
            if(i !== 0) {
                colNames = colNames + ',';
            }
            colNames = colNames + this.gridColumns[i].name;
        }
    }
    return colNames;
}

Grid.prototype.tableStoredLocally = function (tableName)
{
    this.openDb();
  
    try
    {
        var rs = this.db.execute('select count(*) from ' + tableName);
        if( rs.field(0) >= 0 )
        {
            return true;
        }
    }
    catch ( e )
    {
        try {rs.close();} catch (e) {}
    }

    this.db.close();
    return false;
}

//Grid.prototype.onKeyPressed = function (code, ctrl, shift)
//{ 
//    if(ctrl && code == 67)
//    {
//        this.dhtmlxGrid.copyBlockToClipboard();
//    }
//    return true;
//}


Grid.prototype.keyUp = function (e)
{
    var up = 38;
    var down = 40;
    var escape = 27;
    var enter = 13;
    var keynum = GetKeyNum(e);
    currentSuggest = this;

    if(keynum == down)
    {
        this.intSelected = this.intSelected + 1;
        if(this.intSelected == this.items.length)
        {
            this.intSelected = 0;
        }
        this.select(keynum);
    }
}

Grid.prototype.onmousedown = function (e)
{
    if(isRightClick(e))
    {
//        var dEl0=window.document.documentElement;
//        var dEl1=window.document.body;
//        var corrector = new Array((dEl0.scrollLeft||dEl1.scrollLeft),(dEl0.scrollTop||dEl1.scrollTop));
//        
//        if (_isIE)
//        {
//            var x = ev.clientX + corrector[0];
//            var y = ev.clientY - corrector[1];
//        } 
//        else
//        {
//            var x = ev.pageX;
//            var y = ev.pageY;
//        }
		//this.showContextMenu(ev.clientX-1,ev.clientY-1);
    }
}

Grid.prototype.getCheckedRows = function()
{
    for(var i = 0; i < this.gridColumns.length; i++)
    {
        if(this.gridColumns[i].dbName.toLowerCase() === 'term')
        {
            var termIndex = i + 1;
        }
        else if(this.gridColumns[i].dbName == 'CostPerClick' || this.gridColumns[i].dbName ==  'Max_Price' || this.gridColumns[i].dbName ==  'Max_CPC')
        {
            var cpcIndex = i + 1;
        }
    }
    if( !termIndex || !cpcIndex)
    {
        throw "Could not find columns needed for Google Adwords Export in getCheckedRows function of grid."
    }
    else
    {
        var rows = this.dhtmlxGrid.getAllCheckedRows();
        var exportRows = [];
        for(var i = 0; i < rows.length; i++)
        {
            cellArry = new Array();
            //these checks for null should be unnecessary,
            //but for some old "taco" search they were needed for me, steve
            if(rows[i].cells[termIndex] != null) 
            {
                cellArry.push(getInnerText(rows[i].cells[termIndex]));
            }
            else
            {
                cellArry.push('');
            }
            if(rows[i].cells[cpcIndex] != null)
            {
                cellArry.push(getInnerText(rows[i].cells[cpcIndex]));
            }
            else
            {
                cellArry.push(0);
            }
            exportRows.push(cellArry);
        }
    }
   
    return exportRows;
}

Grid.prototype.get_checked_terms = function()
{
    for(var i = 0; i < this.gridColumns.length; i++)
    {
        if(this.gridColumns[i].dbName.toLowerCase() === 'term')
        {
            var termIndex = i + 1;
        }
    }
    if( !termIndex)
    {
        throw "Could not find columns needed for get_checked_terms function of grid."
    }
    else
    {
        var rows = this.dhtmlxGrid.getAllCheckedRows();
        var exportRows = [];
        for(var i = 0; i < rows.length; i++)
        {
            cellArry = new Array();
            //these checks for null should be unnecessary,
            //but for some old "taco" search they were needed for me, steve
            if(rows[i].cells[termIndex] != null) 
            {
                cellArry.push(getInnerText(rows[i].cells[termIndex]));
            }
            else
            {
                cellArry.push('');
            }
            exportRows.push(cellArry);
        }
    }
    return exportRows;
}

Grid.prototype.getTableId = function(tableName)
{
    return this.getSingleValueFromDb("SELECT TableId FROM DGridTable WHERE TableName = '" + tableName + "'");
}

Grid.prototype.addIncludeExcludeFilter = function(colName, values, type)
{
    var propName;
    if(type == 'include')
    {
        propName = "IncludeValues";
    }
    else
    {
        propName = "ExcludeValues";
    }
    this.setColProp(colName, propName, values);
    this.filter();
}

Grid.prototype.pageDropDwnClick = function(e, dropDown)
{
    target = dropDown;
    try
    {
        value = parseInt(target.options[target.selectedIndex].value);
        if(value != this.pageNumber + 1) // pageNumber is zero based
        {
            this.pageNumber = value - 1;
            this.fill();
        }
    }
    catch (e)
    {
    }
}

Grid.prototype.nextPageClick = function(e)
{
    if(this.pageNumber < this.numPages - 1)
    {
        this.pageNumber++;
        this.fill();
    }
    return true;
}

Grid.prototype.prevPageClick = function(e)
{
    if(this.pageNumber != 0)
    {
        this.pageNumber--;
        this.fill();
    }
    return true;
}

Grid.prototype.resize = function (e)
{
    this.parentDiv.style.height = String($(window).height() - 200) + "px";
    //elm('div_gridParent').style.width = $(window).width() - 100;
    //elm('div_main').style.height = $(window).height() - 200;
    this.parentDiv.style.width = String($(window).width() - 40) + "px";
}

Grid.prototype.keydown = function (e)
{
    var enter = 13;
    if(!e)
    {
        var e = event;
    }
    keynum = GetKeyNum(e);

    // Don't submit form on enter within grid
    if (keynum == enter) 
    { 
        e.cancelBubble;
        if (e.stopPropagation )
        {
            e.stopPropagation();
        }
        return false; // don't submit
    }
}

Grid.prototype.my_keywords_add_click = function() {
    var div = elm('my_kw_dlg');
    var dlg = $(div);
    var that = this;
    dlg.dialog({
        autoopen: false,
        title: 'Add to My Keywords', 
        dialogClass: 'my_kw_add',
        width: 350,
        height: 190,
        resizable: false,
        buttons: {
            "Cancel": function() { dlg.dialog("close"); },
            "Ok": function() { dlg.dialog("close"); }
        }
    });
    keywordArry = this.get_checked_terms();
    if(!keywordArry || keywordArry.length == 0) {
        $('#my_kw_add').hide();
        $('#my_kw_error').show();
        $('#my_kw_error').html('Please check one or more keywords to add.');
        dlg.dialog('option', 'buttons', {"Ok": function() { dlg.dialog("close"); }});
        dlg.dialog('open');
    }
    else {
        $('#my_kw_error').hide();
        $('#my_kw_add').show();
        $('#my_kw_group_text').val('');
        function ok (e) {
            if(e.keyCode == 13) {//enter was pressed
                dlg.dialog('option', 'buttons').Ok();
            }
        }
        $('#my_kw_group_text').keyup(ok);
        $('#my_kw_group_select').keyup(ok);
                
        $.ajax({
            type: "GET",
            cache: false,
            url: '/my_keywords/default.aspx?action=get_keyword_groups',
            timeout: 2000,
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                $('#my_kw_add').hide();
                $('#my_kw_error').show();
                $('#my_kw_error').html('Problem with internet connection. Please try again.');
                dlg.dialog('option', 'buttons', {"Ok": function() { dlg.dialog("close"); }});
                dlg.dialog('open');
            },
            success: function(keyword_groups) {
                try {keyword_groups = spyfu_ns.json(keyword_groups).groups;}
                catch (e) {keyword_groups = [];}
                
                if(keyword_groups.length === 0) {
                    elm('my_kw_group_select').innerHTML = '<option value="Default">Default</option>';
                }
                else {
                    elm('my_kw_group_select').innerHTML = '';
                    
                    for(var i = 0; i < keyword_groups.length; i++) {
                        elm('my_kw_group_select').innerHTML = elm('my_kw_group_select').innerHTML +
                            '<option value="' + keyword_groups[i] + '">' + keyword_groups[i] + '</option>';
                    }
                }
                
                function error_adding() {
                    $('#my_kw_add').hide();
                    $('#my_kw_error').show();
                    $('#my_kw_error').html('An error occurred. Try again or click ' + elm('live_chat_span').innerHTML + '.');
                    dlg.dialog('option', 'buttons', {"Ok": function() { dlg.dialog("close"); }});
                    dlg.dialog('open');     
                }

                dlg.dialog('option', 'buttons', {
                    "Ok": function() {
                        var keywords = keywordArry.join(',');
                        var keyword_group;
                        if( $('#my_kw_group_text').val() !== '') {
                            keyword_group = $('#my_kw_group_text').val();
                        }
                        else {
                            keyword_group = $('#my_kw_group_select').val();
                        }
                        $.ajax({
                            type: "POST",
                            cache: false,
                            url: '/my_keywords/default.aspx?action=add_keywords&keyword_group=' + keyword_group,
                            data: '&keywords=' + keywords,
                            success: function(msg){
                                if(msg !== 'ok') {
                                    error_adding();
                                }
                                else {
                                    dlg.dialog("close");
                                }
                            },
                            timeout: 2000,
                            error: error_adding
                        });
                    },
                    "Cancel": function() { dlg.dialog("close"); }
                })
                dlg.dialog('open');
            }
        });
    }
}

// Grid object constructor
function Grid(
    globalName
    , divId
    , gridDivId
    , dataUrl
    , rowsPerChunk
    , dbTableName
    , hasCheckBoxColumn
    , processingRequestDivId
    , resultCountSpanTopId
    , resultCountSpanBottomId
    , pagingTopDivId
    , pagingBottomDivId
    , loadingSpanTopId
    , loadingSpanBottomId
    , menuBarDivId
    , statusBarTopDivId
    , statusBarBottomDivId
    , nextPageTopDivId
    , nextPageBottomDivId
    , prevPageTopDivId
    , prevPageBottomDivId
    , pageDropDwnTopId
    , pageDropDwnBottomId
    , removeFiltersLinkTopId
    , removeFiltersLinkBottomId
    , resetSortsLinkTopId
    , resetSortsLinkBottomId
    , topAnchor
    , askAfterApiTimeout
    , parentDivId
    , allowSortByRelevance
    , size_to_window
    , export_filename
    , export_all_url)
{    
    // WARNING: Do not access the database until getVersion is run. Look for the // DB READY comment.
    this.globalName = globalName;
    this.div = elm(divId);
    this.dataUrl = dataUrl;
    this.rowsPerChunk = rowsPerChunk;
    this.tableName = dbTableName;
    this.hasCheckBoxColumn = hasCheckBoxColumn;
    this.processingRequestDiv = elm(processingRequestDivId);
    this.resultCountSpanTop = elm(resultCountSpanTopId);
    this.resultCountSpanBottom = elm(resultCountSpanBottomId);
    this.pagingTopDiv = elm(pagingTopDivId);
    this.pagingBottomDiv = elm(pagingBottomDivId);
    this.loadingSpanTop = elm(loadingSpanTopId);
    this.loadingSpanBottom = elm(loadingSpanBottomId);
    this.menuBarDiv = elm(menuBarDivId);
    this.statusBarTopDiv = elm(statusBarTopDivId);
    this.nextPageTopDiv = elm(nextPageTopDivId);
    this.nextPageBottomDiv = elm(nextPageBottomDivId);
    this.prevPageTopDiv = elm(prevPageTopDivId);
    this.prevPageBottomDiv = elm(prevPageBottomDivId);
    this.pageDropDwnTop = elm(pageDropDwnTopId);
    this.pageDropDwnBottom = elm(pageDropDwnBottomId);
    this.removeFiltersLinkTop = elm(removeFiltersLinkTopId);
    this.removeFiltersLinkBottom = elm(removeFiltersLinkBottomId);
    this.resetSortsLinkTop = elm(resetSortsLinkTopId);
    this.resetSortsLinkBottom = elm(resetSortsLinkBottomId);
    this.topAnchor = topAnchor;
    this.askAfterApiTimeout = (askAfterApiTimeout === 'false') ? false : true;
    this.parentDiv = elm(parentDivId);
    this.allowSortByRelevance = (allowSortByRelevance === 'false') ? false : true;
    this.size_to_window = (size_to_window === 'false') ? false : true;
    this.export_filename = export_filename;
    this.export_all_url = export_all_url;
    
    try {
        this.dhtmlxGrid =  new dhtmlXGridObject(gridDivId);    
    }
    catch (e) {
        // Wait for script to asychronously load
        setTimeout("spyfu_ns.loadGrid()", 50);
        return;
    }
    
    this.db = google.gears.factory.create('beta.database');
    
    var currObj = this;
    function assignEventHandlers(currObj) // Creates a new closure for each object so its event handler has its own version of "this". (Since "this" accesses "currObj" in the closure.)
    {
        if(document.addEventListener)
        {
            currObj.pageDropDwnTop.addEventListener('click', function(event) {currObj.pageDropDwnClick(event, currObj.pageDropDwnTop); }, false);
            currObj.pageDropDwnBottom.addEventListener('click', function(event) {currObj.pageDropDwnClick(event, currObj.pageDropDwnBottom); }, false);
            currObj.nextPageTopDiv.addEventListener('click', function(event) {currObj.nextPageClick(event); }, false);
            currObj.nextPageBottomDiv.addEventListener('click', function(event) {currObj.nextPageClick(event); }, false);
            currObj.prevPageTopDiv.addEventListener('click', function(event) {currObj.prevPageClick(event); }, false);
            currObj.prevPageBottomDiv.addEventListener('click', function(event) {currObj.prevPageClick(event); }, false);
            currObj.removeFiltersLinkTop.addEventListener('click', function(event) {currObj.removeFiltersClick(event); }, false);
            currObj.removeFiltersLinkBottom.addEventListener('click', function(event) {currObj.removeFiltersClick(event); }, false);
            currObj.resetSortsLinkTop.addEventListener('click', function(event) {currObj.resetSortsClick(event); }, false);
            currObj.resetSortsLinkBottom.addEventListener('click', function(event) {currObj.resetSortsClick(event); }, false);
        }
        else if(document.attachEvent) // IE
        {
            currObj.pageDropDwnTop.attachEvent('onclick', function(event) {currObj.pageDropDwnClick(event, currObj.pageDropDwnTop); }, false);
            currObj.pageDropDwnBottom.attachEvent('onclick', function(event) {currObj.pageDropDwnClick(event, currObj.pageDropDwnBottom); }, false);
            currObj.nextPageTopDiv.attachEvent('onclick', function(event) {currObj.nextPageClick(event); }, false);
            currObj.nextPageBottomDiv.attachEvent('onclick', function(event) {currObj.nextPageClick(event); }, false);
            currObj.prevPageTopDiv.attachEvent('onclick', function(event) {currObj.prevPageClick(event); }, false);
            currObj.prevPageBottomDiv.attachEvent('onclick', function(event) {currObj.prevPageClick(event); }, false);
            currObj.removeFiltersLinkTop.attachEvent('onclick', function(event) {currObj.removeFiltersClick(event); }, false);
            currObj.removeFiltersLinkBottom.attachEvent('onclick', function(event) {currObj.removeFiltersClick(event); }, false);
            currObj.resetSortsLinkTop.attachEvent('onclick', function(event) {currObj.resetSortsClick(event); }, false);
            currObj.resetSortsLinkBottom.attachEvent('onclick', function(event) {currObj.resetSortsClick(event); }, false);
        }
        // JQuery
        if(currObj.size_to_window === true) {
            $(window).resize( function(e){ currObj.resize(e) } );
            currObj.resize(undefined);
        }
        currObj.show();
    };
    assignEventHandlers(this);
    //$(document).ready(this.resize);

    this.truncateDbTable();
    // load() makes an asyncrnous request, so subsequent code should be placed after the response, not directly after the next line of code below.
    this.load();
}
// End of Grid Class -------------------------------------------


//// GridColumn Class -------------------------------------------
GridColumn.prototype.div;
GridColumn.prototype.name;
GridColumn.prototype.dbName;
GridColumn.prototype.hasSearchBox;
GridColumn.prototype.directionImg;
GridColumn.prototype.formula;

GridColumn.prototype.setDirection = function(asc)
{   
    //alert(asc);
    if(asc)
    {
        this.div.style.backgroundImage = "url('/UserControls/DGrid/dhtmlx/imgs/sort_asc.gif')";
    }
    else
    {
        this.div.style.backgroundImage = "url('/UserControls/DGrid/dhtmlx/imgs/sort_desc.gif')";
    }
}
GridColumn.prototype.clearDirectionArrow = function()
{
    this.div.style.backgroundImage = '';
}

// GridColumn object constructor
function GridColumn(div, name, dbName, hasSearchBox, formula)
{
    this.div = div;
    this.name = name;
    this.dbName = dbName;
    this.hasSearchBox = hasSearchBox;
    this.formula = formula;
}
// End of GridColumn Class -------------------------------------------


function checkForGears()
{
    if (!window.google || !google.gears)
    {
        customConfirm('Almost there. You just need Google Gears to view this data. Press OK to install Gears now.', 
            function(answer)
            {
                if (answer)
                {
                    location.href = 'http://gears.google.com/?action=install&icon_src=http://spyfu.com/images/logo64x62.gif&message=SpyFu.com asks that you install Gears to gain access to its adavanced analysis features.&return=http://' + document.domain;
                }
            });
    // Do not code here or below, asynchronous call made above
    }
    else
    {
        google.gears.factory.getPermission('SpyFu', '/images/logo64x62.gif', 'We need this to load your Smart Search results.');
    }
}

isRightClick = function(e)
{
    var rightclick;
    if (!e)
    {
        var e = window.event;	
    }
    if (e.which) 
    {
        rightclick = (e.which == 3);  
    }
    else if (e.button) 
    {
        rightclick = (e.button == 2);
    }
    return rightclick;
}

// comes from prototype.js; this is simply easier on the eyes and fingers
function elm(id)
{
    return document.getElementById(id);
}

function reference_to( object, property ) {  
   return function() {  
      return object[property];  
   }  
}

function S4() 
{
   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}

function guid() 
{
   return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}

if(document.addEventListener)
{
    window.addEventListener('load', checkBoxCollectionInitAll, false);
}
else if(document.attachEvent) // IE
{
    window.attachEvent('onload', checkBoxCollectionInitAll, false);
}

////// SpyFu namespace, everything should go in this from now on
window.spyfu_ns = new spyfu_ns();
function spyfu_ns() {
    this.is_ie = (navigator.userAgent.indexOf("MSIE") !== -1);
	this.keywordSmartSearch = {
		validate : function () {
		    if(elm('ctl00_Body_chk_competitors').checked || elm('ctl00_Body_chk_contains').checked) {
		        $("form:first").submit();
		    }
			else {
				customAlert('Please check at least one check box specifying competitors also bought or find similar words.');
			}
		}
	}
		
    function include_dom(script_filename) {
        var html_doc = document.getElementsByTagName('head').item(0);
        var js = document.createElement('script');
        js.setAttribute('language', 'javascript');
        js.setAttribute('type', 'text/javascript');
        js.setAttribute('src', script_filename);
        html_doc.appendChild(js);
        return false;
    }
    
    var included_files = new Array();

    function include_once(script_filename) {
        if (!in_array(script_filename, included_files)) {
            included_files[included_files.length] = script_filename;
            include_dom(script_filename);
        }
    }

    function in_array(needle, haystack) {
        for (var i = 0; i < haystack.length; i++) {
            if (haystack[i] == needle) {
                return true;
            }
        }
        return false;
    }
    
    this.loadGrid = function () {
        checkForGears();
        grid1 = new Grid(
            elm('grid_global_name').value
            , 'div_main'
            , 'div_grid'
            , elm('grid_data_url').value
            , parseInt(elm('grid_rows_per_chunk').value)
            , elm('grid_table_name').value
            , true
            , 'div_processingRequest'
            , 'spn_resultCountTop'
            , 'spn_resultCountBottom'
            , 'div_DGridPagingTop'
            , 'div_DGridPagingBottom'
            , 'spn_loadingTop'
            , 'spn_loadingBottom'
            , 'div_DGridMenuBar'
            , 'div_DGridStatusBarTop'
            , 'div_DGridStatusBarBottom'
            , 'div_DGridNextPageTop'
            , 'div_DGridNextPageBottom'
            , 'div_DGridPrevPageTop'
            , 'div_DGridPrevPageBottom'
            , 'select_DGridPageTop'
            , 'select_DGridPageBottom'
            , 'link_DGridRemoveFiltersTop'
            , 'link_DGridRemoveFiltersBottom'
            , 'link_DGridReSortTop'
            , 'link_DGridReSortBottom'
            , 'topOfGrid'
            , elm('grid_ask_after_api_timeout').value
            , 'div_gridParent'
            , elm('grid_allow_sort_by_relevance').value
            , elm('grid_size_to_window').value
            , elm('grid_export_filename').value
            , elm('grid_export_all_url').value);
            
        
        // Google Adwords Export Dialog Stuff
        ZeroClipboard.setMoviePath( '/swf/ZeroClipboard.swf' );
        
        var gridDialogGoogleExport_HelpOverview = new GridDialogHelp(
            'div_DGridDialogGoogleExport_HelpOverview'
            , 'img_DGridDialogGoogleExport_HelpOverviewClose'
            , 'spn_DGridDialogGoogleExport_HelpOverviewText'
            , 'img_DGridDialogGoogleExport_HelpOverview');
        
        var gridDialogGoogleExport = new GridDialogGoogleExport(
              'div_DGridDialogGoogleExport'
            , 'dropdwn_DGridDialogGoogleExport_BidAmount'
            , 'txt_DGridDialogGoogleExport_BidMultiplier'
            , 'chk_DGridDialogGoogleExport_EnableBidAmount'
            , 'dropdwn_DGridDialogGoogleExport_MatchType'
            , 'txt_DGridDialogGoogleExport_DestUrl'
            , 'chk_DGridDialogGoogleExport_EnableDestUrl'
            , 'txtarea_DGridDialogGoogleExport_KeywordList'
            , 'btn_DGridDialogGoogleExport_CopyClipBrd'
            , 'btn_DGridDialogGoogleExport_SaveFile'
            , 'btn_DGridDialogGoogleExport_Close'
            , 'img_DGridDialogGoogleExport_Close');
        // End Google Adwords Export Dialog Stuff

        // Include Filter Dialog Stuff
        var gridDialogIncludeFilter_HelpOverview = new GridDialogHelp(
            'div_DGridDialogIncludeFilter_HelpOverview'
            , 'img_DGridDialogIncludeFilter_HelpOverviewClose'
            , 'spn_DGridDialogIncludeFilter_HelpOverviewText'
            , 'img_DGridDialogIncludeFilter_HelpOverview');

        var gridDialogIncludeFilter = new GridDialogIncludeExcludeFilter(
            grid1
            , 'div_DGridDialogIncludeFilter'
            , 'dropdwn_DGridDialogIncludeFilter_Column'
            , 'txtarea_DGridDialogIncludeFilter_List'
            , 'btn_DGridDialogIncludeFilter_Ok'
            , 'btn_DGridDialogIncludeFilter_Cancel'
            , 'img_DGridDialogIncludeFilter_Close'
            , 'include');
        // End Include Filter Dialog Stuff            

        // Exclude Filter Dialog Stuff
        var gridDialogExcludeFilter_HelpOverview = new GridDialogHelp(
            'div_DGridDialogExcludeFilter_HelpOverview'
            , 'img_DGridDialogExcludeFilter_HelpOverviewClose'
            , 'spn_DGridDialogExcludeFilter_HelpOverviewText'
            , 'img_DGridDialogExcludeFilter_HelpOverview');

        var gridDialogExcludeFilter = new GridDialogIncludeExcludeFilter(
            grid1
            , 'div_DGridDialogExcludeFilter'
            , 'dropdwn_DGridDialogExcludeFilter_Column'
            , 'txtarea_DGridDialogExcludeFilter_List'
            , 'btn_DGridDialogExcludeFilter_Ok'
            , 'btn_DGridDialogExcludeFilter_Cancel'
            , 'img_DGridDialogExcludeFilter_Close'
            , 'exclude');
        // End Exclude Filter Dialog Stuff            

        // Custom Filter Dialog Stuff
        var gridDialogCustomFilter_HelpOverview = new GridDialogHelp(
            'div_DGridDialogCustomFilter_HelpOverview'
            , 'img_DGridDialogCustomFilter_HelpOverviewClose'
            , 'spn_DGridDialogCustomFilter_HelpOverviewText'
            , 'img_DGridDialogCustomFilter_HelpOverview');

        var gridDialogCustomFilter = new GridDialogCustomFilter(
            grid1.addCustomFilter
            , 'div_DGridDialogCustomFilter'
            , 'btn_DGridDialogCustomFilter_AddLevel'
            , 'btn_DGridDialogCustomFilter_DeleteLevel'
            , '' //radio button group id
            , '' //divFilterTable
            , 'btn_DGridDialogCustomFilter_Ok'
            , 'btn_DGridDialogCustomFilter_Cancel'
            , 'img_DGridDialogCustomFilter_Close');
        // End Custom Filter Dialog Stuff            

        // Custom Sort Dialog Stuff
        var gridDialogCustomSort_HelpOverview = new GridDialogHelp(
            'div_DGridDialogCustomSort_HelpOverview'
            , 'img_DGridDialogCustomSort_HelpOverviewClose'
            , 'spn_DGridDialogCustomSort_HelpOverviewText'
            , 'img_DGridDialogCustomSort_HelpOverview');

        var gridDialogCustomSort = new GridDialogCustomSort(
            grid1.addCustomSort
            , 'div_DGridDialogCustomSort'
            , 'btn_DGridDialogCustomSort_Ok'
            , 'btn_DGridDialogCustomSort_Cancel'
            , 'img_DGridDialogCustomSort_Close');
        // End Custom Sort Dialog Stuff

        // Calculated Column Dialog Stuff
        var gridDialogCalculatedColumn_HelpOverview = new GridDialogHelp(
            'div_DGridDialogCalculatedColumn_HelpOverview'
            , 'img_DGridDialogCalculatedColumn_HelpOverviewClose'
            , 'spn_DGridDialogCalculatedColumn_HelpOverviewText'
            , 'img_DGridDialogCalculatedColumn_HelpOverview');

        var gridDialogCalculatedColumn = new GridDialogCalculatedColumn(
            grid1.addCalculatedColumn
            , 'div_DGridDialogCalculatedColumn'
            , 'btn_DGridDialogCalculatedColumn_Ok'
            , 'btn_DGridDialogCalculatedColumn_Cancel'
            , 'img_DGridDialogCalculatedColumn_Close');
        // End Calculated Column Dialog Stuff
            
        var gridToolbar = new GridToolBar(
            'div_DGridMenuBar'
            , grid1
            , 'img_DGridToolbar_IncludeFilter', gridDialogIncludeFilter
            , 'img_DGridToolbar_ExcludeFilter', gridDialogExcludeFilter
            , 'img_DGridToolbar_CustomFilter', gridDialogCustomFilter
            , 'img_DGridToolbar_CustomSort', gridDialogCustomSort
            , 'img_DGridToolbar_ShowHideColumns'
            , 'img_DGridToolbar_CalculatedColumn', gridDialogCalculatedColumn
            , 'img_DGridToolbar_Refresh'
            , 'a_DGridExportToAdwords', gridDialogGoogleExport);
    }
    this.remove_html = function(str) { 		
        if(str && str != '') {
            return str.replace(/<\/?[^>]+(>|$)/g, "");                    
        }
        else {
            return '';
        }
    }
    
    this.string_builder = function()
    {
        this.buffer = new Array;
        this.index = 0;
    }
     
    this.string_builder.prototype.append = function(str)
    {
        this.buffer[this.index++] = str;
    }
     
    this.string_builder.prototype.string = function()
    {
        return this.buffer.join('');
    }
    
    var dgrid_export_all_callback;
    var dgrid_export_edited_callback;
    this.dgrid_export_dialog = function(all_callback, edited_callback) {
        elm('dgrid_export_dialog').style.display = 'block';
        $('#dgrid_cstm_export_all_results_btn').focus();
        $('#dgrid_export_dialog').centerInClient();
        dgrid_export_all_callback = all_callback;
        dgrid_export_edited_callback = edited_callback;
    }
    this.dgrid_export_all = function() {
        dgrid_export_all_callback();
        dgrid_export_hide();
    }
    this.dgrid_export_edited = function() {
        dgrid_export_edited_callback();
        dgrid_export_hide();
    }
    this.dgrid_export_cancel = function() {
        dgrid_export_hide();
    }
    function dgrid_export_hide() {
        elm('dgrid_export_dialog').style.display = 'none';
    }
    this.trim = function(stringToTrim) {
	    return stringToTrim.replace(/^\s+|\s+$/g,"");
	}
	this.KeyPressReturnCancelBubble = function(event) {
        var intKeyCode = (event.keyCode ? event.keyCode : (event.which ? event.which : event.charCode));
        if (intKeyCode == 13) {
            event.cancelBubble = true;
        }
        return true;
    }
    this.catch_key = function(e) 
    {
        var enter = 13;
        var tab = 9;
        var sugg_open = suggestBoxOpen();
        if(!e)
        {
            var e = event;
        }
        keynum = GetKeyNum(e);
        // Don't submit form on enter when pressing enter in suggest box
        if (keynum === enter && sugg_open === true) {
            return spyfu_ns.stifle(e);
        }
        return true; // submit
    }
    
    this.stifle = function(event) {
        event.cancelBubble;
        event.returnValue = false;
        if(this.is_ie === false) {
            event.preventDefault();
        }
        return false;
    }
    
    this.json = function(string) {
        if(string === undefined || string === '') {
            return undefined;
        }
        return eval('(' + string + ')');
    }
}
////// End SpyFu namespace

if(suggestBoxes.length > 0)
{
    if (window.Event) {
        window.captureEvents(Event.KEYDOWN);
        window.onkeydown = spyfu_ns.catch_key;
    }
    else {
        document.onkeydown = spyfu_ns.catch_key;
    }
        
    if(spyfu_ns.is_ie === false) {
        document.onkeypress = spyfu_ns.catch_key;    
    }
}
