<HTML>
<HEAD>
<!-- Copyright 2000-2007 Adobe Systems Incorporated.  All rights reserved. -->

<TITLE>ColdFusion Translator</TITLE>

<meta http-equiv="Content-Type" content="text/html; charset=">

<script SRC="../Shared/MM/Scripts/CMN/quickString.js"></SCRIPT>
<SCRIPT SRC="../Shared/Common/Scripts/dwscripts.js"></SCRIPT>
<SCRIPT SRC="TransData.js"></SCRIPT>
<SCRIPT SRC="TMCallback.js"></SCRIPT>
<SCRIPT SRC="Translator.js"></SCRIPT>
<SCRIPT SRC="TranslationManager.js"></SCRIPT>
<SCRIPT SRC="ColdFusion/ExternalTranslation.js"></SCRIPT>

<SCRIPT>

// *************** GLOBALS VARS *****************

var SERVER_MODEL_NAME   = "ColdFusion";
var SERVER_MODEL_FOLDER = "ColdFusion";
var TRANSLATOR_CLASS    = "MM_COLDFUSION";
var TRANSLATOR_NAME     = SERVER_MODEL_NAME;

var BEGINNING_MARKER  = "<!-- MMDW:beginning -->";
var SUCCESS_MARKER    = "<!-- MMDW:success -->";

var BEGIN_LOCK        = "<mm:beginlock translatorClass=\"MM_LIVE_DATA\" type=\"" + TRANSLATOR_CLASS + "\" orig=\"%s\">";
var END_LOCK          = "<mm:endlock>";
var TRANSLATED_ATTR   = "mmvisible=false";

var serverGeneratedInclude = "\n<SCRIPT LANGUAGE=\"JavaScript\" TYPE=\"text/javascript\" SRC=\"/CFIDE/scripts/cfform.js\"></SCR"+"IPT>\n";
var serverGeneratedScript = "function  _CF_";
var serverGeneratedObject = "CLASSID=\"clsid:D702FBF4-EE60-11d0-BD5B-00A0C91F4635\"";
var serverGeneratedMetaTag = "<META content=\"MSHTML 6.00.2600.0\" name=GENERATOR>"


//cfimport match - to add prefix to list of prefix array
var cfImportRegEx1 = /<cfimport\s*prefix\s*=\s*"([^"]*)"\s*taglib\s*=\s*"([^"]*)"\s*>/gi;
var cfImportRegEx2 = /<cfimport\s*taglib\s*=\s*"([^"]*)"\s*prefix\s*=\s*"([^"]*)"\s*>/gi;


// Specify which tags are cfml: everything with a "cf" prefix
var prefixArray = new Array();
prefixArray[0] = "cf";

// Specify which tags will NOT contain editable HTML in the returned doc
var nonHtmlArray = new Array();
nonHtmlArray[0] = "cfgrid";
nonHtmlArray[1] = "cfhttp";
nonHtmlArray[2] = "cfmail";
nonHtmlArray[3] = "cfquery";
nonHtmlArray[4] = "cfreport";
nonHtmlArray[5] = "cfscript";
nonHtmlArray[6] = "cftree";
nonHtmlArray[7] = "cfrow";
nonHtmlArray[8] = "cftable";
nonHtmlArray[9] = "cfstoredproc";


var debugTranslator   = false;
var macBeforeFileName = "Desktop:BeforeTranslation.txt";
var macAfterFileName  = "Desktop:AfterTranslation.txt";
var winBeforeFileName = "C:\\BeforeTranslation.txt";
var winAfterFileName  = "C:\\AfterTranslation.txt";

var liveDebugTranslator   = false;
var liveMacBeforeFileName = "Desktop:LiveBeforeTranslation.txt";
var liveMacAfterFileName  = "Desktop:LiveAfterTranslation.txt";
var liveWinBeforeFileName = "C:\\LiveBeforeTranslation.txt";
var liveWinAfterFileName  = "C:\\LiveAfterTranslation.txt";


// ******************* API **********************

//--------------------------------------------------------------------
// FUNCTION:
//   getTranslatorInfo
//
// DESCRIPTION:
//   Provides information about the translator and the files it can
//   act upon
//
// ARGUMENTS:
//   none
//
// RETURNS:
//   JavaScript array
//--------------------------------------------------------------------

function getTranslatorInfo()
{
  returnArray = new Array(9);

  returnArray[0] = "MM_COLDFUSION";    // The TRANSLATOR_CLASS
  returnArray[1] = "ColdFusion";       // The title
  returnArray[2] = "1" 				   // The number of extensions
  returnArray[3] = "%ColdFusion%" 	   // Extensions - indicate that we run on any
									   // extension for the ColdFusion document type
									   // as defined in MMDocumentTypes.xml
  returnArray[4] = "3";                // The number of expressions
  returnArray[5] = "<cf";              // Expression for PI and insert
  returnArray[6] = "#[^\\r\\n]{1,500}#";              // Expression for PI and insert
  returnArray[7] = "MM_XSLT_SB"			//XSLT Translator is can be on
  returnArray[8] = "byExpression";         // Run if doc contains expr
  returnArray[9] = "50";               // priority order to apply translator

  return returnArray
}


//--------------------------------------------------------------------
// FUNCTION:
//   translateMarkup
//
// DESCRIPTION:
//   This function performs the translation.
//
// ARGUMENTS:
//   docNameStr - string - URL of the document to be translated
//   siteRootStr - string - URL of the site root
//   inStr - string - the contents of the document
//
// RETURNS:
//   string - the translated contents of the document
//--------------------------------------------------------------------

function translateMarkup(docNameStr, siteRootStr, inStr)
{
  var outStr = "";

  var serverModel = "";
  if (dw.getDocumentDOM())
  {
    serverModel = dw.getDocumentDOM().serverModel.getFolderName();
    if (serverModel == "")
    {
      serverModel = dw.getDocumentDOM().getServerModelFolderNameFromDoc();
    }
  }

  if (serverModel == SERVER_MODEL_FOLDER &&
      inStr.length > 0)
  {

    if (debugTranslator)
    {
      if (dwscripts.IS_MAC)
        DWfile.write( macBeforeFileName, inStr );
      else
        DWfile.write( winBeforeFileName, inStr );
    }

    // translate
    if (inStr.indexOf("<cf") != -1 || inStr.indexOf("<CF") != -1 || inStr.indexOf("<xsl") != -1)
    {
      var TM = new TranslationManager(TRANSLATOR_CLASS, SERVER_MODEL_FOLDER, "");
      TM.serverModelAlwaysCheckTag = myAlwaysCheckTag;
      TM.serverModelAlwaysCheckAttribute = myAlwaysCheckAttribute;

      var split = TranslationManager.splitBody(inStr);
      outStr = TM.translate(split.inStr);
      if (outStr != "")
        outStr = split.preInStr + outStr + split.postInStr;
    }

    // update Server Behavior list
    dw.serverBehaviorInspector.findAllServerBehaviors();

    if (debugTranslator)
    {
      if (dwscripts.IS_MAC)
        DWfile.write( macAfterFileName, outStr );
      else
        DWfile.write( winAfterFileName, outStr );
    }

  }

  return outStr;
}


//--------------------------------------------------------------------
// FUNCTION:
//   myAlwaysCheckTag
//
// DESCRIPTION:
//   This function is called by the translation manager to determine
//   if the given tag should be checked for dynamic info.
//
// ARGUMENTS:
//   tagName - string - the name of the tag to check
//
// RETURNS:
//   boolean
//--------------------------------------------------------------------

function myAlwaysCheckTag(tagName)
{
  return (tagName.substr(0, 2) == "cf");
}


//--------------------------------------------------------------------
// FUNCTION:
//   myAlwaysCheckAttribute
//
// DESCRIPTION:
//   This function is called by the translation manager to determine
//   if the given attribute code should be checked for dynamic info.
//
// ARGUMENTS:
//   code - string - the code to check
//
// RETURNS:
//   boolean
//--------------------------------------------------------------------

function myAlwaysCheckAttribute(code)
{
  var result = (code.indexOf("#") != -1 ||
                code.indexOf("<cf") != -1);
  return result;
}

//--------------------------------------------------------------------
// FUNCTION:
//   addCustomTagPrefix
//
// DESCRIPTION:
//	this function add custom tag prefix to list of standard 
//	tag prefixes and updates the prefixArray
//
// ARGUMENTS:
//	 docStr
//
// RETURNS:
//   none
//--------------------------------------------------------------------

function addCustomTagPrefix(inStr)
{
  var arr = null;
  while ((arr = cfImportRegEx1.exec(inStr)) != null)
  {
	//the first match is prefix name for custom tag library
	prefixArray.push(RegExp.$1);
  }
  while ((arr = cfImportRegEx2.exec(inStr)) != null)
  {
    //the second match is prefix name for custom tag library
	prefixArray.push(RegExp.$2);
  }
}



//--------------------------------------------------------------------
// FUNCTION:
//   liveDataTranslateMarkup
//
// DESCRIPTION:
//   This function performs the translation while in live data mode
//
// ARGUMENTS:
//   docNameStr - string - the URL of the document to be translated
//   siteRootStr - string - the URL of the site root
//   inStr - string - the contents of the document
//
// RETURNS:
//   string - the translated contents of the document
//--------------------------------------------------------------------

function liveDataTranslateMarkup( docNameStr, siteRootStr, inStr )
{
  var outStr = "";

  if (dw.getDocumentDOM() &&
      dw.getDocumentDOM().serverModel.getFolderName() == SERVER_MODEL_FOLDER &&
      inStr.length > 0 &&
      siteRootStr.length > 0)
  {

    if (liveDebugTranslator)
    {
      if (dwscripts.IS_MAC)
        DWfile.write( liveMacBeforeFileName, inStr );
      else
        DWfile.write( liveWinBeforeFileName, inStr );
    }

    var success = true;
    var translated = "";

    // Send init tags to the server and request requst them
    initTags = dreamweaver.getLiveDataInitTags();
    if (initTags.length > 0)
    {
      // Add a marker at the end so we can check for successful completion
      initTags = initTags + SUCCESS_MARKER;

      // Send file to server and get the response
      translated = dreamweaver.liveDataTranslate(docNameStr, initTags);

      // If we can't find our marker, report that an error occurred
      if (translated.data.lastIndexOf(SUCCESS_MARKER) == -1)
      {
        if (translated.data.length > 0)
        {
          reportError(docNameStr, initTags);
        }
        success = false;
      }
    }

    // if initialization was successful, send the document
    if (success)
    {
      // Wrap labels around the cfml tags
      LiveDataTranslator.initialize();
      //check the doc-string for list of custom imports
      addCustomTagPrefix(inStr);
      LiveDataTranslator.beginPreTranslate("<!---", "--->", prefixArray, nonHtmlArray);
      dreamweaver.scanSourceString(inStr, LiveDataTranslator);
      var labels = LiveDataTranslator.endPreTranslate(inStr);

      // The ColdFusion server will replace all the escaped "##" characters that
      // are inside cfoutput tags with an unescaped "#".  When we do the "diff"
      // below, we want hashes inside cfoutput tags to be escaped again.  The
      // solution is to make every "##" be a "####" right now, so that those
      // previously-escaped "##" characters come back from the server as "##".
      labels = LiveDataTranslator.replaceTextInsideTag(labels,"cfoutput", "##", "####");

      // Add a marker at the end so we can check for successful completion
      // Add a marker at the beginning so we can trim code that was inserted
      // at the top of the document.  This occurs when application.cfm contains
      // static content, such as HTML comments.
      labelsAndMarker = BEGINNING_MARKER + labels + SUCCESS_MARKER;

      // If we send a file with Mac-style "\r" line endings to a Unix server,
      // the server will ignore the "\r" characters (line ending is "\n" on
      // Unix) and send back a file with no line endings.  Instead, we'll
      // convert line endings to "\r\n" before sending to the server.
      labelsAndMarker = LiveDataTranslator.setLineEndingsToMatch("\r\n",labelsAndMarker);

      // Send file to server and get the response
      translated = dreamweaver.liveDataTranslate(docNameStr, labelsAndMarker);

      // If we can't find our marker, report that an error occurred
      endIndex = translated.data.lastIndexOf(SUCCESS_MARKER);
      if (endIndex == -1)
      {
        if (translated.data.length > 0)
        {
          reportError(docNameStr, inStr);
        }
        LiveDataTranslator.cleanup();
        success = false;
      }
    }

    // if the page was returned successfully, get the returned string
    if (success)
    {
      // Trim everything after SUCCESS_MARKER
      if (endIndex != -1)
        translated.data = translated.data.substring(0, endIndex);

      // Trim everything before BEGINNING_MARKER.
      //
      // If BEGINNING_MARKER is missing and the original string contained
      // a cfcontent tag, then everything before the cfcontent tag was
      // removed from the document.  Add back the "<!-- MMDW:0 -->" marker,
      // so that our diff algorithm will treat everything before the cfcontent
      // tag as server markup.
      beginIndex = translated.data.indexOf(BEGINNING_MARKER);
      if (beginIndex != -1)
        translated.data = translated.data.substring(beginIndex+BEGINNING_MARKER.length);
      else if (inStr.search(/<cfcontent/i) != -1)
        translated.data = "<!--MMDW 0 -->" + translated.data;

      // If the server added script or object tags at the top of the document,
      // then remove them.  Otherwise, they may be added to the document without
      // being inside a locked region.
      var bSomethingFound = false;
      var beforeRemoveTag;
      do
      {
        bSomethingFound = false;
        index = translated.data.indexOf(serverGeneratedInclude);
        if (index != -1 && inStr.indexOf(serverGeneratedInclude) == -1)
        {
          beforeRemoveTag = translated.data;
          translated.data = translated.data.substring(0, index) + translated.data.substring(index + serverGeneratedInclude.length);
          if (beforeRemoveTag != translated.data)
            bSomethingFound = true;
        }
        index = translated.data.indexOf(serverGeneratedScript);
        if (index != -1 && inStr.indexOf(serverGeneratedScript) == -1)
        {
          beforeRemoveTag = translated.data;
          translated.data = removeTag("\n\r<script", "</scr" + "ipt>\n\r", translated.data, index);
          if (beforeRemoveTag == translated.data)
            translated.data = removeTag("\r\n<script", "</scr" + "ipt>\r\n", translated.data, index);
          if (beforeRemoveTag == translated.data)
            translated.data = removeTag("\n\n<SCRIPT", "</SCR" + "IPT>\n", translated.data, index);
          if (beforeRemoveTag == translated.data)
            translated.data = removeTag("\r\n<script", "</scr" + "ipt>", translated.data, index);
          if (beforeRemoveTag != translated.data)
            bSomethingFound = true;
        }
        index = translated.data.indexOf(serverGeneratedObject);
        if (index != -1 && inStr.indexOf(serverGeneratedObject) == -1)
        {
          beforeRemoveTag = translated.data;
          translated.data = removeTag("\r\n\r\n<OBJECT", "</OBJECT>\r\n\r\n\n\r", translated.data, index);
          if (beforeRemoveTag != translated.data)
            bSomethingFound = true;
        }
        index = translated.data.indexOf(serverGeneratedMetaTag);
        if (index != -1 && inStr.indexOf(serverGeneratedMetaTag) == -1)
        {
          beforeRemoveTag = translated.data;
          translated.data = translated.data.substring(0, index) + translated.data.substring(index + serverGeneratedMetaTag.length);
          if (beforeRemoveTag != translated.data)
            bSomethingFound = true;
        }
      } while (bSomethingFound);

      // Some HTTP servers change the line endings in the file
      translated.data = LiveDataTranslator.setLineEndingsToMatch(inStr,translated.data);

      // If the scripts on the page were not executed or there are no scripts
      // on the page, then just abort
      if (translated.data == labels)
      {
        success = false;
      }
    }

    if (success)
    {
      // Remove the labels and add lock tags
      LiveDataTranslator.beginPostTranslate();
      dreamweaver.scanSourceString(translated.data, LiveDataTranslator);
      outStr = LiveDataTranslator.endPostTranslate(inStr, translated.data, BEGIN_LOCK, END_LOCK, TRANSLATED_ATTR);
      LiveDataTranslator.cleanup();
    }

    if (liveDebugTranslator)
    {
      if (dwscripts.IS_MAC)
        DWfile.write( liveMacAfterFileName, outStr );
      else
        DWfile.write( liveWinAfterFileName, outStr );
    }

  }

  return outStr;
}


//--------------------------------------------------------------------
// FUNCTION:
//   reportError
//
// DESCRIPTION:
//   <description>
//
// ARGUMENTS:
//   <arg1> - <type and description>
//
// RETURNS:
//   <type and description>
//--------------------------------------------------------------------

function reportError(docNameStr, inStr)
{
  var translated, errorStr, extraHead, index;

  // We're going to abort, so first clean up
  LiveDataTranslator.cleanup();

  // The error message will have the wrong column numbers because we've
  // inserted our proprietary labels.  Send file to server again without
  // the labels.
  translated = dreamweaver.liveDataTranslate(docNameStr, inStr);
  errorStr = translated.data;

  // Our parser gets confused if it finds a second head in the
  // HTML string.  Remove the head that's added by ColdFusion.
  errorStr = removeSecondHead(errorStr);

  // Tell Dreamweaver to display the error message
  dreamweaver.setLiveDataError(errorStr);
}



//--------------------------------------------------------------------
// FUNCTION:
//   removeTag
//
// DESCRIPTION:
//   If a tag of the specified type surrounds the character at str[index],
//   then remove the entire contents of the tag.
//
// ARGUMENTS:
//   <arg1> - <type and description>
//
// RETURNS:
//   <type and description>
//--------------------------------------------------------------------

function removeTag(tagOpen, tagClose, str, index)
{
    var startIndex = str.lastIndexOf(tagOpen, index);
    var endIndex = str.indexOf(tagClose, index);
    var length = str.length;
    var outStr;

    // If opening or closing tag not found, then bail
    if (startIndex == -1 || endIndex == -1)
        return str;

    // Add text before tag's opening to outStr
    if (startIndex > 0)
        outStr = str.substring(0, startIndex);
    else
        outStr = "";

    // Add text after tag's closing to outStr
    endIndex = endIndex + tagClose.length;
    if (endIndex < length - 1)
        outStr = outStr + str.substring(endIndex, length);

    return outStr;
}


//--------------------------------------------------------------------
// FUNCTION:
//   removeSecondHead
//
// DESCRIPTION:
//   <description>
//
// ARGUMENTS:
//   <arg1> - <type and description>
//
// RETURNS:
//   <type and description>
//--------------------------------------------------------------------

function removeSecondHead(inStr)
{
  // Create an object to hold pointers to the callback funcs
  var callbackObj = new Object();
  callbackObj.openTagBegin  = removeHeadOpenTagBegin;
  callbackObj.closeTagBegin = removeHeadCloseTagBegin;
  callbackObj.closeTagEnd   = removeHeadCloseTagEnd;

  // Scan the inStr looking for a second head tag
  numHeadsFound = 0;
  headStart = -1;
  headEnd = -1;
  dreamweaver.scanSourceString(inStr, callbackObj);

  // If second head found, return string without it
  if (numHeadsFound > 1)
  {
    return inStr.substring(0, headStart) +
           inStr.substring(headEnd, inStr.length);
  }

  // No second head found, so just return inStr
  return inStr;
}


//--------------------------------------------------------------------
// FUNCTION:
//   removeHeadOpenTagBegin
//
// DESCRIPTION:
//   <description>
//
// ARGUMENTS:
//   <arg1> - <type and description>
//
// RETURNS:
//   <type and description>
//--------------------------------------------------------------------

function removeHeadOpenTagBegin(name, offset)
{
  if (name.toUpperCase() == "HEAD")
    headStart = offset;
}


//--------------------------------------------------------------------
// FUNCTION:
//   removeHeadCloseTagBegin
//
// DESCRIPTION:
//   <description>
//
// ARGUMENTS:
//   <arg1> - <type and description>
//
// RETURNS:
//   <type and description>
//--------------------------------------------------------------------

function removeHeadCloseTagBegin(name, offset)
{
  closeTagIsHead = (name.toUpperCase() == "HEAD") ? true : false;

  if (closeTagIsHead)
    numHeadsFound++;
}


//--------------------------------------------------------------------
// FUNCTION:
//   removeHeadCloseTagEnd
//
// DESCRIPTION:
//   <description>
//
// ARGUMENTS:
//   <arg1> - <type and description>
//
// RETURNS:
//   <type and description>
//--------------------------------------------------------------------

function removeHeadCloseTagEnd(offset)
{
  if (closeTagIsHead)
    headEnd = offset;
}


</SCRIPT>

</HEAD>

<BODY>
</BODY>
</HTML>
