// Copyright 2000, 2001, 2002, 2003 Macromedia, Inc. All rights reserved.

// This file contains functions for inserting Fireworks HTML
//
// dependencies:
// $Dreamweaver/Configuration/Shared/MM/Scripts/Class/FileClass.js
//
// errors:
/*
var MSG_copyFilesToSite =
      "Some referenced files are outside of the root folder of site '%s'\n" +
      "and may not be accessible when you publish the site.\n\n" +
      "The referenced files are:\n%s\n\n" +
      "Your root folder is:\n%s\n\n" +
      "Continue copying these files into your site?";
var MSG_folderNotUnderSite =
      "The folder you have selected is not under the site root.\n\n" +
      "Your root folder is:\n%s\n\n" +
      "Please select a folder within the site.";
var MSG_overwriteFiles =
      "Some files being copied already exist in the selected folder.\n\n" +
      "The existing files are:\n%s\n\n" +
      "Do you want to replace the existing files?";
var MSG_copyFailed =
      "Some files could not be copied to the selected folder.\n\n" +
      "The files not copied are:\n%s";
var MSG_docUnsaved = 
      "In order to generate correct references to files that you are\n" + 
      "importing, you must save your document.\n\n" +
      "Click OK to save the document now, or Cancel to continue without\n" + 
      "saving.";
*/


//*************** GLOBALS  *****************

//List of unsupported Fireworks JavaScript functions
var LIST_fwSpecificFns = new Array("GrpRestore", "GrpSwap", "GrpDown");
var FW4_MENU_FCN = "fwLoadMenus";
var FW4_MENU_FILE = "fw_menu.js"
var DEBUG_FILE = dw.getConfigurationPath() + "/POPUP_DEBUG.txt";

var PRELOAD_REFS = new Array();



//***************** LOCAL FUNCTIONS  ******************

//Returns true if the given document is a Fireworks HTML file.
// Find the Fireworks comment
//
function isFireworksHTML(theHTML) {
  var start, stop, target, retVal = false;
  start = theHTML.indexOf("<!-- Fireworks ");
  if (start == -1){
  	start = theHTML.indexOf("<!--Fireworks ");
  }
  if (start != -1) {
    stop = theHTML.indexOf("-->", start);
    if (stop != -1) {
      target = theHTML.indexOf("target.", start);
      retVal = (target >= 0) && (target < stop);
  } }
  return retVal;
}


//Returns true if the given document is a Fireworks HTML file targeted
// at Dreamweaver.  Find the Dreamweaver string within the Fireworks
// comment.
//
function isDWStyle(theHTML) {
  var start, stop, target, retVal = false;
  start = theHTML.indexOf("<!-- Fireworks ");
  if (start == -1)
  	start = theHTML.indexOf("<!--Fireworks ");
  if (start != -1) {
    stop = theHTML.indexOf("-->", start);
    if (stop != -1) {
      target = theHTML.indexOf("Dreamweaver", start);
      retVal = (target >= 0) && (target < stop);
  } }
  return retVal;
}


//Returns true if the given document contains only supported DW
// behavior functions.  (This should only be an issue for FW1 and FW2)
//
function usesDWBehaviors(theHTML) {
  var fw1or2, searchPatt, retVal = true;
  fw1or2 = (theHTML.indexOf("<!-- Fireworks 1") != -1) ||
           (theHTML.indexOf("<!-- Fireworks 2") != -1);
  if (fw1or2) {
    for (var i=0; retVal && i < LIST_fwSpecificFns.length; i++) {
      searchPatt = new RegExp("function\\s+" + LIST_fwSpecificFns[i] + "\\s*\\(");
      if (theHTML.search(searchPatt) != -1) retVal = false;
  } }
  return retVal;
}


//Return the HTML that should be inserted at the IP
//
function insertFireworksHTML(fwDOM, fwURL, docRootURL, siteRootURL) {
  var fileList, tagList, jsList;
  var siteURL, sitePath, copyList, folderURL, folder;
  var existsList, failedList, overwrite;
  var targetDOM;
  var scriptList, arrowFile, menuFile, theHead, relPath;
  
  fileList = new FileRefList(fwURL);
  tagList = new TagList(fwDOM, fileList);
  jsList = new JsRefList(fwDOM, fileList, tagList);
  scriptList = new ScriptTagList(fwDOM, fileList);
  loadMenusList = new StringInFnList(fwDOM, fileList);
  
  if (!docRootURL){
    var saveDoc = confirm(MSG_docUnsaved);
    if (saveDoc){
      var dwDoc = dw.getDocumentDOM()
      dw.saveDocument(dwDoc);
      docRootURL = dwDoc.URL.substring(0,dwDoc.URL.lastIndexOf('/')+1);
    }
  }    

  siteURL = dreamweaver.getSiteRoot();

  if (docRootURL && siteURL) {
    sitePath = MMNotes.localURLToFilePath(siteURL);
    siteName = site.getCurrentSite();
    copyList = fileList.getOutsideOfSite(siteURL);
    
    if (copyList.length > 0) {
      while (true) {
        copy = confirm(printf(MSG_copyFilesToSite,
                              siteName, displayArray(copyList, 5, '\n'), sitePath));

        // copy files to the site.
        if (copy) {
          while (true) {
            folderURL =  dw.browseForFolderURL(LABEL_selectFolder, siteURL);
            if (folderURL.lastIndexOf('/') != folderURL.length-1)
            	folderURL = folderURL + '/';
            folder = new File(folderURL);
            inSite = (folderURL.toLowerCase().indexOf(siteURL.toLowerCase()) == 0)
            if (!folderURL || (folder.exists() && inSite)) break;
            alert(printf(MSG_folderNotUnderSite, sitePath));
          }

          if (!folderURL) continue;
          else if (folder.exists()) {
            existsList = fileList.getTargetExists(folder.getAbsolutePath(), siteURL);
            if (existsList.length > 0)
              overwrite = confirm(printf(MSG_overwriteFiles, displayArray(existsList, 5, '\n')));
            failedList = fileList.copyToFolder(folder.getAbsolutePath(), siteURL, overwrite);
            if (failedList.length >0)
              alert(printf(MSG_copyFailed, displayArray(failedList, 10, '\n')));
           } 
        }
        break;
      }
    }
  }

  // update the URLs
  fileList.updateURLs(docRootURL, siteRootURL, fwDOM);
	
  // make the tag names unique
  targetDOM = dreamweaver.getDocumentDOM('document');
  tagList.makeNamesUnique(targetDOM);

  // FWMX sometimes adds a style block to the head; we need to
  // copy this style block manually.
  if (fwDOM.documentElement.childNodes[0].innerHTML.toLowerCase().indexOf('<style') != -1){
    mergeStyleBlocks(fwDOM);
  }

  if (scriptList.list.length > 0){
    // For DW/FW MX: add or update the mmLoadMenus() function 
    var hasMXPopupMenus = update_mmLoadMenus(fwDOM);
    
    if (!hasMXPopupMenus){
      // For DW/FW 4: search for fwLoadMenu script and copy it if it exists
      replaceHeadFcns(targetDOM,scriptList);
    }
    
    arrowFile = new File("arrows.gif", fwURL);
    menuFile = new File("mm_menu.js", fwURL);
    theHead = targetDOM.getElementsByTagName("HEAD")[0];
    var childMenuIcon = theHead.innerHTML.indexOf('childMenuIcon="');
    var absPath;
    
    if (!arrowFile.exists()){
      arrowFile = new File("arrows.gif", fwURL.substring(0,fwURL.lastIndexOf('/')+1) + 'images/');
    }
    if (arrowFile.exists() && childMenuIcon != -1){
      if (folder){
        if (arrowFile.copyTo(folder.fullUrl+"/arrows.gif")){
          arrowFile.setPath(folder.fullUrl+"/arrows.gif");
        }
      }
      relPath = absoluteToRelativeURL(arrowFile.fullUrl,docRootURL,true);
      theHead.innerHTML = theHead.innerHTML.replace(/childMenuIcon="[^"]*"/g,'childMenuIcon="'+relPath+'"');

    // Assumption here is that the menu was pasted from Fireworks, and that
    // the arrows.gif file shows up with an absolute reference.
    }else if (childMenuIcon != -1){
      // Get the current absolute reference from the mmLoadMenus function.
      absPath = theHead.innerHTML.substring(childMenuIcon+15,theHead.innerHTML.indexOf('"',childMenuIcon + 15));

      // if the user copied files from outside the site, the arrows.gif
      // file will be in a different spot than is currently listed in
      // the function. Copy the arrows file to the new spot, then update
      // the path.
      if (folder){
        arrowFile = new File("arrows.gif",absPath.substring(0,absPath.lastIndexOf('/')+1));
        if (arrowFile.copyTo(folder.fullUrl+"/arrows.gif")){
          arrowFile.setPath(folder.fullUrl+"/arrows.gif");
          absPath = arrowFile.fullUrl;
        }
      }

      // This will update the absolute ref to the relative ref whether arrows.gif
      // had to be copied or not.
      relPath = absoluteToRelativeURL(absPath,docRootURL,true);
      theHead.innerHTML = theHead.innerHTML.replace(/childMenuIcon="[^"]*"/g,'childMenuIcon="'+relPath+'"');
    }

     if (menuFile.exists() && targetDOM.getElementsByTagName("HEAD")[0].innerHTML.indexOf('mm_menu.js') == -1){
      if (folder){
        if (menuFile.copyTo(folder.fullUrl+"/mm_menu.js")){
           menuFile.setPath(folder.fullUrl+"/mm_menu.js");
        }
      }
      relPath = absoluteToRelativeURL(menuFile.fullUrl,docRootURL,true);
      // Get the HEAD again for safety
       theHead = targetDOM.getElementsByTagName("HEAD")[0];
       theHead.innerHTML = theHead.innerHTML + '<script language="JavaScript" src="' + relPath + '"></script>';
    }
  }
  
  retVal = getInsertedHTML(fwDOM);
  return retVal;
}


//Returns the HTML that should be inserted into the current document.
//
function getInsertedHTML(sourceDOM) {
  var retVal = sourceDOM.body.innerHTML;
  var start = '', stop = '', item;
  var startExp = /<!-+\s*BEGIN COPYING[\w\s]*-+>\s*/;
  var stopExp  = /\s*<!-+\s*STOP COPYING[\w\s]*-+>/;
  item = retVal.match(startExp);
  if (item != null) start = item.index + item[0].length;
  item = retVal.match(stopExp);
  if (item != null) stop = item.index;
  if (start && stop){
    retVal = retVal.substring(start,stop);
  }
  var loadMenusCall = '<script language="JavaScript1.2">mmLoadMenus();</' + 'script>';
  if (retVal.indexOf(loadMenusCall) != -1 && dw.getDocumentDOM().body.innerHTML.indexOf(loadMenusCall) != -1){
    retVal = retVal.substring(retVal.indexOf(loadMenusCall)+loadMenusCall.length);
  }
  return retVal;
}


function replaceHeadFcns(sourceDOM,scriptList){
  var headChildren, scriptHTML, searchPatt,srcStr,fwHTML;
  var replacedArr = new Array(false,false);
  var sourceHead = sourceDOM.getElementsByTagName("HEAD")[0];
  fwHTML = scriptList.findFunction(FW4_MENU_FCN);

  if(!fwHTML)
    return;
  var startFwFn = fwHTML.indexOf("function " + FW4_MENU_FCN);
  var endFwFn = fwHTML.indexOf("// " + FW4_MENU_FCN + "()");
  replaceStr = fwHTML.substring(startFwFn,endFwFn + FW4_MENU_FCN.length+5);
  headChildren = sourceHead.childNodes;
  for (var i=0; i< headChildren.length; i++){
  if (headChildren[i].nodeType==Node.ELEMENT_NODE && headChildren[i].tagName == "SCRIPT"){
    scriptHTML = headChildren[i].innerHTML;
    var startDwFn = scriptHTML.indexOf("function " + FW4_MENU_FCN);
    var endDwFn = scriptHTML.indexOf("// " + FW4_MENU_FCN + "()");

    if (replaceStr && startDwFn != -1){
      headChildren[i].innerHTML = scriptHTML.substring(0,startDwFn) + replaceStr + scriptHTML.substring(endDwFn + FW4_MENU_FCN.length+5);
      replacedArr[0]=true;
    }
    srcStr = headChildren[i].getAttribute("SRC");
    if (replaceStr && srcStr && srcStr.toLowerCase().search(FW4_MENU_FILE+'"') != -1){
      headChildren[i].outerHTML = scriptList.findFile(FW4_MENU_FILE);
      replacedArr[1] = true;
      }
    }
  }
  if (!replacedArr[0] && fwHTML){
    sourceHead.innerHTML += "\n"+fwHTML+"\n";
    if (!replacedArr[1])
      sourceHead.innerHTML += "\n"+scriptList.findFile(FW4_MENU_FILE)+"\n";
  }
}

// Looks for a style block in the Fireworks file and either inserts
// it into the Dreamweaver doc, or adds its declarations to an existing
// style block in the Dreamweaver doc.
function mergeStyleBlocks(fwDOM) {
  var dom = dw.getDocumentDOM();
  var docEl = dom.documentElement;
  var dwHead = docEl.childNodes[0];
  var fwDocEl = fwDOM.documentElement;
  var fwHead = fwDocEl.childNodes[0];
  var fwStyleBlock = null;
  var dwStyleBlock = null;
  
  for (var i=0; i < fwHead.childNodes.length; i++){
    if (fwHead.childNodes[i].nodeType == Node.ELEMENT_NODE && fwHead.childNodes[i].tagName == 'STYLE'){
      fwStyleBlock = fwHead.childNodes[i];
      break;
    }
  }
  
  if (fwStyleBlock){
    for (var j=0; j < dwHead.childNodes.length; j++){
      if (dwHead.childNodes[j].nodeType == Node.ELEMENT_NODE && dwHead.childNodes[j].tagName == 'STYLE'){
        dwStyleBlock = dwHead.childNodes[j];
        break;
      }
    }
  }
  
  if (fwStyleBlock && !dwStyleBlock){
    dwHead.innerHTML = dwHead.innerHTML + "\n" + fwStyleBlock.outerHTML + "\n";
  }
  
  if (fwStyleBlock && dwStyleBlock){
    var endLastDec = dwStyleBlock.innerHTML.lastIndexOf('}')+1;
    dwStyleBlock.innerHTML = dwStyleBlock.innerHTML.substring(0,endLastDec) + "\n" + fwStyleBlock.innerHTML + "\n" + dwStyleBlock.innerHTML.substring(endLastDec);
  }
}

// Similar to applyBehavior() in Show Pop-Up Menu.js -- looks for existing
// mmLoadMenus() function, and also for a menu of the same ID. 
function update_mmLoadMenus(fwDOM) {
  var dom = dw.getDocumentDOM();
  var docEl = dom.documentElement;
  var theHead = docEl.childNodes[0];
  var fwDocEl = fwDOM.documentElement;
  var fwHead = fwDocEl.childNodes[0];

  // This function is now going to return a boolean indicating whether
  // any mmLoadMenus() adding/updating occurred.
  var retVal = false;

  // Extract menu info from FW document.
  var menuStrings = new Array();
  var startFwFn = fwHead.innerHTML.indexOf('function mmLoadMenus()');
  if (startFwFn != -1){
    retVal = true;
    var endFwFn = fwHead.innerHTML.indexOf('// mmLoadMenus()')+16;
    var fwFnStr = fwHead.innerHTML.substring(startFwFn,endFwFn);
  
    // Find the mmLoadMenus() function in the DW document
    var startFn = theHead.innerHTML.indexOf('function mmLoadMenus()');
    // If the function was found, go ahead and extract the code for the
    // various menus from the Fireworks document for comparison with 
    // the ones in the Dreamweaver document.
    if (startFn != -1){
      var startFwMenu = null;
      var endFwMenu = null;
      var menuId = null;
        
      var numRegExp = new RegExp('(window\\.)?(mm_menu_\\d+_\\d+)[^\\)]','g')
      var numResult;
      while ((numResult = numRegExp.exec(fwFnStr)) != null){
        if (!menuId){
          menuId = numResult[2];
          startFwMenu = numRegExp.lastIndex - (numResult[0].length);
        }
        if (numResult[2] != menuId){
          endFwMenu = numRegExp.lastIndex - (numResult[0].length);
          menuStrings.push(menuId)
          menuStrings.push(fwFnStr.substring(startFwMenu,endFwMenu));
          menuId = numResult[2];
          startFwMenu = numRegExp.lastIndex - (numResult[0].length);
        }else{
          endFwMenu = numRegExp.lastIndex - (numResult[0].length);
        }
      }
      // Push the last menu found.
      menuStrings.push(menuId);
      menuStrings.push(fwFnStr.substring(startFwMenu,endFwMenu));
      
      // menuStrings now has an array of string pairs in it. the first string in the pair is the id of a
      // menu (such as mm_menu_0805214100_0), and the second string is the code for the menu associated with
      // that id. Next, we need to determine whether any of the ids in menuStrings is already in use in the
      // document. If so, we'll replace that menu with the one here. If not, we'll add the a new menu to
      // the mmLoadMenus() function, if it exists.
    
      var endFn = theHead.innerHTML.indexOf('// mmLoadMenus()')+16;
      var beforeMenuFn = theHead.innerHTML.substring(0,startFn);
      var afterMenuFn = theHead.innerHTML.substring(endFn);
      var menuFn = theHead.innerHTML.substring(startFn,endFn);
      var startMenu = null;
      var endMenu = null;
      var newMenuStrings = new Array();

      for (var i=0; i < menuStrings.length; i=i+2){
        menuId = menuStrings[i];
        if (menuFn.indexOf(menuId) == -1){
          // This menu was not found in the current document. Push its id and menuString into
          // a new array which we'll use to add to the function.
          newMenuStrings.push(menuStrings[i]);
          newMenuStrings.push(menuStrings[i+1]);
        }else{
          var numRegExp = new RegExp('(window\\.)?(mm_menu_\\d+_\\d+)[^\\)]','g')
          var numResult;
          while ((numResult = numRegExp.exec(menuFn)) != null){
            if (numResult[2] == menuId && !startMenu){
              startMenu = numRegExp.lastIndex - (numResult[0].length);
            }else if (numResult[2] != menuId && startMenu){
              endMenu = numRegExp.lastIndex - (numResult[0].length);
              menuFn = menuFn.substring(0,startMenu) + menuStrings[i+1] + menuFn.substring(endMenu);
              startMenu = null;
              endMenu = null;
              break;
            }else{
              endMenu = numRegExp.lastIndex - (numResult[0].length);
            }
          }
          // Replace the last menu, if its id was found in both the FW and DW document.
          if (startMenu){
            menuFn = menuFn.substring(0,startMenu) + menuStrings[i+1] + menuFn.substring(endMenu);
          }
        }  
      }
      // Add new menus to existing mmLoadMenus() by finding the writeMenus() line
      // and inserting before it.
      if (newMenuStrings.length > 0){
        var searchPatt = /mm_menu_\d+_\d+\.writeMenus\(\)/;
        var beginWriteMenus = menuFn.search(searchPatt);
        var endWriteMenus = beginWriteMenus + menuFn.match(searchPatt)[0].length;
        // Replace the id in the writeMenus() line with the id of the
        // last menu to be added to the function.
        var newWriteMenus = menuFn.substring(beginWriteMenus,endWriteMenus).replace(/mm_menu_\d+_\d+/,newMenuStrings[newMenuStrings.length-2]);
        var afterWriteMenus = menuFn.substring(endWriteMenus);
        
        // Chop off the writeMenus() line for the moment so we can add new menus
        // to the end of the function; we'll add it back in after the loop.
        menuFn = menuFn.substring(0,beginWriteMenus)

        for (var f=1; f < newMenuStrings.length; f=f+2){
          menuFn = menuFn + newMenuStrings[f];
        }
      
        // Add the writeMenus() line (and the remainder of the function) back in
        menuFn = menuFn + newWriteMenus + afterWriteMenus;
      }    

      // Replace the old mmLoadMenus() with the new one.
        theHead.innerHTML = theHead.innerHTML.substring(0,startFn) + menuFn + theHead.innerHTML.substring(endFn);
//      }
      
    // There's no mmLoadMenus() function currently in the document, so just grab
    // the one from Fireworks.
    }else{ //if (!pastingFWHTML){
      menuFn = fwFnStr;
      // Insert the menu function into the HEAD.
      var scriptTag = theHead.getElementsByTagName('SCRIPT');
      if (scriptTag.length > 0){
        scriptTag[0].innerHTML = menuFn + scriptTag[0].innerHTML;
      }else{
        theHead.innerHTML = theHead.innerHTML + '<script language="JavaScript">\n<!--\n' + menuFn + '\n//-->\n</' + 'script>\n';
      }
      // If there's no mmLoadMenus() function, there's also no reference to mm_menu.js.
      // Make sure to grab that from the Fireworks file as well.
      var startMenuFileRef = fwHead.innerHTML.search(/<script language="JavaScript1\.2" src="/);
      if (startMenuFileRef == -1){
        // try with "type"
        startMenuFileRef = fwHead.innerHTML.search(/<script language="JavaScript1\.2" type="text\/javascript" src="/);
      }
      if (startMenuFileRef != -1){
        var menuFileRef = fwHead.innerHTML.substring(startMenuFileRef,fwHead.innerHTML.indexOf('>',startMenuFileRef)+1);
        theHead.innerHTML = theHead.innerHTML + menuFileRef + '<' + '/script>';
      }
    }
  }
  return retVal;
}



//******************* GENERAL CLASSES **********************

//*************************************

//File reference node
//
function FileRef(fileURL, docURL) {
  this.file = new File(fileURL, docURL);
  this.refs = new Array();
}


//List of referenced files
//
function FileRefList(theDocURL) {
  this.docURL = theDocURL;
  this.list = new Array();
}
FileRefList.prototype.add = FileRefList_add;
FileRefList.prototype.getOutsideOfSite = FileRefList_getOutsideOfSite;
FileRefList.prototype.getTargetExists = FileRefList_getTargetExists;
FileRefList.prototype.copyToFolder = FileRefList_copyToFolder;
FileRefList.prototype.updateURLs = FileRefList_updateURLs;

function FileRefList_add(fileURL, refObj) {
  var node;
  for (var i=0; !node && i < this.list.length; i++)
    if (this.list[i].file.getPath() == fileURL) node = this.list[i];
  if (!node) {
    node = new FileRef(fileURL, this.docURL);
    this.list.push(node);
  }
  node.refs.push(refObj);
}

//Returns the list of files which are outside of the current site root
function FileRefList_getOutsideOfSite(theSiteURL) {
  var file, retList = new Array();
  for (var i=0; i < this.list.length; i++) {
    file = this.list[i].file;
    if (file.exists() && file.getAbsolutePath().toLowerCase().indexOf(theSiteURL.toLowerCase()) != 0)
      retList.push(MMNotes.localURLToFilePath(file.getAbsolutePath()));
  }
  return retList;
}

//Returns the list of files which already exist in the target folder
function FileRefList_getTargetExists(theFolderURL, theSiteURL) {
  var file, newFile, retList = new Array();
  for (var i=0; i < this.list.length; i++) {
    file = this.list[i].file;
    if (file.exists() && theSiteURL && file.getAbsolutePath().toLowerCase().indexOf(theSiteURL.toLowerCase()) != 0) {
      newFile = new File(theFolderURL + File.separator + file.getName());
      if (newFile.exists())
        retList.push(MMNotes.localURLToFilePath(newFile.getAbsolutePath()));
  } }
  return retList;
}

//Returns the list of files that could not be copied
function FileRefList_copyToFolder(theFolderURL, theSiteURL, overwrite) {
  var file, newFile, retList = new Array();
  for (var i=0; i < this.list.length; i++) {
    file = this.list[i].file;
    if (file.exists() && theSiteURL && file.getAbsolutePath().toLowerCase().indexOf(theSiteURL.toLowerCase()) != 0) {
      newFile = new File(theFolderURL + File.separator + file.getName());
      if (newFile.exists() && !overwrite) {
        file.setPath(newFile.getPath());
      } else {
        result = file.copyTo(newFile.getPath());
        if (!result)
          retList.push(MMNotes.localURLToFilePath(newFile.getAbsolutePath()));
        file.setPath(newFile.getPath());
  } } }
  return retList;
}

function FileRefList_updateURLs(newDocURL, newSiteURL,fwDOM) {
  var newRef, fullURL, index, filePath, docPath;
  for (var i=0; i < this.list.length; i++) with (this) {
    if (list[i].file.exists()) {
      newRef = '';
      fullURL = list[i].file.getAbsolutePath();
      if (newSiteURL && fullURL.toLowerCase().indexOf(newSiteURL.toLowerCase()) == 0)
        newRef = fullURL.substring(newSiteURL.length); // site relative
      else if (newDocURL && fullURL.toLowerCase().indexOf(newDocURL.toLowerCase()) == 0)
        newRef = fullURL.substring(newDocURL.length); // doc relative, below doc
      else if (newDocURL) {  // doc relative, above doc
        for (index=0; index < fullURL.length && index < newDocURL.length; index++)
          if (fullURL.charAt(index) != newDocURL.charAt(index)) break;
        index = fullURL.substring(0, index).lastIndexOf(File.separator)+1; // backup to last directory
        filePath = fullURL.substring(index);
        docPath = newDocURL.substring(index);
        if (docPath && docPath.indexOf('|') == -1) {  // image on a separate drive
          for (var j=0; j < docPath.length; j++)
            if (docPath.charAt(j) == File.separator) newRef += "../";
          newRef += filePath;
      } }
      if (!newRef) newRef = fullURL;  // local file ref

      for (var j=0; j < list[i].refs.length; j++)
        list[i].refs[j].setURL(newRef);
  } }
  
  // We can't get at the jsRefs in the preloadImages fn call
  // in the normal way when pasting FWHTML because the entire 
  // clipboard text is in the body of the temp file. (Dreamweaver
  // can't find the BODY tag that's *in* the body, because
  // that would be invalid. :) So what we do instead is build
  // an array of refs from other function calls (in JsFileRef_setURL),
  // on the assumption that the URLs in the preloadImages call 
  // came from those other function calls. Then, when all URL 
  // updating is done, we we do a regexp search & replace for 
  // any refs that still need updating. The assumption is that 
  // the only ones left will be the ones in the preload call.
  if (PRELOAD_REFS.length > 0 && fwDOM.body.innerHTML.indexOf('onLoad="MM_preloadImages') != -1){
    for (var p=0; p < PRELOAD_REFS.length; p=p+2){
      if (PRELOAD_REFS[p].indexOf('|') != -1){
        PRELOAD_REFS[p] = PRELOAD_REFS[p].replace(/\|/,'\\|');
      }
      var oldURL = new RegExp(PRELOAD_REFS[p]);
      if (fwDOM.body.innerHTML.match(PRELOAD_REFS[p])){
        fwDOM.body.innerHTML = fwDOM.body.innerHTML.replace(oldURL,PRELOAD_REFS[p+1]);
      }
    }   
  }
}


//****************************************************************
// Special case for images that are in the mmLoadMenus() function.
//
// List of all image refs in fn
function StringInFnList(sourceDOM, theFileList){
  this.list = new Array();
  this.init(sourceDOM, theFileList);
}
StringInFnList.prototype.init = StringInFnList_init;

function StringInFnList_init(sourceDOM, theFileList) {
  var strings, node;
  var search = new RegExp('(bgImage(Up|Over))="([^"]*)";','g');
  while ((strings = search.exec(sourceDOM.documentElement.childNodes[0].innerHTML)) != null){
    node = new StringInFn(sourceDOM.documentElement.childNodes[0],strings[0],strings[1],strings[3]);
    this.list.push(node);
    theFileList.add(strings[3], node);
  }
}

//
// Individual image refs
function StringInFn(theHead,theString,subString1,subString3) {
  this.fullStr = theString;
  this.propName = subString1;
  this.url = subString3;
  this.theHead = theHead;
}
StringInFn.prototype.setURL = StringInFn_setURL;

function StringInFn_setURL(newURL) {
  // If this.url happens to be a file:// URL, it'll have
  // a pipe in the path -- which must be escaped for it
  // to be used in a regular expression pattern.
  if (this.url.indexOf('|') != -1){
    this.url = this.url.replace(/\|/,'\\|');
  }
  var matchStr = new RegExp('(' + this.propName + ')="(' + this.url + ')"');
  this.theHead.innerHTML = this.theHead.innerHTML.replace(matchStr, '$1="' + newURL + '"');
}

//*************************************
//Image Tag
//
function ImageTag(theImgTag) {
  this.imgTag = theImgTag;
  this.name = this.imgTag.name;
  this.jsRefs = new Array();  // list of js references to this tag
}
ImageTag.prototype.getURL = ImageTag_getURL;
ImageTag.prototype.setURL = ImageTag_setURL;
ImageTag.prototype.addJsRef = ImageTag_addJsRef;
ImageTag.prototype.makeNameUnique = ImageTag_makeNameUnique;

function ImageTag_getURL() {
  return this.imgTag.src;
}

function ImageTag_setURL(newURL) {
  this.imgTag.src = newURL;
}

function ImageTag_addJsRef(theJsRef) {
  this.jsRefs.push(theJsRef);
}

function ImageTag_makeNameUnique(theNameList) {
  var index=0, count = 2, newName = this.name;
  while (index < theNameList.length)
    if (theNameList[index] == newName) {
      newName = this.name + "_" + count++; // increment name
      index=0; // re-start search
    } else index++;
  if (newName != this.name) {
    this.imgTag.name = newName;
    for (var i=0; i < this.jsRefs.length; i++)
      this.jsRefs[i].setTagName(newName);
  }
}

//*************************************

//Script Tag
//
function ScriptTag(theScriptTag) {
  this.scriptTag = theScriptTag;
}
ScriptTag.prototype.getURL = ScriptTag_getURL;
ScriptTag.prototype.setURL = ScriptTag_setURL;
ScriptTag.prototype.getHTML = ScriptTag_getHTML;

function ScriptTag_getURL() {
  return this.scriptTag.src;
}

function ScriptTag_setURL(newURL) {
  this.scriptTag.src = newURL;
}

function ScriptTag_getHTML() {
  return this.scriptTag.outerHTML;
}
//*************************************

//List of Script tags
//
function ScriptTagList(sourceDOM, theFileList) {
  this.list = new Array();
  this.init(sourceDOM, theFileList);
}
ScriptTagList.prototype.init = ScriptTagList_init;
ScriptTagList.prototype.findFunction = ScriptTagList_findFunction;
ScriptTagList.prototype.findFile = ScriptTagList_findFile;

function ScriptTagList_init(sourceDOM, theFileList) {
  var tags = new Array(), node;
  // add image tags
  tags = sourceDOM.getElementsByTagName("SCRIPT");
  for (var i=0; i < tags.size; i++) {
    node = new ScriptTag(tags.item(i));
    this.list.push(node);
    if (node.getURL())
      theFileList.add(node.getURL(), node);
  }
  // add flash, director, etc
}

function ScriptTagList_findFunction(theFunction){
  var theHTML;
  var retVal="";
  for (var i=0; i<this.list.length; i++){
  theHTML = this.list[i].getHTML();
    if (theHTML){
      var startFn = theHTML.indexOf("function " + theFunction);
      var endFn = theHTML.indexOf("// " + theFunction);
      if (startFn != -1 && endFn != -1){
        retVal = theHTML;
        break;
      }
    }
  }
  return retVal;
}

function ScriptTagList_findFile(theFile){
  var theURL;
  var retVal="";
  var searchPatt = new RegExp(theFile);
  for (var i=0; i<this.list.length; i++){
  theURL = this.list[i].getURL();
    if (theURL && theURL.search(searchPatt)!=-1){
      retVal= this.list[i].getHTML();
      break;
    }
  }
  return retVal;
}
//*************************************

//List of HTML tags
//
function TagList(sourceDOM, theFileList) {
  this.list = new Array();
  this.init(sourceDOM, theFileList);
}
TagList.prototype.init = TagList_init;
TagList.prototype.addJsRef = TagList_addJsRef;
TagList.prototype.makeNamesUnique = TagList_makeNamesUnique;
TagList.prototype.fixJsRefs = TagList_fixJsRefs;

function TagList_init(sourceDOM, theFileList) {
  var tags, node;
  // add image tags
  tags = sourceDOM.getElementsByTagName("IMG");
  for (var i=0; i < tags.size; i++) {
    node = new ImageTag(tags.item(i));
    this.list.push(node);
    theFileList.add(node.getURL(), node);
  }
  // add flash, director, etc
}

function TagList_addJsRef(tagName, refObj) {
  var node;
  for (var i=0; !node && i < this.list.length; i++)
    if (this.list[i].name == tagName) node = this.list[i];
  if (node) node.addJsRef(refObj);
}

function TagList_makeNamesUnique(targetDOM) {
  var tags, nameList = new Array();
  // create tag name list
  tags = targetDOM.getElementsByTagName("IMG");
  for (var i=0; i < tags.size; i++)
    nameList.push(tags.item(i).name);
  // make names unique
  for (var i=0; i < this.list.length; i++)
    if (this.list[i].makeNameUnique != null)
      this.list[i].makeNameUnique(nameList);
}

// update the JS references if they are being inserted in layers
function TagList_fixJsRefs(targetDOM, selection) {
}


//*************************************

//JavaScript File reference
//
function JsFileRef(theTag, theAttr, theFileURL) {
  this.tag = theTag;
  this.attr = theAttr;
  this.url = stripSpaces(theFileURL);
}
JsFileRef.prototype.getURL = JsFileRef_getURL;
JsFileRef.prototype.setURL = JsFileRef_setURL;

function JsFileRef_getURL() {
  return stripQuotes(this.url);
}

function JsFileRef_setURL(newURL) {
  var jsCalls, index;
  jsCalls = this.tag.getAttribute(this.attr);
  index = jsCalls.indexOf(this.url);
  if (index >= 0) {
    jsCalls = jsCalls.substring(0,index) +
              "'" + newURL + "'" +
              jsCalls.substring(index + this.url.length);
    this.tag.setAttribute(this.attr, jsCalls);
    PRELOAD_REFS.push(this.url);
    PRELOAD_REFS.push("'" + newURL + "'");
  }
}


//*************************************

//JavaScript Tag reference
//
function JsTagRef(theTag, theAttr, theTagRef, theRefType) {
  this.tag = theTag;
  this.attr = theAttr;
  this.tagRef = stripSpaces(theTagRef);
  this.refType = theRefType; // NS4.0REF or IE4.0REF or objName

  // Convert to the new find object behavior parameters
  if (this.refType == "NS4.0REF")
    this.setTagRef("'" + getNameFromRef(stripQuotes(this.tagRef)) + "'");
  else if (this.refType == "IE4.0REF")
    this.setTagName('');
}
JsTagRef.prototype.getTagName = JsTagRef_getTagName;
JsTagRef.prototype.setTagName = JsTagRef_setTagName;
JsTagRef.prototype.setTagRef = JsTagRef_setTagRef;

function JsTagRef_getTagName() {
  var retVal, index;
  retVal = stripQuotes(this.tagRef);
  index = retVal.indexOf('?')
  if (index >= 0) retVal = retVal.substring(0, index); // remove frame name
  return retVal;
}

function JsTagRef_setTagName(newName) {
  var index, newRef, tagName;
  if (this.tagRef != "''") with (this) {
    tagName = this.getTagName();
    index = tagRef.indexOf(tagName);
    if (index >= 0) {
      newRef = tagRef.substring(0, index) +
               newName +
               tagRef.substring(index + tagName.length);
      setTagRef(newRef);
  } }
}

function JsTagRef_setTagRef(newRef) {
  var jsCalls, refStart;
  with (this) {
    jsCalls = tag.getAttribute(attr);
    refStart = jsCalls.indexOf(tagRef);
    if (refStart >= 0) {
      jsCalls = jsCalls.substring(0,refStart) +
                newRef +
                jsCalls.substring(refStart + tagRef.length);
      tag.setAttribute(attr, jsCalls);
      this.tagRef = newRef;
  } }
}


//*************************************

//List of JavaScript calls which have file or tag references
//
function JsRefList(sourceDOM, theFileList, theTagList) {
  this.list = new Array();
  this.init(sourceDOM, theFileList, theTagList);
}
JsRefList.prototype.init = JsRefList_init;
JsRefList.prototype.add = JsRefList_add;
JsRefList.prototype.getBehArgs = JsRefList_getBehArgs;

JsRefList.TagList = new Array("BODY", "A", "AREA", "IMG");
JsRefList.AttrList = new Array("onClick", "onMouseOver", "onMouseOut", "onLoad");
JsRefList.swapImageFn  = "MM_swapImage";
JsRefList.nbGroupFn    = "MM_nbGroup";
JsRefList.preloadFn    = "MM_preloadImages";


function JsRefList_init(sourceDOM, theFileList, theTagList) {
  for (var i=0; i < JsRefList.TagList.length; i++) {
    tagList = sourceDOM.getElementsByTagName(JsRefList.TagList[i]);
    for (var j=0; j < tagList.size; j++){
      for (var k=0; k < JsRefList.AttrList.length; k++)
        if (tagList.item(j).getAttribute(JsRefList.AttrList[k]) != null)
          this.add(theFileList, theTagList, tagList.item(j), JsRefList.AttrList[k]);
    }
  }
}

function JsRefList_add(theFileList, theTagList, theTag, theAttr) {
  var callString, jsCalls, fnCall, args, argMask, node, mask;
  callString = theTag.getAttribute(theAttr);
  jsCalls = dreamweaver.getTokens(callString, ";");
  for (var i=0; i < jsCalls.length; i++) {
    fnCall = dreamweaver.getTokens(jsCalls[i], "()");
    if (fnCall.length > 1) {
      args = dreamweaver.getTokens(fnCall[1], ",");
      argMask = this.getBehArgs(fnCall[0], jsCalls[i]);
      for (var j=0; j < argMask.length && j < args.length; j++) {
        mask = argMask[j].toUpperCase();
        if (mask == "DEP" || mask == "URL") {
          node = new JsFileRef(theTag, theAttr, args[j]);
          this.list.push(node);
          theFileList.add(node.getURL(), node);
        } else if (mask == "NS4.0REF" || mask == "IE4.0REF") {
          node = new JsTagRef(theTag, theAttr, args[j], mask);
          this.list.push(node);
          theTagList.addJsRef(node.getTagName(), node);
        } else if (mask == "OBJNAME") {
          node = new JsTagRef(theTag, theAttr, args[j], mask);
          this.list.push(node);
          theTagList.addJsRef(node.getTagName(), node);
  } } } }
}

function JsRefList_getBehArgs(theFn, theFnCall) {
  var retList= '';
  if (stripSpaces(theFn) == JsRefList.swapImageFn)
    retList = SwapImage_identifyBehaviorArguments(theFnCall);
  if (stripSpaces(theFn) == JsRefList.nbGroupFn)
    retList = NavBarGroup_identifyBehaviorArguments(theFnCall);
  if (stripSpaces(theFn) == JsRefList.preloadFn)
    retList = PreloadImages_identifyBehaviorArguments(theFnCall);
  if (retList) retList = retList.split(',');
  return retList;
}


//******************* BEHAVIOR FUNCTIONS **********************

function SwapImage_identifyBehaviorArguments(fnCallStr) {
  var argList, argArray, numArgGroups, i;

  argList = "";
  argArray = extractArgs(fnCallStr);
  numArgGroups = Math.floor((argArray.length - 1) / 3); //args come in triplets
  for (i=0; i<numArgGroups; i++) {          //with each NSobj,IEobj,URL triplet
    if (argList) argList += ",";
    //if no dot in the name, return simple name; else, return NS/IE refs
    argList += (argArray[3*i+1].indexOf(".")==-1)? "objName,other,DEP":"NS4.0ref,IE4.0ref,DEP";
  }
  return argList;
}


function NavBarGroup_identifyBehaviorArguments(fnCallStr) {
  var argList = '';
  var args = extractArgs(fnCallStr);
  if (args[1] == 'init' || args[1] == 'down') {
    argList = "other,other";
    for (i=3; i+1 < args.length; i+=2)
      argList += ",objName,DEP";
  } else if (args[1] == 'over') {
    argList = "other";
    for (i=2; i+2 < args.length; i+=3)
      argList += ",objName,DEP,DEP";
  } else if (args[1] == 'out') {
    argList = "other";
  }
  return argList;
}

function PreloadImages_identifyBehaviorArguments(fnCallStr) {
  var argList = '';
  var args = extractArgs(fnCallStr);
  if (args.length > 1){
    argList = "DEP";
  }
  for (var i=2; i < args.length; i++){
    argList += ",DEP";
  }
  return argList;
}

//******************* GENERAL FUNCTIONS **********************

function displayArray(array, limit, separator, hideElipsis) {
  var retList = array, retVal = '';
  if (array.length > limit) {
    retList = array.slice(0, limit);
    if (!hideElipsis) retList.push('...');
  }
  return retList.join(separator);
}

function extractArgs(behFnCallStr){
  var i, theStr, lastPos, argArray;

  argArray = getTokens(behFnCallStr,"(),");
  for (i=0; i<argArray.length; i++) {
    theStr = unescQuotes(argArray[i]);
    lastPos = theStr.length-1;
    if (theStr.charAt(0) == "'" && lastPos > 0 && theStr.charAt(lastPos) == "'")
      argArray[i] = theStr.substring(1,lastPos);
  }
  return argArray
}

function unescQuotes(theStr){
  var strLen, i, theChar, unescStr = "";
  strLen = theStr.length;
  for(i=0; i<strLen; i++) {
    theChar = theStr.charAt(i);
    if (theChar == "\\" && i < strLen - 1) //if escape char and not end
      theChar = theStr.charAt(++i); //append next char and skip over
    unescStr += theChar;
  }
  return unescStr;
}

//Removes any quotes around the given string
function stripQuotes(theStr) {
  var theQuote;
  if (theStr.length > 1) { //if possibly quoted
    theQuote = theStr.charAt(0);
    if ((theQuote == "'" || theQuote == '"') &&
        theStr.charAt(theStr.length-1) == theQuote)
      theStr = theStr.substring(1,theStr.length-1);
  }
  return theStr
}

//Removes any spaces at the beginning or end of the string
function stripSpaces(theStr) {
  if (!theStr) theStr = "";  //ensure its not null
  theStr = theStr.replace(/^\s*/,""); //strip leading
  theStr = theStr.replace(/\s*$/,""); //strip trailing
  return theStr;
}

function printf() {
var i,numArgs,retStr="",argNum=0,startPos;

  numArgs = arguments.length;
  if (numArgs) {
    theStr = arguments[argNum++];
    startPos = 0;  endPos = theStr.indexOf("%s",startPos);
    if (endPos == -1) endPos = theStr.length;
    while (startPos < theStr.length) {
      retStr += theStr.substring(startPos,endPos);
      if (argNum < numArgs && endPos < theStr.length) retStr += arguments[argNum++];
      startPos = endPos+2;  endPos = theStr.indexOf("%s",startPos);
      if (endPos == -1) endPos = theStr.length;
    }
    if (!retStr) retStr = arguments[0];
  }
  return retStr;
}

function absoluteToRelativeURL(absURL, docURL,check)
{
  var newRef, fullURL, index, filePath, docPath;
    if ((!check || DWfile.exists(absURL)) && absURL)
  {
      newRef = '';
      fullURL = absURL;
      if (docURL && fullURL.indexOf(docURL) == 0)
        newRef = fullURL.substring(docURL.length); // doc relative, below doc
      else if (docURL) {  // doc relative, above doc
        for (index=0; index < fullURL.length && index < docURL.length; index++)
          if (fullURL.charAt(index) != docURL.charAt(index)) break;
        index = fullURL.substring(0, index).lastIndexOf(File.separator)+1; // backup to last directory
        filePath = fullURL.substring(index);
        docPath = docURL.substring(index);
        if (docPath && docPath.indexOf('|') == -1) {  // image on a separate drive
          for (var j=0; j < docPath.length; j++)
            if (docPath.charAt(j) == File.separator) newRef += "../";
          newRef += filePath;
      } }
      if (!newRef) newRef = fullURL;  // local file ref

    }
  return newRef;
}


