
//
// Begin class PageHeaderLink (derives from WSSHTLink)
//

function PageHeaderLink()
{
}
PageHeaderLink.prototype = new WSSHTLink()

//
// Begin class PageFooterLocations
//

function PageFooterLocations()
{
	this.NONE   = 0
	this.LEFT   = 1
	this.CENTER = 2
	this.RIGHT  = 3
}
var g_PageFooterLocations = new PageFooterLocations()

//
// Begin class PageFooterLink (derives from WSSHTLink)
//

function PageFooterLink()
{
    this.nPos = g_PageFooterLocations.CENTER
}
PageFooterLink.prototype = new WSSHTLink()

//
// Begin class PageHeader
//

function addHeaderLink( sLink, sLinkDesc, sTarget, sID )
{
    if ((sLink > '') && (sLinkDesc > '' ))
	{
		var objLink = new PageHeaderLink()
		objLink.sID     = sID
		objLink.sText   = sLinkDesc
		objLink.sTitle  = sLinkDesc
		objLink.sHREF   = sLink
		objLink.sTarget = sTarget
        this.arLinks.addLink(objLink)
	}        
}

function PageHeader()
{
	this.arLinks = new LinkArray() // of PageHeaderLinks
    this.objLogo = new WSSHTLink()

	this.addHeaderLink = addHeaderLink
}

//
// Begin class PageFooter
//

function PageFooter()
{
	this.arLinks = new LinkArray()  // of PageFooterLinks
}

//
// Begin class PageTrail
//
function PageTrail()
{
	this.arLinks = new LinkArray()
}

//
// Begin class PageGrid
//

// PageGrid is a combination of table data and a view for displaying it.
function PageGrid()
{
	this.nSortField    = -1
	this.bSortAscending = true

	this.objDataTable   = null // Contains the data
	this.objDisplayView = null // Contains view for how to represent data
	this.arColumnMap    = null // Contains mapping from view column to data column
	this.fnLinkHandler  = null // Function which handles links clicked inside the grid
}

// Maps the data columns into the display view columns
PageGrid.prototype.mapColumns = function()
{
	// Allocate the array
	this.arColumnMap = new Array()

	// Map the fields
	var arFields = this.objDisplayView.getFields()
	var nNumFields = this.objDisplayView.getNumFields()
	for (var i = 0; i < nNumFields; i++)
	{
		this.arColumnMap[i] = this.objDataTable.getColumnID( arFields[i].sName, arFields[i].bUseLUT )

		// If the column wasn't found (and we have a UseLUT flag set), then try to find the data column
		// with the opposite LUT value set (i.e. let's try to display something - maybe the LUT was dropped since deploy).
		if ( (this.arColumnMap[i] == -1) && (arFields[i].bUseLUT != null) )
			this.arColumnMap[i] = this.objDataTable.getColumnID( arFields[i].sName, !arFields[i].bUseLUT )
		// alert('View column ' + i + ' maps to data column ' + this.arColumnMap[i])

		// Map the sort column if it is one that is displayed
		if ( this.objDataTable.nSortColumn == this.arColumnMap[i] )
		{
			this.nSortField = i
			this.bSortAscending = this.objDataTable.bSortAscending
		}
	}
}

// Returns the current sort column id
PageGrid.prototype.getSortColumnID = function()
{
	return this.nSortField
}

// Returns the current sort column name
PageGrid.prototype.getSortColumn = function()
{
	if ( this.nSortField >= 0 )
		return this.objDisplayView.getField( this.nSortField )
	else
		return null
}

// Returns true if the current sort column is the one specified
PageGrid.prototype.isSortColumn = function(sField)
{
	var sortColumn = this.getSortColumn()
	return ( (sortColumn != null) && (sField == sortColumn.sName) )
}

// Returns true if the current sort direction is ascending
PageGrid.prototype.getSortAscending = function()
{
	return this.bSortAscending
}

// Sorts the data in the PageGrid based on the specified column name
PageGrid.prototype.sortOnColumn = function( sColumnName, bAscending )
{
	// Get the column id for sorting
	var nFieldID = this.objDisplayView.getFieldID( sColumnName )
	if (nFieldID < 0)
		alert('Invalid column for sorting: ' + sColumnName)
	else
		this.sortOnFieldID(nFieldID, bAscending)
}

// Sorts the data in the PageGrid based on the specified column id
PageGrid.prototype.sortOnFieldID = function( nFieldID, bAscending )
{
	// Get the datatype for the column
	var eDataType = this.objDisplayView.arFields[nFieldID].eDataType

	// Determine how we sort
	var eSortType = null
	var bAsc = null
	if ( eDataType == WSSDataTypes.CHARACTER )
	{
		eSortType = g_SortTypes.CHARACTER_CASE_INSENSITIVE
		bAsc = (bAscending == null) ? true: bAscending
	}
	else if ( eDataType == WSSDataTypes.DATE )
	{
		eSortType = g_SortTypes.DATE
		bAsc = (bAscending == null) ? true: bAscending
	}
	else // if ( eDataType == g_WSSDataTypes.NUMBER )
	{
		eSortType = g_SortTypes.NUMBER
		bAsc = (bAscending == null) ? true: bAscending
	}

	// Grab a reference to the underlying DataTable object
	var objData = this.objDataTable

	// Get the physical column we will be sorting based on
	var nSortCol = this.arColumnMap[nFieldID]

	// Get a reference to the field lookup table
	var objLUT = this.objDisplayView.arFields[nFieldID].objLUT

	// Check if the underlying DataTable is already sorted as we
	// intend (this is basically a "first time through" check).
	// If there is a LUT on the sort field, then it isn't going
	// to already be sorted properly.
	if ( (objLUT   == null) &&
	     (nSortCol == objData.nSortColumn) &&
		 (bAsc     == objData.bSortAscending) )
	{
		// We're already sorted the way that we want to be - just
		// fix up our member variables and exit
		this.nSortField     = nFieldID
		this.bSortAscending = bAsc
	}
	else // Only sort if this is not the current sort column and ordering.
	if ( ( nFieldID != this.nSortField ) || ( bAsc != this.bSortAscending ) )
	{
		var sFieldName = this.objDisplayView.arFields[nFieldID].sName

		var nTimeColID = -1
		if ( sFieldName == "CLASS.XSTARTDATE" )
			nTimeColID = objData.getColumnID("CLASS.XSTARTTIME")
		else if ( sFieldName == "CLASS.XENDDATE" )
			nTimeColID = objData.getColumnID("CLASS.XENDTIME")
		else if ( sFieldName == "COURSE.XSTARTDATE" )
			nTimeColID = objData.getColumnID("COURSE.XSTARTTIME")
		else if ( sFieldName == "COURSE.XENDDATE" )
			nTimeColID = objData.getColumnID("COURSE.XENDTIME")

		// Create a new comparison function object
		var objSort = new PageGridSort(nSortCol,
		                               eSortType,
									   bAsc,
									   objLUT,
									   nTimeColID)

		// Do the sort
		mergeSortEx(objData.arData, objSort)

		// Set our member variables to indicate the sorting that was done.
		this.nSortField     = nFieldID
		this.bSortAscending = bAsc

		// Set the DataTable member variables as well...
		// If there is no LUT, then flag the DataTable's sort column
		// to indicate the sort column (which is accurate).
		// Otherwise, set the sort column to -1.
		if ( objLUT == null )
		{
			objData.nSortColumn    = nSortCol
			objData.bSortAscending = bAsc
		}
		else
			objData.nSortColumn    = -1
	}
}

// Sorts the data in the PageGrid based on the first valid data column
PageGrid.prototype.sortOnFirstColumn = function( bAscending )
{
	var nNumFields = this.arColumnMap.length
	for (var i = 0; i < nNumFields; i++)
	{
		if ( this.arColumnMap[i] >= 0 )
		{
			this.sortOnFieldID(i, bAscending)
			break
		}
	}
}

// PageGridSort object used in conjunction with quicksortEx
// to sort the data in the PageGrid.objDataTable object.
function PageGridSort(nColumnID, eSortType, bAscending, objLUT, nTimeColID)
{
	this.nColumnID  = nColumnID
	this.eSortType  = eSortType
	this.bAscending = bAscending
	this.objLUT     = objLUT

	this.nTimeColID = nTimeColID
}

// quicksortEx specifically expects a function name "compare"
// in the "compareObject" passed to it.
PageGridSort.prototype.compare = function(a, b)
{
	// Extract the column values we are comparing.
	var a1 = a[this.nColumnID]
	var b1 = b[this.nColumnID]

	// If there is a lookup table, extract the lookup values.
	if ( this.objLUT != null )
	{
		// Grab the lookup value - if not found, just use the original value.
		var a2 = this.objLUT.lookupValue(a1)
		if ( a2 != null )
			a1 = a2

		// Grab the lookup value - if not found, just use the original value.
		var b2 = this.objLUT.lookupValue(b1)
		if ( b2 != null )
			b1 = b2
	}
	else if ( this.nTimeColID >= 0 )
	{
		var aTime = a[this.nTimeColID]
		var bTime = b[this.nTimeColID]

		a1 = new Date(a1.getFullYear(), a1.getMonth(), a1.getDate(), aTime.substr(0,2), aTime.substr(3,2))
		b1 = new Date(b1.getFullYear(), b1.getMonth(), b1.getDate(), bTime.substr(0,2), bTime.substr(3,2))
	}

	// If numeric sort, make sure a1 and b1 are not strings
	if ( this.eSortType == g_SortTypes.NUMBER )
	{
		a1 = (a1==null) ? null : parseFloat(a1)
		b1 = (b1==null) ? null : parseFloat(b1)
	}

	// Check on the sort type - if it is specified as case-insensitive
	// then "upper()" the values.
	if ( this.eSortType == g_SortTypes.CHARACTER_CASE_INSENSITIVE )
	{
		a1 = (a1==null) ? null : a1.toUpperCase()
		b1 = (b1==null) ? null : b1.toUpperCase()
	}

	if ( a1 == null )
	{
		if (b1 == null)
			return 0
		else // treat as a1 < b1
			return ( this.bAscending ) ? -1 : 1
	}
	else if ( b1 == null ) // treat as a1 > b1
		return ( this.bAscending ) ? 1 : -1
			
	if ( a1 < b1 )
		return ( this.bAscending ) ? -1 : 1
	if ( a1 > b1 )
		return ( this.bAscending ) ? 1 : -1
	return 0
}


//
// Begin class PageSearchOption
//

function PageSearchOption()
{
    this.sCaption = ''
    this.sValue   = ''
    this.sName    = ''
    this.sChecked = ''
}

//
// Begin class PageSearch
//

function PageSearch()
{
    this.sFromPrompt   = ''
    this.sFromDate     = ''
    this.sFromTag      = ''
    this.sFromImg      = ''
    this.sFromURL      = ''

    this.sToPrompt     = ''
    this.sToDate       = ''
    this.sToTag        = ''
    this.sToImg        = ''
    this.sToURL        = ''
    
    this.sPickADate    = ''

    this.objOptions    = new Array() // of PageSearchOption objects

    this.sTextPrompt   = ''
    this.sTextValue    = ''
    this.sTextName     = ''

    this.sListText     = new Array() // of strings
    this.sListValue    = new Array() // of strings
    this.nListSize     = 0
    this.sListMsg      = ''
    this.nListSelected = -1
    
    // Core form elements
    this.sFormName     = 'SForm'
    this.sMethod       = 'POST'
    this.sActionURL    = ''
    
    // FORM parameters not related to actual processing done by the API.
    this.arFormParams = new Array() // of PageDataEntryElement() objects
}

//
// Begin class PageDataEntry
//

function PageDataEntry()
{
	// Core form elements
	this.sFormTitle = ''
	this.sFormName  = 'DEForm'
	this.sMethod    = 'POST'
	this.sActionURL = ''

	// FORM parameters not related to actual processing done by the API.
	this.arFormParams = new Array() // of PageDataEntryElement() objects

	// FORM parameters to be processed directly by the API
	this.arElements = new Array() // of PageDataEntryElement() objects

	// Used for setting the "par<n>" values for each of the Data Entry elements
	this.nNextParamID = 0

	// Used for setting the "focus" to a specific field on initial display
	this.sFocusFieldTag = null
	this.sFocusFieldName = null
	
	// Used for hiding the form for the Electronic Signature resubmit
	this.bHideForm = false;
	
}

PageDataEntry.prototype.setupDataEntry = function( sMethod, sActionURL, sFocusFieldTag )
	{
	this.sMethod        = sMethod
	this.sActionURL     = sActionURL
	this.sFocusFieldTag = sFocusFieldTag
	}

PageDataEntry.prototype.addStandardDEParameters = function( psapi, template )
	{
	// Get a reference to the form parameters array
	var arFormParams = this.arFormParams

	// Keep a parameter id to specify each parameter added
	var i = arFormParams.length-1

	// Hidden elements

	// psapi
	i++
	arFormParams[i] = (PageDataEntryElement==null) ? new objCore.PageDataEntryElement() : new PageDataEntryElement()
	arFormParams[i].sItemPrompt = ''
	arFormParams[i].sItemTag    = objCore.WSSInternalURLs.URLPARAM_API
	arFormParams[i].sItemValue  = psapi
	arFormParams[i].nItemAttrib = objCore.PageDataEntryElementAttributes.HIDDEN
	arFormParams[i].nItemWidth  = -1
	arFormParams[i].nItemMax    = -1

	// page
	i++
	arFormParams[i] = (PageDataEntryElement==null) ? new objCore.PageDataEntryElement() : new PageDataEntryElement()
	arFormParams[i].sItemPrompt = ''
	arFormParams[i].sItemTag    = objCore.WSSInternalURLs.URLPARAM_TEMPLATE
	arFormParams[i].sItemValue  = template
	arFormParams[i].nItemAttrib = objCore.PageDataEntryElementAttributes.HIDDEN
	arFormParams[i].nItemWidth  = -1
	arFormParams[i].nItemMax    = -1
	}

// Begin class PageCalendarEntry
function PageCalendarEntry()
{
	this.date    = null
	this.arLinks = null
}

//
// Begin class PageCalendar
//

function PageCalendar()
{
	this.arEntries = new Array() // of PageCalendarEntry objects
}

PageCalendar.prototype.addEntry = function( objEntry )
{
	this.arEntries[this.arEntries.length] = objEntry
}

// 
// Begin class PageInfoSection
//

function PageInfoSectionEntry()
{
	this.sCaption = ''
	this.sText    = ''
}

function PageInfoSection()
{
	this.arInfoSections = new Array() // Array of PageInfoSectionEntry objects
}

PageInfoSection.prototype.addSection = function ( objInfoSection )
{
	this.arInfoSections[this.arInfoSections.length] = objInfoSection
}

//
// End class PageInfoSection
//

//
// Begin class PageStatusItem
//

function PageStatusItem()
{
	this.arStatusItems = new Array(); // Array of status items
}

PageStatusItem.prototype.addStatusItem = function ( sText, eLinkType, fIsSubItem )
{
	var item = new Object();
	item.sText      = sText;
	item.eLinkType  = eLinkType;
	item.fIsSubItem = fIsSubItem;

	this.arStatusItems.push( item );
}

//
// End class PageStatusItem
//

//
// Begin class PageScreenExitError
//

function PageScreenExitError()
{
	this.nErrorCode = 0; // For future use
	this.sErrorMsg = '';
	this.sXMLErrorMsg = '';
	this.sKey = '';
	this.sURL = '';
	this.sURLText = '';
	this.sXML = '';
}

//
// End class PageScreenExitError
//


//
// Begin class PageDescriptor
//

function PageDescriptor()
{
	// Page Identifier ("outputPage") and Page Type
	this.ePageID   = -1
	this.ePageType = -1

	// Error code and error message
	this.nErrorCode    = -1
	this.sErrorMessage = ''

	// Page data
	// this.objPageData = null

	// Target frame for internal URLs
	// By default (and probably always) target the main display frame
	this.sTargetFrame = getMainFrameName()

	// Generic Page elements
	this.fNoHeader = false // suppresses header, trail links, and title area
	this.fNoFooter = false // suppresses footer images and copyright

	this.objPageHeader = new PageHeader()

	this.objPageTrail = new PageTrail()

	this.sUserDisplayName          = ''
	this.sEffectiveUserDisplayName = ''
	this.fEditMode                 = false;
	this.sWorkingOn                = ''

	this.sPageTitle = ''

	this.sPageDesc = ''
	this.fPageDescIsHTML = false

	this.objPageFooter = new PageFooter()

	this.sProductName   = ''
	this.sModuleName    = ''
	this.sModuleVersion = ''
	this.sCopyright     = ''

	// Page-specific elements
	this.sPageButtons = new Array() // of WSSHTLink() objects

	this.sPageBody = '' // Typically used for "MsgBox"-type pages and "Turnaround"-type pages.
	this.fPageBodyIsHtml = false

	this.objGrid  = null // PageGrid object

	this.objCalendar = null // PageCalendar object

	this.imgMenuItem = '' // Menu list bullet image. Note: This should one day be in the style-sheet "LIST-STYLE-IMAGE".
	this.arLinks = new LinkArray()

	this.objDataEntry = new PageDataEntry()
	this.objSearchPage = new PageSearch()
	this.objInfoSection = new PageInfoSection()
	this.objStatusItem = new PageStatusItem()
	this.objSEError = new PageScreenExitError()
}

PageDescriptor.prototype.addLink = function( objLink )
{
	this.arLinks.addLink(objLink)
}

// Returns all links of the specified category
PageDescriptor.prototype.getLinks = function( nCategory )
{
	// Initialize our return variable
	var arLinks = new LinkArray()

	// Iterate over the links extracting buttons
	var nNumLinks = this.arLinks.getNumLinks()
	for (var i = 0; i < nNumLinks; i++)
	{
		var objLink = this.arLinks.getLink(i)
		if ( objLink.nCategory == nCategory )
			arLinks.addLink(objLink)
	}

	return arLinks
}

PageDescriptor.prototype.addButton = function( text, href, title )
{
	var objButton = (WSSHTLink==null) ? new objCore.WSSHTLink() : new WSSHTLink()

	objButton.nCategory = objCore.WSSHTLinkCategories.BUTTON
	objButton.sText     = text
	objButton.sHREF		= href
	objButton.sTitle    = title;

	this.addLink( objButton )
}

// Class clRowObject

function ro_getValue( sColumn, bIsLUTValue )
	{
	var nNum = this.aColumns.length;
	for ( var i=0; i < nNum; i++ )
		{
		// Search for the column.  If bIsLUTValue is not specified, then ignore
		// the aColumnIsLUT value.
		if ( (this.aColumns[i] == sColumn)
		  && ( (this.aColumnIsLUT[i] == bIsLUTValue) || (bIsLUTValue==null) ) )
			return this.aValues[i];
		}

	return null;
	}

function ro_setValue( sColumn, bIsLUTValue, oValue )
	{
	var i;
	var nNum = this.aColumns.length;
	for ( var i=0; i < nNum; i++ )
		{
		if ( (this.aColumns[i] == sColumn) && (this.aColumnIsLUT[i] == bIsLUTValue) )
			break;
		}

	this.aColumns[i]     = sColumn;
	this.aColumnIsLUT[i] = bIsLUTValue;
	this.aValues[i]      = oValue;
	}

function clRowObject()
	{
	this.aColumns     = new Array();
	this.aColumnIsLUT = new Array();
	this.aValues      = new Array();

	this.GetValue = ro_getValue;
	this.SetValue = ro_setValue;
	}

