/**
* Template JS for standard pages
*/

(function ($) {
    /**
    * List of functions required to enable template effects/hacks
    * @var array
    */
    var templateSetup = new Array();

    /**
    * Add a new template function
    * @param function func the function to be called on a jQuery object
    * @param boolean prioritary set to true to call the function before all others (optional)
    * @return void
    */
    $.fn.addTemplateSetup = function (func, prioritary) {
        if (prioritary) {
            templateSetup.unshift(func);
        }
        else {
            templateSetup.push(func);
        }
    };

    /**
    * Call every template function over a jQuery object (for instance : $('body').applyTemplateSetup())
    * @return void
    */
    $.fn.applyTemplateSetup = function () {
        var max = templateSetup.length;
        for (var i = 0; i < max; ++i) {
            templateSetup[i].apply(this);
        }

        return this;
    };

    // Common (mobile and standard) template setup
    $.fn.addTemplateSetup(function () {
        // Collapsible fieldsets
        this.find('fieldset legend > a, .fieldset .legend > a').click(function (event) {
            $(this).toggleFieldsetOpen();
            event.preventDefault();
        });
        this.find('fieldset.collapse, .fieldset.collapse').toggleFieldsetOpen().removeClass('collapse');

        // Equalize tab content-blocks heights
        this.find('.tabs.same-height, .side-tabs.same-height, .mini-tabs.same-height, .controls-tabs.same-height').equalizeTabContentHeight();

        // Update tabs
        this.find('.js-tabs').updateTabs();

        // Input switches
        this.find('input[type=radio].switch, input[type=checkbox].switch').hide().after('<span class="switch-replace"></span>').next().click(function () {
            $(this).prev().click();
        }).prev('.with-tip').next().addClass('with-tip').each(function () {
            $(this).attr('title', $(this).prev().attr('title'));
        });
        this.find('input[type=radio].mini-switch, input[type=checkbox].mini-switch').hide().after('<span class="mini-switch-replace"></span>').next().click(function () {
            $(this).prev().click();
        }).prev('.with-tip').next().addClass('with-tip').each(function () {
            $(this).attr('title', $(this).prev().attr('title'));
        });

        // Tabs links behaviour
        this.find('.js-tabs a[href^="#"]').click(function (event) {
            event.preventDefault();

            // If hashtag enabled
            if ($.fn.updateTabs.enabledHash) {
                // Retrieve hash parts
                var element = $(this);
                var hash = $.trim(window.location.hash || '');
                if (hash.length > 1) {
                    // Remove hash from other tabs of the group
                    var hashParts = hash.substring(1).split('&');
                    var dummyIndex;
                    while ((dummyIndex = $.inArray('', hashParts)) > -1) {
                        hashParts.splice(dummyIndex, 1);
                    }
                    while ((dummyIndex = $.inArray('none', hashParts)) > -1) {
                        hashParts.splice(dummyIndex, 1);
                    }
                    element.parent().parent().find('a[href^="#"]').each(function (i) {
                        var index = $.inArray($(this).attr('href').substring(1), hashParts);
                        if (index > -1) {
                            hashParts.splice(index, 1);
                        }
                    });
                }
                else {
                    var hashParts = [];
                }

                // Add current tab to hash (not if default)
                var defaultTab = getDefaultTabIndex(element.parent().parent());
                if (element.parent().index() != defaultTab) {
                    hashParts.push(element.attr('href').substring(1));
                }

                // If only one tab, add a empty id to prevent document from jumping to selected content
                if (hashParts.length == 1) {
                    hashParts.unshift('');
                }

                // Put hash, will trigger refresh
                window.location.hash = (hashParts.length > 0) ? '#' + hashParts.join('&') : '#none';
            }
            else {
                var li = $(this).closest('li');
                li.addClass('current').siblings().removeClass('current');
                li.parent().updateTabs();
            }
        });
    });

    // Document initial setup
    $(document).ready(function () {
        // Template setup
        $(document.body).applyTemplateSetup();

        // Listener
        $(window).bind('hashchange', function () {
            $(document.body).find('.js-tabs').updateTabs();
        });

    });

    /**
    * Get tab group default tab
    */
    function getDefaultTabIndex(tabGroup) {
        var defaultTab = tabGroup.data('defaultTab');
        if (defaultTab === null || defaultTab === '' || defaultTab === undefined) {
            var firstTab = tabGroup.children('.current:first');
            defaultTab = (firstTab.length > 0) ? firstTab.index() : 0;
            tabGroup.data('defaultTab', defaultTab);
        }

        return defaultTab;
    };

    /**
    * Update tabs
    */
    $.fn.updateTabs = function () {
        // If hashtags enabled
        if ($.fn.updateTabs.enabledHash) {
            var hash = $.trim(window.location.hash || '');
            var hashParts = (hash.length > 1) ? hash.substring(1).split('&') : [];
        }
        else {
            var hash = '';
            var hashParts = [];
        }

        // Check all tabs
        var hasHash = (hashParts.length > 0);
        this.each(function (i) {
            // Check if already inited
            var tabGroup = $(this);
            var defaultTab = getDefaultTabIndex(tabGroup);

            // Look for current tab
            var current = false;
            if ($.fn.updateTabs.enabledHash) {
                if (hasHash) {
                    var links = tabGroup.find('a[href^="#"]');
                    links.each(function (i) {
                        var linkHash = $(this).attr('href').substring(1);
                        if (linkHash.length > 0) {
                            var index = $.inArray(linkHash, hashParts);
                            if (index > -1) {
                                current = $(this).parent();
                                return false;
                            }
                        }
                    });
                }
            }
            else {
                current = tabGroup.children('.current:first');
            }

            // If none found : get the default tab
            if (!current) {
                current = tabGroup.children(':eq(' + defaultTab + ')');
            }

            if (current.length > 0) {
                // Display current tab content block
                hash = $.trim(current.children('a').attr('href').substring(1));
                if (hash.length > 0) {
                    // Highlight current
                    current.addClass('current');
                    var tabContainer = $('#' + hash),
						tabHidden = tabContainer.is(':hidden');

                    // Show if hidden
                    if (tabHidden) {
                        tabContainer.show();
                    }

                    // Hide others
                    current.siblings().removeClass('current').children('a').each(function (i) {
                        var hash = $.trim($(this).attr('href').substring(1));
                        if (hash.length > 0) {
                            var tabContainer = $('#' + hash);

                            // Hide if visible
                            if (tabContainer.is(':visible')) {
                                tabContainer.trigger('tabhide').hide();
                            }
                            // Or init if first round
                            else if (!tabContainer.data('tabInited')) {
                                tabContainer.trigger('tabhide');
                                tabContainer.data('tabInited', true);
                            }
                        }
                    });

                    // Callback
                    if (tabHidden) {
                        tabContainer.trigger('tabshow');
                    }
                    // Or init if first round
                    else if (!tabContainer.data('tabInited')) {
                        tabContainer.trigger('tabshow');
                        tabContainer.data('tabInited', true);
                    }
                }
            }
        });

        return this;
    };

    /**
    * Indicate whereas JS tabs hashtag is enabled
    * @var boolean
    */
    $.fn.updateTabs.enabledHash = true;

    /**
    * Reset tab content blocks heights
    */
    $.fn.resetTabContentHeight = function () {
        this.find('a[href^="#"]').each(function (i) {
            var hash = $.trim($(this).attr('href').substring(1));
            if (hash.length > 0) {
                $('#' + hash).css('height', '');
            }
        });

        return this;
    }

    /**
    * Equalize tab content blocks heights
    */
    $.fn.equalizeTabContentHeight = function () {
        var i;
        var g;
        var maxHeight;
        var tabContainers;
        var block;
        var blockHeight;
        var marginAdjustTop;
        var marginAdjustBot;
        var first;
        var last;
        var firstMargin;
        var lastMargin;

        // Process in reverse order to equalize sub-tabs first
        for (i = this.length - 1; i >= 0; --i) {
            // Look for max height
            maxHeight = -1;
            tabContainers = [];
            this.eq(i).find('a[href^="#"]').each(function (i) {
                var hash = $.trim($(this).attr('href').substring(1));
                if (hash.length > 0) {
                    block = $('#' + hash);
                    if (block.length > 0) {
                        blockHeight = block.outerHeight() + parseInt(block.css('margin-bottom'));

                        // First element top-margin affects real height
                        marginAdjustTop = 0;
                        first = block.children(':first');
                        if (first.length > 0) {
                            firstMargin = parseInt(first.css('margin-top'));
                            if (!isNaN(firstMargin) && firstMargin < 0) {
                                marginAdjustTop = firstMargin;
                            }
                        }

                        // Same for last element bottom-margin
                        marginAdjustBot = 0;
                        last = block.children(':last');
                        if (last.length > 0) {
                            lastMargin = parseInt(last.css('margin-bottom'));
                            if (!isNaN(lastMargin) && lastMargin < 0) {
                                marginAdjustBot = lastMargin;
                            }
                        }

                        if (blockHeight + marginAdjustTop + marginAdjustBot > maxHeight) {
                            maxHeight = blockHeight + marginAdjustTop + marginAdjustBot;
                        }

                        tabContainers.push([block, marginAdjustTop]);
                    }
                }
            });

            // Setup height
            for (g = 0; g < tabContainers.length; ++g) {
                tabContainers[g][0].height(maxHeight - parseInt(tabContainers[g][0].css('padding-top')) - parseInt(tabContainers[g][0].css('padding-bottom')) - parseInt(tabContainers[g][0].css('margin-bottom')) - tabContainers[g][1]);

                // Only the first tab remains visible
                if (g > 0) {
                    tabContainers[g][0].hide();
                }
            }
        }

        return this;
    };

    /**
    * Display the selected tab (apply on tab content-blocks, not tabs, ie: $('#my-tab').showTab(); )
    */
    $.fn.showTab = function () {
        this.each(function (i) {
            $('a[href="#' + this.id + '"]').trigger('click');
        });

        return this;
    };

    /**
    * Add a listener fired when a tab is shown
    * @param function callback any function to call when the listener is fired.
    * @param boolean runOnce if true, the callback will be run one time only. Default: false - optional
    */
    $.fn.onTabShow = function (callback, runOnce) {
        if (runOnce) {
            var runOnceFunc = function () {
                callback.apply(this, arguments);
                $(this).unbind('tabshow', runOnceFunc);
            }
            this.bind('tabshow', runOnceFunc);
        }
        else {
            this.bind('tabshow', callback);
        }

        return this;
    };

    /**
    * Add a listener fired when a tab is hidden
    * @param function callback any function to call when the listener is fired.
    * @param boolean runOnce if true, the callback will be run one time only. Default: false - optional
    */
    $.fn.onTabHide = function (callback, runOnce) {
        if (runOnce) {
            var runOnceFunc = function () {
                callback.apply(this, arguments);
                $(this).unbind('tabhide', runOnceFunc);
            }
            this.bind('tabhide', runOnceFunc);
        }
        else {
            this.bind('tabhide', callback);
        }

        return this;
    };

    /**
    * Insert a message into a block
    * @param string|array message a message, or an array of messages to inserted
    * @param object options optional object with following values:
    * 		- type: one of the available message classes : 'info' (default), 'warning', 'error', 'success', 'loading'
    * 		- position: either 'top' (default) or 'bottom'
    * 		- animate: true to show the message with an animation (default), else false
    * 		- noMargin: true to apply the no-margin class to the message (default), else false
    */
    $.fn.blockMessage = function (message, options) {
        var settings = $.extend({}, $.fn.blockMessage.defaults, options);

        this.each(function (i) {
            // Locate content block
            var block = $(this);
            if (!block.hasClass('block-content')) {
                block = block.find('.block-content:first');
                if (block.length == 0) {
                    block = $(this).closest('.block-content');
                    if (block.length == 0) {
                        return;
                    }
                }
            }

            // Compose message
            var messageClass = (settings.type == 'info') ? 'message' : 'message ' + settings.type;
            if (settings.noMargin) {
                messageClass += ' no-margin';
            }
            var finalMessage = (typeof message == 'object') ? '<ul class="' + messageClass + '"><li>' + message.join('</li><li>') + '</li></ul>' : '<p class="' + messageClass + '">' + message + '</p>';

            // Insert message
            if (settings.position == 'top') {
                var children = block.find('h1, .h1, .block-controls, .block-header, .wizard-steps');
                if (children.length > 0) {
                    var lastHeader = children.last();
                    var next = lastHeader.next('.message');
                    while (next.length > 0) {
                        lastHeader = next;
                        next = lastHeader.next('.message');
                    }
                    var messageElement = lastHeader.after(finalMessage).next();
                }
                else {
                    var messageElement = block.prepend(finalMessage).children(':first');
                }
            }
            else {
                var children = block.find('.block-footer:last-child');
                if (children.length > 0) {
                    var messageElement = children.before(finalMessage).prev();
                }
                else {
                    var messageElement = block.append(finalMessage).children(':last');
                }
            }

            if (settings.animate) {
                messageElement.expand();
            }
        });

        return this;
    };

    // Default config for the blockMessage function
    $.fn.blockMessage.defaults = {
        type: 'info',
        position: 'top',
        animate: true,
        noMargin: true
    };

    /**
    * Remove all messages from the block
    * @param object options optional object with following values:
    * 		- only: string or array of strings of message classes which will be removed
    * 		- except: string or array of strings of message classes which will not be removed (ignored if 'only' is provided)
    * 		- animate: true to remove the message with an animation (default), else false
    */
    $.fn.removeBlockMessages = function (options) {
        var settings = $.extend({}, $.fn.removeBlockMessages.defaults, options);

        this.each(function (i) {
            // Locate content block
            var block = $(this);
            if (!block.hasClass('block-content')) {
                block = block.find('.block-content:first');
                if (block.length == 0) {
                    block = $(this).closest('.block-content');
                    if (block.length == 0) {
                        return;
                    }
                }
            }

            var messages = block.find('.message');
            if (settings.only) {
                if (typeof settings.only == 'string') {
                    settings.only = [settings.only];
                }
                messages = messages.filter('.' + settings.only.join(', .'));
            }
            else if (settings.except) {
                if (typeof settings.except == 'string') {
                    settings.except = [settings.except];
                }
                messages = messages.not('.' + settings.except.join(', .'));
            }

            if (settings.animate) {
                messages.foldAndRemove();
            }
            else {
                messages.remove();
            }
        });

        return this;
    };

    // Default config for the blockMessage function
    $.fn.removeBlockMessages.defaults = {
        only: false, 			// string or array of strings of message classes which will be removed
        except: false, 			// except: string or array of strings of message classes which will not be removed (ignored if only is provided)
        animate: true				// animate: true to remove the message with an animation (default), else false
    };

    /**
    * Fold an element
    * @param string|int duration a string (fast, normal or slow) or a number of millisecond. Default: 'normal'. - optional
    * @param function callback any function to call at the end of the effect. Default: none. - optional
    */
    $.fn.fold = function (duration, callback) {
        this.each(function (i) {
            var element = $(this);
            var marginTop = parseInt(element.css('margin-top'));
            var marginBottom = parseInt(element.css('margin-bottom'));

            var anim = {
                'height': 0,
                'paddingTop': 0,
                'paddingBottom': 0
            };

            // IE8 and lower do not understand border-xx-width
            // http://forum.jquery.com/topic/ie-invalid-argument
            if (!$.browser.msie || $.browser.version > 8) {
                // Border width is not set to 0 because it does not allow fluid movement 
                anim.borderTopWidth = '1px';
                anim.borderBottomWidth = '1px';
            }

            // Detection of elements sticking to their predecessor
            var prev = element.prev();
            if (prev.length === 0 && parseInt(element.css('margin-bottom')) + marginTop !== 0) {
                anim.marginTop = Math.min(0, marginTop);
                anim.marginBottom = Math.min(0, marginBottom);
            }

            // Effect
            element.stop(true).css({
                'overflow': 'hidden'
            }).animate(anim, {
                'duration': duration,
                'complete': function () {
                    // Reset properties
                    $(this).css({
                        'display': 'none',
                        'overflow': '',
                        'height': '',
                        'paddingTop': '',
                        'paddingBottom': '',
                        'borderTopWidth': '',
                        'borderBottomWidth': '',
                        'marginTop': '',
                        'marginBottom': ''
                    });

                    // Callback function
                    if (callback) {
                        callback.apply(this);
                    }
                }
            });
        });

        return this;
    };

    /*
    * Expand an element
    * @param string|int duration a string (fast, normal or slow) or a number of millisecond. Default: 'normal'. - optional
    * @param function callback any function to call at the end of the effect. Default: none. - optional
    */
    $.fn.expand = function (duration, callback) {
        this.each(function (i) {
            // Init
            var element = $(this);
            element.css('display', 'block');

            // Reset and get values
            element.stop(true).css({
                'overflow': '',
                'height': '',
                'paddingTop': '',
                'paddingBottom': '',
                'borderTopWidth': '',
                'borderBottomWidth': '',
                'marginTop': '',
                'marginBottom': ''
            });
            var height = element.height();
            var paddingTop = parseInt(element.css('padding-top'));
            var paddingBottom = parseInt(element.css('padding-bottom'));
            var marginTop = parseInt(element.css('margin-top'));
            var marginBottom = parseInt(element.css('margin-bottom'));

            // Initial and target values
            var css = {
                'overflow': 'hidden',
                'height': 0,
                'paddingTop': 0,
                'paddingBottom': 0
            };
            var anim = {
                'height': height,
                'paddingTop': paddingTop,
                'paddingBottom': paddingBottom
            };

            // IE8 and lower do not understand border-xx-width
            // http://forum.jquery.com/topic/ie-invalid-argument
            if (!$.browser.msie || $.browser.version > 8) {
                var borderTopWidth = parseInt(element.css('border-top-width'));
                var borderBottomWidth = parseInt(element.css('border-bottom-width'));

                // Border width is not set to 0 because it does not allow fluid movement 
                css.borderTopWidth = '1px';
                css.borderBottomWidth = '1px';
                anim.borderTopWidth = borderTopWidth;
                anim.borderBottomWidth = borderBottomWidth;
            }

            // Detection of elements sticking to their predecessor
            var prev = element.prev();
            if (prev.length === 0 && parseInt(element.css('margin-bottom')) + marginTop !== 0) {
                css.marginTop = Math.min(0, marginTop);
                css.marginBottom = Math.min(0, marginBottom);
                anim.marginTop = marginTop;
                anim.marginBottom = marginBottom;
            }

            // Effect
            element.stop(true).css(css).animate(anim, {
                'duration': duration,
                'complete': function () {
                    // Reset properties
                    $(this).css({
                        'display': '',
                        'overflow': '',
                        'height': '',
                        'paddingTop': '',
                        'paddingBottom': '',
                        'borderTopWidth': '',
                        'borderBottomWidth': '',
                        'marginTop': '',
                        'marginBottom': ''
                    });

                    // Callback function
                    if (callback) {
                        callback.apply(this);
                    }

                    // Required for IE7 - don't ask me why...
                    if ($.browser.msie && $.browser.version < 8) {
                        $(this).css('zoom', 1);
                    }
                }
            });
        });

        return this;
    };

    /**
    * Remove an element with folding effect
    * @param string|int duration a string (fast, normal or slow) or a number of millisecond. Default: 'normal'. - optional
    * @param function callback any function to call at the end of the effect. Default: none. - optional
    */
    $.fn.foldAndRemove = function (duration, callback) {
        $(this).fold(duration, function () {
            // Callback function
            if (callback) {
                callback.apply(this);
            }

            $(this).remove();
        });

        return this;
    }

    /**
    * Remove an element with fading then folding effect
    * @param string|int duration a string (fast, normal or slow) or a number of millisecond. Default: 'normal'. - optional
    * @param function callback any function to call at the end of the effect. Default: none. - optional
    */
    $.fn.fadeAndRemove = function (duration, callback) {
        this.animate({ 'opacity': 0 }, {
            'duration': duration,
            'complete': function () {
                // No folding required if the element has position: absolute (not in the elements flow)
                if ($(this).css('position') == 'absolute') {
                    // Callback function
                    if (callback) {
                        callback.apply(this);
                    }

                    $(this).remove();
                }
                else {
                    $(this).slideUp(duration, function () {
                        // Callback function
                        if (callback) {
                            callback.apply(this);
                        }

                        $(this).remove();
                    });
                }
            }
        });

        return this;
    };

    /**
    * Open/close fieldsets
    */
    $.fn.toggleFieldsetOpen = function () {
        this.each(function () {
            /*
            * Tip: if you want to add animation or do anything that should not occur at startup closing, 
            * check if the element has the class 'collapse':
            * if (!$(this).hasClass('collapse')) { // Anything that sould no occur at startup }
            */

            // Change
            $(this).closest('fieldset, .fieldset').toggleClass('collapsed');
        });

        return this;
    };

    /**
    * Add a semi-transparent layer in front of an element
    */
    $.fn.addEffectLayer = function (options) {
        var settings = $.extend({}, $.fn.addEffectLayer.defaults, options);

        this.each(function (i) {
            var element = $(this);

            // Add layer
            var refElement = getNodeRefElement(this);
            var layer = $('<div class="loading-mask"><span>' + settings.message + '</span></div>').insertAfter(refElement);

            // Position
            var elementOffset = element.position();
            layer.css({
                top: elementOffset.top + 'px',
                left: elementOffset.left + 'px'
            }).width(element.outerWidth()).height(element.outerHeight());

            // Effect
            var span = layer.children('span');
            var marginTop = parseInt(span.css('margin-top'));
            span.css({ 'opacity': 0, 'marginTop': (marginTop - 40) + 'px' });
            layer.css({ 'opacity': 0 }).animate({ 'opacity': 1 }, {
                'complete': function () {
                    span.animate({ 'opacity': 1, 'marginTop': marginTop + 'px' });
                }
            });
        });

        return this;
    };

    /**
    * Retrieve the reference element after which the layer will be inserted
    * @param HTMLelement node the node on which the effect is applied
    * @return HTMLelement the reference node
    */
    function getNodeRefElement(node) {
        var element = $(node);

        // Special case
        if (node.nodeName.toLowerCase() == 'document' || node.nodeName.toLowerCase() == 'body') {
            var refElement = $(document.body).children(':last').get(0);
        }
        else {
            // Look for the reference element, the one after which the layer will be inserted
            var refElement = node;
            var offsetParent = element.offsetParent().get(0);

            // List of elements in which we can add a div
            var absPos = ['absolute', 'relative'];
            while (refElement && refElement !== offsetParent && !$.inArray($(refElement.parentNode).css('position'), absPos)) {
                refElement = refElement.parentNode;
            }
        }

        return refElement;
    }

    // Default params for the loading effect layer
    $.fn.addEffectLayer.defaults = {
        message: 'Please wait...'
    };

    /**
    * jQuery load() method wrapper : add a visual effect on the load() target
    * Parameters are the same as load()
    */
    $.fn.loadWithEffect = function () {
        // Add effect layer
        this.addEffectLayer({
            message: $.fn.loadWithEffect.defaults.message
        });

        // Rewrite callback function
        var target = this;
        var callback = false;
        var args = $.makeArray(arguments);
        var index = args.length;
        if (args[2] && typeof args[2] == 'function') {
            callback = args[2];
            index = 2;
        }
        else if (args[1] && typeof args[1] == 'function') {
            callback = args[1];
            index = 1;
        }

        // Custom callback
        args[index] = function (responseText, textStatus, XMLHttpRequest) {
            // Get the effect layer
            var refElement = getNodeRefElement(this);
            var layer = $(refElement).next('.loading-mask');
            var span = layer.children('span');

            // If success
            if (textStatus == 'success' || textStatus == 'notmodified') {
                // Initial callback
                if (callback) {
                    callback.apply(this, arguments);
                }

                // Remove effect layer
                layer.stop(true);
                span.stop(true);
                var currentMarginTop = parseInt(span.css('margin-top'));
                var marginTop = parseInt(span.css('margin-top', '').css('margin-top'));
                span.css({ 'marginTop': currentMarginTop + 'px' }).animate({ 'opacity': 0, 'marginTop': (marginTop - 40) + 'px' }, {
                    'complete': function () {
                        layer.fadeAndRemove();
                    }
                });
            }
            else {
                span.addClass('error').html($.fn.loadWithEffect.defaults.errorMessage + '<br><a href="#">' + $.fn.loadWithEffect.defaults.retry + '</a> / <a href="#">' + $.fn.loadWithEffect.defaults.cancel + '</a>');
                span.children('a:first').click(function (event) {
                    event.preventDefault();

                    // Relaunch request
                    $.fn.load.apply(target, args);

                    // Reset
                    span.removeClass('error').html($.fn.loadWithEffect.defaults.message).css('margin-left', '');
                });
                span.children('a:last').click(function (event) {
                    event.preventDefault();

                    // Remove effect layer
                    layer.stop(true);
                    span.stop(true);
                    var currentMarginTop = parseInt(span.css('margin-top'));
                    var marginTop = parseInt(span.css('margin-top', '').css('margin-top'));
                    span.css({ 'marginTop': currentMarginTop + 'px' }).animate({ 'opacity': 0, 'marginTop': (marginTop - 40) + 'px' }, {
                        'complete': function () {
                            layer.fadeAndRemove();
                        }
                    });
                });

                // Centering
                span.css('margin-left', -Math.round(span.outerWidth() / 2));
            }
        };

        // Redirect to jQuery load
        $.fn.load.apply(target, args);

        return this;
    };

    // Default texts for the loading effect layer
    $.fn.loadWithEffect.defaults = {
        message: 'Loading...',
        errorMessage: 'Error while loading',
        retry: 'Retry',
        cancel: 'Cancel'
    };

    /**
    * Enable any button with workaround for IE lack of :disabled selector
    */
    $.fn.enableBt = function () {
        $(this).attr('disabled', false);
        if ($.browser.msie && $.browser.version < 9) {
            $(this).removeClass('disabled');
        }
    }

    /**
    * Disable any button with workaround for IE lack of :disabled selector
    */
    $.fn.disableBt = function () {
        $(this).attr('disabled', true);
        if ($.browser.msie && $.browser.version < 9) {
            $(this).addClass('disabled');
        }
    }

})(jQuery);


/**
* Template JS for standard pages
*/

(function ($) {
    // Standard template setup
    $.fn.addTemplateSetup(function () {
        // Mini menu
        this.find('.mini-menu').css({ opacity: 0 }).parent().hover(function () {
            $(this).children('.mini-menu').stop(true).animate({ opacity: 1 });

        }, function () {
            $(this).children('.mini-menu').css('display', 'block').stop(true).animate({ opacity: 0 }, { 'complete': function () { $(this).css('display', ''); } });

        });

        // CSS Menu improvement
        this.find('.menu, .menu li:has(ul)').hover(function () {
            $(this).openDropDownMenu();

        }, function () {
            // Remove in case of future window resizing
            $(this).children('ul').removeClass('reverted');
        });

        // Scroll top button
        $('a[href="#top"]').click(function (event) {
            event.preventDefault();
            $('html, body').animate({ scrollTop: 0 });
        });
    });

    // Close buttons
    $('.close-bt').live('click', function () {
        $(this).parent().fadeAndRemove();
    });

    // Document initial setup
    $(document).ready(function () {
        // Notifications blocks
        var notifications = $('<ul id="notifications"></ul>').appendTo(document.body);
        var notificationsTop = parseInt(notifications.css('top'));

        // If it is a standard page
        if (!$(document.body).hasClass('special-page')) {
            // Main nav - click style
            $('nav > ul > li').click(function (event) {
                // If not already active and has sub-menu
                if (!$(this).hasClass('current') && $(this).find('ul li').length > 0) {
                    $(this).addClass('current').siblings().removeClass('current');
                    $('nav > ul > li').refreshTip();
                    event.preventDefault();
                }
            }).tip({
                stickIfCurrent: true,
                offset: -3
            });

            // Main nav - hover style
            /*$('nav > ul > li').hover(function() {
            $(this).addClass('current').siblings().removeClass('current');
            $('nav > ul > li').refreshTip();
            }, function() {}).tip({
            stickIfCurrent: true,
            offset: -3
            });*/

            // Advanced search field
            if ($.fn.advancedSearchField) {
                $('#s').advancedSearchField();
            }

            // Status bar buttons : drop-downs fade In/Out
            function convertDropLists() {
                $(this).find('.result-block .small-files-list').accessibleList({ moreAfter: false });

                // Run only once
                $(this).unbind('mouseenter', convertDropLists);
            }
            $('#status-infos li:has(.result-block)').hover(function () {
                $(this).find('.result-block').stop(true).css('display', 'none').fadeIn('normal', function () {
                    $(this).css('opacity', '');
                });

            }, function () {
                $(this).find('.result-block').stop(true).css('display', 'block').fadeOut('normal', function () {
                    $(this).css('opacity', '');
                });

            }).bind('mouseenter', convertDropLists);

            // Fixed control bar
            var controlBar = $('#control-bar');
            if (controlBar.length > 0) {
                var cbPlaceHolder = controlBar.after('<div id="cb-place-holder" style="height:' + controlBar.outerHeight() + 'px"></div>').next();

                // Effect
                controlBar.hover(function () {
                    if ($(this).hasClass('fixed')) {
                        $(this).stop(true).fadeTo('fast', 1);
                    }

                }, function () {
                    if ($(this).hasClass('fixed')) {
                        $(this).stop(true).fadeTo('slow', 0.5);
                    }
                });

                // Listener
                $(window).scroll(function () {
                    // Check top position
                    var controlBarPos = controlBar.hasClass('fixed') ? cbPlaceHolder.offset().top : controlBar.offset().top;

                    if ($(window).scrollTop() > controlBarPos) {
                        if (!controlBar.hasClass('fixed')) {
                            cbPlaceHolder.height(controlBar.outerHeight()).show();
                            controlBar.addClass('fixed').stop(true).fadeTo('slow', 0.5);

                            // Notifications
                            $('#notifications').animate({ 'top': controlBar.outerHeight() + notificationsTop });
                        }
                    }
                    else {
                        if (controlBar.hasClass('fixed')) {
                            cbPlaceHolder.hide();
                            controlBar.removeClass('fixed').stop(true).fadeTo('fast', 1, function () {
                                // Required for IE
                                $(this).css('filter', '');
                            });

                            // Notifications
                            $('#notifications').animate({ 'top': notificationsTop });
                        }
                    }
                }).trigger('scroll');
            }
        }

    });

    /**
    * Internal function to open drop-down menus, required for context menu
    */
    $.fn.openDropDownMenu = function () {
        var ul = this.children('ul');

        // Position check
        if (ul.offset().left + ul.outerWidth() - $(window).scrollLeft() > $(window).width()) {
            ul.addClass('reverted');
        }

        // Effect - IE < 9 uses filter for opacity, cutting out sub-menus
        if (!$.browser.msie || $.browser.version > 8) {
            ul.stop(true).css({ opacity: 0 }).animate({ opacity: 1 });
        }
    };

})(jQuery);


/**
* Display a notification. If the page is not yet ready, delay the notification until it is ready.
* @var string message a text or html message to display
* @var object options an object with any options for the message - optional
* 		- closeButton: true to add a close button to the message (default: true)
* 		- autoClose: true to close message after (closeDelay) ms (default: true)
* 		- closeDelay: delay before message close (default: 8000)
*/
var notify = function (message, options) {
    var block = jQuery('#notifications');

    // If ready
    if (block.length > 0) {
        var settings = jQuery.extend({}, notify.defaults, options);

        // Append message
        var closeButton = settings.closeButton ? '<span class="close-bt"></span>' : '';
        var element = jQuery('#notifications').append('<li>' + message + closeButton + '</li>').children(':last-child');

        // Effect
        element.expand();

        // If closing
        if (settings.autoClose) {
            // Timer
            var timeoutId = setTimeout(function () { element.fadeAndRemove(); }, settings.closeDelay);

            // Prevent closing when hover
            element.hover(function () {
                clearTimeout(timeoutId);

            }, function () {
                timeoutId = setTimeout(function () { element.fadeAndRemove(); }, settings.closeDelay);
            });
        }
    }
    else {
        // Not ready, delay action
        setTimeout(function () { notify(message, options); }, 40);
    }
};

// Defaults values for the notify method
notify.defaults = {
    closeButton: true, 		// Add a close button to the message
    autoClose: true, 		// Message will close after (closeDelay) ms
    closeDelay: 8000			// Delay before message closes
};
/**
* Add tip to any element
*/

(function ($) {
    /**
    * Adds tip on selected elements
    * @param object options an object with any of the following options
    * 		- content: Content of the tip (may be HTML) or false for auto detect (default: false)
    * 		- onHover: Show tip only on hover (default: true)
    * 		- reverseHover: Hide tip on hover in, show on hover out (default: false)
    * 		- stickIfCurrent: Enable permanent tip on elements with the class 'current' (default: false)
    * 		- currentClass: Name of the 'current' class (default: 'current')
    * 		- offset: Offset of the tip from the element (default: 4)
    * 		- position: Position of the tip relative to the element (default: 'top')
    * 		- animationOffset: Offset for the animation (pixels) (default: 4)
    * 		- delay: Delay before tip shows up on hover (milliseconds) (default: 0)
    */
    $.fn.tip = function (options) {
        var settings = $.extend({}, $.fn.tip.defaults, options);

        // Mode
        if (settings.onHover) {
            // Detect current elements
            if (settings.stickIfCurrent) {
                this.filter('.' + settings.currentClass).each(function (i) {
                    $(this).createTip(settings);
                });
            }

            // Effect
            if (settings.reverseHover) {
                $(this).createTip(settings);

                this.hover(function () {
                    if (!settings.stickIfCurrent || !$(this).hasClass(settings.currentClass)) {
                        $(this).hideTip();
                    }

                }, function () {
                    $(this).showTip(settings);
                });
            }
            else {
                this.hover(function () {
                    $(this).showTip(settings);

                }, function () {
                    if (!settings.stickIfCurrent || !$(this).hasClass(settings.currentClass)) {
                        $(this).hideTip();
                    }
                });
            }
        }
        else {
            this.createTip(settings);
        }

        return this;
    };

    $.fn.tip.defaults = {
        /**
        * Content of the tip (may be HTML) or false for auto detect
        * @var string|boolean
        */
        content: false,

        /**
        * Show tip only on hover
        * @var boolean
        */
        onHover: true,

        /**
        * Hide tip on hover in, show on hover out
        * @var boolean
        */
        reverseHover: false,

        /**
        * Enable permanent tip on elements with the class 'current'
        * @var boolean
        */
        stickIfCurrent: false,

        /**
        * Name of the 'current' class
        * @var boolean
        */
        currentClass: 'current',

        /**
        * Offset of the tip from the element (pixels)
        * @var int
        */
        offset: 4,

        /**
        * Position of the tip relative to the element
        * @var string
        */
        position: 'top',

        /**
        * Offset for the animation (pixels)
        * @var int
        */
        animationOffset: 4,

        /**
        * Delay before tip shows up on hover (milliseconds)
        * @var int
        */
        delay: 0
    };

    /**
    * Add tip (if not exist) and show it with effect
    * @param object options same as the tip() method
    */
    $.fn.showTip = function (options) {
        var settings = $.extend({}, $.fn.tip.defaults, options);

        this.each(function (i) {
            var element = $(this);
            var oldIE = ($.browser.msie && $.browser.version < 9);

            // If tip does not already exist (if current), create it
            var tip = element.data('tip');
            if (!tip) {
                element.createTip(settings, oldIE ? false : true);
                tip = element.data('tip');
            }
            else if (settings.content !== element.data('settings').content) {
                element.updateTipContent(options.content);
            }

            // Animation
            if (!oldIE)	// IE6-8 filters do not allow correct animation (the arrow is truncated)
            {
                var position = getTipPosition(element, tip, settings, false);
                tip.stop(true).delay(settings.delay).animate({
                    opacity: 1,
                    top: position.top,
                    left: position.left
                }, 'fast');
            }
        });

        return this;
    };

    /**
    * Hide then remove tip
    */
    $.fn.hideTip = function () {
        this.each(function (i) {
            var element = $(this);
            var tip = element.data('tip');
            if (tip) {
                var settings = element.data('settings');

                if ($.browser.msie && $.browser.version < 9) {
                    // IE8 and lower filters do not allow correct animation (the arrow is truncated)
                    tip.children('.arrow').remove();
                    this.title = tip.html();
                    element.data('tip', false);
                    tip.remove();
                }
                else {
                    // Hiding is not relative to the parent element, to prevent weird behaviour if parent is moved or removed
                    var position = getFinalPosition(tip, settings);
                    var offset = tip.offset();

                    switch (position) {
                        case 'right':
                            offset.left += settings.animationOffset + settings.offset;
                            break;

                        case 'bottom':
                            offset.top += settings.animationOffset + settings.offset;
                            break;

                        case 'left':
                            offset.left -= settings.animationOffset + settings.offset;
                            break;

                        default:
                            offset.top -= settings.animationOffset + settings.offset;
                            break;
                    }

                    tip.animate({
                        opacity: 0,
                        top: offset.top,
                        left: offset.left
                    }, {
                        complete: function () {
                            // Restore node
                            var tip = $(this);
                            var node = tip.data('node');
                            if (node) {
                                tip.children('.arrow').remove();
                                node.attr('title', tip.html());
                                node.data('tip', false);
                            }

                            // Remove tip
                            tip.remove();
                        }
                    });
                }
            }
        });

        return this;
    };

    /**
    * Create tip bubble
    * @param object settings the options object given to tip()
    * @param boolean hide indicate whether to hide the tip after creating it or not (default : false)
    */
    $.fn.createTip = function (settings, hide) {
        this.each(function (i) {
            var element = $(this);
            var tips = getTipDiv();

            // Insertion
            tips.append('<div></div>');
            var tip = tips.children(':last-child');

            // Position class
            if (settings.position == 'right' || element.hasClass('tip-right') || element.parent().hasClass('children-tip-right')) {
                tip.addClass('tip-right');
            }
            else if (settings.position == 'bottom' || element.hasClass('tip-bottom') || element.parent().hasClass('children-tip-bottom')) {
                tip.addClass('tip-bottom');
            }
            else if (settings.position == 'left' || element.hasClass('tip-left') || element.parent().hasClass('children-tip-left')) {
                tip.addClass('tip-left');
            }

            // Cross references
            tip.data('node', element);
            element.data('tip', tip);
            element.data('settings', settings);

            // Content
            element.updateTipContent(settings.content, hide);

            // Effect
            if (hide) {
                tip.css({ opacity: 0 });
            }
        });

        return this;
    };

    /**
    * Update tip content
    * @param mixed content any content (text or HTML) for the tip, of false for automatic detection
    * @param boolean hide optional, compatibility with createTip()
    */
    $.fn.updateTipContent = function (content, hide) {
        this.each(function (i) {
            var element = $(this);
            var tip = element.data('tip');
            var settings = element.data('settings');

            // If auto tip content
            if (!content) {
                if (this.title && this.title.length > 0) {
                    var finalContent = this.title;
                    this.title = '';
                }
                else {
                    var subTitle = element.find('[title]:first');
                    if (subTitle.length > 0) {
                        var finalContent = subTitle.attr('title');
                        subTitle.attr('title', '');
                    }
                    else {
                        var finalContent = element.text();
                    }
                }
            }
            else {
                var finalContent = content;
            }

            // If empty
            if (!finalContent || $.trim(finalContent).length == 0) {
                finalContent = '<em>No tip</em>';
            }

            // Insert
            tip.html(finalContent + '<span class="arrow"><span></span></span>');

            // Position
            tip.stop(true, true);
            var position = getTipPosition(element, tip, settings, hide);
            tip.offset(position);
        });

        return this;
    };

    /**
    * Call this function to refresh tips when using the stickIfCurrent option 
    * and the 'current' element has changed
    */
    $.fn.refreshTip = function () {
        this.each(function (i) {
            var settings = $(this).data('settings');
            if (settings && settings.stickIfCurrent) {
                var element = $(this);
                if (element.hasClass(settings.currentClass)) {
                    element.showTip(settings);
                }
                else {
                    element.hideTip(settings);
                }
            }
        });

        return this;
    };

    /**
    * Detect final position for the tip
    * @param jQuery tip the tip element
    * @param Object settings the tip options
    * @return string the final position
    */
    function getFinalPosition(tip, settings) {
        var position = settings.position;
        if (tip.hasClass('tip-right')) {
            position = 'right';
        }
        else if (tip.hasClass('tip-bottom')) {
            position = 'bottom';
        }
        else if (tip.hasClass('tip-left')) {
            position = 'left';
        }

        return position;
    }

    /**
    * Get tip position, relative to the element
    * @param jQuery element the element on which the the tip show
    * @param jQuery tip the tip element
    * @param Object settings the tip options
    * @param boolean animStart tells wether the tip should be positioned at the start of the animation or not
    * @return Object an object with two values : 'top' and 'left'
    */
    function getTipPosition(element, tip, settings, animStart) {
        var offset = element.offset();
        var position = getFinalPosition(tip, settings);

        switch (position) {
            case 'right':
                return {
                    top: Math.round(offset.top + (element.outerHeight() / 2) - (tip.outerHeight() / 2)),
                    left: Math.round(offset.left + element.outerWidth() + (animStart ? settings.animationOffset + settings.offset : settings.offset))
                };
                break;

            case 'bottom':
                return {
                    top: Math.round(offset.top + element.outerHeight() + (animStart ? settings.animationOffset + settings.offset : settings.offset)),
                    left: Math.round(offset.left + (element.outerWidth() / 2) - (tip.outerWidth() / 2))
                };
                break;

            case 'left':
                return {
                    top: Math.round(offset.top + (element.outerHeight() / 2) - (tip.outerHeight() / 2)),
                    left: Math.round(offset.left - tip.outerWidth() - (animStart ? settings.animationOffset + settings.offset : settings.offset))
                };
                break;

            default:
                return {
                    top: Math.round(offset.top - tip.outerHeight() - (animStart ? settings.animationOffset + settings.offset : settings.offset)),
                    left: Math.round(offset.left + (element.outerWidth() / 2) - (tip.outerWidth() / 2))
                };
                break;
        }
    }

    // If template common functions loaded
    if ($.fn.addTemplateSetup) {
        $.fn.addTemplateSetup(function () {
            this.find('.with-tip, .with-children-tip > *').tip();
        });
    }
    else {
        // Default behaviour
        $(document).ready(function () {
            $('.with-tip, .with-children-tip > *').tip();
        });
    }

    /**
    * Return the tips div, or create it if it does not exist
    */
    function getTipDiv() {
        var tips = $('#tips');
        if (tips.length == 0) {
            $(document.body).append('<div id="tips"></div>');
            tips = $('#tips');
        }

        return tips;
    }

    // Handle viewport resizing
    $(window).resize(function () {
        getTipDiv().children().each(function (i) {
            // Init
            var tip = $(this);
            var element = tip.data('node');
            var settings = element.data('settings');
            var isCurrent = settings.stickIfCurrent && element.hasClass(settings.currentClass);

            // Position
            var animate = (settings.onHover && !isCurrent);
            tip.stop(true, true);
            var position = getTipPosition(element, tip, settings, animate);
            tip.offset(position);
        });
    });

})(jQuery);
/**
* Enables the context menu for elements bound to the 'contextMenu' event
*/

(function ($) {
    /*
    * Enable context menu
    */
    document.oncontextmenu = function (event) {
        var e = window.event || event;
        var target = $(e.target || e.srcElement);
        var list = [];
        target.trigger('contextMenu', [list]);

        // If some menu elements added
        if (list.length > 0) {
            // Mouse position
            var posx = 0;
            var posy = 0;
            if (e.pageX || e.pageY) {
                posx = e.pageX;
                posy = e.pageY;
            }
            else if (e.clientX || e.clientY) {
                posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
            }

            $('#contextMenu').html(buildMenuLevel(list)).css({
                top: posy + 'px',
                left: posx + 'px'
            }).show().openDropDownMenu();

            // Listener
            $(document).bind('click', closeContextMenu);

            // Prevent browser menu
            return false;
        }
    };

    /*
    * Simple functions for closing menu
    */
    function closeContextMenu() {
        $('#contextMenu').empty().hide();
        removeBinding();
    };
    function removeBinding() {
        $(document).unbind('click', closeContextMenu);
    };

    // Insert menu element
    $(document).ready(function () {
        $(document.body).append('<div id="contextMenu" class="menu"></div>');
    });

    /**
    * Builds a level of the menu (recursive function)
    * @param array list the menu elements list
    */
    function buildMenuLevel(list) {
        var html = '<ul>';
        var defaults = {
            text: 'Link',
            alt: '',
            link: '',
            subs: [],
            icon: ''
        };

        for (var element in list) {
            // If separation
            if (typeof (list[element]) != 'object') {
                html += '<li class="sep"></li>';
            }
            else {
                var el = $.extend({}, defaults, list[element]);
                var alt = (el.alt.length > 0) ? ' title="' + el.alt + '"' : '';
                var icon = (el.icon.length > 0) ? ' class="icon_' + el.icon + '"' : '';
                if (el.link.length > 0) {
                    var opener = 'a href="' + el.link + '"';
                    var closer = 'a';
                }
                else {
                    var opener = 'span';
                    var closer = 'span';
                }

                // Opening
                html += '<li' + icon + '><' + opener + alt + '>' + el.text + '</' + closer + '>';

                // If sub menus
                if (typeof (el.subs) == 'object' && el.subs.length > 0) {
                    html += buildMenuLevel(el.subs);
                }

                // Close
                html += '</li>';
            }
        }

        return html + '</ul>';
    };

})(jQuery);
/**
* Modal window extension
*/

(function ($) {
    /**
    * Opens a new modal window
    * @param object options an object with any of the following options
    * @return object the jQuery object of the new window
    */
    $.modal = function (options) {
        var settings = $.extend({}, $.modal.defaults, options),
			root = getModalDiv(),

        // Vars for resizeFunc and moveFunc
			winX = 0,
			winY = 0,
			contentWidth = 0,
			contentHeight = 0,
			mouseX = 0,
			mouseY = 0,
			resized;

        // Get contents
        var content = '';
        var contentObj;
        if (settings.content) {
            if (typeof (settings.content) == 'string') {
                content = settings.content;
            }
            else {
                contentObj = settings.content.clone(true).show();
            }
        }
        else {
            // No content
            content = '';
        }

        // Title
        var titleClass = settings.title ? '' : ' no-title';
        var title = settings.title ? '<h1>' + settings.title + '</h1>' : '';

        // Content size
        var sizeParts = new Array();
        sizeParts.push('min-width:' + settings.minWidth + 'px;');
        sizeParts.push('min-height:' + settings.minHeight + 'px;');
        if (settings.width) {
            sizeParts.push('width:' + settings.width + 'px; ');
        }
        if (settings.height) {
            sizeParts.push('height:' + settings.height + 'px; ');
        }
        if (settings.maxWidth) {
            sizeParts.push('max-width:' + settings.maxWidth + 'px; ');
        }
        if (settings.maxHeight) {
            sizeParts.push('max-height:' + settings.maxHeight + 'px; ');
        }
        var contentStyle = (sizeParts.length > 0) ? ' style="' + sizeParts.join(' ') + '"' : '';

        // Borders
        var borderOpen = settings.border ? '"><div class="block-content' + titleClass : titleClass;
        var borderClose = settings.border ? '></div' : '';

        // Scrolling
        var scrollClass = settings.scrolling ? ' modal-scroll' : '';

        // Insert window
        var win = $('<div class="modal-window block-border' + borderOpen + '">' + title + '<div class="modal-content' + scrollClass + '"' + contentStyle + '>' + content + '</div></div' + borderClose + '>').appendTo(root);
        var contentDiv = win.find('.modal-content');
        if (contentObj) {
            contentObj.appendTo(contentDiv);
        }

        // If resizable
        if (settings.resizable && settings.border) {
            // Custom function (to use correct var scope)
            var resizeFunc = function (event) {
                // Mouse offset
                var offsetX = event.pageX - mouseX,
					offsetY = event.pageY - mouseY,

                // New size
					newWidth = Math.max(settings.minWidth, contentWidth + (resized.width * offsetX)),
					newHeight = Math.max(settings.minHeight, contentHeight + (resized.height * offsetY)),

                // Position correction
					correctX = 0,
					correctY = 0;

                // If max sizes are defined
                if (settings.maxWidth && newWidth > settings.maxWidth) {
                    correctX = newWidth - settings.maxWidth;
                    newWidth = settings.maxWidth;
                }
                if (settings.maxHeight && newHeight > settings.maxHeight) {
                    correctY = newHeight - settings.maxHeight;
                    newHeight = settings.maxHeight;
                }

                contentDiv.css({
                    width: newWidth + 'px',
                    height: newHeight + 'px'
                });
                win.css({
                    left: (winX + (resized.left * (offsetX + correctX))) + 'px',
                    top: (winY + (resized.top * (offsetY + correctY))) + 'px'
                });
            };

            // Create resize handlers
            $('<div class="modal-resize-tl"></div>').appendTo(win).data('modal-resize', {
                top: 1, left: 1,
                height: -1, width: -1

            }).add(
				$('<div class="modal-resize-t"></div>').appendTo(win).data('modal-resize', {
				    top: 1, left: 0,
				    height: -1, width: 0
				})
			).add(
				$('<div class="modal-resize-tr"></div>').appendTo(win).data('modal-resize', {
				    top: 1, left: 0,
				    height: -1, width: 1
				})
			).add(
				$('<div class="modal-resize-r"></div>').appendTo(win).data('modal-resize', {
				    top: 0, left: 0,
				    height: 0, width: 1
				})
			).add(
				$('<div class="modal-resize-br"></div>').appendTo(win).data('modal-resize', {
				    top: 0, left: 0,
				    height: 1, width: 1
				})
			).add(
				$('<div class="modal-resize-b"></div>').appendTo(win).data('modal-resize', {
				    top: 0, left: 0,
				    height: 1, width: 0
				})
			).add(
				$('<div class="modal-resize-bl"></div>').appendTo(win).data('modal-resize', {
				    top: 0, left: 1,
				    height: 1, width: -1
				})
			).add(
				$('<div class="modal-resize-l"></div>').appendTo(win).data('modal-resize', {
				    top: 0, left: 1,
				    height: 0, width: -1
				})
			).mousedown(function (event) {
			    // Detect positions
			    contentWidth = contentDiv.width();
			    contentHeight = contentDiv.height();
			    var position = win.position();
			    winX = position.left;
			    winY = position.top;
			    mouseX = event.pageX;
			    mouseY = event.pageY;
			    resized = $(this).data('modal-resize');

			    // Prevent text selection
			    document.onselectstart = function () { return false; };

			    $(document).bind('mousemove', resizeFunc);

			})
            root.mouseup(function () {
                $(document).unbind('mousemove', resizeFunc);

                // Restore text selection
                document.onselectstart = null;
            });
        }

        // Put in front
        win.mousedown(function () {
            $(this).putModalOnFront();
        });

        // If movable
        if (settings.draggable && title) {
            // Custom functions (to use correct var scope)
            var moveFunc = function (event) {
                // Window and document sizes
                var width = win.outerWidth(),
					height = win.outerHeight();

                // New position
                win.css({
                    left: Math.max(0, Math.min(winX + (event.pageX - mouseX), $(root).width() - width)) + 'px',
                    top: Math.max(0, Math.min(winY + (event.pageY - mouseY), $(root).height() - height)) + 'px'
                });
            };

            // Listeners
            win.find('h1:first').mousedown(function (event) {
                // Detect positions
                var position = win.position();
                winX = position.left;
                winY = position.top;
                mouseX = event.pageX;
                mouseY = event.pageY;

                // Prevent text selection
                document.onselectstart = function () { return false; };

                $(document).bind('mousemove', moveFunc);

            })
            root.mouseup(function () {
                $(document).unbind('mousemove', moveFunc);

                // Restore text selection
                document.onselectstart = null;
            });

        }

        // Close button
        if (settings.closeButton) {
            $('<ul class="action-tabs right"><li><a href="#" title="Close window"><img src="images/icons/fugue/cross-circle.png" width="16" height="16"></a></li></ul>')
				.prependTo(win)
				.find('a').click(function (event) {
				    event.preventDefault();
				    $(this).closest('.modal-window').closeModal();
				});
        }

        // Bottom buttons
        var buttonsFooter = false;
        $.each(settings.buttons, function (key, value) {
            // Button zone
            if (!buttonsFooter) {
                buttonsFooter = $('<div class="block-footer align-' + settings.buttonsAlign + '"></div>').insertAfter(contentDiv);
            }
            else {
                // Spacing
                buttonsFooter.append('&nbsp;');
            }

            $('<button type="button">' + key + '</button>').appendTo(buttonsFooter).click(function (event) {
                value.call(this, $(this).closest('.modal-window'), event);
            });
        });

        // Close function
        if (settings.onClose) {
            win.bind('closeModal', settings.onClose);
        }

        // Apply template setup
        win.applyTemplateSetup();

        // Effect
        if (!root.is(':visible')) {
            win.hide();
            root.fadeIn('normal', function () {
                win.show().centerModal();
            });
        }
        else {
            win.centerModal();
        }

        // Store as current
        $.modal.current = win;
        $.modal.all = root.children('.modal-window');

        // Callback
        if (settings.onOpen) {
            settings.onOpen.call(win.get(0));
        }

        // If content url
        if (settings.url) {
            win.loadModalContent(settings.url, settings);
        }

        return win;
    };

    /**
    * Shortcut to the current window
    * @var jQuery|boolean
    */
    $.modal.current = false;

    /**
    * jQuery selection of all opened modal windows
    * @var jQuery
    */
    $.modal.all = $();

    /**
    * Wraps the selected elements content in a new modal window
    * @param object options same as $.modal()
    * @return jQuery the new windows
    */
    $.fn.modal = function (options) {
        var modals = $();

        this.each(function () {
            modals.add($.modal($.extend({}, $.modal.defaults, { content: $(this).clone(true).show() })));
        });

        return modals;
    };

    /**
    * Use this method to retrieve the content div in the modal window
    */
    $.fn.getModalContentBlock = function () {
        return this.find('.modal-content');
    }

    /**
    * Use this method to retrieve the modal window from any element within it
    */
    $.fn.getModalWindow = function () {
        return this.closest('.modal-window');
    }

    /**
    * Set window content
    * @param string|jQuery content the content to put: HTML or a jQuery object
    * @param boolean resize use true to resize window to fit content (height only)
    */
    $.fn.setModalContent = function (content, resize) {
        this.each(function () {
            var contentBlock = $(this).getModalContentBlock();

            // Set content
            if (typeof (content) == 'string') {
                contentBlock.html(content);
            }
            else {
                content.clone(true).show().appendTo(contentBlock);
            }
            contentBlock.applyTemplateSetup();

            // Resizing
            if (resize) {
                contentBlock.setModalContentSize(true, false);
            }
        });

        return this;
    }

    /**
    * Set window content-block size
    * @param int|boolean width the width to set, true to keep current or false for fluid width
    * @param int|boolean height the height to set, true to keep current or false for fluid height
    */
    $.fn.setModalContentSize = function (width, height) {
        this.each(function () {
            var contentBlock = $(this).getModalContentBlock();

            // Resizing
            if (width !== true) {
                contentBlock.css('width', width ? width + 'px' : '');
            }
            if (height !== true) {
                contentBlock.css('height', height ? height + 'px' : '');
            }
        });

        return this;
    }

    /**
    * Load AJAX content
    * @param string url the content url
    * @param object options an object with any of the following options:
    * 		- string loadingMessage any message to display while loading (may contain HTML), or leave empty to keep current content
    * 		- string|object data a map or string that is sent to the server with the request (same as jQuery.load())
    * 		- function complete a callback function that is executed when the request completes. (same as jQuery.load())
    * 		- boolean resize use true to resize window on loading message and when content is loaded. To define separately, use options below:
    * 		- boolean resizeOnMessage use true to resize window on loading message
    * 		- boolean resizeOnLoad use true to resize window when content is loaded
    */
    $.fn.loadModalContent = function (url, options) {
        var settings = $.extend({
            loadingMessage: '',
            data: {},
            complete: function (responseText, textStatus, XMLHttpRequest) { },
            resize: true,
            resizeOnMessage: false,
            resizeOnLoad: false
        }, options)

        this.each(function () {
            var win = $(this),
				contentBlock = win.getModalContentBlock();

            // If loading message
            if (settings.loadingMessage) {
                win.setModalContent('<div class="modal-loading">' + settings.loadingMessage + '</div>', (settings.resize || settings.resizeOnMessage));
            }

            contentBlock.load(url, settings.data, function (responseText, textStatus, XMLHttpRequest) {
                // Template functions
                contentBlock.applyTemplateSetup();

                if (settings.resize || settings.resizeOnLoad) {
                    contentBlock.setModalContentSize(true, false);
                }

                // Callback
                settings.complete.call(this, responseText, textStatus, XMLHttpRequest);
            });
        });

        return this;
    }

    /**
    * Set modal title
    * @param string newTitle the new title (may contain HTML), or an empty string to remove the title
    */
    $.fn.setModalTitle = function (newTitle) {
        this.each(function () {
            var win = $(this),
				title = $(this).find('h1'),
				contentBlock = win.hasClass('block-content') ? win : win.children('.block-content:first');

            if (newTitle.length > 0) {
                if (title.length == 0) {
                    contentBlock.removeClass('no-title');
                    title = $('<h1>' + newTitle + '</h1>').prependTo(contentBlock);
                }

                title.html(newTitle);
            }
            else if (title.length > 0) {
                title.remove();
                contentBlock.addClass('no-title');
            }
        });

        return this;
    }

    /**
    * Center the window
    * @param boolean animate true to animate the window movement
    */
    $.fn.centerModal = function (animate) {
        var root = getModalDiv(),
			rootW = root.width() / 2,
			rootH = root.height() / 2;

        this.each(function () {
            var win = $(this),
				winW = Math.round(win.outerWidth() / 2),
				winH = Math.round(win.outerHeight() / 2);

            win[animate ? 'animate' : 'css']({
                left: (rootW - winW) + 'px',
                top: (rootH - winH) + 'px'
            });
        });

        return this;
    };

    /**
    * Put modal on front
    */
    $.fn.putModalOnFront = function () {
        if ($.modal.all.length > 1) {
            var root = getModalDiv();
            this.each(function () {
                if ($(this).next('.modal-window').length > 0) {
                    $(this).detach().appendTo(root);
                }
            });
        }

        return this;
    };

    /**
    * Closes the window
    */
    $.fn.closeModal = function () {
        this.each(function () {
            var event = $.Event('closeModal'),
				win = $(this);

            // Events on close
            win.trigger(event);
            if (!event.isDefaultPrevented()) {
                win.remove();

                // Modal root element
                var root = getModalDiv();
                $.modal.all = root.children('.modal-window');
                if ($.modal.all.length == 0) {
                    $.modal.current = false;
                    root.fadeOut('normal');
                }
                else {
                    // Refresh current
                    $.modal.current = $.modal.all.last();
                }
            }
        });

        return this;
    };

    /**
    * New modal window options
    */
    $.modal.defaults = {
        /**
        * Content of the window: HTML or jQuery object
        * @var string|jQuery
        */
        content: false,

        /**
        * Url for loading content
        * @var string|boolean
        */
        url: false,

        /**
        * Title of the window, or false for none
        * @var string|boolean
        */
        title: false,

        /**
        * Add glass-like border to the window (required to enable resizing)
        * @var boolean
        */
        border: true,

        /**
        * Enable window moving (only work if title is defined)
        * @var boolean
        */
        draggable: true,

        /**
        * Enable window resizing (only work if border is true)
        * @var boolean
        */
        resizable: true,

        /**
        * If  true, enable content vertical scrollbar if content is higher than 'height' (or 'maxHeight' if 'height' is undefined)
        * @var boolean
        */
        scrolling: true,

        /**
        * Wether or not to display the close window button
        * @var boolean
        */
        closeButton: true,

        /**
        * Map of bottom buttons, with text as key and function on click as value
        * Ex: {'Close' : function(win) { win.closeModal(); } }
        * @var object
        */
        buttons: {},

        /**
        * Alignement of buttons ('left', 'center' or 'right')
        * @var string
        */
        buttonsAlign: 'right',

        /**
        * Function called when opening window
        * @var function
        */
        onOpen: false,

        /**
        * Function called when closing window. It may return false or call event.preventDefault() to prevent closing
        * @var function
        */
        onClose: false,

        /**
        * Minimum content height
        * @var int
        */
        minHeight: 40,

        /**
        * Minimum content width
        * @var int
        */
        minWidth: 800,

        /**
        * Maximum content width, or false for no limit
        * @var int|boolean
        */
        maxHeight: false,

        /**
        * Maximum content height, or false for no limit
        * @var int|boolean
        */
        maxWidth: false,

        /**
        * Initial content height, or false for fluid size
        * @var int|boolean
        */
        height: false,

        /**
        * Initial content width, or false for fluid size
        * @var int|boolean
        */
        width: 450,

        /**
        * If AJAX load only - loading message, or false for none (can be HTML)
        * @var string|boolean
        */
        loadingMessage: 'Loading...',

        /**
        * If AJAX load only - data a map or string that is sent to the server with the request (same as jQuery.load())
        * @var string|object
        */
        data: {},

        /**
        * If AJAX load only - a callback function that is executed when the request completes. (same as jQuery.load())
        * @var function
        */
        complete: function (responseText, textStatus, XMLHttpRequest) { },

        /**
        * If AJAX load only - true to resize window on loading message and when content is loaded. To define separately, use options below.
        * @var boolean
        */
        resize: true,

        /**
        * If AJAX load only - use true to resize window on loading message
        * @var boolean
        */
        resizeOnMessage: false,

        /**
        * If AJAX load only - use true to resize window when content is loaded
        * @var boolean
        */
        resizeOnLoad: false
    };

    /**
    * Return the modal windows root div
    */
    function getModalDiv() {
        var modal = $('#modal');
        if (modal.length == 0) {
            $(document.body).append('<div id="modal"></div>');
            modal = $('#modal').hide();
        }

        return modal;
    };

})(jQuery);
function setCookie(name, value, days) {
    if (days) {
        var date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); var expires = "; expires=" + date.toGMTString();
    }
    else
        var expires = ""; document.cookie = name + "=" + value + expires + "; path=/";
} function getCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); } return null; } function deleteCookie(name) { setCookie(name, "", -1); }



