/**
 * Several class definitions which will be used globally are located here.
 *
 */
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
});

/**
 * Initiates global classes and objects like the thumbnail objects.
 */
function GlobalHandler()
{
    var m_Animation_SearchDropdown = {
        show: 100,
        hide: 100
    };

    // TODO: Is the metadata field hard coded or should it be dynamically
    var m_KeywordCloud_MetadataField    = 101;
    var m_KeywordCloud_CategoryCount    = 10;
    var m_KeywordCloud_ItemsPerCategory = 5;

    var m_defaultSearchText = TRANSLATIONS.Search;
    var m_bSearchClick = false;
    var KeywordPopDiv;

    construct();
    function construct()
    {
        // no global init for default page
        if($("body.page-default").length)
            return;
        
        initThumbnails();
        initPanels();

        // initiating navigation panel
        NavigationPaneControl.call($("#NavigationPaneControl")[0]);

        initArchiveMenu();
        initToolsMenu();
        initViewMenu();
        initAdministrationMenu();
        
        initSearchControl();
        initKeywordPopUp();

    }

    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
        $.getScript("js/jquery/jquery.rating.pack.js", function() { 
            $(".SelectableImage").each(function(){
                Thumbnail.call(this); // mutating the images to Thumbnail objects
            });
        });
    }

    function initPanels(){
        $(".Panel").each(function(){
            Panel.call(this); // mutating the panels to concret Panel objects
        });
    }

    function initArchiveMenu() {
        if(!$("#ArchiveMenu").length)
            return;

        $.get("cmdrequest/rest/ArchiveList.fwx", {
            ts: +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>');
        }, "xml");
    }

    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", "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");
            } 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;
    }

    function initToolsMenu()
    {
        if(!$("#TopMenuToolsList").length)
            return;

        $.get("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");

    }

    function initAdministrationMenu()
    {
        if(!$("#TopMenuAdministrationList").length)
            return;

        $("#TopMenuAdministration").hide();

        $.get("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");

    }

    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("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 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);

        $("#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);
        });
    }

    function initKeywordPopUp(){
        if(!$("#KeywordsPopUpTrigger")[0])
            return;

        $("#KeywordsPopUpTrigger").hide();

        var data = {
            ar: GLOBALS.ArchiveId,
            md: m_KeywordCloud_MetadataField,
            cc: m_KeywordCloud_CategoryCount,
            ic: m_KeywordCloud_ItemsPerCategory
        };

        $.get("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
        });
    }

    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();
        for(i=0; i<m_KeywordCloud_CategoryCount; i++)
        {
            for(j=0; j<m_KeywordCloud_ItemsPerCategory; j++)
            {
                Relevance = (m_KeywordCloud_CategoryCount-i);
                TagNode = $(Categories[i]).find("Value");
                // FIXME: Should be searched for the given metadata field?
                href = 'Grid.fwx?archiveId='+GLOBALS.ArchiveId+'&position=1&search='+$(TagNode[j]).attr("Value");
                TagNodes.push($('<a rel="'+Relevance+'" href="'+href+'">'+$(TagNode[j]).attr("Value")+'</a>'));
            }
        }

        // 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");
        });

        console.log(Content);
        $(Content).find("a").tagcloud({
            size: {
                start: 1,
                end: 2,
                unit: "em"
            },
            color: {
                start: '#999',
                end: '#333'
            }
        });

        return Content;
    }

    function createKeywordPopdiv(Content){

        KeywordPopDiv = new PopDiv("KeywordCloud", "KeywordCloudPopUp");
        KeywordPopDiv.setCaption("Keyword-Cloud");
        KeywordPopDiv.setContent(Content);
        KeywordPopDiv.setOverlaySpeed(200, 200);
        // IE7 and IE8 do have problems with animated alpha channel of png files
        // so an really fast value was chosen here.
        KeywordPopDiv.setPopDivSpeed(1, 1);
    }
}


/**
 * 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="+$(Profiles[i]).attr("Name"));
            $(a).attr("title", $(Profiles[i]).attr("Description"));

            var name = $(Profiles[i]).attr("Name");
            if($(Profiles[i]).attr("Name") == "<SEND_UNMODIFIED>")
                name = TRANSLATIONS.SendUnmodified;

            $(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("Download", "DownloadPopUp");
        DownloadProfilesPopup.setCaption("Download");
        DownloadProfilesPopup.setOverlaySpeed(200, 200);
        // IE7 and IE8 do have problems with animated alpha channel of png files
        // so an really fast value was chosen here.
        DownloadProfilesPopup.setPopDivSpeed(1, 1);
    }
}

/**
 * TopRowDropDown Class
 */
function TopRowDropDown(node)
{
    var m_node = node;
    var m_bLayoutDropdownClick;
    var m_hideEvent;
    var m_AnimationSpeed = {
        up: 150,
        down: 150
    };

    construct();
    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();
        })
    }

    function showDropdown() {
        $(m_node).find(".TopRowDropDownSwitch").addClass("Active")
        .next(".TopRowDropDown").slideDown(m_AnimationSpeed.down);
    }

    function hideDropdown()
    {
        $(m_node).find(".TopRowDropDown").slideUp(m_AnimationSpeed.up, function(){
            $(m_node).find(".TopRowDropDownSwitch").removeClass("Active");
        });

        if(m_hideEvent)
            m_hideEvent();
    }

    function setHideEvent(func)
    {
        m_hideEvent = func;
    }

    this.hide = hideDropdown;
    this.setHideEvent = setHideEvent;
}

/**
 * Mutation class for the Panels
 *
 */
function Panel(){

    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;

    /**
     * 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(){

        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();

            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");
            $(this).find(".InnerContainer", function(){

                if(typeof m_interPostEvent == "function")
                    m_interPostEvent();
                if(typeof m_postUpdateEvent == "function")
                    m_postUpdateEvent();
            });
        }
        else {
            $(this).find(".InnerContainer", function(){
                $(this).closest(".Panel").removeClass("OpenedPanel");

                if(typeof m_interPostEvent == "function")
                    m_interPostEvent();
                if(typeof m_postUpdateEvent == "function")
                    m_postUpdateEvent();
            });
        }

        // resetting internal events
        m_interPreEvent = false;
        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();
}


/**
 * Mutation Class for an Image Thumbnail Container within a Grid.
 *
 * @constructor
 */
function Thumbnail(){

    this.foxtoken    = "";
    this.position = 0;
    this.fckCheckbox = false;
    this.isMarked    = false;
    this.previewUrl  = "";
    this.downloadId  = "";
    this.filename    = "";
    this.preventProcessing = 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.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();
    }

    this.initRatings = function()
    {
        $(this).find(".Rating .submitrating").rating({
            callback: function(value){
                if(!value)
                    return;
                var ImageNum = parseInt(this.parentNode.id.replace(/\D/g, ""));
                $.get("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(){
            self.preventProcessing = true;
            var value = 0;
            var ImageNum = parseInt(this.parentNode.parentNode.id.replace(/Rating/, ""));
            $(this).rating('select');
            $.get("cmdrequest/FileAction.fwx?fwSetRating=1&rating="+parseInt(value)+"&f="+GLOBALS.ImageInfos[ImageNum].foxtoken);
        });

        $(this).find(".Rating .submitrating").click(function(){
            self.preventProcessing = true;
        })
    }

    /**
     * Function which delegates the event to the corresponding target.
     */
    this.processClick = function(e){
        if(this.preventProcessing){
            this.preventProcessing = false;
            return;
        }

        console.log("Processing click", this);
        console.log(e);

        var t = e.target;

        // event delegations
        if($(t).hasClass("Download")) {
            $("#NavigationPaneControl")[0].processDownloadRequest.call(this);
        }
        else if($(t).hasClass("Rating")) {
        // TODO: Rating: "do nothing does not work correctly"
        // do nothing, cause this will be handled by the rating plugin itself
        }
        else if($(t).hasClass("Selection")) {
            this.select();
        }
        else if($(t).hasClass("Zoom")) {
            this.goToPreview();
        }
        else if($(t).hasClass("Thumbnail") && GLOBALS.PreviewAccess) {
            this.goToPreview();
        }
        else{ // default, clicking the free space around the image for selection e.g.
            this.toggleSelect();
        }

    }

    /**
     * Function which relocates to the specific preview site of the
     * thumbnail container.
     */
    this.goToPreview = function(){
        console.log("Going to preview", this);
        var location = "Preview.fwx?";
        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;

        /*
            <%$=Site.PageName.Preview%>?position=<%$=image.number %>&archiveType=Album&albumId=<%$=imageList.currentArchive.id %>&sorting=<%$=imageList.sorting %>&search=<%$ InsertVariable encoding="url" name="imageList.search" /%>&fileId=<%$=image.foxToken %>
         */
    }

    /**
     * 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.toggleSelect = 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;
        }
    }

    this.init();
}

/**
 * <b>Mutator</b>
 * NavigationPaneControl 
 *
 * @constructor
 *
 */
function NavigationPaneControl()
{
    var self = this;

    var m_DownloadProfilesPopup = new DownloadPopup();

    /**
     * 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;
    
    /**
     * If currently an update is in progress, this indicator will be true. It
     * will be used to prevent adding and removing images, while the system is
     * busy.
     */
    this.updateInProgress = false;

    this.ActionPanel    = $(self).find(".Actions")[0];
    this.SelectionPanel = $(self).find(".Selection")[0];
    this.StructurePanel = $(self).find(".Structure")[0];

    /**
     * Pseudo constructor. Will be called at the end of the class.
     *
     * @namespace NavigationPaneControl
     */
    this.construct = function()
    {
        initPanelRelations();

        // updating selection and structure on reload
        $(window).load(function(){
            window.setTimeout(function(){
                if(self.StructurePanel)
                    self.StructurePanel.show();
                
                if(self.SelectionPanel)
                    self.updateSelection();
            }, 500 );
        });

        initStructure();

        updateActions();

        this.initOutboxMenu();

        this.initSelection();
    }

    /**
     * Initiates the outbox menu if there is one.
     *
     */
    this.initOutboxMenu = function(){
        if($("#OutboxMenu").length)
            this.updateOutboxMenu();
    }

    /**
     * Updates the outbox menu if there is one.
     *
     */
    this.updateOutboxMenu = function(callback){
        this.updateMenu("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='ActionsMenuSeperator'></div></li>");
            return;
        }

        // FIXME: prevent showing "Find Similar" for techdays
        if(DisplayName.match(/Find Similar/i)) {
            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;

        // 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="cmdrequest/ClearSelection.fwx"></form>');
            document.ClearSelectionForm.submit();
        });

        $(this.SelectionPanel).find(".PanelHeader").append('<span id="btnClearSelection" title="'+TRANSLATIONS.ClearSelection+'"></span>');
        $("#btnClearSelection").hide();
    }

    /**
     * Initiates the relations between the Panels.
     *
     * @namespace NavigationPaneControl
     */
    function initPanelRelations() {
        // closing the structure if the selection opens and v.v.
        if(self.SelectionPanel && self.StructurePanel){
            self.SelectionPanel.setPreShowEvent(function(){
                self.StructurePanel.hide();
            });
            self.StructurePanel.setPreShowEvent(function(){
                self.SelectionPanel.hide();
            });
        }

        // updating the Actions when activating
        if(self.ActionPanel){
            self.ActionPanel.setPreActivateEvent(function(){
                updateActions();
            });
        }

        if(self.StructurePanel) {
            self.StructurePanel.setPostShowEvent(function(){
                console.log("correcting the structure classes");
                // workaround to accomplish setting correct classes to even hidden treeview container
                // handle closes ones
                $(self).find("#FolderList li").filter(":has(>ul:hidden)")
                .addClass($.fn.treeview.classes.expandable)
                .replaceClass($.fn.treeview.classes.last, $.fn.treeview.classes.lastExpandable);

                // handle open ones
                $(self).find("#FolderList 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 initStructure()
    {
        var structureList = $(self.StructurePanel).find(".StructureList");
        if(!structureList.length)
            return;

        $.get("cmdrequest/rest/FolderList.fwx", {
            ar: GLOBALS.ArchiveId,
            r: 100
        }, function(data){

            structureList.empty();

            $(data).find("FolderList > Folder").each(function(){
                integrateIntoStructure(this, structureList);
            });

            structureList.treeview({
                animated: "fast",
                collapsed: true,
                // lack of jquery treeview plugin
                // TODO: no reset between archives
                // TODO: "plus" and "minus" buttons have wrong classes after reload
                //persist: "cookie",
                cookieId: "structure"
            });
        }, "xml");
    }

    /**
     * Integrates a single folder/link into the given Ul and calls this function
     * recursive for each of its childs.
     *
     * @namespace NavigationPaneControl
     */
    function integrateIntoStructure(folder, RootUl)
    {
        var DisplayName = $(folder).attr("DisplayName");
        var SearchString = $(folder).attr("SearchString");
        var li = document.createElement("li");
        var a = document.createElement("a");
        $(a).attr("href", "?search="+encodeURIComponent(SearchString));
        $(a).text(DisplayName);
        $(li).append(a);

        if(folder.hasChildNodes())
        {
            var ul = document.createElement("ul");
            $(folder).children("Folder").each(function(){
                integrateIntoStructure(this, ul);
            });
            $(li).append(ul);
        }

        $(RootUl).append(li);
    }

    /**
     * Updates the actions panel.
     *
     * @namespace NavigationPaneControl
     *
     * @param {function} callback
     */
    function updateActions(callback)
    {
        if(typeof GLOBALS == "undefined")
            return;

        if( !self.ActionPanel || !$(self.ActionPanel).find(".ActionsList").length)
            return;

        var ActionsOptions = {
            rn: GLOBALS.PostBackUrl,
            at: GLOBALS.ArchiveType,
            rd: +new Date
        };

        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("cmdrequest/rest/ActionsMenu.fwx",
            ActionsOptions,
            $(self.ActionPanel).find(".ActionsList")[0],
            callback);
    }

    /**
     * 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 it was a click on a link, which has probably the correct single download url within -> single Download
        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 = "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 = "cmdrequest/Download.fwx?incsel=1";
            downloadType = "selection";
        }

        $.ajax({
            url:"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("download type '"+Type+"'");

                console.log("download type ", 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 = "cmdrequest/rest/Preview.fwx?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("cmdrequest/GetSelection.fwx?sendXML=1", {
            sendXML: 1,
            rd: +new Date
        }, function (xml){

            var Images = new Array();
            var img;
            var foxtoken;
            $(xml).find("Item").each(function(){
                img = new Image();
                foxtoken = $(this).attr("foxToken");
                img.onload = onloadEvent;
                img.src = 'cmdrequest/rest/Preview.fwx?f='+foxtoken+'&as=rt&asw=82&ash=82&rd='+new Date;
                Images.push(img);
            });

            callback(Images);
        }, "xml");
    }

    /**
     * rebuild and updates the selection items
     *
     * @namespace NavigationPaneControl
     */
    this.updateSelection = function(recall)
    {
        recall = typeof recall == "undefined" ? true : recall;

        this.updateInProgress = true;
        $("body").addClass("loading");

        $.get("cmdrequest/GetSelection.fwx", {
            sendXML: 1,
            dp: +new Date
        }, function(xml){
            self.updateSelectionByXML(xml, recall);
            self.updateInProgress = false;
            $("body").removeClass("loading");
        }, "xml");
    }

    /**
     * rebuild and updates the selection by a given xml
     *
     * @namespace NavigationPaneControl
     *
     * @param {string} xml Data of the selected images.
     * @param {boolean} recall Triggers if an additional delayed recall of this function will be executed.
     */
    this.updateSelectionByXML = function(xml, recall)
    {
        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", 'cmdrequest/rest/Preview.fwx?f='+foxtoken+'&as=rt&asw=63&ash=63&rd='+(+new Date()));
            else
                $(thumb).attr("src", "fwbin/preview.dll?D="+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();
                }, 3000);
            });
        }

        // updating the navigation panel accordingly
        var listCount = parseInt($(xml).find("SelectionList").attr("listCount"));
        if(listCount > 0) { // if selection has entries
            self.ActionPanel.activate();
            self.SelectionPanel.activate();
            self.ActionPanel.show();
            self.SelectionPanel.show();
            $("#btnClearSelection").show();
        }
        else { // if selection is empty
            self.ActionPanel.disable();
            self.SelectionPanel.disable();
            $("#btnClearSelection").hide();
        }

        // additional delayed call of the update selection method
        // after the last normal call of it. That should provide an always synchron
        // selection list.
        if(this.updateSelectionRecallTimeoutIndicator)
            window.clearTimeout(this.updateSelectionRecallTimeoutIndicator);

        if(recall) {
            this.updateSelectionRecallTimeoutIndicator = window.setTimeout(function(){
                console.log("delayed call of update selection");
                self.updateSelection(false);
            }, 3000);
        }

    }

    /**
     * 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];
            console.log("ImgCell"+imgnum);
            console.log(ImgCell.foxtoken);
            console.log(foxtoken);
            if(foxtoken == ImgCell.foxtoken) {
                foundInGrid = true;
                ImgCell.unselect();
            }
        }

        // if it was not found, manually deselecting it from the selection
        if(!foundInGrid)
        {
            $.get("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){

        if(this.updateInProgress)
            return;

        $.get("cmdrequest/AddToSelection.fwx", {
            sendXML: 1,
            fileId: Thumbnail.foxtoken,
            dp: +new Date
        }, function(){
            self.updateSelection();
            if(typeof callback == "function")
                callback();
        }, "xml");
    }

    /**
     * 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){

        if(this.updateInProgress)
            return;

        $.get("cmdrequest/RemoveFromSelection.fwx", {
            sendXML: 1,
            fileId: Thumbnail.foxtoken,
            dp: +new Date
        }, function(){
            self.updateSelection();
            if(typeof callback == "function")
                callback();
        }, "xml");
    }

    //  construct 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);
}
