/**
 * $Header: /home/mpdavig/proj/repository/html/home/work/company/ccib/library/lib/drag_n_drop.js,v 1.2 2008/12/02 05:02:56 mpdavig Exp $
 *
 * COPYRIGHT:
 *
 * This software is Copyright (c) 2006-2008 Marc Davignon
 *                         <mpdavig@users.sourceforge.net>
 *
 * LICENSE:
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of Version 2 of the GNU General Public License as published by the
 * Free Software Foundation.
 *
 * Drag and drop based on code from "The JavaScript Anthology pg. 281"
 */


/**
 * First, we need to attach event listeners to the draggable objects so that
 * when a user clicks and holds the mouse button on one of them, that object
 * enters a dragging state. We’ll use the addLoadListener function from Chapter
 * 1 to install all the listeners when the page loads:
 */
addLoadListener(initDragNDrop);
addLoadListener(initDragOverOut);

/**
 * A browser detection technique from Chapter 11 is used to prevent Internet
 * Explorer 5 for Mac from executing the script. We take this precaution
 * because the values the script returns for object positions and mouse cursor
 * events in this browser are a little buggy; it’s safest to serve degraded
 * functionality to IE 5 for Mac. (more info pg. 282)
 */
function initDragNDrop()
{
  if (identifyBrowser().indexOf("ie") >= 0 && identifyOS() == "mac")
  {
    return false;
  }

  // Test to be sure the required elements exist
  if (document.getElementById("dropZone")) {
    initDragNDropList( document.getElementById("dropZone").getElementsByTagName("li") );
    //initDragNDropList( document.getElementById("dropZone").getElementsByTagName("div") );
  }
  if (document.getElementById("movable")) {
    initDragNDropList( document.getElementById("movable").getElementsByTagName("li") );
  }
} // function initDragNDrop()


function initDragNDropList(LIs) {
  for (var i = 0; i < LIs.length; i++) {
    attachEventListener(LIs[i], "mousedown", mousedownDragNDrop, false);
    LIs[i].style.cursor = "move";
  }
} // function initDragNDropList(LIs)


function initDragOverOut()
{
  // Test to be sure the required elements exist
  if (document.getElementById("dropZones")) {
    var FORMs = document.getElementById("dropZones").getElementsByTagName("form");

    for (var i = 0; i < FORMs.length; i++) {
      attachEventListener(FORMs[i], "mouseover", mouseoverDragNDrop, false);
      attachEventListener(FORMs[i], "mouseout", mouseoutDragNDrop, false);
    }
  }
}


function mouseoverDragNDrop(event)
{
  if (typeof event == "undefined")
  {
    event = window.event;
  }

  var target = getEventTarget(event);

  while (target.nodeName.toLowerCase() != "form")
  {
    target = target.parentNode;
  }

  // Change on hover
  target.style.backgroundColor = '#EFEEC9';

}


function mouseoutDragNDrop(event)
{
  if (typeof event == "undefined")
  {
    event = window.event;
  }

  var target = getEventTarget(event);

  while (target.nodeName.toLowerCase() != "form")
  {
    target = target.parentNode;
  }

  // Change on out hover
  target.style.backgroundColor = '';
}


/**
 * mouseDownDragNDrop makes an object ready to be dragged. It calculates
 * position coordinates, and attaches event handlers that react to mouse
 * movements: (more info pg. 283)
 */
function mousedownDragNDrop(event)
{
  if (typeof event == "undefined")
  {
    event = window.event;
  }

  if (typeof event.pageX == "undefined")
  {
    event.pageX = event.clientX + getScrollingPosition()[0];
    event.pageY = event.clientY + getScrollingPosition()[1];
  }

  var target = getEventTarget(event);

  while (target.nodeName.toLowerCase() != "li")
  //while (target.nodeName.toLowerCase() != "div")
  {
    target = target.parentNode;
  }

  /* Keep the movable image above everything else */
  target.style.zIndex = "101";

  /* track the currently dragged element */
  document.currentTarget = target;

  var currentLeft = parseInt(target.style.left);
  var currentTop = parseInt(target.style.top);
  //var currentLeft = parseInt(target.style.marginLeft);
  //var currentTop = parseInt(target.style.marginTop);

  if (isNaN(currentLeft))
  {
    currentLeft = "0";
  }

  if (isNaN(currentTop))
  {
    currentTop = "0";
  }

  /**
   * originLeft and originTop are added as properties of the draggable object
   * the first time it is clicked upon; they store the value of the object’s
   * original position so that it can be returned to the origin if an invalid
   * drop is made.
   */
  if (typeof target.originLeft == "undefined")
  {
    target.originLeft = currentLeft;
    target.originTop = currentTop;
  }

  /**
   * clickOriginX and clickOriginY store the coordinates of the mousedown event
   * itself. These values are used later to determine how far from the
   * mousedown point the user has moved the cursor. differenceX and differenceY
   * store the difference between the position of the top-left corner of the
   * object and the location of the mousedown event. (more info pg. 285)
   */
  target.clickOriginX = event.pageX;
  target.clickOriginY = event.pageY;
  target.differenceX = currentLeft - event.pageX;
  target.differenceY = currentTop - event.pageY;

  // Added: mdavignon 10/28/2008 a single click should simulate a drop
  attachEventListener(document, "click", mouseupDragNDrop, false);

  attachEventListener(document, "mousemove", mousemoveCheckThreshold, false);
  attachEventListener(document, "mouseup", mouseupCancelThreshold, false);

  stopDefaultAction(event);

  return false;
} // function mousedownDragNDrop(event)


/**
 * mousemoveCheckThreshold detects whether the cursor has moved those three
 * pixels: (more info pg. 285 and 286)
 */
function mousemoveCheckThreshold(event)
{
  if (typeof event == "undefined")
  {
    event = window.event;
  }

  if (typeof event.pageX == "undefined")
  {
    event.pageX = event.clientX + getScrollingPosition()[0];
    event.pageY = event.clientY + getScrollingPosition()[1];
  }

  var target = document.currentTarget;

  if (Math.abs(target.clickOriginX - event.pageX) > 3 || Math.abs(target.clickOriginY - event.pageY) > 3)
  {
    detachEventListener(document, "mousemove", mousemoveCheckThreshold, false);
    detachEventListener(document, "mouseup", mouseupCancelThreshold, false);

    /* Event listeners which do the real work */
    attachEventListener(document, "mousemove", mousemoveDragNDrop, false);
    attachEventListener(document, "mouseup", mouseupDragNDrop, false);
    attachEventListener(document, "click", clickDragNDrop, false);
  }

  stopDefaultAction(event);

  return true;
} // function mousemoveCheckThreshold(event)


/**
 * The second interim listener, mouseupCancelThreshold is triggered when the
 * user releases the mouse button without moving the cursor more than three
 * pixels in any direction. This simply removes our interim listeners,
 * cancelling the drag operation that would otherwise have been initiated:
 */
function mouseupCancelThreshold()
{
  detachEventListener(document, "mousemove", mousemoveCheckThreshold, false);
  detachEventListener(document, "mouseup", mouseupCancelThreshold, false);

  return true;
} // function mouseupCancelThreshold()


/**
 * In mousemoveDragNDrop, we use the variables initialized in mousedownDragNDrop
 * to display the draggable object in the right place:
 */
function mousemoveDragNDrop(event)
{
  if (typeof event == "undefined")
  {
    event = window.event;
  }

  if (typeof event.pageX == "undefined")
  {
    event.pageX = event.clientX + getScrollingPosition()[0];
    event.pageY = event.clientY + getScrollingPosition()[1];
  }

  var target = document.currentTarget;

  /**
   * The vertical and horizontal positions of the cursor are added to the stored
   * differenceX and differenceY values for the dragged object, and are then
   * assigned to its top and left style properties. This has the effect of
   * moving the object around with the cursor.
   */
  target.style.left = event.pageX + target.differenceX + "px";
  target.style.top = event.pageY + target.differenceY + "px";

  /* Added code to change the drop zone background color */
  var FORMs= document.getElementById("dropZones").getElementsByTagName("form");

  for (var i = 0; i < FORMs.length; i++) {
    var hotZone = FORMs[i];
    var hotZonePosition = getPosition(hotZone);

    // See if we are in the hot zone, else is we are. Should multiply this to
    // create more hot zones.
    if (!((event.pageX > hotZonePosition[0]) && (event.pageX < hotZonePosition[0] + hotZone.offsetWidth) && (event.pageY > hotZonePosition[1]) && (event.pageY < hotZonePosition[1] + hotZone.offsetHeight)))
    {
      hotZone.style.backgroundColor = '';
    }
    else
    {
      hotZone.style.backgroundColor = '#EFEEC9';
    }
  }

  //target.style.marginLeft = event.pageX + target.differenceX + "px";
  //target.style.marginTop = event.pageY + target.differenceY + "px";

  stopDefaultAction(event);

  return true;
} // function mousemoveDragNDrop(event)


/**
 * When the mouse button is released, we want the dragging effect to cease.
 * This is achieved using the mouseupDragNDrop function:
 */
function mouseupDragNDrop(event)
{
  if (typeof event == "undefined")
  {
    event = window.event;
  }

  if (typeof event.pageX == "undefined")
  {
    event.pageX = event.clientX + getScrollingPosition()[0];
    event.pageY = event.clientY + getScrollingPosition()[1];
  }

  /* track the currently dragged element */
  var target = document.currentTarget;

  /* This bit should become a for loop to check for one or more hot zones */
  //var hotZone = document.getElementById("targetZone");
  //var hotZonePosition = getPosition(hotZone);

  var FORMs= document.getElementById("dropZones").getElementsByTagName("form");

  for (var i = 0; i < FORMs.length; i++) {
    var hotZone = FORMs[i];
    var hotZonePosition = getPosition(hotZone);

    // See if we are in the hot zone, else is we are. Should multiply this to
    // create more hot zones.
    if (!((event.pageX > hotZonePosition[0]) && (event.pageX < hotZonePosition[0] + hotZone.offsetWidth) && (event.pageY > hotZonePosition[1]) && (event.pageY < hotZonePosition[1] + hotZone.offsetHeight))) {
      target.style.zIndex = "1";
      target.style.left = target.originLeft + "px";
      target.style.top = target.originTop + "px";
    } else {
      var cartInput = document.getElementById("cartInput");

      if (cartInput == null) {
        var cartInput = document.createElement("input");

        cartInput.setAttribute("id", "cartInput");
        cartInput.setAttribute("name", "cartInput");
        cartInput.setAttribute("type", "hidden");
        cartInput.setAttribute("value", target.getAttribute("id"));
        //document.getElementById("targetZone").appendChild(cartInput);
        FORMs[i].appendChild(cartInput);
      } else {
        cartInput.setAttribute("value", cartInput.getAttribute("value") + "," + target.getAttribute("id"));
      }

      // In a practical system, you would probably submit the form here.
      //alert("Item dropped on shopping cart!");
      //alert("Item dropped into grid space " + FORMs[i].name + " " + target.firstChild.nodeValue + "!");
      //$multi.="\t<option value=\"\" onclick=\"return popitup('" . $params["update_url"] . "?xml=" . $xml . "&action=edit&update_datatype=" . $params['xml'] . "&update_id=$key&update_url=" . $params["update_url"] . "&update_child=true', 800, 600)\">[Add a new record...]</option>\n";
      //popitup('lab-containers.htm', 800, 600);

      // Fixed: mdavignon 10/28/2008 2nd option is an IE6 safe way of pointing to a form element
      //var containerElement = document.getElementById('nwcontainer');
      var containerElementLarge = document.selectFormstorage_location.nwstorage_location;
      var containerElementSmall = document.selectFormcontainer.nwcontainer;
      var containerElement = '';
      var containerName = '';
      var containerType = 'nwtype';
      var containerLocation = 'nwlocation';

      // First try Large Containers then Small and define the differences
      if (containerElementLarge.selectedIndex) {
        containerElement = containerElementLarge;
        containerName = 'storage_location';
      } else if (containerElementSmall.selectedIndex) {
        containerElement = containerElementSmall;
        containerName = 'storage_location';
      }

      //alert(containerElement.options[containerElement.selectedIndex].value); // Testing
      // Target is the object being moved
      var classes = target.className.split(" ");
      var nwtype = "";
      var nwid = "";
      for (i=0; i < classes.length; i++) {
        if (/^container/.test(classes[i])) {
          nwtype = classes[i].replace("container", "");
          nwtype = nwtype.replace("Small", "");
          nwtype = nwtype.replace("Very", "");
          nwtype = nwtype.replace("NoImage", "");
        } else if (/^librarySample$/.test(classes[i])) {
          containerName = 'containername';
          containerType = 'nwcontainertype';
          containerLocation = 'nwpreparationgrid';
        } else if (/^\w{32}$/.test(classes[i])) {
          nwid = classes[i];
          //alert(nwid);
        }
      }

      var hotclasses = '';
      var hotnwid = nwid;
      if (hotZone.firstChild && hotZone.firstChild.firstChild && hotZone.firstChild.firstChild.nodeName.toLowerCase() == "li") {
        hotnwid = '';
        hotclasses = hotZone.firstChild.firstChild.className.split(" ");
        for (i=0; i < hotclasses.length; i++) {
          if (/^\w{32}$/.test(hotclasses[i])) hotnwid = hotclasses[i];
        }
      }

      // Parameters beginning with "nwnw" should override the returned DB value
      // mdavignon 11/19/2008: Not necessary anymore
      //alert(containerElement.selectedIndex + " " + hotZone.name);
      // IE6/7 will not pass a GET/POST name of 'id' use 'nwid' instead
      if (nwid != hotnwid) {
        //alert(hotnwid);
        var objType = (nwid == '') ? "a new" : "another";
        document.getElementById("storageError").innerHTML = "Must delete or move the existing object before placing " + objType + " object here!";
        target.style.left = target.originLeft + "px";
        target.style.top = target.originTop + "px";
      } else {
        //alert(hotZone.name); // Testing
        //alert(containerElement.options[containerElement.selectedIndex].value); // Testing
        if (hotZone.name == 'targetTrash') {
          submitform(hotZone.name, 'Drop Zone', 'nwid', nwid, 'container', containerElement.options[containerElement.selectedIndex].value);
        } else {
	  //return popitup('site-category.htm?xml=categories&action=edit&update_datatype=containers&update_id=grid&update_child=true', 800, 600, '', options[selectedIndex])
	  //return popitup('lab-edit.htm?xml=vendors&action=edit&update_datatype=items&update_id=vendor&update_child=true', 800, 600, '', options[selectedIndex])
	  // TODO: The following need to be fixed or removed: update_datatype, update_id
	  // Not ready for prime time

	  //alert(nwtype); // Testing

	  popitup('lab-edit.htm?xml=containers&action=edit&update_datatype=items&update_id=vendor&update_child=true&submitvalue=Drop Zone&nwid='+nwid+'&nw'+containerName+'='+containerElement.options[containerElement.selectedIndex].value+'&'+containerType+'='+nwtype+'&'+containerLocation+'='+hotZone.name+'&override_defaults=1', 800, 900);

//          submitform(hotZone.name, 'Drop Zone', 'nwid', nwid, 'nw'+containerName, containerElement.options[containerElement.selectedIndex].value, containerType, nwtype, containerLocation, hotZone.name, 'override_defaults', 1);
        }
      }
      break;
    }
  } // for (var i = 0; i < FORMs.length; i++)

  // (more info pg. 289)
  detachEventListener(document, "mousemove", mousemoveDragNDrop, false);
  detachEventListener(document, "mouseup", mouseupDragNDrop, false);

  return true;
} // function mouseupDragNDrop(event)


/**
 * Lastly, the clickDragNDrop function cancels any click events that arise
 * during the drag-and-drop process. This stops links from being followed after
 * the mouse button has been released: (more info pg. 289)
 */
function clickDragNDrop(event)
{
  if (typeof event == "undefined")
  {
    event = window.event;
  }

  detachEventListener(document, "click", clickDragNDrop, false);

  stopDefaultAction(event);

  return true;
} // function clickDragNDrop(event)


/**
 *
 */
function addLoadListener(fn)
{
  if (typeof window.addEventListener != 'undefined')
  {
    window.addEventListener('load', fn, false);
  }
  else if (typeof document.addEventListener != 'undefined')
  {
    document.addEventListener('load', fn, false);
  }
  else if (typeof window.attachEvent != 'undefined')
  {
    window.attachEvent('onload', fn);
  }
  else
  {
    var oldfn = window.onload;
    if (typeof window.onload != 'function')
    {
      window.onload = fn;
    }
    else
    {
      window.onload = function()
      {
        oldfn();
        fn();
      };
    }
  }
}; // function addLoadListener(fn)


/**
 *
 */
function attachEventListener(target, eventType, functionRef, capture)
{
    if (typeof target.addEventListener != "undefined")
    {
        target.addEventListener(eventType, functionRef, capture);
    }
    else if (typeof target.attachEvent != "undefined")
    {
        target.attachEvent("on" + eventType, functionRef);
    }
    else
    {
        eventType = "on" + eventType;

        if (typeof target[eventType] == "function")
        {
            var oldListener = target[eventType];

            target[eventType] = function()
            {
                oldListener();

                return functionRef();
            }
        }
        else
        {
            target[eventType] = functionRef;
        }
    }

    return true;
}; // function attachEventListener(target, eventType, functionRef, capture)


/**
 * Here, the last two calls to the detachEventListener function from Chapter 13
 * remove the event listeners from the document, meaning that further
 * interaction won’t occur until the user clicks on another draggable object.
 */
function detachEventListener(target, eventType, functionRef, capture)
{
    if (typeof target.removeEventListener != "undefined")
    {
        target.removeEventListener(eventType, functionRef, capture)
    }
    else if (typeof target.detachEvent != "undefined")
    {
        target.detachEvent("on" + eventType, functionRef);
    }
    else
    {
        target["on" + eventType] = null;
    }

    return true;
}; // function detachEventListener(target, eventType, functionRef, capture)


/**
 * Our listeners need to know which object is currently being dragged, so we’ve
 * created a property—document.currentTarget—to keep track of the currently
 * dragged element. To detect this element, we use the getEventTarget function
 * from Chapter 13. (more info pg. 284)
 */
function getEventTarget(event)
{
    var targetElement = null;

    if (typeof event.target != "undefined")
    {
        targetElement = event.target;
    }
    else
    {
        targetElement = event.srcElement;
    }

    while (targetElement.nodeType == 3 && targetElement.parentNode != null)
    {
        targetElement = targetElement.parentNode;
    }

    return targetElement;
}; // function getEventTarget(event)


/**
 * mousemoveDragNDrop also calls the stopDefaultAction function we saw in
 * Chapter 13 in order to prevent standard actions such as text selection from
 * occurring while an object is being dragged.
 */
function stopDefaultAction(event)
{
    event.returnValue = false;

    if (typeof event.preventDefault != "undefined")
    {
        event.preventDefault();
    }

    return true;
}; // function stopDefaultAction(event)


/**
 *
 */
function getScrollingPosition()
{
  var position = [0, 0];

  if (typeof window.pageYOffset != 'undefined')
  {
    position = [
        window.pageXOffset,
        window.pageYOffset
    ];
  }

  else if (typeof document.documentElement.scrollTop != 'undefined'
      && document.documentElement.scrollTop > 0)
  {
    position = [
        document.documentElement.scrollLeft,
        document.documentElement.scrollTop
    ];
  }

  else if (typeof document.body.scrollTop != 'undefined')
  {
    position = [
        document.body.scrollLeft,
        document.body.scrollTop
    ];
  }

  return position;
} // function getScrollingPosition()


/**
 *
 */
function getPosition(theElement)
{
    var positionX = 0;
    var positionY = 0;

    while (theElement != null)
    {
        positionX += theElement.offsetLeft;
        positionY += theElement.offsetTop;
        theElement = theElement.offsetParent;
    }

    return [positionX, positionY];
}; // function getPosition(theElement)


/**
 *
 */
function identifyBrowser()
{
    var agent = navigator.userAgent.toLowerCase();

    if (typeof navigator.vendor != "undefined" && navigator.vendor == "KDE" && typeof window.sidebar != "undefined")
    {
        return "kde";
    }
    else if (typeof window.opera != "undefined")
    {
      var version = parseFloat(agent.replace(/.*opera[\/ ]([^ $]+).*/, "$1"));

      if (version >= 7)
      {
        return "opera7";
      }
      else if (version >= 5)
      {
        return "opera5";
      }

      return false;
    }
    else if (typeof document.all != "undefined")
    {
        if (typeof document.getElementById != "undefined")
        {
          var browser = agent.replace(/.*ms(ie[\/ ][^ $]+).*/, "$1").replace(/ /, "");

      if (typeof document.uniqueID != "undefined")
      {
        if (browser.indexOf("5.5") != -1)
        {
          return browser.replace(/(.*5\.5).*/, "$1");
        }
        else
        {
          return browser.replace(/(.*)\..*/, "$1");
        }
      }
      else
      {
        return "ie5mac";
      }
        }

        return false;
    }
    else if (typeof document.getElementById != "undefined")
    {
        if (navigator.vendor.indexOf("Apple Computer, Inc.") != -1)
        {
            if (typeof window.XMLHttpRequest != "undefined")
            {
                return "safari1.2";
            }

            return "safari1";
        }
        else if (agent.indexOf("gecko") != -1)
        {
            return "mozilla";
        }
    }

    return false;
}; // function identifyBrowser()


/**
 *
 */
function identifyOS()
{
    var agent = navigator.userAgent.toLowerCase();

    if (agent.indexOf("win") != -1)
    {
        return "win";
    }
    else if (agent.indexOf("mac"))
    {
        return "mac";
    }
    else
    {
        return "unix";
    }

    return false;
}; // function identifyOS()

// EOF
