//SHARE-IN-MEMORY=true
// Copyright 2006-2007 Adobe Systems Incorporated.  All rights reserved.

//--------------------------------------------------------------------
// CLASS:
//   AIRUtils
//
// DESCRIPTION:
//   This class contains helper methods and global properties used by all commands
//   from the Adobe AIR Extension.
//
//   Constructor:
//
//       AIRUtils()
//
//
// PUBLIC FUNCTIONS:
//
//      addNodeAsChild(theDOM, parentNode, nodeName, nodeString, position, relativeNodeName)
//      alert(message)
//      confirmDialog(message)
//      disableDocRelativeWarning(restore)
//      getImageDimensions(imageURL)
//      getNode(parentNode, elementPath)
//      getSiteRoot()
//      getSiteRelativePath(fileURL)
//      stripCharsForAppIDPreview(strContent)
//      stripCharsForAppID(strContent)
//      stripVolumeName(value)
//      trimNodeContent(strContent)
//      addClassName(className)
//      removeClassName(className)
//      parseApplicationDescriptor(APP_ICONS)
//      getPersistentFields()
//      updateAppDescriptor(paramObj, xmlFile, APP_ICONS, _sysChromeValue, _transparentValue)
//      getAppDescriptorDOM(xmlFile)
//      setListContents(gridElement, list)
//      getListContents(gridElement)
//      addItemToList(gridElement, item)
//      addItemAtPosition(gridElement, itemValue, pos)
//      getItemIndex(gridElement, itemValue)
//      removeItemFromList(gridElement, itemIndex, dontFixSelection)
//      removeAllItemsFromList(gridElement)
//      validateImagePathAndSize(site_root, imgPath, imgDim)
//--------------------------------------------------------------------


function AIRUtils()
{
}


// Public members
AIRUtils.FileSep                  = (navigator.platform == "Win32") ? "\\" : ":";
AIRUtils.appDescriptorName        = "application.xml";
AIRUtils.appDescrTemplateFolder   = AIRUtils.FileSep + "Shared" + AIRUtils.FileSep + "AdobeAIR" + AIRUtils.FileSep + "templates" + AIRUtils.FileSep;
AIRUtils.appDescrRootNode         = "application";
AIRUtils.appRootContentNode       = "rootContent";

AIRUtils.appContentNode           = "content";
AIRUtils.appInitialWindowNode     = "initialWindow";
AIRUtils.appSystemChromeNode      = "systemChrome";
AIRUtils.appTransparentNode       = "transparent";
AIRUtils.appVisibleNode           = "visible";
AIRUtils.appWidthNode             = "width";
AIRUtils.appHeightNode            = "height";

AIRUtils.appDescriptionNode       = "description";
AIRUtils.appCopyrightNode         = "copyright"; 
AIRUtils.appFileNameNode          = "filename";
AIRUtils.appFileNameNodeM5        = "name";
AIRUtils.appNameNode              = "name";
AIRUtils.appNameNodeM5            = "title";
AIRUtils.appUpdate                = "customUpdateUI";
AIRUtils.appUpdateM5              = "handleUpdates";
AIRUtils.appIDNode                = "id";
AIRUtils.appVersionNode           = "version";
AIRUtils.appIconsNode             = "icon";
AIRUtils.appImageNodePrefix       = "image";
AIRUtils.appImageNodeSuffixes     = ["128x128", "48x48", "32x32", "16x16"];

AIRUtils.fileTypesNode            = "fileTypes";
AIRUtils.fileTypeNode             = "fileType";
AIRUtils.fileTypeNameNode         = "name";
AIRUtils.fileTypeExtensionNode    = "extension";
AIRUtils.fileTypeDescriptionNode  = "description";
AIRUtils.fileTypeContentTypeNode  = "contentType";

AIRUtils.appProgramMenuFolderNode  = "programMenuFolder";
AIRUtils.appRootContentAttributes  = ["systemChrome", "transparent", "width", "height"];
AIRUtils.appIDAttribute           = "appId";
AIRUtils.appVersionAttribute      = "version";
AIRUtils.appXMLNSAttribute        = "xmlns";
AIRUtils.appXMLNSDefVal           = "http://ns.adobe.com/air/application/1.0.M6"
AIRUtils.appSystemChromeAttribute = "systemChrome";
AIRUtils.appTransparentAttribute  = "transparent";
AIRUtils.appWidthAttribute        = "width";
AIRUtils.appHeightAttribute       = "height";
// Package interface values
AIRUtils.windowModeLabels      = ["System Chrome", "Custom Chrome (opaque)", "Custom Chrome (transparent)"];
AIRUtils.windowModeValues      = ["system", "opaque", "transparent"];
AIRUtils.windowModeDefValue      = "system";
AIRUtils.windowWidthDefValue     = "800";
AIRUtils.windowHeightDefValue    = "600";
AIRUtils.versionDefValue         = "1";
AIRUtils.applicationDefName      = "AIR application";
AIRUtils.packageTypeAIRValue     = "packageAIR"
AIRUtils.packageTypeAIRIValue    = "packageAIRI"
// Choose icons interface values
AIRUtils.acceptedImageTypes      = ["PNG Files (*.png)|*.png|TEXT|"];
AIRUtils.acceptedAppIconDims     = ["128", "48", "32", "16"];
AIRUtils.htmlDocumentsFilter     = ["HTML Documents (*.htm; *.html; *.xhtml)|*.htm;*.html;*.xhtml|TEXT|"];

AIRUtils.airOnWeb                = "http://www.adobe.com/go/air";
AIRUtils.NotesGlobalPersistance  = AIRUtils.FileSep + "Shared" + AIRUtils.FileSep + "AdobeAIR" + AIRUtils.FileSep + "air_settings";
AIRUtils.NotesLocalPersistance   = "air_settings";
AIRUtils.NotesKeyAIRDestFolder   = "airDestinationFolder";
AIRUtils.NotesKeyAIRDestFileName = "airDestinationFileName";
AIRUtils.NotesKeyAIRCopyright    = "airCopyright";
AIRUtils.NotesKeyAIRFilesList    = "airFilesList";
AIRUtils.NotesKeyAIRFullFilesList     = "airFullFilesList";
AIRUtils.NotesKeyAIRProgramMenuFolder = "airProgramMenuFolder";
AIRUtils.NotesKeyAIRMRUCerts     = "airMRUCerts";
AIRUtils.NotesKeyAIRChosenCert   = "airChosenCert";
AIRUtils.NotesKeyAIRPackageType  = "airPackageType";
AIRUtils.NotesKeyAIRRememberPassword = "airRememberPassword";
AIRUtils.NotesKeyAIRPublisher    = "airPublisher";
AIRUtils.NotesKeyAIROrgName      = "airOrgName";
AIRUtils.NotesKeyAIROrgUnit      = "airOrgUnit";
AIRUtils.NotesKeyAIRCountry      = "airCountry";
AIRUtils.NotesKeyAIRCertType     = "airCertType";
AIRUtils.NotesKeyAIRLastSelfCert = "airLastSelfCert";

AIRUtils.NotesKeyAIRSep          = "#"; 
AIRUtils.suppressDocRelativeWarning = "TRUE";
AIRUtils.TempAppXMLPath          = "/app-tmp.xml";

// Self-signed certificate values
AIRUtils.countryLabels           = [ "United States", "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowin", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Repub", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Island", "Colombia", "Comoros", "Congo", "Cook Islands", "Costa Rica", "Cote D'Ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands", "Faroe Islands", "Fiji", "Finland", "France", "France, Metropolitan", "French Guiana", "French Polynesia", "French Southern Terri", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-bissau", "Guyana", "Haiti", "Heard and Mc Donald I", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Korea, Democratic Peo", "Korea, Republic of", "Kuwait", "Kyrgyzstan", "Lao People's Democrat", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriy", "Liechtenstein", "Lithuania", "Luxembourg", "Macau", "Macedonia, The Former", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated", "Moldova, Republic of", "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "Saint Kitts and Nevis", "Saint Lucia", "Saint Vincent and the", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and the", "Spain", "Sri Lanka", "St. Helena", "St. Pierre and Miquel", "Sudan", "Suriname", "Svalbard and Jan Maye", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", "Taiwan", "Tajikistan", "Tanzania, United Repu", "Thailand", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom","United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Virgin Islands (British)", "Virgin Islands (U.S.)", "Wallis and Futuna Islands", "Western Sahara", "Yemen", "Yugoslavia", "Zaire", "Zambia", "Zimbabwe"];
AIRUtils.countryValues           = ["US", "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CK", "CR", "CI", "HR", "CU", "CY", "CZ", "DK", "DJ", "DM", "DO", "TP", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "FX", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GN", "GW", "GY", "HT", "HM", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IL", "IT", "JM", "JP", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "AN", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "NO", "OM", "PK", "PW", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "KN", "LC", "VC", "WS", "SM", "ST", "SA", "SN", "SC", "SL", "SG", "SK", "SI", "SB", "SO", "ZA", "GS", "ES", "LK", "SH", "PM", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "UM", "UY", "UZ", "VU", "VA", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "YU", "ZR", "ZM", "ZW"];
AIRUtils.certTypeLabels          = ["1024-RSA", "2048-RSA"];
AIRUtils.certTypeValues          = ["1024-RSA", "2048-RSA"];
AIRUtils.acceptedCertTypes       = ["Digital ID Files (*.pfx)|*.pfx|"];
AIRUtils.certTypeDefValue        = "1024-RSA";

// Static Methods
AIRUtils.trimNodeContent            = AIRUtils_trimNodeContent;
AIRUtils.getNode                    = AIRUtils_getNode;
AIRUtils.getChildNode               = AIRUtils_getChildNode;
AIRUtils.stripCharsForAppID         = AIRUtils_stripCharsForAppID;
AIRUtils.getImageDimensions         = AIRUtils_getImageDimensions;
AIRUtils.getSiteRoot                = AIRUtils_getSiteRoot;
AIRUtils.getSiteRelativePath        = AIRUtils_getSiteRelativePath;
AIRUtils.addNodeAsChild             = AIRUtils_addNodeAsChild;
AIRUtils.disableDocRelativeWarning  = AIRUtils_disableDocRelativeWarning;
AIRUtils.stripCharsForAppIDPreview  = AIRUtils_stripCharsForAppIDPreview;
AIRUtils.stripVolumeName            = AIRUtils_stripVolumeName;
AIRUtils.alert                      = AIRUtils_alert;
AIRUtils.confirmDialog              = AIRUtils_confirmDialog;
AIRUtils.enablePreview              = AIRUtils_enablePreview;
AIRUtils.runPreview                 = AIRUtils_runPreview;
AIRUtils.addClassName               = AIRUtils_addClassName;
AIRUtils.removeClassName            = AIRUtils_removeClassName;
AIRUtils.appendErr                  = AIRUtils_appendErr;
AIRUtils.parseApplicationDescriptor = AIRUTILS_parseApplicationDescriptor;
AIRUtils.getPersistentFields        = AIRUTILS_getPersistentFields;
AIRUtils.updateAppDescriptor        = AIRUTILS_updateAppDescriptor;
AIRUtils.getAppDescriptorDOM        = AIRUTILS_getAppDescriptorDOM;
AIRUtils.setListContents            = AIRUTILS_setListContents;
AIRUtils.getListContents            = AIRUTILS_getListContents;
AIRUtils.addItemToList              = AIRUTILS_addItemToList;
AIRUtils.addItemAtPosition          = AIRUTILS_addItemAtPosition;
AIRUtils.getItemIndex               = AIRUTILS_getItemIndex;
AIRUtils.removeItemFromList         = AIRUTILS_removeItemFromList;
AIRUtils.removeAllItemsFromList     = AIRUTILS_removeAllItemsFromList;
AIRUtils.validateImagePathAndSize   = AIRUTILS_validateImagePathAndSize;

//--------------------------------------------------------------------
// FUNCTION:
//   trimNodeContent
//
// DESCRIPTION:
//   This function removes the new lines, spaces and tabs from the begining and from the end 
//   of the supplied string. 
//
// ARGUMENTS:
//   strContent - string - original string that needs to be trimmed
//
// RETURNS:
//   (string) - trimmed string
//--------------------------------------------------------------------
function AIRUtils_trimNodeContent(strContent)
{
  if (typeof(strContent) == "string")
  {
    return strContent.replace(/^[\r\n\t ]*/, "").replace(/[\r\n\t ]*$/, "");
  }
  return strContent;
}

//--------------------------------------------------------------------
// FUNCTION:
//   getNode
//
// DESCRIPTION:
//   Helper function that returns the node specified by path parameter which is a child
//   of the theNode parameter.
//   Note - this goes down to any level of decendents, not just direct children
// ARGUMENTS:
//   parentNode - object - reference dom object
//   path - string - path that specify the node that needs to be found
//
// RETURNS:
//   object - dom object if the node is found, null otherwise
//--------------------------------------------------------------------
function AIRUtils_getNode(parentNode, elementPath) {
  var intermediateNodes = elementPath.split("/");
  var node = parentNode;

  while (intermediateNodes && intermediateNodes.length && node) {
    var children = parentNode.getElementsByTagName(intermediateNodes[0]);
    if (children && children[0]) {
      node = children[0];
      intermediateNodes.splice(0, 1);
    } else {
      node = null;
      break;
    }
  }

  return node;
}

//--------------------------------------------------------------------
// FUNCTION:
//   getChildNode
//
// DESCRIPTION:
//   Similar to getNode, but just gets the direct child (no grandchildren, etc)
//   with the given tagname.
// ARGUMENTS:
//   parentNode - object - reference dom object
//   tagName - name of the child to be found
//
// RETURNS:
//   object - dom object if the node is found, null otherwise
//--------------------------------------------------------------------
function AIRUtils_getChildNode(parentNode, tagName) {
  if (!parentNode || typeof(parentNode) == "undefined")
    return null;

  var children = parentNode.childNodes;
  for (var i = 0; i < children.length; i++)
  {
     // need to lowercase the names because sometimes they end up in upper case
     if (children[i].tagName.toLowerCase() == tagName.toLowerCase())
     {
       return children[i];
     }
  }

  return null;
}



//--------------------------------------------------------------------
// FUNCTION:
//   stripCharsForAppIDPreview
//
// DESCRIPTION:
//   Removes all the characters that are not valid for the Application ID.
//
//
// ARGUMENTS:
//   strContent (string) - original string
//
// RETURNS:
//   (string) - stripped string
//--------------------------------------------------------------------
function AIRUtils_stripCharsForAppIDPreview(strContent)
{
  // for preview will strip all characters that are not in the range bellow
  // since the application id has only a symbolic meaning
  strContent = strContent.replace(/[^a-zA-Z0-9-.]*/g, "");
  return strContent; 
}

//--------------------------------------------------------------------
// FUNCTION:
//   stripCharsForAppID
//
// DESCRIPTION:
//   This function removes all punctuation characters  from a given string.
//
// ARGUMENTS:
//   strContent (string) - original string
//
// RETURNS:
//   (string) - stripped string
//--------------------------------------------------------------------
function AIRUtils_stripCharsForAppID(strContent)
{
  strContent = strContent.replace(/[\x20-\x2C\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]*/g, "");
  return strContent; 
}

//--------------------------------------------------------------------
// FUNCTION:
//   getImageDimensions
//
// DESCRIPTION:
//   This function retrieves dimensions for the specified image.
//
// ARGUMENTS:
//   imageURL (string) - full path to an image file
//
// RETURNS:
//   (string) -  a string wich has the form nnxmm where nn is the width and mm is the height 
//               of the specified image if the file specified by the imageURL parameter
//               is a valid image file
//--------------------------------------------------------------------
function AIRUtils_getImageDimensions(imageURL) {
  var dimStr = "";
  var dimensions = dw.getNaturalSize(imageURL);
  if (dimensions && dimensions[0] && dimensions[1]) {
    dimStr = dimensions[0] + "x" + dimensions[1];
  }

  return dimStr;
}

//--------------------------------------------------------------------
// FUNCTION:
//   getSiteRoot
//
// DESCRIPTION:
//   This function returns the local path of the current selected site 
//   in the Site panel and ensure that the path ends with a path separator.  
//   
// ARGUMENTS:
//   none
//
// RETURNS:
//   string - the path to the current site
//--------------------------------------------------------------------
function AIRUtils_getSiteRoot() {
  var siteName = site.getCurrentSite();
  var siteRoot = "";

  if (siteName)
  {  
    siteRoot =  site.getLocalRootURL(siteName);
    siteRoot += "/";
    siteRoot = siteRoot.replace(/\/\/$/gi, "/");
  }

  return siteRoot;
}



function AIRPREVIEW_getSiteRelativePath(fileURL)
{
  var relativePath = fileURL;
  var siteName = site.getSiteForURL(fileURL);
  if (siteName != "")
  {
    var siteRootPath = AIRUtils.getSiteRoot()
    
  	//we have a site, remove the site root from the beginning of the file's path
  	relativePath = relativePath.substring(siteRootPath.length);
  }

  return relativePath;
}


//--------------------------------------------------------------------
// FUNCTION:
//   getSiteRelativePath
//
// DESCRIPTION:
//   Given a file URL, this function returns the site relative path.
//   It returns the local file path if the file doesn't belong to
//	 the current site
// NOTE:
//   There is based on dwscripts.getSiteRelativePath(), but that
//   gives the wrong answer if there is a file with focus that is
//   outside of the site root (because of site.getLocalPathToFiles()).
//
// ARGUMENTS:
//   fileURL - string - the URL to convert
//
// RETURNS:
//   string
//--------------------------------------------------------------------
function AIRUtils_getSiteRelativePath(fileURL)
{

  var nameStr = dwscripts.localURLToFilePath(fileURL);
  
  //see if this file is part of a site and if so, set the nameStr to be the
  //site relative path instead of the full file path
  var siteName = site.getSiteForURL(fileURL);
  if (siteName != "" && siteName == site.getCurrentSite())
  {
	//we have a site, remove the site root from the beginning of the file's path
	var localRootURL = site.getLocalRootURL(site.getCurrentSite());
	var localRootPath = dwscripts.localURLToFilePath(localRootURL);

	nameStr = nameStr.substring(localRootPath.length);
  }

  return nameStr;
}

//--------------------------------------------------------------------
// FUNCTION:
//   addNodeAsChild
//
// DESCRIPTION:
//   This function insert a node in the document specified by the "theDOM" parameter,
//   in the position specified by "position" parameter.
//
// ARGUMENTS:
//   theDOM (object) - the document that needs to be modified
//   parentNode (object) - dom object to wich the specified node will be added as a child
//   nodeName (string) - name of the new node (this is usefful for retrieving the newly created node)
//   nodeString (string) - the new node text
//   position (string) - represents the relative position where the  node will be inserted
//                     - accepted values are: 
//                            - firstChild - insert this node as first child of the parentNode parameter     
//                            - lastChild  - insert this node as last child of the parentNode parameter     
//                            - before     - insert this node before the node specified as relativeNodeName parameter     
//                            - after      - insert this node after the node specified as relativeNodeName parameter     
//  relativeNodeName (string) - this parameter make sense only when the value of the position parameter is "before" or "after"
//                             
//
// RETURNS:
//   (object) - the new inserted node if no error occured, null otherwise
//--------------------------------------------------------------------
function AIRUtils_addNodeAsChild(theDOM, parentNode, nodeName, nodeString, position, relativeNodeName)
{
  var retNode;
  // create this node as first child of the root element
  switch(position)
  {
    case "firstChild":
        parentNode.innerHTML = nodeString + "\n" + parentNode.innerHTML;
        break;
    case "lastChild":
        parentNode.innerHTML += "\n" + nodeString;
        break;
    case "before":
        var relativeNode = AIRUtils.getNode(parentNode, relativeNodeName);
        if (relativeNode)
        {
          relativeNode.outerHTML = nodeString + "\n" + relativeNode.outerHTML          
        }
        break;
    case "after":
        var relativeNode = AIRUtils.getNode(parentNode, relativeNodeName);
        if (relativeNode)
        {
          relativeNode.outerHTML += "\n" + nodeString;          
        }
        break;
  }
  var nodes = parentNode.getElementsByTagName(nodeName);
  if (nodes && nodes.length)
  {
    for (var i = 0; i < nodes.length; i++)
    {
      if (nodes[i].nodeType == Node.ELEMENT_NODE && nodes[i].outerHTML.indexOf(nodeString) != -1)
      {
        retNode = nodes[i];
      }
    }
  }
  
  //we need to determine the modified dom to refresh itself
  theDOM.documentElement.outerHTML = theDOM.documentElement.outerHTML; 
  
  return retNode;
}


//--------------------------------------------------------------------
// FUNCTION:
//   disableDocRelativeWarning
//
// DESCRIPTION:
//   This function disable/restore the value of the "Doc Relative Warning" registry key.
//
// ARGUMENTS:
//   restore (boolean) - decide if the previous value should be restored or the 
//                      alert should not be shown
//
// RETURNS:
//   nothing
//--------------------------------------------------------------------
function AIRUtils_disableDocRelativeWarning(restore)
{
  var value = "TRUE";
  if (!restore)
  {
    AIRUtils.suppressDocRelativeWarning = dw.getPreferenceString("Optional Dialog Windows", "Doc Relative Warning", "FALSE");
  }
  else
  {
    value = AIRUtils.suppressDocRelativeWarning;
  }
  dw.setPreferenceString("Optional Dialog Windows", "Doc Relative Warning", value);
}

//--------------------------------------------------------------------
// FUNCTION:
//   stripVolumeName
//
// DESCRIPTION:
//   This function its usefull for the MAC platform and removes the volume name 
//   from the path specified by value parameter.
//    
//
// ARGUMENTS:
//   value (string) - an absolute mac system path 
//
// RETURNS:
//   string - path from which the volume name was stripped
//--------------------------------------------------------------------
function AIRUtils_stripVolumeName(value)
{
  var retValue = value;
  
  if (retValue)
  {
    retValue = retValue.replace(/^[^:]+:/, '');
  }
   return retValue;
}

//--------------------------------------------------------------------
// FUNCTION:
//   alert
//
// DESCRIPTION:
//   This function it's used to display all error messages from the Adobe AIR Extension.
//
// ARGUMENTS:
//   message (string) - the message that needs to be displayed
//
// RETURNS:
//   nothing
//--------------------------------------------------------------------
function AIRUtils_alert(message)
{
  var dialogObj = new Object();
  dialogObj.dialogType = "alert";
  dialogObj.message = message;
  dwscripts.callCommand("AIRAlert.html", dialogObj);
}

//--------------------------------------------------------------------
// FUNCTION:
//   confirmDialog
//
// DESCRIPTION:
//   This function display a confirmation dialog, with Yes and No buttons.
//
// ARGUMENTS:
//   message (string) - message that is displayed in the dialog
//
// RETURNS:
//   (number) - 1 if the Yes button was pressed in the confirm dialog
//              0 if the No button was pressed
//              empty string if the dialog was dismised using the Esc key or using 
//              the close button  
//              
//--------------------------------------------------------------------
function AIRUtils_confirmDialog(message)
{
  return dwscripts.callCommand("AIRConfirm.html", message);
}

//--------------------------------------------------------------------
// FUNCTION:
//   enablePreview
//
// DESCRIPTION:
//   This function decide if the Preview in Adobe AIR menu item from the site 
//   contextual menu will be enbaled or grayed. 
//
// ARGUMENTS:
//   none
//
// RETURNS:
//   (boolean) - true - if current selected file from the site is html file, false otherwise
//              
//--------------------------------------------------------------------
function AIRUtils_enablePreview()
{
  var retValue = false;
  var siteRootPath = AIRUtils.getSiteRoot();

  if (siteRootPath.length = 0)
    return false;

  var arrSelection = site.getSelection();
  if (arrSelection && arrSelection.length == 1)
  {
    var fileExt = dwscripts.getFileExtension(arrSelection[0]);
    var acceptedExtensions = ["htm", "html", "xhtml", "shtm", "shtml", "swf"];
    if (dwscripts.findInArray(acceptedExtensions, fileExt) != -1 )
    {
      retValue = true;    
    }
  }
  
  return retValue;
}

//--------------------------------------------------------------------
// FUNCTION:
//   runPreview
//
// DESCRIPTION:
//   This function is called from from the site contextual menu to preview 
//  selected file.
//
// ARGUMENTS:
//   none
//
// RETURNS:
//   nothing
//              
//--------------------------------------------------------------------
function AIRUtils_runPreview()
{
  var arrSelection = site.getSelection();
  if (arrSelection && arrSelection.length == 1)
  {
    var previewParamObj = new Object();
    previewParamObj.contentFile = arrSelection[0];
    dwscripts.callCommand("AIRPreview.htm", previewParamObj);
  }
}

//--------------------------------------------------------------------
// FUNCTION:
//   addClassName
//
// DESCRIPTION:
//   Add the given className to the given element, if it's not already there
//
// ARGUMENTS:
//   element, className
//
// RETURNS:
//   nothing
//              
//--------------------------------------------------------------------
function AIRUtils_addClassName(ele, className)
{
	if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) != -1))
		return;
	ele.className += (ele.className ? " " : "") + className;
};

//--------------------------------------------------------------------
// FUNCTION:
//   removeClassName
//
// DESCRIPTION:
//   Remove the given className from the given element, if it's there.
//
// ARGUMENTS:
//   none
//
// RETURNS:
//   nothing
//              
//--------------------------------------------------------------------
function AIRUtils_removeClassName(ele, className)
{
  if (!ele || !className || (ele.className && ele.className.search(new RegExp("\\b" + className + "\\b")) == -1))
    return;
  ele.className = ele.className.replace(new RegExp("\\s*\\b" + className + "\\b", "g"), "");
};


//--------------------------------------------------------------------
// FUNCTION:
//   appendErr
//
// DESCRIPTION:
//   Builds error string, with blank lines in between errors,
//   as appropriate.
//   ALSO highlight passed-in element
//
// ARGUMENTS:
//   errStr: existing error string
//   thisErrStr: new error to append
//   
//
// RETURNS:
//   errStr, with new error appended
//--------------------------------------------------------------------
function AIRUtils_appendErr(errStr, thisErrStr, elem) {
  if (errStr)
  {
    errStr = errStr + "<br/><br/>" + thisErrStr;
  }
  else
  {
    errStr = thisErrStr;
  }

  if (elem)
  {
    AIRUtils.addClassName(elem, "hilite");
  }
  return errStr;
}


//--------------------------------------------------------------------
// FUNCTION:
//   parseApplicationDescriptor
//
// DESCRIPTION:
//   This function will parse the application.xml file if it exists in current site, and will
//   return an object with all the values that can be retrieved.
//   It can parse AIR m4, m5 and m6 descriptor files.
//
//
// ARGUMENTS:
//   APP_ICONS
//
// RETURNS:
//   An object with key-value pairs if the application.xml exists in current site 
//   and contain valid values, NULL otherwise.
//--------------------------------------------------------------------
function AIRUTILS_parseApplicationDescriptor(APP_ICONS)
{
  var theDOM;
  var xmlFile = AIRUtils.getSiteRoot() + AIRUtils.appDescriptorName;

  if (DWfile.exists(xmlFile))
  {
    // make sure it isn't a folder
    if (dwscripts.isFolder(xmlFile))
    {
      var errStr = dw.loadString("AIRMsg_AppXMLFolder");
      AIRUtils.alert(errStr);
      return;
    }

    theDOM = dw.getDocumentDOM(xmlFile);
  }
  else
  {
    return;
  }

  var error = false;
  var paramObj = new Object();
  paramObj.invalidXML = false;
  var parsingM5orEarlier = false;

  if (theDOM)
  {
    // validate the current document
    theDOM.runValidation("xml", false);

    if (!dreamweaver.resultsPalette.validator.getItemCount())
    {
      var rootNode = theDOM.getElementsByTagName(AIRUtils.appDescrRootNode);
      if (rootNode && rootNode[0])
      {
        // xmlns
        var attribVal = rootNode[0].getAttribute(AIRUtils.appXMLNSAttribute);
        // does it have M4 or M5?

        if ( (attribVal.search(/M4/) != -1) || (attribVal.search(/M5/) != -1) )
          parsingM5orEarlier = true;

        // id (only for M5 or earlier)
        attribVal = rootNode[0].getAttribute(AIRUtils.appIDAttribute);
        if (attribVal)
        {
          paramObj[AIRUtils.appIDNode] = attribVal;
        }

        // version (only for M5 or earlier)
        attribVal = rootNode[0].getAttribute(AIRUtils.appVersionAttribute);
        if (attribVal)
        {
          paramObj[AIRUtils.appVersionNode] = attribVal;
        }

        var childElements = rootNode[0].childNodes;
        if (childElements && childElements.length)
        {
          for (var i = 0; i < childElements.length; i++)
          {
            if (childElements[i].nodeType == Node.ELEMENT_NODE)
            {
              if (childElements[i].tagName.toLowerCase() == AIRUtils.appRootContentNode.toLowerCase())
              {
                // get the attributes of the rootContent node
                for (var j = 0; j < AIRUtils.appRootContentAttributes.length; j++)
                {
                  paramObj[AIRUtils.appRootContentAttributes[j]] = childElements[i].getAttribute(AIRUtils.appRootContentAttributes[j]);
                }
                paramObj[AIRUtils.appRootContentNode] = AIRUtils.trimNodeContent(childElements[i].innerHTML);
              }
              // get the icons
              else if (childElements[i].tagName.toLowerCase() == AIRUtils.appIconsNode)
              {
                var iconNodes = childElements[i].childNodes;
                for (var j = 0; j < iconNodes.length; j++)
                {
                  if (iconNodes[j].nodeType == Node.ELEMENT_NODE)
                  {
                    if (iconNodes[j].tagName.toLowerCase().indexOf(AIRUtils.appImageNodePrefix) != -1)
                    {
                      var iconName =  iconNodes[j].tagName.replace(new RegExp(AIRUtils.appImageNodePrefix, "i"), "");
                      APP_ICONS[iconName.toLowerCase()] =  AIRUtils.trimNodeContent(iconNodes[j].innerHTML); 
                    }
                  }             
                }
              }
              else if (childElements[i].tagName.toLowerCase() == AIRUtils.fileTypesNode.toLowerCase())
              {
                // file type registration
                var fileTypeNodes = childElements[i].childNodes;
                if (fileTypeNodes.length)
                {
                  paramObj.fileTypes = new Array();
                }
                for (var j = 0; j < fileTypeNodes.length; j++)
                {
                  if (fileTypeNodes[j].nodeType == Node.ELEMENT_NODE && fileTypeNodes[j].tagName.toLowerCase() == AIRUtils.fileTypeNode.toLowerCase())
                  {
                    // make a new fileType structure in file types list
                    fileTypeObj = new Object();
                    var fileTypeChildren = fileTypeNodes[j].childNodes;
                    if (fileTypeChildren && fileTypeChildren.length)
                    {
                      for (var k = 0; k < fileTypeChildren.length; k++)
                      {
                        if (fileTypeChildren[k].tagName.toLowerCase() == AIRUtils.appIconsNode)
                        {
                          var localAppIcons = new Object(); // = {"128x128": "", "48x48": "", "32x32": "", "16x16": ""};
                          var iconNodes = fileTypeChildren[k].childNodes;

                          for (var n = 0; n < iconNodes.length; n++)
                          {
                            if (iconNodes[n].nodeType == Node.ELEMENT_NODE)
                            {
                              if (iconNodes[n].tagName.toLowerCase().indexOf(AIRUtils.appImageNodePrefix) != -1)
                              {
                                var iconName =  iconNodes[n].tagName.replace(new RegExp(AIRUtils.appImageNodePrefix, "i"), "");
                                localAppIcons[iconName] =  AIRUtils.trimNodeContent(iconNodes[n].innerHTML);
                              }
                            }
                          } // for iconNodes

                          fileTypeObj.icons = localAppIcons;

                        } // app icons node
                        else
                        {
                          fileTypeObj[fileTypeChildren[k].tagName.toLowerCase()] = AIRUtils.trimNodeContent(fileTypeChildren[k].innerHTML);
                        }
                      } // children of fileType node
                    }
                    // add this filetype to the list
                    paramObj.fileTypes.push(fileTypeObj);
                  } // fileType node

                }
              } // fileTypes node
              else if (childElements[i].tagName.toLowerCase() == AIRUtils.appInitialWindowNode.toLowerCase())
              {
                var winChildElements = childElements[i].childNodes;
                if (winChildElements && winChildElements.length)
                {
                  for (var j = 0; j < winChildElements.length; j++)
                  {
                    if (winChildElements[j].nodeType == Node.ELEMENT_NODE)
                    {
                      if (winChildElements[j].tagName.toLowerCase() == AIRUtils.appContentNode.toLowerCase())
                      {
                        paramObj[AIRUtils.appRootContentNode] = AIRUtils.trimNodeContent(winChildElements[j].innerHTML);
                      }
                      else
                      {
                        paramObj[winChildElements[j].tagName] = AIRUtils.trimNodeContent(winChildElements[j].innerHTML);
                      }
                    }
                  }
                }
              }
              else if (childElements[i].tagName.toLowerCase() == AIRUtils.appProgramMenuFolderNode.toLowerCase())
              {
                paramObj[AIRUtils.appProgramMenuFolderNode] = AIRUtils.trimNodeContent(childElements[i].innerHTML);
              }
              else
              {
                // if app descriptor is M5 or earlier, save title as name, name as filename
                var keepName = true;
                if (parsingM5orEarlier)
                {
                  keepName = false;
                  if (childElements[i].tagName.toLowerCase() == AIRUtils.appNameNodeM5.toLowerCase())
                  {
                    paramObj[AIRUtils.appNameNode] = AIRUtils.trimNodeContent(childElements[i].innerHTML);
                    childElements[i].outerHTML = "";
                  }
                  else if (childElements[i].tagName.toLowerCase() == AIRUtils.appFileNameNodeM5.toLowerCase())
                  {
                    paramObj[AIRUtils.appFileNameNode] = AIRUtils.trimNodeContent(childElements[i].innerHTML);
                  }
                  else if (childElements[i].tagName.toLowerCase() == AIRUtils.appUpdateM5.toLowerCase())
                  {
                    paramObj[AIRUtils.appUpdate] = "true";
                  }
                  else
                    keepName = true;
                }
                if (keepName)
                {
                  paramObj[childElements[i].tagName.toLowerCase()] = AIRUtils.trimNodeContent(childElements[i].innerHTML);
                }
              }
            }
          } // for childElements
        } // if childElements

        paramObj.parsingM5orEarlier = parsingM5orEarlier;
      } // root node ok
      else
      {
        var dialogObj = new Object();
        dialogObj.dialogType = "alert";
        dialogObj.message = dwscripts.sprintf(dw.loadString('AIRMsg_AppXMLRootNodeMissing'), 
                                  "dw.openDocument('" + theDOM.URL + "'); window.close();");
        dwscripts.callCommand("AIRAlert.htm", dialogObj);
        paramObj.invalidXML = true;
        window.close();
      }
    }
    else
    {
      var dialogObj = new Object();
      dialogObj.dialogType = "alert";
      dialogObj.message = dwscripts.sprintf(dw.loadString('AIRMsg_AppXMLCorupted'), 
                                  "dw.openDocument('" + theDOM.URL + "'); dw.showResults('validation', '" + theDOM.URL + "'); window.close();");
      dwscripts.callCommand("AIRAlert.html", dialogObj);
      paramObj.invalidXML = true;
      window.close();
    }
  }

  return paramObj;
}


//--------------------------------------------------------------------
// FUNCTION:
//   getPersistentFields
//
// DESCRIPTION:
//   This function will extract all values from the "local persistence" file and "global persistence"
//   file, and pack them as an array of key-value pairs.
//
// ARGUMENTS:
//   none 
//
// RETURNS:
//   (object) - an array of key-value pairs if there are some. 
//--------------------------------------------------------------------
function AIRUTILS_getPersistentFields()
{
  var paramObj;
  var globalPersistenceFilePath = dwscripts.filePathToLocalURL(AIRext.GetAIRInstallPath() + AIRUtils.NotesGlobalPersistance);
  var localPersistenceFilePath = AIRUtils.getSiteRoot() + AIRUtils.NotesLocalPersistance;
  var notesHandleGlobal = MMNotes.open(globalPersistenceFilePath);
  var notesHandleLocal = MMNotes.open(localPersistenceFilePath);

  paramObj = new Object();
  if (notesHandleGlobal)
  {
    paramObj.copyright = MMNotes.get(notesHandleGlobal, AIRUtils.NotesKeyAIRCopyright);
    paramObj.programMenuFolder = MMNotes.get(notesHandleGlobal, AIRUtils.NotesKeyAIRProgramMenuFolder);

    var mruCertsStr = MMNotes.get(notesHandleGlobal, AIRUtils.NotesKeyAIRMRUCerts);
    MMNotes.close(notesHandleGlobal);
	 
	if (mruCertsStr)
	{
      var arrMruCertsList = mruCertsStr.split(AIRUtils.NotesKeyAIRSep);
      if (arrMruCertsList)
      {
        paramObj.mruCertsValsList = new Array();
        paramObj.mruCertsLabelsList = new Array();
        if (arrMruCertsList.length)
        {
          for (var i = 0; i < arrMruCertsList.length; i++)
          {
            if (arrMruCertsList[i])
            {
              paramObj.mruCertsValsList.push(arrMruCertsList[i]);
              // put just the tail into the labels list
              var fullPath = arrMruCertsList[i];
              var label = dwscripts.filePathToLocalURL(fullPath)
              label = dwscripts.getFileName(label);            
              paramObj.mruCertsLabelsList.push(label);
            }
          }        
        }
        else
        {
          var fullPath = arrMruCertsList[i];
          var label = fullPath;
          paramObj.mruCertsValsList.push(arrMruCertsList);
          paramObj.mruCertsLabelsList(fullPath);
        }
      }
	}
  }

  if (notesHandleLocal)
  {
     paramObj.packageDestinationFolder = MMNotes.get(notesHandleLocal, AIRUtils.NotesKeyAIRDestFolder);
     paramObj.packageDestinationFile = MMNotes.get(notesHandleLocal, AIRUtils.NotesKeyAIRDestFileName);
     paramObj.chosenCert = MMNotes.get(notesHandleLocal, AIRUtils.NotesKeyAIRChosenCert);
     paramObj.packageType = MMNotes.get(notesHandleLocal, AIRUtils.NotesKeyAIRPackageType);
     paramObj.rememberPassword = MMNotes.get(notesHandleLocal, AIRUtils.NotesKeyAIRRememberPassword);

     // set some defaults, if values are missing
     // if packageType isn't set, set it to AIR
     if (paramObj.packageType = "")
        paramObj.packageType = AIRUtils.packageTypeAIRValue;
     if (paramObj.rememberPassword = "")
       paramObj.rememberPassword = false;

     var filesListStr = MMNotes.get(notesHandleLocal, AIRUtils.NotesKeyAIRFilesList);
     var fullFilesListStr = MMNotes.get(notesHandleLocal, AIRUtils.NotesKeyAIRFullFilesList);

     if (filesListStr)
     {
      var arrFilesList = filesListStr.split(AIRUtils.NotesKeyAIRSep);
      if (arrFilesList)
      {
        paramObj.filesList = new Array();
        if (arrFilesList.length)
        {
          for (var i = 0; i < arrFilesList.length; i++)
          {
            if (arrFilesList[i] && arrFilesList[i] != AIRUtils.appDescriptorName && DWfile.exists(AIRUtils.getSiteRoot() + arrFilesList[i]))
            {
              paramObj.filesList.push(arrFilesList[i]);
            }
          }        
        }
        else if (arrFilesList != AIRUtils.appDescriptorName && DWfile.exists(AIRUtils.getSiteRoot() + arrFilesList))
        {
          paramObj.filesList.push(arrFilesList);
        }
      } 
     }
     if (fullFilesListStr)
     {
      var arrFilesList = fullFilesListStr.split(AIRUtils.NotesKeyAIRSep);
      if (arrFilesList)
      {
        paramObj.fullFilesList = new Array();
        if (arrFilesList.length)
        {
          for (var i = 0; i < arrFilesList.length; i++)
          {
            if (arrFilesList[i] && arrFilesList[i] != AIRUtils.appDescriptorName && DWfile.exists(AIRUtils.getSiteRoot() + arrFilesList[i]))
            {
              paramObj.fullFilesList.push(arrFilesList[i]);
            }
          }        
        }
        else if (arrFilesList != AIRUtils.appDescriptorName && DWfile.exists(AIRUtils.getSiteRoot() + arrFilesList))
        {
          paramObj.fullFilesList.push(arrFilesList);
        }
      } 
     }
     MMNotes.close(notesHandleLocal);
  }
  
  return paramObj;
}


//--------------------------------------------------------------------
// FUNCTION:
//   updateAppDescriptor
//
// DESCRIPTION:
//   This function will create or update the application.xml file. 
//   If some nodes are not found they will be created.
//   If for some nodes no value is supplied those nodes will not be erased.
//   The same thing will happen and for nodes attributes.
//     
//
// ARGUMENTS:
//   paramObj (object) - an array of key-value pairs 
//   xmlFile (string) - path to app descriptor file
// RETURNS:
//   An error message if the application.xml could not be created/updated; an empty
//   string if no error occurs.
//--------------------------------------------------------------------
function AIRUTILS_updateAppDescriptor(paramObj, xmlFile, APP_ICONS, _sysChromeValue, _transparentValue)
{
  var theDOM = AIRUtils.getAppDescriptorDOM(xmlFile);
  var errStr;
  var updateNode;

  if (theDOM)
  {
    // first, fix some fields in paramObj that might have two different names. Yes, this is ugly.
//xxx fix this??
    if (paramObj[AIRUtils.appWidthNode])
      paramObj.windWidth = paramObj[AIRUtils.appWidthNode];
    if (paramObj[AIRUtils.appHeightNode])
      paramObj.windHeight = paramObj[AIRUtils.appHeightNode];
    if (paramObj[AIRUtils.appFileNameNode])
      paramObj.applicationFileName = paramObj[AIRUtils.appFileNameNode];
    if (paramObj[AIRUtils.appNameNode])
      paramObj.applicationName = paramObj[AIRUtils.appNameNode];

    // find the root node, since we'll need it in a few places
    var rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);

    if (!rootNode)
    {
      errStr = dw.loadString("AIRMsg_AppXMLInvalidNotUpdate");
    }
    else
    {
      // note: AIRUtils.appFileNameNode was "name" for M5, and is "filename" starting with M6
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appFileNameNode);
      if (updateNode)
      {
        updateNode.innerHTML = paramObj.applicationFileName;
      }
      else
      {
        // create this node as first child of the root element
        var strOuterHTML = "<" + AIRUtils.appFileNameNode + ">" + paramObj.applicationFileName + "</" + AIRUtils.appFileNameNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appFileNameNode, strOuterHTML, "firstChild");
      }

      // id is now a node; put it right after appFileNameNode
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appIDNode);
      if (updateNode)
      {
        if (paramObj[AIRUtils.appIDNode])
        {
          updateNode.innerHTML = paramObj[AIRUtils.appIDNode];
        }
        else
        {
          updateNode.innerHTML = "";
        }
      }
      else
      {
        // create this node after the AIRUtils.appFileNameNode element, if we need it
        var strOuterHTML = "<" + AIRUtils.appIDNode + ">" + paramObj[AIRUtils.appIDNode] + "</" + AIRUtils.appIDNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appIDNode, strOuterHTML, "after", AIRUtils.appFileNameNode);
      }

      // version is now a node; put it right after appFileNameNode
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appVersionNode);
      if (updateNode)
      {
        if (paramObj.version)
        {
          updateNode.innerHTML = paramObj.version;
        }
        else
        {
          updateNode.innerHTML = "";
        }
      }
      else
      {
        // create this node after the AIRUtils.appFileNameNode element, if we need it
        var strOuterHTML = "<" + AIRUtils.appVersionNode + ">" + paramObj.version + "</" + AIRUtils.appVersionNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appVersionNode, strOuterHTML, "after", AIRUtils.appIDNode);
      }
 
      // remove old (pre-M5) "rootcontent" node, if exists
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appRootContentNode);
      if (updateNode)
      {
        updateNode.outerHTML = "";
      }

      // note: AIRUtils.appNameNode was "title" for M5, and is "name" starting with M6
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appNameNode);
      if (updateNode)
      {
        if (paramObj.applicationName)
        {
          updateNode.innerHTML = paramObj.applicationName;
        }
        else
        {
          updateNode.outerHTML = "";
        }
      }
      else if (paramObj.applicationName)
      {
        // create this node after the AIRUtils.appFileNameNode element, if we need it
        var strOuterHTML = "<" + AIRUtils.appNameNode + ">" + paramObj.applicationName + "</" + AIRUtils.appNameNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appNameNode, strOuterHTML, "after", AIRUtils.appFileNameNode);
      }

      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appUpdate);
      if (updateNode)
      {
        if (typeof(paramObj[AIRUtils.appUpdate]) != "undefined")
        {
          updateNode.innerHTML = paramObj[AIRUtils.appUpdate];
        }
        else
        {
          updateNode.outerHTML = "";
        }
      }
      else if (typeof(paramObj[AIRUtils.appUpdate]) != "undefined")
      {
        // create this node after the AIRUtils.appFileNameNode element, if we need it
        rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
        var strOuterHTML = "<" + AIRUtils.appUpdate + ">" + paramObj[AIRUtils.appUpdate] + "</" + AIRUtils.appUpdate + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appUpdate, strOuterHTML, "after", AIRUtils.appFileNameNode);
      }

      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appInitialWindowNode);
      if (!updateNode)
      {
        // create this node after the AIRUtils.appVersionNode element
        rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
        var strOuterHTML = "<" + AIRUtils.appInitialWindowNode + ">\n</" + AIRUtils.appInitialWindowNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appInitialWindowNode, strOuterHTML, "after", AIRUtils.appVersionNode);
        updateNode = AIRUtils.getNode(theDOM, AIRUtils.appInitialWindowNode);
      }

      if (updateNode)
      {
        var initialWindowNode = updateNode;
        updateNode = AIRUtils.getNode(initialWindowNode, AIRUtils.appContentNode);
        if (updateNode)
        {
          // this node is required, even if there's no content set
          if (!paramObj.rootContent)
          {
            paramObj.rootContent = "";
          }
          updateNode.innerHTML = paramObj.rootContent;
        }
        else
        {
          var strOuterHTML = "<" + AIRUtils.appContentNode + ">" + paramObj.rootContent + "</" + AIRUtils.appContentNode + ">";
          AIRUtils.addNodeAsChild(theDOM, initialWindowNode, AIRUtils.appContentNode, strOuterHTML, "firstChild");
        }

        updateNode = AIRUtils.getNode(initialWindowNode, AIRUtils.appSystemChromeNode);

        if (updateNode)
        {
          updateNode.innerHTML = _sysChromeValue;
        }
        else
        {
          var strOuterHTML = "<" + AIRUtils.appSystemChromeNode + ">" + _sysChromeValue + "</" + AIRUtils.appSystemChromeNode + ">";
          AIRUtils.addNodeAsChild(theDOM, initialWindowNode, AIRUtils.appSystemChromeNode, strOuterHTML, "after", AIRUtils.appContentNode);
        }

        updateNode = AIRUtils.getNode(initialWindowNode, AIRUtils.appTransparentNode);
        if (updateNode)
        {
          updateNode.innerHTML = _transparentValue;
        }
        else
        {
          var strOuterHTML = "<" + AIRUtils.appTransparentNode + ">" + _transparentValue + "</" + AIRUtils.appTransparentNode + ">";
          AIRUtils.addNodeAsChild(theDOM, initialWindowNode, AIRUtils.appTransparentNode, strOuterHTML, "after", AIRUtils.appContentNode);
        }

        updateNode = AIRUtils.getNode(initialWindowNode, AIRUtils.appVisibleNode);
        if (updateNode)
        {
          updateNode.innerHTML = "true";
        }
        else
        {
          var strOuterHTML = "<" + AIRUtils.appVisibleNode + ">true</" + AIRUtils.appVisibleNode + ">";
          AIRUtils.addNodeAsChild(theDOM, initialWindowNode, AIRUtils.appVisibleNode, strOuterHTML, "after", AIRUtils.appContentNode);
        }

        updateNode = AIRUtils.getNode(initialWindowNode, AIRUtils.appWidthNode);
        if (updateNode)
        {
		  if (paramObj.windWidth)
          {
            updateNode.innerHTML = paramObj.windWidth;
          }
          else
          {
            updateNode.outerHTML = "";
          }
        }
        else if (paramObj.windWidth)
        {
          // create this node after the content node, if we need it
          var strOuterHTML = "<" + AIRUtils.appWidthNode + ">" + paramObj.windWidth + "</" + AIRUtils.appWidthNode + ">";
          AIRUtils.addNodeAsChild(theDOM, initialWindowNode, AIRUtils.appWidthNode, strOuterHTML, "after", AIRUtils.appContentNode);
        }

        updateNode = AIRUtils.getNode(initialWindowNode, AIRUtils.appHeightNode);
        if (updateNode)
        {
		  if (paramObj.windHeight)
          {
            updateNode.innerHTML = paramObj.windHeight;
          }
          else
          {
            updateNode.outerHTML = "";
          }
        }
        else if (paramObj.windHeight)
        {
          // create this node after the content node, if we need it
          var strOuterHTML = "<" + AIRUtils.appHeightNode + ">" + paramObj.windHeight + "</" + AIRUtils.appHeightNode + ">";
          AIRUtils.addNodeAsChild(theDOM, initialWindowNode, AIRUtils.appHeightNode, strOuterHTML, "after", AIRUtils.appContentNode);
        }

      } // initialWindowNode

      // get root node again; sometimes it's messed up when we get here
      rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);

      // now the description node, but careful, because there's one directly under
      // application (the one we want), and then possibly more under fileTypes
      updateNode = AIRUtils.getChildNode(rootNode, AIRUtils.appDescriptionNode);

      if (updateNode)
      {
        if (paramObj.description)
          updateNode.innerHTML = paramObj.description;
        else
          updateNode.outerHTML = "";
      }
      else if (paramObj.description)
      {
        // create this node after the appFileNameNode element
        rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
        var strOuterHTML = "<" + AIRUtils.appDescriptionNode + ">" + paramObj.description + "</" + AIRUtils.appDescriptionNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appDescriptionNode, strOuterHTML, "after", AIRUtils.appFileNameNode);
      }

      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appCopyrightNode);
      if (updateNode)
      {
        if (paramObj.copyright)
          updateNode.innerHTML = paramObj.copyright;
        else
          updateNode.outerHTML = "";
      }
      else if (paramObj.copyright)
      {
        // create this node after the appFileNameNode element
        rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
        var strOuterHTML =  "\n<" + AIRUtils.appCopyrightNode + ">" + paramObj.copyright + "</" + AIRUtils.appCopyrightNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appCopyrightNode, strOuterHTML, "after", AIRUtils.appFileNameNode);
      }
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appProgramMenuFolderNode);
      if (updateNode)
      {
        if (paramObj.programMenuFolder)
          updateNode.innerHTML = paramObj.programMenuFolder;
        else
          updateNode.outerHTML = "";
      }
      else if (paramObj.programMenuFolder)
      {
        // create this node after the name element
        rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
        var strOuterHTML =  "\n<" + AIRUtils.appProgramMenuFolderNode + ">" + paramObj.programMenuFolder + "</" + AIRUtils.appProgramMenuFolderNode + ">";
        AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.appProgramMenuFolderNode, strOuterHTML, "after", AIRUtils.appFileNameNode);
      }

      // update application images section
      // first we need to see if all images are removed
      var imgsExists = false;
      for (var key in APP_ICONS)
      {
        if (APP_ICONS[key])
        {
          imgsExists = true;
          break;
        }
      }

      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appIconsNode);
      if (!updateNode && imgsExists)
      {
        // if the icons node is not found we should create it
        // we would add this node as the last child of the root element
         rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
         if (rootNode)
         {
            rootNode.innerHTML += "<" + AIRUtils.appIconsNode  + "></" + AIRUtils.appIconsNode  + ">";
            updateNode = AIRUtils.getNode(theDOM, AIRUtils.appIconsNode);
         }
        
      }
      if (updateNode && imgsExists)
      {
         for (var key in APP_ICONS)
         {
            var imgNode = AIRUtils.getNode(updateNode, AIRUtils.appImageNodePrefix + key);
            if (APP_ICONS[key])
            {
              if (imgNode)
              {
                imgNode.innerHTML = APP_ICONS[key]; 
              }
              else
              {
                updateNode.innerHTML += "<" + AIRUtils.appImageNodePrefix + key + ">" + APP_ICONS[key] + "</" + AIRUtils.appImageNodePrefix + key + ">";  
              }
            }
            else
            {
              // remove image node
              if (imgNode)
              {
                imgNode.outerHTML = "";
              }
            }
         }

      }
      else if (updateNode && !imgsExists)
      {
        updateNode.outerHTML = "";
      }

      // update the registered file types section, if any
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.fileTypesNode);

      if (paramObj.fileTypes && paramObj.fileTypes.length)
      {
        if (!updateNode)
        {
          // make the node as the last child of the root node
         var rootNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
         if (rootNode)
         {
           var strOuterHTML =  "\n<" + AIRUtils.fileTypesNode + ">\n  </" + AIRUtils.fileTypesNode + ">";
           AIRUtils.addNodeAsChild(theDOM, rootNode, AIRUtils.fileTypesNode, strOuterHTML, "lastChild");
           updateNode = AIRUtils.getNode(theDOM, AIRUtils.fileTypesNode);
         }
        }

        if (updateNode)
        {
          updateNode.innerHTML = "";
          for (var i = 0; i < paramObj.fileTypes.length; i++)
          {
            var fileTypeObj = paramObj.fileTypes[i];
            var strOuterHTML = "    <" + AIRUtils.fileTypeNode + ">";
            if (fileTypeObj[AIRUtils.fileTypeNameNode])
              strOuterHTML += "\n      <" + AIRUtils.fileTypeNameNode + ">" + fileTypeObj[AIRUtils.fileTypeNameNode] + "</" + AIRUtils.fileTypeNameNode + ">";
            if (fileTypeObj[AIRUtils.fileTypeExtensionNode])
              strOuterHTML += "\n      <" + AIRUtils.fileTypeExtensionNode + ">" + fileTypeObj[AIRUtils.fileTypeExtensionNode] + "</" + AIRUtils.fileTypeExtensionNode + ">";
            if (fileTypeObj[AIRUtils.fileTypeDescriptionNode])
              strOuterHTML += "\n      <" + AIRUtils.fileTypeDescriptionNode + ">" + fileTypeObj[AIRUtils.fileTypeDescriptionNode] + "</" + AIRUtils.fileTypeDescriptionNode + ">";
            if (fileTypeObj[AIRUtils.fileTypeContentTypeNode.toLowerCase()])
              strOuterHTML += "\n      <" + AIRUtils.fileTypeContentTypeNode + ">" + fileTypeObj[AIRUtils.fileTypeContentTypeNode.toLowerCase()] + "</" + AIRUtils.fileTypeContentTypeNode + ">";

            if (fileTypeObj.icons)
            {
              strOuterHTML += "\n      <" + AIRUtils.appIconsNode + ">";
              for (var key in fileTypeObj.icons)
              {
                var imgNode = AIRUtils.getNode(updateNode, AIRUtils.appImageNodePrefix + key);
                if (fileTypeObj.icons[key])
                {
                  strOuterHTML += "\n        <" + AIRUtils.appImageNodePrefix + key + ">" + fileTypeObj.icons[key] + "</" + AIRUtils.appImageNodePrefix + key + ">";  
                }
              }
              strOuterHTML += "\n      </" + AIRUtils.appIconsNode + ">";
            }

            strOuterHTML += "\n    </" + AIRUtils.fileTypeNode + ">\n"; 
            AIRUtils.addNodeAsChild(theDOM, updateNode, AIRUtils.fileTypeNode, strOuterHTML, "lastChild");

            // get fileTypes node again to be sure we have it (workaround bug)
            updateNode = AIRUtils.getNode(theDOM, AIRUtils.fileTypesNode);
          }
        }
      }
      else if (updateNode)
      {
        // no file types, but there's a fileTypes node, so remove it
        updateNode.outerHTML = "";
      }

      // go back to app node to maybe fix xmlns - need to do this last because of a DW JS bug
      updateNode = AIRUtils.getNode(theDOM, AIRUtils.appDescrRootNode);
      if (updateNode)
      {
        var xmlnsAttrib = updateNode.getAttribute(AIRUtils.appXMLNSAttribute);
        if (xmlnsAttrib != AIRUtils.appXMLNSDefVal)
        {
          updateNode.setAttribute(AIRUtils.appXMLNSAttribute, AIRUtils.appXMLNSDefVal);
         // remove old M4/M5 attribs from root node
         updateNode.removeAttribute(AIRUtils.appIDAttribute);
         updateNode.removeAttribute(AIRUtils.appVersionAttribute);
        }
      }

      // update document
      if (!DWfile.write(xmlFile, theDOM.documentElement.outerHTML))
      {
        errStr = dw.loadString("AIRMsg_AppXMLNotUpdated");
      }
    }
  }
  else
  {
    errStr = dw.loadString("AIRMsg_AppXMLNotCreated");
  }

  return errStr;
}


//--------------------------------------------------------------------
// FUNCTION:
//   getAppDescriptorDOM
//
// DESCRIPTION:
//   Helper function that retrieves the DOM of the existing application.xml file,
//   or the DOM of the application.xml template from the configuration folder if 
//   no application.xml file is found in the current site.
//
// ARGUMENTS:
//   xmlFile  - the xmlfile we care about; will be created if doesn't exist
//
// RETURNS:
//   A DOM object if one of the files are found, NULL oterwise.  
//--------------------------------------------------------------------
function AIRUTILS_getAppDescriptorDOM(xmlFile)
{
  var appDOM;

  if (!DWfile.exists(xmlFile))  
  {
    var strAppXMLTemplate = dwscripts.filePathToLocalURL(AIRext.GetAIRInstallPath() + AIRUtils.appDescrTemplateFolder + AIRUtils.appDescriptorName);

    if(DWfile.exists(strAppXMLTemplate))
    {
      if (DWfile.copy(strAppXMLTemplate, xmlFile))
      {
        appDOM = dw.getDocumentDOM(xmlFile);
      }
    }
  }
  else
  {
    appDOM = dw.getDocumentDOM(xmlFile);
  }
  return appDOM;
}


//************************* FILE/FOLDERS grid utility functions **************************

//--------------------------------------------------------------------
// FUNCTION:
//   setListContents
//
// DESCRIPTION:
//   This function sets the contenst of the list. Any existent item
//   will be removed first.
//
// ARGUMENTS:
//   list (array) - a list of items to be added to the list
//
// RETURNS:
//   nothing
//--------------------------------------------------------------------
function AIRUTILS_setListContents(gridElement, list) {
  if (dwscripts.IS_MAC) {		
    // For mac create an empty column node
    gridElement.innerHTML ="<mm:treecolumn width='200'>";
  } else {
    gridElement.innerHTML = "";
  }
  
  // sort list 
  if (list && list.length)
  { 
    var strGridContent = "";
    for (var i=0; i<list.length; i++) {
      var isFolder = false;
      // If the given item ends with / or \ this means that it is a folder
      if (list[i] && list[i].match && list[i].match(/[\\\/]$/gi)) {
        isFolder = true;
      }
      strGridContent += "<mm:treenode icon=\"" + (isFolder ? 1 : 2) + "\" value=\"" + list[i] + "\"></mm:treenode>"
    }
    gridElement.innerHTML += strGridContent;
  }
}


//--------------------------------------------------------------------
// FUNCTION:
//   getListContents
//
// DESCRIPTION:
//   This function returns a list of all items in the list
//
// ARGUMENTS:
//   none
//
// RETURNS:
//   (array) - the list contents
//--------------------------------------------------------------------
function AIRUTILS_getListContents(gridElement) {
  var list = new Array();
  var treeNodes = gridElement.getElementsByTagName("MM:TREENODE");
  
  for (var i=0; i<treeNodes.length; i++) {
    var value = treeNodes[i].getAttribute("value");
    if (value)
    {
      list.push(value);
    }
  }
  
  return list;
}


//--------------------------------------------------------------------
// FUNCTION:
//   addItemToList
//
// DESCRIPTION:
//   This function adds the given item to the list. 
//
// ARGUMENTS:
//   item (string) - the item to be added
//
// RETURNS:
//   (boolean) - true is successfull, false otherwise
//--------------------------------------------------------------------
function AIRUTILS_addItemToList(gridElement, item) {
  var status = false;  
  var isFolder = false;
  // If the given item ends with / or \ this means that it is a folder
  if (item && item.match && item.match(/[\\\/]$/gi)) {
    isFolder = true;
  }

  gridElement.innerHTML += "<mm:treenode icon=\"" + (isFolder ? 1 : 2) + "\" value=\"" + item + "\"></mm:treenode>";
  status = true;
  
  return status;
}

//--------------------------------------------------------------------
// FUNCTION:
//   addItemAtPosition
//
// DESCRIPTION:
//   This function insert the given item at position specified by pos parameter.
//
// ARGUMENTS:
//   itemValue (string) - the item to be added
//   pos (number) - position in grid
//
// RETURNS:
//   (boolean) - true if successfull, false otherwise
//--------------------------------------------------------------------
function AIRUTILS_addItemAtPosition(gridElement, itemValue, pos) {
  var status = false;  
  var isFolder = false;
  var treeNodes = gridElement.getElementsByTagName("MM:TREENODE");

  if (treeNodes)
  {
    // If the given item ends with / or \ this eans that it is a folder
    if (itemValue && itemValue.match && itemValue.match(/[\\\/]$/gi)) {
      isFolder = true;
    }
    if (pos >= 0 && pos < treeNodes.length)
    {
        var strGridContent = "";
        if (dwscripts.IS_MAC) {		
          // For mac create an empty column node
          strGridContent = "<mm:treecolumn width='200'>";
        }
        for (var i = 0; i < treeNodes.length; i++)
        {
          if (pos == i)
          {
            strGridContent += "<mm:treenode icon=\"" + (isFolder ? 1 : 2) + "\" value=\"" + itemValue + "\"></mm:treenode>";
          }
          strGridContent += treeNodes[i].outerHTML;
       }
      gridElement.innerHTML = strGridContent;
    }
    if (pos == treeNodes.length)
    {
       gridElement.innerHTML += "<mm:treenode icon=\"" + (isFolder ? 1 : 2) + "\" value=\"" + itemValue + "\"></mm:treenode>";
    }
  }  
  return status;
}


//--------------------------------------------------------------------
// FUNCTION:
//   getItemIndex
//
// DESCRIPTION:
//   This function returns the index of the selected node,
//   or the node which has value matching itemValue
//
// ARGUMENTS:
//   itemValue (string) - (optional) the item value. If missing, the
//                        current selected row is constederd and the
//                        function will return the index of the current
//                        selected row
//
// RETURNS:
//   (number) - the index in the list or -1 if the element is missing
//--------------------------------------------------------------------
function AIRUTILS_getItemIndex(gridElement, itemValue) {
  var index = -1;
  var selectedNode = gridElement.selectedNodes[0];
  var treeNodes = gridElement.getElementsByTagName("MM:TREENODE");
  for (var i=0; i<treeNodes.length; i++) {
    if ((typeof(itemValue) == "undefined" && (treeNodes[i] == selectedNode)) ||
        (typeof(itemValue) != "undefined" && (treeNodes[i].value == itemValue))) {
      index = i;
      break;
    }
  }


  return index;
}


//--------------------------------------------------------------------
// FUNCTION:
//   removeItemFromList
//
// DESCRIPTION:
//   This function removes the item from the list. The item is specified
//   by its index.
//
// ARGUMENTS:
//   itemIndex (number) - the item to be removed specified by its index
//   dontFixSelection (boolean) - (optional)specifies wheter ot not to
//                                fix the selection. It is usefull when
//                                removing selected row.
//
// RETURNS:
//   (boolean) - true is successfull, false otherwise
//--------------------------------------------------------------------
function AIRUTILS_removeItemFromList(gridElement, itemIndex, dontFixSelection) {
  var status = false;
  var treeNodes = gridElement.getElementsByTagName("MM:TREENODE");
  
  if (itemIndex >= 0 && itemIndex < treeNodes.length) {
    treeNodes[itemIndex].outerHTML = "";
    status = true;
    
    if (!dontFixSelection) {
      treeNodes = gridElement.getElementsByTagName("MM:TREENODE");
      if (treeNodes && treeNodes.length) {
        if (itemIndex >= treeNodes.length) {
          itemIndex = treeNodes.length - 1;
        }
        treeNodes[itemIndex].setAttribute("selected", "selected");
      }
    }
  }
  
  return status;
}


//--------------------------------------------------------------------
// FUNCTION:
//   removeAllItemsFromList
//
// DESCRIPTION:
//   This function removes the item from the list. The item is specified
//   by its index.
//
// ARGUMENTS:
//
// RETURNS:
//   (boolean) - true is successfull, false otherwise
//--------------------------------------------------------------------
function AIRUTILS_removeAllItemsFromList(gridElement)
{
  var status = false;
  var treeNodes = gridElement.getElementsByTagName("MM:TREENODE");

  for (var i = 0; i < treeNodes.length; i++)
  {
    treeNodes[i].outerHTML = "";
    status = true;
  }

  return status;
}

//--------------------------------------------------------------------
// FUNCTION:
//   validateImagePathAndSize
//
// DESCRIPTION:
//
//
// ARGUMENTS:
//   none
//
// RETURNS:
//   nothing
//--------------------------------------------------------------------
function AIRUTILS_validateImagePathAndSize(site_root, imgPath, imgDim)
{
  var errStr;
  var absPath = site_root + imgPath;
  
  if (imgPath.search(/\s/) != -1)
  {
    // path contains spaces, which won't work in M5 ADT
    errStr = dwscripts.sprintf(dw.loadString("AIRMsg_InvalidImagePathSpace"), imgDim);
  }
  else if (DWfile.exists(absPath))
  {
    var actualDim = AIRUtils.getImageDimensions(absPath);
    if (actualDim != imgDim)
    {
      errStr = dwscripts.sprintf(dw.loadString("AIRMsg_InvalidImageSize"), actualDim, imgDim);
    }
    else
    {
      var extn = dwscripts.getFileExtension(imgPath);
      if (extn.toLowerCase() != "png")
      {
        errStr = dwscripts.sprintf(dw.loadString("AIRMsg_InvalidImageFileDim"), imgDim);
      }
    }
  }
  else
  {
     errStr = dwscripts.sprintf(dw.loadString("AIRMsg_InvalidImagePath"), imgDim);
  }
  
  return errStr;
}
