// LXP - 04/05/2010

// do: no ajaxrows or sql sort ordering being used - should this be final?
// do: maybe add support for scrolling tables if too many columns?
// do: silly white area below other content when making a popup box - no idea why - scrollbar?

var xmlDoc;
var jsEvalList;     // large string holding javascript code to be evaluated - normally passed from sql
var extraRowClasses;
var rowID;

var IDList = new Array();   // not used by the popups

// list of all tables created
var tableList = new Array();

// should the tables get numbered rows?
var tablesNumberedRows = false;




$(document).ready(function()
{
    // run/setup bdf related functions
    bdfProcessing();

    // add the sorting functions for uk date for datatables
    jQuery.fn.dataTableExt.oSort['uk_date-asc']  = function(a,b){
        var ukDatea = a.split('/');
        var ukDateb = b.split('/');

        //Treat blank/non date formats as highest sort
        if (isNaN(parseInt(ukDatea[0])))
                return 1;

        if (isNaN(parseInt(ukDateb[0])))
                return -1;

        var x = (ukDatea[2] + ukDatea[1] + ukDatea[0]) * 1;
        var y = (ukDateb[2] + ukDateb[1] + ukDateb[0]) * 1;

        return ((x < y) ? -1 : ((x > y) ?  1 : 0));
    };

    jQuery.fn.dataTableExt.oSort['uk_date-desc'] = function(a,b){
        var ukDatea = a.split('/');
        var ukDateb = b.split('/');

        //Treat blank/non date formats as highest sort
        if (isNaN(parseInt(ukDatea[0])))
                return -1;

        if (isNaN(parseInt(ukDateb[0])))
                return 1;

        var x = (ukDatea[2] + ukDatea[1] + ukDatea[0]) * 1;
        var y = (ukDateb[2] + ukDateb[1] + ukDateb[0]) * 1;

        return ((x < y) ? 1 : ((x > y) ?  -1 : 0));
    };

    // add uk date sorting detection - must add to the beginning of the list otherwise normal date sorting will be used
    jQuery.fn.dataTableExt.aTypes.unshift (function (sortData){
        if (sortData.match(/^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/(19|20|21)\d\d$/))
            return 'uk_date';
        else
            return null;
    });

    // set table tools swf location   
    TableToolsInit.sSwfPath = "jquery/swf/zeroclipboard.swf";

    // finally run setup code specific to this page
    setupPage();
    // apply dataTable to any BDF grid where class = "bdfGrid"
    bdfGrid2dataTable();
});

//*****************************************************************************
//  Dummy immplimentations for functions that needs to be overridden on the page
//*****************************************************************************

function bdfProcessing()
{   
   $(".jqDateCtrl").datepicker();

} 

function setupPage()
{
}

function setupPopup (title, params)
{
}

function cleanupPopup ()
{
}

// get the params string for the stored procedure based on filters used
function getParams()
{
    return '';
}

function canRightAlignColumn (columnName)
{
    return false;
}

function canBoldColumn (columnName)
{
    return false;
}

// returns whether the given table is part of a popup
function isTablePopup (tableDivID)
{
    return false;
}

// get the initial column to sort by for given table
function getTableInitialSortBy (tableDivID)
{
    return 0;
}

// are the rows for the given table selectable
function isTableRowsSelectable (tableDivID)
{
    return false;
}

// this is run if rows are selectable and when user clicks on row
function handleTableSelectedRow (tableDivID, tableRow)
{
}





// clickID is the id of the link for this particular action
// tableDivID is the id for the div that will hold the table
// popupDivID is the overall div that forms the popup
function setupPopupClick(clickID, popupDivID, title, params)
{
    $(clickID).colorbox
        ({
            width: $('#colorbox').css('min-width'),
            height: $('#colorbox').css('min-height'),
            inline: true,
            overlayClose: false,
            href: popupDivID,
            speed: 200,
            onLoad:function()
            {
                setupPopup (title, params);
            },
            onCleanup:function()
            {
                cleanupPopup();
            }
        });
}


function startTableSearch (procTag, params, tableDivID)
{
    // show the ajax loading animation
    $(tableDivID + '_AJAXNOTIFICATION').css('visibility','visible');

    // hide buttons while searching
    $(tableDivID + '_BTNSEARCH').css('visibility','hidden');
    $(tableDivID + '_BTNSEARCH2').css('visibility','hidden');
    $(tableDivID + '_BTNUPDATE').css('visibility','hidden');
    $(tableDivID + '_BTNREMOVE').css('visibility','hidden');

    // must first completely reset what is displayed in the content holder since otherwise the
    // jquery datatable will insert the modified table inside the previous table
    // - do it here so the old data is not briefly displayed before the new data appears
    $(tableDivID + '_CONTENTHOLDER').html('<div id = "' + tableDivID.substring(1, tableDivID.length) + '_CONTENT"></div>');
    
//a=prompt('',params);

    // send the tableDivID as the context
    callAjax(procTag, params, tableDivID);
}



//*****************************************************************************
//                 RESULTS FUNCTIONS
//*****************************************************************************

// normally the ajaxContext will be the table where the data will go - for normal field loading use _load_fields_
// special sql column names: _addID_ , _rowID_ , _extraRowClasses_ , _Sel., _eval_xxx, anything beginning with _


// tableDivID is the ajaxContext
function handleAJAXResults (xmlResult, ajaxContext)
{
    parseXML(xmlResult);

    if (ajaxContext == '_load_fields_')             // load fields directly from the xml - would expect 1 sql row returned
    {
        finishLoadingFields();
    }
    else if (ajaxContext == '_letters_and_certs_')  // response from letters and certs to say job has been scheduled...
    {
        finishLettersAndCerts();
    }
    else if (ajaxContext.substring(0,2) == '__')    // special processing not covered by the other options if the first 2 characters are __
    {
        finishSpecialLoading(ajaxContext);
    }
    else                                            // load tables - ajaxContext is the name of the table wrapper div
    {
        var divID = ajaxContext;

        $(divID + '_AJAXNOTIFICATION').css('visibility','hidden');

        finishTableSearch(divID);

        $(divID + '_BTNSEARCH').css('visibility','visible');
        $(divID + '_BTNSEARCH2').css('visibility','visible');
        $(divID + '_BTNUPDATE').css('visibility','visible');
        $(divID + '_BTNREMOVE').css('visibility','visible');
    }
}

// this loads the returned values into fields with ids matching the sql field names - expecting just 1 sql row returned
function finishLoadingFields()
{
    var nodes = xmlDoc.documentElement.childNodes;
    var i, j, nodeName, nodeValue;

    // loop through root nodes
    for (i = 0; i < nodes.length; i ++)
    {
        if (nodes[i].nodeType != 3 && nodes[i].nodeName == 'Results')
        {
            for (j = 0; j < nodes[i].childNodes.length; j ++)
            {
                if (nodes[i].childNodes[j].nodeType != 3)
                {
                    nodeName = nodes[i].childNodes[j].nodeName;
                    nodeValue = (nodes[i].childNodes[j].hasChildNodes()) ? nodes[i].childNodes[j].childNodes[0].nodeValue : '';

                    if (nodeValue && $('#' + nodeName.toUpperCase()).attr('type') == 'checkbox')
                        $('#' + nodeName.toUpperCase()).attr('checked', 'checked');
                    else
                        $('#' + nodeName.toUpperCase()).val (nodeValue);
                }
            }
        } 
    }  
}

// this handles the returned error/success code from letters and certs
function finishLettersAndCerts()
{
}

// this handles other information being returned
function finishSpecialLoading(ajaxContext)
{
}

// this handles the creation of a table based upon the returned sql results
function finishTableSearch(tableDivID)
{
    jsEvalList = '';
    extraRowClasses = '';

    // only clear the list if not a popup
    if (!isTablePopup(tableDivID))
        IDList.length = 0;

    // tableDiv is the same as tableDivID but with the # removed from the beginning
    var tableDiv = tableDivID.substring(1, tableDivID.length);

    var res = '';
    var resHead = '';
    var resData = '';
    var resFoot = '';
    var rowData = '';
    var tableColumnsNo = 0;
    var rowNo = 0;

    var nodes = xmlDoc.documentElement.childNodes;
    var i, j;

    if (tablesNumberedRows)
        resHead = '<th>#</th>';

    // loop through root nodes
    for (i = 0; i < nodes.length; i++)
    {
        if (nodes[i].nodeType != 3 && nodes[i].nodeName == 'Results')
        {
            // loop through children of 'Results'
            rowData = '';
            rowID = '';
            for (j = 0; j < nodes[i].childNodes.length; j ++)
            {
                if (nodes[i].childNodes[j].nodeType != 3)
                {
                    // row header for table - if this is the first row then can add the column titles as well
                    if (rowNo == 0)
                    {
                        resHead += headerEntry (tableDiv, nodes[i].childNodes[j].nodeName);
                        tableColumnsNo ++;
                    }

                    // add data entry
                    if (nodes[i].childNodes[j].hasChildNodes())
                        rowData += dataEntry (tableDiv, nodes[i].childNodes[j].nodeName, nodes[i].childNodes[j].childNodes[0].nodeValue);
                    // if no data then add empty entry
                    else
                        rowData += dataEntry (tableDiv, nodes[i].childNodes[j].nodeName, '');
                }
            }

            // make the complete row
            if (rowData != '')
            {
                if (tablesNumberedRows)
                    rowData = '<td class="' + tableDiv + '_TD">' + rowNo + '</td>' + rowData;
                resData += '<tr class="' + tableDiv + '_TR ' + extraRowClasses + '"';
                if (rowID != '')
                    resData += ' id="' + rowID + '"';
                resData += '>' + rowData + '</tr>'
                rowNo ++;
                extraRowClasses = '';
            }

        } // if (nodes[i].nodeType!=3&&nodes[i].nodeName=='Results')

    }  //for (i=0;i<y.length;i++)

    // make sure atleast 1 table head element so jquery datatables accepts its an empty but valid table
    if (resHead == '')
        resHead = '<th></th>';

    // make the final table from the data
    resHead = '<thead id="' + tableDiv + '_THEAD"><tr class="' + tableDiv + '_TR">' + resHead + '</tr></thead>';
    resData = '<tbody id="' + tableDiv + '_TBODY">' + resData + '</tbody>';
    // table must use the display class to correctly display
    res = '<table id="' + tableDiv + '_TABLE" class="display">' + resHead + resData + resFoot + '</table>';

    displayResultsInTable(tableDivID, res, tableColumnsNo);
}


function headerEntry(tableDiv, colhead)
{
    var i;
    var hClass = '';
    var resHead = '';

    // ignore columns that start with _
    if (colhead.substring(0,1) != '_' || colhead.substring(0,6) == '_eval_')
    {
        // special heading replaced with space
        if (colhead == 'Sel._')
            colhead = '&nbsp;';
        else if (colhead.substring(0,6) == '_eval_')
            colhead = colhead.substring(6,colhead.length);

        // if the heading has a _ then split over multiple lines
        var ch = colhead.split('_');
        for (i = 0; i < ch.length; i ++)
        {
            if (resHead != '')
                resHead += '<br />';
            resHead += ch[i];
        }

        hClass = tableDiv + '_TH';
        if (canRightAlignColumn(colhead))
            hClass += ' right';

        resHead = '<th class="' + hClass + '">' + resHead + '</th>';
    }

    return resHead;
}

function dataEntry(tableDiv, nodeName, nodeValue)
{
    var dclass = '';
    var resData = '';

    if (nodeName == '_addID_')                  // add id to the array
    {
        if (!isTablePopup(tableDiv))
            addIDToList(nodeValue);
    }
    else if (nodeName == '_rowID_')             // set the id for the tr
    {
        rowID = nodeValue;
    }
    else if (nodeName == '_extraRowClasses_')   // specify extra classes for the td
    {
        extraRowClasses = nodeValue;
    }
    else if (nodeName.substring(0,1) != '_' || nodeName.substring(0,6) == '_eval_')    // ignore columns that start with a _
    {
        dclass = tableDiv + '_TD';
        if (canRightAlignColumn(nodeName))
            dclass += ' right';
        if (canBoldColumn(nodeName))
            dclass += ' bold';

        resData = '<td class="' + dclass + '">';

        // if the column is ref. then run the code passed in - this will also change the nodeValue
       if (nodeName.substring(0,6) == '_eval_')
            eval(nodeValue);

        if (nodeValue == '') nodeValue = '&nbsp;';
        resData += nodeValue + '</td>';
    }

    return resData;
}


// there can be 2 tables - the main table and the popup table identified by POPUP in its name
function displayResultsInTable(tableDivID, res, tableColumnsNo)
{
    var isPopup = isTablePopup(tableDivID);

    // add the results into the results box - id looking for will be tableDiv + '_CONTENT'
    $(tableDivID + '_CONTENT').html(res);

    // set the column to sort by
    var initialSortBy = getTableInitialSortBy (tableDivID);
    if (initialSortBy > tableColumnsNo)
        initialSortBy = 0;

    // setup datatables
    tableList[tableDivID + '_TABLE'] = $(tableDivID + '_TABLE').dataTable
    ({
        'bPaginate': !isPopup,
        'sPaginationType': 'full_numbers',
        'sDom': '<"top"plf>rt<"bottom"piT>',
        'iDisplayLength': 25,
        'aaSorting': [[ initialSortBy, 'asc' ]]
    });

	// Add a click handler to the rows
    if (isTableRowsSelectable (tableDivID))
    {
        $(tableDivID + '_TABLE tbody').click(function(event) {
            // first remove selection from all other rows
            $(tableList[tableDivID + '_TABLE'].fnSettings().aoData).each(function() {
                $(this.nTr).removeClass('row_selected');
            });
            $(event.target.parentNode).addClass('row_selected');
            
            // external code to handle the row selected
            handleTableSelectedRow(tableDivID + '_TABLE', this.nTr);
        });
    }
    
    // the checkboxes add/remove entries from the id list - setup the click behaviour
    $('.selectedIDList').click(function(){
       if ($(this).is(':checked'))
           addIDToList ($(this).attr('id'));
       else
           removeIDFromList ($(this).attr('id'));
    });

    // now the table is made, evaluate the javascript code that was passed -
    // this will add all the code for loading colorboxes since the table and hence
    // <a> tags didnt actually exist when the jsEvalList string was being created
    // while loading the data for example
    eval(jsEvalList);

    // make any hyperlinks of class button into jquery buttons
    $(tableDivID + '_CONTENT .button').button();

    // make any tooltips into qtips
// Do: enable this when qtip is available for all and add parameters
    //$(tableDivID + '_CONTENT .button').qtip();
    
    // if this is a popup thats running, resize the colorbox now all loaded fully
    if (isPopup)
        $.fn.colorbox.resize();
}



//*****************************************************************************
//                 XML FUNCTIONS
//*****************************************************************************
function parseXML(inXML)
{
    try //Internet Explorer
    {
        xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
        xmlDoc.async = "false";
        xmlDoc.loadXML (inXML);
    }
    catch(e)
    {
        try //Firefox,Mozilla, Opera,etc.
        {
            var parser;
            parser = new DOMParser();
            xmlDoc = parser.parseFromString (inXML, "text/xml");
        }
        catch(e)
        {
            alert(e.message);
            return;
        }
    }
}

function getNode(nodename)
{
    if(xmlDoc.getElementsByTagName (nodename).length < 1)
        return '';
    else if(xmlDoc.getElementsByTagName (nodename)[0].childNodes.length < 1)
        return '';
    else
        return xmlDoc.getElementsByTagName (nodename)[0].childNodes[0].nodeValue;
}



//*****************************************************************************
//                 AUTO APPLY dataTABLE TO BDF GRID CONTROLS
//*****************************************************************************


function bdfGrid2dataTable(){
    
  // auto apply dataTable to BDF Grid where outer div has class "bdfGrid"
 // loop through each element looking for those with class bdfGrid
    // note the class should be specified in the Grid control Outer DIV style
    // also note we don't have a class field for the outer div, so we use a hack to force one
    // by closing the style, setting the class and re-opening the style eg
    // " class="bdfGrid"  style="margin-left:10px;
   $('.bdfGrid').each(function(){
        // get its id
        c=$(this).attr('id');
        
        // get the id of the outer div
        // remove the 'outer' prefix so we get the id of the data grid itself
        gridid = c.substring(5);
        // have we overruled the initial sort order by using a 'dtSort' tag?
        sortcolraw=$('#'+c).attr("dtSort");
        sortcol=(sortcolraw==undefined)?2:sortcolraw;
        sortcol-=1; // simply to adjust column number (humans prefer based on 1) to 0 based column
        // also, have we specified the no of rows
        rowsraw=$('#'+c).attr("dtRows");
        dtrows=(rowsraw==undefined)?25:rowsraw;
        dtrows=dtrows*1.0; // force it to be a number
        //dtrows=10;
        // if the grid id empty, this will cause an error with dataTable, so check for content
        
        if($('#'+gridid).html()!=null){
         // initialise the dataTable
         
         $('#'+c).dataTable
            ({
             'bPaginate': true,
                'sPaginationType': 'full_numbers',
             'sDom': '<"top"plf>rt<"bottom"piT>',
              'iDisplayLength': dtrows,
               'aaSorting': [[ sortcol, 'asc' ]],
                'bAutoWidth':false
            });


            //  (BDF prefixes the id with 'outer' for the id of the outer div
            // get the grid's width so we can set the same width for the dataTable top bar'
            gridwid = $('#'+gridid).css('width');
            // also get the outer div's margin-left
            gridmarginleft =$('#'+c).css('margin-left');
            // the jQuery dataTable plug in puts the BDF div inside one with the same name,
            // suffixed by _wrapper
            w=c+'_wrapper';
            // now loop through all the children div of the _wrapper div
            // looking for class 'top' and 'bottom' to manipulate the header (set width and margin-left)
            // and the footer (hide - not currently needed)
            $('#' + w + ' >  div').each(function(){
                if($(this).hasClass('top')){
                    $(this).css('width',gridwid);
                    $(this).css('margin-left',gridmarginleft);
                }
                if($(this).hasClass('bottom')){
                    $(this).css('display','none');
                }
            })
        }
    })




}






//*****************************************************************************
//                 DOCS & CERTS FUNCTIONS
//*****************************************************************************
function getUniqueJobName(jobPrefix)
{
    var g = ((new Date()).getTime() + "" + Math.floor(Math.random() * 1000000)).substr(0, 18);
    return jobPrefix + '~' + g;
}

function getRunDateTime()
{
    var dt = new Date();
    var newTime = dt.getTime();

    // move on by 5 seconds
    newTime += 5 * 1000;
    dt.setTime(newTime);
    return dt.format('d MMM y HH:mm:ss');
}


//*****************************************************************************
//                 ID LIST FUNCTIONS
//*****************************************************************************
function addIDToList (id)
{
    if (id != 0)
        IDList.push (id);
}

function removeIDFromList (id)
{
    var found = IDList.indexOf(id);
    if (found != -1)
        IDList.remove(found);
}


//*****************************************************************************
//                 HELPER FUNCTIONS
//*****************************************************************************

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to)
{
    var rest = this.slice((to || from) + 1 || this.length);
    this.length = from < 0 ? this.length + from : from;
    return this.push.apply(this, rest);
};


// returns whether the key is allowed for a numeric only input - uses jquery keypress event so takes ascii code not key code
function isNumberCharacter (keyCode)
{
    if (keyCode == 46 || keyCode == 8 ) 
    {
        return true;
    }
    else
    {
        if (keyCode < 95)
        {
            if (keyCode < 48 || keyCode > 57 )
            {
                return false;
            }
        }
        else
        {
            if (keyCode < 96 || keyCode > 105 )
            {
                return false;
            }
        }
    }
    return true;

/*    // allow control keys and backspace and return
    if (character == 0 || character == 8 || character == 10 || character == 13)
        return true;
    // allow ctrl+z,x,c,v,y
    else if (character == 99 || character == 118 || character == 120 || character == 121 || character == 122)
        return true;
    else if (character >= 48 && character <= 57)
        return true;
    else
        return false;
    */
}


//*****************************************************************************
//                 jqueryFileTree FileTree functions
//*****************************************************************************
function FileTree (ftRoot, ftDiv, ftReturnFolder, ftFoldersOnly){
    // pass in the root folder to start searching
    // and the div name where the results should be stored
    // and true to allow folder name to be returned when clicked (only allows 1 level) or false to set folder clicks to browse
    // down a level, and only return when files are clicked
    // this will then call the jqueryFileTree function to
    // display the folder contents
    // there is a callback functions which can be overridden on the local page
    // ftFileClick(filename) which will return the selected filename
    // and can be used as required on the target page

    var scriptFile = '/jqueryFileTree/jqueryFileTree.aspx';
    if (ftFoldersOnly)
        scriptFile = '/jqueryFileTree/jqueryFileTreeFoldersOnly.aspx';
    
    $('#' + ftDiv).fileTree({
            root: ftRoot,
            folderEvent: 'click',
            script: scriptFile,
            multiFolder: false,
            returnFolder: ftReturnFolder,
            foldersOnly: ftFoldersOnly
        },
        function(file) {
     //$('#'+ftDiv).fileTree({root: ftRoot, folderEvent: 'click',script: 'http://si475/jqueryFileTree/jqueryFileTree.aspx',multiFolder: false}, function(file) {
            ftFileClick(file);
        }
    );
}


function ftFileClick(filename){
    // callback function to be overridden
    alert('Chosen file = ' + filename);
}

function insertAtCursor(myField, myValue) {
//IE support

if (document.selection) {
myField.focus();

//in effect we are creating a text range with zero
//length at the cursor location and replacing it
//with myValue
sel = document.selection.createRange();
sel.text = myValue;
}

//Mozilla/Firefox/Netscape 7+ support
//else if (myField.selectionStart ¦¦ myField.selectionStart == '0') {
else if ( myField.selectionStart != '0') {

//Here we get the start and end points of the
//selection. Then we create substrings up to the
//start of the selection and from the end point
//of the selection to the end of the field value.
//Then we concatenate the first substring, myValue,
//and the second substring to get the new value.
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)+ myValue+ myField.value.substring(endPos, myField.value.length);
} else {
myField.value += myValue;
}
}


/*
 *
var catcher = function() {

   var changed = false;
   $('form').each(function() {

     if ($(this).data('initialForm') != $(this).serialize()) {

       changed = true;

       $(this).addClass('changed');

     } else {

       $(this).removeClass('changed');

     }

   });

   if (changed) {

     return 'One or more forms have changed!';

   }
 };



 $(function() {

   $('form').each(function() {

     $(this).data('initialForm', $(this).serialize());

   }).submit(function(e) {

     var formEl = this;

     var changed = false;

     $('form').each(function() {

       if (this != formEl && $(this).data('initialForm') != $(this).serialize()) {

         changed = true;

         $(this).addClass('changed');

       } else {

         $(this).removeClass('changed');

       }

     });

     if (changed && !confirm('Another form has been changed. Continue with submission?')) {

       e.preventDefault();

     } else {

       $(window).unbind('beforeunload', catcher);

     }

   });

   $(window).bind('beforeunload', catcher);

 });

 */


/***************************************************************
**
**   Script by Small Hadron Collider
**   http://www.smallhadroncollider.com
**
**   Distributed under a Creative Commons by-sa License
**   http://creativecommons.org/licenses/by-sa/3.0/
**
**   WARNING: Top 500 passwords array at the bottom contains
**            naughty words. Don't look at it if you're easily
**            offended.
**
***************************************************************/

// Usage: Pass the password string to passwordStrength and string with time it will take is returned

function passwordStrength(password)
{
	// Get password length
	var length = password.length;

	// Check password against common passwords of that length to see if it's in top 500
	if (length > 2 && length < 9)
	{
		for (var i=0; i<arrayOfPasswords[length].length; i++)
		{
			if (password.toLowerCase() == arrayOfPasswords[length][i])
			{
				return 'One of the 500 most common passwords';
			}
		}
	}


	// Calculations per second. Ten million is roughly the number a decent PC could manage uninhibited
	var calculationsPerSecond = 10000000;


	// Keep track of how many character sets are used
	var possibleCharacters = 0;

	// Lowercase
	if (password.match(/[a-z]/)) {possibleCharacters += 26;}

	// Uppercase
	if (password.match(/[A-Z]/)) {possibleCharacters += 26;}

	// Numbers
	if (password.match(/\d+/)) {possibleCharacters += 10;}

	// Symbols
	if (password.match(/[!,@,#,$,%,^,&,*,?,_,~,-,(,)]/) ) {possibleCharacters += 13};


	// Work out the number of possible combinations: possible characters to the power of the password length
	var possibleCombinations = Math.pow(possibleCharacters, password.length);

	// Divide the number of possible combinations by the calculations a PC can do per second
	var computerTimeInSecs = possibleCombinations / calculationsPerSecond;


	// Set up an array of periods with their lenghts in seconds
	var arrayOfPeriods = new Array();
	arrayOfPeriods['minute'] = 60;
	arrayOfPeriods['hour'] = arrayOfPeriods['minute'] * 60;
	arrayOfPeriods['day'] = arrayOfPeriods['hour'] * 24;
	arrayOfPeriods['year'] = arrayOfPeriods['day'] * 365.25;
	arrayOfPeriods['thousand years'] = arrayOfPeriods['year'] * 1000;
	arrayOfPeriods['million years'] = arrayOfPeriods['thousand years'] * 1000;
	arrayOfPeriods['billion years'] = arrayOfPeriods['million years'] * 1000;
	arrayOfPeriods['trillion years'] = arrayOfPeriods['billion years'] * 1000;
	arrayOfPeriods['quadtrillion years'] = arrayOfPeriods['trillion years'] * 1000;
	arrayOfPeriods['quintillion years'] = arrayOfPeriods['quadtrillion years'] * 1000;
	arrayOfPeriods['sextillion years'] = arrayOfPeriods['quintillion years'] * 1000;
	arrayOfPeriods['septillion years'] = arrayOfPeriods['sextillion years'] * 1000;
	arrayOfPeriods['octillion years'] = arrayOfPeriods['septillion years'] * 1000;
	arrayOfPeriods['nonillion years'] = arrayOfPeriods['octillion years'] * 1000;


	// Period type is 'seconds' by default
	var periodType = 'second';
	var strength = '';

	// If it will take less than a second to crack the password, show exactly how long
	if (computerTimeInSecs < 1) {strength = computerTimeInSecs+" seconds";}

	// Otherwise show a rounded-down number
	else
	{

		// Adds the 's' onto anything before a thousand years
		var intoThousands = 's';

		// Adds the 'About' to anything after seconds
		var intoMinutes = '';

		// Round down the time
		var newTime = Math.floor(computerTimeInSecs);

		// Go through different period lengths
		for (var i in arrayOfPeriods)
		{
			// Stop when got to right period
			if (computerTimeInSecs < arrayOfPeriods[i]) {break;}
			else
			{
				if (i == "thousand years") {intoThousands = '';}
				if (i == "minute") {intoMinutes = 'About ';}
				newTime = Math.floor(computerTimeInSecs / arrayOfPeriods[i]);
				periodType = i;
			}
		}

		// If singular
		if (newTime == 1)
		{
			var aType = 'a ';

			// Puts 'an' where needed
			if (periodType == 'hour' || periodType == 'octillion years')
			{
				aType = 'an ';
			}

			strength = "About "+aType+periodType;
		}
		else
		{
			// Convert newTime to string
			newTime += '';

			// Split with commas every three digits
			var regex = /(\d+)(\d{3})/;

			while (regex.test(newTime))
			{
				newTime = newTime.replace(regex, '$1' + ',' + '$2');
			}

			// Combine with 'about' if needed, the time it will take, the period type, and the 's' if needed
			strength = intoMinutes+newTime+" "+periodType+intoThousands;
		}
	}

	return strength;
}


var arrayOfPasswords = new Array();

arrayOfPasswords[3] = ['god','sex'];

arrayOfPasswords[4] = ['1234','cool','1313','star','golf','bear','dave','pass','aaaa','6969','jake','matt','1212','fish','fuck','porn','4321','2000','4128','test','shit','love','baby','cunt','mark','3333','john','sexy','5150','4444','2112','fred','mike','1111','tits','paul','mine','king','fire','5555','slut','girl','2222','asdf','time','7777','rock','xxxx','ford','dick','bill','wolf','blue','alex','cock','beer','eric','6666','jack'];

arrayOfPasswords[5] = ['beach','great','black','pussy','12345','frank','tiger','japan','money','naked','11111','angel','stars','apple','porno','steve','viper','horny','ou812','kevin','buddy','teens','young','jason','lucky','girls','lover','brian','kitty','bubba','happy','cream','james','xxxxx','booty','kelly','boobs','penis','eagle','white','enter','chevy','smith','chris','green','sammy','super','magic','power','enjoy','scott','david','video','qwert','paris','women','juice','dirty','music','peter','bitch','house','hello','billy','movie'];

arrayOfPasswords[6] = ['123456','prince','guitar','butter','jaguar','united','turtle','muffin','cooper','nascar','redsox','dragon','zxcvbn','qwerty','tomcat','696969','654321','murphy','987654','amanda','brazil','wizard','hannah','lauren','master','doctor','eagle1','gators','squirt','shadow','mickey','mother','monkey','bailey','junior','nathan','abc123','knight','alexis','iceman','fuckme','tigers','badboy','bonnie','purple','debbie','angela','jordan','andrea','spider','harley','ranger','dakota','booger','iwantu','aaaaaa','lovers','player','flyers','suckit','hunter','beaver','morgan','matrix','boomer','runner','batman','scooby','edward','thomas','walter','helpme','gordon','tigger','jackie','casper','robert','booboo','boston','monica','stupid','access','coffee','braves','xxxxxx','yankee','saturn','buster','gemini','barney','apples','soccer','rabbit','victor','august','hockey','peanut','tucker','killer','canada','george','johnny','sierra','blazer','andrew','spanky','doggie','232323','winter','zzzzzz','brandy','gunner','beavis','compaq','horney','112233','carlos','arthur','dallas','tennis','sophie','ladies','calvin','shaved','pepper','giants','surfer','fender','samson','austin','member','blonde','blowme','fucked','daniel','donald','golden','golfer','cookie','summer','bronco','racing','sandra','hammer','pookie','joseph','hentai','joshua','diablo','birdie','maggie','sexsex','little','biteme','666666','topgun','ashley','willie','sticky','cowboy','animal','silver','yamaha','qazwsx','fucker','justin','skippy','orange','banana','lakers','marvin','merlin','driver','rachel','marine','slayer','angels','asdfgh','bigdog','vagina','apollo','cheese','toyota','parker','maddog','travis','121212','london','hotdog','wilson','sydney','martin','dennis','voodoo','ginger','magnum','action','nicole','carter','erotic','sparky','jasper','777777','yellow','smokey','dreams','camaro','xavier','teresa','freddy','secret','steven','jeremy','viking','falcon','snoopy','russia','taylor','nipple','111111','eagles','131313','winner','tester','123123','miller','rocket','legend','flower','theman','please','oliver','albert'];

arrayOfPasswords[7] = ['porsche','rosebud','chelsea','amateur','7777777','diamond','tiffany','jackson','scorpio','cameron','testing','shannon','madison','mustang','bond007','letmein','michael','gateway','phoenix','thx1138','raiders','forever','peaches','jasmine','melissa','gregory','cowboys','dolphin','charles','cumshot','college','bulldog','1234567','ncc1701','gandalf','leather','cumming','hunting','charlie','rainbow','asshole','bigcock','fuckyou','jessica','panties','johnson','naughty','brandon','anthony','william','ferrari','chicken','heather','chicago','voyager','yankees','rangers','packers','newyork','trouble','bigtits','winston','thunder','welcome','bitches','warrior','panther','broncos','richard','8675309','private','zxcvbnm','nipples','blondes','fishing','matthew','hooters','patrick','freedom','fucking','extreme','blowjob','captain','bigdick','abgrtyu','chester','monster','maxwell','arsenal','crystal','rebecca','pussies','florida','phantom','scooter','success'];

arrayOfPasswords[8] = ['firebird','password','12345678','steelers','mountain','computer','baseball','xxxxxxxx','football','qwertyui','jennifer','danielle','sunshine','starwars','whatever','nicholas','swimming','trustno1','midnight','princess','startrek','mercedes','superman','bigdaddy','maverick','einstein','dolphins','hardcore','redwings','cocacola','michelle','victoria','corvette','butthead','marlboro','srinivas','internet','redskins','11111111','access14','rush2112','scorpion','iloveyou','samantha','mistress'];



/***********************************************
 *    tooltips
 **********************************************/
function qTooltip(divName,content)
{
    $('#'+divName).qtip({
        content:content,
        style:'cisiTooltips',
        position: {
             type: 'fixed',  
            corner: {
                target: 'topMiddle',
                tooltip: 'bottomLeft'
            }
        }
    })
}





/***********************************************
 *    misc
 **********************************************/

function timeTo(localDate,UTCYear,UTCMonth,UTCDate,UTCHours,UTCMinutes,UTCSeconds){
// calculate the time from now to the specified date
// parameters should be UTC time ie GMT
// compareDate will be in local time

var tgt = new Date()
tgt.setUTCFullYear(UTCYear,UTCMonth-1);
tgt.setUTCMonth(UTCMonth,UTCDate);//months start at zero for January

tgt.setUTCHours(UTCHours,UTCMinutes,UTCSeconds);


diff = tgt - localDate;


if(diff<0){
    diffD=0;
    diffH=0;
    diffM=0;
    diffS=0;
}else{


diffD = Math.floor(diff / (24.0 * 60.0 * 60.0 * 1000.0))
diff-=(diffD * 24.0 * 60.0 * 60.0 * 1000.0);

diffH = Math.floor(diff / (60.0 * 60.0 * 1000.0))
diff-=(diffH * 60.0 * 60.0 * 1000.0);


diffM = Math.floor(diff / (60.0 * 1000.0))

diff-=(diffM * 60.0 * 1000.0);
diffS = Math.floor(diff / (1000.0))
}


//convert to 2 character strings
if(diffD<10){
    diffD='00'+diffD;
}else{
   if(diffD<100){
       diffD='0'+diffD;
   }
}
diffD+='';

if(diffH<10){
    diffH='0'+diffH;
}
diffH+='';

if(diffM<10){
    diffM='0'+diffM;
}
diffM+='';

if(diffS<10){
    diffS='0'+diffS;
}
diffS+='';

return({dd:diffD,hh:diffH,mm:diffM,ss:diffS})





}

var cTimeout=0;
var  cdTimer=0;


function cdtmr(yy,mm,dd,hh,mn,ss){
    this.yy=yy;
    this.mm=mm-1;//months begin at 0 (for January)
    this.hh=hh;
    this.dd=dd;
    this.mn=mn;
    this.ss=ss;
}

function initCountdowntimer(yy,mm,dd,hh,mn,ss){
    cdTimer= new cdtmr(yy,mm,dd,hh,mn,ss)

    cTimeout=setTimeout("countdownTimer()",1000)
}

function countdownTimer(){
cdT=new Date();


res=timeTo(cdT,cdTimer.yy,cdTimer.mm,cdTimer.dd,cdTimer.hh,cdTimer.mn,cdTimer.ss)
$('#tickerd1').html(res.dd.substring(0,1))
$('#tickerd2').html(res.dd.substring(1,2))
$('#tickerd3').html(res.dd.substring(2,3))
$('#tickerh1').html(res.hh.substring(0,1))
$('#tickerh2').html(res.hh.substring(1,2))
$('#tickerm1').html(res.mm.substring(0,1))
$('#tickerm2').html(res.mm.substring(1,2))
$('#tickers1').html(res.ss.substring(0,1))
$('#tickers2').html(res.ss.substring(1,2))
cTimeout=setTimeout("countdownTimer()",1000)
}

/*****************************
 * to use countdown timer, set up divs or table cells
 * that are to hold the remaining days, hours etc
 * ids are tickerd1 (1st char of days = tickerd1 etc)
 *****************************/