    // creates the array containing the animations
    _animationEvents = [];

    // the number of milliseconds the visual effects last
    _flashTime = 1500;

    // boolean to say if we want animations or not
    _animate = true;

    // array to keep and count the exceptions in animationTick (max 100)
    _animationErrors = [];
    
    // Object used to save the arrays of color fadings calculated
    _colorArrays = {};   // use an objet as a Map

    // Array of original colors of cells
    _originalColors = [];
    
    _rate = 100;
    
    // the two colors used for flashing
    GREEN_COLOR  = "#00FF00";
    RED_COLOR    = "#FF0000";
    
    // src for the up and down images
    UP_IMG_SRC   = '/resources/images/dojo/arrow_up_black.gif';
    DOWN_IMG_SRC = '/resources/images/dojo/arrow_down_black.gif';
    
    // Flashing columns
    FLASH_COLIDS = ["BidPrice", "AskPrice", "LastPrice"];
  
  /*******************************************************
   * Update a particular cell with the update event      *
   *******************************************************/
  updateCell = function(updateEvent, portalSegment) {
    if ("ShortName" == updateEvent.columnid)
    	return; // why would we update the shortname?


    var trElement = dojo.byId("PE_" + portalSegment + "_" + updateEvent.rowid); // The table row
    
    if (!trElement) {
//    	if ("LastPrice" == updateEvent.columnid) {
//    	    _indexValue = updateEvent.newValue;
//    	}
    	return;
    }
    
    var tdElement = _findCell(trElement, "PE_"+updateEvent.columnid);   // the table cell to update/flash

    // change the actual html text data with the updated one
    if (tdElement == null) {
      _log("Unable to find cell row:"+updateEvent.rowid+" column:"+updateEvent.columnid);
      return;
    }
 // If there is NOBR element inside the cell update the text inside the tag 
    var nobrElement = tdElement.getElementsByTagName("nobr");
    var updateElement = tdElement;
    if( nobrElement != null || nobrElement.length >0){
    	updateElement = nobrElement[0];
    }
    
    if ("LastDelta" == updateEvent.columnid || "ClosingDelta" == updateEvent.columnid ) {
    	changeDeltaColor(tdElement, updateEvent.newValue);
    }
    
    if (updateElement.lastChild.nodeName == "#text") {  // there was already text in it
    	updateElement.lastChild.data = updateEvent.newValue;
    } else {  // there was no text (like daily volume and market was closed before)
    	updateElement.innerHTML += updateEvent.newValue;
    }
    
    if (_animate && updateEvent.doFlash) {

      // the image tag (used to change the arrow)
      var imgInTd = tdElement.getElementsByTagName('img')[0];     // the first image in the cell

      var flashColor = null;
      if (updateEvent.delta > 0) {
        flashColor = GREEN_COLOR;          // we will flash it green
        imgInTd.src = UP_IMG_SRC;      // it is going up
      } else if (updateEvent.delta < 0) {
        flashColor = RED_COLOR;            // we will flash it red
        imgInTd.src = DOWN_IMG_SRC;    // it is gowing down
      }

      if (flashColor != null) {    // only flash when whe have a delta != 0
        // The original color (created the first time we flash someone on that row)
        var originalColor = _getOrCreateOriginalColor(tdElement, updateEvent.rowid);

        _myFlash(updateEvent.rowid+updateEvent.columnid, tdElement, imgInTd, flashColor, originalColor);
      }
    }
  };

  /*******************************************************/
  changeDeltaColor = function(cell, niceValue) {
    var delta = niceValue.replace("%", "");
    var color = "black";
    if (delta>0) {
      color = "green";
    } else if (delta<0) {
      color = "red";
    }
    
    dojo.style(cell, "color", color);
  }
  
  
  /*******************************************************
   * Find the original color of a cell and save it       *
   *******************************************************/
  _getOrCreateOriginalColor = function(node, uniqueId) {  
    if (_originalColors[uniqueId] != null) {
      return _originalColors[uniqueId];
    } else {
      var originalColor = dojo.style(node, "backgroundColor");
      // Treat fully transparent as "transparent" rgba(0, 0, 0, 0) in safari!
      if ( /^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/i.test(originalColor)) { 
        originalColor = "transparent"; 
      }
      while (originalColor == "transparent" && node.nodeType != 9) {
        node = node.parentNode;
        originalColor = dojo.style(node, "backgroundColor");
        if ( /^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/i.test(originalColor)) {
          originalColor = "transparent"; 
        }
      }
      _originalColors[uniqueId] = originalColor;
      return originalColor;
    }
  };

  /*******************************************************
   * Find a cell in a table row with the id 'id'         *
   *******************************************************/
  _findCell = function(tr, id) {
    for (var i=0; i<tr.cells.length; i++) {
      if (tr.cells[i].id == id) {
        return tr.cells[i];
      }
    }
  };

  /*******************************************************
   * This function add the flash to the _animationEvents *
   *******************************************************/
  _myFlash = function(uniqueCellName, node, imgNode, startColor, endColor) {
    var flashEvent = {};
    flashEvent.id = uniqueCellName;
    flashEvent.node = node;
    flashEvent.arrayLength = Math.round(_flashTime / _rate) + 1;
    flashEvent.colorArray = _getOrCreateColorArray(startColor, endColor, flashEvent.arrayLength);
    flashEvent.startTime = null;

    // image part
    flashEvent.imgNode = imgNode;
  
    _animationEvents.push(flashEvent);
  };


  /*******************************************************
   * This function gets or creates the array of colors   *
   * containing the fading colors used for the flashing  *
   *******************************************************/
  _getOrCreateColorArray = function(startColor, endColor, length) {
    var colorArray = _colorArrays[startColor + endColor + length];
    if (colorArray == null) {
      colorArray = _createColorArray(startColor, endColor, length);
      _colorArrays[startColor + endColor + length] = colorArray;
    }
    return colorArray;
  };

  /*******************************************************
   * This function creates the array of colors           *
   * containing the fading colors used for the flashing  *
   * array of size length from startColor to endColor    *
   *******************************************************/
  _createColorArray = function(startColor, endColor, length) {
    var startRGB = new dojo.Color(startColor).toRgb();
    var endRGB   = new dojo.Color(endColor).toRgb();

    if (length == 1) {  // oh well... :(  Who knows!
      return ["rgb(" + endRGB.join(",") + ")"];
    }

    var redStep = (endRGB[0] - startRGB[0]) / (length - 1);
    var greenStep = (endRGB[1] - startRGB[1]) / (length - 1);
    var blueStep = (endRGB[2] - startRGB[2]) / (length - 1);


    var colorArray = new Array(length);
    colorArray[0] = "rgb(" + startRGB.join(",") + ")";

    for (var i=1; i<length; i++) {
      colorArray[i] = "rgb(" + Math.round(startRGB[0] + i*redStep) + "," +
                               Math.round(startRGB[1] + i*greenStep) + "," +
                               Math.round(startRGB[2] + i*blueStep) + ")";
    }
    colorArray[length-1] = "rgb(" + endRGB.join(",") + ")";

//  for (var i=0; i<colorArray.length; i++) {
//    alert(colorArray[i]);
//  }
  
    return colorArray;
  };

/*************************************************************************************************
 * Animates the cells found in the animation queue                                               *
 *************************************************************************************************/
  animateCells = function(actualTime) {
    // this whole function is in a try catch... If a cell did not flash correctly, it is 'ok'
    try {
      var alreadyTreated = [];

      // i starts backwards because we will remove duplicates with splice
      for (var i=_animationEvents.length-1; i>=0; i--) {
        var flashMe = _animationEvents[i];

        if (alreadyTreated[flashMe.id]) {     // this node is already used, we remove it
          _animationEvents.splice(i, 1);
          continue;  // try next node
        }
        alreadyTreated[flashMe.id] = true;    // we are flashing this one, we consider it used

        if (flashMe.startTime == null) {
          flashMe.startTime = actualTime;
        }

        var index = Math.round((actualTime - flashMe.startTime) / _rate);
        if (index > (flashMe.arrayLength - 1)) {
          index = flashMe.arrayLength - 1;
        }

        dojo.style(flashMe.node, "backgroundColor", flashMe.colorArray[index]);

        // <img part>
        dojo.style(flashMe.imgNode, "opacity", 1 - index / (flashMe.arrayLength - 1));
        // </img part>

        if (index >= (flashMe.arrayLength - 1)) {
          // we are done with this node, we just set the last color. 
          // We will not update it again, let's remove it
          _animationEvents.splice(i, 1);  
        }
      }
    } catch(err) {
      if (_animationErrors.length > 100) {
        _animationsErrors = [];   // reset it
      }
      _animationErrors.push(err);
    }
  };

/*************************************************************************************************
 * 2 Simple log (alert or silent or anything...)                                                 *
 *************************************************************************************************/
  _log = function(stringToLog) {
    //alert(stringToLog);
  };
  
  _error = function(stringToLog) {
    alert(stringToLog);
  };
  
/*************************************************************************************************
 * Public method to get or set the animation boolean                                            *
 *************************************************************************************************/
  getAnimation = function() {
    return _animate;
  };
  setAnimation = function(bool) {
    _animate = bool;
  };
  
/*************************************************************************************************
 * Public method to change the flashTime value                                                   *
 *************************************************************************************************/
  setFlashTime = function(newTime) {
    // Save some memory... colorArrays are now changed since flashTime is changing
    _colorArrays = {};
    
    _flashTime = newTime;
  }

// Converts a string, delimited by apostrophes, into a float.
function toNumber(n) {
    var nObj = new String(n);
    var tokens = nObj.split("'");
    var nString = "";
    for (i = 0; i < tokens.length; i++) {
    	nString += tokens[i];
    }
    var nFloat = parseFloat(nString);
    return nFloat;
}

// Takes an integer and inserts thousands-delimiters (12345 -> 12'345)
function insertKiloDelimiter(myString) {

    // Split the string into digits then run them backwards, putting in an apostrophe
    // every three digits.
    var digits = new String(myString).split("");
    var j = 1;
    var reversed = "";
    for (var i = digits.length - 1; i > -1; i--) {
    	reversed +=  digits[i];
    	if (j % 3 == 0 && i != 0)
    	    reversed += "'";
    	j++;
    }
    
    // Split the reversed string and run it backwards again to get the right order.
    digits = new String(reversed).split("");
    var forward = "";
    for (var i = digits.length - 1; i > -1; i--) {
    	forward += digits[i];
    }
    return forward;
}    
