(function()
{
    'use strict';
    var page = jQuery(document);

    var load_content = function( node, options )
    {
        options = options || {};

        var box = jQuery(node);

        var set_content = function( node, content, options )
        {
            var result_node = node;
            if ((options) && (options.target_method == 'replace'))
            {
                result_node = jQuery(content);
                node.replaceWith( result_node );
            }
            else
            {
                node.html( content );
                node.addClass('loaded');
            }

            return result_node;
        };

        var set_messages = function( node, xhr, _options )
        {
            // if an ajax request returns a 404 or 500
            // then fetch out any messages and display only them as the expected content

            // only these status codes get picked up
            // to avoi interfering with other ajax error handlers like required authentication etc

            if (!xhr || ([404, 500].indexOf( xhr.status ) == -1))
            {
                return;
            }
            var content = jQuery('<html />').append( xhr.responseText );
            var messages = content.find('> .messages');
            node.html(messages);
        };


        var xhr = jQuery(node).data('contentxhr');

        if (xhr)
        {
            xhr.abort();
        }

        if (options.url)
        {
            // by default the whole box gets loadingstart triggered
            // in case this is not needed, loader: false can be passed in options
            var trigger_loading = options.hasOwnProperty('loader') ? options.loader : true;
            if (trigger_loading)
            {
                box.trigger('loadingstart');
            }

            var data = options.data || [];
            data.push( { name: 'ajax', value: 1 } );

            xhr = jQuery.ajax(
            {
                url:    options.url,
                type:   options.method || 'get',
                data:   data,

                success: function( result )
                {
                    var fragment = options.url.split('#')[1];
                    var content_node_selector = (options && options.selector) || (fragment ? '#' + fragment : null );

                    var content;

                    if (content_node_selector)
                    {
                        var html = jQuery('<html />').append( result );

                        if (options.target_method == 'replace')
                        {
                            content = html.find(content_node_selector);
                        }
                        else
                        {
                            content = html.find(content_node_selector).html();
                        }
                    }
                    else
                    {
                        content = result;
                    }

                    var result_node = set_content( box, content, options );

                    result_node.trigger('loadingend');
                    result_node.trigger('contentloaded');

                    if (options.success)
                    {
                        options.success.call( result_node[0], result );
                    }
                },
                error: function( xhr )
                {
                    set_messages( box, xhr, options );
                    if (trigger_loading)
                    {
                        box.trigger('loadingend');
                    }
                    if (options.error)
                    {
                        options.error.call( box, xhr );
                    }

                }
            });
            jQuery(node).data('contentxhr', xhr);

        }
        else if (options.content)
        {
            var result_node = set_content( box, options.content, options );
            result_node.trigger('contentloaded');
        }
    };

    page.on('contentload', function(e, options )
    {
        // replaces the content of the target element
        // with the result of an ajax request to url
        options = options || {};
        options.target_method = 'load';
        load_content( e.target, options );
    });

    page.on('contentreplace', function(e, options )
    {
        // replaces the target element itself
        // with the result of an ajax request to url

        options = options || {};
        options.target_method = 'replace';
        load_content( e.target, options );
    });


    page.on('scrollintoview', function(e)
    {
        // scroll to target object
        // if it is not visible (is lower than 80% of the viewport or higher than viewport top)

        var target = jQuery(e.target);

        var target_position         = target.offset().top;
        var viewport_height         = jQuery(window).height();
        var almost_viewport_height  = 0.6 * viewport_height;
        var current_page_position   = jQuery(document).scrollTop();


        // if the target comfortably fits in the page when the page is scrolled to top
        // then prefer scrolling to top of the page instead of to the target itself
        if (target_position < almost_viewport_height)
        {
            jQuery('html,body').scrollTop( 0 );
        }
        else if
        (
            (target_position < current_page_position)  // page is scrolled down so that the target is not visible
            ||
            (target_position > (current_page_position + almost_viewport_height)) // page is scrolled up
        )
        {
            jQuery('html,body').scrollTop( target_position - 24 );
        }

    });

    page.on('scrolltoviewporttop', function(e)
    {
        var target = jQuery(e.target);
        var target_position = target.offset().top;
        jQuery('html,body').scrollTop( target_position );
    });


    jQuery(function()
    {
        // use setTimeout to trigger this after all scripts have been loaded
        // and attached their initial handlers for this event
        setTimeout( function()
        {
            page.trigger('contentloaded');
        }, 0);
    });

}());
