﻿/// <reference path="MicrosoftLibrary.js" />

/*
/// <summary></summary>
/// <param name="void"></param>
/// <returns>void</returns>
*/

/// <summary>Vårat root namespace</summary>
window.Capitex = { __namespace: true, __typeName: "Capitex", getName: function() { return "Capitex" }, __upperCaseTypes: {} }
Capitex.__rootNamespaces = [Capitex];

/// <summary>Lägger till ett objekt i arrayen</summary>
/// <param name="item">objektet som ska läggas till</param>
Array.prototype.add = function(item) {
    Array.add(this, item);
}

/// <summary>Kontrollerar om en sträng är tom</summary>
/// <param name="str">Strängen som ska kontrolleras</param>
/// <returns>En boolean som indikerar ifall strängen är tom</returns>
String.isNullOrEmpty = function(str) { return typeof str === "undefined" || str === null || str === ""; }

/// <summary>Skriver ut ett debug meddelande sist på sidan</summary>
/// <param name="message">Meddelandet som ska skrivas ut</param>
/// <returns>void</returns>
document.writeDebug = function(message) {
    document.body.innerHTML += message;
}

/// <summary>Skriver ut alla fält och metoder på ett objekt</summary>
/// <param name="obj">Objektet som undersökas</param>
/// <param name="inline">Om objektet ska skrivas ut på anropningsplatsen</param>
/// <returns>void</returns>
document.writeObject = function(obj, inline) {
    var message = "<table class='debugObjectTable'>";
    for (var item in obj)
        message += "<tr class='debugObjectRow'><td class='debugObjectColumnId'>" + String(item) + "</td><td class='debugObjectColumnValue'>" + obj[item] + "</td></tr>";

    message += "</table>\r\n";

    if (inline)
        document.write(message);
    else
        document.body.innerHTML += message;
}

/// <summary>Klass som representerar en delegatlista för eventhantering</summary>
Capitex.EventDelegateList = function() {
    this.items = [];
}

/// <summary>Lägger till en delegat i listan</summary>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
/// <returns>Objektet som lades till</returns>
Capitex.EventDelegateList.prototype.add = function(instance, method) {
    var item = new Capitex.DelegateItem(instance, method);
    this.items[this.items.length] = item;
    return item;
}

/// <summary>Tar bort en delegat i listan</summary>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
/// <returns>void</returns>
Capitex.EventDelegateList.prototype.remove = function(instance, method) {
    var index = this.indexOf(instance, method);
    if (index >= 0)
        this.removeAt(index);
}

/// <summary>Tar bort en delegat i listan</summary>
/// <param name="item">Delegatitemet som ska tas bort</param>
/// <returns>void</returns>
Capitex.EventDelegateList.prototype.removeItem = function(item) {
    for (var index = 0; index < this.items.length; index++) {
        if (this.items[index] === item) {
            this.removeAt(index);
            return;
        }
    }
}

/// <summary>Tar bort en delegat i listan</summary>
/// <param name="index">Indexet på delegaten som ska tas bort</param>
/// <returns>void</returns>
Capitex.EventDelegateList.prototype.removeAt = function(index) {
    this.items.splice(index, 1);
}

/// <summary>Letar upp en delegat i listan baserat på metoden som ska anropas</summary>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
/// <returns>Delegaten</returns>
Capitex.EventDelegateList.prototype.find = function(instance, method) {
    var index = this.indexOf(instance, method);
    if (index >= 0)
        return this.items[index];
    else
        return null;
}

/// <summary>Letar upp en delegat i listan baserat på metoden som ska anropas</summary>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
/// <returns>Indexet där delegaten ligger i listan</returns>
Capitex.EventDelegateList.prototype.indexOf = function(instance, method) {
    var item;
    for (var index = 0; index < this.items.length; index++) {
        item = this.items[index];
        if (item.instance === instance && item.method === method)
            return index;
    }
    return -1;
}

/// <summary>Lägger till en eventhanterare för ett event</summary>
/// <param name="element">Elementet som innehåller eventet</param>
/// <param name="eventName">Namnet på eventet</param>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
/// <returns>void</returns>
Capitex.EventDelegateList.prototype.addHandler = function(element, eventName, instance, method) {
    var item = this.add(instance, method);
    $addHandler(element, eventName, item.delegate);
}

/// <summary>Tar bort en eventhanterare för ett event</summary>
/// <param name="element">Elementet som innehåller eventet</param>
/// <param name="eventName">Namnet på eventet</param>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
/// <returns>void</returns>
Capitex.EventDelegateList.prototype.removeHandler = function(element, eventName, instance, method) {
    var index = this.indexOf(instance, method);
    if (index >= 0) {
        var item = this.items[index];
        $removeHandler(element, eventName, item.delegate);
        this.removeAt(index);
    }
}

Capitex.EventDelegateList.registerClass("Capitex.EventDelegateList");

/// <summary>Klass som representerar information om hur en metod ska anropas</summary>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
Capitex.DelegateItem = function(instance, method) {
    this.instance = instance;
    this.method = method;
    this.delegate = Type.createDelegate(this.instance, this.method);
}

Capitex.DelegateItem.registerClass("Capitex.DelegateItem");

Capitex.eventDelegates = new Capitex.EventDelegateList();

/// <summary>Klass som representerar information on hur en metod ska anropas</summary>
/// <param name="instance">Instansen som metoden ligger i</param>
/// <param name="method">Metoden som ska anropas</param>
Capitex.EventItem = function(instance, method) {
    this.instance = instance;
    this.method = method;
}

Capitex.EventItem.registerClass("Capitex.EventItem");

/*-----------------------------------------------------------------------------------------------*\
| Class _EventList
| En klass som representera en lista med events
| Avänds för att få rätt ordning på events framför allt för attachEvent metoden
|
| Parameters
| element       -   Elementet som innehåller eventet
| eventName     -   Namnet på eventet
\*-----------------------------------------------------------------------------------------------*/
Capitex._EventList = function(element, eventName) {
    this._items = [];
    Capitex.eventDelegates.addHandler(element, eventName, this, this.onFire);
}

/*-----------------------------------------------------------------------------------------------*\
| Sub add
| Lägger till en eventhanterare för eventet som denna instans hanterar
|
| Parameters
| instance      -   Instansen som metoden ligger i
| method        -   Metoden som ska anropas
\*-----------------------------------------------------------------------------------------------*/
Capitex._EventList.prototype.add = function(instance, method) {
    this._items.add(new Capitex.EventItem(instance, method));
}

/*-----------------------------------------------------------------------------------------------*\
| Sub remove
| Tar bort en eventhanterare för eventet som denna instans hanterar
|
| Parameters
| instance      -   Instansen som metoden ligger i
| method        -   Metoden som ska anropas
\*-----------------------------------------------------------------------------------------------*/
Capitex._EventList.prototype.remove = function(instance, method) {
    var item;
    for (var index = 0; index < this._items.length; index++) {
        item = this._items[index];
        if (item.method === method && item.instance === instance) {
            this._items.splice(index, 1);
            return true;
        }
    }
    return false;
}

Capitex._EventList.prototype.onFire = function() {
    var item;
    for (var index = 0; index < this._items.length; index++) {
        item = this._items[index];
        item.method.call(item.instance);
    }
}

Capitex._EventList.registerClass("Capitex._EventList");

Capitex.eventsLoad = new Capitex._EventList(window, "load");
Capitex.eventsResize = new Capitex._EventList(window, "resize");
Capitex.eventDelegatesLoadAsync = null;

Capitex._fireAsyncLoadEvents = function(sender, args) {
    for (var i=0; i<Capitex.eventDelegatesLoadAsync.items.length; i++) {
        var item = Capitex.eventDelegatesLoadAsync.items[i];
        item.delegate();
    }
}

/*-----------------------------------------------------------------------------------------------*\
| Sub $addLoadHandler
| Metod som lägger till en load hanterare
|
| Parameters:
| instance      -   Instansen av klassen som metoden ska köras i
| method        -   Metoden som ska köras
\*-----------------------------------------------------------------------------------------------*/
$addLoadHandler = function(instance, method) {

    if (!Capitex.eventDelegatesLoadAsync) {

        var prm = null;
        if ($isDefined(Sys.WebForms) && $isDefined(Sys.WebForms.PageRequestManager)) {
            prm = Sys.WebForms.PageRequestManager.getInstance();
        }
        if (prm && prm.get_isInAsyncPostBack()) {
            Capitex.eventDelegatesLoadAsync = new Capitex.EventDelegateList();
            Capitex.eventDelegatesLoadAsync.add(instance, method);
            prm.add_pageLoaded(Capitex._fireAsyncLoadEvents);
        }
        else
            Capitex.eventsLoad.add(instance, method);
    }
    else
        Capitex.eventDelegatesLoadAsync.add(instance, method);
}

/*-----------------------------------------------------------------------------------------------*\
| Sub $removeLoadHandler
| Metod som tar bort en load hanterare
|
| Parameters:
| instance      -   Instansen av klassen som metoden ska köras i
| method        -   Metoden som ska köras
\*-----------------------------------------------------------------------------------------------*/
$removeLoadHandler = function(instance, method) {
    Capitex.eventsLoad.remove(instance, method);
}

/*-----------------------------------------------------------------------------------------------*\
| Sub $addLoadHandler
| Metod som lägger till en load hanterare
|
| Parameters:
| instance      -   Instansen av klassen som metoden ska köras i
| method        -   Metoden som ska köras
\*-----------------------------------------------------------------------------------------------*/
$addResizeHandler = function(instance, method) {
    Capitex.eventsResize.add(instance, method);
}

/*-----------------------------------------------------------------------------------------------*\
| Sub $removeLoadHandler
| Metod som tar bort en load hanterare
|
| Parameters:
| instance      -   Instansen av klassen som metoden ska köras i
| method        -   Metoden som ska köras
\*-----------------------------------------------------------------------------------------------*/
$removeResizeHandler = function(instance, method) {
    Capitex.eventsResize.add(instance, method);
}

/*-----------------------------------------------------------------------------------------------*\
| Sub $setTimeout
| Metod sätter upp en metod till att anropas vid specifika tillfällen
|
| Parameters:
| instance      -   Instansen av klassen som metoden ska köras i
| method        -   Metoden som ska köras
| 
| Returns:      -   Ett timeout id som kan användas för att ta bort timeouten
\*-----------------------------------------------------------------------------------------------*/
$setTimeout = function(instance, method, millisec, lang) {
    return setTimeout(Type.createDelegate(instance, method), millisec, lang);
}

$isDefined = function(obj) {
    return (typeof(obj) !== "undefined");
}

/*-----------------------------------------------------------------------------------------------*\
| Class TemplateParser
| Klass för att parsa mallar och ersätta viewpaths med information från objekt
|
| ViewPathen ska vara enligt följande modell:
| [Typnamn.fält]
| om fält är ett objekt så kan man även fortsätta viewpathen med att ange ett underfält:
| [Typnamn.fält.underfält]
| Alternativt så kan ett prefix specificeras för ett objekt, ViewPathen blir då följande
| [Prefix.fält.underfält]
\*-----------------------------------------------------------------------------------------------*/
Capitex.TemplateParser = function(template, keyStartChar, keyStopChar) {
    this._template = template;
    if (String.isNullOrEmpty(keyStartChar) || String.isNullOrEmpty(keyStopChar)) {
        this._regexKey = Capitex.TemplateParser.getKeyRegExp();
    }
    else {
        this._regexKey = new RegExp();
        this._regexKey.compile("\\" + keyStartChar + "([^\\" + keyStopChar + ":]+):?([^\\" + keyStopChar + "]+)?\\" + keyStopChar, "g");
    }
    this._objects = {};
}

Capitex.TemplateParser._keyPattern = "\\[([^\\]:]+):?([^\\]]+)?\\]";
Capitex.TemplateParser._keyRegExp = null;
Capitex.TemplateParser.getKeyRegExp = function() {
    if (Capitex.TemplateParser._keyRegExp === null) {
        Capitex.TemplateParser._keyRegExp = new RegExp();
        Capitex.TemplateParser._keyRegExp.compile(Capitex.TemplateParser._keyPattern, "g");
    }
    return Capitex.TemplateParser._keyRegExp;
}

/*-----------------------------------------------------------------------------------------------*\
| Sub addObject
| Metod som lägger till ett objekt som parsningen ska ta hänsyn till när en text parsas
|
| Parametrar:
| obj       -   objektet som ska finnas med vid parsningen
| prefix    -   om detta är null eller tomt, så blir prefixet typnamnet på objektet
\*-----------------------------------------------------------------------------------------------*/
Capitex.TemplateParser.prototype.addObject = function(obj, prefix) {
    if (String.isNullOrEmpty(prefix))
        prefix = obj.getName();

    this._objects[prefix] = obj;
}

/*-----------------------------------------------------------------------------------------------*\
| Sub parse
| Metod som parsar en mall
|
| Parametrar:
| template      -   mall som innehåller nyckelord som ska ersättas
\*-----------------------------------------------------------------------------------------------*/
Capitex.TemplateParser.prototype.parse = function(template) {
    if (String.isNullOrEmpty(template))
        template = this._template;
    if (String.isNullOrEmpty(template))
        throw Error.argumentNull("template");

    var regex = this._regexKey;
    var builder = new Sys.StringBuilder();
    var lastIndex = 0

    var result = regex.exec(template);
    while (result !== null && result.length > 0) {
        builder.append(template.substr(lastIndex, result.index - lastIndex));
        builder.append(this._getValue(result[1]));
        lastIndex = regex.lastIndex;
        result = regex.exec(template);
    }

    if (lastIndex < template.length - 1)
        builder.append(template.substr(lastIndex));

    return builder.toString();
}

Capitex.TemplateParser.prototype._getValue = function(viewPath) {
    var items = viewPath.split(".");
    var obj = this._objects[items[0]];

    for (var index = 1; index < items.length; index++)
        obj = obj[items[index]];

    return String(obj);
}

Capitex.TemplateParser.registerClass("Capitex.TemplateParser");

Type.registerNamespace("Capitex.UI")

Capitex.UI.findElement = function(startElement, func, searchChildren) {
    var element = startElement;
    if (func(element)) {
        return element;
    }

    if (searchChildren) {
        if (element.children != null) {
            for (var i = 0; i < element.children.length; i++) {
                var result = Capitex.UI.findElement(element.children[i], func, true)
                if (result != null)
                    return result;
            }
        }
    }
    else {
        if (element.parentElement != null) {
            return Capitex.UI.findElement(element.parentElement, func, false)
        }
    }
    return null;
}

Capitex.UI.findElementById = function(startElement, id, searchChildren) {
    return Capitex.UI.findElement(startElement, function(e) { return e.id == id; }, searchChildren)
}

Capitex.UI.findElementByTagName = function(startElement, tagName, searchChildren) {
    var tn = tagName.toLowerCase();
    return Capitex.UI.findElement(startElement, function(e) { return e.tagName.toLowerCase() == tn; }, searchChildren)
}