/*******************************************************************************
 * reportlistcl.js
 *
 * HTML Rendering of the ReportList
 *
 * Copyright:    Copyright (c) 2008
 * Company:      Neolane
 *
 * $Author$
 * $Revision$
 ******************************************************************************/ 

/** Constructor 
  *
  * @htmlParent HTML element in the document where to render the list. 
  * @data       Data to display in an XML format. 
  * @style      Cell style definitions. */ 
function ReportList(htmlParent, data, styles, defaultFontSize)
{
  this.data       = data
  this.htmlTable  = htmlParent
  this.styles     = new Array()
  this.rowNum     = 0 // used by css generation (even odd classes)

  // compile styles  
  var childNode = styles.documentElement.firstChild
  while ( childNode != null )
  {
    if ( childNode.nodeType == XML.ELEMENT_NODE )
      this.compileStyle(childNode, defaultFontSize)

    childNode = childNode.nextSibling
  }
}

ReportList.prototype.COLORS  = new Array("#3B87A2", "#FF8012", "#8CA23B", "#A23B5E", "#004A62",
                                "#FFCCA0", "#C75400", "#CAD4A5", "#A5C8D4", "#6D0026",
                                "#D4A5B4", "#4F6400")

/** Compile a style to create a template of the <td>.
  *
  * @style the XML definition of the style. The schema of 
  *        this element is the one defined for the flat report
  *        format:
  *
  *   <style format="percentage" alignement="center">
  *     <font bold="true"/>
  *   </style>
  *
  * @see https://wiki.neolane.net/wiki/Reporting_5.00#srcSchema_like_xml_structure. */
ReportList.prototype.compileStyle = function(style, defaultFontSize)
{
  var td = document.createElement("td")
  var compiledStyle = compileStyle(style, td, defaultFontSize)
  compiledStyle.td = td
  this.styles.push(compiledStyle)
}

/** Render the list as an HTML table. */
ReportList.prototype.render = function()
{
  // clear the previous rendering of the table
  while ( this.htmlTable.firstChild != null )
    this.htmlTable.removeChild(this.htmlTable.firstChild)
    
  var htmlBody = document.createElement("tbody")
  this.renderNode(this.data, htmlBody)
  this.htmlTable.appendChild(htmlBody)
}

/** Click on a cell */
ReportList.prototype.onCellClick = function(ctx, action, target, transition, option, param, e)
{
  PerformClickAction(ctx, action, target, transition, option, param, e)
}

/** Generate a function for a clickable cell
  *
  * @object     the list.
  * @ctx        an array of value to set in the context before executing the action.
  * @action     action to perform (url, next, previous, refresh, transition)
  * @target     window target.
  * @transition name of transition if action parameter is 'transition'. 
  * @href       URL field. 
  * @param      URL parameters. */
ReportList.prototype.makeOnClickEvent = function(object, ctx, action, target, transition, href, param)
{
  return function(e)
  {
    return ReportList.prototype.onCellClick.call(object, ctx, action, target, transition, href, param, e)
  }
}

/** Render a node the of the list.
  *
  * @node       XML definition of the node.
  * @htmlParent HTML element in the document where to render the node. */
ReportList.prototype.renderNode = function(aData, htmlParent)
{
  for( var iRowIndex = 0 ; iRowIndex < aData.length ; iRowIndex++ )
  {
    var row = aData[iRowIndex]
    trNode = document.createElement("tr")
    trNode.className = row.t //type of the row (header, footer, row)
    trNode.className += ++this.rowNum % 2 == 0 ? " even" : " odd"
    htmlParent.appendChild(trNode)
    
    for( var iCellIndex = 0 ; iCellIndex < row.d.length ; iCellIndex++ )
    {
      var cell = row.d[iCellIndex]
      var value = null
      if ( typeof cell != "undefined" && typeof cell.s != "undefined" )
      { 
        var rawValue = cell.v        
        var style = this.styles[cell.s]
        
        htmlNode = style.td.cloneNode(true)
        if ( style.hideValue != true )
          value = FormatHelper.format[style.format].call(this, rawValue, style)
        
        if ( style.onclick == true )
        {
          var aContext = new Array()
          var aParam = new Array()
          var bNotClickable = false
          
          if( typeof cell.l != "undefined" )
            for( var i = 0 ; i < cell.l.length ; i++ )
            {
              if( cell.l[i].t == "set" )
                aContext.push({"xpath" : cell.l[i].xpath, "value" : cell.l[i].v})
              else if( cell.l[i].t == "param" )
                aParam.push({"name" : cell.l[i].name, "value" : cell.l[i].v})
              else if( cell.l[i].t == "notClickableIf" )
                bNotClickable = cell.l[i].v
            }

          if( bNotClickable == "false" || bNotClickable == false )
            htmlNode.onclick = this.makeOnClickEvent(this, aContext, style.onclickAction, 
              style.onclickTarget, style.onclickTransition, style.onclickOption, aParam)
          else
          {
            if( htmlNode.firstChild != null )
              // Remove "A" element 
              htmlNode.removeChild(htmlNode.firstChild)
          }
        }

        // showing extra visuals
        if ( style.bargraph != undefined )
        {
          var bargraph = style.bargraph.cloneNode(true)
          var bargraphValue = Math.max(parseFloat(rawValue) * 100, 0)        
          bargraph.firstChild.firstChild.style.width = bargraphValue.toString() + "%"
          htmlNode.appendChild(bargraph)
        }
        else if ( style.rating == true )
        {
          var fValue = parseFloat(rawValue)
          for (var i=1; i <= style.ratingMax; i++)
          {
            var div = document.createElement("div")
            div.className = "rating" + (fValue >= i ? "On" : "Off")
            htmlNode.appendChild(div)
          }
        }
        else if ( style.color == true && rawValue != "" && !isNaN(rawValue) )
        {
          var colorIndex = parseInt(rawValue)
          var div = document.createElement("div")
          div.className = "color"
          div.style.backgroundColor = this.COLORS[colorIndex % this.COLORS.length]
          htmlNode.appendChild(div)
        }
      }
      else
        // no style defined => empty cell
        htmlNode = document.createElement("td")

      if ( value != null )
      {
        if ( style.onclick == true )
        { // the value 
          var childNode = htmlNode.firstChild
          while ( childNode != null && childNode.nodeName != "A" )
            childNode = childNode.nextSibling
            
          if( childNode == null ) 
            // "A" element was removed previously, add text element to htmlNode
            htmlNode.appendChild(document.createTextNode(value))
          else
            childNode.appendChild(document.createTextNode(value))
        }
        else
          htmlNode.appendChild(document.createTextNode(value))
      }


      if ( typeof cell.cs != "undefined" )
        htmlNode.colSpan = cell.cs

      trNode.appendChild(htmlNode)
    }
  }
}

function FormatHelper()
{}

/** Formating values functions */
FormatHelper.format = {
 
  "number": function(text, style)
  {
    var value  = parseFloat(text)
    var options = {
      "pattern": style.pattern
    }
    
    return value.format(options)
  },

  "percentage": function(text, style)
  {
    var value  = parseFloat(text) * 100
    var options = {
      "pattern":  style.pattern,
      "symbol":   style.symbol
    }
    
    return value.format(options)
  },

  "currency": function(text, style)
  {
    var value  = parseFloat(text) 
    var options = {
      "pattern":  style.pattern,
      "symbol":   '$'
    }
    
    return value.format(options)
  },
  
  "date": function(text, style)
  {
    var d = Format.parseDateTimeInter(text)
    var fmt = Format.formatDate(d, false)
    if ( style.useTime )
      fmt += " " + Format.formatTime(d, !style.useSec)
      
    return fmt
  },

  "timespan": function(text, style)
  {
    var value = parseFloat(text)
    var fmt   = Format.formatTimeSpan(value)
    return fmt
  },
  
  "string": function(text)
  {
    return text
  }
}

/** Compile a style to create a template of the <td>.
  *
  * @style the XML definition of the style. The schema of 
  *        this element is the one defined for the flat report
  *        format:
  *
  *   <style format="percentage" alignement="center">
  *     <font bold="true"/>
  *   </style>
  *
  * @see https://wiki.neolane.net/wiki/Reporting_5.00#srcSchema_like_xml_structure. */

function compileStyle(style, htmlElement, defaultFontSize)
{
  var format = style.getAttribute("format")
  
  if ( format == null )
    format = "string"

  var compiledStyle = {
    "format":             format, 
    "negativeNumberRed":  style.getAttribute("negativeNumberRed") == "true" ? true : false
  }
  
  // In IE, td inherits text-align property from their parents
  htmlElement.style.textAlign = "left"

  if ( format == "number" || format == "currency" || format == "percentage" )
  { // 
    // numeric formats
    //   
    
    // create the formating pattern
    var pattern
    if ( style.getAttribute("use1000Sep") == "true" )
      pattern = "#,###"
    else
      pattern = "#"
      
    var decimalPlaces = style.getAttribute("decimalPlaces")
    decimalPlaces = decimalPlaces == null ? 2 : decimalPlaces
    if ( decimalPlaces > 0 )
    {
      pattern += "."
      for (var i=0; i < decimalPlaces; i++)
        pattern += "0"
    }
    
    if ( format == "percentage" )
    {
      pattern += "¤"
      if( Format.settings.percentSeparator )
        compiledStyle.symbol = Format.settings.percentSeparator + "%"
      else
        compiledStyle.symbol = "%"
    }
    
    compiledStyle.pattern = pattern
  
    // default alignment for numeric values is right 
    htmlElement.style.textAlign = "right"
  }
  else if ( format == "date" || format == "time" )
  {
    compiledStyle.useTime = style.getAttribute("useTime") == "true"
    compiledStyle.useSec  = style.getAttribute("useSec") == "true"
  }
    
  if ( style.getAttribute("alignment") != null 
    && style.getAttribute("alignment") != "auto" )
    htmlElement.style.textAlign = style.getAttribute("alignment")
    
  if( style.getAttribute("verticalAlignment") != null && style.getAttribute("verticalAlignment") != "default" )
    htmlElement.style.verticalAlign = style.getAttribute("verticalAlignment")
  
  var widthMode = style.getAttribute("widthMode")
  if ( widthMode == 'max' )
    htmlElement.style.width = style.getAttribute("width") + "%"
  else if ( widthMode == 'manual' )
    htmlElement.style.width = style.getAttribute("width") + "em"
  // automatic widthMode has no width, it uses content to size itself
  
  if( style.getAttribute("wrapText") != null && 
      style.getAttribute("wrapText") == "true" )
    htmlElement.style.whiteSpace = 'normal'
  else
    // By default, no wrap
    htmlElement.style.whiteSpace = 'nowrap'
  
  var childNode = style.firstChild
  while ( childNode != null )
  {
    if ( childNode.nodeType == XML.ELEMENT_NODE )
    {
      if ( childNode.nodeName == "font" )
      {
        var fontStyle = childNode.getAttribute("style")
        if ( fontStyle == "bold" || fontStyle == "boldItalic" )
          htmlElement.style.fontWeight = "bold"
        if ( fontStyle == "italic" || fontStyle == "boldItalic" )
          htmlElement.style.fontStyle = "italic"
        if( fontStyle == "normal" )
        {
          htmlElement.style.fontWeight = "normal"
          htmlElement.style.fontStyle = "normal"
        }
          
        var fontSize = childNode.getAttribute("size")
        if ( fontSize != undefined && fontSize != "0" && fontSize != "" )
        {
          if( isNaN(defaultFontSize) )
            htmlElement.style.fontSize = "1em"
          else
            htmlElement.style.fontSize = (fontSize / defaultFontSize) + "em"
        }
        var color = childNode.getAttribute("color")
        if ( color != undefined )
          htmlElement.style.color = color
      }
      else if ( childNode.nodeName == "extra" )
      {
        var visual = childNode.getAttribute("visual")
        if ( visual == "bargraph" )
        {
          var bargraph = document.createElement("div")
          bargraph.className = "bargraph"
          var bargraphContainer = document.createElement("div")
          bargraphContainer.className = "container"
          bargraph.appendChild(bargraphContainer)          
          var bargraphValue = document.createElement("div")
          bargraphValue.className = "blue"
          bargraphContainer.appendChild(bargraphValue)
          compiledStyle.bargraph = bargraph
        }
        else if ( visual == "rating" )
        {
          compiledStyle.rating = true
          compiledStyle.ratingMax = parseInt(childNode.getAttribute("ratingMax"))
        }
        else if ( visual == "color" )
        {
          compiledStyle.color = true
          compiledStyle.hideValue = true
        }
        
        if ( childNode.getAttribute("hideValue") == "true" )
          // do not display the value in cell
          compiledStyle.hideValue = true
      }
      else if ( childNode.nodeName == "onclick" 
        && childNode.getAttribute("action") != "none" )
      {
        var a = document.createElement("a")
        a.href = "#"
                
        // Need to get URL href
        compiledStyle.onclick = true        
        compiledStyle.onclickAction     = childNode.getAttribute("action")
        compiledStyle.onclickTarget     = childNode.getAttribute("target")
        compiledStyle.onclickTransition = childNode.getAttribute("transition")
        if( compiledStyle.onclickAction == "url" )
          compiledStyle.onclickOption = childNode.getAttribute("href")
        else if( compiledStyle.onclickAction == "javascript" )
          compiledStyle.onclickOption = getXPathValue(childNode, "javascript");
        else if( compiledStyle.onclickAction == "refreshData" )
        {

          compiledStyle.onclickOption = new Array()      
          var styleChildNode = childNode.firstChild
          while ( styleChildNode != null )
          {
            if( styleChildNode.nodeName == "objectTarget" )
              compiledStyle.onclickOption.push(styleChildNode.getAttribute("identifier"))              
            styleChildNode = styleChildNode.nextSibling
          }
        }

        
        htmlElement.appendChild(a)        
        
        if ( compiledStyle.onclickAction == "transition" )
          compiledStyle.onclickAction = "next"
      }
      else if ( childNode.nodeName == "border" )
      {
        var ndBorder = childNode.firstChild
        var strToEvalBorderProperties = ""
        while( ndBorder != null )
        {
          if( ndBorder.nodeType == XML.ELEMENT_NODE )
            // As setProperty is not supported under IE, htmlElement.style.setProperty(ndBorder.nodeName, ndBorder.getAttribute("value"),"") is not possible
            // Note that in this case, ndBorder.nodeName must be formated like "border-top" and not "borderTop"
            // Workaround: generate a string that contains affectations and evaluate it
            strToEvalBorderProperties += "htmlElement.style." + ndBorder.nodeName + " = \"" + ndBorder.attributes[0].nodeValue + "\";"
          ndBorder = ndBorder.nextSibling
        }
        eval(strToEvalBorderProperties)
      }
    }
    childNode = childNode.nextSibling
  }

  return compiledStyle
}