﻿function ajaxFail()
{
    alert("Sorry, an error occurred communicating with the server - please try again. It is possible that your session has timed-out - use your brower's 'Refresh Page' option and you will be asked to login again.  If the problem persists then please contact the administrator.");
}

function showP()
{
    var updating = $('loadingMessage');
    if (updating != null)
    {
        updating.style.top = getScrollTop() + getWindowHeight() - updating.clientHeight - document.getElementsByClassName('contentHeader')[0].offsetTop - 20 + 'px';
        updating.style.visibility = "visible";
    }
}

function getEventSource(evt)
{
    if (window.event)
    {
        return window.event.srcElement;
    }
    else
    {
        return evt.target;
    }
}

function simulateClick(element)
{
    var evt;
    if (document.createEvent){
        evt = document.createEvent("MouseEvents");
        evt.initMouseEvent("click", true, true, window,
        0, 0, 0, 0, 0, false, false, false, false, 0, null);
    }
    (evt)? element.dispatchEvent(evt):(element.click && element.click());
}

function selectListItem(selectedRow)
{
    var idSpec = new idspec_class();
    idSpec.m_parentSpec = null;
    SetIdSpecFromId(idSpec, selectedRow.id);
    window.location = "../Auth/App.aspx?list=" + idSpec.m_type + "&id=" + idSpec.m_id;
}

function selectListItemGuid(selectedRow)
{
    var idSpec = new idspec_class();
    idSpec.m_parentSpec = null;
    SetIdSpecFromId(idSpec, selectedRow.id);
    window.location = "../Auth/App.aspx?list=" + idSpec.m_type + "&guid=" + selectedRow.id; // + idSpec.m_type + "&id=" + idSpec.m_id;
}

function selectListCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
          var mainBody = $('mainBody');
          mainBody.innerHTML = res.value;
          setDefaultExpansionOnTree(mainBody);
    } 
    else
    {
        ajaxFail();
    }
}

function hsortList(callingLink)
{
    var firstRow = callingLink.parentNode.parentNode;
    var e = buildElementSpecForEdit(firstRow);
    
    var row = firstRow.parentNode.parentNode.rows[firstRow.rowIndex + 1];
    var e = buildElementSpecForEdit(row); // add in the new element
    var associatedTable = row.childNodes[1].childNodes[0];
    var rowNum = associatedTable.rows.length;

    if (Element.hasClassName(associatedTable, "treeBottom") == false)
    {
        e.m_generations[e.m_generations.length] = rowNum / 2;
    }
    else
    {
        e.m_generations[e.m_generations.length] = rowNum;
    }
    e.m_newType = null;
    
    g_elementSpec = e;
    g_associatedTable = associatedTable;
    showP();
    ap.GetListSorter(g_elementSpec, sortListCallback);
}

function sortList(callingButton)
{
    var spec = buildElementSpecForNew(callingButton, null);
    var associatedTable = getAssociatedTableForAddButton(callingButton);

    g_elementSpec = spec;
    g_associatedTable = associatedTable;
    showP();
    ap.GetListSorter(g_elementSpec, sortListCallback);
}

function sortListCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        lb = new lightbox(g_elementSpec, res.value, g_associatedTable, null, true);
        lb.activate();       
        var ul = $('sort');
        Sortable.create(ul, {tag:'li', ghosting:true});
    } 
    else
    {
        ajaxFail();
    }
}

function sortListItems()
{
    showP();
    ap.GetListItemsSorter(1, sortListItemsCallback);
}

function sortListItemsCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        lb = new lightbox(null, res.value, null, null, true);
        lb.activate();       
        var ul = $('sort');
        Sortable.create(ul, {tag:'li', ghosting:true});
    } 
    else
    {
        ajaxFail();
    }
}

function getOwnerOfRelatedAnchors(clickedAnchor)
{
    var tabContainer = clickedAnchor.parentNode.parentNode.parentNode;
    return tabContainer.parentNode.parentNode;
}


function getList(clickedAnchor)
{
    var listType = clickedAnchor.id;
    var currentActive = document.getElementsByClassName('tab-active', getOwnerOfRelatedAnchors(clickedAnchor));
    if (currentActive.length == 1)
    {
        Element.removeClassName(currentActive[0], 'tab-active');
    }
    // Set this one as active
    Element.addClassName(clickedAnchor, 'tab-active');
    showP();
    ap.GetMainList(listType, mainListCallback);
}

function pageList(listType, pageNum)
{
    showP();
    ap.PageList(listType, pageNum, mainListCallback);
}

function switchTab(tabName)
{
    var tab = $(tabName);
    if (tab != null)
    {
        simulateClick(tab);
    }
}

function mainListCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        setMainList(res.value);
    } 
    else
    {
        ajaxFail();
    }
}

function setMainList(markup)
{
    var mainBody = $('mainBody');
    if (mainBody != null)
    {
      mainBody.innerHTML = markup;
      setDefaultExpansionOnTree(mainBody);
    }
}

function getSubAuditResults(selectedRow, event)
{
    var selectedTD = getEventSource(event);
    var parentTable = selectedRow.parentNode.parentNode;
    var currentSelected = document.getElementsByClassName('selSubAudit', selectedRow);
    if (currentSelected.length == 1)
    {
        Element.removeClassName(currentSelected[0], 'selSubAudit');
    }
    // Set this one as active
    Element.addClassName(selectedTD, 'selSubAudit');
    showP();
    ap.GetSubAuditResults(parentTable.id, selectedTD.id, subAuditCallback);
}

function subAuditCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
      var auditRes = $('auditRes');
      if (auditRes != null)
      {
          auditRes.outerHTML = res.value;
      }
    } 
    else
    {
        ajaxFail();
    }
}

function setDefaultExpansionOnTree(treeElement, processChildren)
{
    if (processChildren == null)
    {
        setRearrangeActions(treeElement);
    }
    var topImage = document.getElementsByClassName('expandOff', treeElement)[0];
    if (topImage == null)
    {
        return;
    }
    if (Element.hasClassName(topImage, 'hide') == true)
    {
        toggleExpansion(topImage);
    }
    
    if (processChildren != false)
    {
        var parentRow = topImage.parentNode;
        var parentTable = parentRow.parentNode;

        var existingRowNumber = parentRow.rowIndex;
        var childrenControlledByToggle = parentTable.rows[existingRowNumber + 1];
        
        var nextSet = document.getElementsByClassName('expandOff', childrenControlledByToggle);
        for (var i = 0 ; i < nextSet.length ; i++)
        {
            setDefaultExpansionOnTree(nextSet[i].parentNode.parentNode.parentNode, false);
        }
     }
}

function setRearrangeActions(treeElement)
{
    var els = document.getElementsByClassName('action', treeElement);
    for (var i = 0 ; i < els.length ; i++)
    {
        var actionLink = els[i];
        var parentTable = actionLink.parentNode.childNodes[0];
        var wantRows = 4;
        if (Element.hasClassName(parentTable, "treeBottom") == true)
        {
            wantRows = 2;
        }
        if (parentTable.rows.length < wantRows)
        {
            setVisibility(actionLink, 'hidden');
        }
        else
        {
            setVisibility(actionLink, 'visible');
        }
    }
}

function focusFirstChild(parentElement) {
    if (parentElement != null)
    {
        var children = parentElement.getElementsByTagName("*");
        for (var i = 0 ; i < children.length ; i++)
        {
            var type = children[i].type;
            if ((type != null))
            {
                if (children[i].disabled == false)
                {
                    setFocus(children[i]);	
                    break;    
                }
            }
        }
    }
}

function setFocus(e)
{
    try
    {
        Field.focus(e);
    }
    catch(ex){}
}

function getScrollTop()
{
    if (self.pageYOffset) {
        return self.pageYOffset;
    } else if (document.documentElement && document.documentElement.scrollTop){  // Explorer 6 Strict
        return document.documentElement.scrollTop; 
    } else if (document.body) {// all other Explorers
        return document.body.scrollTop;
    }
    return 0;
}

function getWindowHeight()
{
    if (window.innerHeight)
    {
        return window.innerHeight;
    }
    return document.documentElement.offsetHeight;
}

function getWindowWidth()
{
    if (window.innerWidth)
    {
        return window.innerWidth;
    }
    return document.documentElement.offsetWidth;
}

function loading_hide()
{
    hideElement('loadingMessage');
}

function hideElement(elementName)
{
    setVisibility(elementName, "hidden");
}

function showElement(elementName)
{
    setVisibility(elementName, "visible");
}

function setVisibility(elementName, vis)
{
    var element = $(elementName);
    if (element != null)
    {
        element.style.visibility = vis;
    }
}

function expandBranch(imageElement)
{
    Element.removeClassName(imageElement, "expandOff");
    Element.addClassName(imageElement, "expandOn");
    setDisplayStyleOfDescendants(imageElement, "");
}

function contractBranch(imageElement)
{
    Element.removeClassName(imageElement, "expandOn");
    Element.addClassName(imageElement, "expandOff");
    setDisplayStyleOfDescendants(imageElement, "none");
}

function setDisplayStyleOfDescendants(imageElement, newDisplayStyle)
{
    var parentRow = imageElement.parentNode;
    var parentTable = parentRow.parentNode;
    var rowsInTable = parentTable.rows.length;
    var existingRowNumber = parentRow.rowIndex;
    parentTable.rows[existingRowNumber + 1].style.display = newDisplayStyle;
}

function toggleExpansion(imageElement)
{
    var newDisplayStyle = "";
    if (Element.hasClassName(imageElement, "expandOn"))    // if we test for file name directly we get problems because it's now a full uri
    {
        contractBranch(imageElement);
    }
    else
    {
        expandBranch(imageElement);
    }
}

function getRowsPerItemForTable(associatedTable)
{
    if (Element.hasClassName(associatedTable, 'treeBottom') == false)
    {
        return 2;
    }
    return 1;
}

function getRowNumForElementSpec(elementSpec, associatedTable)
{
    var rowNum = 0;
    if (elementSpec.m_generations.length > 0)
    {
        var numGenerations = elementSpec.m_generations.length;
        rowNum = elementSpec.m_generations[numGenerations - 1];
        rowNum = (rowNum * getRowsPerItemForTable(associatedTable));
    }
    if (associatedTable.tHead != null)
    {
        rowNum += associatedTable.tHead.rows.length;
    }
    return rowNum;
}

function xmlFragmentFromMarkup(markup)
{
    var xmlFragment = "<res>" + markup + "</res>";
    var xmlDoc;
    if (window.ActiveXObject)
    {
      xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
      xmlDoc.async = false;
      xmlDoc.loadXML(xmlFragment);
    }
    else
    {
        var parser=new DOMParser();
        xmlDoc = parser.parseFromString(xmlFragment,"text/xml");
    }
    return xmlDoc.documentElement;
}

function updateRowMarkup(markup, elementSpec, associatedTable)
{
    var xmlObj = xmlFragmentFromMarkup(markup);
    var currentExpandClass = "";
    if (elementSpec.m_newType == "")
    {
        var rowNum = getRowNumForElementSpec(elementSpec, associatedTable);
        // store the current expandOnOff status for restoration later
        var currentRow = associatedTable.rows[rowNum];
        if (currentRow.cells.length > 0)
        {
            var currentExpandStateTD = currentRow.cells[0];
            currentExpandClass = currentExpandStateTD.className;
        }
        for (var i = 0 ; i < xmlObj.childNodes.length ; i++)
        {
            associatedTable.deleteRow(rowNum);
        }
        updateTableHTML(xmlObj, rowNum, associatedTable);
        if (currentExpandClass != "")
        {
            var newRow = associatedTable.rows[rowNum];
            var expandItem = newRow.cells[0];
            if (expandItem.className != "")
            {
                Element.removeClassName(expandItem, expandItem.className);
            }
            Element.addClassName(expandItem, currentExpandClass);
        }
    }
    else
    {
        var numRows = associatedTable.rows.length;
        updateTableHTML(xmlObj, numRows, associatedTable);
    }
}

function updateTableHTML(xmlObj, startRow, associatedTable)
{
    for (var i = 0 ; i < xmlObj.childNodes.length ; i++)
    {
        var rowHTML = xmlObj.childNodes[i];
        var newRow = associatedTable.insertRow(startRow + i);
        for (var k = 0 ; k < rowHTML.attributes.length; k++)
        {
            var att = rowHTML.attributes[k];
            setAttributeFromMarkup(newRow, att);
        }
        for (var j = 0 ; j < rowHTML.childNodes.length ; j++)
        {
            var colObj = rowHTML.childNodes[j];
            // colObj will be the full xml <td att="x">inside stuff</td>. But we can't set this directly into outerHTML unfortunately
            var newCol = newRow.insertCell(j);
            for (var m = 0 ; m < colObj.attributes.length ; m++)
            {
                var att = colObj.attributes[m];
                setAttributeFromMarkup(newCol, att);
            }
            if (colObj.childNodes.length > 0)
            {
                var innerHTML = getHTMLFromXmlNodes(colObj);
                if (innerHTML != "")
                {
                    new Insertion.Bottom(newCol, innerHTML);
                }
            }
        }
    }
}

function getHTMLFromXmlNodes(xmlObj)
{
    var html = "";
    for (var n = 0 ; n < xmlObj.childNodes.length ; n++)
    {
        var node = xmlObj.childNodes[n];
        if (node.xml)
        {
            html += node.xml;
        }
        else
        {
            var ser = new XMLSerializer();
            var x = ser.serializeToString(node);
            html += x;
        }
    }
    return html;
}

function setAttributeFromMarkup(element, att)
{
    // This function is necessary because setting the markup for an element directly doesn't create attributes correctly
    if (att.nodeName == "class")
    {
        Element.addClassName(element, att.nodeValue);
    }
    else if (att.nodeName.substring(0, 2) == "on" && (!element.addEventListener)) // !!!!! See http://www.faqts.com/knowledge_base/view.phtml/aid/9592 (event doesn't fire otherwise)
    {
        element[att.nodeName] = new Function(att.nodeValue);
    }
    else
    {
        element.setAttribute(att.nodeName, att.nodeValue);               
    }
}

function getItemNumber(callingElement)
{
    var parentTR = callingElement.parentNode;
    return parentTR.rowIndex;
}

var g_currentElement = null;  // !!!!!! only to pass to callback - quick hack
var g_associatedTable = null;
var g_idSpec = null;
var g_itemDivHTML = null;
var g_callingBtnItemAction = null;
var g_promptType = null;
var g_contextMenuRow = null;
var g_contextMenuType = null;
var g_operationTarget = null;

function getTopLightbox()
{
    var lightboxCount = g_lightboxes.length;
    if (lightboxCount > 0)
    {
        return g_lightboxes[lightboxCount - 1];
    }
    return null;
}

function createParentElementSpecTypeFromLightbox(lightbox)
{
    var parentElementSpec = new pespec_class();
    parentElementSpec.m_newType = lightbox.elementSpec.m_newType;
    parentElementSpec.m_generations = lightbox.elementSpec.m_generations;
    parentElementSpec.m_joinCondition = lightbox.getActivePaneName();
    return parentElementSpec;
}

function getParentElementSpec()
{
    var parentElementSpec = null;
    var parentLightbox = getTopLightbox();
    if (parentLightbox != null)
    {
        parentElementSpec = createParentElementSpecTypeFromLightbox(parentLightbox);
    }
    return parentElementSpec;
}

function getAndDisplayEdit(elementSpec, associatedTable, idSpec, relatedId)
{
    showP();

    g_currentElement = elementSpec;
    g_associatedTable = associatedTable;  
    g_idSpec = idSpec;
    
    parentElementSpec = getParentElementSpec();
    
    if (idSpec == null)
    {
        ap.GetEditMarkupForElement(elementSpec, parentElementSpec, editCallback);
    }
    else
    {
        if (relatedId == null)
        {
            relatedId = -1;
        }
        ap.GetIdBasedEditMarkupForElement(idSpec, relatedId, editCallback);
    }
}

function editCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        lb = new lightbox(g_currentElement, res.value, g_associatedTable, g_idSpec, false);
        lb.activate();       
    }
    else
    {
        ajaxFail();
    }
}

function getNextRowUpInTree(row)
{
    var parentTBody = row.parentNode;
    var parentTable = parentTBody.parentNode;
    var parentTD = parentTable.parentNode;
    return parentTD.parentNode;
}

function getRowNumForCallingAddButton(callingButton)
{
    var associatedTable = getAssociatedTableForAddButton(callingButton);
    return associatedTable.rows.length;
}

function getAssociatedTableForAddButton(callingButton)
{
    var parentTD = callingButton.parentNode;
    return parentTD.childNodes[0];
}

function getNextRowUpFromAddButton(addButton)
{
    var parentTD = addButton.parentNode;
    return parentTD.parentNode;
}

function wasExpandOnOffClicked(evt)
{
    var src = getEventSource(evt);
    if (src == null)
    {
        return false;
    }
    if (src.className == "expandOn" || (src.className == "expandOff") || (src.className == "photoInd") || (src.className == "thumb"))
    {
        return true;
    }
    return false;
}

function wasSubItemClicked(evt)
{
    var src = getEventSource(evt);
    if (src == null)
    {
        return false;
    }
    var tagName = src.tagName.toUpperCase();
    if (tagName == "INPUT" || (tagName == "A") || (tagName == "IMG"))
    {
        return true;
    }
    return false;
}

function getAssociatedTableForElementSpec(elementSpec, mainDoc)
{
    var topLevelTree = document.getElementsByClassName('treeTop')[0]; // topLevelDiv.getElementsByTagName('table')[0];
    var elementSpecRow;
    if (mainDoc == true)
    {
        topLevelTree = topLevelTree.getElementsByTagName('table')[0];
    }
    var currentTree = topLevelTree;
    var generation = 0;
    for (var generation = 0 ; generation < elementSpec.m_generations.length - 1 ; generation++)
    {
        var header = currentTree.thead;
        var headerRows = 0;
        if (header != null)
        {
            headerRows = header.rows.length;
        }
        var rowsPerItem = 2;
        // However, for result display we also have spacer rows (ie. 3 rows for each item)
        var testRow = currentTree.rows[headerRows + 2];
        if ((testRow != null) && (testRow.className == "spacer"))
        {
            rowsPerItem = 3;
        }

        var parentRowOfNextTree = currentTree.rows[headerRows + (elementSpec.m_generations[generation] * rowsPerItem) + 1];
        var parentTDOfNextTree = parentRowOfNextTree.getElementsByTagName('td')[1];
        currentTree = parentTDOfNextTree.getElementsByTagName('table')[0];
    } 
    return currentTree;
}

function getParentTable(element)
{
    var parent = element.parentNode;
    while (parent.tagName.toLowerCase() != "table")
    {
        parent = parent.parentNode;
    }
    return parent;
}

function getParentTableRow(element)
{
    var parent = element.parentNode;
    while (parent.tagName.toLowerCase() != "tr")
    {
        parent = parent.parentNode;
    }
    return parent;
}

function replaceTableMarkup(associatedTable, markup)
{
    var parentTable = getParentTable(associatedTable);
    var newParent = parentTable.parentNode;
    new Insertion.After(parentTable, markup);
    Element.remove(parentTable);
    setDefaultExpansionOnTree(newParent.childNodes[0]);
}

function buildElementSpecForEdit(callingRow)
{
    var e = new espec_class();
    e.m_newType = "";
    e.m_generations = new Array();
    var wrongWayAround = new Array();
    var tableRow = callingRow;
    var parentTable = tableRow.parentNode.parentNode;
    var i = 0;
    while (Element.hasClassName(parentTable, "treeTop") == false)
    {
        if (Element.hasClassName(parentTable, "treeBottom") == false)
        {
            var rowsPerItem = 2;    // usually have header showing row details, plus another row which is the descendant
            // However, for result display we also have spacer rows (ie. 3 rows for each item)
            var endOfItemIndex = tableRow.rowIndex;
            if ((tableRow.rowIndex + 1) < parentTable.rows.length)
            {
                if (parentTable.rows[tableRow.rowIndex + 1].className == "spacer")
                {
                    rowsPerItem = 3;
                    endOfItemIndex = endOfItemIndex + 1;
                }
            }
            wrongWayAround[i] = Math.round((endOfItemIndex / rowsPerItem) - ((endOfItemIndex % rowsPerItem) / rowsPerItem));   // yeesh, integer division
        }
        else
        {
            var headerRows = 0; // should apply this to other calculations involving rows.length, but at the moment this is the only case where we might have a header
            if (parentTable.tHead != null)
            {
                headerRows = parentTable.tHead.rows.length;
            }
            wrongWayAround[i] = tableRow.rowIndex - headerRows;  
        }
        tableRow = getNextRowUpInTree(tableRow);
        parentTable = tableRow.parentNode.parentNode;
        tableParent = parentTable.parentNode;
        i++;
    }
    for (var j = 0 ; j < i ; j++)
    {
        e.m_generations[j] = wrongWayAround[i - j - 1];
    }
    return e;
}

function buildElementSpecForNew(callingButton, newType)
{
    var nextRowUp = getNextRowUpFromAddButton(callingButton);   // get the spec for the tree above this point    
    var e = buildElementSpecForEdit(nextRowUp); // add in the new element
    var associatedTable = getAssociatedTableForAddButton(callingButton);
    var rowNum = getRowNumForCallingAddButton(callingButton);

    if (Element.hasClassName(associatedTable, "treeBottom") == false)
    {
        e.m_generations[e.m_generations.length] = rowNum / 2;   // !!!! Cope with spacer rows (although we don't use AddNew at the moment for those)
    }
    else
    {
        e.m_generations[e.m_generations.length] = rowNum;
    }
    e.m_newType = newType;
    return e;
}

function editTreeRow(callingRow, evt)
{
   if (wasExpandOnOffClicked(evt) == false && (wasSubItemClicked(evt) == false))
   {
        var spec = buildElementSpecForEdit(callingRow);
        var parentTable = callingRow.parentNode.parentNode;
        var idSpec = getIdSpecForRow(callingRow, null, false);
        getAndDisplayEdit(spec, parentTable, idSpec, null);
   }
}

function newTreeRowPrompt(callingButton, pType)
{
    g_callingBtnItemAction = callingButton;
    g_promptType = pType;
    if ((g_itemDivHTML == null) || (g_itemDivHTML[pType] == null))
    {
        showP();
        ap.GetAddItemDiv(pType, itemDivCallback);   
    }
    else
    {
        showAddItemDiv();
    }
}

function showAddItemDiv()
{
    lb = new lightbox(null, g_itemDivHTML[g_promptType], null, null, false);
    lb.activate();       
}

function itemDivCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        if (g_itemDivHTML == null)
        {
            g_itemDivHTML = new Array();
        }
        g_itemDivHTML[g_promptType] = res.value;
        showAddItemDiv();
    }
    else
    {
        ajaxFail();
    }
}

function deleteImage(callingLink, imageId)
{
    if (confirm('This image will be deleted permanently. Is that OK ?') == true)
    {
        var parentRow = getParentTableRow(callingLink);
        g_elementSpec = buildElementSpecForEdit(parentRow);
        g_associatedTable = getParentTable(parentRow);
        var parentElementSpec = getParentElementSpec();
        showP();
        ap.DeleteImage(g_elementSpec, parentElementSpec, imageId, deleteImageCallback);
    }
}

function deleteImageCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        if (res.value.m_resultType == mrt.MarkupResultType.STANDARD_MARKUP)
        {
            updateRowMarkup(res.value.m_markup, g_elementSpec, g_associatedTable);
        }
    } 
    else
    {
        ajaxFail();
    }
}

function getImage(callingLink)
{
    showImage('i', callingLink.id);
}

function getSig(uniqueName)
{
    showImage('s', uniqueName);
}

function showImage(type, val)
{
    var markup = "<div class='lbCaption'><span class='nodeTextH2'>Image</span><a href='#' id='btnClose' class='lbAction' rel='deactivate'>Close</a></div>";
    var lb = new lightbox(null, markup + "<div class='photo'><img src='Image.aspx?" + type + "=" + val + "'/></div>", null, null, false);
    showLb();
}

function showLb()
{
    var lb = getTopLightbox();
    lb.activate();
}

function newTreeRow(callingControl, newType)
{
    var callingButton;
    if (callingControl.tagName.toLowerCase() != "input")
    {
        callingButton = g_callingBtnItemAction; // this was saved before calling the AddItem div
        // Need to get rid of the lightbox too
        var lb = getTopLightbox();
        if (lb != null)
        {
            lb.deactivate();
        }
    }
    else
    {
        callingButton = callingControl;
    }
    var spec = buildElementSpecForNew(callingButton, newType);
    var associatedTable = getAssociatedTableForAddButton(callingButton);
    getAndDisplayEdit(spec, associatedTable, null, null);
}

function newIdTreeRow(callingButton, newType)
{
    newIdTreeRow(callingButton, newType, null);
}


function newIdTreeRow(callingButton, newType, relatedId)
{
    var spec = buildElementSpecForNew(callingButton, newType);
    var associatedTable = getAssociatedTableForAddButton(callingButton);
    var nextRowUp = getNextRowUpFromAddButton(callingButton); // this is the data parent, the row above this contains the id of the owning element
    var wantRowIndex = nextRowUp.rowIndex - 1;
    var rowContainingParentId = nextRowUp.parentNode.parentNode.rows[wantRowIndex];
    getAndDisplayEdit(spec, associatedTable, getIdSpecForRow(rowContainingParentId, newType, false), relatedId);
}

function getAncestorRow(callingRow)
{
    var parentRow = getNextRowUpInTree(callingRow);
    if (parentRow.tagName.toLowerCase() == "tr")
    {
        var wantRowIndex = parentRow.rowIndex - 1;
        return parentRow.parentNode.parentNode.rows[wantRowIndex];
    }
    else
    {
        return null;
    }
}

function getParentRowId(callingRow)
{
    var parentRowId = "";
    var ancestorRow = callingRow;
    while ((ancestorRow != null) && (parentRowId == ""))
    {
        ancestorRow = getAncestorRow(ancestorRow);
        if (ancestorRow != null)
        {
            parentRowId = ancestorRow.id;
        }
        else
        {
            parentRowId = "";
        }  
    }
    return parentRowId;
}

function getIdSpecForRow(callingRow, newType, getParent)
{
    if ((newType == null) && (callingRow.id == ""))
    {
        return null;    // this isn't an id-based edit (no id for editing, no new type for creating)
    }
    var idSpec = new idspec_class();
    var parentSpec = new idspec_class();
    idSpec.m_parentSpec = parentSpec;
    if (newType == null)    // this is an edit, so we have an existing id
    {
        SetIdSpecFromId(idSpec, callingRow.id);
        if (getParent == true)  // linked entity - get parent
        {
            var parentRowId = getParentRowId(callingRow);
            SetIdSpecFromId(idSpec.m_parentSpec, parentRowId);
        }
        else
        {
            idSpec.m_parent = null;
        }
    }
    else    // we don't have an id but we need the parent id
    {
        idSpec.m_type = newType;
        idSpec.m_id = -1;
        var parentRowId = callingRow.id;    // the calling row passed in is a parent to the button
        if (parentRowId == "")
        {
            parentRowId = getParentRowId(callingRow);
        }        
        SetIdSpecFromId(idSpec.m_parentSpec, parentRowId);
    }
    return idSpec;
}

function SetIdSpecFromId(idSpec, rowId)
{
     var splitString = rowId.split("_");
     idSpec.m_id = splitString[1];
     idSpec.m_type = splitString[0];
}

function rightClick(callingRow, menuType, evt)
{
    var src = getEventSource(evt);
    if (src != null && ((src.tagName.toLowerCase() == "a") || (src.tagName.toLowerCase() == "img")))
    {
        evt.returnValue = true;
        evt.cancelBubble = false;
        return true;
    }
    var xPos = Event.pointerX(evt || window.event);
    var yPos = Event.pointerY(evt || window.event);
    var context = $('context');
    if (context == null || (g_contextMenuType != menuType))
    {
        showP();
        res = ap.GetContextMenu(menuType);
        loading_hide();
        if (res.value != null)
        {
            if (context != null)
            {
                Element.remove(context);
            }
	        var end = document.getElementsByClassName('masterFooter')[0];
	        new Insertion.Before(end, res.value);
	        context = $('context');
	        g_contextMenuType = menuType;
        }
    }
    else
    {
        removeAllHighlighting();
    }
    if (context == null)
    {
        evt.returnValue = true;
        evt.cancelBubble = false;
        return true;
    }
    g_contextMenuRow = callingRow;
    Element.addClassName(g_contextMenuRow, 'highlightedBox');
    var evtz = (evt) ? evt : ((event) ? event : null);
    
     var left, top;
            if (evtz.pageX) {
                left = evtz.pageX;
                top = evtz.pageY;
            } else if (evtz.offsetX || evt.offsetY) {
                left = evtz.offsetX;
                top = evtz.offsetY;
            } else if (evtz.clientX) {
                left = evtz.clientX;
                top = evtz.clientY;
            }
    var chOffset = document.getElementsByClassName('contentHeader')[0].offsetTop;
    context.style.left = left + "px";
    context.style.top = top - chOffset + "px";   
    context.style.leftPos += 10;
    context.style.posLeft = xPos; // + 'px';
    context.style.posTop = yPos - chOffset; // + 'px';
    context.style.visibility = "visible";
    if (context.setCapture)
    {
        context.setCapture();
    }
    if (evt.preventDefault)
    {
        evt.preventDefault();
    }
    evt.returnValue = false;
    return false;
}

function removeAllHighlighting()
{
    var h = document.getElementsByClassName('highlighted', $('context'));
    for (var i = 0 ; i < h.length ; i++)
    {
        Element.removeClassName(h[i], 'highlighted');
    }
}

function contextSwitched(evt)
{  
   var clicked = getEventSource(evt);
   if (Element.hasClassName(clicked, 'contextOption') == true)
   {
       var currH = Element.hasClassName(clicked, 'highlighted');
       removeAllHighlighting();
       if (!currH)
       {
          Element.addClassName(clicked, 'highlighted');
       }
   }
}

function contextOut(evt)
{  
   contextSwitched(evt);
   var source = getEventSource(evt);
   if (!source.setCapture)  // ie. not IE
   {
       if (Element.hasClassName(evt.relatedTarget, 'contextOption') == false)
       {
           removeContext();
       }
   }
   else
   {
        contextSwitched(window.event);
   }
}

function removeContext()
{
   var context = $('context');
   if (context.releaseCapture)
   {
       context.releaseCapture();
   }
   context.style.visibility = "hidden";
   if (g_contextMenuRow != null)
   {
       Element.removeClassName(g_contextMenuRow, 'highlightedBox');
   }
}

function contextClicked(evt)
{
   removeContext();
   var clickedElement = getEventSource(evt);
   if ((Element.hasClassName(clickedElement, 'contextOption') == true) && (g_contextMenuRow != null))
   {
        var optionId = clickedElement.id;
        g_elementSpec = buildElementSpecForEdit(g_contextMenuRow);
        g_associatedTable = g_contextMenuRow.parentNode.parentNode;

        if (optionId == "a_Del")
        {
            if (confirm('This entry will be deleted permanently. Is that OK ?') == true)
            {
                var parentElementSpec = getParentElementSpec();
                showP();
                ap.DeleteElement(g_elementSpec, parentElementSpec, deleteElementCallback);
            }
        }
        else if (optionId == "a_Duplicate")
        {
            showP();
            ap.DuplicateElement(g_elementSpec, duplicateCallback);      
        }
        else if (optionId == "a_Copy")
        {
            showP();
            ap.CopyElement(g_elementSpec, copyCallback);      
        }
        else if (optionId == "a_Paste")
        {
            showP();
            ap.PasteElement(g_elementSpec, pasteCallback);      
        }
        else if (optionId == "id_Del")
        {
            if (confirm('This entry will be deleted permanently. Is that OK ?') == true)
            {
                var idSpec = getIdSpecForRow(g_contextMenuRow, null);
                ap.DeleteId(idSpec, deleteIdCallback, false);
            }
        }
        else if (optionId == "id_Rem")
        {
            var idSpec = getIdSpecForRow(g_contextMenuRow, null, true);
            ap.UnlinkEntity(idSpec, unlinkCallback);     
        }
        else if (optionId == "id_Duplicate")
        {
            var idSpec = getIdSpecForRow(g_contextMenuRow, null, true);
            ap.DuplicateAudit(g_elementSpec, idSpec, duplicateAuditCallback);     
        }
        else if (optionId == "id_Password")
        {
            g_idSpec = getIdSpecForRow(g_contextMenuRow, null, true);
            g_idSpec.m_type = "pwChange";
            ap.ResetPasswordMarkup(g_elementSpec, g_idSpec, resetPasswordMarkupCallback);
        }
        else if (optionId == "g_Del")
        {
            if (confirm('This entry will be deleted permanently. Is that OK ?') == true)
            {
                var idSpec = getIdSpecForRow(g_contextMenuRow, null);
                ap.DeleteGuid(idSpec.m_id, deleteIdCallback, false);
            }
        }
        else if (optionId == "g_RevInc" || (optionId == "g_RevCom"))
        {
            var idSpec = getIdSpecForRow(g_contextMenuRow, null);
            var setIncomplete = (optionId == "g_RevInc") ? true : false;
            ap.RevertAuditWork(idSpec.m_id, setIncomplete, revertIdCallback);
        }
        else if (optionId == "g_CngDat" || (optionId == "g_CngLoc") || (optionId == "g_CngCus"))
        {
            g_idSpec = getIdSpecForRow(g_contextMenuRow, null, true);
            g_idSpec.m_type = g_idSpec.m_type + "*" + optionId;
            ap.ReportHeaderEditMarkup(g_idSpec.m_id, optionId, editReportHeaderCallback);
        }

   }
   // else, we get any other clicks because setCapture is on - ignore these, but we've closed the menu
}

function deleteElementCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        if (res.value == 1)
        {
            var rowNum = getRowNumForElementSpec(g_elementSpec, g_associatedTable);
            var rowsToDelete = getRowsPerItemForTable(g_associatedTable);
            for (var i = 0 ; i < rowsToDelete ; i++)
            {
                g_associatedTable.deleteRow(rowNum);    
            }
            var lb = getTopLightbox();
            if (lb != null)
            {
                var tab = lb.getActiveTab();
                if (tab != null)
                {
                    simulateClick(tab);  // re-jig button doesn't work on its own
                }
            }
            if (Element.hasClassName(g_associatedTable, "treeTop") == false)
            {
                setRearrangeActions(getParentTable(g_associatedTable));
            }
        }
        else
        {
            alert('Deletion of this item failed on the server, please try again');
        }
    } 
    else
    {
        ajaxFail();
    }
}

function deleteIdCallback(res)
{
    generalIdRemoval(res, 'Deletion of this item failed on the server, please try again');
}

function revertIdCallback(res)
{
    generalIdRemoval(res, 'The status of this report could not be changed, please try again');
}

function generalIdRemoval(res, failureMessage)
{
    loading_hide();
    if (res.value != null)
    {
        if (res.value == 1)
        {
            var rowNum = getRowNumForElementSpec(g_elementSpec, g_associatedTable);
            var rowsToDelete = getRowsPerItemForTable(g_associatedTable);
            for (var i = 0 ; i < rowsToDelete ; i++)
            {
                g_associatedTable.deleteRow(rowNum);    
            }
        }
        else
        {
            alert(failureMessage);
        }
    } 
    else
    {
        ajaxFail();
    }
}

function unlinkCallback(res)
{
    generalIdRemoval(res, 'Deletion of this item failed on the server, please try again');
}

function copyCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        if (res.value == false)
        {
            alert('Unable to copy this item, please try again');
        }
    } 
    else
    {
        ajaxFail();
    }
}

function duplicateCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        if (res.value.m_resultType == 0)    // Paste only
        {
            alert("There is nothing suitable to be 'Pasted' - use 'Copy' to copy an element and then 'Paste' it at the appropriate place.");
            return;
        }
        var elementSpec = res.value.m_additionalElements[0].m_elementSpec;
        var markup = res.value.m_additionalElements[0].m_elementMarkup;
        var associatedTable = getAssociatedTableForElementSpec(elementSpec, true);
        if (res.value.m_resultType == mrt.MarkupResultType.ADDITIONAL_ROWS_ONLY)
        {
            if (elementSpec.m_generations.length == 0)
            {
                // the whole table structure needs replacing
                replaceTableMarkup(associatedTable, markup);
            }
            else
            {
                updateRowMarkup(markup, elementSpec, associatedTable);
                setDefaultExpansionOnTree(associatedTable);
            } 
        }
    } 
    else
    {
        ajaxFail();
    }
}

function pasteCallback(res)
{
    duplicateCallback(res);
}

function duplicateAuditCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        if (res.value.m_resultType == mrt.MarkupResultType.STANDARD_MARKUP)
        {
            g_elementSpec.m_newType = "x";  // force insert
            updateRowMarkup(res.value.m_markup, g_elementSpec, g_associatedTable);
        }
    } 
    else
    {
        ajaxFail();
    }
}

function editReportHeaderMarkupCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        lb = new lightbox(g_currentElement, res.value, g_associatedTable, g_idSpec, true);
        lb.activate();       
    }
    else
    {
        ajaxFail();
    }
}

function resetPasswordMarkupCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        lb = new lightbox(g_currentElement, res.value, g_associatedTable, g_idSpec, true);
        lb.activate();       
    }
    else
    {
        ajaxFail();
    }
}

function editReportHeaderCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        lb = new lightbox(g_currentElement, res.value, g_associatedTable, g_idSpec, true);
        lb.activate();       
    }
    else
    {
        ajaxFail();
    }
}

function getPDF(callingButton)
{
    showP();
    g_pdfButton = callingButton;
    ap.GetPDF(1, pdfCallback);
}

function pdfCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        var pdfLink = $('pdf');
        if (pdfLink != null)
        {
            Element.remove(pdfLink);
        }
        pdfLink = new Insertion.After(g_pdfButton, res.value);
        Element.scrollTo('pdf');
    }
    else
    {
        ajaxFail();
    }
}

function rebuildCab(callingButton)
{
    showP();
    ap.RebuildCab(1, cabCallback);
}

function cabCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        alert('The PDA cab files have been re-built with the current settings');
    }
    else
    {
        ajaxFail();
    }
}

function releaseAudit(callingButton, releaseType)
{
    showP();
    g_releaseButton = callingButton;
    ap.ReleaseAudit(releaseType, releaseCallback);
}

function releaseCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
          alert(res.value);
    } 
    else
    {
        ajaxFail();
    }
}

function validateInt(element)
{
    var valid = true;
    var str = $F(element);
    for (i = 0; i < str.length; i++)
    {
        ch = str.substring(i, i +1 );
        if (ch < "0" || ch > "9")
        {
            valid = false;
            break;
        }
    }
    if (valid == false)
    {
        alert("Please use only numeric values for this field.");
        setFocus(element);
    }
}

function validateUniqueName(uniqueName)
{
    if ((uniqueName == null) || (uniqueName == ""))
    {
        alert("Please enter a value for the unique name");
        return false;
    }
    if (uniqueName.indexOf(' ') != -1)
    {
        alert("Unique names cannot contain spaces");
        return false;
    }
    if (uniqueName.indexOf(':') != -1)
    {
        alert("Unique names cannot contain colons (:)");
        return false;
    }
    var validFirst = "abcdefghijklmnopqrstuvwxyz";
    var first = uniqueName.charAt(0).toLowerCase();
    if ((validFirst.indexOf(first) == -1) && (first != "_"))
    {
        alert("The first character of the unique name must be a letter or an underscore (_)");
        return false;
    }
    if (uniqueName.length > 2 && (uniqueName.substring(0, 3).toLowerCase() == "xml"))
    {
        alert("The unique name cannot start with the characters 'xml'");
        return false;
    }
    return true;
}

function addTableResultRow(callingTD, rowMarkup)
{
    var parentRow = callingTD.parentNode.parentNode;
    var parentTable = parentRow.parentNode.parentNode;
    var buttonRowIndex = parentRow.rowIndex;
    var templateRow = parentTable.rows[buttonRowIndex - 1];
    var newRow = parentTable.insertRow(buttonRowIndex);
    var firstNewCell = null;
    for (var i = 0 ; i < templateRow.cells.length ; i++)
    {
        var newCell = newRow.insertCell(i);
        newCell.innerHTML = rowMarkup.replace('xplacex', 100 / templateRow.cells.length);
        if (i == 0)
        {
            firstNewCell = newCell;
        }
    }
    var thisLb = getTopLightbox();
    thisLb.rejigSaveButton();
    setFocus(firstNewCell.childNodes[0]);
}

function cascadeList(parentList, childEntity, targetId)
{
    showP();
    var selectedValue = $F(parentList);
    g_operationTarget = $(targetId);
    ap.CascadeList(childEntity, selectedValue, Element.hasClassName(g_operationTarget.parentNode, "mand"), cascadeCallback);
}

function cascadeCallback(res)
{
    loading_hide();
    if (res.value != null)
    {
        g_operationTarget.options.length = 0;
        var xmlObj = xmlFragmentFromMarkup(res.value);
        for (var i = 0 ; i < xmlObj.childNodes.length ; i++)
        {
            var option = document.createElement("option");
            option.innerHTML = getHTMLFromXmlNodes(xmlObj.childNodes[i]);
            option.value = xmlObj.childNodes[i].attributes[0].nodeValue;
            g_operationTarget.appendChild(option);
        }
    } 
    else
    {
        ajaxFail();
    }
}

function displayOn(e)
{
    var el = $(e);
    if ((el != 'undefined') && (el != null))
    {
        el.style.display = '';
    }
}

function displayOff(e)
{
    var el = $(e);
    if ((el != 'undefined') && (el != null))
    {
        el.style.display = 'none';
    }
}

function toggleHelp(a)
{
    var help = $('help');
    if (help != null)
    {
        if (help.style.display != 'none')
        {
            help.style.display = 'none';
            a.childNodes[0].nodeValue = "Show help";
        }
        else
        {
            help.style.display = '';
            a.childNodes[0].nodeValue = "Hide help";
        }
    }
}

function helpsel(a)
{
    var help = $('help');
    var currentSelected = document.getElementsByClassName('hsel', help);
    if (currentSelected.length == 1)
    {
        Element.removeClassName(currentSelected[0], 'hsel');
    }
    Element.addClassName(a, 'hsel');
    var itemNum = a.id.substring(1, 2);
    var turnOn = $('ht' + itemNum);
    if (turnOn != null)
    {
        Element.removeClassName(turnOn, 'invis');
    }
    for (var i = 1; i < 4; i++)
    {
        if (i.toString() != itemNum)
        {
            var turnOff = $('ht' + i);
            if (turnOff != null)
            {
                Element.addClassName(turnOff, 'invis');
            }
        }
    }
}

function contraChanged(selectedContraChoice, newType)
{
    if (newType == 'na')
    {
        displayOff('tblcomply');
        displayOff('tblstd');
        displayOff('tblcrit');
    }
    else if (newType == "comp")
    {
        displayOff('tblstd');
        displayOff('tblcrit');
        displayOn('tblcomply');
    }
    else if (newType == "std")
    {
        displayOff('tblcomply');
        displayOff('tblcrit');
        displayOn('tblstd');
    }
    else if (newType == "crit")
    {
        displayOff('tblcomply');
        displayOn('tblcrit');
        displayOn('tblstd');
    }
}