/**
 * Several class definitions which will be used globally are located here.
 *
 */

// creating global instance for the global handler
var Global;
$("document").ready(function(){
    Global = new GlobalHandler();
});

// we cache everything, even javascript files.
// If we want to force a reload, we will add a generated timestamp or similar
// to the GET request parameters.
$.ajaxSetup({
    cache: true
});

// setting a global indicator which indicates if the site is currently busy or not.
var g_isLoading = false;
$(document).ajaxStart(function(){
    // adding the "loading" class to the body whenever an ajax request starts.
    $("body").addClass("loading");
    g_isLoading = true;
    showLoading();
});

$(document).ajaxStop(function(){
    // removing the loading class from the body if no ajax request is working anymore.
    $("body").removeClass("loading");
    g_isLoading = false;
    hideLoading();
});

// declaring global default values
var GLOBALS = {
    ArchiveId: null,
    ArchiveType: "",
    Columns: 4,
    Rows: 1,
    ThumbnailSize: 170,
    Sorting: "ModifiedTimeAsc",
    Search: "",
    Preview: false,
    PreviewAccess: false,
    PostBackUrl: "",
    KeywordMetaFieldId: 0
};

var CONFIG = {
    // the delay (in ms) which will occur after every selection
    // till the synchronizing process will be triggered
    // recommended: 1000-1500
    SelectionSyncDelay: 1250,
    // the size in pixels which will be used for the
    // hovering thumbnail preview
    // default: 450
    HoverPreviewSize: 450
}

/**
 * shows the loading indicator, will be called whenever a ajax request is running
 */
function showLoading(){
    $('.loadingIndicator .background').css("opacity", 0.2);
    $('.loadingIndicator').fadeIn(300);
}

/**
 * hides the loading indicator again
 */
function hideLoading(){
    window.setTimeout(function(){
        if(g_isLoading){
            return;
        }
        $('.loadingIndicator').fadeOut(300);
    }, 500);
}

/**
 * Initiates global classes and objects like the thumbnail objects.
 */
function GlobalHandler()
{
    // #### Animation Speeds ####

    var m_Animation_LayoutDropdown = {
        show: 150,
        hide: 150
    };

    var m_Animation_Config_extInfo = {
        show: 300,
        hide: 300
    };
    var m_Animation_Config_Rating  = {
        show: 300,
        hide: 300
    };

    var m_Animation_SearchDropdown = {
        show: 100,
        hide: 100
    };

    var m_KeywordCloud_CategoryCount    = 10;
    var m_KeywordCloud_ItemsPerCategory = 5;

    var m_defaultSearchText = TRANSLATIONS.Search;
    var m_bSearchClick = false;
    var KeywordPopDiv;

    /**
     * Signilizes if the value of images per page were changed or not.
     */
    var m_ImagesPerPageChanged = false;

    /**
     * Signilizes if the value of the quick sorter were changed or not.
     */
    var m_QuickSorterChanged = false;

    /**
     * Standard beginning dimension of the Pictures.
     *
     * @type integer
     */
    var m_StandardPicScale = 170;

    /**
     * Will be set to the corresponding value of the slider for internal use.
     *
     * @type integer
     */
    var m_ImageScale = 0;

    /**
     * Determines the max square dimension of the hover Preview.
     *
     * @type integer
     *
     */
    var m_picHoverZoomSize = CONFIG.HoverPreviewSize;

    /**
     * @namespace GlobalHandler
     */
    function construct()
    {
        // no global init for default page
        if($("body.page-default").length)
            return;
        
        console.log("using unminified version of global.js");

        processGLOBALS();
        
        initThumbnails();
        initGridScaleSlider();
        initPanels();

        // initiating navigation panel
        NavigationPaneControl.call($("#NavigationPaneControl")[0]);

        initArchiveMenu();
        initToolsMenu();
        initViewMenu();
        initAdministrationMenu();

        initLayoutDropdown();
        
        initSearchControl();
    }

    /**
     * @namespace GlobalHandler
     */
    function processGLOBALS(){
        GLOBALS.ArchiveId     = parseInt(GLOBALS.ArchiveId);
        GLOBALS.Rows          = parseInt(GLOBALS.Rows);
        GLOBALS.Columns       = parseInt(GLOBALS.Columns);
        GLOBALS.ImagesPerPage = GLOBALS.Rows*GLOBALS.Columns;
        GLOBALS.PreviewAccess = parseInt(GLOBALS.PreviewAccess);
    }

    /**
     * @namespace GlobalHandler
     */
    function initThumbnails(){
        // FIXME: Instead of using ".SelectableImage" we should promote the
        // ".ThumbContainer" class within, but that
        // will involve significant css changes
        if(!$(".SelectableImage").length){
            return; // get out if we have no thumbnails
        }

        // we need the rating plugin
        // the css definition were injected into the global Appearence.css
        $(".SelectableImage").each(function(){
            Thumbnail.call(this); // mutating the images to Thumbnail objects
        });
    }

    //### injected methods from Grid page (start) ###//
    /**
     * Modifies the window.location with the new parameters needed to
     * achieve the new amount of images.
     *
     * @param {number} AmountOfImages
     * @namespace GlobalHandler
     */
    function updateImagesPerPage(AmountOfImages)
    {
        var step = 4;
        var rowValue = AmountOfImages/step;
        var newLocation = window.location.toString();
        // is the query string already set?
        if(window.location.search.length)
        {
            newLocation = newLocation.replace(/#/, "");

            // Columns value is static, cause we don't really have columns anymore
            // instead we use it as a kind of step
            if(newLocation.match(/columns=/))
                newLocation = newLocation.replace(/columns=\d*/, "columns="+step+"");
            else
                newLocation = newLocation+"&columns="+step+"";

            if(newLocation.match(/rows=/))
                newLocation = newLocation.replace(/rows=\d*/, "rows="+rowValue);
            else
                newLocation = newLocation+"&rows="+rowValue;
        }
        else
            newLocation = newLocation+"?columns="+step+"&rows="+rowValue;

        window.location = newLocation;
    }

    /**
     * Modifies the location with the new parameters needed to
     * achieve the new images' sorting.
     *
     * @param {string} Sorting
     * @namespace GlobalHandler
     */
    function updateSorting(Sorting)
    {
        var newLocation = window.location.toString();

        // is the query string already set?
        if(window.location.search.length)
        {
            newLocation = newLocation.replace(/#/, "");

            if(newLocation.match(/sorting=/))
                newLocation = newLocation.replace(/sorting=\w*/, "sorting="+Sorting);
            else
                newLocation = newLocation+"&sorting="+Sorting;
        }
        else
            newLocation = newLocation+"?sorting="+Sorting;

        window.location = newLocation;
    }

    /**
     * initiates the configured layout options by either cookies or default values
     *
     * @namespace GlobalHandler
     */
    function initLayoutPreselection()
    {
        // direct preview
        if($.cookie("directPreview") == 1)
            $("#directPreview").attr("checked", "checked");
        else if($.cookie("directPreview") == 0)
            $("#directPreview").removeAttr("checked");
        else // default value if no cookie was set
            $("#directPreview").attr("checked", "checked");

        // extended information
        if($.cookie("extendedInfo") == 1)
            $("#extendedInfo").attr("checked", "checked");
        else
            $("#extendedInfo").removeAttr("checked");

        // Rating
        if($.cookie("showRating") == 1)
            $("#showRating").attr("checked", "checked");
        else
            $("#showRating").removeAttr("checked");

        // images / page
        $("#ImagesPerPage option").each(function(){
            if($(this).val() == GLOBALS.ImagesPerPage)
                $(this).attr("selected", "selected");
        });

        // quick sorter
        $("#ImageQuickSorter option").each(function(){
            if($(this).val() == GLOBALS.Sorting)
                $(this).attr("selected", "selected");
        });
    }

    /**
     * Triggers the showing or hiding of the additional thumbnail container elements.
     * Additionally it stores the current selection into cookies.
     *
     * @param animated
     *
     * @namespace GlobalHandler
     */
    function updateLayoutConfig(animated)
    {
        // direct preview
        if($("#directPreview").attr("checked") )
        {
            $.cookie("directPreview", 1, {
                expires: 30
            });
            $(".SelectableImage .Thumb img").tooltip({
                delay: 200,
                track: true,
                showURL: false,
                bodyHandler: function() {
                    var newSrc = this.src.replace(/sz=\d*/, "sz="+m_picHoverZoomSize);
                    return $("<img/>").attr("src", newSrc);
                }
            });
        }
        else
        {
            $.cookie("directPreview", 0, {
                expires: 30
            });
            $(".SelectableImage img").unbind("mouseover").unbind("mouseout");
        }

        // showing rating
        if($("#showRating").attr("checked"))
        {
            $.cookie("showRating", 1, {
                expires: 30
            });
            if(animated)
                $(".ThumbContainer .Rating").fadeIn(m_Animation_Config_Rating.show);
            else
                $(".ThumbContainer .Rating").show();
        }
        else
        {
            $.cookie("showRating", 0, {
                expires: 30
            });
            if(animated)
                $(".ThumbContainer .Rating").fadeOut(m_Animation_Config_Rating.hide);
            else
                $(".ThumbContainer .Rating").hide();
        }

        // showing extended pic info if configured
        var showExtInfo = false;
        if($("#extendedInfo").attr("checked"))
        {
            $.cookie("extendedInfo", 1, {
                expires: 30
            });
            showExtInfo = true;
        }
        else
        {
            $.cookie("extendedInfo", 0, {
                expires: 30
            });
            showExtInfo = false;
        }

        // if the image scale is too small
        if(showExtInfo && m_ImageScale > 100)
        {
            if(animated)
                $(".ThumbContainer .Info").fadeIn(m_Animation_Config_extInfo.show);
            else
                $(".ThumbContainer .Info").show();
        }
        else
        {
            if(animated)
                $(".ThumbContainer .Info").fadeOut(m_Animation_Config_extInfo.hide);
            else
                $(".ThumbContainer .Info").hide();
        }

        if(m_ImagesPerPageChanged){
            updateImagesPerPage($("#ImagesPerPage").val());
        }

        if(m_QuickSorterChanged){
            updateSorting($("#ImageQuickSorter").val());
        }
    }

    /**
     * Initiates the layout dropdown with its checkboxes and their behavior.
     *
     * // TODO: Convert to the global TopRowDropDown object like it's used for Preview.fwx
     *
     *  @namespace GlobalHandler
     */
    function initLayoutDropdown()
    {
        // do not proceed if no layout config is available
        if(!$('#LayoutSwitch').length){
            return;
    }

        initLayoutPreselection();
        updateLayoutConfig(false);

        $("#LayoutSwitch").click(function(){
            if($(this).hasClass("Active"))
                hideLayoutDropdown("revert");
            else
                showLayoutDropdown();
        });

        $("#LayoutSwitch").add("#LayoutDropdown").click(function(e){
            e.stopPropagation();
        });

        $("#btnLayoutSave").click(function(){
            updateLayoutConfig(true); // animated
            hideLayoutDropdown();
        });

        $("#btnLayoutCancel").click(function(){
            hideLayoutDropdown("revert");
        });

        $("#ImagesPerPage").change(function(){
            if($(this).val() != GLOBALS.ImagesPerPage){
                m_ImagesPerPageChanged = true;
            }
            else{
                m_ImagesPerPageChanged = false;
            }

        });

        $("#ImageQuickSorter").change(function(){
            if($(this).val() != GLOBALS.Sorting){
                m_QuickSorterChanged = true;
            }
            else {
                m_QuickSorterChanged = false;
            }

        });

        // hiding layout dropdown when clicked on body
        $("html").click(function(){
            hideLayoutDropdown("revert");
        })
    }

    /**
     * opens the layout dropdown.
     *
     * @namespace GlobalHandler
     */
    function showLayoutDropdown()
    {
        $("#LayoutSwitch").addClass("Active");
        $("#LayoutDropdown").slideDown(m_Animation_LayoutDropdown.hide);
    }

    /**
     * hides the layout dropdown
     *
     * @param mode can be [default|save] defines, wether the checkboxstatus will be reverted or not
     *
     * @namespace GlobalHandler
     */
    function hideLayoutDropdown(mode)
    {
        $("#LayoutDropdown").slideUp(m_Animation_LayoutDropdown.show, function(){
            $("#LayoutSwitch").removeClass("Active");

            // canceling and resetting the changes
            if(mode == "revert")
                initLayoutPreselection();
        });
    }

    /**
     * Initiates the grid scaler.
     *
     * @namespace GlobalHandler
     */
    function initGridScaleSlider()
    {
        console.log("PreviewAccess "+GLOBALS.PreviewAccess);
        if(!GLOBALS.PreviewAccess)
        {
            initNormalThumbnails();
            return;
        }

        // standard or configured size?
        m_ImageScale = $.cookie('ImageScale') ? $.cookie('ImageScale') : m_StandardPicScale;

        resizePicsTo(m_ImageScale);

        $("#ImageScaleSlider").slider({
            value:m_ImageScale,
            min: 100,
            max: 450,
            step: 70,
            stop: function(event, ui) {
                m_ImageScale = ui.value;
                resizePicsTo(ui.value);
                // need to be called, cause if the thumbnail container is too small,
                // the additional information has to be hidden
                // TODO: implementing the Proxy Pattern would be a better approach
                updateLayoutConfig(true);
            }
        });
    }


    /**
     * resizes all images to specific scale
     *
     * @param scale max width and height definition for an image
     *
     * @namespace GlobalHandler
     */
    function resizePicsTo(scale)
    {
        console.log("rezising pics to", scale);

        $(".SelectableImage").each(function(){
            this.resize(scale);
        });

        $.cookie("ImageScale", scale, {
            expires: 30
        });
    }

    /**
     * Function which will be called if preview access is unavailable. Only the access to the preview.fwx
     * it is possible to render scaled images. So no preview access, no image scalor.
     *
     * @namespace GlobalHandler
     */
    function initNormalThumbnails(){
        $(".SelectableImage").each(function(){
            var img = $(this).find("img")[0];
            var ImageNum = $(this).attr("id").replace(/\D/g, "");
            var currentContainer = this;
            m_ImageScale = GLOBALS.ThumbnailSize;

            // That's the magic! To achieve the effect, that an img will be faded in AFTER it was completly loaded,
            // we has to bind an onload event to the image, before we set the new source!
            img.onload = function(){
                $(this).fadeIn(300);
            };

            // TODO: getting rid of setting the width for all containers. Setting the width to the image should be the goal. - visibility="hidden" ?
            $(currentContainer).find(".ThumbContainer").width(m_ImageScale+"px");
            $(currentContainer).find(".Thumb").width(m_ImageScale+"px");
            $(currentContainer).find(".Thumb").height(m_ImageScale+"px");
            var src = $(img).attr("src");
            //src = src.replace(/sz=\d*/, "sz="+scale);
            src = "/fotoweb/cmdrequest/rest/preview.fwx?f="+GLOBALS.ImageInfos[ImageNum].thumbnailId;
            $(img).attr("src", src);
        });
    }

    //### injected methods from Grid page (end) ###//

    /**
     * @namespace GlobalHandler
     */
    function initPanels(){
        $(".Panel").each(function(i){
            Panel.call(this, "Panel_"+i); // mutating the panels to concret Panel objects
        });
    }

    /**
     * @namespace GlobalHandler
     */
    function initArchiveMenu() {
        if(!$("#ArchiveMenu").length)
            return;

        $.get("/fotoweb/cmdrequest/rest/ArchiveList.fwx", {
            rd: +new Date
        }, function(xml){
            var ul = parseArchiveXML(xml);
            $(ul).attr("id", "ArchiveList");
            $("#ArchiveMenu").append(ul);
            $("#ArchiveMenu").parent().addClass("multilvl-menu horizontal scrollable");
            MultiLvlMenu.call($("#ArchiveMenu").parent()[0]);
            $("#ArchiveMenu").find(".scroller").append('<div class="bottom"></div>');

            // after the archive menu xml was parsed, init the keyword cloud.
            initKeywordPopUp();
        }, "xml");
    }

    /**
     * @namespace GlobalHandler
     */
    function parseArchiveXML(xml) {
        var ArchiveEntries = $(xml).find("Archive");
        var ul = document.createElement("ul");
        var liReferences = new Array();

        var i;
        var li;
        var ArchiveId;
        var a;
        for(i=0;i<ArchiveEntries.length;i++) {
            ArchiveId = $(ArchiveEntries[i]).attr("Id");

            li = document.createElement("li");
            $(li).attr("id", "Archive_"+ArchiveId );

            a = document.createElement("a");
            $(a).attr("href", "/fotoweb/Grid.fwx?archiveId="+ArchiveId+"&position=1&search=");
            //Grid.fwx?archiveId=<%$=archive.id %>&amp;position=1&amp;search=<%$=SearchString%>
            $(a).text($(ArchiveEntries[i]).attr("Name") );

            try{
                if(GLOBALS.ArchiveId == ArchiveId){
                    $(a).addClass("isCurrent");
                    GLOBALS.KeywordMetaFieldId = $(ArchiveEntries[i]).attr("KeywordCloudField");
                }
            } catch(e) {
            // we expecting errors
            }

            $(li).append(a);
            $(ul).append(li);

            liReferences.push(li);
        }

        var ParentId;
        for(i=0;i<liReferences.length;i++) {
            ParentId = $(ArchiveEntries[i]).attr("ParentId");

            if(ParentId == 0)
                continue;

            ParentNode = $(ul).find("#Archive_"+ParentId);
            CurrentNode = liReferences[i];

            $(ParentNode).append(CurrentNode);
        }

        $(ul).find("li:has(li)").each(function(){
            $(this).children("li").wrapAll("<ul/>");
        });

        $(ul).find("a").wrap("<span/>");

        return ul;
    }

    /**
     * @namespace GlobalHandler
     */
    function initToolsMenu()
    {
        if(!$("#TopMenuToolsList").length)
            return;

        $.get("/fotoweb/cmdrequest/rest/ToolsMenu.fwx", {
            rd: +new Date
        }, function(data){
            $("#TopMenuToolsList").empty();
            $(data).find("ToolsMenu > MenuItem > MenuItem").each(function(){
                var href = $(this).attr("JavaScript") ? $(this).attr("JavaScript") : $(this).attr("Url");
                $("#TopMenuToolsList").append("<li><span><a href='"+href+"'>"+
                    $(this).attr("DisplayName")+"</a></span></li>");
            });

        }, "xml");

    }

    /**
     * @namespace GlobalHandler
     */
    function initAdministrationMenu()
    {
        if(!$("#TopMenuAdministrationList").length)
            return;

        $("#TopMenuAdministration").hide();

        $.get("/fotoweb/cmdrequest/rest/AdministrationMenu.fwx", {
            rd: +new Date
        }, function(data){
            $("#TopMenuAdministrationList").empty();

            $(data).find("AdministrationMenu > MenuItem > MenuItem").each(function(){
                var href = $(this).attr("JavaScript") ? $(this).attr("JavaScript") : $(this).attr("Url");
                $("#TopMenuAdministrationList").append("<li><span><a href='"+href+"'>"+
                    $(this).attr("DisplayName")+"</a></span></li>");
            });

            if($("#TopMenuAdministrationList li").length)
                $("#TopMenuAdministration").show();

        }, "xml");

    }

    /**
     * @namespace GlobalHandler
     */
    function initViewMenu()
    {
        if(!$("#TopMenuViewList").length)
            return;

        var params = new Object();
        params.ts = +new Date;
        
        if(typeof GLOBALS != "undefined"){
            if(typeof GLOBALS.Preview != "undefined" && GLOBALS.Preview)
                params.pm = 1;

            if(typeof GLOBALS.ArchiveType != "undefined" && GLOBALS.ArchiveType != "")
                params.at = GLOBALS.ArchiveType;
        }

        $("#TopMenuViewList").empty();

        $.get("/fotoweb/cmdrequest/rest/ViewMenu.fwx", 
            params
            , function(data){
                $(data).find("ViewMenu > MenuItem > MenuItem").each(function(){
                    var href = $(this).attr("JavaScript") ? $(this).attr("JavaScript") : $(this).attr("Url");
                    $("#TopMenuViewList").append("<li><span><a href='"+href+"'>"+
                        $(this).attr("DisplayName")+"</a></span></li>");
                });
            }, "xml");

    }
	
	function showHideClearSearchButton()
	{
		var searchValue = $("#searchInputField").val();
		if(searchValue == m_defaultSearchText || searchValue == "")
		{
			$("#ClearSearchButton").css("display", "none");
		}
		else
		{
			$("#ClearSearchButton").css("display", "inline");
		}
	}

    /**
     * @namespace GlobalHandler
     */
    function initSearchControl()
    {
        if( !$("#searchInputField").length )
            return; // get out, cause of no existing search element

        $("#searchInputField").addClass("searchInputField_Inactive");

        if(GLOBALS.Search == "")
        {
		    $("#searchInputField").val(m_defaultSearchText);
		}
        else
		{
            $("#searchInputField").val(GLOBALS.Search);
		}
		
		showHideClearSearchButton();
		
		// Show the clear button when starting to type in the search box
		$("#searchInputField").keyup(function(){
			showHideClearSearchButton();
		});

        $("#MainFrameOpacityLayer").css("opacity", 0);

        // focusing the search input reveals the search popup
        $("#searchInputField").focus(function(){
            //      $("#MainFrameOpacityLayer").show();
            //      $("#MainFrameOpacityLayer").fadeTo(1000, 0.3);

            $("#SearchPopUp").slideDown(m_Animation_SearchDropdown.show);

            if($(this).val() == m_defaultSearchText)
            {
				$("#searchInputField").val("");
			}
			
            $("#searchInputField").addClass("searchInputField_Active");
            $("#searchInputField").removeClass("searchInputField_Inactive");
        });

        $("#SearchPopUp,#searchInputField,#SearchSubmit").click(function(){
            m_bSearchClick = true;
        });

        $("html").click(function(){

            if(m_bSearchClick) {
                m_bSearchClick = false; // resetting
                return;
            }

            if($("#searchInputField").val() == "")
                $("#searchInputField").val(m_defaultSearchText);
				
            //      $("#MainFrameOpacityLayer").fadeTo(1000, 0, function(){
            //        $("#MainFrameOpacityLayer").hide();
            //      });

            $("#searchInputField").addClass("searchInputField_Inactive");
            $("#searchInputField").removeClass("searchInputField_Active");

            $("#SearchPopUp").slideUp(m_Animation_SearchDropdown.hide);
        });
    }

    /**
     * @namespace GlobalHandler
     */
    function initKeywordPopUp(){
        if(!$("#KeywordsPopUpTrigger")[0])
            return;

        $("#KeywordsPopUpTrigger").hide();

        var data = {
            ar: GLOBALS.ArchiveId,
            md: GLOBALS.KeywordMetaFieldId, // will be set after initArchiveMenu()
            cc: m_KeywordCloud_CategoryCount,
            ic: m_KeywordCloud_ItemsPerCategory
        };

        $.get("/fotoweb/cmdrequest/rest/KeywordCloud.fwx", data, function(xml){
            if($(xml).find("KeywordCloud")) {
                var Content = buildKeywordCloud(xml);

                createKeywordPopdiv(Content);
                $("#KeywordsPopUpTrigger").show();
            }
        }, "xml");

        $("#KeywordsPopUpTrigger").click(function(){
            if(KeywordPopDiv){
                KeywordPopDiv.show();
            }

            return false; // prevent following the link
        });
    }

    /**
     * @namespace GlobalHandler
     */
    function buildKeywordCloud(xml){
        var Categories = $(xml).find("Category");

        // declaring scope variables
        var i;
        var j;
        var Relevance;
        var TagNode;
        var href = "";

        // getting all tag entries and build corresponding "a" elements
        var TagNodes = new Array();
        var currentValues = null;

        console.log(Categories);

        for(i=0; i<m_KeywordCloud_CategoryCount; i++)
        {
            if(!Categories[i]){
                continue;
            }

            currentValues = $(Categories[i]).find("Value");
            
            for(j=0; j<m_KeywordCloud_ItemsPerCategory; j++){

                if(!currentValues[j]){
                    continue;
                }

                TagNode = currentValues[j];
                Relevance = (m_KeywordCloud_CategoryCount-i);
                href = '/fotoweb/Grid.fwx?archiveId='+GLOBALS.ArchiveId+'&position=1&search='+$(TagNode).attr("Value");
                TagNodes.push($('<a rel="'+Relevance+'" href="'+href+'">'+$(TagNode).attr("Value")+'</a>'));
            }
        }

        console.log("KeywordCloud TagNodes", TagNodes);


        // shuffling the tag elements within the cloud
        var TagCount = TagNodes.length;
        var randomPick;
        var storage;
        for(i=0; i<TagCount; i++)
        {
            randomPick = Math.floor(Math.random() * TagNodes.length) + 1;

            storage = TagNodes[randomPick];
            TagNodes[randomPick] = TagNodes[i];
            TagNodes[i] = storage;
        }

        var Content = $("<div/>");
        $(TagNodes).each(function(){
            Content.append(this).append("\n");
        });

        $(Content).find("a").tagcloud({
            size: {
                start: 1,
                end: 2,
                unit: "em"
            },
            color: {
                start: '#999',
                end: '#333'
            }
        });

        return Content;
    }

    /**
     * @namespace GlobalHandler
     */
    function createKeywordPopdiv(Content){

        KeywordPopDiv = new PopDiv("KeywordCloudPopUp");
        KeywordPopDiv.setCaption($("#KeywordsPopUpTrigger").text());
        KeywordPopDiv.setContent(Content);
        KeywordPopDiv.setOverlaySpeed(200, 200);
        KeywordPopDiv.setPopDivSpeed(200, 200);
    }

    construct(); // calling the pseudo constructor
}


/**
 * Class DownloadPopup
 *
 * @constructor
 */
function DownloadPopup(){

    var DownloadProfilesPopup;

    /**
     * Fills and shows the DownloadPopup.
     *
     * @param Images The image objects which should be shown with the download options.
     * @param ProfilesXML The XML data of the process profiles. The will be parsed
     * and shown as selectable options.
     * @param executionURL The URL which will be used when a process profile was selected.
     */
    this.show = function(Images, ProfilesXML, executionURL){

        if(!DownloadProfilesPopup) {
            this.create();
        }

        var i;

        var Content = document.createElement("div");

        // filling and creating the image container
        var ImagesContainer = document.createElement("div");
        $(ImagesContainer).addClass("downloadpopup-images");

        if(Images.length == 1){
            $(ImagesContainer).addClass("single");
        }

        for(i=0; i<Images.length; i++) {
            ImagesContainer.appendChild(Images[i]);
        }
        $(ImagesContainer).find("img").wrap('<div class="downloadpopup-image-container"><div><div></div></div></div>')
        .addClass("downloadpopup-image");

        // filling and creating the profile container
        var ProfileContainer = document.createElement("div");
        $(ProfileContainer).addClass("downloadpopup-profile-container");

        var Profiles = $(ProfilesXML).find("DirectDownloads ProcessingProfile");
        for(i=0; i<Profiles.length; i++) {
            var a = document.createElement("a");
            $(a).attr("href", executionURL+"&pp="+encodeURIComponent($(Profiles[i]).attr("Name")));
            $(a).attr("title", $(Profiles[i]).attr("Description"));

            var name = $(Profiles[i]).attr("DisplayName");

            $(a).text(name);

            $(a).addClass("downloadpopup-profile");
            ProfileContainer.appendChild(a);
        }

        Content.appendChild(ImagesContainer);
        Content.appendChild(ProfileContainer);

        DownloadProfilesPopup.setContent(Content);
        DownloadProfilesPopup.show();
    }

    /**
     * Creates the download popup node.
     */
    this.create = function() {
        DownloadProfilesPopup = new PopDiv("DownloadPopUp");
        DownloadProfilesPopup.setCaption(TRANSLATIONS.Download);
        DownloadProfilesPopup.setOverlaySpeed(200, 200);
        DownloadProfilesPopup.setPopDivSpeed(200, 200);
    }
}

/**
 * TopRowDropDown Class
 */
function TopRowDropDown(node)
{
    var m_node = node;
    var m_bLayoutDropdownClick;
    var m_hideEvent;
    var m_AnimationSpeed = {
        up: 150,
        down: 150
    };

    /**
     * @namespace TopRowDropDown
     */
    function construct()
    {
        $(m_node).find(".TopRowDropDownSwitch").click(function(){
            if($(this).hasClass("Active"))
                hideDropdown();
            else
                showDropdown();
        });

        $(m_node).find(".TopRowDropDownSwitch, .TopRowDropDown").click(function(){
            m_bLayoutDropdownClick = true;
        });

        $("html").click(function(){

            if(m_bLayoutDropdownClick) {
                m_bLayoutDropdownClick = false; // resetting
                return;
            }

            hideDropdown();
        })
    }

    /**
     * @namespace TopRowDropDown
     */
    function showDropdown() {
        $(m_node).find(".TopRowDropDownSwitch").addClass("Active")
        .next(".TopRowDropDown").slideDown(m_AnimationSpeed.down);
    }

    /**
     * @namespace TopRowDropDown
     */
    function hideDropdown()
    {
        $(m_node).find(".TopRowDropDown").slideUp(m_AnimationSpeed.up, function(){
            $(m_node).find(".TopRowDropDownSwitch").removeClass("Active");
        });

        if(m_hideEvent)
            m_hideEvent();
    }

    /**
     * @namespace TopRowDropDown
     */
    function setHideEvent(func)
    {
        m_hideEvent = func;
    }

    this.hide = hideDropdown;
    this.setHideEvent = setHideEvent;

    construct();
}

/**
 * Mutation class for the Panels
 *
 */
function Panel(id){

    var m_AnimationSpeed = {
        show: 200,
        hide: 200
    };

    var m_isShown = false;
    var m_isActive = true;

    var m_preShowEvent = false;
    var m_postShowEvent = false;

    var m_preHideEvent = false;
    var m_postHideEvent = false;

    var m_preActivateEvent = false;
    var m_postActivateEvent = false;

    var m_preUpdateEvent = false;
    var m_postUpdateEvent = false;

    var m_interPreEvent = false;
    var m_interPostEvent = false;

    var _cookieId = "";

    /**
     * Own reference for use in event calls.
     */
    var self = this;

    /**
     * pseudo constructor, will be called at the end of the current class definition
     */
    this.construct = function(id){
        this.id = id;

        _cookieId = id+"_status_on_"+window.location.pathname.replace(/\//g, "");

        console.log("Panel cookie id", _cookieId);

        console.log("Panel cookie value", $.cookie(_cookieId));

        if($.cookie(_cookieId) ){
            if($.cookie(_cookieId) == "open"){
                m_isShown = true;
            }
        }
        else if($(this).hasClass("OpenedPanel")){
            m_isShown = true;
        }

        $(this).find(".PanelHeader").click(function(){
            self.toggle();
        });
        
        this.update();
    }

    /**
     * toggles the panel to show and hide
     */
    this.toggle = function() {
        if(m_isShown)
            this.hide();
        else
            this.show();
    }

    /**
     * showing the panel
     */
    this.show = function(){
        if(typeof m_preShowEvent == "function")
            m_interPreEvent = m_preShowEvent;
        if(typeof m_postShowEvent == "function")
            m_interPostEvent = m_postShowEvent;

        m_isShown = true;
        this.update();
    }

    /**
     * hiding the panel
     */
    this.hide = function(){
        if(typeof m_preHideEvent == "function")
            m_interPreEvent = m_preHideEvent;
        if(typeof m_postHideEvent == "function")
            m_interPostEvent = m_postHideEvent;

        m_isShown = false;
        this.update();
    }

    /**
     * disabling the panel
     */
    this.disable = function(){
        m_isActive = false;
        this.update();
    }

    /**
     * activating the panel
     */
    this.activate = function(){
        if(typeof m_preActivateEvent == "function")
            m_interPreEvent = m_preActivateEvent;
        if(typeof m_postActivateEvent == "function")
            m_interPostEvent = m_postActivateEvent;

        m_isActive = true;
        this.update();
    }

    /**
     * updating the panel depending on own environment variables
     */
    this.update = function(){
        // adding or removing activation style classes
        if(m_isActive)
        {
            // triggering pre events
            if(typeof m_interPreEvent == "function")
                m_interPreEvent();

            // resetting internal pre event
            m_interPreEvent = false;

            if(typeof m_preUpdateEvent == "function")
                m_preUpdateEvent();

            $(this).removeClass("DisabledPanel");
        }
        else
            $(this).addClass("DisabledPanel");

        // triggering animations and setting corresponding status style classes
        if(m_isShown && m_isActive) {
            $(this).addClass("OpenedPanel");
            $.cookie(_cookieId, "open",{
                expires: 30
            });
            $(this).find(".InnerContainer").slideDown(m_AnimationSpeed.show, function(){

                if(typeof m_interPostEvent == "function")
                    m_interPostEvent();
                if(typeof m_postUpdateEvent == "function")
                    m_postUpdateEvent();
            });
        }
        else {
            $.cookie(_cookieId, "closed",{
                expires: 30
            });
            $(this).find(".InnerContainer").slideUp(m_AnimationSpeed.hide, function(){
                $(this).closest(".Panel").removeClass("OpenedPanel");

                if(typeof m_interPostEvent == "function")
                    m_interPostEvent();
                if(typeof m_postUpdateEvent == "function")
                    m_postUpdateEvent();

                // resetting internal post event
                m_interPostEvent = false;
            });
        }
    }

    /**
     * Sets a pre show event, which will be called before the Panel will be shown.
     */
    this.setPreShowEvent = function(callback){
        m_preShowEvent = callback;
    }

    /**
     * Sets a post show event, which will be called after the Panel was shown.
     */
    this.setPostShowEvent = function(callback){
        m_postShowEvent = callback;
    }

    /**
     * Sets a pre active event, which will be called before the Panel will be activated.
     */
    this.setPreActivateEvent = function(callback){
        m_preActivateEvent = callback;
    }

    /**
     * Sets a post active event, which will be called after the Panel was activated.
     */
    this.setPostActivateEvent = function(callback){
        m_postActivateEvent = callback;
    }

    /**
     * Sets a pre show event, which will be called before the Panel will be hidden.
     */
    this.setPreHideEvent = function(callback){
        m_preHideEvent = callback;
    }

    /**
     * Sets a post show event, which will be called after the Panel was hidden.
     */
    this.setPostHideEvent = function(callback){
        m_postHideEvent = callback;
    }

    /**
     * Sets a generally pre update event, which will be called before the Panel will be updated.
     */
    this.setPreUpdateEvent = function(callback){
        m_preUpdateEvent = callback;
    }

    /**
     * Sets a generally post update event, which will be called after the Panel was updated.
     */
    this.setPostUpdateEvent = function(callback){
        m_postUpdateEvent = callback;
    }

    this.construct(id);
}


/**
 * Mutation Class for an Image Thumbnail Container within a Grid.
 *
 * @constructor
 */
function Thumbnail(){

    this.foxtoken    = "";
    this.thumbnailId = "";
    this.position = 0;
    this.fckCheckbox = false;
    this.isMarked    = false;
    this.previewUrl  = "";
    this.downloadId  = "";
    this.filename    = "";

    var _PrioSelector = false;
    var self = this;

    /**
     * Initiates the class environment.
     *
     * Will be called at the bottom of this class.
     *
     * @namespace Thumbnail
     * @private
     */
    this.init = function(){

        this.position = this.id.replace(/\D/g, "");

        // getting the information from the Grid definitions
        this.foxtoken = GLOBALS.ImageInfos[this.position].foxtoken;
        this.thumbnailId = GLOBALS.ImageInfos[this.position].thumbnailId;
        this.previewUrl = GLOBALS.ImageInfos[this.position].PreviewUrl;
        this.downloadId = GLOBALS.ImageInfos[this.position].downloadId;
        this.filename = GLOBALS.ImageInfos[this.position].filename;

        this.fckCheckbox = $("#fck"+this.position)[0];
        this.isMarked = this.fckCheckbox.checked;

        $(this).click(this.processClick);

        // enabling hovering effects by adding the hover class dynamically
        $(this).hover(function(){
            $(this).addClass("hover");
        }, function(){
            $(this).removeClass("hover");
        });

        this.initRatings();
    }

    /**
     * Initiates the Rating control of the current Thumbnail
     */
    this.initRatings = function()
    {
        $(this).find(".Rating .submitrating").rating({
            callback: function(value){
                if(!value)
                    return;
                var ImageNum = parseInt(this.parentNode.id.replace(/\D/g, ""));
                $.get("/fotoweb/cmdrequest/FileAction.fwx?fwSetRating=1&rating="+parseInt(value)+"&f="+GLOBALS.ImageInfos[ImageNum].foxtoken);
            }
        });

        $(this).find(".Rating").css("visibility", "visible");

        // workaround to get the cancel button work properly
        $(this).find('.rating-cancel').click(function(e){
            e.stopImmediatePropagation();
            var value = 0;
            var ImageNum = parseInt(this.parentNode.parentNode.id.replace(/Rating/, ""));
            $(this).rating('select');
            $.get("/fotoweb/cmdrequest/FileAction.fwx?fwSetRating=1&rating="+parseInt(value)+"&f="+GLOBALS.ImageInfos[ImageNum].foxtoken);
        });

        // preventing the rating control to bubbles  events
        $(this).find(".Rating .submitrating").click(function(e){
            e.stopImmediatePropagation();
        })
    }

    /**
     * Function which delegates the event to the corresponding target.
     */
    this.processClick = function(e){
        console.log("Processing click", this);
        console.log(e);

        var t = e.target;

        // event delegations
        if($(t).hasClass("Download")) {
            e.stopImmediatePropagation();
            $("#NavigationPaneControl")[0].processDownloadRequest.call(this);
        }
        else if($(t).hasClass("Rating")) {
        // do nothing, cause this will be handled by the rating plugin itself
        }
        else if($(t).hasClass("Selection")) {
            e.stopImmediatePropagation();
            this.toggle();
        }
        else if($(t).hasClass("Zoom")) {
            e.stopImmediatePropagation();
            this.goToPreview();
        }
        else if($(t).hasClass("Thumbnail") && GLOBALS.PreviewAccess) {
            e.stopImmediatePropagation();
            this.goToPreview();
        }
        else if($(t).hasClass("Priority")){
            e.stopImmediatePropagation();
            this.showPrioritySelector();
        }
    }

    /**
     * Loads and displays the Priority selector for the current Thumbnail.
     */
    this.showPrioritySelector = function(){
        
        // if there is already a PrioSelector, try to show it and get out
        if(_PrioSelector){
            
            // hiding all except this one
            $(".PrioSelector:visible").not(_PrioSelector).trigger("hide");

            // if it is hidden
            var e = $(_PrioSelector).find(":hidden").length ? "show" : "hide";

            $(_PrioSelector).trigger(e);

            return;
        }

        // hiding all...
        $(".PrioSelector:visible").trigger("hide");

        // ... and create the new one
        $.ajax({
            url: "/fotoweb/cmdrequest/rest/ActionsMenu.fwx",
            data: {
                f: this.foxtoken,
                pm: 1,
                at: GLOBALS.ArchiveType,
                ar: GLOBALS.ArchiveId,
                rd: +new Date()
            },
            success: function(xml){
                // creating and showing priority selector
                _PrioSelector = createPrioritySelector(xml);

                // get out if the prio node is invalid
                if(!_PrioSelector){
                    return;
                }

                // setting up the hide and show events
                $(_PrioSelector).bind("show", function (){
                    $(this).slideDown(300);
                });

                $(_PrioSelector).bind("hide", function (){
                    $(this).slideUp(300);
                });

                // if the document was clicked, hide it
                $(document).click(function(){
                    $(_PrioSelector).trigger("hide");
                });

                $(_PrioSelector).find("a").click(function(e){
                    e.preventDefault();
                    processPriorityChange(this);
                });

                $(_PrioSelector).hide();
                $(self).find(".ThumbContainer").append(_PrioSelector);
                $(_PrioSelector).trigger("show");


            },
            error: function(){
            // error occured
            }
        });
    }

    /**
     * Changes the priority depending on the given link item.
     *
     */
    function processPriorityChange(link){
        $.ajax({
            url: $(link).attr("href"),
            success: function(){
                var lvl = $(link).attr("href").match(/priority=(\d)/);
                lvl = lvl && lvl[1] ? lvl[1] : "";
                console.log("desired level", lvl);
                var prio = $(self).find(".Priority");
                $(prio).removeClass();
                $(prio).addClass("Priority Prio"+lvl);
                $(_PrioSelector).trigger("hide");
            }
        });
    }

    /**
     * Creates a complete priority selector.
     *
     * @namespace Thumbnail
     *
     * @param xml The xml of the /fotoweb/cmdrequest/ActionsMenu.fwx. It filters the Priority commands
     * for itself.
     */
    function createPrioritySelector(xml){
        var priorityItems = $(xml).find("[Url*=fwSetPriority]");
        if(!$(priorityItems).length){
            return false;
        }

        var PrioDiv = $('<div class="PrioSelector"></div>');
        var ul = $("<ul/>");
        var item = null;
        $(priorityItems).each(function(){
            item = $('<li><a href="'+$(this).attr("Url")+'"><span>'+$(this).attr("DisplayName")+'</span></a></li>');
            $(item).find("a").css("background-image", "url("+$(this).attr("IconUrl")+")");
            $(item).click(function(e){
                // prevent further bubbling
                e.stopImmediatePropagation();
            });
            $(ul).append(item);
        });
        $(PrioDiv).append(ul);

        return PrioDiv;
    }

    /**
     * Function which relocates to the specific preview site of the
     * thumbnail container.
     */
    this.goToPreview = function(){
        console.log("Going to preview", this);
        var location = GLOBALS.PreviewUrl+"?";
        location += "&position="+this.position;
        location += "&archiveType="+GLOBALS.ArchiveType;
        location += "&archiveId="+GLOBALS.ArchiveId;
        location += "&albumId="+GLOBALS.ArchiveId;
        location += "&sorting="+GLOBALS.Sorting;
        location += "&search="+GLOBALS.Search;
        location += "&fileId="+this.foxtoken;

        window.location = location;
    }

    /**
     * Shows the Zoom view.
     */
    this.showZoom = function(){
        OpenCompingImageWindow("Zoom.fwx"
            +"?position="+this.position
            +"&archiveId="+GLOBALS.ArchiveId
            +"&sorting="+GLOBALS.Sorting
            +"&columns="+GLOBALS.Columns
            +"&row="+GLOBALS.Rows
            +"&search="+GLOBALS.Search
            +"&fileId="+this.foxtoken
            +"&archiveType="+GLOBALS.ArchiveType, 475, 370);
    }

    /**
     * Toggles the selection of this Thumbnail Container.
     */
    this.toggle = function(){
        if( this.isMarked )
            this.unselect();
        else
            this.select();
    }

    /**
     * Selects the Thumbnail Container.
     */
    this.select = function(){

        if(!$("#NavigationPaneControl")[0].SelectionPanel) {
            return;// get out if the navigation pane control is unavailable
        }

        console.log("Select Thumbnail Container", this);

        $("#NavigationPaneControl")[0].addToSelection(this, function(){
            self.fckCheckbox.checked = true;
            self.update();
        }, "xml");
    };

    /**
     * Unselects the Thumbnail Container.
     */
    this.unselect = function(){
        
        if(!$("#NavigationPaneControl")[0].SelectionPanel) {
            return;// get out if the navigation pane control is unavailable
        }

        console.log("Unselect Thumbnail Container", this);

        $("#NavigationPaneControl")[0].removeFromSelection(this, function(){
            self.fckCheckbox.checked = false;
            self.update();
        }, "xml");
    };

    /**
     * Updates the Thumbnail Container.
     *
     * Currently it just updates the marking of the Thumbnail to signilize if
     * the Thumbnail is selected or not.
     */
    this.update = function(){

        console.log("Updating Thumbnail Container", this);

        // marking
        if ( this.fckCheckbox.checked ) {
            $(this).addClass('DocMtxSelectedCell');
            $(this).removeClass('DocMtxCell');
            this.isMarked = true;
        }
        else{
            $(this).removeClass('DocMtxSelectedCell');
            $(this).addClass('DocMtxCell');
            this.isMarked = false;
        }
    }

    /**
     * Resizes the Thumbnail to the given scale value.
     *
     * @param scale
     */
    this.resize = function(scale){
        var img = $(this).find(".Thumb img")[0];
        var ImageNum = $(this).attr("id").replace(/\D/g, "");
        var curThumb = this;
        var reloadParam = "";

        if(window.location.search.match(/reload=1/)){
            reloadParam = "&dp="+new Date().getTime();
        }

        // That's the magic! To achieve the effect, that an img will be faded in AFTER it was completely loaded,
        // we has to bind an onload event to the image, BEFORE we set the new source!
        img.onload = function(){
            $(this).fadeIn(300);
        };

        $(img).fadeOut(300, function(){
            // TODO: getting rid of setting the width for all containers. Setting the width to the image should be the goal. - visibility="hidden" ?
            $(curThumb).find(".ThumbContainer").width(scale+"px");
            $(curThumb).find(".Thumb").width(scale+"px");
            $(curThumb).find(".Thumb").height(scale+"px");
            var src = $(img).attr("src");
            //src = src.replace(/sz=\d*/, "sz="+scale);
            src = "/fotoweb/cmdrequest/rest/Preview.fwx?rt=1&f="+GLOBALS.ImageInfos[ImageNum].foxtoken+"&sz="+scale+reloadParam;
            $(img).attr("src", src);
        });
    }

    /**
     * Initiates the thumbnail after declaration.
     *
     */
    this.init();
}

/**
 * <b>Mutator</b>
 * NavigationPaneControl 
 *
 * @constructor
 *
 */
function NavigationPaneControl()
{
    var self = this;

    var m_DownloadProfilesPopup = new DownloadPopup();
    var m_PreviewSizePopup = false;
    var _syncTimeout = false;

    var _toAddQueue = [];
    var _toRemoveQueue = [];

    /**
     * Will be used for an additional call of the selection update, so that a
     * synchron selection list can be provided.
     *
     * @namespace NavigationPaneControl
     *
     * @private
     */
    this.updateSelectionRecallTimeoutIndicator = 0;

    this.ActionPanel     = $(self).find(".ActionsMenu")[0];
    this.SelectionPanel  = $(self).find(".Selection")[0];
    this.StructurePanel  = $(self).find(".Structure")[0];
    this.DataMiningPanel = $(self).find(".DataMining")[0];
    this.SizesPanel      = $(self).find(".SizesMenu")[0];
    this.KeywordPanel    = $(self).find(".KeywordMenu")[0];
    this.SearchtreePanel = $(self).find(".Searchtree")[0];

    /**
     * Pseudo constructor. Will be called at the end of the class.
     *
     * @namespace NavigationPaneControl
     */
    this.construct = function()
    {
        initPanelRelations();

        $.asap(function(){
            if(!$(".Thumbnail").length){
                return true;
            }
            var ret = true;
            $(".Thumbnail").each(function(){
                if(!this.complete){
                    ret = false;
                }
            });
            return ret;
        }, function(){
            self.initSelection();
            updateActions();
            initStructure();
            initDataMining();
            initSearchtree();
            initSizesMenu();
            initKeywordLists();
            self.initOutboxMenu();
        }, {delay: 500});
    }

    /**
     * Initiates the preview Panel which will be used on the preview site to show the
     * preview sizes for the popup preview.
     *
     * @namespace NavigationPaneControl
     */
    function initSizesMenu(){
        // if it does not exist, get out
        if(!self.SizesPanel){
            return;
        }

        $(self.SizesPanel).hide();
        
        initSizesPopup();

        var options = {
            ar: GLOBALS.ArchiveId
        };

        var openPreviewSize = function(size){

            showLoading();

            console.log("open preview viewer with size", size);
            $('body').addClass("loading");
            var img = new Image();
            img.onload = function(){
                m_PreviewSizePopup.show();
                $('body').removeClass("loading");
                hideLoading();
            };
            img.src = "/fotoweb/cmdrequest/rest/preview.fwx/"+GLOBALS.filename+"?rt=1&f="+GLOBALS.foxtoken+"&sz="+size;
            m_PreviewSizePopup.setCaption(TRANSLATIONS.PreviewSize+" - "+size);
            m_PreviewSizePopup.setContent(img);
        };

        $.get("/fotoweb/cmdrequest/rest/PreviewSizeList.fwx", options, function(xml){

            var list = $(self.SizesPanel).find("ul").empty();

            $(xml).find("PreviewSize").each(function(){

                var name = $(this).attr("Name");
                var size = $(this).attr("Size");
                var item = $('<li><a href="javascript:">'+name+' - '+size+'</a></li>');

                $(item).find("a").click(function(){
                    openPreviewSize(size);
                });
                
                $(list).append(item);
            });

            $(self.SizesPanel).fadeIn(300);
        });
    }

    /**
     * creates and initiates the popup used for showing the preview sizes.
     *
     */
    function initSizesPopup(){
        m_PreviewSizePopup = new PopDiv("PreviewSizePopUp");
        m_PreviewSizePopup.setOverlaySpeed(200, 200);
        m_PreviewSizePopup.setPopDivSpeed(200, 200);
    }

    /**
     * Initiates the outbox menu if there is one.
     *
     */
    this.initOutboxMenu = function(){
        if($("#OutboxMenuList").length)
            this.updateOutboxMenu();
    }

    /**
     * Updates the outbox menu if there is one.
     *
     */
    this.updateOutboxMenu = function(callback){
        this.updateMenu("/fotoweb/cmdrequest/rest/outboxMenu.fwx", "", $("#OutboxMenuList")[0], callback);
    }

    /**
     * Updates any <b>list</b> with the parsed xml requested by the given
     * <b>url</b> and <b>data</b>.
     * Afterwards <b>callback</b> will be called.
     *
     */
    this.updateMenu = function(url, data, list, callback){
        $.get(url, data, function(xml){

            $(list).empty();

            var selector = "";

            if($(xml).find("FotoWebRest > *:first-child > MenuItem").length > 1)
                selector = "FotoWebRest > *:first-child > MenuItem";
            else
                selector = "FotoWebRest > *:first-child > MenuItem > MenuItem";

            $(xml).find(selector).each(function(){
                self.integrateIntoMenu(this, $(list));
            });

            $(list).closest(".multilvl-menu")[0].update(); // updating the jquery menu

            if(callback)
                callback();

        }, "xml");
    }

    /**
     * Will be called recursively to generate nested unordered lists
     * for the action panel.
     *
     * Icons and displaynames will be determined by the xml.
     *
     * @param MenuItem node from the xml which describes the to-insert item
     * @param RootUl reference to the ul node which determines the root of the given MenuItem
     */
    this.integrateIntoMenu = function(MenuItem, RootUl){
        var DisplayName = $(MenuItem).attr("DisplayName");
        var href = $(MenuItem).attr("Url");
        var iconUrl = $(MenuItem).attr("IconUrl");
        var li = "";

        // showing a Seperator
        // TODO: ActionsMenuSeparator to MenuSeparator
        if(DisplayName == "---") {
            RootUl.append("<li><div class='MenuSeperator'></div></li>");
            return;
        }

        // constructing a single li element and append it to the RootUl
        href = typeof href == "undefined" ? "" : href;

        var a = document.createElement("a");

        // exception of the deletion relocating to a specific source
        if(href.match(/DeleteFiles/i) && GLOBALS.deleteReturnUrl)
            href = GLOBALS.deleteReturnUrl;

        // if it's the rating we don't need the display text, so we exclude it here
        if(!href.match(/SetRating/i))
            $(a).text(DisplayName);

        // if it's the download action we propably need to modify the url accordingly to configured ProcessProfiles
        if(href.match(/download/i))
            $(a).click(self.processDownloadRequest);

        $(a).attr("href", href);

        $(a).css("background-image", "url("+iconUrl+")");
        var subindicator = document.createElement("div");
        $(subindicator).addClass("submenu-indicator");
        a.appendChild(subindicator);

        li = document.createElement("li");
        var span = document.createElement("span");
        span.appendChild(a);
        $(span).hover(function(){
            $(this).addClass("hover");
        }, function(){
            $(this).removeClass("hover");
        });

        li.appendChild(span);

        // recursive call of integrateIntoActionsMenu()
        if(MenuItem.hasChildNodes())
        {
            // signilize, that the submenu-indicator is active
            $(subindicator).addClass("active");

            // creating a new root ul and call this function recursively
            var ul = document.createElement("ul");
            $(MenuItem).children("MenuItem").each(function(){
                self.integrateIntoMenu(this, ul);
            });
            $(li).append(ul);
        }

        $(RootUl).append(li);
    }

    /**
     * Initiates the selection if there is one.
     */
    this.initSelection = function(){
        if(!this.SelectionPanel){
            return;
        }

        $(".Grid").add(".gridTable").explore(".SelectableImage", "DocMtxSelectedCell");

        // adding removing functionality for each selection item as live event
        $(".SelectionItemRemove").live("click", function(){
            self.removeFromSelectionBySelectionItem(this);
        });

        $("#btnClearSelection").live("click", function(e){
            $("body").append('<form name="ClearSelectionForm" method="post" action="/fotoweb/cmdrequest/ClearSelection.fwx"></form>');
            document.ClearSelectionForm.submit();
        });

        $(this.SelectionPanel).find(".PanelHeader").append('<span id="btnClearSelection" title="'+TRANSLATIONS.ClearSelection+'"></span>');
        $("#btnClearSelection").hide();

        self.updateSelection();
    }

    /**
     * Initiates the relations between the Panels.
     *
     * @namespace NavigationPaneControl
     */
    function initPanelRelations() {
        // updating the Actions when activating
        if(self.ActionPanel){
            self.ActionPanel.setPreActivateEvent(function(){
                updateActions();
            });
        }

        // TODO: finding another way to solve the following workarounds, it is a lack of
        // functionality of the treeview plugin

        if(self.StructurePanel) {
            self.StructurePanel.setPostShowEvent(function(){
                // workaround for "display none" objects
                // those classes can only automatically be added if the treeview is
                // visible or "visibility hidden". So we reassign those classes manually.
                $(self).find(".StructureList li").filter(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.expandable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastExpandable);

                // handle open ones
                $(self).find(".StructureList li").not(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.collapsable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastCollapsable);
            });
        }

        if(self.DataMiningPanel) {
            self.DataMiningPanel.setPostShowEvent(function(){
                // workaround for "display none" objects
                // those classes can only automatically be added if the treeview is
                // visible or "visibility hidden". So we reassign those classes manually.
                $(self).find(".DataMining li").filter(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.expandable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastExpandable);

                // handle open ones
                $(self).find(".DataMining li").not(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.collapsable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastCollapsable);
            });
        }
        
        if(self.KeywordPanel) {
            self.KeywordPanel.setPostShowEvent(function(){
                // workaround for "display none" objects
                // those classes can only automatically be added if the treeview is
                // visible or "visibility hidden". So we reassign those classes manually.
                $(self).find(".KeywordList li").filter(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.expandable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastExpandable);

                // handle open ones
                $(self).find(".KeywordList li").not(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.collapsable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastCollapsable);
            });
        }

        if(self.SearchtreePanel) {
            self.SearchtreePanel.setPostShowEvent(function(){
                // workaround for "display none" objects
                // those classes can only automatically be added if the treeview is
                // visible or "visibility hidden". So we reassign those classes manually.
                $(self).find(".SearchtreeList li").filter(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.expandable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastExpandable);

                // handle open ones
                $(self).find(".SearchtreeList li").not(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.collapsable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastCollapsable);
            });
        }
    }

    /**
     * Initiates a structure panel.
     *
     * @namespace NavigationPaneControl
     */
    function initSearchtree()
    {
        if(!self.SearchtreePanel){
            return;
        }

        var SearchtreeList = $(self.SearchtreePanel).find("ul")[0];

        // will be called for each node to get its information
        var getInfoCallback = function(item){

            // getting the displaying name
            var display = "";
            display = $(item).attr("Name");

            // getting the search string
            var href = "";
            if ($(item).attr("Search")) {
                href = "?search="+encodeURIComponent($(item).attr("Search"));
            }
            else {
                href = "javascript:"; // disabling the link
            }

            return {
                display: display,
                href: href
            }
        }

        // will be a called for each expand process
        var searchTreeExpendProcess = function(){
            console.log("called searchTreeProcess");

            var ParentLiNode = this;
            var curNamespace = $(this).data("Name");

            var QueryString = "?ar="+GLOBALS.ArchiveId+"&nm="+$(this).data("Name")
            +"&r=1&rt="+$(this).data("Root");

            var options = {
                url: "fotoweb/cmdrequest/rest/SearchTreeContent.fwx"+QueryString,
                async: false, // cannot be async
                datatype: "xml",
                success: function(xml){
                    var ul = document.createElement("ul");
                    $(xml).find("SearchTreeContent > SearchNode").each(function(){
                        var liNode = integrateIntoTree(this, ul, getInfoCallback);
                        $(liNode).data("Name", curNamespace);
                        $(liNode).data("Root", $(this).attr("Root"));
                    });
                    console.log("new created ul", ul);

                    $(SearchtreeList)[0].addBranch(ParentLiNode, ul);
                }
            };

            $.ajax(options);
        };

        // initiating the first level
        $.get("/fotoweb/cmdrequest/rest/SearchTreeList.fwx", {
            ar: GLOBALS.ArchiveId
        }, function(xml){

            $(SearchtreeList).empty();

            $(xml).find("SearchTree").each(function(){
                var liNode = integrateIntoTree(this, SearchtreeList, getInfoCallback);
                // adding a dummy, so treeview thinks it is a expandable item
                // TODO: Has every treeview children? Currently we suppose that!
                $(liNode).append($("<ul><li></li></ul>"));
                $(liNode).data("Name", $(this).attr("Name"));
                $(liNode).data("Root", "/");
            });

            if($(SearchtreeList).find("li").length){
                $(SearchtreeList).xenview({
                    //                    animated: "fast",
                    collapsed: true,
                    persist: "cookie",
                    cookieId: "SearchTreeFor"+GLOBALS.ArchiveId
                }, function(){
                    console.log("expand process on", this);

                    searchTreeExpendProcess.call(this);
                });

                $(self.SearchtreePanel).fadeIn(300);
            }

        }, "xml");
    }

    /**
     * Initiates a structure panel.
     *
     * @namespace NavigationPaneControl
     */
    function initKeywordLists()
    {
        if(!self.KeywordPanel){
            return;
        }

        var KeywordList = $(self.KeywordPanel).find("ul")[0];

        // will be called for each node to get its information
        var getInfoCallback = function(item){

            // getting the displaying name
            var display = "";
            display = $(item).attr("Element") ? $(item).attr("Element"): $(item).attr("Name");

            // getting the search string
            var href = "";
            if ($(item).attr("Element")) {
                href = "?search="+encodeURIComponent($(item).attr("Element"));
            }
            else {
                href = "javascript:"; // disabling the link
            }

            return {
                display: display,
                href: href
            }
        }

        // will be a called for each expand process
        var processExpandClick = function(){
            console.log("called expand process");

            var ParentLiNode = this;

            var QueryString = "?ar="+GLOBALS.ArchiveId+"&id="+$(this).data("Id");

            var options = {
                url: "fotoweb/cmdrequest/rest/KeywordListContent.fwx"+QueryString,
                async: false, // cannot be async
                datatype: "xml",
                success: function(xml){
                    var ul = document.createElement("ul");
                    $(xml).find("KeywordListContent > KeywordList").each(function(){
                        integrateIntoTree(this, ul, getInfoCallback);
                    });
                    console.log("new created ul", ul);

                    $(KeywordList)[0].addBranch(ParentLiNode, ul);
                }
            };

            $.ajax(options);
        };

        // initiating the first level
        $.get("/fotoweb/cmdrequest/rest/KeywordList.fwx", {
            ar: GLOBALS.ArchiveId
        }, function(xml){

            $(KeywordList).empty();

            $(xml).find("KeywordList").each(function(){
                var liNode = integrateIntoTree(this, KeywordList, getInfoCallback);
                // adding a dummy, so treeview thinks it is a expandable item
                // TODO: Has every treeview children? Currently we suppose that!
                $(liNode).append($("<ul><li></li></ul>"));
                $(liNode).data("Id", $(this).attr("Id"));
            });

            if($(KeywordList).find("li").length){
                $(KeywordList).xenview({
                    collapsed: true,
                    persist: "cookie",
                    cookieId: "KeywordListFor"+GLOBALS.ArchiveId
                }, function(){
                    console.log("expand process on", this);

                    processExpandClick.call(this);
                });

                $(self.KeywordPanel).fadeIn(300);
            }

        }, "xml");
    }

    /**
     * Initiates a structure panel.
     *
     * @namespace NavigationPaneControl
     */
    function initStructure()
    {
        if(!self.StructurePanel){
            return;
        }

        var StructureList = $(self.StructurePanel).find("ul")[0];

        // will be called for each node to get its information
        var getInfoCallback = function(item){

            // getting the displaying name
            var display = $(item).attr("DisplayName");

            // getting the search string
            var href = "/fotoweb/Grid.fwx?search="+$(item).attr("SearchString");
            
            return {
                display: display,
                href: href
            }
        }

        // will be called for each expand process
        var processExpandClick = function(){
            console.log("called expand process");

            var ParentLiNode = this;
            
            var rootLvl = encodeURIComponent($(this).data("Root"));

            var QueryString = "?ar="+GLOBALS.ArchiveId+"&rt="+rootLvl+"&r=1";

            var options = {
                url: "fotoweb/cmdrequest/rest/FolderList.fwx"+QueryString,
                async: false, // cannot be async
                datatype: "xml",
                success: function(xml){
                    var ul = document.createElement("ul");
                    $(xml).find("FolderList > Folder > Folder").each(function(){
                        var newLiNode = integrateIntoTree(this, ul, getInfoCallback);
                        $(newLiNode).data("Root", $(this).attr("Root"));
                    });
                    console.log("new created ul", ul);

                    $(StructureList)[0].addBranch(ParentLiNode, ul);
                }
            };

            $.ajax(options);
        };

        // initiating the first level
        $.get("/fotoweb/cmdrequest/rest/FolderList.fwx", {
            ar: GLOBALS.ArchiveId,
            r: 1
        }, function(xml){

            $(StructureList).empty();

            $(xml).find("FolderList > Folder").each(function(){
                var liNode = integrateIntoTree(this, StructureList, getInfoCallback);
                // storing the data we need for a consecutive request to the given node
                $(liNode).data("Root", $(this).attr("Root"));
            });

            if($(StructureList).find("li").length){
                $(StructureList).xenview({
                    collapsed: true,
                    persist: "cookie",
                    cookieId: "StructureFor"+GLOBALS.ArchiveId
                }, function(){
                    console.log("expand process on", this);
                    processExpandClick.call(this);
                });

                $(self.StructurePanel).fadeIn(300);
            }

        }, "xml");
    }

    /**
     * Initiates a structure panel.
     *
     * @namespace NavigationPaneControl
     */
    function initDataMining()
    {
        if(!self.DataMiningPanel){
            return;
        }

        var DataMiningList = $(self.DataMiningPanel).find("ul")[0];

        // will be called for each node to get its information
        var getInfoCallback = function(item){

            // getting the displaying name
            var display = $(item).attr("Name") 
                ? $(item).attr("Name")
                : $(item).attr("Display");

            // getting the search string
            var href = $(item).attr("Search") 
                ? "?search="+encodeURIComponent($(item).attr("Search"))
                : "javascript:";

            return {
                display: display,
                href: href
            }
        }

        // will be called for each expand process
        var processExpandClick = function(){
            console.log("called expand process");

            var ParentLiNode = this;

            var rootLvl = $(this).data("Root");

            var QueryString = "?ar="+GLOBALS.ArchiveId+"&rt="+rootLvl;

            var options = {
                url: "fotoweb/cmdrequest/rest/DataMining.fwx"+QueryString,
                async: false, // cannot be async
                datatype: "xml",
                success: function(xml){
                    var ul = document.createElement("ul");
                    $(xml).find("DataMining > Value").each(function(){
                        integrateIntoTree(this, ul, getInfoCallback);
                    });
                    console.log("new created ul", ul);

                    $(DataMiningList)[0].addBranch(ParentLiNode, ul);
                }
            };

            $.ajax(options);
        };

        // initiating the first level
        $.ajax({
            url: "/fotoweb/cmdrequest/rest/DataMining.fwx",
            data: {
                ar: GLOBALS.ArchiveId
            },
            contentType: "xml",
            success: function(xml){

                $(DataMiningList).empty();

                $(xml).find("DataMining > Field").each(function(){
                    var liNode = integrateIntoTree(this, DataMiningList, getInfoCallback);
                    // faking the next level
                    $(liNode).append($("<ul><li></li></ul>"));
                    // storing the data we need for a consecutive request to the given node
                    $(liNode).data("Root", $(this).attr("Root"));
                });

                if($(DataMiningList).find("li").length){
                    $(DataMiningList).xenview({
                        collapsed: true,
                        persist: "cookie",
                        cookieId: "DataMiningFor"+GLOBALS.ArchiveId
                    }, function(){
                        console.log("expand process on", this);
                        processExpandClick.call(this);
                    });

                    $(self.DataMiningPanel).fadeIn(300);
                }

            }
        });
    }

    /**
     * Integrates a single item into the given Ul and calls this function
     * recursive for each of its childs.
     * 
     * @namespace NavigationPaneControl
     *
     * @param item The item node which will be used for integration.
     * @param RootUl The Ul node where the created node will be injected.
     * @param getTargetInfo A anonymous function which will be called on each item. It should return
     * an Object with the properties <b>display</b> for the displaying name and <b>href</b> for the
     * link which will be used.
     * @param recursive
     *
     * @return node Node of the injected li element
     *
     */
    function integrateIntoTree(item, RootUl, getTargetInfo, recursive){
        
        // default value for recursive mode
        recursive = typeof recursive == "undefined" ? true: recursive;

        var info = getTargetInfo(item);
        var li = document.createElement("li");
        var a = document.createElement("a");
        
        $(a).attr("href", info.href);
        $(a).text(info.display);
        $(li).append(a);

        if(recursive && item.hasChildNodes())
        {
            var ul = document.createElement("ul");
            $(item).children().each(function(){
                integrateIntoTree(this, ul, getTargetInfo);
            });
            $(li).append(ul);
        }

        $(RootUl).append(li);

        return li;
    }

    /**
     * Updates the actions panel.
     *
     * @namespace NavigationPaneControl
     *
     */
    function updateActions()
    {
        if(typeof GLOBALS == "undefined")
            return;

        if( !self.ActionPanel || !$(self.ActionPanel).find(".ActionsList").length)
            return;

        var postbackUrl = GLOBALS.PostBackUrl.match(/\?(.*)/)
        ? GLOBALS.PostBackUrl+"&reload=1" : GLOBALS.PostBackUrl+"?reload=1";

        var ActionsOptions = {
            rn: postbackUrl,
            at: GLOBALS.ArchiveType,
            rd: +new Date().getTime()
        };

        if(GLOBALS.ArchiveId)
            ActionsOptions.ar = GLOBALS.ArchiveId;

        // if we are on the preview site, let the server know for which picture
        // we want to get the actions list
        if(GLOBALS.Preview)
        {
            ActionsOptions.pm = "1";
            ActionsOptions.f = GLOBALS.foxtoken;
        }
        else
        {
            ActionsOptions.pm = "0";
        }
        
        self.updateMenu("/fotoweb/cmdrequest/rest/ActionsMenu.fwx",
            ActionsOptions,
            $(self.ActionPanel).find(".ActionsList")[0]);
    }

    /**
     * Processes a download request. NOTE: The context of "this" should be the source object
     * where the request was received. Use processDownloadRequest.call(srcObject).
     */
    this.processDownloadRequest = function(){
        console.log("process download request...");
        
        /* determine where the click comes from */
        var execURL = "";
        var foxtoken = ""; // for single download
        var downloadType = "single";

		if($(this).attr("href") && $(this).attr("href").match(/download.dll\/ZipDownload\/SelectionList/i))
		{
			execURL = $(this).attr("href");
			downloadType = "selection";
		}
        // if it was a click on a link, which has probably the correct single download url within -> single Download
        else if($(this).attr("href") && $(this).attr("href").match(/download.dll/i)) {
            foxtoken = GLOBALS.foxtoken;
            execURL = $(this).attr("href");
        }
        // if it has a foxtoken, so it seems it is a Thumbnail object -> single Download
        else if(this.foxtoken) {
            foxtoken = this.foxtoken;
            execURL = "/fotoweb/fwbin/download.dll/"+this.filename+"?D="+this.downloadId+"&ForceSaveDialog=yes";
        }
        // if we don't have a clue -> multiple download depending on the selection
        else
        {
            execURL = "/fotoweb/cmdrequest/Download.fwx?incsel=1";
            downloadType = "selection";
        }

        $.ajax({
            url:"/fotoweb/cmdrequest/rest/ProcessingProfileList.fwx",
            data: {
                ar: GLOBALS.ArchiveId
            },
            datatype: "xml",
            success: function(xml){
                console.log("response received");
                var DirectDownloads = $(xml).find("DirectDownloads");
                var Type = $(DirectDownloads).attr("Type");
                console.log("Preconfigured method by FAC", Type);

                console.log("download invoked by", downloadType);

                if(Type != "Custom") {
                    console.log("automatic profile selection");
                    window.location = execURL;
                }
                else {
                    if(downloadType == "single") {
                        var img = new Image();
                        img.onload = function(){
                            console.log("profile selection by popup");
                            m_DownloadProfilesPopup.show([img], xml, execURL);
                        }
                        img.src = "/fotoweb/cmdrequest/rest/Preview.fwx?rt=1&f="+foxtoken+"&sz=240";
                    }
                    else {
                        getSelectionPictures(function (){
                            $(this).fadeIn(300);
                        }, function(Images){
                            m_DownloadProfilesPopup.show(Images, xml, execURL);
                        });
                    }
                }

            },
            error: function(){ // fallback
                console.log("no correct response - fallback");
                console.log("automatic profile selection");
                window.location = execURL;                
            }
        });

        return false; // prevent following the real link location
    }


    /**
     * Loads all the selected images, sets an onload event for each
     * and hands them up to the callback function.
     *
     * @namespace NavigationPaneControl
     *
     * @param {function} onloadEvent Event which will be added to the images.
     * @param {function} callback 
     */
    function getSelectionPictures(onloadEvent, callback){
        onloadEvent = typeof onloadEvent == "function" ? onloadEvent : function(){};

        $.get("/fotoweb/cmdrequest/GetSelection.fwx?sendXML=1", {
            sendXML: 1,
            rd: +new Date
        }, function (xml){

            var Images = new Array();
            var img;
            var foxtoken;
            var xmlItems = $(xml).find("Item");
            var scaleParams = "&sz=240"; // scale value for single image download popup

            // if there are more then one images, create squares
            if(xmlItems.length > 1){
                scaleParams = '&as=rt&asw=82&ash=82';
            }

            $(xmlItems).each(function(){
                img = new Image();
                foxtoken = $(this).attr("foxToken");
                img.onload = onloadEvent;
                img.src = '/fotoweb/cmdrequest/rest/Preview.fwx?rt=1&f='+foxtoken+scaleParams+'&rd='+new Date;
                Images.push(img);
            });

            callback(Images);
        }, "xml");
    }

    /**
     * Synchronizes the current selection with the server.
     *
     * @namespace NavigationPaneControl
     */
    function syncSelection(){
        // declarations
        var i, forLength, QueryStringParts = [];

        // removing all queued images
        forLength = _toRemoveQueue.length;
        for(i = 0; i < forLength; i++){
            QueryStringParts.push("f="+_toRemoveQueue[i].foxtoken);
        }
        QueryStringParts.push("sendXML=yes");

        if(forLength > 0){
            $.ajax({
                url: "/fotoweb/cmdrequest/RemoveFromSelection.fwx",
                data: QueryStringParts.join("&"),
                async: false,
                dataType: 'xml',
                type: 'POST'
            });
        }

        // resetting query string parts
        QueryStringParts = []; 

        // adding all queued images
        forLength = _toAddQueue.length;
        for(i = 0; i < forLength; i++){
            QueryStringParts.push("f="+_toAddQueue[i].foxtoken);
        }
        QueryStringParts.push("sendXML=yes");

        if(forLength > 0){
            $.ajax({
                url: "/fotoweb/cmdrequest/AddToSelection.fwx",
                data: QueryStringParts.join("&"),
                async: false,
                dataType: "xml",
                type: "POST"
            });
        }

        self._updateSelectionStatus();

        // resetting queues
        _toAddQueue = [];
        _toRemoveQueue = [];
    }

    /**
     * rebuild and updates the selection items
     *
     * @namespace NavigationPaneControl
     */
    this.updateSelection = function(){
        
        var options = {
            sendXML: 1,
            dp: +new Date
        };

        $.ajax({
            url:"/fotoweb/cmdrequest/GetSelection.fwx",
            data: options,
            async: false,
            success: function(xml){
                self.updateSelectionByXML(xml);
            }
        });
    }

    /**
     * rebuild and updates the selection by a given xml
     *
     * @namespace NavigationPaneControl
     *
     * @param {string} xml Data of the selected images.
     */
    this.updateSelectionByXML = function(xml)
    {
        console.log("updating selection by xml", xml);

        var Items = $(xml).find("Item");

        // getting a fresh template node for the selection items
        var SelectionItemTpl = $(self.SelectionPanel).find(".SelectionItem.node-tpl").clone().removeClass("node-tpl");

        // declaration
        var newItem = "";
        var foxtoken = "";
        var pvtoken = "";
        var i = 0;
        var j = 0;

        var selectionList = $(self.SelectionPanel).find(".SelectionList");
        var curSelectionItems = $(selectionList).find(".SelectionItem");
        var isAlreadyIn = false;
        var itemsLength = Items.length;
        var selItemsLength = curSelectionItems.length;
        var toPersistItems = new Array();

        // building and cloning the necessary selection item nodes
        for(i=0; i<itemsLength; i++) {
            isAlreadyIn = false;

            newItem = $(SelectionItemTpl).clone(); // cloning the node tpl
            foxtoken = $(Items[i]).attr("foxToken");
            pvtoken = $(Items[i]).attr("pvToken");

            // looking in the current selection items if there is already the image selected
            for(j=0; j<selItemsLength; j++) {

                if(curSelectionItems[j].id == "foxtoken_"+foxtoken) {
                    isAlreadyIn = true;
                    // memorize which images of the selection where also found in the xml
                    toPersistItems[j] = true;
                    break; // found, so get out of this loop
                }

            }

            // if the image is already in the selection box
            if(isAlreadyIn)
                continue; // get to the next image received by the xml

            $(newItem).attr("id", "foxtoken_"+foxtoken);

            var thumb = new Image();
            // opera does not trigger the onload event of pictures having the style
            // rule display="none". So to keep thinks working, we're firstly using the visibility
            // style and set it to "hidden", afterwards, when the onload event was finally
            // triggered we switch to the display="none" style rule and directly
            // set visibility to "visible" again. That will keep the animation going well, even for opera.
            // NOTE: Since the image is not integrated into the DOM yet, it makes no difference of using visibility or display.
            $(thumb).css("visibility", "hidden");
            $(thumb)[0].onload = function(){
                $(this).hide();
                $(this).css("visibility", "visible");
                $(this).fadeIn(300);
                $(this).closest(".SelectionItem").removeClass("loading");
            };

            // replacing the image element of the node template with the new created image
            $(newItem).find("img").replaceWith(thumb);
            $(newItem).addClass("loading");

            // according to the preview access of the user take the Preview.fwx requests or the old preview.dll behavior
            // NOTE: By the preview.dll way it is not possible to show resized and cropped images for the scaling or the selection functionality
            if(GLOBALS.PreviewAccess)
                $(thumb).attr("src", '/fotoweb/cmdrequest/rest/Preview.fwx?rt=1&f='+foxtoken+'&as=rt&asw=63&ash=63&rd='+(+new Date()));
            else
                $(thumb).attr("src", "/fotoweb/cmdrequest/rest/preview.fwx?f="+pvtoken);

            // add the image to the selection
            selectionList.append(newItem);
        }

        // removing the selection items which where not found in the received
        // updated xml response
        for(j=0; j<selItemsLength; j++) {

            if(toPersistItems[j] == true)
                continue;

            $(curSelectionItems[j]).fadeOut("fast", function(){
                // removing the item with an delay, to provide smoother reaction
                var item = this;
                window.setTimeout(function(){
                    $(item).remove();
                    delete item;
                }, 3000);
            });
        }

        this._updateSelectionStatus();
    }

    this._updateSelectionStatus = function(){
        // updating the navigation panel accordingly
        // if selection has entries
        console.log("current Items in selection", $(this.SelectionPanel).find(".SelectionList .SelectionItem"));

        if($(self.SelectionPanel).find(".SelectionList .SelectionItem").length > 0) {
            if(self.ActionPanel){
                self.ActionPanel.activate();
                self.ActionPanel.show();
            }
            if(self.SelectionPanel){
                self.SelectionPanel.activate();
                self.hideSelectionHintText();
                self.SelectionPanel.show();
                $("#btnClearSelection").show();
            }
        }
        else { // if selection is empty
            if(self.ActionPanel){
                self.ActionPanel.disable();
            }
            if(self.SelectionPanel){
                self.showSelectionHintText();
            }
            $("#btnClearSelection").hide();
        }
        
        $(self.SelectionPanel).fadeIn(300);
    }

    /**
     * Shows a hint of the keyboard controls within the selection panel
     *
     */
    this.showSelectionHintText = function(){
        if($(self.SelectionPanel).find(".Hint").length){
            $(self.SelectionPanel).find(".Hint").slideDown(300);
            return;
        }

        // loading the text
        $(self.SelectionPanel).find(".SelectionList").load("/fotoweb/SelectionHint.fwx");
        self.SelectionPanel.show();
    }

    /**
     * Hides the hint of the keyboard controls
     *
     */
    this.hideSelectionHintText = function(){
        $(self.SelectionPanel).find(".Hint").slideUp(300);
    }

    /**
     * Handles the event on clicking an remove button under a thumbnail within
     * the Selection.
     *
     * @namespace NavigationPaneControl
     */
    this.removeFromSelectionBySelectionItem = function(selItem)
    {
        var SelectionItem = $(selItem).closest(".SelectionItem")[0];
        var foxtoken = $(SelectionItem).attr("id").replace(/foxtoken_/, "");

        var imgnum = 0;
        var foundInGrid = false;
        var ImgCell;
        // go through all current pictures in the grid and look, if the
        // picture is in there. If so, just deselect it, otherwise remove it manually!
        for(imgnum in GLOBALS.ImageInfos) {
            ImgCell = $("#imgCell"+imgnum)[0];
            if(foxtoken == ImgCell.foxtoken) {
                foundInGrid = true;
                ImgCell.unselect();
            }
        }

        // if it was not found, manually deselecting it from the selection
        if(!foundInGrid)
        {
            $.get("/fotoweb/cmdrequest/RemoveFromSelection.fwx", {
                sendXML: 1,
                fileId: foxtoken,
                dp: +new Date
            }, function(){
                self.updateSelection();
            }, "xml");
        }
    }

    /**
     * Adds an image to the Selection.
     *
     * @param {Thumbnail} Thumbnail Instance of the image node/object which should be added to the
     * Selection.
     * @param {Function} callback Function which will be called after adding process.
     *
     */
    this.addToSelection = function(Thumbnail, callback){

        // only allow selections if the system is currently not busy
        if($('body').hasClass("loading")){
            return;
        }

        _toAddQueue.push(Thumbnail);
        this._injectIntoSelection(Thumbnail);

        if(typeof callback == "function"){
            callback();
        }

        if(_syncTimeout){
            window.clearTimeout(_syncTimeout);
        }

        _syncTimeout = window.setTimeout(function(){
            syncSelection()
        }, CONFIG.SelectionSyncDelay);
    }

    /**
     * Removes an image from the Selection.
     *
     * @param Thumbnail Instance of the image node/object which should be removed
     * from the Selection.
     * 
     * @param callback Function which will be called after adding process.
     */
    this.removeFromSelection = function(Thumbnail, callback){

        // only allow selections if the system is currently not busy
        if($('body').hasClass("loading")){
            return;
        }

        _toRemoveQueue.push(Thumbnail);
        this._stripFromSelection(Thumbnail);

        if(typeof callback == "function"){
            callback();
        }

        if(_syncTimeout){
            window.clearTimeout(_syncTimeout);
        }

        _syncTimeout = window.setTimeout(function(){
            syncSelection()
        }, CONFIG.SelectionSyncDelay);

    }

    /**
     * Removes the given (grid) Thumbnail object from selection panel.
     *
     * @param Thumbnail A Thumbnail object/node of the grid.
     */
    this._stripFromSelection = function(Thumbnail){
        console.log("stripping from selection list", Thumbnail);

        $(self.SelectionPanel).find(".SelectionItem").each(function(){
            if(this.id == "foxtoken_"+Thumbnail.foxtoken){
                var img = this;
                $(this).fadeOut(300, function(){
                    $(img).remove();
                    delete img;
                });
            }
        });
    }

    /**
     * Injects the given (grid) Thumbnail object/node to the selection panel.
     *
     * @param Thumbnail A Thumbnail object/node of the grid.
     */
    this._injectIntoSelection = function(Thumbnail){
        console.log("inject into selection", Thumbnail);

        // getting a fresh template node for the selection item
        var SelectionItemTpl = $(self.SelectionPanel).find(".SelectionItem.node-tpl").clone();
        SelectionItemTpl.removeClass("node-tpl");

        console.log("SelectionTemplate", SelectionItemTpl);

        $(SelectionItemTpl).attr("id", "foxtoken_"+Thumbnail.foxtoken);

        var thumb = new Image();
        // opera does not trigger the onload event of pictures having the style
        // rule display="none". So to keep thinks working, we're firstly using the visibility
        // style and set it to "hidden", afterwards, when the onload event was finally
        // triggered we switch to the display="none" style rule and directly
        // set visibility to "visible" again. That will keep the animation going well, even for
        // opera.
        // NOTE: Since the image is not integrated into the DOM yet, it makes no difference of
        // using visibility or display.
        $(thumb).css("visibility", "hidden");
        $(thumb)[0].onload = function(){
            $(this).hide();
            $(this).css("visibility", "visible");
            $(this).fadeIn(300);
            $(this).closest(".SelectionItem").removeClass("loading");
        };

        // replacing the image element of the node template with the new created image object
        $(SelectionItemTpl).find("img").replaceWith(thumb);
        $(SelectionItemTpl).addClass("loading");

        // add it to the selection
        $(self.SelectionPanel).find(".SelectionList").append(SelectionItemTpl);

        // according to the preview access of the user take the Preview.fwx requests
        // or the old preview.dll behavior
        // NOTE: By the preview.dll way it is not possible to show resized and cropped
        // images for the scaling or the selection functionality
        if(GLOBALS.PreviewAccess)
            $(thumb).attr("src", '/fotoweb/cmdrequest/rest/Preview.fwx?rt=1&f='+Thumbnail.foxtoken+'&as=rt&asw=63&ash=63&rd='+(+new Date()));
        else
            $(thumb).attr("src", "/fotoweb/cmdrequest/rest/preview.fwx?f="+Thumbnail.thumbnailId);
    }

    // calling the constructor after declarations
    this.construct();
}

/* real global functions - (not a good approach, but an effective one) */

function OpenBarView(){
    var barViewURL = "BarView.fwx";
    barViewURL += "?position="+GLOBALS.postition;
    barViewURL += "&archiveId="+GLOBALS.ArchiveId;
    barViewURL += "&columns="+GLOBALS.Columns;
    barViewURL += "&rows="+GLOBALS.Rows;
    barViewURL += "&sorting="+GLOBALS.Sorting;
    barViewURL += "&search="+GLOBALS.Search;

    windowFeatures = "dependent,width=200,height=800,left=0,top=0,menubar=0,scrollbars=1,status=1,titlebar=0,toolbar=0,resizable=1";

    window.open(barViewURL, "FWBarView", windowFeatures);
}

// Open the BarView window for drop till QuarkXPress
function OpenAlbumBarView()
{
    var barViewURL = "AlbumBarView.fwx"
    barViewURL += "?position="+GLOBALS.postition;
    barViewURL += "&albumId="+GLOBALS.ArchiveId;
    barViewURL += "&columns="+GLOBALS.Columns;
    barViewURL += "&rows="+GLOBALS.Rows;
    barViewURL += "&sorting="+GLOBALS.Sorting;

    windowFeatures = "dependent,width=200,height=800,left=0,top=0,menubar=0,scrollbars=1,status=1,titlebar=0,toolbar=0,resizable=1";

    window.open(barViewURL, "FWBarView", windowFeatures);
}