﻿if (!window.Hof)
{
    window.Hof = {};
};

Hof.Undefined;

Hof.IsNullOrUndefined = function (value)
{
    return (value == null || value == Hof.Undefined);
};

Hof.NullIndex = -1;


/************************************************************************************************************
String class
************************************************************************************************************/
Hof.String = function ()
{
};

Hof.String.Format = function (s)
{
    if (arguments.length == 0)
    {
        return "";
    }

    if (arguments.length == 1)
    {
        return s;
    }

    for (var i = 0, c = arguments.length - 1; i < c; i++)
    {
        s = s.replace(new RegExp("\\{" + i + "\\}", "g"), arguments[i + 1]);
    }

    return s;
};

Hof.String.IsNullOrEmpty = function (s)
{
    return (s == null || s.toString().length == 0);
}

Hof.String.PadLeft = function (str, chr, length)
{
    if (Hof.String.IsNullOrEmpty(str))
    {
        return str;
    }

    str = str.toString();
    while (str.length < length)
    {
        str = chr + str;
    }

    return str;
}

Hof.String.PadRight = function (str, chr, length)
{
    if (Hof.String.IsNullOrEmpty(str))
    {
        return str;
    }

    str = str.toString();
    while (str.length < length)
    {
        str += chr;
    }

    return str;
}

Hof.String.Empty = "";

String.Format = Hof.String.Format;
String.IsNullOrEmpty = Hof.String.IsNullOrEmpty;

/************************************************************************************************************
Register namespace
************************************************************************************************************/
if (!window.Hof.Collections)
{
    window.Hof.Collections = {};
};

/************************************************************************************************************
Collection object
************************************************************************************************************/
Hof.Collections.Collection = function (a)
{
    this._array = null;
    if (!a)
    {
        this._array = [];
    }
    else
    {
        if (!a._array)
        {
            this._array = a;
        }
        else
        {
            this._array = a._array;
        }
    }
};

/*
Add item at the end of the collection
*/
Hof.Collections.Collection.prototype.Add = function (item)
{
    this._array.push(item);
};

/*
Join all items in the collection
*/
Hof.Collections.Collection.prototype.Join = function (delimiter)
{
    return this._array.join(delimiter);
};

/*
Clear items in array
*/
Hof.Collections.Collection.prototype.Clear = function ()
{
    this._array = [];
};

/*
Number of items in collection
*/
Hof.Collections.Collection.prototype.Count = function ()
{
    return this._array.length;
}

/*
Find item in collection
*/
Hof.Collections.Collection.prototype.Find = function (predicate)
{
    for (var i = 0, l = this._array.length; i < l; i++)
    {
        if (predicate && predicate(this._array[i]))
        {
            return this._array[i];
        }
    }

    return null;
};

/*
Iterate items in collection
*/
Hof.Collections.Collection.prototype.ForEach = function (action)
{
    for (var i = 0, c = this._array.length; i < c; i++)
    {
        if (action)
        {
            action(this._array[i])
        }
    }
};

/*
Get item from collection
*/
Hof.Collections.Collection.prototype.Get = function (index)
{
    return this._array[index];
};

/*
Index of item
*/
Hof.Collections.Collection.prototype.IndexOf = function (item)
{
    for (var i = 0, l = this._array.length - 1; i < l; i++)
    {
        if (this._array[i] == item)
        {
            return i;
        }
    }

    return Hof.NullIndex;
};

/*
Insert item
*/
Hof.Collections.Collection.prototype.Insert = function (index, item)
{
    if (index >= this._array.length)
    {
        this._array.push(item);
    }
    else
    {
        this._array.splice(index, 0, item);
    }
};

/*
Set item in collection
*/
Hof.Collections.Collection.prototype.Set = function (index, value)
{
    this._array[index] = value;
}

/*
Sort collection
*/
Hof.Collections.Collection.prototype.Sort = function (predicate)
{
    if (predicate)
    {
        this._array.sort(predicate);
    }
    else
    {
        this._array.sort();
    }
};

/*
Swap items in collection
*/
Hof.Collections.Collection.prototype.Swap = function (index1, index2)
{
    var item = this._array[index1];
    this._array[index1] = this._array[index2];
    this._array[index2] = item;
};

/*
    Reverse collection
*/
Hof.Collections.Collection.prototype.Reverse = function ()
{
    this._array = this._array.reverse();
};

/************************************************************************************************************
    NameValueCollection object
************************************************************************************************************/
Hof.Collections.NameValueCollection = function ()
{
    this.Collection = new Hof.Collections.Collection();
};

Hof.Collections.NameValueCollection.prototype.Add = function (name, value)
{
    var item = { Name: name, Value: value };
    this.Collection.Add(item);
    return item;
};

Hof.Collections.NameValueCollection.prototype.Count = function ()
{
    return this.Collection.Count();
};

Hof.Collections.NameValueCollection.prototype.FindByName = function (name)
{
    return this.Collection.Find(
        function (i)
        {
            return i.Name == name;
        });
};

Hof.Collections.NameValueCollection.prototype.ForEach = function (action)
{
    this.Collection.ForEach(
        function (i)
        {
            action(i, i.Name, i.Value);
        });
}

Hof.Collections.NameValueCollection.prototype.Get = function (name)
{
    var item = this.FindByName(name);
    if (item == null)
    {
        return null;
    }

    return item.Value;
};

Hof.Collections.NameValueCollection.prototype.Set = function (name, value)
{
    var item = this.FindByName(name);
    if (item == null)
    {
        item = { Name: name, Value: value };
        this.Collection.Add(item);
    }
    else
    {
        item.Value = value;
    }

    return item;
};

/************************************************************************************************************
    Stack object
************************************************************************************************************/
Hof.Collections.Stack = function ()
{
    this._data = [];
}

Hof.Collections.Stack.prototype.Count = function ()
{
    return this._data.length;
}

Hof.Collections.Stack.prototype.Push = function (item)
{
    this._data.push(item);
}

Hof.Collections.Stack.prototype.Pop = function ()
{
    if (this._data.length <= 0)
    {
        return null;
    }

    return this._data.pop();
}

Hof.Collections.Stack.prototype.Peek = function ()
{
    if (this._data.length <= 0)
    {
        return null;
    }

    return this._data[this._data.length - 1];
}

/************************************************************************************************************
    Register namespace
************************************************************************************************************/
if (!window.Hof.Text)
{
    window.Hof.Text = {};
};

/************************************************************************************************************
    Constantes
************************************************************************************************************/
Hof.Text.Eol = "\r\n";

/************************************************************************************************************
    StringBuilder class
************************************************************************************************************/
Hof.Text.StringBuilder = function ()
{
    this._data = new Hof.Collections.Collection();
};

Hof.Text.StringBuilder.prototype.Append = function (value)
{
    this._data.Add("" + value);
};

Hof.Text.StringBuilder.prototype.AppendLine = function (value)
{
    this._data.Add("" + value);
    this._data.Add(Hof.Text.Eol);
};

Hof.Text.StringBuilder.prototype.Clear = function ()
{
    this._data.Clear();
};

Hof.Text.StringBuilder.prototype.ToString = function ()
{
    return this._data.Join("");
};

/************************************************************************************************************
    Register namespace(s)
************************************************************************************************************/
if (!window.Hof.Xml)
{
    window.Hof.Xml = {};
};

Hof.Xml.Encode = function (value)
{
    return value.toString().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

/************************************************************************************************************
    Xml writer class
************************************************************************************************************/
Hof.Xml.XmlWriter = function (settings)
{
    this.Settings = settings || new Hof.Xml.XmlWriterSettings();

    this.Stack = new Hof.Collections.Stack();
    this.Elements = new Hof.Collections.Collection();
};

Hof.Xml.XmlWriter.prototype.Current = function ()
{
    return this.Stack.Peek();
};

Hof.Xml.XmlWriter.prototype.StartElement = function (name, value, namespace)
{
    var current = this.Current(), elm = new Hof.Xml.XmlWriterElement(name, value, namespace);
    if (current == null)
    {
        this.Elements.Add(elm);
    }
    else
    {
        current.Elements.Add(elm);
    }

    this.Stack.Push(elm);
};

Hof.Xml.XmlWriter.prototype.EndElement = function ()
{
    this.Stack.Pop();
};

Hof.Xml.XmlWriter.prototype.WriteAttribute = function (name, value)
{
    var current = this.Current();
    if (current == null)
    {
        throw new Error("No active element");
    }

    current.AddAttribute(name, value);
};

Hof.Xml.XmlWriter.prototype.WriteNamespace = function (name, value)
{
    var current = this.Current();
    if (current == null)
    {
        throw new Error("No active element");
    }

    current.AddNamespace(name, value);
};

Hof.Xml.XmlWriter.prototype.WriteValue = function (value)
{
    var current = this.Current();
    if (current == null)
    {
        throw new Error("No active element");
    }

    current.Value = value;
};

Hof.Xml.XmlWriter.prototype.WriteElement = function (name, value, namespace)
{
    this.StartElement(name, value, namespace);
    this.EndElement();
};

Hof.Xml.XmlWriter.prototype.ToString = function ()
{
    var xml = new Hof.Text.StringBuilder();

    if (this.Settings.Header)
    {
        xml.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        xml.Append(this.Settings.WriteEndOfLine());
    }

    for (var i = 0, c = this.Elements.Count(); i < c; i++)
    {
        xml.Append(this.Elements.Get(i).ToString(this.Settings, 0));
    }

    return xml.ToString();
};

/************************************************************************************************************
    Xml writer element class
************************************************************************************************************/
Hof.Xml.XmlWriterElement = function (name, value, namespace)
{
    this.Attributes = new Hof.Collections.NameValueCollection();
    this.Namespaces = new Hof.Collections.NameValueCollection();

    this.Elements = new Hof.Collections.Collection();

    this.Name = name;

    this.Value = null;
    if (arguments.length > 1)
    {
        this.Value = value;
    }

    this.Namespace = null;
    if (arguments.length > 2)
    {
        this.Namespace = namespace;
    }
};

Hof.Xml.XmlWriterElement.prototype.AddAttribute = function (name, value)
{
    this.Attributes.Set(name, value);
};

Hof.Xml.XmlWriterElement.prototype.AddNamespace = function (name, value)
{
    this.Namespaces.Set(name, value);
};

Hof.Xml.XmlWriterElement.prototype.ToString = function (settings, indent)
{
    var xml = new Hof.Text.StringBuilder();
    xml.Append(settings.WriteIndent(indent));
    xml.Append("<");
    if (!String.IsNullOrEmpty(this.Namespace))
    {
        xml.Append(String.Format("{0}:", this.Namespace));
    }

    xml.Append(this.Name);

    this.Namespaces.ForEach(
        function (i, n, v)
        {
            v = String.IsNullOrEmpty(v) ? "" : v.toString().replace("\"", "\\\"");
            xml.Append(" xmlns");
            if (!String.IsNullOrEmpty(n))
            {
                xml.Append(String.Format(":{0}", n));
            }
            xml.Append(String.Format("=\"{0}\"", v));
        });

    this.Attributes.ForEach(
        function (i, n, v)
        {
            xml.Append(String.Format(" {0}=\"{1}\"", n, String.IsNullOrEmpty(v) ? "" : v.toString().replace("\"", "\\\"")));
        });

    if (this.Elements.Count() > 0)
    {
        xml.Append(">");
        xml.Append(settings.WriteEndOfLine());

        for (var i = 0, c = this.Elements.Count(); i < c; i++)
        {
            xml.Append(this.Elements.Get(i).ToString(settings, indent + 1));
        }

        xml.Append(settings.WriteIndent(indent));

        xml.Append("</");
        if (!String.IsNullOrEmpty(this.Namespace))
        {
            xml.Append(String.Format("{0}:", this.Namespace));
        }
        xml.Append(String.Format("{0}>", this.Name));
    }
    else
    {
        if (!String.IsNullOrEmpty(this.Value))
        {
            xml.Append(String.Format(">{0}</", Hof.Xml.Encode(this.Value)));
            if (!String.IsNullOrEmpty(this.Namespace))
            {
                xml.Append(String.Format("{0}:", this.Namespace));
            }
            xml.Append(String.Format("{0}>", this.Name));
        }
        else
        {
            xml.Append(" />");
        }
    }

    xml.Append(settings.WriteEndOfLine());
    return xml.ToString();
};

/************************************************************************************************************
    Xml writer settings class
************************************************************************************************************/
Hof.Xml.XmlWriterSettings = function ()
{
    this.EndOfLine = false;         //  Write end of line

    this.Header = false;            //  Write xml header

    this.Indent = false;            //  Write indent
    this.IndentSize = 4;            //  Indent size
};

Hof.Xml.XmlWriterSettings.prototype.WriteIndent = function (value)
{
    var indent = "";
    if (this.Indent)
    {
        var max = this.IndentSize * value;
        while (indent.length < max)
        {
            indent += " ";
        }
    }

    return indent;
};

Hof.Xml.XmlWriterSettings.prototype.WriteEndOfLine = function ()
{
    if (this.EndOfLine)
    {
        return Hof.Text.Eol;
    }

    return "";
};

/************************************************************************************************************
    Register namespace(s)
************************************************************************************************************/
if (!window.Hof.Web)
{
    window.Hof.Web = {};
};

/************************************************************************************************************
    Wsdl cache
************************************************************************************************************/
Hof.Web.Wsdl = {
    Cache: {},                              //  Webservice cache

    Get: function (name)
    {
        // Check cached wsdl
        var ws = Hof.Web.Wsdl.Cache[name.toLowerCase()];
        if (!Hof.IsNullOrUndefined(ws))
        {
            return ws;
        }

        // Service not found, load wsdl (sync)
        var settings = {
            async: false,
            contentType: "text/xml; charset=utf-8",
            dataType: "xml",
            type: "GET",
            url: name + "?wsdl"
        };

        // Create and cache wsdl
        var response = $.ajax(settings);
        if (response && response.responseXML)
        {
            ws = Hof.Web.Wsdl.Cache[name.toLowerCase()] = new Hof.Web.Wsdl.WebService(response.responseXML);
        }

        return ws;
    }
}

/************************************************************************************************************
    Wsdl web service, web service
************************************************************************************************************/
Hof.Web.Wsdl.WebService = function (response)
{
    this.Wsdl = $(response);                                                                    //  Wsdl
    this.Namespace = this.Wsdl.find("[nodeName='wsdl:definitions']").attr("targetNamespace");   //  Namespace
    this.ComplexTypes = [];                                                                     //  Complex types (With a name attribute)
    this.SimpleTypes = [];                                                                      //  Simple types (With a name attribute)
    this.Methods = {};                                                                          //  Wsdl web method cache

    var this_ = this;
    this.Wsdl.find("[nodeName='s:complexType']").each(
        function ()
        {
            var name = $(this).attr("name");
            if (name)
            {
                this_.ComplexTypes[name] = { Name: name, Value: $(this), Elements: null };
            }
        });

    this.Wsdl.find("[nodeName='s:simpleType']").each(
        function ()
        {
            var name = $(this).attr("name");
            if (name)
            {
                this_.SimpleTypes[name] = { Name: name, Value: $(this), Type: null };
            }
        });
}

Hof.Web.Wsdl.WebService.prototype.Get = function (methodName)
{
    // Check cached methods
    var method = this.Methods[methodName.toLowerCase()];
    if (!Hof.IsNullOrUndefined(method))
    {
        return method;
    }

    // Method not found, create new method 
    method = new Hof.Web.Wsdl.Method(this, methodName);

    this.Wsdl.find("[nodeName='s:schema']").find("[nodeName='s:element']").each(
        function ()
        {
            if ($(this).attr("name") == methodName)
            {
                method.Call = $(this).find("[nodeName='s:complexType']").find("[nodeName='s:sequence']").find("[nodeName='s:element']");
            }
            else if ($(this).attr("name") == methodName + "Response")
            {
                method.Response = $(this).find("[nodeName='s:complexType']").find("[nodeName='s:sequence']").find("[nodeName='s:element']");
            }
        });

    // Check if method exists in wsdl
    if (method.Call == null && method.Response == null)
    {
        throw new Error(String.Format("Webmethod : '{0}' doesn't exist in the wsdl.", methodName));
    }

    return this.Methods[methodName.toLowerCase()] = method;
}

/************************************************************************************************************
    Wsdl web service, method
************************************************************************************************************/
Hof.Web.Wsdl.Method = function (service, name)
{
    this.Service = service;             //  Reference to the wsdl web service
    this.Name = name;                   //  Method name
    this.Call = null;                   //  Invoke arguments
    this.Response = null;               //  Return type
}

Hof.Web.Wsdl.Method.prototype.Deserialize = function (data)
{
    if (this.Response.length == 0)
    {
        return null;
    }

    return Hof.Web.Wsdl.Serializer.Deserialize(this, this.Response, data);
}

Hof.Web.Wsdl.Method.prototype.Serialize = function (parm)
{
    var xws = new Hof.Xml.XmlWriterSettings();
    xws.Header = true; // xws.EndOfLine = xws.Indent = true;

    var xw = new Hof.Xml.XmlWriter(xws);

    xw.StartElement("Envelope", null, "soap");
    xw.WriteNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
    xw.WriteNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
    xw.WriteNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
    xw.StartElement("Body", null, "soap");
    xw.StartElement(this.Name);
    xw.WriteNamespace("", this.Service.Namespace);

    var this_ = this;
    this.Call.each(
            function ()
            {
                var name = $(this).attr("name"), value = parm.Data[name];
                if (!Hof.IsNullOrUndefined(value))
                {
                    xw.StartElement(name);
                    Hof.Web.Wsdl.Serializer.Serialize(this_, $(this), value, xw);
                    xw.EndElement();
                }
            });

    xw.EndElement();
    xw.EndElement();
    xw.EndElement();

    var xml = xw.ToString();
    //alert(xml);
    return xml;
}

/************************************************************************************************************
    Wsdl serializer / deserializer
************************************************************************************************************/
Hof.Web.Wsdl.Serializer = {
    Deserialize: function (method, type, data)
    {
        function GetNodeValue(data, defaultValue)
        {
            var result = defaultValue;

            if (data != null && data.length > 0)
            {
                if (data[0].childNodes.length > 0)
                {
                    if (data[0].childNodes.length > 1 && data[0].childNodes[0].nodeName == "#text")
                    {
                        // Browser splitted, combine again
                        result = "";
                        for (var i = 0, l = data[0].childNodes.length; i < l; i++)
                        {
                            result += data[0].childNodes[i].nodeValue;
                        }
                    }
                    else
                    {
                        result = data[0].childNodes[0].nodeValue;
                    }
                }
            }

            return result;
        }

        type = Hof.Web.Wsdl.Serializer.GetType(type);
        switch (type.toLowerCase())
        {
            case "long":                //  Treat long / unsigned long as string : js doesn't support Int64
            case "unsignedlong":
            case "string":
                return GetNodeValue(data, "");
            case "int":
            case "integer":
            case "negativeinteger":
            case "positiveinteger":
            case "short":
            case "unsignedshort":
            case "byte":
            case "unsignedbyte":
                var v = GetNodeValue(data, null);
                if (!v)
                {
                    return null;
                }
                return parseInt(v);
            case "double":
            case "float":
                var v = GetNodeValue(data, null);
                if (!v)
                {
                    return null;
                }
                return parseFloat(v);
            case "boolean":
                return GetNodeValue(data, "false").toLowerCase() == "true";
            case "time":
            case "timestamp":
                throw new Error("Time / timestamp not supported.");
            case "datetime":
                if (data.length == 0)
                {
                    return null;
                }

                data = GetNodeValue(data, "");
                data = data.substring(0, (data.lastIndexOf(".") < 0 ? data.length : data.lastIndexOf(".")));
                data = data.replace(/T/g, " ").replace(/-/g, "/");

                var d = new Date();
                d.setTime(Date.parse(data));
                return d;
            default:
                if (data.length == 0)
                {
                    return null;
                }

                // Is it a simple type?
                var st = Hof.Web.Wsdl.Serializer.GetSimpleType(method, type);
                if (st != null)
                {
                    return Hof.Web.Wsdl.Serializer.Deserialize(method, st, data);
                }

                // Get complex type elements
                var result = null, elms = Hof.Web.Wsdl.Serializer.GetComplexElements(method, type);
                if (elms.length == 0)
                {
                    throw new Error(String.Format("Type '{0}' not supported.", type));
                }
                else if (elms.length == 1 && elms.attr("maxOccurs") == "unbounded")
                {
                    // Array
                    result = [];
                    data.children().each(
                        function ()
                        {
                            result.push(Hof.Web.Wsdl.Serializer.Deserialize(method, elms, $(this)));
                        });
                }
                else
                {
                    // Object
                    result = {};
                    elms.each(
                        function ()
                        {
                            var name = $(this).attr("name"), val = data.children(name);
                            if (val.length > 0)
                            {
                                result[name] = Hof.Web.Wsdl.Serializer.Deserialize(method, $(this), val);
                            }
                        });
                }

                return result;
        }
    },

    Serialize: function (method, type, data, writer)
    {
        type = Hof.Web.Wsdl.Serializer.GetType(type);
        switch (type.toLowerCase())
        {
            case "string":
                if (!Hof.IsNullOrUndefined(data))
                {
                    writer.WriteValue(data.toString());
                }
                break;
            case "long":                //  Treat long / unsigned long : js doesn't support Int64
            case "unsignedlong":

            case "int":
            case "integer":
            case "negativeinteger":
            case "positiveinteger":
            case "short":
            case "unsignedshort":
            case "byte":
            case "unsignedbyte":

            case "double":
            case "float":

            case "boolean":
                if (!Hof.IsNullOrUndefined(data) && ("" + data).length > 0)
                {
                    writer.WriteValue(data.toString());
                }
                break;
            case "time":
            case "timestamp":
                throw new Error("Time / timestamp not supported.");
            case "datetime":
                var date = String.Format("{0}-{1}-{2}", data.getFullYear(), Hof.String.PadLeft(data.getMonth() + 1, "0", 2), Hof.String.PadLeft(data.getDate(), "0", 2));
                date += String.Format("T{0}:{1}:{2}", Hof.String.PadLeft(data.getHours(), "0", 2), Hof.String.PadLeft(data.getMinutes(), "0", 2), Hof.String.PadLeft(data.getSeconds(), "0", 2));
                date += String.Format(".{0}", data.getMilliseconds());
                date += String.Format("+{0}:00", Hof.String.PadLeft(-data.getTimezoneOffset() / 60, "0", 2));
                writer.WriteValue(date);
                break;
            default:
                // Is it a simple type?
                var st = Hof.Web.Wsdl.Serializer.GetSimpleType(method, type);
                if (st != null)
                {
                    Hof.Web.Wsdl.Serializer.Serialize(method, st, data, writer);
                    return;
                }

                // Get complex type elements
                var elms = Hof.Web.Wsdl.Serializer.GetComplexElements(method, type);
                if (elms.length == 0)
                {
                    throw new Error(String.Format("Type '{0}' not supported.", type));
                }
                else if (elms.length == 1 && elms.attr("maxOccurs") == "unbounded")
                {
                    // Array, get type and iterate items in array
                    type = Hof.Web.Wsdl.Serializer.GetType(elms);
                    $.each(data,
                        function (i, v)
                        {
                            writer.StartElement(type);
                            Hof.Web.Wsdl.Serializer.Serialize(method, elms, v, writer);
                            writer.EndElement();
                        });
                }
                else
                {
                    // Object, iterate object properties
                    elms.each(
                        function ()
                        {
                            var name = $(this).attr("name"), val = data[name];
                            if (!Hof.IsNullOrUndefined(val))
                            {
                                writer.StartElement(name);
                                Hof.Web.Wsdl.Serializer.Serialize(method, $(this), val, writer);
                                writer.EndElement();
                            }
                        });
                }

                break;
        }
    },

    GetComplexElements: function (method, typeName)
    {
        // Get the elements, if cached
        var result = method.Service.ComplexTypes[typeName].Elements;
        if (result == null)
        {
            // Elements not cached, get elements and cache them
            var type = method.Service.ComplexTypes[typeName].Value, ext = null;
            result = type.find("[nodeName='s:element']")

            // Add nested (inherited) elements
            while ((ext = type.find("[nodeName='s:extension']")).length > 0)
            {
                type = method.Service.ComplexTypes[Hof.Web.Wsdl.Serializer.GetType(ext)].Value;
                result = result.add(type.find("[nodeName='s:element']"));
            }

            method.Service.ComplexTypes[typeName].Elements = result;
        }

        return result;
    },

    GetSimpleType: function (method, typeName)
    {
        // Simple type exists?
        var type = method.Service.SimpleTypes[typeName]
        if (Hof.IsNullOrUndefined(type))
        {
            return null;
        }

        // We already have the type?
        var result = type.Type;
        if (result == null)
        {
            result = type.Value.find("[nodeName='s:restriction']");
            if (result.length <= 0)
            {
                throw new Error(String.Format("Unable to determine type of '{0}'.", typeName));
            }

            // Cache the type
            type.Type = result;
        }

        return result;
    },

    GetType: function (type)
    {
        var t = type.attr("type");
        if (Hof.IsNullOrUndefined(t))
        {
            t = type.attr("base");
        }

        var colon = t.indexOf(":");
        t = (colon < 0) ? t : t.substring(colon + 1);
        return t;
    }
};

/************************************************************************************************************
    Ajax
************************************************************************************************************/
Hof.Web.Ajax =
{
    // Invoke ajax call
    Invoke: function (service, method, parms, async, callback)
    {
        // Validate parameters
        parms = Hof.IsNullOrUndefined(parms) ? new Hof.Web.Ajax.Parameters() : parms;

        // Find / get service
        var wsdlService = Hof.Web.Wsdl.Get(service);
        if (Hof.IsNullOrUndefined(wsdlService))
        {
            throw new Error(String.Format("Unable to find service / load wsdl ({0})", service));
        }

        // Find / get method
        var wsdlMethod = wsdlService.Get(method);

        // Show status indicator
        Hof.Web.Ajax.StatusIndicator.Show();

        var result = null, options = {
            async: async,
            type: "POST",
            url: service,
            data: wsdlMethod.Serialize(parms),
            beforeSend: function (xhr, settings)
            {
                xhr.setRequestHeader("SOAPAction", wsdlService.Namespace + (wsdlService.Namespace[wsdlService.Namespace.length - 1] != '/' ? "/" : "") + method);
            },
            contentType: "text/xml; charset=utf-8",
            dataType: "xml",
            success: function (data)
            {
                // Hide status indicator
                Hof.Web.Ajax.StatusIndicator.Hide();

                result = Hof.Web.Ajax.Response.Success(wsdlMethod, data, callback);

                // Invoke global callback
                if (!Hof.IsNullOrUndefined(Hof.Web.Ajax.Callback.OnSuccess))
                {
                    Hof.Web.Ajax.Callback.OnSuccess(result);
                }

                // Invoke callback with result
                if (callback)
                {
                    callback(result.Data);
                }
            },
            error: function (data)
            {
                // Hide status indicator
                Hof.Web.Ajax.StatusIndicator.Hide();

                result = Hof.Web.Ajax.Response.Error(wsdlMethod, data, callback);

                // Invoke global callback
                if (!Hof.IsNullOrUndefined(Hof.Web.Ajax.Callback.OnError))
                {
                    Hof.Web.Ajax.Callback.OnError(result);
                }
                else
                {
                    if (result.Message.indexOf("SessionTimeoutException") >= 0)
                    {
                        alert(Language.Timeout_GoingToRefresh);

                        document.location.reload();

                        return;
                    }
                    else if (result.Message.indexOf("SecurityException") >= 0 || result.Message.indexOf("AuthorizationError") >= 0)
                    {
                        document.location = BaseSiteUrl + "inloggen?status=expired";

                        return;
                    }

                    // IE bug : Doesn't redirect to loginUrl (web.config) instead the redirect page is the result of the webmethod call
                    if (data.status == 504 || result.Message.match(/<[\s]*![\s]*DOCTYPE/gim))
                    {
                        document.location = BaseSiteUrl + "inloggen?status=expired";

                        return;
                    }

                    throw new Error(result.Message);
                }
            }
        };

        // Invoke webservice
        $.ajax(options);
        if (async)
        {
            // Async call, return null
            return null;
        }

        // Sync call, return result
        return result == null ? null : result.Data;
    },

    // Global callback
    Callback:
    {
        OnError: null,
        OnSuccess: null
    },

    // Ajax response
    Response:
    {
        Data: function (callback, success, status, message, data)
        {
            return { Success: success, Status: status, Message: message, Data: data };
        },

        Error: function (method, data, callback)
        {
            var msg = "Internal server error.", status = 0;
            if (data)
            {
                if (data.responseXML)
                {
                    var nl = $(data.responseXML).find("faultstring");
                    if (nl.length > 0)
                    {
                        msg = nl[0].childNodes[0].nodeValue;
                    }
                    else
                    {
                        msg = data.responseText;
                    }
                }

                status = data.status;
            }
            else
            {
                msg = "Internal server error or timeout.";
                status = 500;
            }

            return Hof.Web.Ajax.Response.Data(callback, false, status, msg, null);
        },

        Success: function (method, data, callback)
        {
            var d = null, result = $(data).find(method.Name + "Result");
            if (result.length == 1)
            {
                d = method.Deserialize(result);
            }

            return Hof.Web.Ajax.Response.Data(callback, true, 200, "", d);
        }
    },

    // Ajax status indicator
    StatusIndicator:
    {
        counter: 0,
        elementId: "hsStatusIndicator",

        Busy: function ()
        {
            return (Hof.Web.Ajax.StatusIndicator.counter > 0);
        },

        Hide: function ()
        {
            Hof.Web.Ajax.StatusIndicator.counter--;
            if (Hof.Web.Ajax.StatusIndicator.counter <= 0)
            {
                var si = $("#" + Hof.Web.Ajax.StatusIndicator.elementId);
                if (si.length > 0)
                {
                    si.hide();
                }

                if (document.body)
                {
                    document.body.style.cursor = "";
                }

                Hof.Web.Ajax.StatusIndicator.counter = 0;
            }
        },

        Show: function ()
        {
            Hof.Web.Ajax.StatusIndicator.counter++;
            if (Hof.Web.Ajax.StatusIndicator.counter > 0)
            {
                var si = $("#" + Hof.Web.Ajax.StatusIndicator.elementId);
                if (si.length > 0)
                {
                    si.show();
                }

                if (document.body)
                {
                    document.body.style.cursor = "wait";
                }
            }
        }
    }
}

/************************************************************************************************************
    Ajax parameter class
************************************************************************************************************/
Hof.Web.Ajax.Parameters = function ()
{
    this.Data = [];
};

Hof.Web.Ajax.Parameters.prototype.Add = function (name, value)
{
    this.Data[name] = value;
}

/************************************************************************************************************
    Ajax call shortcut
************************************************************************************************************/
function $Ajax(service, method, parms, async, callback, onError)
{
    return Hof.Web.Ajax.Invoke(service, method, parms, async, callback, onError);
};

function $AjaxParameters()
{
    return new Hof.Web.Ajax.Parameters();
}


/************************************************************************************************************
    Old soap client interface
************************************************************************************************************/

function isSoapClientBusy()
{
    return Hof.Web.Ajax.StatusIndicator.Busy();
}

function SOAPClientParameters()
{
    Hof.Web.Ajax.Parameters.call(this);
}

SOAPClientParameters.prototype = new Hof.Web.Ajax.Parameters();

SOAPClientParameters.prototype.add = function (name, value)
{
    this.Add(name, value);
}

var SOAPClient =
{
    invoke: function (url, method, parameters, async, callback)
    {
        return $Ajax(url, method, parameters, async, callback);
    }
};

/************************************************************************************************************
That's all folks
************************************************************************************************************/

