/*
   Pop-Over on Crack
   Copyright 2005 by Robert Plank.
*/

// Example: Action.dragGravity("popup") where "popup" is the ID of a DIV layer in an HTML document.

var counter;
//var event;

var Action = {

   // Simple draggable popover
	drag : function(objName) {
		Drag.init(objName);
	},

   // Bouncing draggable popover (with gravity)
   bounce : function(objName) {
      Gravity.init(objName);
      Drag.init(objName);
   },

   // Spiral outwards then bounce
	spiralOut : function(objName) {
		Spiral.init(objName);

		Gravity.yAcceleration = 1;

		Fade.step = 20;
		Fade.init(objName, 0, 100);

		Spiral.angularSpeed = 15;
		Spiral.radiusSpeed = 0.7;
		Spiral.radius = 0;
		Spiral.endRadius = 150;

		Spiral.onFinish = function() {
		   Gravity.init(objName);
		   Drag.init(objName);
			Gravity.yAcceleration = 1;
		}
	},

   // Spiral in (no gravity)
	spiralIn : function(objName) {
      Gravity.xAcceleration = 0;
		Gravity.yAcceleration = 0;

      Spiral.angularSpeed = 8;
      Spiral.radius = 300
      Spiral.radiusSpeed = -5;
      Spiral.endRadius = 0

      Spiral.init(objName);
   },

   // Stick to the right side of the screen
   edgeMagnet : function(objName) {
      Gravity.xAcceleration = 100;
      Gravity.yAcceleration = 0;
      Gravity.init(objName);
   },

   // Popover that follows the mouse cursor around
   cursorMagnet : function(objName) {
      Move.steps = 20;
      Magnet.init(objName);
   }
};

var Path = {

   // Move the layer in a specified path
   move : function(objName, stack) {

      function moveQueue() {
         if (stack.length > 0) {
            var element = stack[0];
            stack = stack.slice(1);

            Gravity.xAcceleration = 0;
            Gravity.yAcceleration = 0;
            Move.setRelativeCoordinates(element.x, element.y);
         }
         else {
            if (typeof Path.move.onFinish == "function") {
               Path.move.onFinish();
            }
         }
      }

      Move.init(objName);
      Move.coast = false;
      Move.onMoveEnd = moveQueue;
      Move.onMoveEnd();
   },

   // Fish hook pattern
   fishHook : function(objName) {
      var stack = [
         {x:491, y:30},
         {x:559, y:60},
         {x:653, y:164},
         {x:621, y:274},
         {x:531, y:330},
         {x:381, y:256}
      ];

      Path.move(objName, stack);
      Path.move.onFinish = Path.fishHook.onFinish;
   },

   // Fish hook with gravity
   fishHookGrav : function(objName) {
      Path.fishHook.onFinish = function() {
         Gravity.yAcceleration = 3;
         Gravity.init(objName);
         Drag.init(objName);
      }
      Path.fishHook(objName);
   },

   // Zig zag pattern
   zigZag : function(objName) {
      var stack = [
         {x:60, y:25},
         {x:240, y:50},
         {x:60, y:150},
         {x:240, y:200},
         {x:60, y:300},
         {x:240, y:400}
      ];

      Path.move(objName, stack);
      Path.move.onFinish = Path.fishHook.onFinish;
   }

}

var Move = {

   delay : 10,

   previousX : null,
   previousY : null,

   movements : new Array(),

   box : null,
   coast : true,

   initX : null,
   initY : null,

   realX : null,
   realY : null,

   steps : 5,
   stepsLeft : 0,

   // Is the layer moving right now?
   isMoving : false,

   init : function(name) {
	  Move.reset();
      Move.box = document.getElementById(name);
      Move.find();
   },

   setSpeed : function(x, y) {
      if (Move.previousX != null && Move.previousY != null) {
         Gravity.xSpeed = x - Move.previousX;
         Gravity.ySpeed = y - Move.previousY;
      }

      Move.previousX = x;
      Move.previousY = y;
   },

   inBounds : function(x, y) {
      var topLeftX = parseInt(Move.box.style.left);
      var topLeftY = parseInt(Move.box.style.top);

      var topRightX = parseInt(Move.box.style.left) + parseInt(Move.box.style.width);
      var bottomLeftY = parseInt(Move.box.style.top) + parseInt(Move.box.style.height);

      // Figure out if we are inside the bounds of the box
        
      return (
         (x >= topLeftX && x <= topRightX) && 
         (y >= topLeftY && y <= bottomLeftY)
      );
   },

   getCenter : function() {
      var topLeftX = parseInt(Move.box.style.left);
      var topLeftY = parseInt(Move.box.style.top);

      var topRightX = parseInt(Move.box.style.left) + parseInt(Move.box.style.width);
      var bottomLeftY = parseInt(Move.box.style.top) + parseInt(Move.box.style.height);

      var centerX = parseInt((topLeftX + topRightX) / 2);
      var centerY = parseInt((topLeftY + bottomLeftY) / 2);

      var result = new Object();
      result.x = centerX;
      result.y = centerY;

      return result;
   },

   // Adjusts to place the box in the center of your coords
   getOffset : function() {

      var center = Move.getCenter();
      var topLeftX = parseInt(Move.box.style.left);
      var topLeftY = parseInt(Move.box.style.top);

      var cornerX = parseInt(center.x - topLeftX);
      var cornerY = parseInt(center.y - topLeftY);

      var result = new Object();
      result.x = cornerX;
      result.y = cornerY;

      return result;
   },

   set : function(x, y) {
      // Update the layer properties
      Move.realX = x;
      Move.realY = y;

      Move.box.style.left = Math.round(x) + "px";
      Move.box.style.top = Math.round(y) + "px";
   },

   height : function() {
      return parseInt(Move.box.style.height);
   },

   width : function() {
      return parseInt(Move.box.style.width);
   },

   end : function(x, y) {
      //Move.realX = null;
      //Move.realY = null;

      if (typeof Move.onMoveEnd == "function") {
         Move.onMoveEnd();
      }
   },

   clear : function() {
      Move.onMoveEnd = null;

      document.onmousedown = null;
      document.onmouseup = null;
      document.onmousemove = Cursor.getCursor;

      //Gravity.yAcceleration = 0;
      //Gravity.xAcceleration = 0;
   },

   jump : function(x, y) {
      Move.set(x, y);
      setTimeout("Move.end();", Move.delay);
   },

   find : function() {
      // If there are no realX and realY values yet,
      // read those off the DHTML layer.
      if (Move.realX == null) {
         Move.realX = parseInt(Move.box.style.left);
         Move.initX = Move.realX;
      }
      if (Move.realY == null) {
         Move.realY = parseInt(Move.box.style.top);
         Move.initY = Move.realY;
      }
   },

   setRelativeCoordinates : function(x, y) {
      return Move.setCoordinates(x + Move.screenLeft(), y + Move.screenTop());
   },

   /**
      Set our box to coordinates x and y
   */
   setCoordinates : function(x, y) {

      if (Move.box == null) return false;
      Move.isMoving = true;

      Move.find();

      // Calculate distance to travel
      if (Move.stepsLeft <= 0) Move.stepsLeft = Move.steps;

      var distanceX = (x - Move.realX) / Move.stepsLeft;
      var distanceY = (y - Move.realY) / Move.stepsLeft;

      if (Move.coast == false) {
         Move.stepsLeft--;
      }

      Move.set(Move.realX + distanceX, Move.realY + distanceY);

      // If we still need to keep going...
      if (Math.round(Move.realX) != x || Math.round(Move.realY) != y) {
         Move.setSpeed(Move.realX, Move.realY);
         setTimeout("Move.setCoordinates(" + x + "," + y + ");", Move.delay);
      }
      // We are done.
      else {
         Move.end(x, y);
      }
   },

   getVerticalScroll : function() {
      if (window.pageYOffset) return parseInt(window.pageYOffset);
      return document.body.scrollTop;
   },

   getHorizontalScroll : function() {
      if (window.pageXOffset) return parseInt(window.pageXOffset);
      return document.body.scrollLeft;
   },

   screenTop : function() {
      return Move.getVerticalScroll();
   },

   screenBottom : function() {
      return Move.getVerticalScroll() + Move.browserHeight();
   },

   screenLeft : function() {
      return Move.getHorizontalScroll();
   },

   screenRight : function() {
      return Move.getHorizontalScroll() + Move.browserWidth()-50;
   },

   browserHeight : function() {
      if(typeof(window.innerWidth) == 'number') {
         //Non-IE
         return window.innerHeight - 2;
      }
      else if(document.documentElement && (document.documentElement.clientHeight)) {
         //IE 6+ in 'standards compliant mode'
         return document.documentElement.clientHeight;
      }
      else if(document.body && (document.body.clientHeight)) {
         //IE 4 compatible
         return document.body.clientHeight;
      }
   },

   browserWidth : function() {
      if(typeof(window.innerWidth) == 'number') {
         //Non-IE
         return window.innerWidth - 2;
      }
      else if(document.documentElement && (document.documentElement.clientWidth)) {
         //IE 6+ in 'standards compliant mode'
         return document.documentElement.clientWidth;
      }
      else if(document.body && (document.body.clientWidth)) {
         //IE 4 compatible
         return document.body.clientWidth;
      }
   },

   reset : function() {
	   Magnet.disable();
   }

}

var Circle = {
   radius : 100,
   xOffset : 300,
   yOffset : 300,

   xPoint : 0,
   yPoint : 0,

   angle : 0,
   degree : 0,

   /**
      Translates the parameters of the Circle object into rectangular coordinates.
   */
   calculate : function() {

      /* Convert to radians

         In a circle, degrees are measured from 0 to 360, right?
         So basically you're chopping a circle into 360 equal parts...

         Same thing with radians, only 2 times PI is a full circle, so that
         is chopped into (2*PI) equal parts.

         JavaScript's sine and cosine functions only deal with radians... so
         to do the conversion, first reduce degrees into how much of a percentage
         that angle represents... for example, 90 degrees is 1/4th of a circle.

         90 / 360 = 0.25 or 1/4th

         You have your circle "percentage", now multiply that by (2*PI) to get
         it in radians.
      */

      Circle.xPoint = (Math.sin(Circle.angle) * Circle.radius) + Circle.xOffset;
      Circle.yPoint = (Math.cos(Circle.angle) * Circle.radius) + Circle.yOffset;
   },

   setOffsets : function(x, y) {
      Circle.xOffset = x;
      Circle.yOffset = y;
      Circle.calculate();
   },

   setDegrees : function(newAngle) {
      // Convert larger numbers (outside the range of 0 to 360) into "relative" numbers

      var maxValue = 360;
      var minValue = 0;

      // "Plus because we are adding a negative number, which is really subtracting
      if (newAngle < minValue) newAngle = maxValue + newAngle;
      else if (newAngle >= maxValue) {
         newAngle = newAngle % maxValue;
      }

      Circle.degree = newAngle;

      Circle.angle = (newAngle / 360) * (2 * Math.PI);
      Circle.calculate();
   },

   setRadius : function(newRadius) {
      // Positive radius only
      newRadius = Math.max(0, newRadius);

      Circle.radius = newRadius;
      Circle.calculate();
   },

   getCoordinates : function() {
      var obj = new Object();
      obj.x = Circle.xPoint;
      obj.y = Circle.yPoint;
      return obj;
   }
};

var Fade = {

   current : 0,
   target : 0,
   timer : null,
   panel : null,

   delay : 50,
   step : 10,

   counter : 0,

   init : function(newLayer, newCurrent, newTarget) {
	   clearInterval(Fade.timer);
	   Fade.timer = null;

      Fade.panel = document.getElementById(newLayer);

      if (newCurrent != undefined) {
         Fade.target = newTarget;
         Fade.current = newCurrent;
         Fade.setOpacity(newCurrent);

         Fade.timer = setInterval("Fade.adjust()", Fade.delay);
      }
   },

   adjust : function() {
      difference = Fade.target - Fade.current;
      difference = difference / Fade.step;

      if (difference < 0) change = Math.floor(difference);
      else change = Math.ceil(difference);

      Fade.current = Fade.current + change;
      Fade.setOpacity(Fade.current);

      Fade.panel.style.display='block';
      Fade.panel.style.visibility='visible';
      Fade.panel.style.position='absolute';

      if (Fade.timer && Fade.current == Fade.target) {
         clearInterval(Fade.timer);

         if (typeof Fade.onFinish == "function") {
            Fade.onFinish();
         }
      }
   },

   setOpacity : function(level) {
      Fade.panel.style.MozOpacity = level / 100;
      Fade.panel.style.filter = "alpha(opacity=" + level + ")";
   },

   // Simple fade out.
   out : function(objName) {
      Fade.onFinish = function() {
         document.getElementById(objName).style.display='none';
      }
      Fade.init(objName, 100, 0);
   },

   // Simple fade in.
   up : function(objName) {
      Fade.init(objName, 0, 100);
   }
};

var Gravity = {

   yDirection: 1,
   yAcceleration: 2,

   xDirection: 1,
   xAcceleration: 0,

   ySpeed : -20,
   xSpeed : -15,

   yFriction : 0.85,
   xFriction : 0.95,

   init : function(name) {
      Move.onMoveEnd = Gravity.onMoveEnd;

      Move.init(name);
      Move.onMoveEnd();
   },

   onMoveEnd : function() {
      // Move the popover down
      Gravity.ySpeed += Gravity.yAcceleration;
      Gravity.xSpeed += Gravity.xAcceleration;

      var finalY = Math.min(Move.screenBottom() - Move.height(), Move.realY + Gravity.ySpeed);
      finalY = Math.max(Move.screenTop(), finalY);

      var finalX = Math.min(Move.screenRight() - Move.width(), Move.realX + Gravity.xSpeed);
      finalX = Math.max(Move.screenLeft(), finalX);


      if (Gravity.ySpeed > 0 && finalY + Move.height() >= Move.screenBottom()) {
         Gravity.yCollide();
      }

      else if (Gravity.ySpeed < 0 && finalY <= Move.screenTop()) {
         Gravity.yCollide();
      }

      if (Gravity.xSpeed > 0 && finalX + Move.width() >= Move.screenRight()) {
         Gravity.xCollide();
      }

      else if (Gravity.xSpeed < 0 && finalX <= Move.screenLeft()) {
         Gravity.xCollide();
      }


      Move.jump(finalX, finalY);
   },

   yCollide : function() {
      Gravity.ySpeed = -1 * Gravity.yFriction * Gravity.ySpeed;
      Gravity.xSpeed = Gravity.xFriction * Gravity.xSpeed;
   },

   xCollide : function() {
      Gravity.xSpeed = -1 * Gravity.xFriction * Gravity.xSpeed;
      Gravity.ySpeed = Gravity.yFriction * Gravity.ySpeed;
   }

}

/*

*/

/**
   A spiral is pretty easy to do in JavaScript if you can figure out how to
   make a layer move in a circle.  To make something spiral all you have to
   do is make a layer orbit some center, and have its orbit get smaller and
   smaller until it reaches the middle (think of something being sucked into
   a black hole or a toilet drain).

   So, how do you move something in a circle?  Well, you have to set parameters
   like the radius of a circle, and the angle off the ground you are pointing.

   These are called polar coordinates.  You have to translate these into
   rectangular coordinates, or a set of X and Y values.  I know, it sounds too
   much like math.
*/

var Spiral = {

   // delay before updating cursor position
   delay : 0,
   nextUpdate : 0,

   degrees : 0,
   radius : 300,
   endRadius : 0,

   endDegrees : null,

   /* Number of degrees moved every iteration (10 ms)
      Positive values are counter-clockwise, negative values are clockwise

      Reducing this number will slow down the spiral, but if you do that
      be sure to reduce radiusSpeed as well, or the circle will shrink too
      fast and you won't really get a spiral effect.
   */
   angularSpeed : 5,

   // How much to reduce the radius by iteration
   radiusSpeed : -1,

   // Counter to tell how many steps we have taken
   counter : 0,

   // Total steps this spiral process will take
   steps : 0,

   /**
      Set everything up.
   */
   init : function(name) {
      // Makes the popup be drawn towards the mouse pointer.
      // Comment out the two lines below to disable.
      document.onmousemove = Spiral.magneticCursor;
      window.onmousemove = Spiral.magneticCursor;

      // Randomly choose clockwise or counter-clockwise
      var sign = Math.round(Math.random());
      Spiral.angularSpeed *= (sign == 0) ? -1 : 1;

      // Set the final X and Y coordinates where we want to end up
      // (Irrelevant if magnetic cursor is turned on)
      //Circle.setOffsets(300,300);

      Move.onMoveEnd = Spiral.onMoveEnd;

      Spiral.counter = 0;

      // Allow the requested layer to be moved
      Move.init(name);

      // Allow the layer to be faded
      //Fade.init(name);

      /*
         OPTIONAL: FADE IN AS WE GO

         Figuring out how many steps we will have to take for the pop-up to
         spiral in is easy.

         It's just the initial radius size divided by that small amount the
         radius is subtracted in each step.

         For example if the initial radius is 300 pixels, and 10 pixels are
         taken away from the radius each turn, that means we will go through
         30 steps.
      */
      Spiral.steps = Math.abs(Spiral.radius / Spiral.radiusSpeed);

      // Begin spiral
      Move.onMoveEnd();
   },

   magneticCursor : function(event) {
      Cursor.getCursor(event);

      var width = parseInt(Move.box.style.width);
      var height = parseInt(Move.box.style.height);

      width = Math.round(width / 2);
      height = Math.round(height / 2);

      Circle.xOffset = Cursor.x - width;
      Circle.yOffset = Cursor.y - height;
   },

   angleDone : function() {
      //if (Spiral.endDegrees == null) return true;

      if (Spiral.angularVelocity > 0)
         return (Spiral.degrees <= Spiral.endDegrees) ? true : false

      else
         return (Spiral.degrees >= Spiral.endDegrees) ? true : false;
   },

   onMoveEnd : function() {
      // Adjust the angle for this step
      Spiral.degrees = Spiral.degrees + Spiral.angularSpeed;

      // Shrink the radius for this step
      Spiral.radius = Spiral.radius + Spiral.radiusSpeed;


      /*
         Then, to figure out the opacity, first figure out how far along we
         are in the radius shrinking process.  For example if there were
         30 steps to take, and we were on step 15, 15 divided by 30 is 1/2.

         Then just multiply that by 100 to get the percent value needed to
         plug it into our Fade.setOpacity() function.  Something like 100
         times 1/2 would be 50.
      */

      // Check Spiral.steps BEFORE dividing to avoid divide-by-zero errors.
      
      //if (Spiral.steps > 0) {
         //var percentage = Spiral.counter / Spiral.steps;
         //var newOpacity = Math.round(percentage * 100);
         //Fade.setOpacity(newOpacity);
      //}

      Circle.setRadius(Spiral.radius);
      Circle.setDegrees(Spiral.degrees);
      Spiral.degrees = Circle.degree;

      /* Translate polar (circular) coordinates into a rectangular system
         so we know where to place the popup. */

      var coords = Circle.getCoordinates();
      if (
         Spiral.radiusSpeed < 0 && Spiral.radius > Spiral.endRadius ||
         Spiral.radiusSpeed > 0 && Spiral.radius < Spiral.endRadius) {

         Spiral.counter++; // Add 1 to the step counter
         Move.jump(coords.x, coords.y);
         Move.setSpeed(coords.x, coords.y);
      }

      else if (Spiral.radiusSpeed == 0) {
         Move.jump(coords.x, coords.y);
         Move.setSpeed(coords.x, coords.y);
      }

      else {
         // Done
         Move.onMoveEnd = null;
         if (Spiral.onFinish != null && typeof Spiral.onFinish == "function") {
            Spiral.onFinish();
         }
      }

   }
};

var Drag = {

   oldMoveEnd : null,

   pressed : false,

   differenceX : 0,
   differenceY : 0,

   init : function(name) {
      Move.init(name);

      document.onmousedown = Drag.onmousedown;
      document.onmouseup = Drag.onmouseup;
   },

   movePopup : function(event) {
      Cursor.getCursor(event);
      var newX = Cursor.x - Drag.differenceX;
      var newY = Cursor.y - Drag.differenceY;
      Move.set(newX, newY);
      Move.setSpeed(newX, newY);

   },

   onmousedown : function(e) {

      // Calculate corners
      var topLeftX = parseInt(Move.box.style.left);
      var topLeftY = parseInt(Move.box.style.top);

      //var topRightX = parseInt(Move.box.style.left) + parseInt(Move.box.style.width);
      //var bottomLeftY = parseInt(Move.box.style.top) + parseInt(Move.box.style.height);

      Cursor.getCursor(e);

      // Figure out if we are inside the bounds of the box
      /*
      Drag.pressed = 
         (Cursor.x >= topLeftX && Cursor.x <= topRightX) && 
         (Cursor.y >= topLeftY && Cursor.y <= bottomLeftY);
      */
      Drag.pressed = Move.inBounds(Cursor.x, Cursor.y);

      if (Drag.pressed) {
         document.onmousemove = Drag.movePopup;

         Drag.oldMoveEnd = Move.onMoveEnd;
         Move.onMoveEnd = null;
      }

      Drag.differenceX = Cursor.x - topLeftX;
      Drag.differenceY = Cursor.y - topLeftY;
   },

   onmouseup : function() {
      if (Drag.pressed) {
         document.onmousemove = Cursor.getCursor;
         Move.onMoveEnd = Drag.oldMoveEnd;
         Drag.pressed = false;
         Move.end();
      }
   }
};

var Magnet = {

   maxDistance : 200,
   lastCursor : null,

   lastX : null,
   lastY : null,

   moving : false,

   init : function(name) {
      Move.init(name);
      Move.coast = false;
      Magnet.enable();
   },

   move : function(event) {

      Cursor.getCursor(event);
      //window.status = Cursor.x;

      if (Magnet.moving == true) return false;
      if (Move.inBounds(Cursor.x, Cursor.y)) return false;

      var center = Move.getCenter();

      var xDistance = Cursor.x - center.x;
      var yDistance = Cursor.y - center.y;

      var distance = Math.sqrt(
         (xDistance * xDistance) + (yDistance * yDistance)
      );

      //window.status = distance;

      if (distance > Magnet.maxDistance || Magnet.moving) {
         Magnet.moving = true;

         var offset = Move.getOffset();
         Move.setRelativeCoordinates(Cursor.x - offset.x, Cursor.y - offset.y);

         Move.onMoveEnd = Magnet.enable;
      }

   },

   disable : function() {
      document.onmousemove = null;
      window.onmousemove = null;
   },

   enable : function() {
      document.onmousemove = Magnet.move;
      window.onmousemove = Magnet.move;

      if (Cursor.x == null) return false;

      // Start up again towards the cursor if we havent hit it yet
      if (!Move.inBounds(Cursor.x, Cursor.y)) {
         var offset = Move.getOffset();
         Move.setRelativeCoordinates(Cursor.x - offset.x, Cursor.y - offset.y);
      }
      else {
         Magnet.moving = false;
         Move.onMoveEnd = null;
      }
   }

};

var Cursor = {
   x : null,
   y : null,

   lastX : null,
   lastY : null,

   archive : function() {
	   Cursor.lastX = Cursor.x;
	   Cursor.lastY = Cursor.y;
   },

   getCursor : function(e) {
      var e = e ? e:event;

      if (e != undefined && e.pageX && e.pageY) {
		   Cursor.archive();
         Cursor.x = parseInt(e.pageX);
         Cursor.y = parseInt(e.pageY);
      }
      else if (e && e.clientX && e.clientY) {
         Cursor.archive();
         Cursor.x = parseInt(e.clientX + document.body.scrollLeft);
         Cursor.y = parseInt(e.clientY + document.body.scrollTop);
      }
   }
};