﻿var Utilities = {
    isDefined : function( t_object )
    {
        return typeof t_object != "undefined";
    },
    
    ifDefined : function( t_value, t_alternate )
    {        
        return Utilities.isDefined(t_value) ? t_value : t_alternate;
    },
    
    invokeOnResponse : function( t_function, t_url, t_eval )
    {
        new Ajax.Request(t_url, {
                method : "GET",
                onSuccess : (function(t_transport, t_function, t_eval)
                {
                    var t_response = null;
                    if( $defined(t_eval) )
                    {
                        t_response = eval(t_transport.responseText);
                    }
                    else
                    {
                        t_response = t_transport.responseText;
                    }
                    t_function(t_response);
                }).bindAsEventListener(window, t_function, t_eval),
                onFailure : function()
                {
                }
            });
    }
};
var $defined = Utilities.isDefined;
var $ifDefined = Utilities.ifDefined;
var $invokeOnResponse = Utilities.invokeOnResponse;

Utilities.Window = Object.extend(Utilities, {
    getSize : function()
    {
        var t_calcWidth, t_calcHeight;
        if (window.innerWidth) {
			t_calcWidth = window.innerWidth;
			t_calcHeight = window.innerHeight;
		} else {
			t_calcWidth = document.compatMode == 'CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth;
			t_calcHeight = document.compatMode == 'CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight;
		}
		return {
		    width: t_calcWidth,
		    height : t_calcHeight
		}
    }
});

Utilities.Page = Object.extend(Utilities, {
    _pageLoaded : false,
    _arrToInvoke : [],
    _attemptInvoke : function( t_func )
    {
        if( !Utilities.Page._pageLoaded )
        {
            Utilities.Page._arrToInvoke.push(t_func);
        }
        else
        {
            t_func();
        }    
    },
    
    _invokePending : function()
    {
        try
        {   
			// TODO: Harald: Denne var reversert, men du husker ikke hvorfor.. Har nå tatt bort reverseringen, og det ser ut til å fungere..?
//			Utilities.Page._arrToInvoke.each(
//				function(t_func) {
//				    try{t_func();}catch(e)
//				    {
//				        var t_msg = $ifDefined(e.message, e);
//				        alert(t_msg + " - " + t_func);
//				    }
//				}
//			);
			
			for(var i=0; i<Utilities.Page._arrToInvoke.length; i++)
			{
			    var t_func = Utilities.Page._arrToInvoke[i];
			    try{t_func();}catch(e)
			    {
			        var t_msg = $ifDefined(e.message, e);
			        alert(t_msg + " - " + t_func);
			    }
			}
			
		}
		finally
		{
			Utilities.Page._arrToInvoke.clear();
            Utilities.Page._pageLoaded = true; 
		}
    }
});
var $attemptInvoke = Utilities.Page._attemptInvoke;
var $invokePending = Utilities.Page._invokePending;

Utilities.DOM =  Object.extend(Utilities, {
    create : function(t_elementName, t_text, t_childNodes, t_parent)
    {    
        return $(document.createElement(t_elementName)).$add(t_childNodes).$text(t_text).$addTo(t_parent);    	
    },
    
    _addAttribute : function(t_element, t_name, t_value)
    {
        if( t_name == "style" )
        { 
            try
            {
                var t_tokens = t_value.split(";").compact();
                
                var t_hash = t_tokens.inject({}, function(t_params, t_pairString)
                    {
                        var t_pair = t_pairString.split(':');
                        t_params[t_pair[0]] = t_pair[1];
                        return t_params;
                    });
                
                Element.setStyle(
                    t_element, 
                    t_hash);
            }
            catch(e)
            {
                alert("Exception adding attribute style: " + e.message);
            }
        }
        else if( t_name == "class" )
        {
            t_element.addClassName( t_value );
        }
        else
        {
            t_element.writeAttribute(t_name,t_value);
        }
        return t_element;
    },
    
    _addConditionalAttribute : function( t_element, t_name, t_value, t_condition )
    {
        return t_condition ? t_element.$attr(t_name,t_value) : t_element;
    },
    
    _appendChild : function(t_element)
    {
        t_element.appendChild($dom.apply(this, $A(arguments).slice(1)));
        return t_element;
    },
    
    _setText : function( t_element, t_text )
    {
        if(typeof t_text != "undefined" && t_text != null)
	    {
            var t_node = document.createTextNode(t_text);
            t_element.appendChild(t_node);
        }
        return t_element;
    },
    
    _setParent : function( t_element, t_parent )
    {
        if( typeof t_parent != "undefined" && t_parent != null )
        {
            t_parent.appendChild(t_element);
        }
        return t_element;
    },
    
    _addChild : function( t_element, t_childNodes )
    {
        if(typeof t_childNodes != "undefined" && t_childNodes != null)
	    {	    
	        try
	        {
                if( $defined(t_childNodes.length) )
	            {
	                var i, t_length = t_childNodes.length;
		            for(i=0; i < t_length; i++)
		            {
			            t_element.appendChild(t_childNodes[i]);
		            }
	            }
	            else
	            {
		            t_element.appendChild(t_childNodes);
	            }        
            }
		    catch(e)
		    {
			    throw "Invalid child node " + t_childNodes[i] + ". " + e.message;
		    }
        }
        
        return t_element;
    }
});

/*
Elements are only extended in IE6 if they are processed through the $-method
*/
Element.addMethods({
        $attr : Utilities.DOM._addAttribute,
        $condAttr : Utilities.DOM._addConditionalAttribute,
        $dom : Utilities.DOM._appendChild,
        $text : Utilities.DOM._setText,
        $addTo : Utilities.DOM._setParent,
        $add : Utilities.DOM._addChild
    });

var $dom = Utilities.DOM.create;


Utilities.Event = Object.extend(Utilities, {
	Button : 
	{
		LEFT : 1,
		RIGHT : 2,
		MIDDLE : 3
	},

	whichButton : function(t_event)
	{
		var t_button;
        if (t_event.which == null){
            /* IE case */
            t_button = (t_event.button < 2) ? Utilities.Event.Button.LEFT :
                 ((t_event.button == 4) ? Utilities.Event.Button.MIDDLE : Utilities.Event.Button.RIGHT);
        }
        else{
            /* All others */
            t_button= (t_event.which < 2) ? Utilities.Event.Button.LEFT :
                 ((t_event.which == 2) ? Utilities.Event.Button.MIDDLE : Utilities.Event.Button.RIGHT);
        }
        return t_button;
	}
});


Utilities.Karto = Object.extend(Utilities, {
    //Compute the distance from AB to C
	//if isSegment is true, AB is a segment, not a line.
	linePointDistance : function( aX, aY, bX, bY, cX, cY, isSegment)
	{
	    var dist=Utilities.Karto.cross(aX, aY, bX, bY, cX, cY)/Utilities.Karto.distance(aX, aY, bX, bY);
		if(isSegment)
		{
		    var dot1=Utilities.Karto.dot(aX, aY, bX, bY, cX, cY);
		    if (dot1>0) return Utilities.Karto.distance(bX, bY, cX, cY);
		    var dot2=Utilities.Karto.dot(bX, bY, aX, aY, cX, cY);
		    if (dot2>0) return Utilities.Karto.distance(aX, aY, cX, cY);
		}
		return Math.abs(dist);
	},
	
	//Compute the cross product AB x AC
	cross: function(aX, aY, bX, bY, cX, cY)
    {
	    //var AB = new Array[2];
	    //var AC = new Array[2];
	    var ab0=bX-aX;
	    var ab1=bY-aY;
	    var ac0=cX-aX;
	    var ac1=cY-aY;
	    return ab0*ac1-ab1*ac0;
    },
    
    //Compute the distance from A to B
    distance: function(aX, aY, bX, bY)
    {
        var d1=aX-bX;
        var d2=aY-bY;
	    return Math.sqrt(d1*d1+d2*d2);
    },
    
    //For calculation of distance to line or segment. http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry1
	//Compute the dot product AB x BC
    dot: function(aX, aY, bX, bY, cX, cY)
	{
		//var AB = new Array[2];
		//var BC = new Array[2];
		var ab0=bX-aX;
		var ab1=bY-aY;
		var bc0=cX-bX;
		var bc1=cY-bY;
		return ab0*bc0+ab1*bc1;
	},
	
	angle : function(aX, aY, bX, bY)
    {
        var p2x = bX - aX;
        var p2y = bY - aY;

        var angle = Math.atan2(p2y, p2x) / Math.PI * 180;

        return p2y > 0 ? angle : angle + 360;
    },

	filter : function(t_coordinates, t_minDistance, t_maxAngle)
	{
	    t_maxAngle = $ifDefined(t_maxAngle, null);
	    var t_previousAngle = 0;
	    var points = [t_coordinates[0]];
        var lastCoord = null;
        var i, t_length=t_coordinates.length;
        for(i=1; i<t_length-1; i++)
        {
            var t_coord = t_coordinates[i];
            var t_angle = null;
            if( i<t_length-2 ) //not last point.
            {
                //calc direction of the linesegment from this point to the next.
                t_angle = this.angle(
                    t_coord.x, t_coord.y,
                    t_coordinates[i + 1].x, t_coordinates[i + 1].y);
            }
            
            var doAdd = false;
            if (lastCoord != null)
            {
                var distance = this.distance(lastCoord.x, lastCoord.y, t_coord.x, t_coord.y);
                if (distance > t_minDistance)
                {
                    doAdd = true;
                }
                else if(t_maxAngle != null && t_angle != null) //Check angle.
                {
                    //Denne jobba jeg mye med, men:
                    //double angleDiff = Math.Abs(Math.Atan(Math.Sin(previousAngle)-Math.Sin(angle)));

                    //Som Linda sa: Keep it simple stupid..
                    var t_angleDiff = (t_previousAngle - t_angle)%360;
                    t_angleDiff = t_angleDiff > 180 
                        ? t_angleDiff - 360 
                        : t_angleDiff;

                    if(Math.abs(t_angleDiff) > t_maxAngle)
                    {
                        doAdd = true;
                    }
                }
            }
            else
            {
                doAdd = true;
            }

            if (doAdd)
            {
                points.push(t_coord);
                lastCoord = t_coord;
                t_previousAngle = t_angle;
            }
        }
        if(t_length > 1)
        {
            points.push(t_coordinates[t_length-1]);
        }
        
        return points;
	},
	
	douglasPeuckerReduction : function(points, tolerance)
    {
        if (points == null || points.length < 3)
            return points;

        var firstPoint = 0;
        var lastPoint = points.length - 1;

        //Add the first and last index to the keepers
        //The first and the last point cannot be the same
        while (points[firstPoint].x == points[lastPoint].x &&
                points[firstPoint].y == points[lastPoint].y )
        {
            lastPoint--;
        }
        var pointIndexsToKeep = [firstPoint, lastPoint];
        this._douglasPeuckerReduction(points, firstPoint, lastPoint,
            tolerance, /*ref*/ pointIndexsToKeep);

        var returnPoints = [];
        pointIndexsToKeep = pointIndexsToKeep.sortBy(function(t_element){return t_element});
        var i, t_length = pointIndexsToKeep.length;
        //foreach (index in pointIndexsToKeep)
        for(i=0; i<t_length; i++)
        {
            var index = pointIndexsToKeep[i];
            returnPoints.push(points[index]);
        }

        return returnPoints;
    },

    /// <summary>
    /// Douglases the peucker reduction.
    /// </summary>
    /// <param name="points">The points.</param>
    /// <param name="firstPoint">The first point.</param>
    /// <param name="lastPoint">The last point.</param>
    /// <param name="tolerance">The tolerance.</param>
    /// <param name="pointIndexsToKeep">The point index to keep.</param>
    _douglasPeuckerReduction : function(points, firstPoint, lastPoint, tolerance, pointIndexsToKeep)
    {
        var maxDistance = 0;
        var indexFarthest = 0;

        for (var index = firstPoint; index < lastPoint; index++)
        {
            var distance = this.perpendicularDistance(
                points[firstPoint], points[lastPoint], points[index]);
            if (distance > maxDistance)
            {
                maxDistance = distance;
                indexFarthest = index;
            }
        }

        if (maxDistance > tolerance && indexFarthest != 0)
        {
            //Add the largest point that exceeds the tolerance
            pointIndexsToKeep.push(indexFarthest);

            this._douglasPeuckerReduction(points, firstPoint,
                indexFarthest, tolerance, /*ref*/ pointIndexsToKeep);
            this._douglasPeuckerReduction(points, indexFarthest,
                lastPoint, tolerance, /*ref*/ pointIndexsToKeep);
        }
    },

    /// <summary>
    /// The distance of a point from a line made from point1 and point2.
    /// </summary>
    /// <param name="point1">The PT1.</param>
    /// <param name="point2">The PT2.</param>
    /// <param name="point">The p.</param>
    /// <returns></returns>
    perpendicularDistance : function(point1, point2, point)
    {
        //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)|   *Area of triangle
        //Base = v((x1-x2)²+(x1-x2)²)                               *Base of Triangle*
        //Area = .5*Base*H                                          *Solve for height
        //Height = Area/.5/Base

        var area = Math.abs(.5 * (point1.x * point2.y + point2.x *
            point.y + point.x * point1.y - point2.x * point1.y - point.x *
            point2.y - point1.x * point.y));
        var bottom = Math.sqrt(
            Math.pow(point1.x - point2.x, 2) +
            Math.pow(point1.y - point2.y, 2));
        var height = (area / bottom) * 2;

        return height;

        //Another option
        //Double A = point.X - point1.X;
        //Double B = point.Y - point1.Y;
        //Double C = point2.X - point1.X;
        //Double D = point2.Y - point1.Y;

        //Double dot = A * C + B * D;
        //Double len_sq = C * C + D * D;
        //Double param = dot / len_sq;

        //Double xx, yy;

        //if (param < 0)
        //{
        //    xx = point1.X;
        //    yy = point1.Y;
        //}
        //else if (param > 1)
        //{
        //    xx = point2.X;
        //    yy = point2.Y;
        //}
        //else
        //{
        //    xx = point1.X + param * C;
        //    yy = point1.Y + param * D;
        //}

        //Double d = DistanceBetweenOn2DPlane(point, new point(xx, yy));
    }
});


String.prototype.trim	=	new Function("return this.replace(/^\\s+|\\s+$/g,'')");
Array.prototype.sum = function(){
	for(var i=0,sum=0;i<this.length;sum+=this[i++]);
	return sum;
}

 