// Copyright (c) 2000 internet.com Corp.
// http://www.webreference.com/js/
// License is granted if and only if this entire
// copyright notice is included. By Tomer Shiran.

function launch(newURL, newName, newFeatures, orgName) {
  var remote = open(newURL, newName, newFeatures);
  if (remote.opener == null)
    remote.opener = window;
  remote.opener.name = orgName;
  remote.focus();
  return remote;
}

/**
 * Used to make hover drop downs work in IE for sitewide menues.
 */
sfHover = function() { 
    if ($("nav")) {
        var sfEls = $("nav").getElementsByTagName("LI"); 
        for (var i=0; i<sfEls.length; i++) { 
            sfEls[i].onmouseover=function() { 
                this.className+=" sfhover"; 
            } 
            sfEls[i].onmouseout=function() { 
                this.className=this.className.replace(new RegExp(" sfhover\\b"), ""); 
            } 
        } 
    }
}

/**
 * Fixes a column height issue.
 */
var fixColHeights = function() {
    
    if ($('latestTopicBlock') && $('recentTopicBlock')) {
        var dacols = new Array('latestTopicBlock','recentTopicBlock','sidebar');
    } else {
        var dacols = new Array('content','secondary','sidebar');
    }
    var columnsThatExist = new Array;
      
    dacols.each(function(oneCol){
        if ($(oneCol)) {
            new Insertion.Bottom(oneCol,'<p class="extraspace"></p>');
            columnsThatExist.push("'" + oneCol + "','P'");
        }
    });
    
    if (columnsThatExist.length > 0) {
        eval('P7_equalCols2(0,'+columnsThatExist.toString()+');');
    }
}


/**
 * Used to add onload events
 * Props to: Simon Willison
 * See: http://simon.incutio.com/archive/2004/05/26/addLoadEvent
 */
function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

/*********************************************************************************************
 * Rotate content
 */
var rcTimer = '';
var rcPauseTimer = '';
var rcIndex = 0;
var rcNext = 1;
var rcLoopMode = 'play';
var rcLoopCount = 0;
var prepareRotateContent = function() {
	
	startRotation();
	$('featuredContent').onmouseover = startReading;
	$('featuredContent').onmouseout = stopReading;
	
	$('rcPause').onclick = rcPause;
	
	$('rcPlay').onclick = rcPlay;
	
	$('hpSmaller0').onclick = function() { rcPause(); fadeToFeature(0); rcIndex = 0; }
	$('hpSmaller1').onclick = function() { rcPause(); fadeToFeature(1); rcIndex = 1; }
	$('hpSmaller2').onclick = function() { rcPause(); fadeToFeature(2); rcIndex = 2; }
	
}

var rcPause = function() {
	stopRotation();
	Element.show('rcPlay');
	Element.hide('rcPause');
}

var rcPlay = function() {
	startRotation(1);
	rcLoopCount = 0;
	Element.show('rcPause');
	Element.hide('rcPlay');
}

var startReading = function() {
	if (rcLoopMode == 'play') {
		clearTimeout(rcPauseTimer);
		stopRotation();
		Element.show('rcPauseNotice'+rcIndex);
		rcLoopMode = 'reading';
	}
}

var stopReading = function() {
	if (rcLoopMode == 'reading') {
		rcPauseTimer = setTimeout('new Effect.Fade(\'rcPauseNotice'+rcIndex+'\');',100);
		startRotation();
	}
}

var stopRotation = function() {
	clearTimeout(rcTimer);
	rcLoopMode = 'pause';
}

var startRotation= function(instant) {
	clearTimeout(rcTimer);
	rcLoopMode = 'play';
	if (instant == 1) {
		showNextFeature();
	} else {
		rcTimer = setTimeout('showNextFeature()',4500+(1000*rcLoopCount));
	}
}

var showNextFeature = function() {
	var next = rcIndex + 1;
	if (next > 2) {
		next = 0;
		rcLoopCount = rcLoopCount + 1;
	}
	fadeToFeature(next,true);
}

function fadeToFeature(index) {
	if (rcIndex != index) {	
		rcNext = index;
		Element.removeClassName('hpSmaller'+rcIndex,'whitebox');
		Element.addClassName('hpSmaller'+index,'whitebox');
		new Effect.Fade('hpFeature'+rcIndex,{ duration: .4, afterFinish: fadeInContent});
		
	}
}

var fadeInContent = function() {
	var options = new Object();
	options.duration = .6;
	if (rcLoopMode == 'play') {
		options.afterFinish = startRotation;
	}
	Element.hide('hpFeature'+rcIndex);
	new Effect.Appear('hpFeature'+rcNext,options);
	rcIndex = rcNext;
	rcNext = '';
}

/*
function rcToggle() {
	if ($('rcControl').innerHTML == 'pause') {
		clearTimeout(rcTimer);
		Element.update('rcControl','play');	
	} else {
		continueRotation();
		Element.update('rcControl','pause');	
	}	
}

var startRotation = function(oXML,json) {
	rcContent = json.items;
	rcIndex = 1;
	rcTimer = setTimeout('rotateContent()',3000);
}
var continueRotation = function() {
	rcTimer = setTimeout('rotateContent()',5000)
}

function loadRotatingContent() {
    var url = baseurl + 'ajax/rotateContent.php';
    var ajax = new Ajax.Request(url,
    {
        method:      'post',
        onSuccess:   startRotation
    });
}

function rotateContent() {
	
	new Effect.Fade('rotatingSpot',{ duration: 1, afterFinish: fadeInContent });
	//new Effect.Fade('rcCurrentIndex',{ duration: 1 });
	//new Effect.Highlight('rotatingSpot',{duration: .25, startcolor: '#ff0000', endcolor: '#fff8ef', restorecolor: '#fff8ef' });
}
*/



/*********************************************************************************************
 * Print with comments?
 */

var prepareToolbox = function() {
	
	addRules({
		
		'#printLink:click' : function(el,ev) {			
			Event.stop(ev);			
			printItem();
		},
		
		'#printwithcomments:click' : function(el,ev) {
			Event.stop(ev);
			printItem('yes');
		},
		
		'#printnocomments:click' : function (el,ev) {
			Event.stop(ev);
			printItem('no');
		},
		
		'#deliciousLink:click' : function (el,ev) {
			Event.stop(ev);			
			window.open('http://del.icio.us/post?v=4&noui&jump=close&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title), 'delicious','toolbar=no,width=700,height=400'); 
		},
		
		'#facebookLink:click' : function(el,ev) {			
			Event.stop(ev);
			
			u=location.href + '?eref=FromFacebookShare';
            t=document.title;
            void(window.open('http://www.facebook.com/sharer.php?u='+encodeURIComponent(u)+'&t='+encodeURIComponent(t),
            'sharer','toolbar=no,width=642,height=435'));           
            
            return false;
		}
	});
	
}

var printItem = function(comments) {
    
    if ($('commentLink')) {
        
        if ($('noCommentNotice')) {
            commentsDisplay('off');
            window.print();
        } else if (comments != 'yes' && comments != 'no') {
            
            Element.hide('shareLink','commentLink');
            $('printLink').onclick = '';
            Element.show('withComments');
            var reset = setTimeout('resetToolbox()',3000);
            
        } else if (comments == 'yes') {
 
            commentsDisplay('on');
            Element.show('shareLink','commentLink');
            Element.hide('withComments');
            $('printLink').onclick = printItem;
            window.print();
            
        } else if (comments == 'no') {

            commentsDisplay('off');
            Element.show('shareLink','commentLink');
            Element.hide('withComments');
            $('printLink').onclick = printItem; 
            window.print();           
        }
    } else {
        window.print();
    }
}

var resetToolbox = function() {
    if ($('withComments')) {
        Element.hide('withComments');
    }
    Element.show('shareLink','commentLink');
    $('printLink').onclick = printItem;
}

function commentsDisplay(direction) {
    
    if ($('toggleCommentBlock') && $('commentBlock')) {
        if (direction == 'on') {
            Element.hide('toggleCommentBlock'); 
            Element.show('commentBlock');
        } else {
            Element.hide('commentBlock'); 
            Element.show('toggleCommentBlock');
        }
    }
}


/*********************************************************************************************
 * Functions that help the ask interview question form to work.
 */

var prepareAskQuestionForm = function() {
    var f = $('askQuestionForm');
    if (f) f.onsubmit = askQuestionSubmit;  
    
    f = $('resetForm');
    if (f) { 
        f.onclick = resetQuestionForm;  
        f.href = '#';
    }
}

/**
 * Check the ask an interview question form.
 */
function checkAskQuestionForm() {
    
    var errors = new Object();
    
    var emailField = $('emailI');
    if (emailField) {
        
        var email = $F(emailField);
        
        if (!email) {
            errors.email = 'Please enter your email address.';
        } else if (!emailCheck(email)) {
            errors.email = 'Invalid Email Address';
        }
    }
    
    var nameField = $('nameI');
    if (nameField) {
        var name = $F(nameField);
        if (!name) {
            errors.name = 'Please enter your name.';
        }
    }
    
    var question = $F('questionI');
    
    if (!question) {
        errors.question = 'Please enter a question!';
    }
    
    return errors;
}

/**
 * Submit ask question form
 */
var askQuestionSubmit = function() {
    
    nclearFormErrors();
    
    if (!nshowFormErrors(checkAskQuestionForm())) {  // check the errors and show them if they exist.
        /**
         * Make the AJAX call
         */
        var url = baseurl + 'ajax/interview.php';
        var ajax = new Ajax.Request(url,
        {
            method:      'post',
            parameters:  Form.serialize('askQuestionForm'),
            onSuccess:   questionAsked,
            onFailure:   questionAskedFailed
        });
        
        $('submitButton').disabled = true;
    } 
    
    return false;    
}

var resetQuestionForm = function() { 
    nclearFormErrors();
    Form.reset('askQuestionForm');
    Effect.Fade('questionSubmittedBlock');
    var t = setTimeout("Effect.Appear('askTheQuestion')",1000);
}

var questionAsked = function(oXML,json) {

    $('submitButton').disabled = false;
    
    Effect.Fade('askTheQuestion');
    var t = setTimeout("Effect.Appear('questionSubmittedBlock'); fixColHeights();",1000);
    
    return true;
}

var questionAskedFailed = function(oXML,json) {

    $('submitButton').disabled = false;
    
    showFormErrors(json.errors);
    
    return true;
}

/*********************************************************************************************
 * Functions that help the Hard Question form to work
 */


/**
 * Highlight every non-empty item of class "formError"
 */
function nhighlightFormErrors() {
    var errorElements = document.getElementsByClassName('formError');
    errorElements.each(function(oneElement) {
        if (oneElement && oneElement.innerHTML != '') {
            new Effect.Highlight(oneElement,{endcolor: '#CCCCCC'});
        }
    }); 
}

/**
 * Clear the errors, remove the error images
 */
function nclearFormErrors(form) {
    form = $(form);
    var errorElements = getElementsByTagNames('label,input,textarea,a',form);
    errorElements.each(function(oneElement) {
        if (oneElement && Element.hasClassName(oneElement,'formError')) {
            if (oneElement.tagName.toLowerCase() == 'a') {
                Element.remove(oneElement);
            } else {
                Element.removeClassName(oneElement,'formError');
            }
        }
    });
}

/**
 * Show the errors by highlighting the labels.
 */
function nshowFormErrors(errors) {
    var foundone = false;
    if (errors) {
        var t = '';
        for (var e in errors) {
            t = $(e+'Label');
            if (t) { 
                //Element.update(t,errors[e]);
                Element.addClassName(t,'formError');
                new Insertion.Bottom(t,'<a href="#" title="'+errors[e]+'" class="formError"><img src="'+imageurl+'error.gif" alt="'+errors[e]+'" /></a>')
                foundone = true;
            }
        }        
        if (foundone) nhighlightFormErrors();
    } 
    return foundone;
}

/**
 * prepare the ask a hard question form for ajax work
 */
var preparehqForm = function() {
    var f = $('askhqForm');
    if (f) f.onsubmit = hqSubmit;  
    
    f = $('resetForm');
    if (f) { 
        f.onclick = resethqForm;  
        f.href = '#';
    }
}

var hqSubmit = function() {
    
    nclearFormErrors('askhqForm');

    if (!nshowFormErrors(checkAskQuestionForm())) {  // check the errors and show them if they exist.

        /**
         * Make the AJAX call
         */
        var url = baseurl + 'ajax/hq.php';
        var ajax = new Ajax.Request(url,
        {
            method:      'post',
            parameters:  Form.serialize('askhqForm'),
            onSuccess:   hqAsked,
            onFailure:   questionAskedFailed
        });
        
        $('submitButton').disabled = true;
        $('submitButton').value = 'Submitting';   
    }
    
    return false;
}

var hqAsked = function(oXML,json) {
    
    $('submitButton').disabled = false;
    $('submitButton').value = 'Submit';
    
    Effect.Fade('askThehq');
    var hqtimer = setTimeout("Effect.Appear('hqSubmittedBlock'); fixColHeights();",1000);
    return true;
    
}

var resethqForm = function() {
    nclearFormErrors('askhqForm');
    
    Form.reset('askhqForm');
    
    //Element.toggle('askTheQuestion','questionSubmittedBlock');
    Effect.Fade('hqSubmittedBlock');
    var t = setTimeout("Effect.Appear('askThehq');",1000);
}

/*********************************************************************************************
 * Functions that help the Comment form to work
 */

/**
 * Check the comment form.
 */
function checkCommentForm() {
    
    // check each element
    var errors = new Object();
    
    var emailField = $('emailText');
    if (emailField) {
        
        var email = $F(emailField);
        
        if (!email) {
            errors.email = 'Please enter your email.';
        } else if (!emailCheck(email)) {
            errors.email = 'Invalid Email Address';
        }
    }
    
    var nameField = $('nameText');
    if (nameField) {
        var name = $F(nameField);
        if (!name) {
            errors.name = 'Please enter your name.';
        }
    }
    
    var comment = $F('commentText');
    
    if (!comment) {
        errors.comment = 'Whoops! Did you forget to comment?';
    }
    
    var previewCode = $F('previewCode');
    if (!previewCode && $F('theaction') == 's') {
        errors.id = 'Invalid submission, you must preview first.';
    }
    
    return errors;
}

var commentPosted = function(oXML,json) {
    
    if (json.valid) {  // comment was really submitted.
        
        new Effect.Fade('commentPreview');
        
        Element.remove('previewedComment');
        
        Form.reset('commentForm');
        
        $('previewCode').value = '';
        
        new Effect.Fade('submitButton');
        
        // show the real comment above.
        commentRefresher();
        
    } else {  // they are previewing a comment.
        
        $('previewCode').value = json.previewCode;  
        
        var pc = $('previewedComment');
        
        var commentHTML = json.commenter+':<br />'+json.body;
        
        if (!pc) {      
            
            new Insertion.Top('commentPreviewList','<li id="previewedComment" class="noDivider">'+commentHTML+'</li>');
        
            new Effect.BlindDown('commentPreview');
            
        } else {
            
            Element.update('previewedComment',commentHTML);
            
            new Effect.Highlight('commentPreview');
            
        }
        
        new Effect.Appear('submitButton');
        
    }
    return true;
}

var commentFailed = function(oXML,json) {
    
    showFormErrors(json.errors);
    
    return true;
}

var commentSubmit = function() {
    clearFormErrors();
    
    if (!showFormErrors(checkCommentForm())) {  // check the errors and show them if they exist.
        
        // no errors were shown, so submit the form!
        
        /**
         * Make the AJAX call
         */
        var url = baseurl + 'ajax/comment.php';
        var ajax = new Ajax.Request(url,
        {
            method:      'post',
            parameters:  Form.serialize('commentForm'),
            onSuccess:   commentPosted,
            onFailure:   commentFailed
        });
        
    }
    return false;
}

var cR;

var prepareCommentForm = function() {
    var f = $('commentForm');
    if (f) f.onsubmit = commentSubmit;  
    
    f = $('previewButton');
    if (f) {
        f.onclick = function() {
            $('theaction').value = 'p';
        }
    }
    
    f = $('submitButton');
    if (f) {
        f.onclick = function() {
            $('theaction').value = 's';
        }
    }
    
    f = $('submitButton2');
    if (f) {
        f.onclick = function() {
            $('theaction').value = 's';
        }
    }
    
    cR = setTimeout('commentRefresher();',4000);
}

/*********************************************************************************************
 * Functions that refresh the comment display
 */

function commentRefresher() {
   //logit('commentRefresher()');
   cR = null; 
   checkForNewComments();
   //cR = setTimeout('commentRefresher();',10000);
}

var checkForNewComments = function() {
    
    // first get a list of comments we already have.
    var commentsListItems = getElementsByTagNames('li',$('commentList'));
    var loadedComments = new Array;
    var item_id = $F('itemID');  // from the comment form
    
    if (commentsListItems && commentsListItems.length > 0) {
        
        commentsListItems.each(function(oneli){
            //logit(oneli);
            if (oneli && oneli.id) {
                //logit(oneli.id);
                loadedComments.push(oneli.id.substr(3));  // get the number out of com123
            }
        });
    }
    
    /**
     * Make the AJAX call
     */
    var url = baseurl + 'ajax/loadComments.php';
    var ajax = new Ajax.Request(url,
    {
        method:      'post',
        parameters:  'loaded=' + loadedComments.toString() + '&id='+item_id,
        onSuccess:   displayComments
    });
    
}

var displayComments = function(oXML,json) {
    if (json.newComments) {
        
        if (json.newComments.length >= 1) {
            var noticeTag = $('noCommentNotice');
            if (noticeTag) {
                Element.remove(noticeTag);
            }
        }

        json.newComments.each(function(comment){
            new Insertion.Bottom('commentList','<li id="com'+comment.id+'"><div class="body">'+comment.body+'<br /><div class="postDate">posted on '+comment.created_on+' by '+comment.commenter+'&nbsp;&nbsp;</div></div><div class="dash"></div></li>');
            new Effect.Highlight('com'+comment.id);
        });
    }
}

/*********************************************************************************************
 * Functions that help the report a Comment links to work
 */

var reportCommentFormSubmit = function() {
    var commentID = $F('commentID');
    if (commentID) {
        // turn off submit button
        Element.hide('r'+commentID);   
        Element.update('confirm'+commentID,'<a href="javascript: confirmOffensive(\'yes\')">Yes</a> or <a href="javascript: confirmOffensive(\'no\')">No</a> -  Are you sure this is offensive?');
    }
    return false;
}

function confirmOffensive(value) {

    var commentID = $F('commentID');
    
    if (value == 'yes') {

        /**
         * Make the AJAX call
         */
        var url = baseurl + 'ajax/flag.php';
        
        var ajax = new Ajax.Request(url,
        {
            method:      'post',
            parameters:  Form.serialize('reportCommentForm'),
            onSuccess:   commentReported
        });
        
    } else {
        Element.update('confirm'+commentID,'');
        Element.show('r'+commentID);
    } 
}

var commentReported = function(oXML,json) {
    if (json.reported) {
        var ricon = $('r'+json.reported);
        if (ricon) {
            
            Element.update('confirm'+json.reported,'reported');
            
        }
    }
}

var prepareReportCommentForm = function() {
    
    var f = $('reportCommentForm');
    if (f) f.onsubmit = reportCommentFormSubmit;
    
    // add the effects onto each report it link.
    
    var ilist = getElementsByTagNames('input',$('commentList'));
    
    ilist.each(function(oneInput){
        if (oneInput) {
            oneInput.onmouseover = function() {
                Element.addClassName(oneInput,'highlight');
                //oneInput.src = imageurl+'report-s.gif';
            }
            oneInput.onmouseout = function() {
                Element.removeClassName(oneInput,'highlight');
                //oneInput.src = imageurl+'report.gif';
            }
            oneInput.onclick = function() {
                $('commentID').value = oneInput.id.substr(1);
            }
        }
    });
}


/*********************************************************************************************
 * WAS THIS HELPFUL?
 */

/**
 * Prepare the "was this helpful" question form
 */
var prepareHelpfulForm = function() {
    var f = $('helpfulForm');
    if (f) f.onsubmit = helpfulFormSubmit;
    
    var t = $('up');
    if (t) {
        t.onmouseover = function() {
            $('up').src = imageurl+'up-s.gif';
        }
        t.onmouseout = function() {
            $('up').src = imageurl+'up.gif';
        }
        t.onclick = function() {
            $('direction').value = 'up';
        }
    }
    
    t = $('down');
    
    if (t) {
        t.onmouseover = function() {
            $('down').src = imageurl+'down-s.gif';
        }
        t.onmouseout = function() {
            $('down').src = imageurl+'down.gif';
        }
        t.onclick = function() {
            $('direction').value = 'down';
        }
    }
}

var helpfulFormSubmit = function() {
    
    Form.disable('helpfulForm');
    
    /**
     * Make the AJAX call
     */
    var url = baseurl + 'ajax/rate.php';
    var ajax = new Ajax.Request(url,
    {
        method:      'post',
        parameters:  Form.serialize('helpfulForm'),
        onSuccess:   helpfulReturned,
        onFailure:   helpfulReturnedError
    });
    
    return false;
}

var helpfulReturned = function(oXML,json) {
    
    if (json.success == 1 && json.direction && (json.direction == 'up' || json.direction == 'down')) {
        
        
        
        var up = $('up');
        var down = $('down');
        
        up.onmouseover = null;
        up.onmouseout = null;
        up.onclick = null;
        
        down.onmouseover = null;
        down.onmouseout = null;
        down.onclick = null;
        
        if (json.direction == 'up') {
            up.src = imageurl+'up-s.gif';
            down.src = imageurl+'down.gif'; 
        } else {
            down.src = imageurl+'down-s.gif';
            up.src = imageurl+'up.gif';
        }
        Element.update('totalVotes',json.totalVotes);
        new Effect.Highlight('helpfulTotals');
        Element.update('totalYes',json.totalYes);
    }    
}

var helpfulReturnedError = function(oXML,json) {
    
    Form.enable('helpfulForm');
    
    if (json.errors) {
        showFormErrors(json.errors);
    }
    return false;
}

/*********************************************************************************************
 * Subscription form
 */

var initSubscribeForm = function() {
	var daForm = $('subscribeForm');	
	
	if (daForm) {
		daForm.onsubmit = submitSubscribeForm;
	}
}

var submitSubscribeForm = function() {
	
	if (!showFormErrors(checkSubscribeForm())) {
		/**
         * Make the AJAX call
         */
        var url = baseurl + 'ajax/subscribeList.php';
        
        var ajax = new Ajax.Request(url,
        {
            method:      'get',
            parameters:  'email=' + escape($F('subscribeEmail')),
            onSuccess:   subscribeFormResults
        });
	}
	
	return false;
}

var subscribeFormResults = function(oXML,json) {
	
	json = eval(oXML.responseText);
	
	if (json.errors) {
		showFormErrors(json.errors);
	} else {
		
		Element.update('formDisplay','<br />Thank you for subscribing. You will be receiving an email asking you to confirm your request.')
	}
}

var checkSubscribeForm = function() {
	
	var errors = new Object;
	
	if (!emailCheck($F('subscribeEmail'))) {
		errors.email = 'Please provide a valid email.';
	}
	
	return errors;
}


/*********************************************************************************************
 * HELPER FUNCTIONS
 */

/**
 * Highlight every non-empty item of class "formError"
 */
function highlightFormErrors() {
    var errorElements = document.getElementsByClassName('formError');
    errorElements.each(function(oneElement) {
        if (oneElement && oneElement.innerHTML != '') {
            new Effect.Highlight(oneElement,{endcolor: '#CCCCCC'});
        }
    }); 
}

/**
 * Clear All items of class "formError"
 */
function clearFormErrors() {
    var errorElements = document.getElementsByClassName('formError');
    errorElements.each(function(oneElement) {
        if (oneElement && oneElement.innerHTML != '') {
            Element.update(oneElement,'');
        }
    }); 
}

/**
 * Given an object of the following type: fieldName=>ErrorMessage
 * Show the errors!
 */
function showFormErrors(errors) {
    var foundone = false;
    if (errors) {
        var t = '';
        for (var e in errors) {
            t = $(e+'Error');
            if (t) { 
                Element.update(t,errors[e]);
                foundone = true;
            }
        }        
        if (foundone) highlightFormErrors();
    } 
    return foundone;
}


/**
 * From: http://javascript.internet.com/forms/email-address-validation.html
 * Modified to fit into our system.
 */ 
function emailCheck (emailStr) {

    /* The following variable tells the rest of the function whether or not
    to verify that the address ends in a two-letter country or well-known
    TLD.  1 means check it, 0 means don't. */
    
    var checkTLD=1;
    
    /* The following is the list of known TLDs that an e-mail address must end with. */
    
    var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/;
    
    /* The following pattern is used to check if the entered e-mail address
    fits the user@domain format.  It also is used to separate the username
    from the domain. */
    
    var emailPat=/^(.+)@(.+)$/;
    
    /* The following string represents the pattern for matching all special
    characters.  We don't want to allow special characters in the address. 
    These characters include ( ) < > @ , ; : \ " . [ ] */
    
    var specialChars="\\(\\)><@,;:\\\\\\\"\\.\\[\\]";
    
    /* The following string represents the range of characters allowed in a 
    username or domainname.  It really states which chars aren't allowed.*/
    
    var validChars="\[^\\s" + specialChars + "\]";
    
    /* The following pattern applies if the "user" is a quoted string (in
    which case, there are no rules about which characters are allowed
    and which aren't; anything goes).  E.g. "jiminy cricket"@disney.com
    is a legal e-mail address. */
    
    var quotedUser="(\"[^\"]*\")";
    
    /* The following pattern applies for domains that are IP addresses,
    rather than symbolic names.  E.g. joe@[123.124.233.4] is a legal
    e-mail address. NOTE: The square brackets are required. */
    
    var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
    
    /* The following string represents an atom (basically a series of non-special characters.) */
    
    var atom=validChars + '+';
    
    /* The following string represents one word in the typical username.
    For example, in john.doe@somewhere.com, john and doe are words.
    Basically, a word is either an atom or quoted string. */
    
    var word="(" + atom + "|" + quotedUser + ")";
    
    // The following pattern describes the structure of the user
    
    var userPat=new RegExp("^" + word + "(\\." + word + ")*$");
    
    /* The following pattern describes the structure of a normal symbolic
    domain, as opposed to ipDomainPat, shown above. */
    
    var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$");
    
    /* Finally, let's start trying to figure out if the supplied address is valid. */
    
    /* Begin with the coarse pattern to simply break up user@domain into
    different pieces that are easy to analyze. */
    
    var matchArray=emailStr.match(emailPat);
    
    if (matchArray==null) {
    
        /* Too many/few @'s or something; basically, this address doesn't
        even fit the general mould of a valid e-mail address. */
        
        //alert("Email address seems incorrect (check @ and .'s)");
        return false;
    }
    var user=matchArray[1];
    var domain=matchArray[2];
    
    // Start by checking that only basic ASCII characters are in the strings (0-127).
    
    for (i=0; i<user.length; i++) {
            if (user.charCodeAt(i)>127) {
            //alert("Ths username contains invalid characters.");
            return false;
       }
    }
    
    for (i=0; i<domain.length; i++) {
        if (domain.charCodeAt(i)>127) {
            //alert("Ths domain name contains invalid characters.");
            return false;
        }
    }
    
    // See if "user" is valid 
    
    if (user.match(userPat)==null) {
    
        // user is not valid
        //alert("The username doesn't seem to be valid.");
        return false;
    }
    
    /* if the e-mail address is at an IP address (as opposed to a symbolic
    host name) make sure the IP address is valid. */
    
    var IPArray=domain.match(ipDomainPat);
    if (IPArray!=null) {
    
        // this is an IP address
        
        for (var i=1;i<=4;i++) {
            if (IPArray[i]>255) {
                //alert("Destination IP address is invalid!");
                return false;
            }
        }
        return true;
    }
    
    // Domain is symbolic name.  Check if it's valid.
     
    var atomPat=new RegExp("^" + atom + "$");
    var domArr=domain.split(".");
    var len=domArr.length;
    for (i=0;i<len;i++) {
        if (domArr[i].search(atomPat)==-1) {
            //alert("The domain name does not seem to be valid.");
            return false;
        }
    }
    
    /* domain name seems valid, but now make sure that it ends in a
    known top-level domain (like com, edu, gov) or a two-letter word,
    representing country (uk, nl), and that there's a hostname preceding 
    the domain or country. */
    
    if (checkTLD && domArr[domArr.length-1].length!=2 && domArr[domArr.length-1].search(knownDomsPat)==-1) {
        //alert("The address must end in a well-known domain or two letter " + "country.");
        return false;
    }
    
    // Make sure there's a host name preceding the domain.
    
    if (len<2) {
        //alert("This address is missing a hostname!");
        return false;
    }
    
    // If we've gotten this far, everything's valid!
    return true;
}

/* 
  ------------------------------------------------
  PVII Equal CSS Columns scripts -Version 2
  Copyright (c) 2005 Project Seven Development
  www.projectseven.com
  Version: 2.1.0
  ------------------------------------------------
*/
function P7_colH2(){ //v2.1.0 by PVII-www.projectseven.com
    var i;                  // used for looping
    var oh;                 // height of current element in loop
    var h=0;                // tallest column
    var tg;
    var el;
    var np;                 // difference between height of current element and tallest.
    var dA=document.p7eqc   // all of the elements including columns and last tag in child.
    var an=document.p7eqa;  // if called a second time, then don't animate it.
    var hp;
    var ft=0;               // farthest to top of page
    var oa;                 // cumulative Offset array
      
    if(dA&&dA.length) {
        for(i=1;i<dA.length;i+=2){
            dA[i+1].style.paddingBottom='0px';  // set the last child element's padding to 0;
        }
        
        for(i=1;i<dA.length;i+=2) {  // find the column that is closest to the top
            oa = Position.cumulativeOffset(dA[i]);
            if (ft<oa[1]) {
                ft = oa[1];
            }
        }
        //logit('farthest from top: ' + ft);
        
        for(i=1;i<dA.length;i+=2){            
            oh=dA[i].offsetHeight;      // get the height of the tallest column;
            if(oh>h) {
               h = oh;
               hp = dA[i+1];
               
            }
        }
        Element.hide(hp);
        //logit('Hiding filler '+hp.id);
        //h = h + 1;
        //logit('Tallest: '+h);
        //logit('Now tallest is: '+h);
        for(i=1;i<dA.length;i+=2){
            oh=dA[i].offsetHeight;      // height of each column
            if(oh<h){                   // if it's less, find the difference.
                oa = Position.cumulativeOffset(dA[i]);
                np=(h-oh)+(ft-oa[1]);   // difference plus the difference at the top            
                if(!an&&dA[0]==1){
                    P7_eqA2(dA[i+1].id,0,np);  // animated fill
                }else{ 
                    dA[i+1].style.paddingBottom=np+"px";  // just add the padding
                    //logit('Added '+np+'px to '+dA[i].id+' that was '+oh+' high');
                }
            }
        }
        document.p7eqa=1;  // this has been called so never animate again.
        document.p7eqth=document.p7parent.offsetHeight;
        document.p7eqtw=document.p7parent.offsetWidth;
        
        if (document.p7t < 5) {
            //logit('offset height: '+document.p7parent.offsetHeight);
            //logit('offset width: '+document.p7parent.offsetWidth);
            document.p7t++;
        }
    }
}
function P7_eqT2(){ //v2.1.0 by PVII-www.projectseven.com
    if(document.p7eqth!=document.p7parent.offsetHeight||document.p7eqtw!=document.p7parent.offsetWidth){
        P7_colH2();
    }
}
function P7_equalCols2(){ //v2.1.0 by PVII-www.projectseven.com
    var c;  // the column
    var e;  // all the elments inside the colum
    var el; // the last element in the column
    if(document.getElementById){
        
        document.p7eqc=new Array();       // create an array to hold the elements
        document.p7eqc[0]=arguments[0];   // animate = yes or no.
        document.p7t = 0;
        document.p7parent = $('main');
        for(i=1;i<arguments.length;i+=2){ // step through 'colid','P' of each argument.
            el=null;
            c=document.getElementById(arguments[i]); // get the column
            
            if(c){
                e=c.getElementsByTagName(arguments[i+1]); // get the all the elements
                if(e){
                    el=e[e.length-1];  // pull out the last one.
                    if(!el.id){
                        el.id="p7eq"+i; // set the id of that tag
                    }
                }
            }
            if(c&&el){
                document.p7eqc[document.p7eqc.length]=c;   // append these on in order
                document.p7eqc[document.p7eqc.length]=el;
            }
        }
        //logit('offset height: '+document.p7parent.offsetHeight);
        //logit('offset width: '+document.p7parent.offsetWidth);
        setInterval("P7_eqT2()",10);  // call this every 10 milliseconds
    }
}
function P7_eqA2(el,p,pt){ //v2.1.0 by PVII-www.projectseven.com
    var sp=10;
    var inc=20;
    var g=document.getElementById(el);
    np = (p>=pt)?pt:p;
    g.style.paddingBottom = np+"px";
    if(np<pt){
        np+=inc;
        setTimeout("P7_eqA2('"+el+"',"+np+","+pt+")",sp);
    }
}


/**
 * Logging function to help me know what is going on.
 */
var logindex = 1;
function logit(msg) {
    
    //if (!$F('disableLog')) {
        new Insertion.Top('logdiv','<p><b>' + logindex + ':</b> ' + msg + '</p>');
        logindex++;
    //}   
}

function getElementsByTagNames(list,obj)
{
	if (!obj) var obj = document;
	var tagNames = list.split(',');
	var resultArray = new Array();
	for (var i=0;i<tagNames.length;i++)
	{
		var tags = $A(obj.getElementsByTagName(tagNames[i]));
		tags.each(function(oneTag){
		    resultArray.push(oneTag);
		});
	}
	if (resultArray[0]) {
    	var testNode = resultArray[0];
    	if (testNode.sourceIndex)
    	{
    		resultArray.sort(function (a,b) {
    				return a.sourceIndex - b.sourceIndex;
    		});
    	}
    	else if (testNode.compareDocumentPosition)
    	{
    		resultArray.sort(function (a,b) {
    				return 3 - (a.compareDocumentPosition(b) & 6);
    		});
    	}
	}
	return resultArray;
}

/**********************************************************************************************
 * Sorting functions
 */
var SORT_COLUMN_INDEX;

function sortables_init() {
    // Find all tables with class sortable and make them sortable
    if (!document.getElementsByTagName) return;
    tbls = document.getElementsByTagName("table");
    for (ti=0;ti<tbls.length;ti++) {
        thisTbl = tbls[ti];
        if (((' '+thisTbl.className+' ').indexOf("sortable") != -1) && (thisTbl.id)) {
            //initTable(thisTbl.id);
            ts_makeSortable(thisTbl);
        }
    }
}

function ts_makeSortable(table) {
    if (table.rows && table.rows.length > 0) {
        var firstRow = table.rows[0];
    }
    if (!firstRow) return;
    
    // We have a first row: assume it's the header, and make its contents clickable links
    for (var i=0;i<firstRow.cells.length;i++) {
        var cell = firstRow.cells[i];
        var txt = ts_getInnerText(cell);
        cell.innerHTML = '<a href="#" class="sortheader" '+ 
        'onclick="ts_resortTable(this, '+i+');return false;">' + 
        txt+'<span class="sortarrow">&nbsp;&nbsp;&nbsp;</span></a>';
    }
}

function ts_getInnerText(el) {
	if (typeof el == "string") return el;
	if (typeof el == "undefined") { return el };
	if (el.innerText) return el.innerText;	//Not needed but it is faster
	var str = "";
	
	var cs = el.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		switch (cs[i].nodeType) {
			case 1: //ELEMENT_NODE
				str += ts_getInnerText(cs[i]);
				break;
			case 3:	//TEXT_NODE
				str += cs[i].nodeValue;
				break;
		}
	}
	return str;
}

function ts_resortTable(lnk,clid) {
    // get the span
    var span;
    for (var ci=0;ci<lnk.childNodes.length;ci++) {
        if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span') span = lnk.childNodes[ci];
    }
    var spantext = ts_getInnerText(span);
    var td = lnk.parentNode;
    var column = clid || td.cellIndex;
    var table = getParent(td,'TABLE');
    
    // Work out a type for the column
    if (table.rows.length <= 1) return;
    var itm = ts_getInnerText(table.rows[1].cells[column]);
    sortfn = ts_sort_caseinsensitive;
    if (itm.match(/^\d\d[\/\.-]\d\d[\/\.-]\d\d\d\d$/)) {
    	sortfn = ts_sort_date; 
    } else if (itm.match(/^[\d\.]+$/)) {
    	sortfn = ts_sort_numeric; 
    }
    if (itm.match(/^\d\d[\/\.-]\d\d[\/\.-]\d\d$/)) sortfn = ts_sort_date;
    if (itm.match(/^[£$]/)) sortfn = ts_sort_currency;
    
    SORT_COLUMN_INDEX = column;
    var firstRow = new Array();
    var newRows = new Array();
    for (i=0;i<table.rows[0].length;i++) { firstRow[i] = table.rows[0][i]; }
    for (j=1;j<table.rows.length;j++) { newRows[j-1] = table.rows[j]; }

    newRows.sort(sortfn);

    if (span.getAttribute("sortdir") == 'down') {
        ARROW = '&nbsp;&nbsp;&uarr;';
        newRows.reverse();
        span.setAttribute('sortdir','up');
    } else {
        ARROW = '&nbsp;&nbsp;&darr;';
        span.setAttribute('sortdir','down');
    }
    
    // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
    // don't do sortbottom rows
    for (i=0;i<newRows.length;i++) { if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) table.tBodies[0].appendChild(newRows[i]);}
    // do sortbottom rows only
    for (i=0;i<newRows.length;i++) { if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1)) table.tBodies[0].appendChild(newRows[i]);}
    
    // Delete any other arrows there may be showing
    var allspans = document.getElementsByTagName("span");
    for (var ci=0;ci<allspans.length;ci++) {
        if (allspans[ci].className == 'sortarrow') {
            if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
                allspans[ci].innerHTML = '&nbsp;&nbsp;&nbsp;';
            }
        }
    }
        
    span.innerHTML = ARROW;
}

function getParent(el, pTagName) {
	if (el == null) return null;
	else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase())	// Gecko bug, supposed to be uppercase
		return el;
	else
		return getParent(el.parentNode, pTagName);
}
function ts_sort_date(a,b) {
    // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa.length == 10) {
        dt1 = aa.substr(6,4)+aa.substr(0,2)+aa.substr(3,2); //for MM.DD.YYYY
    } else {
        yr = aa.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt1 = yr+aa.substr(3,2)+aa.substr(0,2);
    }
    if (bb.length == 10) {
        dt2 = bb.substr(6,4)+bb.substr(0,2)+bb.substr(3,2);
    } else {
        yr = bb.substr(6,2);
        if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }
        dt2 = yr+bb.substr(3,2)+bb.substr(0,2);
    }
    if (dt1==dt2) return 0;
    if (dt1<dt2) return -1;
    return 1;
}

function ts_sort_currency(a,b) { 
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
    return parseFloat(aa) - parseFloat(bb);
}

function ts_sort_numeric(a,b) { 
    aa = parseFloat(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
    if (isNaN(aa)) aa = 0;
    bb = parseFloat(ts_getInnerText(b.cells[SORT_COLUMN_INDEX])); 
    if (isNaN(bb)) bb = 0;
    return aa-bb;
}

function ts_sort_caseinsensitive(a,b) {
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
}

function ts_sort_default(a,b) {
    aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
    bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
    if (aa==bb) return 0;
    if (aa<bb) return -1;
    return 1;
}

// use this wherever we want to add new rules
function addRules(obj) {
    var new_obj = {};
    [obj, rule_basket].each ( function (obj) {
        for (method in obj) { 
            new_obj[method] = obj[method]; 
        }
    });
    rule_basket = new_obj;
}