define([
    "./image.togglebutton.builder",
    "./toggleicon.builder",
    "./svg.togglebutton.builder",
    "./svg.toggleicon.builder",
    "jquery",
	"css!./sitemap.css"
],
function(
    ImageToggleButtonBuilder,
    ToggleIconbuilder,
    SvgToggleButtonBuilder,
    SvgToggleIconbuilder,
    jq
){

    "use strict";

    /**
     * @constructor
     */

    return function( options, context, metadata ){

        var sitemapButton,
            button,
            sitemapButtonHotSpotElement,
            sitemapContainer,
            currentItemIndex = -1;

        function hexToRgb(hex) {
            var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
            return result ? {
                r: parseInt(result[1], 16),
                g: parseInt(result[2], 16),
                b: parseInt(result[3], 16)
            } : {
                r: 0,
                g: 0,
                b: 0
            };
        }

        function asRgbaValue( colorValue, transparencyValue ) {

            var color = hexToRgb( colorValue ),
                alpha = asAlpha( transparencyValue );

            return "rgba(" + color.r + ", " + color.g + ", " + color.b + ", " + alpha + ")";
        }

        function asAlpha( percentValue ) {
            return (100 - percentValue) / 100;
        }

        function createSitemap() {

            var imageOptions = options.image.button,
                buttonWidth = options.useSVG ? metadata.getWidth() : ( imageOptions.width / 2 ),
                buttonHeight = options.useSVG ? metadata.getHeight() : ( imageOptions.height / 3 );

            sitemapButton = jq(document.createElement("div"))
                .attr({
                    "id": "sitemapButton" + options.instanceID
                })
                .css({
                    "width": buttonWidth + "px",
                    "height": buttonHeight + "px"
                })
                .addClass("sitemapButton");

            sitemapButtonHotSpotElement = jq(document.createElement("button"))
                .attr({
                    "id": "sitemapButtonHotSpotElement" + options.instanceID,
                    "title": options.caption,
                    "aria-label": options.ariaLabel ? options.ariaLabel : options.caption,
                    "role": "button",
                    "tab-index": "0"
                })
                .css({
                    "width": buttonWidth + "px",
                    "height": buttonHeight + "px",
                    "padding": "0",
                    "border": "none",
                    "background": "none"
                })
                .addClass( "sitemapButtonHotSpotElement" );

            if ( options.useSVG ) {
                button = new SvgToggleButtonBuilder( {
                    id: "svg_sitemapButton_" + metadata.getId(),
                    width: metadata.getWidth(),
                    height: metadata.getHeight(),
                    colors: options.svgButtonColors,
                    name: "svg_sitemapButton",
                    svgPath: '<svg id="svg_sitemapButton_' + metadata.getId() + '" width="' + metadata.getWidth() + 'px" height="' + metadata.getHeight() + 'px" viewBox="0 0 50 50" preserveAspectRatio="none" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background-color: transparent;">' +
                            '<defs></defs>' +
                            '<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
                                '<g class="NavigationButton" transform="translate(0.000000, 0.000000)" fill="' + options.svgButtonColors.default.bg + '">' +
                                    '<rect x="0" y="0" width="100%" height="100%"></rect>' +

                                    '<rect class="NavigationButtonIcon_On" display="block" fill="' + options.svgButtonColors.default.icon + '" x="18" y="15" width="22" height="4" />' +
                                    '<rect class="NavigationButtonIcon_On" display="block" fill="' + options.svgButtonColors.default.icon + '" x="10" y="15" width="4" height="4" />' +
                                    '<rect class="NavigationButtonIcon_On" display="block" fill="' + options.svgButtonColors.default.icon + '" x="18" y="23" width="22" height="4" />' +
                                    '<rect class="NavigationButtonIcon_On" display="block" fill="' + options.svgButtonColors.default.icon + '" x="18" y="31" width="22" height="4" />' +

                                    '<rect class="NavigationButtonIcon_Off" display="none" x="10" y="15" width="4" height="4" fill="' + options.svgButtonColors.default.icon + '" />' +
                                    '<rect class="NavigationButtonIcon_Off" display="none" x="18" y="23" width="22" height="4" fill="' + options.svgButtonColors.default.icon + '" />' +
                                    '<rect class="NavigationButtonIcon_Off" display="none" x="18" y="31" width="22" height="4" fill="#999" />' +
                                    '<path class="NavigationButtonIcon_Off" display="none" d="M30.38,15H18v4H33A8.48,8.48,0,0,1,30.38,15Z" fill="' + options.svgButtonColors.default.icon + '" />' +
                                    '<polygon class="NavigationButtonIcon_Off" display="none" points="43.38 9.38 41.62 7.62 38.5 10.73 35.38 7.62 33.62 9.38 36.73 12.5 33.62 15.62 35.38 17.38 38.5 14.27 41.62 17.38 43.38 15.62 40.27 12.5 43.38 9.38" fill="' + options.svgButtonColors.default.icon + '" />' +
                                '</g>' +
                            '</g>' +
                        '</svg>'
                } ).build();
            } else {
                button = new ImageToggleButtonBuilder( {
                    fileNameURL: imageOptions.fileNameURL,
                    width: imageOptions.width / 2,
                    height: imageOptions.height / 3,
                    name: "img_sitemapButton",
                    captionText: options.caption
                }, context, metadata ).build();
            }

            sitemapContainer = createSitemapContainer();

            sitemapButton.append( button.getElement() );
            sitemapButton.append(sitemapButtonHotSpotElement);
            sitemapButton.append(sitemapContainer.getElement());
        }

        function createSitemapContainer() {

            var items = new Array(),
                isContainerOpen = false,
                position = getMenuPosition(),
                fillColor = options.fillColor,
                fillTransparency = options.fillTransparency,
                borderColor = options.borderColor,
                borderTransparency = options.borderTransparency,
                container = jq(document.createElement("div"))
                    .attr({
                        "id": "sitemapContainer" + options.instanceID,
                        "title": ""
                    })
                    .css({
                        "width": options.sitemapWidth + "px",
                        "height": options.sitemapHeight + "px",
                        "border": options.borderWidth + "px solid " + asRgbaValue(borderColor, borderTransparency),
                        "background-color": asRgbaValue( fillColor, fillTransparency ),
                        "touch-action": "pan-y",
                        "cursor": "default"
                    })
                    .css(position)
                    .addClass("sitemapContainer")
                    .on(options.pointerEvents.pointerDown, function(e){
                        cancelEvent(e);
                        lockNavigation();
                    })
                    .on(options.pointerEvents.pointerUp, function(e){
                        cancelEvent(e);
                        unlockNavigation();
                    })
                    .on( "dblclick", cancelEvent )
                    .on( "touchmove", function ( e ) {
                        e.stopPropagation();
                    } );

            if(options.AuthoringMode && options.showSitemapInAuthoringMode){
                container
                    .css({
                        "opacity" : "0.15",
                        "background-image" : "url(\"" + context.getPath() + "invis.png\")",
                        "text-align" : "center",
                        "line-height" : options.sitemapHeight + "px",
                        "font-size" : "24px"
                    })
                    .text( "Sitemap" )
                    .show();
            }else{
                container.hide();
            }

            return {
                getElement : function(){
                    return container.get(0);
                },
                addItem: function(item){
                    var lastItem = (items.length !== 0) ? items[items.length - 1] : false;

                    items.push( item );

                    if ( item.isSection() ) {
                        item.setCollapsed();
                    }

                    if ( !( item.isSuppressInSitemap() ) ) {
                        container.append( item.getElement() );
                    }

                    // return if first item
                    if ( !lastItem ) {
                        return;
                    }

                    // item is page
                    if ( item.isPage() ) {

                        // lastItem is page
                        if ( lastItem.isPage() ) {

                            // lastItem parent available
                            if ( lastItem.hasParent() ) {
                                item.setParent( lastItem.getParent() );
                                lastItem.getParent().addChild( item );
                            }

                        // lastItem is section
                        } else {
                            item.setParent( lastItem );
                            lastItem.addChild( item );
                        }

                    // item is section
                    } else {

                        // ignore level 1
                        if ( item.getLevel() === 1 ) {
                            return;
                        }

                        // search for item with level smaller than items level
                        for ( var i = items.length - 2; i >= 0; i-- ) {

                            // stop if level 1 is reached
                            if ( items[i].getLevel() === 1 ) {
                                if ( items[i].isPage() ) {
                                    if ( items[i].hasParent() ) {
                                        item.setParent( items[i].getParent() );
                                        items[i].getParent().addChild( item );
                                        return;
                                    }
                                } else {
                                    item.setParent( items[i] );
                                    items[i].addChild( item );
                                    return;
                                }
                            }

                            if ( items[i].getLevel() < item.getLevel() ) {
                                if ( items[i].isPage() ) {
                                    if ( items[i].hasParent() ) {
                                        item.setParent( items[i].getParent() );
                                        items[i].getParent().addChild( item );
                                        return;
                                    }
                                } else {
                                    item.setParent( items[i] );
                                    items[i].addChild( item );
                                    return;
                                }
                            }
                        }

                    }
                },
                getItems : function(){
                    return items;
                },
                hideContainer : function(){
                    isContainerOpen = false;
                    container.hide();
                },
                showContainer : function(){
                    isContainerOpen = true;
                    container.show();
                },
                isOpen : function(){
                    return isContainerOpen;
                },
                resetSitemapContainer : function(){
                    for(var i = 0; i < items.length; i++){
                        items[i].resetItem();
                    }
                    items.length = 0;
                    isContainerOpen = false;
                    container.empty();
                }
            }
        }

        function getMenuPosition() {
            var buttonWidth = options.useSVG ? metadata.getWidth() : (options.image.button.width / 2),
                buttonHeight = options.useSVG ? metadata.getHeight() : (options.image.button.height / 3),
                sitemapWidth = options.sitemapWidth,
                sitemapHeight = options.sitemapHeight,
                border = options.borderWidth,
                menuPositions = [
                    //bottom-left
                    {
                        "top": buttonHeight + "px",
                        "right": buttonWidth + "px"
                    },
                    //top-left
                    {
                        "bottom": buttonHeight + "px",
                        "right": buttonWidth + "px"
                    },
                    //top-centered
                    {
                        "bottom": buttonHeight + "px",
                        "right": - (( sitemapWidth + ( border * 2 ) - buttonWidth ) / 2) + "px"
                    },
                    //top-right
                    {
                        "bottom": buttonHeight + "px",
                        "left": buttonWidth + "px"
                    },
                    //right-centered
                    {
                        "top": - (( sitemapHeight + ( border * 2 ) - buttonHeight ) / 2) + "px",
                        "left": buttonWidth + "px"
                    },
                    //bottom-right
                    {
                        "top": buttonHeight + "px",
                        "left": buttonWidth + "px"
                    },
                    //bottom-centered
                    {
                        "top": buttonHeight + "px",
                        "right": - (( sitemapWidth + ( border * 2 ) - buttonWidth ) / 2) + "px"
                    },
                    //left-centered
                    {
                        "top": - (( sitemapHeight + ( border * 2 ) - buttonHeight ) / 2) + "px",
                        "right": buttonWidth + "px"
                    }
                ],
                posOption = options.position;

            if(posOption !== 8){
                return menuPositions[ posOption ];
            }else{
                return {
                    "top": options.sitemapPositionY + "px",
                    "left": options.sitemapPositionX + "px"
                };
            }
        }

        function createSitemapContent(container) {
            try {
                var pathElements = metadata.stage.getMainPath().getPathElements();

                jq.each( pathElements, function ( index, element ) {
                    var pathElement = element,
                        item,
                        type = pathElement.getType(),
                        title = pathElement.getTitle(),
                        level = pathElement.getLevel(),
                        id,
                        action,
                        isSuppressInSitemap = false;

                    if ( type === "page" ) {
                        isSuppressInSitemap = pathElement.getPageMetadata().suppressInJumplist();
                    }

                    if(pathElement.getPageMetadata()){
                        id = pathElement.getPageMetadata().getId();
                    }else{
                        id = 1;
                    }

                    action = function(e){
                        if(context.getPlayerName() === "html5"){
                            cancelEvent(e);
                            unlockNavigation();
                            if(!isOnTarget(e)) {
                                return;
                            }
                            if ( id !== 1 ) {
                                context.stage.accessibility.turnAccessibilityModeOff();
                                context.stage.navigation.gotoPageById( id );
                            }
                        }else{
                            cancelEvent(e);
                            unlockNavigation();
                            if(!isOnTarget(e)) {
                                return;
                            }
                            context.stage.accessibility.turnAccessibilityModeOff();
                            context.stage.navigation.gotoPage(pathElement.page);
                        }
                    };

                    // workaround for wysiwyg and bug with get title of current step
                    if(title === ""){
                        title = "Page";
                    }

                    item = createSitemapItem( type, title, id, index, action, level, isSuppressInSitemap );

                    /*if ( !( options.isStandardPlayer ) && options.showVisited && context.student.expert.isPageVisited( id ) && type == "page" ) {
                        item.setVisited();
                    }*/

                    container.addItem( item );
                });
            } catch (error) {
                // to prevent script errors in html4 wysiwyg editor
            }
        }

        function createSitemapItem( type, caption, id, index, action, level, isSuppressInSitemap) {

            var item,
                icon,
                index,
                marker,
                visited,
                iconHotSpotElement,
                textColor,
                hoverColor,
                parent,
                childs = new Array(),
                elementId = id,
                separatorColor = options.separatorColor,
                separatorTransparency = options.separatorTransparency,
                isVisited = false,
                isExpanded = false,
                isSection = false,
                isPage = false,
                isMarked = false,
                type = type,
                caption = caption,
                isSuppressInSitemap = isSuppressInSitemap,
                itemPaddingLeft = 10 + parseInt(options.image.icons.width) + parseInt(options.paddingLeft) + ((level - 1) * (5 + parseInt(options.image.icons.width) + parseInt(options.paddingLeft))),
                itemPaddingRight = parseInt(options.paddingRight) + 10 + parseInt(options.image.marker.width) + 20,
                itemWidth = parseInt(options.sitemapWidth) - itemPaddingLeft - itemPaddingRight,
                iconHotSpotLeft = 5 + ( ( level - 1 ) * ( 5 + (options.useSVG ? 14 : parseInt(options.image.icons.width)) + parseInt(options.paddingLeft))),
                iconLeft = 5 + ( ( level - 1 ) * ( 5 + ( options.useSVG ? 14 : parseInt(options.image.icons.width)) + parseInt(options.paddingLeft))),
                maxHeight;

            if(type === "page"){
                textColor = options.pageTextColor;
                hoverColor = options.pageHoverTextColor;
                isPage = true;
            }else{
                textColor = options.sectionTextColor;
                hoverColor = options.sectionHoverTextColor;
                isSection = true;
            }

            item = jq(document.createElement("div"))
                .attr( "id", id + "-item" + options.instanceID )
                .css({
                    "border-bottom": options.separatorWidth + "px solid " + asRgbaValue(separatorColor, separatorTransparency),
                    "color": textColor,
                    "font-family": options.fontFamily,
                    "padding-left": itemPaddingLeft + "px",
                    "padding-right": itemPaddingRight  + "px",
                    "padding-top": options.paddingTop + "px",
                    "padding-bottom": options.paddingBottom + "px",
                    "font-size": options.fontSize + "px",
                    "width" : itemWidth + "px",
                })
                .addClass("sitemapItem sitemapText")
                .text(caption)
                .on(options.pointerEvents.pointerDown, function(e){
                    cancelEvent(e);
                    lockNavigation();
                })
                .on(options.pointerEvents.pointerUp, action)
                .on(options.pointerEvents.pointerMove, function(){
                    jq(this).css("color", hoverColor);
                })
                .on(options.pointerEvents.pointerLeave, function(){
                    jq(this).css("color", textColor);
                })
                .on("dblclick", cancelEvent );

            if(options.cutTitles){
                item
                    .css({
                        "white-space" : "nowrap",
                        "overflow" : "hidden",
                        "text-overflow" : "ellipsis"
                    })
            }

            if (type === "section"){
                item.addClass("sitemapSection");
            }

            maxHeight = getRenderedHeight(item) - options.paddingTop - options.paddingBottom;

            if(maxHeight < options.image.marker.height){
                maxHeight = options.image.marker.height;
            }
            if(maxHeight < options.image.icons.height / 2){
                maxHeight = options.image.icons.height / 2;
            }
            if(!(options.isStandardPlayer) && options.showVisited && !(options.useSVG) && maxHeight < options.image.visited.height){
                maxHeight = options.image.visited.height;
            }

            item.css({
                "height" : maxHeight + "px"
            })

            if ( options.useSVG ) {
                var svgMarker = jq( document.createElement( "div" ) ),
                    svg = svgMarker.html(
                            '<svg id="svg_marker_' + metadata.getId() + '" width="14px" height="14px" viewBox="0 0 14 14" preserveAspectRatio="none" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background-color: transparent;">' +
                                '<title></title>' +
                                '<defs></defs>' +
                                '<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
                                    '<g class="NavigationButton" transform="translate(0.000000, 0.000000)" fill="none">' +
                                        '<rect x="-0.17" y="0.17" width="14" height="14" transform="translate(-0.02 0.02) rotate(-0.13)" fill="none"/></rect>' +
                                        '<circle cx="7" cy="7" r="7" fill="' + options.svgMarkerColor + '"/></circle>' +
                                        '<circle cx="6.83" cy="7.17" r="2.5" fill="#fff"/></circle>' +
                                    '</g>' +
                                '</g>' +
                            '</svg >'
                        )
                        .get( 0 );

                marker = jq( document.createElement( "div" ) )
                    .attr( {
                        "id": id + "-marker" + options.instanceID,
                    })
                    .css( {
                        "width": "14px",
                        "height": "14px",
                        "overflow": "hidden"
                    } )
                    .addClass( "sitemapMarker" )
                    .append( svg )
                    .hide();

                icon = new SvgToggleIconbuilder( {
                    width: 14,
                    height: 14,
                    caption: "",
                    colors: options.svgIconColors,
                    name: "sitemapIcon",
                    svgPath: '<svg id="svg_icon_' + metadata.getId() + '" width="14px" height="14px" viewBox="0 0 14 14" preserveAspectRatio="none" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background-color: transparent;">' +
                        '<title>Icon</title>' +
                        '<defs></defs>' +
                            '<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
                                '<g class="NavigationButton" transform="translate(0.000000, 0.000000)" fill="none">' +
                                    '<rect x="0" y="0" width="14" height="14"></rect>' +
                                    '<polygon class="NavigationButtonIcon_Off" display="none" points="7 11.06 0.97 5.03 2.03 3.97 7 8.94 11.97 3.97 13.03 5.03 7 11.06" fill="' + options.svgIconColors.default.icon + '" />' +
                                    '<polygon class="NavigationButtonIcon_On" display="block" fill="' + options.svgIconColors.default.icon + '" points="11.84 7.03 5.81 13.06 4.75 12 9.72 7.03 4.75 2.06 5.81 1 11.84 7.03" />' +
                                '</g>' +
                            '</g>' +
                        '</svg >'
                } ).build();
            } else {
                marker = jq( document.createElement( "img" ) )
                    .attr( {
                        "id": id + "-marker" + options.instanceID,
                        "src": options.image.marker.fileNameURL
                    } )
                    .css( {
                        "width": options.image.marker.width + "px",
                        "height": options.image.marker.height + "px"
                    } )
                    .addClass( "sitemapMarker" )
                    .hide();
                icon = new ToggleIconbuilder( {
                    fileNameURL: options.image.icons.fileNameURL,
                    width: options.image.icons.width,
                    height: options.image.icons.height / 2,
                    name: "sitemapIcon"
                }, context, metadata ).build();
            }

            icon.setLeftPosition(iconLeft);

            iconHotSpotElement = jq(document.createElement("div"))
                .attr({
                    "id": "sietmapIconHotSpotElement" + options.instanceID
                })
                .css( {
                    "width": options.useSVG ? "14px" : (options.image.icons.width + "px"),
                    "height": options.useSVG ? "14px" : ((options.image.icons.height / 2) + "px"),
                    "left": iconHotSpotLeft + "px"
                })
                .on("mouseenter", function() {
                    item.css("color", hoverColor);
                })
                .on("mouseleave", function() {
                    item.css("color", textColor);
                })
                .on(options.pointerEvents.pointerUp, function(e) {
                    cancelEvent(e);
                    unlockNavigation();
                    if(!isOnTarget(e)) {
                        return;
                    }
                    if(isExpanded){
                        isExpanded = false;
                        icon.onMouseUp();
                        refreshSitemap();
                    }else{
                        isExpanded = true;
                        icon.onMouseUp();
                        refreshSitemap();
                    }
                })
                .on(options.pointerEvents.pointerDown, function(e) {
                    cancelEvent(e);
                    lockNavigation();
                })
                .on("dblclick", cancelEvent )
                .addClass("sitemapIconHotSpotElement")

            item.append( marker );

            if ( !(options.isStandardPlayer) && options.showVisited ) {
                if ( options.useSVG ) {
                    var svgVisited = jq( document.createElement( "div" ) ),
                        svg = svgVisited.html(
                            '<svg id="svg_visited_' + metadata.getId() + '" width="14px" height="14px" viewBox="0 0 14 14" preserveAspectRatio="none" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background-color: transparent;">' +
                            '<title></title>' +
                            '<defs></defs>' +
                            '<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">' +
                            '<g class="NavigationButton" transform="translate(0.000000, 0.000000)" fill="none">' +
                            '<circle id="Icon" cx="7" cy="7" r="7" fill="#999"/></circle>' +
                            '<polygon id="Icon-2" data-name="Icon" points="10.35 3.35 5.79 7.91 3.67 5.79 2.26 7.21 4.38 9.33 4.38 9.33 5.79 10.74 11.77 4.77 10.35 3.35" fill="' + options.svgVisitedColor + '"/></polygon>' +
                            '</g>' +
                            '</g>' +
                            '</svg >'
                            )
                            .get( 0 );

                    visited = jq( document.createElement( "div" ) )
                        .attr( {
                            "id": id + "-visited" + options.instanceID,
                        } )
                        .css( {
                            "width": "14px",
                            "height": "14px",
                            "overflow": "hidden"
                        } )
                        .addClass( "sitemapVisited" )
                        .append( svg )
                        .hide();
                } else {
                    visited = jq( document.createElement( "img" ) )
                        .attr( {
                            "id": id + "-visited" + options.instanceID,
                            "src": options.image.visited.fileNameURL
                        } )
                        .css( {
                            "width": options.image.visited.width + "px",
                            "height": options.image.visited.height + "px"
                        } )
                        .addClass( "sitemapVisited" )
                        .hide();
                }
                item.append( visited );
            }

            if(type === "section") {
                item.append(jq(icon.getElement()));
                item.append(iconHotSpotElement);
            }

            context.page.interaction.provokesFeedback( iconHotSpotElement, false );
            context.page.interaction.provokesAutosolve( iconHotSpotElement, false );
            context.page.interaction.provokesFeedback( item, false );
            context.page.interaction.provokesAutosolve( item, false );

            return {
                getElement : function() {
                    return item;
                },
                getType : function() {
                    return type;
                },
                getId : function() {
                    return elementId;
                },
                getTitle: function () {
                    return caption;
                },
                getIndex : function() {
                    return index;
                },
                getLevel : function() {
                    return level;
                },
                isSection: function () {
                    return isSection;
                },
                isPage: function () {
                    return isPage;
                },
                isSuppressInSitemap: function () {
                    return isSuppressInSitemap;
                },
                isMarked: function () {
                    return isMarked;
                },
                setMarked: function () {
                    if ( !( options.isStandardPlayer ) && options.showVisited ) {
                        visited.hide();
                    }
                    marker.show();
                    isMarked = true;
                },
                unsetMarked: function () {
                    marker.hide();
                    isMarked = false;
                },
                isVisited: function () {
                    return isVisited;
                },
                setVisited: function () {
                    if ( !( options.isStandardPlayer ) && options.showVisited ) {
                        visited.show();
                        isVisited = true;
                    }
                },
                unsetVisited: function () {
                    if ( !( options.isStandardPlayer ) && options.showVisited ) {
                        visited.hide();
                        isVisited = false;
                    }
                },
                hasParent: function () {
                    return !!parent;
                },
                setParent: function ( item ) {
                    parent = item;
                },
                getParent: function () {
                    return parent;
                },
                hasChilds: function () {
                    return childs.length !== 0;
                },
                addChild: function ( item ) {
                    childs.push( item );
                },
                getChilds: function () {
                    return childs;
                },
                isExpanded: function () {
                    return isExpanded;
                },
                setExpanded: function () {
                    if ( type === "section" && !isExpanded ) {
                        icon.onMouseUp();
                        isExpanded = true;
                    }
                },
                setCollapsed: function () {
                    if ( type === "section" && isExpanded ) {
                        icon.onMouseUp();
                        isExpanded = false;
                    }
                },
                showItem: function () {
                    item.show();
                },
                hideItem: function () {
                    item.hide();
                },
                resetItem: function () {
                    type = null;
                    visited = null;
                    elementId = null;
                    index = null;
                    level = null;
                    parent = null;
                    childs.length = 0;
                    isVisited = false;
                    isExpanded = false;
                    isSection = false;
                    isPage = false;
                    isMarked = false;
                },
                hideIcon: function () {
                    if (isSection) {
                        iconHotSpotElement.hide();
                        icon.hideIcon();
                    }
                }
            }
        }

        function getRenderedHeight(elem){
            var renderedHeight,
                clonedElem = elem.clone();

            clonedElem.appendTo('body');
            renderedHeight = clonedElem.innerHeight();
            clonedElem.remove();

            return renderedHeight;
        }

        function hideIcons() {
            var sections = sitemapContainer.getItems().filter(function (item) {
                return item.isSection();
                }),
                unsuppressedChilds;

            for (var j = sections.length - 1; j >= 0; j--) {
                if (!(sections[j].hasChilds())) {
                    sections[j].hideIcon();
                    continue;
                }

                unsuppressedChilds = sections[j].getChilds().filter(function (item) {
                    return !(item.isSuppressInSitemap());
                });

                if (unsuppressedChilds.length === 0) {
                    sections[j].hideIcon();
                }
            }
        }

        function setMarker(){
            var items = sitemapContainer.getItems(),
                thisPageId = metadata.page.getId();

            for( var i = 0; i < items.length; i++ ){

                if ( items[i].getId() === thisPageId && items[i].isPage() ){
                    currentItemIndex = i;

                    //page included
                    if ( !( items[i].isSuppressInSitemap() ) ){
                        items[i].setMarked();
                        return;
                    }else{
                        //page not included
                        if ( items[i].hasParent() ) {
                            items[i].getParent().setMarked();
                            return;
                        } else {
                            //step without section
                            items[i].setMarked();
                            return;
                        }
                    }
                }
            }
        }

        function setVisited(){
            if ( !( options.isStandardPlayer ) && options.showVisited ) {
                var pages = sitemapContainer.getItems().filter( function ( item ) {
                        return item.isPage();
                    } ),
                    sections = sitemapContainer.getItems().filter( function ( item ) {
                        return item.isSection();
                    } ),
                    childs,
                    sectionIsVisited;

                for ( var i = 0; i < pages.length; i++ ) {
                    if ( context.student.expert.isPageVisited( pages[i].getId() ) ) {
                        pages[i].setVisited();
                    }
                }

                for ( var j = sections.length - 1; j >= 0; j-- ) {
                    if ( !(sections[j].hasChilds()) ) {
                        continue;
                    }

                    childs = sections[j].getChilds();
                    sectionIsVisited = true;

                    for ( var k = childs.length - 1; k >= 0; k-- ) {
                        if ( !( childs[k].isVisited() ) ) {
                            sectionIsVisited = false;
                            break;
                        }
                    }

                    if ( sectionIsVisited ) {
                        sections[j].setVisited();
                    }
                }
            }
        }

        function expandSection( section ) {
            if ( section.hasChilds() ) {
                var childs = section.getChilds();

                for ( var i = 0; i < childs.length; i++ ) {
                    if ( childs[i].isPage() && !( childs[i].isSuppressInSitemap() ) ) {
                        childs[i].showItem();
                    } else {
                        childs[i].showItem();
                        if ( childs[i].isExpanded() ) {
                            expandSection( childs[i] );
                        } else {
                            collapseSection( childs[i] );
                        }
                    }
                }
            }
        }

        function collapseSection( section ) {
            if ( section.hasChilds() ) {
                var childs = section.getChilds();

                for ( var i = 0; i < childs.length; i++ ) {
                    if ( childs[i].isPage() ) {
                        childs[i].hideItem();
                    } else {
                        childs[i].hideItem();
                        collapseSection( childs[i] );
                    }
                }
            }
        }

        function refreshSitemap() {
            var items = sitemapContainer.getItems(),
                sections = jq.map( items, function ( item ) {
                    if ( item.isSection() ) {
                        return item;
                    } else {
                        return;
                    }
                } ),
                suppressedInSitemapPagesWithoutSections = new Array();

            for ( var i = 0; i < items.length; i++ ) {
                if ( items[i].isPage() && items[i].isSuppressInSitemap() ) {
                    suppressedInSitemapPagesWithoutSections.push( items[i] );
                }
            }

            for ( var i = sections.length - 1; i >= 0; i-- ) {
                if ( sections[i].isExpanded() ) {
                    expandSection( sections[i] );
                } else {
                    collapseSection( sections[i] );
                }
            }

            if ( suppressedInSitemapPagesWithoutSections ) {
                for ( var i = 0; i < suppressedInSitemapPagesWithoutSections.length; i++ ) {
                    suppressedInSitemapPagesWithoutSections[i].hideItem();
                }
            }
        }

        function setVisibilityOnEnterPage(){
            if ( currentItemIndex > -1 ) {
                var items = sitemapContainer.getItems();

                if ( !(items[currentItemIndex].hasParent()) ) {
                    return;
                }

                var parent = items[currentItemIndex].getParent();

                while ( parent ) {
                    parent.setExpanded()

                    if ( parent.hasParent() ) {
                        parent = parent.getParent();
                    } else {
                        parent = false;
                    }
                }
            }
        }

        function isOnTarget(e) {
            var elem = e.target,
                origEvent = e.originalEvent,
                changedTouch = origEvent.changedTouches && origEvent.changedTouches.length > 0 ? origEvent.changedTouches[0] : undefined;

            if (changedTouch) {
                elem = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY);
            }

            return e.target === elem;
        }

        function lockNavigation() {
            if (context.stage.isInAuthoringMode() || (typeof(context.stage.trigger) === "undefined")) {
                return;
            }
            context.stage.trigger.lock( metadata.getId() );
        }

        function unlockNavigation() {
            if (context.stage.isInAuthoringMode() || (typeof(context.stage.trigger) === "undefined")) {
                return;
            }
            context.stage.trigger.unlock( metadata.getId() );
        }

        function cancelEvent( e ){
            context.device.cancelEvent( e );
        }

        function toggleMenu() {
            if (sitemapContainer.isOpen()) {
                sitemapContainer.hideContainer();
                sitemapButtonHotSpotElement.attr({
                    "title": options.caption,
                    "aria-label": options.ariaLabel ? options.ariaLabel : options.caption
                })
            } else {
                sitemapContainer.showContainer();
                sitemapButtonHotSpotElement.attr({
                    "title": options.captionClose,
                    "aria-label": options.ariaLabelClose ? options.ariaLabelClose : options.captionClose
                })
            }
        }

        function logSitemap() {
            var items = sitemapContainer.getItems(),
                childs,
                parent;

            console.log( "Current sitemap structure:" );

            for ( var i = 0; i < items.length; i++ ) {
                childs = "";
                if ( items[i].hasChilds() ) {
                    for ( var j = 0; j < items[i].getChilds().length; j++ ) {
                        childs = childs + " " + items[i].getChilds()[j].getTitle();
                    }
                } else {
                    childs = "none"
                }

                parent = items[i].hasParent() ? items[i].getParent().getTitle() : "none";

                console.log( "title: " + items[i].getTitle() + ", id: " + items[i].getId() + ", parent: " + parent + ", childs: " + childs + ", type: " + items[i].getType() + ", level: " + items[i].getLevel() + ", isMarked: " + items[i].isMarked() + ", isVisited: " + items[i].isVisited() );
            }
        }

        return {
            build : function(){
                createSitemap();
                return {
                    getElement : function(){
                        return sitemapButton.get(0);
                    },
                    getHotSpotElement : function(){
                        return sitemapButtonHotSpotElement.get(0);
                    },
                    onMouseLeave : function(){
                        button.onMouseLeave();
                    },
                    onMouseOver : function(){
                        button.onMouseOver();
                    },
                    onMouseDown : function(e){
                        cancelEvent(e);
                        lockNavigation();
                        button.onMouseDown();
                    },
                    onMouseUp : function(e){
                        cancelEvent(e);
                        unlockNavigation();
                        button.onMouseLeave();

                        if(!isOnTarget(e)) {
                            return;
                        }

                        toggleMenu();

                        button.onMouseUp();
                    },
                    toggleMenu: toggleMenu,
                    onEnterPage : function() {
                        createSitemapContent( sitemapContainer );
                        setVisited();
                        setMarker();
                        hideIcons();
                        setVisibilityOnEnterPage();
                        refreshSitemap();
                    },
                    onExitPage : function() {
                        sitemapContainer.hideContainer();
                        button.onExitPage();
                        sitemapContainer.resetSitemapContainer();
                    },
                    focusButton: function () {
                        button.focusButton();
                        sitemapButtonHotSpotElement.trigger("focus");
                        sitemapButtonHotSpotElement.on("focusout", function() {
                            button.blurButton();
                        });
                    },
                    blurButton: function () {
                        button.blurButton();
                        sitemapButtonHotSpotElement.trigger("blur");
                        sitemapButtonHotSpotElement.off("focusout");
                    }
                };
            }
        };
    }
});
