﻿var S =
{
    Break: { },
        
    Debugger:
    {
        instance: null,
        
        writeLine: function(message)
        {
            if (this.instance)
                this.instance.writeLine(message);
        }
    },
    
    ToArray: function(iterable)
    {
        if (!iterable) return [];
        if (iterable.toArray) return iterable.toArray();
        var length = iterable.length, results = new Array(length);
        while (length--) results[length] = iterable[length];
        return results;
    },

    Get: function(id)
    {
        return document.getElementById(id);
    },
    
    GetElements: function()
    {
        return this.GetElementsIn(document, arguments);
    },
    
    GetParentByTagName: function(tagName, startingNode)
    {
        if (startingNode != null)
        {
            var node = startingNode.parentNode;
            var compareTagName = tagName.toLowerCase();
            
            while(node != null)
            {
                if (node.tagName && node.tagName.toLowerCase() == compareTagName)
                    return node;
                else
                    node = node.parentNode;
            }
        }
        
        return null;
    },
    
    // Example: var inputs = S.GetElementsIn(container, "input", { type: "checkbox" });
    GetElementsIn: function(container)
    {
        var tagName = arguments[1];
        
        var elements = S.ToArray(container.getElementsByTagName(tagName));
                        
        // Filter output.
        if (elements.length > 0 && arguments.length > 2)
        {
            var filtered = [];
            var properties = S.ToArray(arguments).slice(2);
            
            elements.each(function(element)
            {
                var allPropertiesFound = true;
                
                properties.each(function(property)
                {
                    for (name in property)
                    {
                        var value = property[name].toLowerCase();
                        
                        // blnIE comes from Common.js, should be moved to S...
                        // In IE class is className, damn MS!
                        if (blnIE && name == "class")
                            name = "className";
                            
                        var attribute = element.getAttribute(name);
                            
                        if (name == "class" || name == "className")
                        {
                            if (!attribute || attribute.toLowerCase().indexOf(value) == -1)
                            {
                                allPropertiesFound = false;
                                break;
                            }
                        }
                        else if (!attribute || value != attribute.toLowerCase())
                        {
                            allPropertiesFound = false;
                            break;
                        }
                    }
                });
                
                if (allPropertiesFound)
                    filtered.push(element)
            });
            
            return filtered;
        }
        
        return elements;
    },
    
    SwapNodes: function(item1, item2)
	{
		// We need a clone of the node we want to swap
		var itemtmp = item1.cloneNode(1);

		// We also need the parentNode of the items we are going to swap.
		var parent = item1.parentNode;

		// First replace the second node with the copy of the first node
		// which returns a the new node
		item2 = parent.replaceChild(itemtmp,item2);

		// Then we need to replace the first node with the new second node
		parent.replaceChild(item2,item1);

		// And finally replace the first item with it's copy so that we
		// still use the old nodes but in the new order. This is the reason
		// we don't need to update our Behaviours since we still have
		// the same nodes.
		parent.replaceChild(item1,itemtmp);

		// Free up some memory, we don't want unused nodes in our document.
		itemtmp = null;
	}
    
    /*Object.prototype.isFunction: function(object)
    {
        return typeof object == "function";
    }

    Object.prototype.isString: function(object)
    {
        return typeof object == "string";
    }

    Object.prototype.isNumber: function(object)
    {
        return typeof object == "number";
    }

    Object.prototype.isUndefined: function(object)
    {
        return typeof object == "undefined";
    }*/
}

S.Event =
{
    add: function(element, name, handler, useCapture)
    {
		if (element == null)
		{
			S.Debugger.writeLine(S.Event.add.caller)
			return;
		}
    
        if (element.addEventListener)
        {
            element.addEventListener(name, handler, false, useCapture);
        }
        else
        {
            element.attachEvent("on" + name, handler);
        }
    },
    
    remove: function(element, name, handler, useCapture)
    {
        if (element.removeEventListener)
        {
            element.removeEventListener(name, handler, false, useCapture);
        }
        else
        {
            element.detachEvent("on" + name, handler);
        }
    },    
    
    wrap: function(event)
    {
        var sourceEvent = window.event || event;
        
        var output = { target: null };
        output.target = sourceEvent.target || sourceEvent.srcElement;
        output.keyCode = sourceEvent.keyCode;
        output.mouseButton = sourceEvent.button;
        output.mouseCoordinates = { x: sourceEvent.clientX, y: sourceEvent.clientY };
        
		if (output.target != null && output.target.nodeType == 3)
		    output.target = output.target.parentNode;
		    		    
		if (sourceEvent.preventDefault)
		{
		    output.preventDefault = function() { sourceEvent.preventDefault() };
		}
        else if (sourceEvent.cancelBubble != null)
        {
            output.preventDefault = function()
            {
	            sourceEvent.cancelBubble = true;
	            sourceEvent.returnValue = false;
	        };
        }
		    
	    return output;
    }
}

S.Element =
{
    getPosition: function(el, overflown)
    {
		overflown = overflown || [];
		
		var left = 0, top = 0;
		
		do {
			left += el.offsetLeft || 0;
			top += el.offsetTop || 0;
			el = el.offsetParent;
		} while (el);
		
		overflown.each(function(element)
		{
			left -= element.scrollLeft || 0;
			top -= element.scrollTop || 0;
		});
		
		return {'x': left, 'y': top};
	}
}

function ScorDebugger(messagesOutputDivID, toggleImageID, contentDivID, gfxFolder)
{
    this.messagesDiv = S.Get(messagesOutputDivID);
    this.toggleImage = S.Get(toggleImageID);
    this.contentDiv = S.Get(contentDivID);
    this.gfxFolder = gfxFolder;
    this.lineCount = 0;
    
    this.expandImageSource = "icon_expand_small.png";
    this.collapseImageSource = "icon_collaps_small.png";
    
    S.Event.add(this.toggleImage, "click", this.OnToggleImageClick.bind(this));
}

ScorDebugger.prototype.writeLine = function(message)
{
    if (this.lineCount == 100)
        this.messagesDiv.innerHTML = "";

    this.lineCount++;
    this.messagesDiv.innerHTML = message + "<br />" + this.messagesDiv.innerHTML;
}

ScorDebugger.prototype.OnToggleImageClick = function(message)
{
    var date = new Date();
	date.setTime(date.getTime()+(30 * 24 * 60 * 60 *1000));
		
    if (this.toggleImage.src.indexOf(this.expandImageSource) != -1)
    {
        this.toggleImage.src = this.gfxFolder + this.collapseImageSource;
        this.contentDiv.style.display = "block";
        
        document.cookie = "DebuggerEnabled=true; expires={0}; path=/".format(date.toGMTString());
    }
    else
    {
        this.toggleImage.src = this.gfxFolder + this.expandImageSource;
        this.contentDiv.style.display = "none";
        
        document.cookie = "DebuggerEnabled=false; expires={0}; path=/".format(date.toGMTString());
    }
}

// The following code will collide with Prototype.
if (typeof Prototype == "undefined")
{
    Function.prototype.bind = function()
    {
        if (arguments.length < 2 && arguments[0] === undefined)
            return this;

        var __method = this;
        var args = S.ToArray(arguments);
        var object = args.shift();
        
        return function()
        {
            return __method.apply(object, args.concat(S.ToArray(arguments)));
        }
    }

    Function.prototype.bindAsEventListener = function(object)
    {
        var __method = this;
        var args = S.ToArray(arguments);
        var object = args.shift();
        
        return function(event)
        {
            return __method.apply(object, [event].concat(args));
        }
    }

    Array.prototype.each = function(iterator, context)
    {
        var index = 0;
        iterator = iterator.bind(context);
        
        try
        {
            this._each(function(value)
            {
                iterator(value, index++);
            });
        }
        catch (e)
        {
            if (e != S.Break)
                throw e;
        }
        
        return this;
    }

    Array.prototype._each = function(iterator)
    {
        for (var i = 0, length = this.length; i < length; i++)
            iterator(this[i], i);
    }
    
    Array.prototype.some = function(iterator)
    {
        for (var i = 0, length = this.length; i < length; i++)
            if (iterator(this[i], i))
                return true;
                
        return false;
    }

    Array.prototype.remove = function(item)
    {
		var i = 0;
		var len = this.length;
		
		while (i < len)
		{
			if (this[i] === item)
			{
				this.splice(i, 1);
				len--;
			}
			else
			{
				i++;
			}
		}
		
		return this;
    }

    // Use native browser JS 1.6 implementation if available.
    if (Array.prototype.forEach)
        Array.prototype._each = Array.prototype.forEach;
        
    if (!Array.prototype.indexOf)
    {
        Array.prototype.indexOf = function(item, i)
        {
            i || (i = 0);
            var length = this.length;
            
            if (i < 0)
                i = length + i;

            for (; i < length; i++)
                if (this[i] === item)
                    return i;

            return -1;
        }
    };
}

if (!String.prototype.trim)
{
    String.prototype.trim = function()
    {
        return this.replace(/^\s+|\s+$/g, "");
    }
}

if (!String.prototype.format)
{
    String.prototype.format = function()
    {
        var value = this;

        var parameters = S.ToArray(arguments);

        if (parameters.length == 0)
            value;
        
        parameters.each(function(parameter, index)
        {
            var indexString = "{" + index + "}";

            if (value.indexOf(indexString) == -1)
                throw "String.format(): Index not found (" + index + ")";

            value = value.replace(indexString, parameter);
        });

        return value;
    }
}

// Server request.
function ServerRequest(method, clientOnSuccess, clientOnFailure)
{
    this.method = (method.toLowerCase() || "get");
    this.clientOnSuccess = clientOnSuccess || null;
    this.clientOnFailure = clientOnFailure || null;
    this.running = false;
        
    this.initializeTransport();
    S.Event.add(window, "unload", this.destroy.bind(this));
}

ServerRequest.prototype.initializeTransport = function()
{
    if (this.transport != null)
    {
        this.transport.onreadystatechange = null;
        this.transport = null;
    }
    
    // blnIE comes from Common.js
    this.transport = (window.XMLHttpRequest ? new XMLHttpRequest() : blnIE ? new ActiveXObject('Microsoft.XMLHTTP') : null);
    
    if (this.transport == null)
        throw "ServerRequest(...): Unable to find transport, aborting.";
}

ServerRequest.prototype.send = function(url, data)
{
    if (this.running)
        return;
        
    // Merge url and data if it's a GET request.
    if (this.method == "get" && data != null)
    {
        url = url + (url.indexOf("?") != -1 ? "&" : "?") + data;
        data = null;
    }
    
    try
    {
		// Prevent cache of the output by adding a date to each request.
		url = url + (url.indexOf("?") != -1 ? "&" : "?") + "Date={0}".format((new Date().valueOf()));
	    
		this.running = true;
		this.transport.open(this.method.toUpperCase(), url, true);
		this.transport.onreadystatechange = this.onReadyStateChange.bind(this);
		this.transport.send(data);
	}
	catch (ex)
	{
		S.Debugger.writeLine("ServerRequest.send error: " + ex);
	}
}

ServerRequest.prototype.cancel = function()
{
    if (!this.running)
        return;
        
    this.running = false;
    
	this.transport.abort();
	this.transport.onreadystatechange = null;
	
	this.initializeTransport();
}

ServerRequest.prototype.destroy = function()
{
    this.transport.abort();
    this.transport.onreadystatechange = null;
    this.transport = null;
}

ServerRequest.prototype.onSuccess = function()
{
    if (this.clientOnSuccess != null)
    {
        // Return output dependent on the content type.
        var contentTypeHeader = this.transport.getResponseHeader("Content-Type").toLowerCase();
        var output = null;
        
        if (contentTypeHeader.indexOf("application/json") != -1)
        {
            try
            {
                output = eval("(" + this.transport.responseText + ")");
            }
            catch (ex)
            {
                S.Debugger.writeLine(ex);
            }
        }
        
        this.clientOnSuccess(output);
    }
}

ServerRequest.prototype.onFailure = function()
{
    if (this.clientOnFailure != null)
        this.clientOnFailure();
}

ServerRequest.prototype.onReadyStateChange = function()
{
    if (this.transport.readyState != 4 || !this.running)
        return;
        
    this.running = false;
    var status = 0;
    
    // Sometimes an exception is thrown when accessing the status property.
	try
	{
	    status = this.transport.status;
	}
	catch (ex)
	{
	    S.Debugger.writeLine("ServerRequest.onReadyStateChange status error: " + ex);
	};
	
	if (status == 200)
	    this.onSuccess();
	else
	    this.onFailure();
	    
    this.transport.onreadystatechange = null;
}

// String builder.
function StringBuilder()
{
	this.length = 0;
	this.parts = [];
}

StringBuilder.prototype.append = function(newString)
{
	this.parts[this.length++] = newString;
}

StringBuilder.prototype.getValue = function(separator)
{
    if (separator != null)
        return this.parts.join(separator);
        
	return this.parts.join("");
}

StringBuilder.prototype.getReverted = function()
{
	return this.parts.reverse().join("");
}

StringBuilder.prototype.destroy = function()
{
	this.parts = null;
}
