//SHARE-IN-MEMORY=true
//Copyright 2006 Adobe Systems, Inc. All rights reserved.
//
// This is the base class for implementing an "Active Content extension." An
// Active Content extension is code that knows how to rewrite OBJECT
// tags in such a way as to comply with the Eolas
// patent but still not cause the browser to display the "Press OK to continue
// loading the content of this page" message box.


//----------------------------------------------------------------------------
// Constructor
//----------------------------------------------------------------------------
function ActiveContent()
{
}

// Often subclassed:
ActiveContent.prototype.processAttributes = ActiveContent_processAttributes;
ActiveContent.prototype.getTwoLetterCode = ActiveContent_getTwoLetterCode; // never called before processAttributes()

// Sometimes subclassed:
ActiveContent.prototype.generateScript = ActiveContent_generateScript;
ActiveContent.prototype.generateFunctionName = ActiveContent_generateFunctionName;

// Rarely subclassed:
ActiveContent.prototype.appendParams = ActiveContent_appendParams;
ActiveContent.prototype.appendAttributesOfSpecifiedTag = ActiveContent_appendAttributesOfSpecifiedTag;
ActiveContent.prototype.appendAttributes = ActiveContent_appendAttributes;
ActiveContent.prototype.quoteForJS = ActiveContent_quoteForJS;
ActiveContent.prototype.objectTagHasOnlyParamAndEmbed = ActiveContent_objectTagHasOnlyParamAndEmbed;

//----------------------------------------------------------------------------
// convertToXSLValueOf
//
// Utility function to convert XSLT's curly brace ("{}") syntax to
// <xsl:value-of/> tags.
//
// 'strIn' is a string that might contain curly brace syntax (e.g. '{path/to/item}')
// 'strResult' is the return string with tag syntax (e.g. '<xsl:value-of select="path/to/item" />')
//----------------------------------------------------------------------------
function convertToXSLValueOf(strIn)
{
	var strResult = "";
	while(strIn.length)
	{
		var nFoundBegin = strIn.indexOf('{');
		var nFoundEnd = strIn.indexOf('}', nFoundBegin+1);
		var strMarkup = (nFoundBegin >= 0 && nFoundEnd > nFoundBegin ? strIn.substring(nFoundBegin, nFoundEnd+1) : "");
		if(strMarkup.length)
		{
			// get the string before the markup
			strResult += strIn.substr(0, nFoundBegin);
			// append the altered markup
			strResult += '<xsl:value-of select="' + strMarkup.substr(1, strMarkup.length-2) + '" />';
			// strip what we've already processed
			strIn = strIn.substr(nFoundEnd+1);
		}
		else
		{
			// escape whatever is remaining (or the entire string)
			strResult += strIn;
			strIn = "";
		}
	}
	
	return strResult;
}

//----------------------------------------------------------------------------
// ActiveContent_processAttributes
//
// Called just before all the attributes are concatented into a list of
// arguments for the AC_XX_RunContent function call.  Typically this is used
// to make modifications to the list of arguments -- e.g. to remove ones that
// should not be output as part of the function call.
//
// 'attrs' is an associative array.  For example, attrs["width"] = "100"
//----------------------------------------------------------------------------
function ActiveContent_processAttributes(attrs)
{
  // need to convert XSLT curly braces into <xsl:value-of/> tags
  var dom = dw.getDocumentDOM();
  if(dom && (dom.documentType == 'XSLT-fragment' || dom.documentType == 'XSLT'))
  {
	for (var attr in attrs)
	{
		var strValue = convertToXSLValueOf(attrs[attr]);
		var strName = convertToXSLValueOf(attr);
		if(strName != attr || strName.indexOf("&") >= 0)
		{
			var strTempValue = attrs[attr];
			delete attrs[attr];
			strName = strName.replace(/&/g, "&amp;");
			attr = strName;
			attrs[attr] = strTempValue;
		}
		if(strValue != attrs[attr] || strValue.indexOf("&") >= 0)
		{
			strValue = strValue.replace(/&/g, "&amp;");
			attrs[attr] = strValue;
		}
	}
  }
}

//----------------------------------------------------------------------------
// ActiveContent_getTwoLetterCode
//
// Returns the two-letter code that corresponds to the current active-content
// tag.  E.g. Flash is "FL".  This function must be subclassed.
//----------------------------------------------------------------------------
function ActiveContent_getTwoLetterCode()
{
  // this function should never be called - should always be subclassed
  throw new ReferenceError("ActiveContent_getTwoLetterCode() should never be called");
}

//----------------------------------------------------------------------------
// ActiveContent_appendAttributes
//
// Given a node, parse its array of attributes.  Appends them to an
// associative array in 'outAttrs'.  Note: This function converts the
// attribute names to lower case.  E.g. if the original node was
//     <tagName attrName='attrVal'>
// then the outAttrs array will contain
//     outAttrs['attrname'] = 'attrVal'
//----------------------------------------------------------------------------
function ActiveContent_appendAttributes(node, outAttrs)
{
  var attrs = node.attributes;
  var len = attrs.length;
  for (var i=0; i<len; ++i)
  {
    // don't overwrite existing attribute values
    // default behavior is that <object> attributes trump others
    if(!outAttrs[attrs[i].name.toLowerCase()])
    {
      outAttrs[attrs[i].name.toLowerCase()] = attrs[i].value;
    }
  }
}

//----------------------------------------------------------------------------
// ActiveContent_appendAttributesOfSpecifiedTag
//
// Given a node and a tagname, find one node with the specified tagname.
// It may be the top-level node itself, or it may be a child.
//
// If such a node is found, append that tag's attributes to the 'outAttrs'
// associative array.
//----------------------------------------------------------------------------
function ActiveContent_appendAttributesOfSpecifiedTag(node, tagname, outAttrs)
{
  var nodes;
  if (node.tagName.toLowerCase() == tagname.toLowerCase())
    nodes = new Array( node );
  else
    nodes = node.getElementsByTagName(tagname);
  if (nodes && nodes[0])
  {
    this.appendAttributes(nodes[0], outAttrs);
  }
}

//----------------------------------------------------------------------------
// ActiveContent_appendParams
//
// Append the name/value pairs of all child <param> tags to the 'outAttrs'
// associative array.  Note: This function converts the attribute names to
// lower case.  E.g. if the original node was
//     <param name='paramName' value="paramVal">
// then the outAttrs array will contain
//     outAttrs['paramname'] = 'paramVal'
//----------------------------------------------------------------------------
function ActiveContent_appendParams(node, outAttrs)
{
  var paramNodes = node.getElementsByTagName("param");
  for (var i=0; i<paramNodes.length; ++i)
  {
    var paramNode = paramNodes[i];
    var name = paramNode.getAttribute("name");
    var value = paramNode.getAttribute("value");
    if (name != null && value != null)
      outAttrs[name.toLowerCase()] = value;
  }
}

//----------------------------------------------------------------------------
// ActiveContent_quoteForJS
//
// Quote a string so that it can be output as a JavaScript string.  This
// involves putting single-quote marks around it, and also escaping
// the ' and \ characters.
//----------------------------------------------------------------------------

function ActiveContent_quoteForJS_Internal(str)
{
	// Escape backslashes and single-quotes.
	
	str = str.replace( /([\\\'])/g, "\\$1" );
	
	// If we had \" to start, we now have \\" because of the replace above.
	// We want to turn that into \&quote;
	
	str = str.replace( /\\\\\"/g, "\\&quot;" );
	
	// If we ever get in here with unescaped
	// double-quotes, encode them now.
	
	str = str.replace( /[^\\]"/g, "&quot;" );
				
	return str;
}

function ActiveContent_quoteForJS(str)
{
  var dom = dw.getDocumentDOM();
  var strEscaped = "";
  if(dom)
  {
    var strServerMarkup;
    while(str.length)
    {     
      strServerMarkup = dom.findServerMarkup(str);//eolasTODO should  this be in?
      if(strServerMarkup.length) 
      {
        // escape the non-server markup
        strEscaped += ActiveContent_quoteForJS_Internal(str.substr(0, str.indexOf(strServerMarkup)));
        // append the server markup
        strEscaped += strServerMarkup;
        // strip what we've already processed
        str = str.substr(str.indexOf(strServerMarkup)+strServerMarkup.length);
      }
      else
      {
        // escape whatever is remaining (or the entire string)
        strEscaped += ActiveContent_quoteForJS_Internal(str);
        str = "";
      }
    }
  }
  else
  {
    // escape the entire string
    strEscaped = ActiveContent_quoteForJS_Internal(str);
  }

  return "'" + strEscaped + "'";
}

//----------------------------------------------------------------------------
// ActiveContent_objectTagHasOnlyParamAndEmbed
//
// Given an <object> tag, this returns if the contents of this tag are
// only <param> and <embed> tags. If any other content exists, return FALSE
//----------------------------------------------------------------------------
function ActiveContent_objectTagHasOnlyParamAndEmbed(node)
{
	//verify this is an object tag
	if( node.tagName.toLowerCase() != "object" ){
		return false;
	}
	
	var bResult = true;
	var children = node.childNodes;
	if(children.length)
	{
		// check if we find anything other than <param> and <embed> tags
		var nChildren = children.length;
		for( var i = 0; bResult && (i < nChildren); i++ )
		{
			var current = children.item(i);
			if(current.nodeType == Node.TEXT_NODE || (current.tagName.toLowerCase() != "param" && current.tagName.toLowerCase() != "embed"))
			{
				bResult = false;
			}
			else if( current.hasChildNodes() )
			{
				bResult = false;
			}
		}
	}
	return bResult;
}

//----------------------------------------------------------------------------
// ActiveContent_generateScript
//
// Given an <object> tag, this returns the contents
// of an equivalent script tag.  (The returned string should not include
// the <script> tag itself -- just its contents.)
//----------------------------------------------------------------------------
function ActiveContent_generateScript(node)
{
  var retval = "";

  // combine <object> and <param> data all into one associative array
  var attrs = new Object();
  this.appendAttributesOfSpecifiedTag(node, "object", attrs);
  this.appendAttributesOfSpecifiedTag(node, "embed", attrs);
  this.appendParams(node, attrs);

  // call our subclass to let it change the attributes as necessary
  this.processAttributes(attrs);

  // build the script tag
//retval = 'AC_' + this.getTwoLetterCode() + '_RunContent( ';

  retval = this.generateFunctionName() + '( ';  

  // build comma-separated list of attribute names and values
  var firstAttr = true;
  for (var attr in attrs)
  {
    if (!firstAttr)
      retval += ',';
    firstAttr = false;    
    retval += this.quoteForJS(attr) + ',' + this.quoteForJS(attrs[attr]);        
  }

  retval += ' ); //end AC code';  

  return retval;
}

function ActiveContent_generateFunctionName()//
{
   return 'AC_' + this.getTwoLetterCode() + '_RunContent';
}
