// popupMenu.js version 20110613
// Copyright © 2004-2011 Eric Friedman.  All rights reserved.
//
// This file contains functions to manage Popup Menus for Save Report
// Targets and Drill to other reports.
//
// Version history (in reverse chronological order, newest first):
// --------------------------------------------------------------
// 20110613: Altered function dr() to bypass menu display if only 1 menu choice present
// 20101201: Externalized and rewrote clearAllCells from stMenuSelect to separate
//           function so it can be called separately
// 20100929: Added correction for footer height in getMenuUpperLeft and fixed
//           character-based measurement bug
// 20100928: Updated toggleCell to adhere to prototype.js framework
// 20100825: Modified stMenuSelect to use createParamList function, and call dupe check
//           and saveTarget functions as appropriate
// 20100823: Added createParamList function to create parameter list prior to asp call
// 20100818: Externalize toggleCell functionality so it can be referenced when redisplaying 
//           targeted reports
// 20100721: Removed document.body.scrollLeft and document.body.scrollTop from scroll calc
//           since it was only populated by Chrome and caused an incorrect calculation
// 20100624: Made modifications to handle container-based scrolling in getMenuUpperLeft; 
//           changed stMenuSelect to no longer use nested arrays for information 
//           management and instead use a hash and nested array.  2 reasons for this - 
//           1) prototype extends array and adds many functions that were being
//           exposed via original array looping in method; 2) arrays are best for
//           numeric indexing whereas hash tables are best for storing data based on
//           keys as was the case here.
// 20090812: Added call to disableInput(false) on entry to st and dr.
// 20090806: Added zIndex=1000 to make sure popup Menu appears in front of
//           other objects. Added "px" to left, top and width for Gecko browsers.
// 20090420: Increased height of savetargets window to 400px to accomodate
//           addition of checkbox for target advances.
// 20090124: Fixed missing ';' on two statements. Fixed indenting.
// 20081125: Removed function getAnchorPosition() and replaced function 
//           AnchorPosition_getPageOffset() with getMenuUpperLeft().  Moved
//           popup menu logic to new function stringPixels(), which relies
//           on hidden id 'ruler' on HTML page.
// 20081122: Removed function getEventTarget() to rpFunctions.js. Fixed
//           missing ';' on var menuRows statements in st() and dr().
//           Removed obsolete anchor argument from function.
// 20080903: Extensive rewrite to provide cross browser support.
//           Successfully tested with IE6-7, Mozilla Firefox 3, 
//           Safari 3.1.2, Opera 10, and Google Chrome beta.
// 20080105: Widened Savetarget window to 700 pixels
// 20041115: Calculates menu width ignoring <table>..</table> tags
// 20041108: Calculates menu height using # of menu rows defined and
//           width using an average of 7 pixels per character. Locates
//           popup menu at cursor location instead of adjacent to cell.
// 20041102: Supports both Save Targets and Drill Menus
// 20041024: Concatenates parameter list for all selected cells
// ------------------------------------------------------------------------------

// Module-level variables
var cellList = $H();                                        // Create a hash to hold list of selected cells
var cell, plist;                                            // Remember cellId, params
var display_status = 0;                                     // 1 = show menu text in status bar; 0 = don't
var menuWidth;                                              // Pixel width of popup menu
var menuHeight;                                             // Total height of menu
var menuRowHeight = 20;                                     // Pixel height of each menu row
var avgCharPixels = 6;                                      // Average pixels per character
var paddingPixels = 30;                                     // Left + right padding on menu text
var CURCELL = 0;                                            // Indicates current cell param list
var ALLCELL = 1;                                            // Indicates param list for all cells

//--------------------------------------------------------------------------------------

function st(cellid,params) {

   // Purpose: Function st() handles Saved Target menus.  These menus have
   // 4 static choices related to saving cells as targets.  It is called
   // from an <a href=...> anchor in each table cell requiring the Save
   // Targets popup menu.

   // Arguments: cellid - id of the cell clicked to invoke st
   //            params - parameters passed to the savetarget.asp procedure
   // ---------------------------------------------------------------------

   disableInput(false);                                     // Enable inputs in web page
   cell = cellid;                                           // Remember cell's Id argument
   plist = params;                                          // Remember cell's parameter list

   var str;                                                 // Holds each menu row text
   var stmenu = document.getElementById("stmenu");          // Locate the st menu
   var menuRows = stmenu.getElementsByTagName("div");       // Get its <div> collection
   menuHeight = menuRows.length * menuRowHeight;            // Calculate total menu height
   menuWidth = 0;                                           // Initialize menuWidth

   for (i=0; i<menuRows.length; i++) {                      // Calculate max pixel width of any menu row
      str = menuRows[i].innerHTML;                          // Get inner html from menu row
      str = str.substr(str.lastIndexOf(">")+1);             // Get portion following last ">"
      menuWidth = Math.max(menuWidth,stringPixels(str));    // Calculate pixels for this row
   }
   menuWidth += paddingPixels;                              // Include left + right padding

   var menuLocation = new Object();
   menuLocation = getMenuUpperLeft();                       // Locate popup menu

   // Reset popup menu's properties to reposition it and make it visible
   with(stmenu.style) {
      left = menuLocation.x;
      top = menuLocation.y;
      width = menuWidth+"px";
      visibility = "visible";
      zIndex = "1000";
   }
}

//--------------------------------------------------------------------------------------

function dr(cellid,params) {

   // Purpose: Function dr() handles 'DR'ill to menus.  Drill to menus are created
   // dynamically by the generating asp code and are different for each page.
   // It is called from <a href=...> anchor in each table cell requiring the Drill
   // To popup menu.  If only 1 item appears in the menu, the menu is not displayed
   // and the user is instead taken directly to the only choice.

   // Arguments: cellid - id of the cell clicked to invoke this function
   //            params - parameters passed to the target procedure
   // ----------------------------------------------------------------------------

   disableInput(false);                                     // Enable inputs in web page
   cell = cellid;                                           // Remember cell's Id
   plist = params;                                          // Remember cell's parameter list

   var drmenu = document.getElementById("drmenu");
   var menuRows = drmenu.getElementsByTagName("div");       // Get menu div rows collection

   if (menuRows.length==1) {                                // If only 1 menu choice
      handleFormSubmit(null,menuRows[0].id + plist,false,true); // Call it directly without displaying menu

   } else {

      var str;                                              // Holds each menu row text
      menuHeight = menuRows.length * menuRowHeight;         // Calculate total menu height
      menuWidth = 0;                                        // Initialize menuWidth
   
      for (i=0; i<menuRows.length; i++) {                   // Calculate max pixel width of any menu row
         str = menuRows[i].innerHTML;                       // Get inner html from menu row
         str = str.substr(str.lastIndexOf(">")+1);          // Get portion following last ">"
         menuWidth = Math.max(menuWidth,stringPixels(str)); // Calculate pixels for this row
      }
      menuWidth += paddingPixels;                           // Include left + right padding
   
      var menuLocation = new Object();
      menuLocation = getMenuUpperLeft();                    // Locate popup menu
   
      // Reset popup menu's properties to reposition it and make it visible
      with(drmenu.style) {
         left = menuLocation.x;
         top = menuLocation.y;
         width = menuWidth+"px";
         visibility = "visible";
         zIndex = "1000";
      }
   }
}

//--------------------------------------------------------------------------------------

function getRenderedStyle(oElm, strCssRule) {

   // Purpose: A cross-browser function to return a CSS element's
   //          currently rendered style.

   // Arguments: oElm - CSS Element
   //            strCssRule - rule whose setting we want to retrieve

   // Returns: the element's currently rendered stule as a string.
   // --------------------------------------------------------------

   var strValue = "";
   if (document.defaultView && document.defaultView.getComputedStyle) {   // W3C style
      strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
   }
   else if (oElm.currentStyle) {                            // Convert strCssRule name to IE currentStyle format
      strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1) {
         return p1.toUpperCase() });
      strValue = oElm.currentStyle[strCssRule];             // Get IE currentstyle
   }
   return strValue;
}
//--------------------------------------------------------------------------------------

function highlightCell(myEvent) {

// Purpose:  onMouseover event handler to highlight a popup menu's row color
// -------------------------------------------------------------------------

   var target = getEventTarget(myEvent);
   if (target.className=="menuitems") {
      target.style.backgroundColor="blue";
      target.style.color="white";
      if (display_status==1) {
         window.status=target.innerHTML;
      }
   }
}

//--------------------------------------------------------------------------------------

function lowlightCell(myEvent) {

   // Purpose:  onMouseout event handler to restore the popup menu's row color
   // ------------------------------------------------------------------------

   var target = getEventTarget(myEvent);
   if (target.className=="menuitems") {
      target.style.backgroundColor="";
      target.style.color="black";
      window.status="";
   }
}
//--------------------------------------------------------------------------------------

function getMenuUpperLeft() {

   // Purpose: Function returns the calculated x-axis offset and y-axis offset
   //          for a popup menu's upper left corner so that the menu appears to
   //          emanate from the cursor location of the last event.  The direction
   //          of the menu from the cursor is based on how much room is available
   //          between the edges of the popup menu and the sides of the browser
   //          window.

   // Returns: An object having .x and .y properties containing the x-axis offset
   //          and y-axis offset calculated for a popup menu's upper left corner.
   // ----------------------------------------------------------------------------

   // Get scrolling position of window
   var scrollX = $('page').cumulativeScrollOffset().left;
   var scrollY = $('page').cumulativeScrollOffset().top;

   var viewport = document.viewport.getDimensions();
   var rightMargin = viewport.width;
   var bottomMargin = viewport.height-$('footer_container').getHeight();

   // Determine which sides of cursor have room to display popup menu
   var roomRight = ((cursor.x+menuWidth) < rightMargin);
   var roomBelow = ((cursor.y+menuHeight) < bottomMargin);

   // Add window scrolling to cursor position
   cursor.x += scrollX;
   cursor.y += scrollY;

   // Locate the popup menu wherever room is available within the browser window
   var coordinates = new Object();
   coordinates.x = (roomRight) ? cursor.x : cursor.x-menuWidth;    // Prefer right of cursor
   coordinates.y = (roomBelow) ? cursor.y : cursor.y-menuHeight;   // Prefer below cursor

   // Append "px" to position for Gecko browsers
   coordinates.x = (isNaN(coordinates.x)) ? "0px" : coordinates.x+"px";
   coordinates.y = (isNaN(coordinates.y)) ? "0px" : coordinates.y+"px";

   return coordinates;
}

//--------------------------------------------------------------------------------------

function stMenuSelect(myEvent) {

   // Purpose: Event handler when user clicks on a Save Target menu row.
   //          Function determines which of 4 static menu choices was clicked,
   //          and implements that action (selected / deselecting the current
   //          table cell, deselecting all table cells, calling savetarget.asp
   //          with the current table cell, or with all selected table cells).
   // ------------------------------------------------------------------------

   var value, win, iSelected;
   var params = "";
   var iCount = 0;
   var opts = "toolbar=no,status=no,location=no,menubar=no,resizable=yes,height=400,width=700,scrollbars=yes";

   var target = getEventTarget(myEvent);
   if (target.className=="menuitems") {
      value = target.id;
      switch (value) {
         case "togglecell":
            toggleCell(cell,plist);
            params = createParamList(ALLCELL);
            if (params != "") { checkForDupeTargets(params); }
            break;

         case "savecell": 
//            win = window.open("", "Save_Target", opts);
//            win.focus();
//            win.location = "savetarget.asp?" + plist;
            params = createParamList(CURCELL);
            if (params != "") { checkForDupeTargets(params); }
            setTimeout("saveTargets('"+params+"')",1000);
            break;

         case "clearall": 
            clearAllCells();
            break;

         case "saveall":                                                 // Concatenate parameters from each selected cell
            params = createParamList(ALLCELL);
            if (params == "") {
               alert("No cells have been marked.");
            } else if (params.length > 2025) {                             // Does param string exceed IE url length limit?
               alert("Too many cells have been marked.");
            } else {
               checkForDupeTargets(params);
               setTimeout("saveTargets('"+params+"')",1000);
//               win = window.open("", "Save_Target", opts);
//               win.focus();
//               win.location = "savetarget.asp?" + params.substr(1);
            }
            break;
      }
   }
}

//--------------------------------------------------------------------------------------

function toggleCell(myCell, myParams) {

   // Purpose: Save cell info and toggle background color of cell provided
   // ------------------------------------------------------------------------

   cell = myCell;                                          // Use variable defined in popupMenu.js

   if (!cellList.get(cell)) {                              // If cell is not on currently selected list
      var ary = $A([$(cell).getStyle('backgroundColor'), myParams]);  // Create new cellList row with 2 columns and populate with bg color and param list
      cellList.set(cell,ary);                              // Save cell's original background color, and params
      $(cell).setStyle({backgroundColor:"#E9967A"});           // Set cell's background to "selected" color
   } else {
      $(cell).setStyle({backgroundColor:(cellList.get(cell))[0]});                 //Reset cell's background to its original color
      cellList.unset(cell);                                // Remove cell from selected cell list
   }
}

//--------------------------------------------------------------------------------------

function clearAllCells() {

   // Purpose: Clear all cell info and toggle background colors back to default (nothing selected)
   // ------------------------------------------------------------------------

   cellList.keys().each(function(key) {
      if (!!key) {
         if ($(key) != undefined && $(key) != null) {
            $(key).style.backgroundColor = (cellList.get(key))[0];  //Reset selected cell's background to its original color
         }
         cellList.unset(key);                              // Remove cell from selected cell list
      }
   });
}

//--------------------------------------------------------------------------------------

function createParamList(iWhichCells) {

   var myPlist = "";
   var res = "paramList=";
   var tmpAry;
   var hhCount = 0;
   
   if (iWhichCells == CURCELL) {                           //create param list from single cell params
      myPlist = plist.toUpperCase();
   } else if (iWhichCells == ALLCELL) {                    //create param list from all cells highlighted
      if (cellList.keys().size() == 0) { return ""; }     //no cells selected
   
      cellList.keys().each(function(key) {
         if (!!key) {
            myPlist = myPlist + "&" + ((cellList.get(key))[1]).toUpperCase();
         }
      });
   } else {
      return "";
   }
   
   myPlist = myPlist.parseQuery();                         //parse redundancies into arrays
   tmpAry = Object.keys(myPlist);                          //make iterable array out of object
   
   //tally hh count first
   if (tmpAry.indexOf("HH") > -1)      { 
     if (Object.isArray(myPlist["HH"])) { myPlist["HH"].each(function(v) { hhCount += (v*1); }); }
     else { hhCount = myPlist["HH"] }
   }
   
   //start with concatenating known components; TODO: how to handle where value contains a ','
   if (tmpAry.indexOf("DOWN1") > -1)   {
      if (Object.isArray(myPlist["DOWN1"])) { res += "|DOWN1=" + myPlist["DOWN1"].join(","); }
      else { res += "|DOWN1=" + myPlist["DOWN1"]; }
   }
   if (tmpAry.indexOf("DOWN2") > -1)   { 
      if (Object.isArray(myPlist["DOWN2"])) { res += "|DOWN2=" + myPlist["DOWN2"].join(","); }
      else { res += "|DOWN2=" + myPlist["DOWN2"]; }
   }
   if (tmpAry.indexOf("ACROSS1") > -1) { 
      if (Object.isArray(myPlist["ACROSS1"])) { res += "|ACROSS1=" + myPlist["ACROSS1"].join(","); }
      else { res += "|ACROSS1=" + myPlist["ACROSS1"]; }
   }
   if (tmpAry.indexOf("ACROSS2") > -1) { 
      if (Object.isArray(myPlist["ACROSS2"])) { res += "|ACROSS2=" + myPlist["ACROSS2"].join(","); }
      else { res += "|ACROSS2=" + myPlist["ACROSS2"]; }
   }
   
   //remove known components, leaving remaining "unknown" components to iterate
   tmpAry = tmpAry.without("DOWN1","DOWN2","ACROSS1","ACROSS2","CELL","HH","RQSTID","DELETE","DWNLD","RERUN","ADVANCE","RENAME").sort();
   
   tmpAry.each(function(v) {
      if (Object.isArray(myPlist[v])) { res += "|" + v.toUpperCase() + "=" + myPlist[v].join(","); }
      else { res += "|" + v.toUpperCase() + "=" + myPlist[v]; }
   });
   
   return res + "&hhcount=" + hhCount;
}

//--------------------------------------------------------------------------------------

function drMenuSelect(myEvent) {

   // Purpose: Event handler when user clicks on a Drill To menu row. Since
   //          Drill To menus are dynamic, it redirects the page to the
   //          target's id, which must contain the destination URL.
   //          It passes any parameters set up by report.asp.
   // ---------------------------------------------------------------------

   var target = getEventTarget(myEvent);
   if (target.className=="menuitems") {
      //DMS:20100624: modified to submit new URL within container
      //window.location.href=(target.id + plist);             // Old code, left container
      handleFormSubmit(myEvent,target.id + plist,false,true); // Remains within container
   }
}
//--------------------------------------------------------------------------------------

function stringPixels(str,newFontName,newFontStyle,newFontWeight,newFontSize) {

   // Purpose: Calculate the width in pixels of a string displayed using a
   //          particular font.

   // Arguments: str - the character string to display.
   //            The following arguments are optional.  If present, they
   //            override the default value of the object with id='ruler'.
   //               newFontName - the font in which to display the string
   //               newFontStyle - normal, italic, oblique
   //               newFontWeight - normal, bold, bolder, lighter, 100, 200, ...
   //               newFontSize - the size of the font: 12px, etc.

   // Usage:   stringPixels("Some text"'[,"Arial"[,"Italic"[,"Bold"[,"12px"]]]]);
   //          Put HTML: <span id='ruler' style='visibility:hidden'></span>
   //          in page

   // Returns: Integer # of pixels required
   // ---------------------------------------------------------------------------

   if (document.getElementById) {                           // Make sure browser supports this
      var rulerSpan = document.getElementById('ruler');
      if (!!rulerSpan) {                                    // Did we find id='ruler'?
         with(rulerSpan) {
            with(style) {                                   // Override its style attributes
               if (!!newFontName)
                  fontFamily = newFontName;
               if (!!newFontStyle)
                  fontStyle = newFontStyle;
               if (!!newFontWeight)
                  fontWeight = newFontWeight;
               if (!!newFontSize) {
                  if (Right(newFontSize,2) == "px") {       // Ensure we have "px" on size
                     fontSize = newFontSize;                // Yes, just use arg value
                  }
                  else {
                     fontSize = newFontSize + "px";         // Append "px"
                  }
               }
            }
            innerHTML = str;                                // Insert our character string
            return offsetWidth;                             // Return the resulting pixel width
         }
      }
   }
   return avgCharPixels * str.length;                       // Otherwise use average char Pixels
}

