var Artportalen = function () {
    return {
        // Some custom settings for different plugins
        Settings: {
            hoverIntent: {
                sensitivity: 2,
                interval: 100,
                timeout: 500
            },
            Modal: {
                opacity: "0.6"
            },
            // Settings for the map
            MapColors: {
                ParentSiteColor: "#ffcc00"
            },
            // Fixed navigations are not shown if the viewport is smaller than this value
            ViewportLimit: 600,
            // Tipsy defaults
            Tipsy : {
                opacity: 1,
                gravity: 'sw'
            }
        },
        // Check country via JavaScript
        // CountryIsoCode is added to the HTML-tag as an #ID from webconfig
        // Only numbers are not valid HTML so we have to add the small "c" as a prefix
        // ID c578 is Norway and c752 is Sweden - http://en.wikipedia.org/wiki/ISO_3166-1_numeric
        Country: {
            IsNorway: function () {
                return $("html").is("#c578");
            },
            IsSweden: function () {
                return $("html").is("#c752");
            }
        },
        // A CSS-class named "ap2UserLoggedIn" is added to the BODY-tag in Site.Master if IsCurrentUserAuthenticated
        IsUserLoggedIn : function() {
            return $(document.body).hasClass("ap2UserLoggedIn");
        },
        // Helper functions 
        Helpers: {
            // Replace URL to display images correctly during developement
            FixImageSrc: function () {
                $("#ViewSwitcher img[src*='\AP2\'], #viewimage img[src*='\AP2\']").each(function (index, value) {
                    var src = $(this).attr("src");
                    $(this).attr("src", src.replace("/AP2", "https://www.artportalen.se"));
                });
            },
            // A CSS-class named "ap2DebugMode" is added to the BODY-tag in Site.Master if the site is in debug mode
            IsDebugMode: function () {
                return $(document.body).hasClass("ap2DebugMode");
            },
            // Trying to set something in the localStorage while private browsing in Safari throws an error - use this to skip the QuotaExceededError
            LocalStorageEnabled: function () {
                var testKey = 'test', storage = window.localStorage;
                try {
                    storage.setItem(testKey, '1');
                    storage.removeItem(testKey);
                    return true;
                } catch (error) {
                    return false;
                }
            },
            // Check for placeholder support in input fields
            // http://stackoverflow.com/questions/3937818/how-to-test-if-the-browser-supports-the-native-placeholder-attribute
            PlaceholderSupport: function() {
                var i = document.createElement('input');
                return 'placeholder' in i;
            },
            // First letter Capitalized
            InitCap: function (str) {
                return (str.length) ? str.substring(0, 1).toUpperCase() + str.substring(1, str.length).toLowerCase() : "";
            },
            // Valid links in text will become clickable HTML links
            ReplaceURLWithHTMLLinks: function (text, nofollow) {
                var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
                return text.replace(exp, "<a href='$1' class='ap2-ui-link-external' target='_blank'" + (nofollow ? " rel='nofollow'" : "") + ">$1</a>");
            },
            // Check for touch devices
            IsTouchDevice: function () {
                return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0));
            },
            // Check for iPhone/iPad devices
            IsIphoneOrIpad : function() {
                return ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPad/i)));
            },
            // Formats thousand with separator; "1000000" becomes "1 000 000"
            FormatNumber: function (value, separator) {
                var buf = [];
                value = String(value).split('').reverse();
                for (var i = 0; i < value.length; i++) {
                    if (i % 3 === 0 && i !== 0) {
                        buf.push(separator || ' ');
                    }
                    buf.push(value[i]);
                }
                return buf.reverse().join('');
            },
            // Utility function to get unique from array, speed is (worst case) O(2n)
            arrayToUnique: function (array) {
                var o = {}, i, l = array.length, r = [];
                for (i = 0; i < l; i += 1) o[array[i]] = array[i];
                for (i in o) r.push(o[i]);
                return r;
            },
            // Parse all elements in the DOM and select the ones with a set z-index. Returns an array with elements sorted by its z-index. Use this utility when z-index problems occur...
            ZIndexes: function () {
                var elems = $("*").filter(function () {
                    return /^-?\d+$/.test($(this).css("z-index"));
                }).map(function () {
                    return { element: this, stack: parseFloat($(this).css("z-index")) };
                }).get().sort(function (a, b) {
                    return a.stack === b.stack ? 0 : (a.stack < b.stack ? -1 : 1);
                });
                console.log(elems);
            },
            // Custom date to ISO conversion wrapper. Browsers with no support of the toISOString method...
            DateToISOString: function (dateInput) {
                if (dateInput == null) return dateInput;
                var returnDate; dateInput = new Date(parseInt(dateInput.substr(6)));
                if (!Date.prototype.toISOString) {
                    function pad(n) { return n < 10 ? '0' + n : n; }
                    returnDate = dateInput.getUTCFullYear() + '-'
                        + pad(dateInput.getUTCMonth() + 1) + '-'
                        + pad(dateInput.getUTCDate()) + 'T'
                        + pad(dateInput.getUTCHours()) + ':'
                        + pad(dateInput.getUTCMinutes()) + ':'
                        + pad(dateInput.getUTCSeconds()) + 'Z';
                } else {
                    returnDate = dateInput.toISOString();
                }
                return returnDate.slice(0, 10);
            },
            // Convert a date to string 'yyyy-mm-dd'
            DateToLocalString: function (date) {
                var currDate = date.getDate();
                currDate = currDate + "";
                if (currDate.length == 1) { currDate = "0" + currDate; }
                var currMonth = date.getMonth();
                currMonth++;
                currMonth = currMonth + "";
                if (currMonth.length == 1) { currMonth = "0" + currMonth; }
                var currYear = date.getFullYear();
                return currYear + "-" + currMonth + "-" + currDate;;
            },
            // Convert json date to string, can't use DateToISOString because when we get one day off
            ParseJsonDateString: function (value) {
                var jsonDateRE = /^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/;
                var arr = value && jsonDateRE.exec(value);
                if (arr) {
                    return Artportalen.Helpers.DateToLocalString(new Date(parseInt(arr[1])));
                }
                return value;
            },
            ExtractSpeciesListData: function (renderAsString) {
                console.log("Artportalen.Helpers.ExtractSpeciesListData(bool renderAsString);");
                var listData = [];
                $("#specieslist tbody tr").each(function () {
                    var $row = $(this);
                    var link = $row.find("a").attr("href");
                    if (renderAsString) {
                        listData.push([
                            $row.find("[data-taxonid]").data("taxonid"),
                            $row.find(".speciesname strong").text(),
                            $row.find(".scientific").text(),
                            $row.find(".date").attr("title")
                        ].join(';'));
                    } else {
                        listData.push({
                            taxonId: $row.find("[data-taxonid]").data("taxonid"),
                            taxonName: $row.find(".speciesname strong").text(),
                            taxonNameScientific: $row.find(".scientific").text(),
                            date: $row.find(".date").attr("title")
                        });
                    }
                });
                if (renderAsString) {
                    listData.unshift("TaxonId;TaxonName;TaxonNameScientific;Date");
                    $("<div><textarea/></div>").css({ position: "fixed", "z-index": 9999, top: 0, left: 0 }).appendTo(document.body).find("textarea").css({ width: 500, height: 500 }).val(listData.join("\n")).select();
                }
                return listData;
            },
            // Craete links to external map sites - not tested yet...
            ExternalMapRoute: function (maptype, text, lon, lat) {
                var returnUrl = "";
                switch (maptype)
                {
                    case "bing":
                        returnUrl = 'http://www.bing.com/maps/?v=2&cp=' + lat + '~' + lon + '&lvl=18&dir=0&sty=0&sp=point.' + lat + '_' + lon + '_' + escape(text); break;
                    case "google":
                        returnUrl = 'https://www.google.com/maps/preview?q=' + lat + ',' + lon; break;
                    case "eniro":
                        returnUrl = 'http://www.hitta.se/LargeMap.aspx?z=4&mp=%3Cpts%3E%3Cpt%20i%3D%22http%3A//www.hitta.se/images/point.png%22%20y%3D%22' + lat + '%22%20x%3D%22' + lon + '%22%3E%3Ct%3E' + escape(text) + '%3C/t%3E%3C/pt%3E%3C/pts%3E'; break;
                    case "apple":
                        returnUrl = 'http://maps.apple.com/?q=' + lon + ',' + lat; break;
                }
                return returnUrl;
            }
        },
        setSubmittedSightingCount: function (elementSelector, counterValueElementSelector) {
            var sightingsSubmittedCountHtml = "";
            Artportalen.ajaxPost(Artportalen_ApplicationPath + '/ImportSighting/NumberOfSightingsImporting', null, function (data, code, xht) {
                if (data.Count != 0) {
                    sightingsSubmittedCountHtml = Artportalen.ResourceLabel("Shared_JavaScript_Navigation_Text_UnpublishedSightingsWImport").replace("{1}", data.Count);
                }
                Artportalen.ajaxPost(Artportalen_ApplicationPath + '/ReviewSighting/NumberOfSightingsSubmitted', null, function (data, code, xht) {
                    if (sightingsSubmittedCountHtml.length == 0) {
                        sightingsSubmittedCountHtml = Artportalen.ResourceLabel("Shared_JavaScript_Navigation_Text_UnpublishedSightings");
                    }

                    sightingsSubmittedCountHtml = sightingsSubmittedCountHtml.replace("{0}", data.Count);
                    $(elementSelector).html(sightingsSubmittedCountHtml);

                    // save the count value for further use
                    if ($(counterValueElementSelector).length) {
                        $(counterValueElementSelector).val(data.Count).trigger("setSightingCount");
                    }
                });
            });
        },
        getGo: function (url, params) {
            document.location = url + '?' + $.param(params);
        },
        postGo: function (url, params) {
            ///	<summary>
            ///     Call and go to a action method and posting json data
            ///	</summary>
            ///	<param name="url" type="String">
            ///		The url to the /controller/action
            ///	</param>
            ///	<param name="jsonData" type="JSON object">
            ///		JSON data to post / null.
            ///     Example { param1: value1, param2: value2 }
            ///     To send a ajax call without params put null in jsonData.
            ///	</param>
            var $form = $("<form>")
                .attr("method", "post")
                .attr("action", url);
            $.each(params, function (name, value) {
                $("<input type='hidden'>").attr("name", name).attr("value", value).appendTo($form);
            });
            $form.appendTo("body");
            $form.submit();
        },
        ajaxPost: function (url, jsonData, successCallback, options) {
            ///	<summary>
            ///     Call a action method posting json data and get json data back.
            ///     Use this to call an JSONResult action method.
            ///	</summary>
            ///	<param name="url" type="String">
            ///		The url to the /controller/action
            ///	</param>
            ///	<param name="jsonData" type="JSON object">
            ///		JSON data to post / null.
            ///     Example { param1: value1, param2: value2 }
            ///     To send a ajax call without params put null in jsonData.
            ///	</param>
            ///	<param name="successCallback" type="function">
            ///		Function to execute if the call succeded.
            ///     Example function(data, code, xht) { alert(data); }
            ///	</param>
            /// <param name="options">Options to add to the ajax call. Use special option autocompleteAbort = true if you need to abort the xhr request (e.g. to solve slow autocomplete async timing result problem)</param>
            var defaultSettings = {
                url: url,
                type: 'POST',
                async: true,
                //dataType: 'json', // TODO: problem with jQuery 1.5??
                data: JSON.stringify(jsonData),
                contentType: 'application/json; charset=UTF-8',
                success: function (data, code, xht) {
                    // The login page sets a custom Response Header so that we know here that the user has been loged out and we can redirect to the login page instead
                    if (xht.getResponseHeader('REQUIRES_AUTH') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Logon";
                        return false;
                    }
                    successCallback(data, code, xht);
                },
                complete: function (xht, status) {
                    if (xht.getResponseHeader('ROLE_NOT_AUTHORIZED') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Error/RoleNotAuthorized";
                        return false;
                    }
                    if (xht.getResponseHeader('SESSION_TIMEOUT') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Error/Timeout";
                        return false;
                    }
                    if (xht.getResponseHeader('SQL_TIMEOUT') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Error/SqlTimeout";
                        return false;
                    }
                },
                error: function (xht, errorType, exception) {
                    if (xht.statusText == 'abort' || (typeof options != "undefined" && options.autocompleteAbort)) {
                        // TODO: Log to console.
                    } else {
                        // Show exception instead of friendly message when in debug mode
                        if ($("body").is(".ap2DebugMode")) {
                            Artportalen.AjaxNotificationMessage(Artportalen.SharedResources.Shared_AjaxPostError + ", details; xht.statusText: " + xht.statusText + " errorType:" + errorType + ", exception: " + exception, true, "error");
                        } else {
                            Artportalen.AjaxNotificationMessage(Artportalen.SharedResources.Shared_AjaxErrorMessage, true, "error");
                        }
                    }
                }
            };

            if (jsonData != null) {
                defaultSettings.data = JSON.stringify(jsonData);
            }

            var ajaxSettings = $.extend(defaultSettings, options);

            return $.ajax(ajaxSettings);
        },
        ajaxGet: function (url, jsonData, successCallback, options) {
            ///	<summary>
            ///     Call a action method posting json data and get json data back.
            ///     Use this to call an JSONResult action method.
            ///	</summary>
            ///	<param name="url" type="String">
            ///		The url to the /controller/action
            ///	</param>
            ///	<param name="jsonData" type="JSON object">
            ///		JSON data to post / null.
            ///     Example { param1: value1, param2: value2 }
            ///     To send a ajax call without params put null in jsonData.
            ///	</param>
            ///	<param name="successCallback" type="function">
            ///		Function to execute if the call succeded.
            ///     Example function(data, code, xht) { alert(data); }
            ///	</param>
            var defaultSettings = {
                url: url,
                type: 'GET',
                async: true,
                //dataType: 'json', // TODO: problem with jQuery 1.5??
                data: JSON.stringify(jsonData),
                contentType: 'application/json; charset=UTF-8',
                success: function (data, code, xht) {
                    // The login page sets a custom Response Header so that we know here that the user has been loged out and we can redirect to the login page instead
                    if (xht.getResponseHeader('REQUIRES_AUTH') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Logon";
                        return false;
                    }
                    if (jQuery.isFunction(successCallback)) {
                        successCallback(data, code, xht);
                    }
                },
                complete: function (xht, status) {
                    if (xht.getResponseHeader('ROLE_NOT_AUTHORIZED') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Error/RoleNotAuthorized";
                        return false;
                    }
                    if (xht.getResponseHeader('SESSION_TIMEOUT') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Error/Timeout";
                        return false;
                    }
                    if (xht.getResponseHeader('SQL_TIMEOUT') === '1') {
                        location.href = Artportalen_ApplicationPath + "/Error/SqlTimeout";
                        return false;
                    }
                },
                error: function (xht, errorType, exception) {
                    Artportalen.AjaxNotificationMessage(Artportalen.SharedResources.Shared_AjaxPostError + ", details; xht.statusText: " + xht.statusText + " errorType:" + errorType + ", exception: " + exception, true, "error");
                }
            };

            if (jsonData != null) {
                defaultSettings.data = JSON.stringify(jsonData);
            }

            var ajaxSettings = $.extend(defaultSettings, options);

            $.ajax(ajaxSettings);
        },
        getGrid: function (gridId) {
            ///	<summary>
            ///     Wrapper function to get a telerik grid object from it's id.   
            ///	</summary>
            ///	<param name="gridId" type="String">
            ///		The element Id attribute for the grid.
            ///	</param>
            return $(gridId).data('tGrid');
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### NO COOKIES? - Display a message to the user if cookies are disabled. #nocookies-div exists in Site.Master
        DisplayNoCookiesMessage: function () {
            var cookieEnabled = (navigator.cookieEnabled) ? true : false;
            if (typeof navigator.cookieEnabled == "undefined" && !cookieEnabled) {
                document.cookie = "testcookie";
                cookieEnabled = (document.cookie.indexOf("testcookie") != -1) ? true : false;
            }
            if (!cookieEnabled) {
                $("#nocookies").show();
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### GET A JAVASCRIPT RESOURCE LABEL - Check that the resource label exist
        ResourceLabel: function (label) {
            ///	<summary>
            ///     Get a resource label by its key.   
            ///	</summary>
            ///	<param name="label" type="String">
            ///		The resource-key (ex "Shared_JavaScript_Yes")
            ///	</param>
            if (!Artportalen.SharedResources) {
                if (Artportalen.Helpers.IsDebugMode()) {
                    console.warn("Artportalen.ResourceLabel '" + label + "' was not found");
                }
                return "- Resource not found -";
            } else if (!Artportalen.SharedResources.hasOwnProperty(label)) {
                if (Artportalen.Helpers.IsDebugMode()) {
                    console.warn("Artportalen.ResourceLabel '" + label + "' was not found");
                }
                return "- Resource not found -";
            } else {
                return Artportalen.SharedResources[label];
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### FIX CONSOLE.LOG ISSUE - Console.log() is used by FireBug to log in JS but it is not defined in IE.
        // If console.log() is used IE will throw an error - the solution is to always define an own console-wrapper if doesn't exist already
        FixConsole: function () {
            var alertFallback = false;
            if (typeof console === "undefined") {
                console = {}; // define it if it doesn't exist already
            }
            if (typeof console.log === "undefined") {
                if (alertFallback) { console.log = function (msg) { alert(msg); }; }
                else { console.log = function () { }; }
            }
            if (typeof console.dir === "undefined") {
                if (alertFallback) {
                    // THIS COULD BE IMPROVED� maybe list all the object properties?
                    console.dir = function (obj) { alert("DIR: " + obj); };
                }
                else { console.dir = function () { }; }
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### IMAGE REPLACEMENT - Switch the <H1> text header to an image by changing the background-position and text-indent
        LogoImageReplace: function () {
            var $headerImage = $('#headerimage'); // Cache the object for performance
            function setRandomBackgroundImage() {
                if ($headerImage.length !== 0) {
                    var originalBackgroundImageUrl = $headerImage.css('background-image');
                    var imageName = originalBackgroundImageUrl.substring(originalBackgroundImageUrl.lastIndexOf('/') + 1).replace('")', '');
                    imageName = imageName.replace(')', ''); // To make it work in Chrome
                    var headerImagesArray = [
                        "fjallpipare_2.jpg|Fj\u00e4llpipare|Johan S\u00f6dercrantz",
                        "fjallpipare.jpg|Fj\u00e4llpipare|Johan S\u00f6dercrantz",
                        "dvargsparv.jpg|Dv\u00e4rgsparv|Johan S\u00f6dercrantz",
                        "sandodla.jpg|Sand\u00f6dla|Johan S\u00f6dercrantz",
                        "svart-rodstjart.jpg|Svart r\u00f6dstj\u00e4rt|Johan S\u00f6dercrantz",
                        "mandelblom.jpg|Mandelblom|Johan S\u00f6dercrantz",
                        "lappuggla_2.jpg|Lappuggla|Johan S\u00f6dercrantz",
                        "storre-korsnabb.jpg|St\u00f6rre korsn\u00e4bb|Johan S\u00f6dercrantz",
                        "lovsangare.jpg|L\u00f6vs\u00e5ngare|Johan S\u00f6dercrantz",
                        "strandpadda.jpg|Strandpadda|Johan S\u00f6dercrantz",
                        "mindre-tatelsmygare.jpg|Mindre t\u00e5telsmygare|Johan S\u00f6dercrantz",
                        "silverstreckad_02.jpg|Silverstreckad p\u00e4rlemorfj\u00e4ril|Johan S\u00f6dercrantz",
                        "ljungpipare.jpg|Ljungpipare|Johan S\u00f6dercrantz",
                        "kattunvisslare.jpg|Kattunvisslare|Johan S\u00f6dercrantz",
                        "midsommarblavinge.jpg|Midsommarbl\u00e5vinge|Johan S\u00f6dercrantz",
                        "asknatsfjaril.jpg|Askn\u00e4tsfj\u00e4ril|Johan S\u00f6dercrantz",
                        "midsommarblavinge_2.jpg|Midsommarbl\u00e5vinge|Johan S\u00f6dercrantz",
                        "svartflackig-glanssmygare.jpg|Svartfl\u00e4ckig glanssmygare|Johan S\u00f6dercrantz",
                        "gronsnabbvinge.jpg|Gr\u00f6nsnabbvinge|Johan S\u00f6dercrantz",
                        "fjallrav.jpg|Fj\u00e4llr\u00e4v|Johan S\u00f6dercrantz",
                        "hokuggla_2.jpg|H\u00f6kuggla|Johan S\u00f6dercrantz",
                        "parluggla.jpg|P\u00e4rluggla|Johan S\u00f6dercrantz",
                        "brun-blavinge.jpg|Brun bl\u00e5vinge|Maria Olsson",
                        "talltita.jpg|Talltita|Tomas Lundquist",
                        "fjallripa.jpg|Fj\u00e4llripa|Tomas Lundquist",
                        "storre-dagsvarmare.jpg|St\u00f6rre dagsv\u00e4rmare|Tomas Lundquist",
                        "gronsangare.jpg|Gr\u00f6ns\u00e5ngare|Tomas Lundquist",
                        "svarthakedopping.jpg|Svarthakedopping|Tomas Lundquist",
                        "grahakedopping.jpg|Gr\u00e5hakedopping|Tomas Lundquist",
                        "gulsparv.jpg|Gulsparv|Tomas Lundquist",
                        "knipa.jpg|Knipa|Tomas Lundquist",
                        "vitvingad-tarna.jpg|Vitvingad t\u00e4rna|Tomas Lundquist",
                        "prarielopare.jpg|Pr\u00e4riel\u00f6pare|Tomas Lundquist",
                        "varg2.jpg|Varg|Tomas Lundquist",
                        "storlom.jpg|Storlom|Tomas Lundquist",
                        "brunand.jpg|Brunand|Tomas Lundquist",
                        "arta.jpg|\u00C5rta|Tomas Lundquist",
                        "sadgas.jpg|S\u00e4dg\u00e5s|Tomas Lundquist",
                        "orre.jpg|Orre|Bj\u00f6rn Dellming",
                        "duvhok.jpg|Duvh\u00f6k|Bj\u00f6rn Dellming",
                        "midsommarblomster.jpg|Midsommarblomster|Bj\u00f6rn Dellming",
                        "vitkindad-gas.jpg|Vitkindad g\u00e5s|Bj\u00f6rn Dellming",
                        "storre-strandpipare.jpg|St\u00f6rre strandpipare|Bj\u00f6rn Dellming",
                        "stappvipa.jpg|St\u00e4ppvipa|Bj\u00f6rn Dellming",
                        "myrsnappa.jpg|Myrsn\u00e4ppa|Bj\u00f6rn Dellming",
                        "svarthakad-busk.jpg|Svarthakad busksv\u00e4tta|Bj\u00f6rn Dellming",
                        "gardsmyg.jpg|G\u00e4rdsmyg|Bj\u00f6rn Dellming",
                        "havsorn.jpg|Havs\u00f6rn|Bj\u00f6rn Dellming",
                        "rodrav.jpg|R\u00f6dr\u00e4v|Kaj Krutf\u00e4lt",
                        "strandskata.jpg|Strandskata|Kaj Krutf\u00e4lt",
                        "storspov.jpg|Storspov|Kaj Krutf\u00e4lt",
                        "tjader.jpg|Tj\u00e4der|Kaj Krutf\u00e4lt",
                        "fiskmas.jpg|Fiskm\u00e5s|Kaj Krutf\u00e4lt",
                        "angsparlemorfjaril.jpg|\u00C4ngsp\u00e4rlemorfj\u00f6ril|Henrik Larsson",
                        "ekoxe.jpg|Ekoxe|Henrik Larsson",
                        "sidensvans.jpg|Sidensvans|Berndt Krigsman",
                        "rodspov.jpg|R\u00f6dspov|David Lundgren",
                        "spovsnappa.jpg|Spovsn\u00e4cppa|Ronny Malm",
                        "skedand.jpg|Skedand|Ronny Malm",
                        "rod-glada.jpg|R\u00f6d glada|Ronny Malm",
                        "rostgumpsvala.jpg|Rostgumpsvala|Ronny Malm",
                        "skata.jpg|Skata|Ronny Malm",
                        "lundsangare.jpg|Lunds\u00e5ngare|Ronny Malm",
                        "svart-rodstjart2.jpg|Svart r\u00f6dstj\u00e4rt|Ronny Malm",
                        "eldsnabbvinge.jpg|Eldsnabbvinge|Kerstin Martinsson",
                        "sexflackig-bastardsvarmare.jpg|Sexfl\u00e4ckig bastardsv\u00e4rmare|Lina L\u00f6nnberg",
                        "turkos-blavinge.jpg|Turkos bl\u00e5vinge|Lina L\u00f6nnberg",
                        "brun-grasfjaril.jpg|Brun gr\u00e4sfj\u00e4ril|Lina L\u00f6nnberg",
                        "varg.jpg|Varg|Jens Morin",
                        "aeschna-affinis.jpg|Aeschna affinis|Jens Morin",
                        "lovgroda.jpg|L\u00f6vgroda|Jens Morin",
                        "kartfjaril.jpg|Kartfj\u00e4ril|Jens Morin",
                        "radjur.jpg|R\u00e5djur|Jens Morin",
                        "alggrasparlemorfjaril.jpg|\u00C4lggr\u00e4sp\u00e4rlemorfj\u00e4ril|Jens Morin",
                        "stinknava.jpg|Stinkn\u00e4va|Jens Morin",
                        "lenny_alg.jpg|\u00C4lg|Lenny Johansson",
                        "lenny_alg_2.jpg|\u00C4lg|Lenny Johansson",
                        "lenny_fjallabb.jpg|Fj\u00e4llabb|Lenny Johansson",
                        "lenny_rodhake.jpg|R\u00f6dhake|Lenny Johansson",
                        "lenny_rodspov.jpg|R\u00f6dspov|Lenny Johansson",
                        "lenny_padda.jpg|Vanlig padda|Lenny Johansson",
                        "lenny_rodbena.jpg|R\u00f6dbena|Lenny Johansson",
                        "lenny_blahake.jpg|Bl\u00e5hake|Lenny Johansson",
                        "lenny_skaggdopping.jpg|Sk\u00e4ggdopping|Lenny Johansson",
                        "angshok.jpg|\u00C4ngsh\u00f6k|Mikael Nord",
                        "praktejder.jpg|Praktejder|Mikael Nord",
                        "raka.jpg|R\u00e5ka|Mikael Nord",
                        "knubbsal.jpg|Knubbs\u00e4l|Mikael Nord",
                        "fasan.jpg|Fasan|Mikael Nord",
                        "jorduggla.jpg|Jorduggla|Mikael Nord",
                        "klykstjartad-stormsvala.jpg|Klykstj\u00e4rtad stormsvala|Mikael Nord",
                        "hoksangare.jpg|H\u00f6ks\u00e5ngare|Mikael Nord",
                        "gardsmyg2.jpg|G\u00e4rdsmyg|Mikael Nord",
                        "mindre-flugsnappare.jpg|Mindre flugsnappare|Mikael Nord",
                        "vattenrall.jpg|Vattenrall|Mikael Nord",
                        "fjallvrak.jpg|Fj\u00e4llvr\u00e5k|Mikael Nord",
                        "tarnmas.jpg|T\u00e4rnm\u00e5s|Mikael Nord",
                        "huggorm.jpg|Huggorm|J\u00f6rgen Lindberg",
                        "ekorre.jpg|Ekorre|J\u00f6rgen Lindberg",
                        "notkraka.jpg|N\u00f6tkr\u00e5ka|J\u00f6rgen Lindberg",
                        "smavessla.jpg|Sm\u00e5vessla|J\u00f6rgen Lindberg",
                        "alg.jpg|\u00C4lg|J\u00f6rgen Lindberg",
                        "smalom_2.jpg|Sm\u00e5lom|J\u00f6rgen Lindberg",
                        "gron-sandjagare.jpg|Gr\u00f6n sandj\u00e4gare|J\u00f6rgen Lindberg",
                        "gronbena.jpg|Gr\u00f6nbena|J\u00f6rgen Lindberg",
                        "svartstrupig-jarnsparv.jpg|Svartstrupig j\u00e4rnsparv|J\u00f6rgen Lindberg",
                        "sadesarla.jpg|S\u00e4des\u00e4rla|J\u00f6rgen Lindberg",
                        "luddvedel.jpg|Luddvedel|Margareta Edqvist",
                        "backklover.jpg|Backkl\u00f6ver|Margareta Edqvist",
                        "dansk-iris.jpg|Dansk iris|Margareta Edqvist",
                        "finnogontrost.jpg|Finn\u00f6gontr\u00f6st|Margareta Edqvist",
                        "klibbveronika.jpg|Klibbveronika|Margareta Edqvist",
                        "lunnefagel.jpg|Lunnef\u00e5gel|Knut-Sverre Horn",
                        "vitryggig-hackspett.jpg|Vitryggig hackspett|Rune Voie",
                        "lavskrika.jpg|Lavskrika|Frode Falkenberg",
                        "storlom2.jpg|Storlom|Frode Falkenberg",
                        "kungsfagel.jpg|Kungsf\u00e5gel|Morten Vang",
                        "stromstare_2.jpg|Str\u00f6mstare|Jon Helle",
                        "violett-fingersvamp.jpg|Violett fingersvamp|Arve Gr\u00e6sdal",
                        "halsvivel.jpg|Halsvivel|Morten Nilsen",
                        "smultronklover.jpg|Smultronkl\u00f6ver|Bj\u00f8rn Frostad"
                    ];
                    // If Norway
                    if (Artportalen.Country.IsNorway()) {
                        headerImagesArray = ["lunne-norge-fri.jpg|Lunde|Knut-Sverre Horn"];
                    }

                    var randomImageIndex = Math.round(Math.random() * (headerImagesArray.length - 1));
                    //randomImageIndex = headerImagesArray.length - 1;
                    var imageInfo = headerImagesArray[randomImageIndex].split("|");

                    var newBackgroundImageUrl = originalBackgroundImageUrl.replace(imageName, imageInfo[0]);
                    newBackgroundImageUrl = newBackgroundImageUrl.replace('Header', '');
                    newBackgroundImageUrl = newBackgroundImageUrl.replace('Images', 'Images/Header');
                    newBackgroundImageUrl = newBackgroundImageUrl.replace(/"/g, '');
                    $headerImage.css({
                        'background-image': newBackgroundImageUrl,
                        'background-position': '0px 0px',
                        'background-repeat': 'no-repeat'
                    });
                    $headerImage.find("#headerimage-photoby").html("&copy;&nbsp;" + imageInfo[2]);
                    $headerImage.find("#headerimage-speciesname").html(imageInfo[1]);
                }
            }
            setRandomBackgroundImage();
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### MAIN NAVIGATION  - Some jQuery-enhancement of the main navigation and the login window
        MainNavigation: function () {

            // The container id (used as context selector to increase performance)
            var $navigationContainer = $("#navigation");

            var isTouchDevice = Artportalen.Helpers.IsTouchDevice();
            var iPhoneOrIpad = Artportalen.Helpers.IsIphoneOrIpad();

            if (iPhoneOrIpad || isTouchDevice) {
                $navigationContainer.children("a.top").click(function (event) {
                    event.preventDefault();
                    $(this).closest("li").find(".navContent").toggle();
                });
                if (iPhoneOrIpad) {
                    $navigationContainer.addClass("ipad");
                }
                $("#usernavigation .username > .usernav-li-a").click(function (e) {
                    event.preventDefault();
                    $(this).closest("li").find(".usernav-sub").toggle();
                });
            } else {
                // Disable css-navigation and add JS-funktionality that delays the submenu
                $navigationContainer.removeClass("css-navigation-enabled").find("li.tab").hoverIntent({
                    over: function () {
                        $(this).find(".navContent").css({ "display": "block" });
                    },
                    out: function () {
                        $(this).find(".navContent").css({ "display": "none" }).closest("li.tab").children("a:eq(0)").removeClass("over");
                    },
                    sensitivity: Artportalen.Settings.hoverIntent.sensitivity, // number = sensitivity threshold (must be 1 or higher)
                    interval: Artportalen.Settings.hoverIntent.interval, // number = milliseconds for onMouseOver polling interval
                    timeout: Artportalen.Settings.hoverIntent.timeout // number = milliseconds delay before onMouseOut
                });

                // Creates a fixed menu at the top of the browsers viewport when scrolling down
                // If the browsers vieport is smaller than the limit value, the menu will not be fixed
                // TODO: maybe implement this with some type of debounce-function?
                (function () {
                    var placeholder = $("#navigationplaceholder");
                    var placeholderTop = placeholder.offset().top;
                    var breadcrumbs = $("#navigationwrapper");
                    var view = $(window);
                    $(window).scroll(function () {
                        var viewTop = view.scrollTop(),
                            viewHeight = view.height();
                        if ((viewTop > placeholderTop) && !breadcrumbs.is(".fixed") && (viewHeight > Artportalen.Settings.ViewportLimit)) {
                            placeholder.height(placeholder.height());
                            breadcrumbs.addClass("fixed");
                            placeholder.addClass("fixednav");
                        } else if (((viewTop <= placeholderTop) && breadcrumbs.is(".fixed")) || (viewHeight <= Artportalen.Settings.ViewportLimit)) {
                            placeholder.css("height", "auto");
                            breadcrumbs.removeClass("fixed");
                            placeholder.removeClass("fixednav");
                        }
                    });
                })();
            }

            // Temporary fix that disables not implemented links in the main-navigation, show a message when the user clicks on the link
            /*
            $navigationContainer.click(function (e) {
                if (e.target.nodeName == 'A') {
                    var target = $(e.target);
                    if (target.attr("href") == "#" || target.closest("li").is(".disabled")) {
                        e.preventDefault();
                        target.addClass("not-implemented").css({ "text-decoration": "line-through" });
                        Artportalen.AjaxNotificationMessage(Artportalen.ResourceLabel("Shared_JavaScript_PageNotImplemented"), true, "warning");
                        //Artportalen.AjaxNotificationMessage("Den h\u00e4r sidan \u00e4r \u00e4nnu inte klar!", true, "warning");
                    }
                }
            });
            */

            // Set the over-class on the anchor tag when the menu is displayed
            $navigationContainer.find(".navContent").hover(function () {
                $(this).closest("li.tab").children("a:eq(0)").addClass("over");
            }, function () {
                $(this).closest("li.tab").children("a:eq(0)").removeClass("over");
            });

            // Keyboard navigation
            $navigationContainer.find("a.top").focus(function (e) {
                $navigationContainer.find("li.tab").removeClass("hover over").find("a.top").not(this).removeClass("over");
                $(this).addClass("over").closest("li.tab").addClass("hover over");
            }).find("a").focus(function () {
                $(this).closest("li.tab").addClass("hover over");
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### Toggle a CSS-class that adds blur to the content - use with modals
        ToggleContentBlur: function (state) {
            $("#contentwrapper, #header").toggleClass("content-blur", state);
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### Create a modal alert-box
        AlertBox: function (settings) {
            ///	<summary>
            ///     Create an custom styled alert box.
            ///     Use this to notify the user.
            ///	</summary>
            ///	<param name="settings" type="Object">
            ///		The settings JSON-object.
            ///	    textMessage:  The message that will be shown in the alert box.
            ///	    buttonText: The text on the button (OK, Cancel etc.)
            ///	    buttonFunction: A function that will be executed when the users clicks the button.
            ///	    </param>
            ///	<returns type="bool" />

            // Create the alert div
            var $AlertBox = $("<div class='ap2-ui-modalmessage'></div>").append(["<h5 class='popupdiv_header'>", Artportalen.ResourceLabel("Shared_JavaScript_MessageFromArtportalen"), ":</h5>"].join(''));

            // Create the ok button and bind functions
            var $button = $("<a href='#' class='ap2-ui-actionbutton' />").text(settings.buttonText).bind("click.alertbox", function (event) {
                event.preventDefault();
                $AlertBox.bPopup().close();
                Artportalen.ToggleContentBlur(false);
                if (jQuery.isFunction(settings.buttonFunction)) { settings.buttonFunction.call(); }
            });

            var cssClass = (typeof(settings.cssClass) != "undefined") ? settings.cssClass : "alert";

            var $AlertMessage = $(['<div class="innertext ', cssClass,' clearfix">', settings.textMessage, '<br /></div>'].join('')).append($button);

            // Check if settings.modal exists, otherwise the default value is true
            var bModalOverlay = (typeof (settings.modal) != "undefined") ? settings.modal : true;

            // Create the alert box popup by using the jQ-plugin "bPopup"
            $AlertBox.append($AlertMessage).bPopup({
                fadeSpeed: 1,
                followSpeed: 1,
                modal: bModalOverlay,
                opacity: Artportalen.Settings.Modal.opacity,
                onClose: function () {
                    // Cleanup 
                    $AlertBox.remove();
                    Artportalen.ToggleContentBlur(false);
                    $(document).unbind("keydown.alertbox");
                }
            }, function () {
                Artportalen.ToggleContentBlur(true);
                $AlertBox.find("a:first").focus();
                $(document).bind("keydown.alertbox", function (e) {
                    var pageBlockEls = $AlertBox.find("a");
                    if (e.keyCode && e.keyCode == 9) {
                        pageBlockEls[0].focus();
                    }
                });
            });
            // END AlertBox
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### Create a modal confirm popup with yes/no alternatives
        ModalConfirm: function (settings) {

            // Create the modal div
            var $ModalConfirm = $("<div class='ap2-ui-modalmessage'></div>").append(["<h5 class='popupdiv_header'>", Artportalen.ResourceLabel("Shared_JavaScript_QuestionFromArtportalen"), ":</h5>"].join(''));

            // Create the yes/ok button and its function
            var $yesButton = $("<a href='#' class='ap2-ui-actionbutton' />").css({ "margin-right": "10px" }).html(settings.yesButtonText).bind("click.modalconfirm", function (event) {
                event.preventDefault();
                // If a function is passed into the ModalConfirm yes/ok button, execute it
                if (jQuery.isFunction(settings.yesButtonFunction)) { settings.yesButtonFunction.call(); }

                $ModalConfirm.hide(0, function () {
                    $ModalConfirm.bPopup().close();
                    Artportalen.ToggleContentBlur(false);
                });
                $(document).unbind("keydown.modalconfirm");
                return true;
            });

            // Create the no/cancel button and its function
            var $noButton = $("<a href='#' class='ap2-ui-actionbutton' />").text(settings.noButtonText).bind("click.modalconfirm", function (event) {
                event.preventDefault();
                $ModalConfirm.bPopup().close();
                Artportalen.ToggleContentBlur(false);
                // If a function is passed into the ModalConfirm no/cancel button, execute it
                if (jQuery.isFunction(settings.noButtonFunction)) { settings.noButtonFunction.call(); }
                return false;
            });

            // Create the content of the div, use append to keep the events of the yes-/no-/ok-/cancel-buttons
            var $ModalMessage = $(['<div class="innertext modal clearfix">', settings.questionText, '<br /></div>'].join('')).append($yesButton).append($noButton);

            // Append content to the modal div
            $ModalConfirm.append($ModalMessage);

            // Create the modal popup by using the jQ-plugin "bPopup"
            $ModalConfirm.bPopup({
                escClose: false,
                fadeSpeed: 1,
                followSpeed: 1,
                opacity: Artportalen.Settings.Modal.opacity,
                modalClose: false,
                onClose: function () {
                    // Cleanup 
                    $ModalConfirm.remove();
                    Artportalen.ToggleContentBlur(false);
                    $(document).unbind("keydown.modalconfirm");
                }
            }, function () {
                $("#bModal").css({ "cursor": "not-allowed" }); // Indicate that the UI is blocked
                Artportalen.ToggleContentBlur(true);
                if ($ModalConfirm.find(":input").length) {
                    $ModalConfirm.find(":input:first").focus(); // Set focus to the first input
                } else {
                    $ModalConfirm.find("a:first").focus(); // Set focus to the first button (yes-button)
                }
                // Prevent the focus to leave the buttons and inputs in the modal popup when tabbing
                $(document).bind("keydown.modalconfirm", function (e) {
                    var pageBlockEls = $ModalConfirm.find("a, :input");
                    if (e.keyCode && e.keyCode == 9) {
                        var els = pageBlockEls;
                        var fwd = !e.shiftKey && e.target === els[els.length - 1];
                        var back = e.shiftKey && e.target === els[0];
                        if (fwd || back) {
                            setTimeout(function () {
                                var e = pageBlockEls[back === true ? pageBlockEls.length - 1 : 0];
                                if (e)
                                    e.focus();
                            }, 10);
                            return false;
                        } else {
                            pageBlockEls.first().focus();
                        }
                    }
                });
            });
            // END ModalConfirm
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### Create a modal selector popup with multiple alternatives
        ModalSelector: function (settings) {

            // Create the modal div
            var $ModalSelector = $("<div class='ap2-ui-modalmessage'></div>").append(["<h5 class='popupdiv_header'>", Artportalen.ResourceLabel("Shared_JavaScript_QuestionFromArtportalen"), ":</h5>"].join(''));

            // Create the content of the div, use append to keep the events of the yes-/no-/ok-/cancel-buttons
            var $ModalMessage = $(['<div class="innertext modal clearfix">', settings.questionText, '<br /></div>'].join(''));

            settings.buttons.forEach(function (b) {
                var $newButton = $("<a href='#' class='ap2-ui-actionbutton' />").css({ "margin-right": "10px", "width": "175px" }).html(b.text).bind("click.modalconfirm", function (event) {
                    event.preventDefault();
                    // If a function is passed into the ModalConfirm yes/ok button, execute it
                    if (jQuery.isFunction(b.func)) { b.func.call(); }

                    $ModalSelector.hide(0, function () {
                        $ModalSelector.bPopup().close();
                        Artportalen.ToggleContentBlur(false);
                    });
                    $(document).unbind("keydown.modalconfirm");
                    return true;
                });
                $ModalMessage.append($newButton);
            });

            // Append content to the modal div
            $ModalSelector.append($ModalMessage);

            // Create the modal popup by using the jQ-plugin "bPopup"
            $ModalSelector.bPopup({
                escClose: false,
                fadeSpeed: 1,
                followSpeed: 1,
                opacity: Artportalen.Settings.Modal.opacity,
                modalClose: false,
                onClose: function () {
                    // Cleanup 
                    $ModalSelector.remove();
                    Artportalen.ToggleContentBlur(false);
                    $(document).unbind("keydown.modalconfirm");
                }
            }, function () {
                $("#bModal").css({ "cursor": "not-allowed" }); // Indicate that the UI is blocked
                Artportalen.ToggleContentBlur(true);
                //if ($ModalSelector.find(":input").length) {
                //    $ModalSelector.find(":input:first").focus(); // Set focus to the first input
                //} else {
                //    $ModalSelector.find("a:first").focus(); // Set focus to the first button (yes-button)
                //}
                // Prevent the focus to leave the buttons and inputs in the modal popup when tabbing
                $(document).bind("keydown.modalconfirm", function (e) {
                    var pageBlockEls = $ModalSelector.find("a, :input");
                    if (e.keyCode && e.keyCode == 9) {
                        var els = pageBlockEls;
                        var fwd = !e.shiftKey && e.target === els[els.length - 1];
                        var back = e.shiftKey && e.target === els[0];
                        if (fwd || back) {
                            setTimeout(function () {
                                var e = pageBlockEls[back === true ? pageBlockEls.length - 1 : 0];
                                if (e)
                                    e.focus();
                            }, 10);
                            return false;
                        } else {
                            pageBlockEls.first().focus();
                        }
                    }
                });
            });
            // END ModalConfirm
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### LOGIN WINDOW - jQuery-enhancement of the login window
        LoginWindow: function () {

            // Removed to secury ap2 with SSL 
            return;

            // Select the loginwrapper
            var _loginDiv = $("#loginWrapper");
            if (!_loginDiv.length) { return false; }

            // The link with the id 'loginlink' in the UserNavigationControl.aspx triggers the login window
            $("#loginlink").click(function (e) {
                e.preventDefault();

                // Return false if the loginDiv is null
                if (_loginDiv.length === 0) { return false; }

                // Position the loginwrapper to the right
                _loginDiv.css({ 'z-index': 7000, position: 'absolute', left: $(this).offset().left + $(this).outerWidth() - _loginDiv.outerWidth() + 6, top: 0 });

                // Adjust the position if the window is resized - use smartresize
                var adjustTimer;
                $(window).smartresize(function (event) {
                    clearTimeout(adjustTimer);
                    adjustTimer = setTimeout(function () {
                        _loginDiv.positionCenter();
                    }
                        , 80);
                });

                // Append the login-shader (lightbox effect)
                var $loginShader = $("#ap2-ui-loginshader");
                if ($loginShader.length === 0) {
                    $loginShader = $('<div id="ap2-ui-loginshader"></div>')
                        .css({
                            'background-color': '#28495A',
                            'display': 'none',
                            'position': 'absolute',
                            'top': '0',
                            'left': '0',
                            'min-width': '100%',
                            'min-height': '100%',
                            'z-index': '6999'
                        })
                        .appendTo(document.body)
                        .click(function () {
                            _closeAll('slow');
                        })
                        .fadeTo('slow', 0.6, function () { });
                }

                function _closeAll(speed) {
                    speed = ((speed == "") ? "slow" : speed);
                    _loginDiv.slideUp(speed);
                    $loginShader.fadeOut(speed, function () {
                        $(this).remove()
                    });
                }

                _loginDiv.find("#loginWrapper_close").click(function (e) {
                    e.preventDefault();
                    _closeAll('slow');
                });

                // Slide down and add a lightbox effect
                _loginDiv.positionCenter().slideDown('slow', function () {
                    _loginDiv.find("input:visible:first").focus(); // Set focus to the first input
                });

                // Fade the shade to visualize something happening when the form submits....
                _loginDiv.find("form").submit(function () {
                    _loginDiv.find("h3").find("span.logintitel").hide().end().find("span.logintitel-loading").show().end();
                    _loginDiv.find(".login-content").slideUp('fast');
                    _loginDiv.find("#loginWrapper_close").hide();
                    $loginShader.fadeTo('slow', 0.9, function () { });
                }).find("input:first").keypress(function (e) {
                    // Close the login window if the user hits ESC
                    if (e.keyCode == 27) {
                        _closeAll('fast');
                    }
                });
            });

            /*
            // The close button in the login window
            _loginDiv.find("a.closeTheDiv").click(function (e) {
            e.preventDefault();
            //$(this).hide(); // Hide the close-button before animation (prevent some bad animation looks)
            _loginDiv.slideUp('slow');
            $("#ap2-ui-loginshader").fadeOut('slow', function () { $(this).remove() });
            });
            */

            // Submit the form with the loginbutton-link
            _loginDiv.find("a.loginbutton").click(function (e) {
                e.preventDefault();
                $(this).closest("form").submit();
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### SMALL TABS (NAVIGATION) - Create a tabbed view of any content
        SmallTabs: function (elements) {
            $(elements).each(function () {
                $(this).siblings(".ap2-ui-smalltabs-contentbox:eq(0)").show(); // Show the first content box
                $(this).siblings(".ap2-ui-smalltabs-contentbox:gt(0)").hide(); // Hide all content boxes except the first
                $(this).find("li").removeClass("active").end().find("li:eq(0)").addClass("active");
            });
            $(elements).click(function (event) {
                event.preventDefault();
                var _contentBoxes = $(this).siblings(".ap2-ui-smalltabs-contentbox");
                if (!$(event.target).is("ul")) {
                    _contentBoxes.hide();
                    $(this).find("li").removeClass("active");
                    var _theLI = $(event.target).closest("li").addClass("active");
                    var activeTab = _theLI.find("a").attr("href");
                    $(activeTab).show();
                }
            });
        },
        SmallTabsActive: function (activeElement) {
            $(activeElement).show().siblings(".ap2-ui-smalltabs-contentbox").hide().end() // Show active content-box and hide all other
                .prev(".ap2-ui-smalltabs").find("li").removeClass("active").end() // Remove active-class from all LI:s in the tab menu
                .find("a[href*=" + activeElement + "]").closest("li").addClass("active"); // Add active-class to the active tab
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### CONTENTBOX TABS (NAVIGATION) - Create a tabbed view of any content
        // The tab navigation is an UL-list with an unique id, eg "site-tabs"
        // The content that is being tabbed has to be wrapped with "-content" as id suffix, eg "site-tabs-content"
        ContentBoxTabs: function (elements) {
            $(elements).each(function () {
                //$("#" + $(this).attr("id") + "-content").find(".contentbox-tabmenu-content").filter(":first").show().end().filter(":not(:first)").hide();
                $(this).find("li:not(.disabled)").removeClass("active").filter("li:eq(0)").addClass("active");
            });
            //$(elements).click(function (event) {
            $(elements).bind('click', function (event, obj) {
                event.preventDefault();
                var disableFocus = (obj && obj.disableFocus) ? true : false; // disable focus to the first visible input within tab
                var _isInputDisabled = $(this).hasClass("input-disabled"); // inputs will be disabled
                if (!$(event.target).is("ul") && !$(event.target).is(".disabled")) {
                    var _contentBoxes = $("#" + $(this).attr("id") + "-content").find(".contentbox-tabmenu-content");
                    if (_isInputDisabled) { _contentBoxes.find(":input").attr("disabled", "disabled"); }
                    _contentBoxes.hide();
                    $(this).find("li").removeClass("active");
                    var _theLI = $(event.target).closest("li").addClass("active");
                    var activeTab = _theLI.find("a").attr("href");
                    if (activeTab != "#") {
                        if (_isInputDisabled) {
                            $(activeTab).show().find(":input").removeAttr("disabled");
                        } else {
                            $(activeTab).show();
                        }
                    }

                    var triggerTabSelected = function () {
                        return $(_theLI.find("a")).triggerHandler('onTabSelected');
                    };

                    // Wait for the event onTabSelected to be finished before setting focus to the first input (to prevent wrong position of the formfield instruction tooltip)
                    $.when(triggerTabSelected()).done(function () {
                        // Focus is default set to the first visible input 
                        // If you want to trigger a click without autofocus to the first visible input, use $(element).trigger("click", [{ "disableFocus": true }]);
                        if (!disableFocus) {
                            $(activeTab).show().find(":input:visible:first").focus();
                        }
                    });

                } else {
                    return false;
                }
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### EQUALHEIGHTS - Make a group of elements equal height (as the tallest)
        EqualHeights: function (elementGroup) {
            var maxHeight = 0;
            elementGroup.each(function () {
                var thisHeight = $(this).height();
                if (thisHeight > maxHeight) {
                    maxHeight = thisHeight;
                }
            });
            elementGroup.height(maxHeight);
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### CUSTOM SELECT BOX
        // Create a customized selectbox/button with submenu that uses the $("#selectviewlist") or clones the top menu
        // TODO - maybe fix so it is possible to have multiple selectboxes on one page, now it is bound to one ID in the DOM
        CustomSelectBox: function () {

            $("#selectview").tipsy({
                gravity: 'se',
                opacity: 1
            }).click(function (event) {
                event.preventDefault();
                var $self = $(this);
                var $viewModeMenu = $("#selectviewlist");
                if (!$viewModeMenu.length) {
                    $viewModeMenu = $("#viewSightingMode").clone();
                    $viewModeMenu.find("a").not("#dotMap").attr("title", function () {
                        return $(this).find("small").text();
                    }).tipsy({
                        gravity: 'e',
                        offset: -5,       // pixel offset of tooltip from element
                        opacity: 1
                    });
                    $viewModeMenu.find("#dotMap").attr("title", function () {
                        return $(this).find("small").text();
                    }).tipsy({
                        gravity: 'w',
                        offset: -5,       // pixel offset of tooltip from element
                        opacity: 1
                    });

                    $viewModeMenu
                        .attr("id", "selectviewlist")
                        .insertAfter($self)
                        .slideToggle();
                } else {
                    $viewModeMenu.slideToggle();
                }
                $self.find("span.icon").toggleClass("icon-arrowdown").toggleClass("icon-arrowup");
            });

        },

        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### Toggle a loading message inside a grid
        ToggleGridLoadingMessage: function (element, display) {
            if (!display) {
                $('#ap2-ui-gridloading-message').hide().remove();
                $(element).find(".ap2-ui-gridloading").hide(0, function () {
                    $(this).remove();
                });
            } else if (!$(element).find(".ap2-ui-gridloading").length) {
                var $gridLoadingMessage = $("<div />")
                    .addClass("ap2-ui-gridloading")
                    .html("<div class='ap2-ui-gridloading-image'></div>");
                $gridLoadingMessage.appendTo($(element).css({ "position": "relative" }));
                if (!$('#ap2-ui-gridloading-message').length) $("<div id='ap2-ui-gridloading-message'>" + Artportalen.ResourceLabel('Shared_JavaScript_Loading') + "</div>").appendTo(document.body);
            }
        },
        ToggleGridLoadingMessageError: function (element) {
            $('#ap2-ui-gridloading-message').remove();
            $(element).find(".ap2-ui-gridloading").addClass("ap2-ui-gridloading-error").find(".ap2-ui-gridloading-image").remove().end().html("<img src='" + Artportalen_ApplicationPath + "/Content/Images/Icons/ap2-ui-modal-alert.png'/ class='ap2-ui-gridloading-image'>");
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### Toggle a loading message 
        // Element: a selector for the parent div id
        // Message: the text to display
        // Display: boolean for show/hide
        ToggleLoadingMessage: function (element, message, display) {

            // Set the selector and check that it is implemented correct
            var $element = $(element);
            if (!$element.length) {
                alert("EXCEPTION: Please check the selector passed into the function Artportalen.ToggleLoadingMessage");
            }

            if (display) {
                $element
                    .find("div.ap2-ui-loadingblocker-message span")
                    .text(message)
                    .end()
                    .find("div.ap2-ui-loadingblocker-shade, div.ap2-ui-loadingblocker-message")
                    .show()
                    .filter(".ap2-ui-loadingblocker-shade")
                    .css({ opacity: 0.7 })
                    .end()
                    .filter("div.ap2-ui-loadingblocker-message").css({
                        'position': 'fixed',
                        'top': '40%',
                        'left': '50%',
                        'margin-left': '-' + ($element.find("div.ap2-ui-loadingblocker-message").outerWidth() / 2) + 'px'
                    });
                //console.log(Math.max($element.find("div.ap2-ui-loadingblocker-shade").offset().top, $(window).scrollTop()));
            } else {
                $element.find("div.ap2-ui-loadingblocker-shade, div.ap2-ui-loadingblocker-message").hide();
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### Remove coobserver
        RemoveCoObserver: function (element) {
            var $element = $(element);
            $element.addClass("activatecoobservertoggle")
                .find("span.iscurrentuser")
                .click(function (event) {
                    var $currentSpan = $(this);
                    Artportalen.ModalConfirm({
                        questionText: Artportalen.ResourceLabel("Shared_JavaScript_RemoveCurrentUserAsObserver"),
                        yesButtonText: Artportalen.ResourceLabel("Shared_JavaScript_Yes"),
                        yesButtonFunction: function () {
                            Artportalen.ajaxPost(Artportalen_ApplicationPath + '/ViewSighting/ToggleObserver/' + $currentSpan.attr("data-sightingid"), { id: $currentSpan.attr("data-sightingid") }, function (data) {
                                $currentSpan.removeClass("iscurrentuser").addClass("iscurrentuser_removed");
                                Artportalen.AjaxNotificationMessage(data.Message, true, data.MessageType);
                            });
                        },
                        noButtonText: Artportalen.ResourceLabel("Shared_JavaScript_No"),
                        noButtonFunction: null
                    });
                });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### CAPS LOCK DETECTION - Show a tooltip next to the input that indicates if caps lock is activated. Uses a capslock plugin.
        CapsLockDetection: function (element) {
            ///	<summary>
            ///     Displays a warning next to the input if CAPS LOCK is detected.
            ///	</summary>
            ///	<param name="element" type="String">
            ///		The input selector.
            ///	</param>

            var $element = $(element);
            if (!$element.length) return false;

            // Show a tooltip with the warning message
            var $CapsLockWarningTooltip = $('<div id="ap2-ui-form-capslock-tooltip"></div>').hide().appendTo(document.body);

            var options = {
                caps_lock_on: function () {
                    $CapsLockWarningTooltip.html(Artportalen.ResourceLabel("Shared_JavaScript_CapsLockWarning")).show().css({
                        left: $element.parent().offset().left,
                        top: $element.parent().offset().top + $element.parent().outerHeight() - 1,
                        "z-index": 9999
                    });
                },
                caps_lock_off: function () {
                    $CapsLockWarningTooltip.hide();
                },
                caps_lock_undetermined: function () {
                    $CapsLockWarningTooltip.hide();
                }
            };

            $element.capslock(options).blur(function () {
                $CapsLockWarningTooltip.hide();
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### FIELD INSTRUCTIONS TOOLTIP - Show field instruction as a tooltip.
        // This function run once on every page load and binds a tooltip to inputs.
        // The tooltip shows next to the input when the user sets focus on an input field.
        // The tooltip hides if the user leave the input, writes in the input or mouseover the tooltip

        // Function that destroys existing field instructions and rebinds all inputs with new field instructions
        RebindFormFieldInstructions: function (elementWrapper) {

            // select all inputs within a form or pass in an elementWrapper to only select inputs within that context
            var _inputSelector = $(elementWrapper);
            if (!_inputSelector.length) _inputSelector = $("form");

            // Find all inputs except buttons and hidden inputs 
            var _selectedInputs = _inputSelector.find("input, textarea, select").filter(":not(:submit, :reset, :button, input[type='hidden'], input[type='file'])");
            if (_selectedInputs.length === 0) { return false; }

            // Unbind all events within the current namespaces
            _selectedInputs.unbind(".formFieldInstruction").unbind(".charlimit");

            // Rebind field instructions
            Artportalen.FormFieldInstructions($(elementWrapper));
        },

        // Function that inits and creates field instructions
        FormFieldInstructions: function (elementWrapper) {

            var attachedToBody = {
                "fieldinstruction": false,
                "charlimit" : false
            };

            // select all inputs within a form or pass in an elementWrapper to only select inputs within that context (used when rebinding the field instructions)
            var _inputSelector = $(elementWrapper);
            if (!_inputSelector.length) _inputSelector = $("form");

            // Bind the tooltip function to all inputs except buttons and hidden inputs 
            var _selectedInputs = _inputSelector.find("input, textarea, select").filter(":not(:submit, :reset, :button, input[type='hidden'], input[type='file'])");
            if (_selectedInputs.length === 0) { return false; }

            // Select the tooltip container or add it to document.body if doesnt exists
            var $FFI_ToolTip = $("#ap2-ui-form-fieldinstruction-tooltip");

            // Show a tooltip with the maximum characters
            var $FFI_CharLimit = $("#ap2-ui-form-charlimit-tooltip");

            // Function that displays a warning message when the maximum characters are used
            _selectedInputs.filter("[class|='maxlength']").bind("keyup.charlimit", function () {
                var _me = $(this);
                var maxlimit = _me.attr("class");
                maxlimit = parseInt(maxlimit.replace("maxlength-", ""));
                var warningCount = (maxlimit < 50) ? 5 : 20; // Warning is display when this value is left
                var actualLength = _me.val().length;
                if (actualLength >= (maxlimit - warningCount)) {

                    // Truncate text that exceed the maximum character limit
                    if (actualLength >= maxlimit) {
                        _me.val(_me.val().substring(0, maxlimit));
                        actualLength = maxlimit;
                    }

                    var warningMessage = Artportalen.ResourceLabel("Shared_JavaScript_CharLimitWarning");
                    warningMessage = warningMessage.replace("{0}", maxlimit - actualLength);
                    warningMessage = warningMessage.replace("{1}", maxlimit);

                    if ($FFI_CharLimit.length === 0) {
                        $FFI_CharLimit = $('<div id="ap2-ui-form-charlimit-tooltip"></div>').hide().appendTo(document.body);
                    }

                    $FFI_CharLimit.html(warningMessage).show().css({
                        left: (_me.parent().offset().left + _me.parent().outerWidth()) - $FFI_CharLimit.outerWidth(),
                        top: _me.parent().offset().top + _me.parent().outerHeight() - 1,
                        "z-index": 9999
                    });
                    _me.closest("div").addClass("ap2-ui-form-warning");
                } else {
                    _me.closest("div").removeClass("ap2-ui-form-warning");
                    $FFI_CharLimit.hide();
                }
            });


            // Adjust the tooltip position if the window is resized - uses a timer to prevent bad performance
            var adjustTimer;
            $(window).smartresize(function (event) {
                clearTimeout(adjustTimer);
                if ($FFI_ToolTip.is(":visible")) {
                    adjustTimer = setTimeout(function () {
                        if ($FFI_ToolTip.data("tooltiptarget") !== undefined && $FFI_ToolTip.data("tooltipdirection") !== undefined) {
                            FFI_SetToolTipPosition($FFI_ToolTip, $FFI_ToolTip.data("tooltiptarget").offset(), $FFI_ToolTip.data("tooltipdirection"));
                        }
                    }
                        , 100);
                }
            });

            /* Calculate tooltip position */
            function FFI_SetToolTipPosition(FFI_ToolTip, targetInputPosition, direction, hasError) {
                var FFI_posX; var FFI_posY; var FFI_arrowClass;
                switch (direction.toLowerCase()) {
                    case 'down':
                        FFI_posX = targetInputPosition.left - 6;
                        FFI_posY = targetInputPosition.top - FFI_ToolTip.outerHeight() - 8;
                        FFI_arrowClass = "arrow-down";
                        break;
                    case 'right':
                        FFI_posX = targetInputPosition.left - FFI_ToolTip.outerWidth() - 6;
                        FFI_posY = targetInputPosition.top - 2;
                        FFI_arrowClass = "arrow-right";
                        break;
                    case 'up':
                        FFI_posX = elemPos.left - 6;
                        FFI_posY = elemPos.top + _me.outerHeight() + 10;
                        FFI_arrowClass = "arrow-up";
                        break;
                    default:
                        FFI_posX = targetInputPosition.left - FFI_ToolTip.outerWidth() - 10;
                        FFI_posY = targetInputPosition.top - 4;
                        FFI_arrowClass = "arrow-right";
                        break;
                }
                if (hasError) FFI_posX = FFI_posX + 4; // adjust the position
                FFI_ToolTip.data('tooltipdirection', direction); // Store the direction of the arrow and the tooltip position to be able to recalculate the position if the window is resized
                if (FFI_ToolTip.is(":visible")) {
                    FFI_ToolTip.stop().animate({ left: FFI_posX, top: FFI_posY }, 200, 'swing'); // set the tooltip position next to the input-element (animate if the tooltip is visible)
                } else {
                    FFI_ToolTip.css({ left: FFI_posX, top: FFI_posY }); // set the tooltip position next to the input-element
                }
                FFI_ToolTip.find("div").removeClass().addClass("pointer " + FFI_arrowClass); // set the arrow class
            }

            /* Implement the tooltip functionality */
            function FFI_ToggleToolTip(element, visibility, event) {

                var _me = $(element); /* cache selected element */

                if (!visibility) {

                    $FFI_ToolTip.hide();
                    if (event.type !== "change") { $FFI_CharLimit.hide(); } /* If it is a blur/focusout event - hide the CharLimit message */
                    _me.closest("div").removeClass("ap2-ui-form-active ap2-ui-form-warning").parent().parent().find(["label[for='", _me.attr("id"), "']"].join('')).removeClass("ap2-ui-form-active ap2-ui-form-warning"); // Remove the active class on input and label

                } else {

                    if ($FFI_ToolTip.length === 0) {
                        $FFI_ToolTip = $('<div id="ap2-ui-form-fieldinstruction-tooltip" class="ap2-ui-form-fieldinstruction-tooltip"></div>')
                            .append('<span></span><div class="pointer"><div class="inner-pointer"></div></div>') // Creates an arrow with css, pointing towards the input field
                            .hide()
                            .appendTo(document.body)
                            .mouseover(function () {
                                $(this).slideUp(100); // hide the tooltip if the user moves the mouse (otherwise it will collide with autocompletes etc.)
                            });
                    }

                    var targetInputPosition = (_me.is(":checkbox, :radio")) ? _me.offset() : _me.closest("div").offset(); /* get the position of the input element */
                    var fieldInstruction = _me.closest("div").find('.ap2-ui-form-fieldinstruction').text(); /* select the fieldinstruction text */
                    var bHasError = false; /* track if the input has errors */
                    var fieldValidationError = ""; /* Error message */
                    var tooltiptarget = (_me.is(":checkbox, :radio")) ? _me : _me.closest("div");
                    $FFI_ToolTip.data('tooltiptarget', tooltiptarget); /* Store the target input for the tooltip */
                    $FFI_ToolTip.css({ "max-width": "250px" }); /* Set default width of the tooltip */

                    /* If form fields instructions is disabled by the user. */
                    if ($("body").is(".ap2DisableFormFieldInstructions")) {
                        fieldInstruction = "";
                    }

                    /* make textareas autogrow. */
                    Artportalen.AutoGrowingTextarea(_me);

                    /* if an error message exists - add it to the fieldinstruction text */
                    if (_me.closest("div").find('label.ap2-ui-form-error').length != 0 && _me.closest("div").hasClass("ap2-ui-form-error")) {
                        bHasError = true;
                        fieldValidationError = _me.closest("div").find('label.ap2-ui-form-error').text();
                        fieldInstruction = ["<span class='ap2-ui-form-fieldinstruction-error'>", fieldValidationError, "</span>", fieldInstruction].join('');
                    }
                    /* editable error message */
                    var _unValidatedText = _me.closest("div").siblings("#for-" + _me.attr("id"));
                    if (_unValidatedText.length && _unValidatedText.text() != "") {
                        fieldInstruction = ["<span class='ap2-ui-form-fieldinstruction-error'>", _unValidatedText.text(), "</span>", fieldInstruction].join('');
                    }

                    /* calculate position and show the fieldinstruction tooltip */
                    /* check if there is a message and that the message is not a localized string that begins with "[[" */
                    if (fieldInstruction != "" && fieldInstruction.substring(0, 2) != "[[") {
                        $FFI_ToolTip.find('span').html(fieldInstruction); // set the fieldinstruction text in the tooltip
                        if (_me.is('select')) {
                            FFI_SetToolTipPosition($FFI_ToolTip, targetInputPosition, 'down', bHasError);
                        } else if (_me.length) {
                            $FFI_ToolTip.css({ "max-width": "150px" });
                            var leftPossible = ((targetInputPosition.left - $(window).scrollLeft() - (parseInt($FFI_ToolTip.outerWidth()) + 10)) > 0);
                            var direction = (leftPossible) ? 'right' : 'down'; // show tooltip left or above the input? check if it is enough space to the left
                            FFI_SetToolTipPosition($FFI_ToolTip, targetInputPosition, direction, bHasError);
                        } else {
                            FFI_SetToolTipPosition($FFI_ToolTip, targetInputPosition, 'up', bHasError);
                        }
                        $FFI_ToolTip.show();
                    }

                    /* add an active-class to the label of the input */
                    _me.filter(":not(:checkbox, :radio)").closest("div").addClass("ap2-ui-form-active").parent().parent().find(["label[for='", _me.attr("id"), "']:not(.ap2-ui-form-error)"].join('')).addClass("ap2-ui-form-active"); // Set active class on input and label
                }
            }

            /* Bind focus/blur events to all the inputs - wich event type depends on the element type */
            _selectedInputs.bind("focus.formFieldInstruction", function (event) {
                FFI_ToggleToolTip($(this), true, event);
            }).bind("blur.formFieldInstruction focusout.formFieldInstruction keydown.formFieldInstruction keyup.formFieldInstruction", function (event) {
                if (event.type === "keydown") {
                    $FFI_ToolTip.hide(); /* Just hide the tooltip but keep the active state on the input */
                } else {
                    if (event.type !== "keyup") {
                        FFI_ToggleToolTip($(this), false, event);
                    }
                    Artportalen.ContainsUnvalidCharacters($(this)); /* Validate input to detect unvanted characters */
                }
            });

        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // RegExp that mimics .NET:s System.Web.CrossSiteScriptingValidation
        ContainsUnvalidCharacters: function (_me) {
            var pattern = new RegExp(/^(?!(.|\n)*<[a-z!\/?])(?!(.|\n)*&#)(.|\n)*$/i); // Pattern from http://stackoverflow.com/questions/4948500/client-validation-that-acts-the-same-as-net-page-validator-xss-prevention/4949339#4949339
            var originalString = _me.val();
            // Display a tooltip warning
            var $unvalidated = $("#ap2-ui-form-unvalidated-tooltip");
            if (!pattern.test(originalString)) {
                _me.closest("div").addClass("ap2-ui-form-unvalidated");
                if (!$unvalidated.length) {
                    $unvalidated = $('<div id="ap2-ui-form-unvalidated-tooltip"></div>').hide().appendTo(document.body);
                }
                $unvalidated.html(Artportalen.ResourceLabel("Shared_JavaScript_UnvalidCharacters")).show().css({
                    left: _me.parent().offset().left,
                    top: _me.parent().offset().top + _me.parent().outerHeight() - 1,
                    "z-index": 9999,
                    "max-width": Math.min(_me.parent().outerWidth(), 200)
                });
                return true;
            } else {
                _me.closest("div").removeClass("ap2-ui-form-unvalidated");
                $unvalidated.hide();
                return false;
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### AUTO GROWING TEXTAREA
        AutoGrowingTextarea: function ($element) {
            /* if it is a textarea - make it autogrow. Check if it has already been bind to the TextAreaExpander function */
            if ($element.is("textarea") && $element.data('TextAreaExpander') === undefined) {
                $element.TextAreaExpander($element.outerHeight(), 400);
                $element.data('TextAreaExpander', true);
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### GET AJAX NOTIFICATIONS - Check for notifications from the messaging system
        // Limited to check for notifications X times with a delay
        AjaxNotificationCallCounter: 0,
        GetAjaxNotifications: function () {
            // Wait for 8 sec before the first AJAX-call to check for new nofitications is fired
            window.setTimeout(Artportalen.ExecuteAjaxNotifications, 8000);
            // If nofitications exists, they gets stored in local storage and displayed directly before the AJAX-call
            if ($("html").has(".localstorage")
                && Artportalen.Helpers.LocalStorageEnabled()
                && localStorage.getItem($("#usernavigation[data-useralias]").data("useralias") + "-AjaxNotificationCounters") !== null) {
                var data = JSON.parse(localStorage.getItem($("#usernavigation[data-useralias]").data("useralias") + "-AjaxNotificationCounters"));
                if (data.hasOwnProperty("numberOfNewMessages") && data.hasOwnProperty("numberOfUnreadNotifications")) {
                    Artportalen.DisplayAjaxNotifications(data.numberOfNewMessages, data.numberOfUnreadNotifications, false);
                }
            }
        },
        ExecuteAjaxNotifications: function () {
            // Cycling AJAX-call to check for notifications five times with a delay of XXXX seconds
            Artportalen.ajaxGet(Artportalen_ApplicationPath + '/Notification/NewNotifications', null, function (data, code, xht) {
                // DIRTY FIX: If the users get logged out the responseText will be the login page, check for that here 
                if (xht.responseText.indexOf("<!DOCTYPE") != -1) {
                    location.href = Artportalen_ApplicationPath + "/Logon";
                    return false;
                }
                // If the JSON object is empty, the user is probably logged out. Set the counter to 10 and the AJAX will stop
                else if (!data.hasOwnProperty("stringNotifications")) {
                    Artportalen.AjaxNotificationCallCounter = 10;
                }
                else {
                    if (data.stringNotifications.length) {
                        Artportalen.AjaxNotificationMessage(data.stringNotifications[data.stringNotifications.length - 1], true, "info");
                    }
                    if (data.hasOwnProperty("numberOfNewMessages") && data.hasOwnProperty("numberOfUnreadNotifications")) {
                        Artportalen.DisplayAjaxNotifications(data.numberOfNewMessages, data.numberOfUnreadNotifications, true);
                    }
                }
                Artportalen.AjaxNotificationCallCounter++;
                if (Artportalen.AjaxNotificationCallCounter < 6) {
                    window.setTimeout(Artportalen.ExecuteAjaxNotifications, 180000);
                }
            });
        },
        DisplayAjaxNotifications: function (numberOfNewMessages, numberOfUnreadNotifications, updateLocalStorage) {

            // Create a small object that can be stringified as JSON and stored in the local storage
            var messageCount = {
                numberOfNewMessages: numberOfNewMessages,
                numberOfUnreadNotifications: numberOfUnreadNotifications,
                sumOfMessages: numberOfNewMessages + numberOfUnreadNotifications
            };

            // Update local storage to display the new message notification before the AJAX-check has runned
            if (updateLocalStorage && $("html").has(".localstorage") && Artportalen.Helpers.LocalStorageEnabled()) {
                localStorage.setItem($("#usernavigation[data-useralias]").data("useralias") + "-AjaxNotificationCounters", JSON.stringify(messageCount));
            }

            // Display messages in the GUI
            $("#mypagestab").find("span.icon.user").html(messageCount.sumOfMessages > 0 ? "<span class='ap2-ui-unread-notification-popup'>" + messageCount.sumOfMessages + "</span>" : "");
            $("#mymessages .nrofnotifications").html(messageCount.numberOfNewMessages > 0 ? "(" + messageCount.numberOfNewMessages + ")" : "");
            $("#mynotifications .nrofnotifications").html(messageCount.numberOfUnreadNotifications > 0 ? "(" + messageCount.numberOfUnreadNotifications + ")" : "");
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### AJAX MESSAGES - Create an ajax status message
        AjaxNotificationMessage: function (inputMessage, setVisbility, errorType, settings) {
            ///	<summary>
            ///     Create an ajax status message.
            ///     Use this to notify the user what the result of a user action was.
            ///	</summary>
            ///	<param name="inputMessage" type="String">
            ///		The message to display to the user.
            ///	</param>
            ///	<param name="setVisibility" type="bool">
            ///		Hides inputMessage if setVisbility = false.
            ///	</param>
            ///	<param name="errorType" type="string">
            ///		Valid strings are "success", "info", "error", "warning", "loading".
            ///	</param>
            ///	<param name="settings" type="object">
            ///		A settings object that overrides the default settings { offsetBottom : 60, delayTime : 3000 }
            ///	</param>
            ///	<returns type="bool" />

            var defaults = {
                delayTime: 11000,
                offsetBottom: 10
            }

            var options = $.extend({}, defaults, settings);

            // the message element
            var MessageDivID = "#ap2-ui-ajaxNotificationWrapper";

            // existing message element will be destroyed directly or faded out and destroy based on the value in "setVisbility" 
            if ($(MessageDivID).length) {
                if (setVisbility) {
                    $(MessageDivID).remove();
                } else {
                    var originalColor = $(MessageDivID).css('backgroundColor');
                    $(MessageDivID).clearQueue().stop().animate({ "backgroundColor": "yellow" }, 50).animate({ "backgroundColor": originalColor }, 50).fadeOut('fast', function () {
                        $(this).remove();
                    });
                    return false;
                }
            }

            // If there is no message, stop this.
            if (inputMessage == "") { return false; }

            // create the message element
            var _MessageDiv = $(['<div id="', MessageDivID.replace("#", ""), '"><span class="ap2-ui-form-taxonpicker-settings-scope-close"></span><span class="UI-Icon-16"></span><span class="messagetext"></span><div style="font-size:10px;font-weight:normal;text-align:center;color:#333;">', Artportalen.ResourceLabel("Shared_JavaScript_MessageFromArtportalen"), '</div></div>'].join('')).hide().appendTo(document.body);

            // Set the message to the child-SPAN-tag
            _MessageDiv.find(".messagetext").html(inputMessage);

            // Set the icon class
            var iconClass = "UI-Icon-16 ";
            switch (errorType) {
                case "success": iconClass += " UI-Icon-16-OK"; break;
                case "info": iconClass += " UI-Icon-16-Info"; break;
                case "error": iconClass += " UI-Icon-16-Error"; break;
                case "warning": iconClass += " UI-Icon-16-Warning"; break;
                case "loading": iconClass += " UI-Icon-16-Loading"; break;
                default: iconClass += " UI-Icon-16-OK"; break;
            }
            _MessageDiv.find("span.UI-Icon-16").attr("class", iconClass);

            // set css-class and opacity (prevent a quirk that makes the message semitransparent when calling the stop() function when the fadeOut() animation is running)
            _MessageDiv.addClass("ap2-ui-ajaxNotificationWrapper_" + errorType.toLowerCase()).clearQueue().stop().css({ "opacity": "1", "max-width": "250px" });

            // set message text - wait for X ms and then fade out and hide
            var originalColor = _MessageDiv.css('backgroundColor');
            var $contentWrapper = $("#contentwrapper");
            _MessageDiv.css({
                left: $contentWrapper.offset().left + $contentWrapper.outerWidth() - $(_MessageDiv).outerWidth(),
                width: $(_MessageDiv).outerWidth() - 20 /* 20 px is calculated based on; 2 X 3px border + 2 X 7px padding */
                , bottom: -200
            })
                .show()
                .animate({
                    bottom: "+=" + (200 + options.offsetBottom) + "px"
                }, 700)
                .animate({ "backgroundColor": "yellow" }, 100)
                .animate({ "backgroundColor": originalColor }, 100)
                .delay(options.delayTime)
                .animate({ "backgroundColor": "yellow" }, 100)
                .animate({ "backgroundColor": originalColor }, 100)
                .fadeOut('slow', function () {
                    $(this).remove();
                });

            // Fade out and disappear if the user clicks on the message
            _MessageDiv.click(function () {
                $(this).stop().fadeOut('fast', function () {
                    $(this).remove();
                });
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### CLEAR FORM - Function that clear all form elements
        ClearAllFormElements: function (formObject) {
            $(formObject).find(':input').each(function () {
                switch (this.type) {
                    case 'password':
                    case 'select-multiple':
                    case 'select-one':
                    case 'text':
                    case 'textarea':
                        $(this).val('');
                        break;
                    case 'checkbox':
                    case 'radio':
                        this.checked = false;
                }
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### DATA PROVIDER INJECTION for sighting list and detailed list. TODO: Refactor to component
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        InjectSightingDataProvider: function($selector) {
            var sightings = $($selector).find("[data-sightingid]").map(function() {
                return $(this).attr("data-sightingid");
            }).get();

            var uniqueSighitngs = sightings.getUnique();

            Artportalen.ajaxPost(Artportalen_ApplicationPath + "/SightingDataProvider/ProvidersBySightingIds",
                {
                    SightingIds: uniqueSighitngs
                },
                function(data, code, xht) {
                    function getDataProviderBySightingId(idn) {
                        var sightingDataProviderMap = data.SightingDataProviderMaps.toMap("SightingId", "DataProviderId");
                        var dataProviderMapObj = data.SightingDataProviders.toMap("Id", "this");
                        return dataProviderMapObj[sightingDataProviderMap[idn + ""] + ""];
                    }

                    $.each($($selector).find(".SightingDataProvider"), function(index, dataProviderElement) {
                        var dataProvider = getDataProviderBySightingId($(dataProviderElement).attr("data-sightingid"));
                        if (dataProvider) {
                            var htmlSightingDataProviderLink = ["<a class='has_tooltip' href='", dataProvider.Webaddress, "' data-organisationdetails='", JSON.stringify(dataProvider), "' target='_blank'>", dataProvider.Name, "</a>"].join('');
                            $(dataProviderElement).append(htmlSightingDataProviderLink);
                            $(dataProviderElement).closest("td").find(".DataProviderVia").remove();
                            $(dataProviderElement).find("a").popupInfoTooltip({ mouseInDelay: 500 });
                        }
                    });
                }
            );
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### GRID VISUALIZATION - Shows grid on selected elements when hitting key code F11
        ShowGrid: function () {
            var selectedElements = $("#content, #footer, #headerimage, .box");
            $(document).keyup(function (event) {
                var key = event.keyCode || event.charCode || 0;
                if (key == 122) { // Key Code F11
                    if ($(selectedElements).hasClass("showgridnumbers")) {
                        $(selectedElements).removeClass("showgridnumbers");
                        Artportalen.AjaxNotificationMessage("", false, "");
                    } else {
                        $(selectedElements).addClass("showgridnumbers");
                        Artportalen.AjaxNotificationMessage("Du tryckte F11...", true, "loading");
                    }

                }
            });
        },
        CountEvents: function (selector) {
            var counter = 0;
            var $elm = (selector != "") ? $(selector) : $("*");
            $elm.filter(function () {
                // Return only items that have the
                // events data item.
                console.log($(this).data("events"));
                return ($(this).data("events"));
            }).css("background-color", "gold")
                .each(function () {
                    $(this).addClass("evt" + counter++);
                });
            alert(counter);
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### TIME AGO - Resource labels for the time ago jQuery plugin
        TimeAgo: function (elements) {
            jQuery.timeago.settings.strings = {
                prefixAgo: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_prefixAgo"),
                prefixFromNow: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_prefixFromNow"),
                suffixAgo: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_suffixAgo"),
                suffixFromNow: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_suffixFromNow"),
                seconds: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_suffixFromNow"),
                minute: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_minute"),
                minutes: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_minutes"),
                hour: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_hour"),
                hours: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_hours"),
                day: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_day"),
                days: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_days"),
                month: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_month"),
                months: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_months"),
                year: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_year"),
                years: Artportalen.ResourceLabel("Shared_JavaScript_TimeAgo_years")
            };
            // Call the time ago plugin
            jQuery(elements).timeago();
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### POPUP MAP - Display a map with a selected site. Depending on the MapPopupControl.ascx
        PopupMap: function (mapId, siteId) {

            var $mapPopupDiv = $("#mapPopupDiv");

            // Get information about the site
            function getSiteInfo(siteId) {
                Artportalen.ToggleGridLoadingMessage($mapPopupDiv.find(".mapwrapper"), true); // Show an ajax loader
                Artportalen.ajaxPost(Artportalen_ApplicationPath + '/Site/GetSite', { SiteId: siteId }, function (data, code, xht) {
                    //zoomToSiteByIdOrCoordinate($mapPopupDiv.data("mapreference"), siteId, data.SiteXCoord, data.SiteYCoord, 8); // TODO: The last "8" is the speciesgroup id, should be read out of data.speciesgroupid instead
                    zoomToSiteByCoordinate($mapPopupDiv.data("mapreference"), data.SiteXCoord, data.SiteYCoord, 13);
                    GetSpecificGeoJSONSites($mapPopupDiv.data("mapreference"), [siteId]);
                    Artportalen.ToggleGridLoadingMessage($mapPopupDiv.find(".mapwrapper"), false); // Hide the ajax loader
                });
            }

            // First time the map needs to be initiated
            if (!$mapPopupDiv.hasClass("initiated")) {
                $mapPopupDiv.show(0, function () {
                    $mapPopupDiv.addClass("initiated").positionCenter().draggable({
                        handle: ".popupDiv",
                        cancel: ".mapwrapper",
                        stop: function (event, ui) {
                            var map = $mapPopupDiv.data("mapreference");
                            map.updateSize(); // Update the map after dragging it
                        }
                    }).css({ "cursor": "move" }); // Position the map popup and make it draggable
                    $mapPopupDiv.data("mapreference", window.initMap(mapId)); // Init the map and store the reference
                    getSiteInfo(siteId); // Get the site info and display it on the map

                    var adjustTimer; // Adjust the popup div position if the window is resized - uses a timer to prevent bad performance
                    $(window).smartresize(function (event) {
                        clearTimeout(adjustTimer);
                        adjustTimer = setTimeout(function () {
                            $mapPopupDiv.positionCenter({ animateposition: true });
                        }, 100);
                    });
                }).find(".ap2ContextMenu-closebutton").click(function () {
                    $mapPopupDiv.hide();
                });
            } else {
                // The map is already initiated - show it and then fetch the site info
                $mapPopupDiv.show(0, function () {
                    // Check if its visible inside the viewport
                    if (!$mapPopupDiv.visible( false )) {
                        $mapPopupDiv.positionCenter();
                    }
                    getSiteInfo(siteId);
                });
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### GET TAXON DETAIL - Fetch information from Dyntaxa about
        // TODO: Make a generic function that sets the URL from web.config or something?
        GetTaxonDetails: function (element, taxonId) {
            var url = "https://lampetra2-1.artdata.slu.se/DynTaxa/Taxon/Detail/" + taxonId;
            var $taxonFrame = $("<iframe id='taxoninfo' />");
            $taxonFrame.attr("src", url);
            $taxonFrame.appendTo(document.body);
        },
        GetTaxonInfoFromDynTaxa: function (taxonId) {
            $.ajax({
                url: 'https://www.dyntaxa.se/taxon/detail/' + taxonId + '?jsonp=parseResponse',
                type: 'GET',
                crossDomain: true,
                dataType: 'jsonp',
                success: function (data) {
                    alert("done!");
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    console.log('HTTP Error: ' + errorThrown + ' | Error Message: ' + textStatus);
                }
            });
        },
        // Get all images for a sighting and trigger an "AP2ImagesForSightingFetched" event with the produced HTML
        GetImagesForSighting: function (sightingId) {
            Artportalen.ajaxPost(Artportalen_ApplicationPath + '/Media/ImagesForSightingId/' + sightingId, { sightingid: sightingId }, function (data, code, xht) {
                var imageHtml = "";
                if (data.length) {
                    imageHtml += "<ul class='clearfix popup-thumbnails'>";
                    for (var i = 0; i < data.length; i++) {
                        imageHtml += "<li><a href='" + data[i].fileurl + "' target='_blank' data-imageid='" + data[i].id + "'>";
                        imageHtml += "<img src='" + data[i].thumbnail + "' width='100' height='80'></a></li>";
                    }
                    imageHtml += "</ul>";
                } else {
                    imageHtml = "<h3>Bilder saknas</h3>";
                }
                $(document).trigger("AP2ImagesForSightingFetched", [imageHtml]);
            });
        },
        History: {
            Set: function (url) {
                if ("localStorage" in window && window["localStorage"] != null) {
                    localStorage.setItem("ap2history", url);
                } else {
                    return false;
                }
            },
            Get: function () {
                if ("localStorage" in window && window["localStorage"] != null) {
                    if (localStorage.getItem("ap2history") != null) {
                        window.location.replace(localStorage.getItem("ap2history"));
                    } else {
                        return false;
                    }
                }
            }
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### GALLERY SLIDESHOW
        // Create a slideshow based on the list of thumbnails from the gallery page
        GallerySlideShow: function (imageList) {
            ///	<summary>
            ///     Create a slideshow based on the list of thumbnails from the gallery page
            ///	</summary>
            ///	<param name="element" type="String">
            ///		The list of thumbnails wrapped in anchor tags
            ///	</param>

            // Set the current image and set the indexes for next/prev-navigation 
            var currentImage = imageList.eq(0);
            var thumbIndex = { prev: imageList.length - 1, next: 1 };

            // Calculate indexes for navigation
            function setThumbIndex() {
                thumbIndex = {
                    next: (imageList.index(currentImage) + 1) == imageList.length ? 0 : imageList.index(currentImage) + 1,
                    prev: (imageList.index(currentImage) - 1) == -1 ? imageList.length - 1 : imageList.index(currentImage) - 1
                };
            }

            // Fetch the full size image JSON by its id
            Artportalen.ajaxPost(Artportalen_ApplicationPath + '/Media/GetFullSizeImage/' + currentImage.attr("data-imageid"), { imageId: currentImage.attr("data-imageid") }, function (data, code, xht) {
                if (data) {

                    // Add a modal background
                    var $modal = $("<div class='floatingimage-modal'/>").appendTo(document.body).animate({ 'opacity': '0.7' }, 500).click(function () {
                        killGallerySlideShow();
                    });

                    // Set the title "Image: X of X"
                    function setTitle(index, total) {
                        return Artportalen.ResourceLabel("Shared_Gallery_SlideShow_Title").replace("{0}", index).replace("{1}", total);
                    }

                    var imageSize = {
                        width: data.width,
                        height: data.height,
                        maxSize : $(window).height(),
                        maxHeightOrWidth: function() {
                            return Math.max(imageSize.width, imageSize.height);
                        },
                        scaleRatio : function() {
                            return imageSize.maxHeightOrWidth() / imageSize.maxSize;
                        },
                        scaleDown : function() {
                            return imageSize.maxHeightOrWidth() > imageSize.maxSize;
                        },
                        isLandscapeOrientation : function() {
                            return imageSize.width > imageSize.height;
                        },
                        scaledHeight : function() {
                            return (imageSize.scaleDown()) ? ((imageSize.isLandscapeOrientation()) ? Math.round(imageSize.height / imageSize.scaleRatio(), 0) : imageSize.maxSize) : imageSize.height;
                        },
                        scaledWidth: function () {
                            return (imageSize.scaleDown()) ? ((!imageSize.isLandscapeOrientation()) ? Math.round(imageSize.width / imageSize.scaleRatio(), 0) : imageSize.maxSize) : imageSize.width;
                        }
                    }

                    var imageContent =
                        // Add the image and the link to the image view
                        "<div id='imageplaceholder' style='margin-top: 15px; display:" + (data.isimage ? "block" : "none") + ";'>" +
                        "<a href='" + currentImage.attr("href") + "' class='clearfix' style='width:" + imageSize.scaledWidth() + "px'>" +
                        "<img class='image fullsize' src='" + data.fileurl + "' style='height:" + imageSize.scaledHeight() + "px" + ";width:" + imageSize.scaledWidth() + "px" + "'>" +
                        "<span class='ap2-ui-unread-notification-popup' style='display:" + (data.nrofcomments ? "block" : "none") + ";'>" + data.nrofcomments + "</span>" +
                        // Image information
                        "<span class='imagetext clearfix'>" +
                        "<span class='photoby'>" + data.photoby + "</span>" +
                        "<span class='taxonname'>" + data.taxonname + "</span>" +
                        "<span class='sightinginfo'>" + data.sightingdetails + "</span>" +
                        "<p class='last'>" + (data.comment ? data.comment : "") + "</p>" +
                        "</span>" +
                        "</a>" +
                        "</div>" +

                        // Add the embedded media placeholder
                        "<div id='mediaplaceholder' style='margin-top: 15px; display:" + (data.isimage ? "none" : "block") + ";'>" +
                        (data.isimage ? "" : data.fileurl) +
                        "<span class='ap2-ui-unread-notification-popup' style='display:" + (data.nrofcomments ? "block" : "none") + ";'>" + data.nrofcomments + "</span>" +
                        // Media information
                        "<span class='imagetext clearfix'>" +
                        "<span class='photoby'>" + data.photoby + "</span>" +
                        "<span class='taxonname'>" + data.taxonname + "</span>" +
                        "<span class='sightinginfo'>" + data.sightingdetails + "</span>" +
                        "<p class='last'>" + (data.comment ? data.comment : "") + "</p>" +
                        "</span>" +
                        "</div>";


                    // Create the floating div that will contain the image and all other details
                    var $imageDiv = $("<div class='floatingimage froozen'/>").css({ width: "auto" }).append([
                        imageContent,
                        // Add the toolbar
                        "<div class='toolbar'>",
                        "<span class='ui-icon ui-icon-image'></span>",
                        "<span class='title'>", setTitle(imageList.index(currentImage) + 1, imageList.length), "</b></span>",
                        "</div>",
                        // Close button and navigation
                        "<div class='closebutton'></div>",
                        "<div class='pager prevNav'></div><div class='pager nextNav'></div>",
                        "<div class='pagerthumbnail prevImage'></div><div class='pagerthumbnail nextImage'></div>"
                    ].join(''));

                    // Set the thumbnails for previous and next image
                    setThumbIndex();
                    $imageDiv.find(".prevImage").html(imageList.eq(thumbIndex.prev).find(".thumbnail-frame").html());
                    $imageDiv.find(".nextImage").html(imageList.eq(thumbIndex.next).find(".thumbnail-frame").html());

                    // Interaction for close button 
                    $imageDiv.find(".closebutton").click(function (e) {

                        killGallerySlideShow();

                        // Navigate next/previous image
                    }).end().find(".pagerthumbnail, .pager").click(function () {

                        /// Calculate indexes for the next/prev image and set the current image based on navigation - going forward or backwards?
                        setThumbIndex();
                        currentImage = imageList.eq($(this).is(".nextNav, .nextImage") ? thumbIndex.next : thumbIndex.prev);

                        // Fetch selected image
                        Artportalen.ajaxPost(Artportalen_ApplicationPath + '/Media/GetFullSizeImage/' + currentImage.attr("data-imageid"), { imageId: currentImage.attr("data-imageid") }, function (data, code, xht) {
                            if (data) {
                                imageSize.width = data.width;
                                imageSize.height = data.height;
                                // Update the div with image, thumbnails for previous and next image, etc
                                setThumbIndex();
                                $imageDiv.find(".prevImage").html(imageList.eq(thumbIndex.prev).find(".thumbnail-frame").html());
                                $imageDiv.find(".nextImage").html(imageList.eq(thumbIndex.next).find(".thumbnail-frame").html());
                                $imageDiv.find(".ap2-ui-unread-notification-popup").toggle(data.nrofcomments != 0).html(data.nrofcomments);
                                if (data.isimage) {
                                    $imageDiv.find("#imageplaceholder").show();
                                    $imageDiv.find("#mediaplaceholder").hide();
                                    $imageDiv.find("a").attr("href", currentImage.attr("href")).css("width", imageSize.scaledWidth());
                                    $imageDiv.find("img.image").attr("src", data.fileurl).css("width", imageSize.scaledWidth()).css("height", imageSize.scaledHeight());
                                } else {
                                    $imageDiv.find("#imageplaceholder").hide();
                                    $imageDiv.find("#mediaplaceholder").show();
                                    if ($imageDiv.find("iframe").length > 0) {
                                        $imageDiv.find("iframe").replaceWith(data.fileurl);
                                    } else {
                                        $imageDiv.find("#mediaplaceholder").prepend(data.fileurl);
                                    }
                                }
                                $imageDiv.find(".title").html(setTitle(imageList.index(currentImage) + 1, imageList.length));
                                $imageDiv.find(".taxonname").html(data.taxonname).end().find(".photoby").html(data.photoby);
                                $imageDiv.find(".sightinginfo").html(data.sightingdetails);
                                $imageDiv.find("p.last").html(data.comment);
                                $imageDiv.positionCenter().find("a").focus();
                            }
                        });
                    });

                    // Add the div to body and fade it in
                    $imageDiv.hide().appendTo(document.body).fadeIn("fast", function () {
                        Artportalen.ToggleContentBlur(true);
                    }).positionCenter();

                    // Listen for escape key to remove the slideshow and left/right keys to cycle through images
                    $(document).bind("keyup.escapefullsize", function (e) {
                        switch (e.which) {
                            case 27:
                                killGallerySlideShow(); break;
                            case 37: // left
                                $imageDiv.find(".prevImage").click(); break;
                            case 39: // right
                                $imageDiv.find(".nextImage").click(); break;
                        }
                    });

                    // Kill all divs and clean up event and timer
                    function killGallerySlideShow() {
                        Artportalen.ToggleContentBlur(false);
                        $modal.fadeOut(500, function () {
                            $(this).remove();
                        });
                        $imageDiv.fadeOut(500, function () {
                            $imageDiv.remove();
                            $(document).unbind("keyup.escapefullsize");
                            adjustTimer = null;
                        });
                    }

                    // Adjust the position on screen while resizing the browser window - use a timer to debounce 
                    var adjustTimer;
                    $(window).smartresize(function (event) {
                        clearTimeout(adjustTimer);
                        adjustTimer = setTimeout(function () {
                            $imageDiv.positionCenter({ animateposition: true });
                        }, 100);
                    });

                }
            });
        },
        OpenImageInFloatingDiv: function (imageId) {
            Artportalen.ajaxPost(Artportalen_ApplicationPath + '/Media/GetFullSizeImage/' + imageId, { imageId: imageId }, function (data, code, xht) {
                if (data) {
                    /* TODO - Use this for debugging on localhost
                    data.fileurl = "https://www.artportalen.se" + data.fileurl.replace("AP2", "");
                    data.thumbnail = "https://www.artportalen.se" + data.thumbnail.replace("AP2", "");
                    */

                    if ($("#image-" + data.id).length) return false; // Check if an open image with the current id already exists
                    var $imageDiv = $("<div />").addClass("floatingimage").attr("id", "image-" + data.id); // Create the floating div that will contain the image
                    var $image = $("<img class='image fullsize' />").attr("src", data.fileurl).attr("width", data.width).attr("height", data.height); // Create the image HTML
                    $imageDiv.append("<div class='toolbar'></div><div class='closebutton'></div>").append($image); // Append the toolbar div, the close button and the image to the floating div
                    $imageDiv.find(".closebutton").click(function (e) { // When the user clicks the closebutton, remove the floating div
                        $(this).closest(".floatingimage").remove();
                    });
                    $imageDiv.append("<div class='toggle'><span class='UI-Icon-16 UI-Icon-16-Toggle-Minus'></span></div>"); // Append the toggle button
                    $imageDiv.find(".toggle span").click(function (e) { // Toggles between thumbnail and full sized image
                        var image = $(this).toggleClass("UI-Icon-16-Toggle-Minus UI-Icon-16-Toggle-Plus").closest(".floatingimage").find(".image");
                        var bFullSize = image.hasClass("fullsize");
                        image.closest(".floatingimage").find(".imagetext").toggle(!bFullSize);
                        image.attr("src", !bFullSize ? data.fileurl : data.thumbnail)
                            .attr("width", !bFullSize ? data.width : "100")
                            .attr("height", !bFullSize ? data.height : "80").toggleClass("fullsize");
                    });
                    if (data.comment != "") {
                        $imageDiv.append([
                            "<span class='imagetext clearfix'>",
                            "<span class='photoby'>", data.photoby, "</span>",
                            "<span class='taxonname'>", data.taxonname, "</span>",
                            "<span class='sightinginfo'>", data.sightingdetails, "</span>",
                            "<p class='last'>" + data.comment + "</p>",
                            "</span>"].join(''));
                    } // Add the imagecomment
                    $imageDiv.appendTo(document.body).positionCenter().draggable({ // Append the floating DIV to the document.body and rise z-index when dragging to make the current image jump up a level
                        handle: "div.toolbar",
                        create: function (event, ui) {
                            $(this).addClass("draggable");
                        },
                        start: function (event, ui) {
                            $(this).css("z-index", parseInt($(this).css("z-index")) + 1);
                        }
                    });
                    var existingDivs = $("div.floatingimage").last(); // Find any existing floating div
                    if (existingDivs.length) { // Position next to the floating existing div
                        $imageDiv.css({
                            top: existingDivs.offset().top + 15,
                            left: existingDivs.offset().left + 15
                        });
                    }
                }
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### USER SUMMARY - Get summary for a user. Depends on the popupInfoTooltip function that waits for the fetched-event
        GetUserInformationByAlias: function (userAlias) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/User/UserSummary/' + encodeURIComponent(userAlias), function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2UserFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### SIGHTING COLLECTION SUMMARY
        // Get summary of collection items for one sighting
        SightingCollectionSummary: function (sightingid) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/ViewSighting/GetSightingCollectionSummary/' + sightingid, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2SightingCollectionSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------s------------------------------------
        // ### SIGHTING BARCODE POPUP
        // Get summary of BARCODE for one sighting
        SightingBarCodeSummary: function (sightingid) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/ViewSighting/GetSightingBarCodeSummary/' + sightingid, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2SightingBarCodeSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------s------------------------------------
        // ### SIGHTING ECOLOGY SUMMARY
        // Get summary of ecology for one sighting
        SightingEcologySummary: function (sightingid) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/ViewSighting/GetSightingEcologySummary/' + sightingid, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2SightingEcologySummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------s------------------------------------
        // ### DIARYENTRY ECOLOGY SUMMARY
        // Get summary of ecology for one diary entry
        DiaryEntryEcologySummary: function (diaryentryid) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/Diary/GetEcologySummary/' + diaryentryid, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2SightingEcologySummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------s------------------------------------
        // ### DIARYENTRY PROJECT SUMMARY
        // Get summary of project for one diary entry
        DiaryEntryProjectSummary: function (diaryentryid) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/Diary/GetProjectSummary/' + diaryentryid, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2DiaryEntryProjectSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------s------------------------------------
        // ### DIARYENTRY WEATHER SUMMARY
        // Get summary of weather parameters for one diary entry
        DiaryEntryWeatherSummary: function (diaryentryid) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/Diary/GetWeatherSummary/' + diaryentryid, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2SDiaryEntryWeatherSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### SITE SUMMARY
        // Get summary of a site
        SightingSiteSummary: function (siteId, sightingId) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/Site/GetSightingSiteSummary/', { siteId: siteId, sightingId: sightingId }, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2SightingSiteSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### PUBLIC SITE SUMMARY
        // Get summary of a site
        PublicSiteSummary: function (siteId) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/Site/GetPublicSiteSummary/', { siteId: siteId }, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2PublicSiteSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### PROJECT SUMMARY
        // Get summary of a project
        ProjectSummary: function (projectId) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/Project/GetProjectSummary/', { projectId: projectId }, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2ProjectSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### TRIGGERED VALIDATION RULE INFO
        GetTriggeredValidationRuleSummary: function (sightingId) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/ViewSighting/GetTriggeredValidationRuleSummary/' + sightingId, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2TriggeredValidationRuleSummaryFetched", [result]);
            });
        },
        //----------------------------------------------------------------------------------------------------------------------------------------------------------------
        // ### SIGHTING VALIDATION INFO
        GetSightingValidationSummary: function (sightingid) {
            var result = $("<div />").load(Artportalen_ApplicationPath + '/ViewSighting/GetSightingValidationSummary/' + sightingid, function (response, status, xhr) {
                if (status == "error" || response == "") {
                    result = "<div class='ajax-fetch-error'><span class='UI-Icon-16 UI-Icon-16-Error'></span> " + Artportalen.ResourceLabel("Shared_AjaxFetchError") + "</div>";
                }
                $(document).trigger("AP2SightingValidationSummaryFetched", [result]);
            });
        },
        TaxonLinks: function (taxonid, isLeaf) {
            var links = [
                {
                    url: '/search/gallery/taxon/' + taxonid,
                    icon: "UI-Icon-16-ViewGallery",
                    tooltip: Artportalen.ResourceLabel("Shared_GalleryRoute_Label")
                }, {
                    url: '/search/map/taxon/' + taxonid,
                    icon: "UI-Icon-16-Map",
                    tooltip: Artportalen.ResourceLabel("Shared_MapRoute_Label")
                }, {
                    url: '/search/mapgrid/taxon/' + taxonid,
                    icon: "UI-Icon-16-AtlasMap",
                    tooltip: Artportalen.ResourceLabel("Shared_MapGridRoute_Label")
                }, {
                    url: '/search/histogram/taxon/' + taxonid,
                    icon: "UI-Icon-16-Chart",
                    tooltip: Artportalen.ResourceLabel("Shared_HistogramRoute_Label")
                }, {
                    url: '/search/sightings/taxon/' + taxonid,
                    icon: "UI-Icon-16-Table",
                    tooltip: Artportalen.ResourceLabel("Shared_SightingsRoute_Label")
                }];

            if (!isLeaf) {
                links.push({
                    url: '/search/specieslist/taxon/' + taxonid,
                    icon: "UI-Icon-16-SpeciesList",
                    tooltip: Artportalen.ResourceLabel("Shared_SpeciesListRoute_Label")
                });
            }
            links.push({
                url: '/List/Top/User/AllSpecies/Taxon/' + taxonid + '/Total/Hela%20landet/Alla%20lokaler',
                icon: "UI-Icon-16-Users",
                tooltip: Artportalen.ResourceLabel("Shared_ShowListUsersByTaxon")
            });


            if (Artportalen.IsUserLoggedIn()) {
                links.push({
                    url: '/MySightingsByTaxon/' + taxonid,
                    icon: "UI-Icon-16-Personal",
                    tooltip: Artportalen.ResourceLabel("Shared_MySightingsByTaxon")
                });
            }

            var returnHtml = "<div class='iconbar clearfix'>";
            jQuery.each(links, function (index, item) {
                returnHtml += "<a href='" + Artportalen_ApplicationPath + item.url + "' title='" + item.tooltip + "' target='_blank'><span class='UI-Icon-16 UI-Icon-16-NoMargin " + item.icon + "'></a>";
            });
            returnHtml += "</div>";
            return returnHtml;

        },
        TaxonInfo: function (taxonid) {
            if (Artportalen.Country.IsSweden()) {
                Artportalen.GetExternalTaxonInfo(taxonid);
            } else {
                Artportalen.GetInternalTaxonInfo(taxonid);
            }
        },
        GetInternalTaxonInfo: function (taxonid) {
            Artportalen.ajaxPost(Artportalen_ApplicationPath + '/ViewSighting/GetInternalTaxonInfo/' + taxonid, { taxonid: taxonid }, function (data, code, xht) {

                var taxonInfo = data.TaxonInfo;

                console.log(data);

                // Build up the HTML ouput
                var userHtml = "";
                userHtml += "<table class='userprofiletable'>";
                userHtml += "<tr><td colspan='2'>";
                if (taxonInfo.TaxonName != null) {
                    userHtml += "<h3>" + taxonInfo.TaxonName + "</h3>";
                    userHtml += "<h5><em>" + taxonInfo.TaxonScientificName + "</em>" + (taxonInfo.Auctor != null ? " <small class='author'>" + taxonInfo.Auctor + "</small>" : "") + "</h5>";
                } else {
                    userHtml += "<h3><em>" + taxonInfo.TaxonScientificName + "</em>" + (taxonInfo.Auctor != null ? " <small class='author'>" + taxonInfo.Auctor + "</small>" : "") + "</h3>";
                }
                if (taxonInfo.Synonyms.length) {
                    userHtml += "<tr><th class='synonym'>" + data.Labels.SynonymsLabel + ":</th><td class='synonym'>";
                    for (var i = 0; i < taxonInfo.Synonyms.length; i++) {
                        userHtml += taxonInfo.Synonyms[i] + "<br>";
                    }
                    userHtml += "</td></tr>";

                }
                if (taxonInfo.ProtectionLevelId > 1) {
                    userHtml += "<tr><td colspan='2'><div style='margin:5px -8px; padding:5px; background-color: #fff4f4;border-top: 1px solid #f3caca;border-bottom:1px solid #f3caca;position:relative;'><span class='UI-Icon-16 UI-Icon-16-NoLeftMargin UI-Icon-16-ProtectedBySystem'></span> " + taxonInfo.ProtectionLevelLabel + "</div></th><tr>";
                }
                userHtml += "</td></tr>";
                if (taxonInfo.TaxonParents.length) {
                    userHtml += "<tr><td colspan='2'><h4>" + data.Labels.ClassificationLabel + "</h4></th><tr>";
                    userHtml += "</table><div style='max-height:100px;overflow:auto;line-height:1.2;'><table class='userprofiletable'>";
                    for (var i = 0; i < taxonInfo.TaxonParents.length; i++) {
                        userHtml += "<tr><th>" + taxonInfo.TaxonParents[i].TaxonCategoryNameLocalized + ":</th><td>" + taxonInfo.TaxonParents[i].TaxonScientificName;
                        if (taxonInfo.TaxonParents[i].TaxonName != "") userHtml += " (" + Artportalen.Helpers.InitCap(taxonInfo.TaxonParents[i].TaxonName) + ")";
                        userHtml += "</td></tr>";
                    }
                    userHtml += "</td></tr>";
                    userHtml += "</table></div>";
                } else {
                    userHtml += "</table>";
                }
                userHtml += "<div style='margin: 0px -7px -6px;border-top: 1px solid #eee;' class='clearfix'>" + Artportalen.TaxonLinks(taxonid, taxonInfo.IsLeaf) + "</div>";
                $(document).trigger("AP2TaxonFetched", [userHtml]);
            });
        },
        GetExternalTaxonInfo: function(taxonid) {
            Artportalen.ajaxPost(Artportalen_ApplicationPath + '/ViewSighting/GetExternalTaxonInfo/' + taxonid, { taxonid: taxonid }, function (data, code, xht) {

                var NON_TAXON_GROUP = 100;  // TaxonCategoryType.NonTaxonGroup

                // Links to the external service
                var externalTaxonServiceView = data.ExternalTaxonServiceView;

                // parse the JSON from external service
                // The taxon object has to be wrapped as an array with "[]" at server side to be able to parse it. 
                // Select the zero position in the array = the taxon object
                var parseTaxonObject = JSON.parse(data.TaxonObject);
                var taxonInfo = parseTaxonObject[0];

                // Build up the HTML ouput
                var userHtml = "";
                userHtml += "<table class='userprofiletable'>";
                userHtml += "<tr><td colspan='2'>";
                if (taxonInfo.CommonName.Name !== null) {
                    if (taxonInfo.CategoryId === NON_TAXON_GROUP) {
                        userHtml += "<h3>" + Artportalen.Helpers.InitCap(taxonInfo.CommonName.Name) + "</h3>";
                    } else {
                        userHtml += "<h3><a href='" + externalTaxonServiceView + taxonInfo.Id + "' target='_blank'>" + Artportalen.Helpers.InitCap(taxonInfo.CommonName.Name) + "</a></h3>";
                        if (taxonInfo.ScientificName.Name !== "" && taxonInfo.ScientificName.Name !== null) {
                            userHtml += "<h5>";
                            userHtml += "<em>" + taxonInfo.ScientificName.Name + "</em>";
                            if (taxonInfo.ScientificName.Author !== "" && taxonInfo.ScientificName.Author !== null) userHtml += " <small class='author'>" + taxonInfo.ScientificName.Author + "</small>";
                            userHtml += "</h5>";
                        }
                    }
                } else {
                    userHtml += "<h3><a href='" + externalTaxonServiceView + taxonInfo.Id + "' target='_blank'><em>" + taxonInfo.ScientificName.Name + "</em>";
                    if (taxonInfo.ScientificName.Author !== "" && taxonInfo.ScientificName.Author !== null) userHtml += " <small class='author'>" + taxonInfo.ScientificName.Author + "</small>";
                    userHtml += "</a></h3>";
                }
                userHtml += "</td></tr>";

                if (data.ProtectionLevelId > 2) {
                    userHtml += "<tr><td colspan='2'><div style='margin:5px -8px; padding:5px; background-color: #fff4f4;border-top: 1px solid #f3caca;border-bottom:1px solid #f3caca;position:relative;'><span class='UI-Icon-16 UI-Icon-16-NoLeftMargin UI-Icon-16-ProtectedBySystem'></span> " + data.ProtectionLevelLabel + "</div></th><tr>"; // <a href='" + Artportalen_ApplicationPath + "/Home/Security'>" + data.ReadMoreLabel + "</a>
                }

                userHtml += "<tr><td colspan='2'><h4></h4></th><tr>";
                userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_TaxonId") + ":</i> " + taxonid + "</td></tr>";

                if (data.ForestryAgencysIndicator) {
                    userHtml += "<tr><td style='font-weight: bold'>" + Artportalen.ResourceLabel("Shared_LookupList_SpeciesFact_ForestryAgencysIndicatorSpecies") + "</td></tr>";
                }
                if (data.BirdDirective) {
                    userHtml += "<tr><td style='font-weight: bold'>" + Artportalen.ResourceLabel("Shared_LookupList_SpeciesFact_BirdDirective") + "</td></tr>";
                }
                if (data.ProtectedSpecies) {
                    userHtml += "<tr><td style='font-weight: bold'>" + Artportalen.ResourceLabel("Shared_LookupList_SpeciesFact_ProtectedSpeciesExclBirds") + "</td></tr>";
                }
                if (data.HabitatDirective2) {
                    userHtml += "<tr><td style='font-weight: bold'>" + Artportalen.ResourceLabel("Shared_LookupList_SpeciesFact_HabitatsDirectiveAnnex2") + "</td></tr>";
                }
                if (data.HabitatDirective2Priority) {
                    userHtml += "<tr><td style='font-weight: bold'>" + Artportalen.ResourceLabel("Shared_LookupList_SpeciesFact_HabitatsDirectiveAnnex2PrioritySpecies") + "</td></tr>";
                }
                if (data.HabitatDirective4) {
                    userHtml += "<tr><td style='font-weight: bold'>" + Artportalen.ResourceLabel("Shared_LookupList_SpeciesFact_HabitatsDirectiveAnnex4") + "</td></tr>";
                }
                if (data.HabitatDirective5) {
                    userHtml += "<tr><td style='font-weight: bold'>" + Artportalen.ResourceLabel("Shared_LookupList_SpeciesFact_HabitatsDirectiveAnnex5") + "</td></tr>";
                }
                if (data.AlienSpecies) {
                    userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_AlienSpecies") + ":</i> " + Artportalen.ResourceLabel(data.AlienSpecies) + "</td></tr>";
                }
                if (data.ActionPlanSpecies) {
                    userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_ActionPlanSpecies") + ":</i> " + Artportalen.ResourceLabel(data.ActionPlanSpecies) + "</td></tr>";
                }
                if (data.SwedishOccurrence) {
                    userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_SwedishOccurrence") + ":</i> " + Artportalen.ResourceLabel(data.SwedishOccurrence) + "</td></tr>";
                }

                if (taxonInfo.Classification !== null && taxonInfo.Classification.Parents !== null && taxonInfo.Classification.Parents !== undefined) {
                    for (var i = 0; i < taxonInfo.Classification.Parents.length; ++i) {
                        if (taxonInfo.Classification.Parents[i] &&
                            taxonInfo.Classification.Parents[i].CategoryName &&
                            taxonInfo.Classification.Parents[i].CategoryName === "Klass") {
                            if (taxonInfo.Classification.Parents[i].ScientificName &&
                                taxonInfo.Classification.Parents[i].ScientificName !== "") {
                                userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_TaxonClass") + ":</i> " + taxonInfo.Classification.Parents[i].ScientificName + "</td></tr>";
                            }
                        }
                    }
                }

                if (taxonInfo.Classification !== null && taxonInfo.Classification.Parents !== null && taxonInfo.Classification.Parents !== undefined) {
                    for (var i = 0; i < taxonInfo.Classification.Parents.length; ++i) {
                        if (taxonInfo.Classification.Parents[i] &&
                            taxonInfo.Classification.Parents[i].CategoryName &&
                            taxonInfo.Classification.Parents[i].CategoryName === "Familj") {
                            if (taxonInfo.Classification.Parents[i].ScientificName &&
                                taxonInfo.Classification.Parents[i].ScientificName !== "") {
                                userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_TaxonFamily") + ":</i> " + taxonInfo.Classification.Parents[i].ScientificName + "</td></tr>";
                            }
                        }
                    }
                }

                // Add when taxonkoncept is cleaned up
                /*if (taxonInfo.Category) {
                    userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_TaxonCategory") + ":</i> " + taxonInfo.Category + "</td></tr>";
                }
                // Add when taxonkoncept is cleaned up
                if (taxonInfo.ConceptDefinition) {
                    userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_ConeceptDefinition") + ":</i> " + taxonInfo.ConceptDefinition + "</td></tr>";
                }*/
                /*if (taxonInfo.AlertStatus > 0) {
                    userHtml += "<tr><td style='font-weight: bold'><i>" + Artportalen.ResourceLabel("Shared_ProblematicTaxon") + ":</i> " + taxonInfo.AlertStatus + "</td></tr>";
                }*/

                if (taxonInfo.CategoryId === NON_TAXON_GROUP) {
                    userHtml += "<tr><td colspan='2'><div style='font-size:10px; margin:5px -8px; padding:5px; background-color: #F6F6F6;border-top: 1px solid #eee;border-bottom:1px solid #eee;position:relative;'>Ingen information fr&aring;n Dynamisk taxa f&ouml;r detta artnamn.</div></td></tr>";
                } else {
                    userHtml += "<tr><td colspan='2'><div style='font-size:10px; margin:5px -8px; padding:5px; background-color: #F6F6F6;border-top: 1px solid #eee;border-bottom:1px solid #eee;position:relative;'>" +
                        "<a href='https://www.dyntaxa.se/Taxon/Info/" + taxonid + "' class='userprofile' target='_blank'>Taxonomi</a>" +
                        "&nbsp;&nbsp;" +
                        "<a href='https://artfakta.se/artbestamning/taxon/" + taxonid + "' class='userprofile' target='_blank'>Artfakta.se</a>" +
                        "</div></td></tr>";
                }
                userHtml += "</td></tr></table>";

                userHtml += "<div style='margin: -7px -7px -6px;border-top: 1px solid #eee;' class='clearfix'>" + Artportalen.TaxonLinks(taxonid, data.IsLeaf) + "</div>";
                /*
                userHtml += "<tr><td colspan='2'>";
                var links = [
                {
                    url: '/search/gallery/taxon/' + taxonid,
                    icon: "UI-Icon-16-ViewGallery",
                    tooltip: Artportalen.ResourceLabel("Shared_GalleryRoute_Label")
                }, {
                    url: '/search/map/taxon/' + taxonid,
                    icon: "UI-Icon-16-Map",
                    tooltip: Artportalen.ResourceLabel("Shared_MapRoute_Label")
                }, {
                    url: '/search/mapgrid/taxon/' + taxonid,
                    icon: "UI-Icon-16-AtlasMap",
                    tooltip: Artportalen.ResourceLabel("Shared_MapGridRoute_Label")
                }, {
                    url: '/search/histogram/taxon/' + taxonid,
                    icon: "UI-Icon-16-Chart",
                    tooltip: Artportalen.ResourceLabel("Shared_HistogramRoute_Label")
                }, {
                    url: '/search/sightings/taxon/' + taxonid,
                    icon: "UI-Icon-16-Table",
                    tooltip: Artportalen.ResourceLabel("Shared_SightingsRoute_Label")
                }];

                if (!data.IsLeaf) {
                    links.push({
                        url: '/search/specieslist/taxon/' + taxonid,
                        icon: "UI-Icon-16-SpeciesList",
                        tooltip: Artportalen.ResourceLabel("Shared_SpeciesListRoute_Label")
                    });
                }
                links.push({
                    url: '/List/Top/User/AllSpecies/Taxon/' + taxonid + '/Total/Hela%20landet/Alla%20lokaler',
                    icon: "UI-Icon-16-Users",
                    tooltip: Artportalen.ResourceLabel("Shared_ShowListUsersByTaxon")
                });
                

                if (Artportalen.IsUserLoggedIn()) {
                    links.push({
                        url: '/MySightingsByTaxon/' + taxonid,
                        icon: "UI-Icon-16-Personal",
                        tooltip: Artportalen.ResourceLabel("Shared_MySightingsByTaxon")
                    });
                }
                userHtml += "<div class='iconbar clearfix'>";
                jQuery.each(links, function(index, item) {
                    userHtml += "<a href='" + Artportalen_ApplicationPath + item.url + "' title='" + item.tooltip + "' target='_blank'><span class='UI-Icon-16 UI-Icon-16-NoMargin " + item.icon + "'></a>";
                });
                userHtml += "</div>";
                */
                /*
                userHtml += "<a href='" + Artportalen_ApplicationPath + '/Media/Taxon/' + taxonid + "' style='float:left' target='_blank'><span class='UI-Icon-16 UI-Icon-16-NoLeftMargin UI-Icon-16-ViewGallery'></span> Bilder</a>";
                if ($(document.body).hasClass("ap2UserLoggedIn")) {
                    userHtml += "&nbsp;<a href='" + Artportalen_ApplicationPath + '/MySightingsByTaxon/' + taxonid + "' style='float:left;border-left: 1px solid #ccc;padding-left: 3px;margin-left: 10px;'><span class='UI-Icon-16 UI-Icon-16-List'></span> Mina fynd av arten</a></td></tr>";
                }
                userHtml += "</td></tr>";
                userHtml += "</table>";
                */
                // Trigger an event with the HTML result
                $(document).trigger("AP2TaxonFetched", [userHtml]);
            });
        }
    };
} ();

/* Konami easter egg... */
(function Konami() {
    var kkeys = [], konami = "38,38,40,40,37,39,37,39,66,65";
    $(document).bind('keydown.konami', function(e) {
        kkeys.push(e.keyCode);
        if (kkeys.toString().indexOf(konami) >= 0) {
            $(document).unbind('keydown.konami', arguments.callee);
            for (var i=0;i<=100;i++) {
                GenerateKonamiBug();
            }
        }
    });
    function Rndm(value) {
        return (Math.random() * value).toFixed();
    }
    function GenerateKonamiBug() {
        $("<img src='" + Artportalen_ApplicationPath + "/Content/Images/Icons/bug" + Rndm(2) + ".gif' />")
            .css(
            {'position': 'absolute', 'left': Rndm($(document).width() - 100) + 'px', 'top': Rndm($(document).height() - 100) + 'px', 'display': 'none' }
            ).appendTo('body').delay(Rndm(30000)).fadeIn(500).delay(Rndm(40000)).fadeOut(200, function() {
                $(this).remove();
                GenerateKonamiBug();
            });
    };
})();

(function ($) {

    /**
     * Copyright 2012, Digital Fusion
     * Licensed under the MIT license.
     * http://teamdf.com/jquery-plugins/license/
     *
     * @author Sam Sehnert
     * @desc A small plugin that checks whether elements are within
     *		 the user visible viewport of a web browser.
     *		 only accounts for vertical position, not horizontal.
     */
    $.fn.visible = function (partial) {

        var $t = $(this),
            $w = $(window),
            viewTop = $w.scrollTop(),
            viewBottom = viewTop + $w.height(),
            _top = $t.offset().top,
            _bottom = _top + $t.height(),
            compareTop = partial === true ? _bottom : _top,
            compareBottom = partial === true ? _top : _bottom;

        return ((compareBottom <= viewBottom) && (compareTop >= viewTop));
    };

})(jQuery);

/* MULTISELECTBOX */
(function ($) {
    $.fn.multipleOptions = function (customOptions) {

        var options = {
            optionDisabledClass: 'optionDisabled',
            selectedOptionsClass: 'selectedOptions'
        };

        return this.each(function (index) {
            var $original = $(this);
            var $original_values = $("#" + $(this).attr("id").replace(/_Select$/g, ''));
            var $container = $original.parent();
            var $selectedOptionsList = $container.siblings("ol." + options.selectedOptionsClass);
            var $selectedOptionsListExists = ($selectedOptionsList.length != 0);


            function init() {
                if (!$selectedOptionsListExists) createSelectedOptionsList();
                $original.change(selectChangeEvent);

                $selectedOptionsList.click(function (event) {
                    if (event.target.nodeName == 'A') {
                        event.preventDefault();
                        toggleSelectedOption($original.find("option[value='" + $(event.target).attr("data-id") + "']"), false);
                        $original.focus();
                        //if ($.browser.msie) $original.attr("selected", "selected");
                    }
                });
            }

            // create an unordered list that stores the selected options
            function createSelectedOptionsList() {
                $selectedOptionsList = $("<ol></ol>")
                    .addClass(options.selectedOptionsClass)
                    .insertAfter($container);
                $selectedOptionsListExists = true;
            }

            function selectChangeEvent(e) {
                toggleSelectedOption($(this).children("option:selected"), true);
                $original.find("option:eq(0)").prop('selected', true);
            }

            function toggleSelectedOption(_selectedItem, _state) {
                if (_selectedItem.text() == "" || _selectedItem.val() == "0" || _selectedItem.val() == "-1") return false;
                if (_state) {
                    toggleOption(_selectedItem, _state);
                    if ($selectedOptionsList.find("li a[data-id='" + _selectedItem.val() + "']").length == 0) {
                        var $item = $("<li></li>")
                            .html("<a href='#' data-id='" + _selectedItem.val() + "'>" + _selectedItem.text() + "</a>");
                        $item.appendTo($selectedOptionsList);
                    }
                } else {
                    toggleOption(_selectedItem, _state);
                    $selectedOptionsList.find("li a[data-id='" + _selectedItem.val() + "']").parent().remove();
                    //$original.attr('selectedIndex', '-1');
                    //_selectedItem.removeProp("selected");
                }

                // Creates a array list of id:s
                var idList = [];
                $selectedOptionsList.find("li a").each(function () {
                    idList.push($(this).attr("data-id"));
                });
                idList.join(',');

                // Set id:s to the hidden input
                $original_values.val(idList);
            }

            function toggleOption(_option, _state) {
                _option.toggleClass(options.optionDisabledClass, _state)
                    .prop("selected", _state)
                    .attr("disabled", _state);
                // if ($.browser.msie) $select.hide().show();
            }

            init();
        });
    };
})(jQuery);

/* EQUAL HEIGHT */
(function($) {
    $.fn.equalHeight = function() {
        var height = 0,
            reset = $.browser.msie ? "1%" : "auto";
        return this
            .css("height", reset)
            .each(function() {
                height = Math.max(height, this.offsetHeight);
            })
            .css("height", height)
            .each(function() {
                var h = this.offsetHeight;
                if (h > height) {
                    $(this).css("height", height - (h - height));
                };
            });
    };
})(jQuery);

/* SMART RESIZE FUNCTION */
(function ($, sr) {
    // debouncing function from John Hann
    // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
    var debounce = function (func, threshold, execAsap) {
        var timeout;

        return function debounced() {
            var obj = this, args = arguments;
            function delayed() {
                if (!execAsap)
                    func.apply(obj, args);
                timeout = null;
            };

            if (timeout)
                clearTimeout(timeout);
            else if (execAsap)
                func.apply(obj, args);

            timeout = setTimeout(delayed, threshold || 100);
        };
    };

    // smartresize 
    jQuery.fn[sr] = function (fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };

})(jQuery, 'smartresize');

/* POSITION CENTER ON SCREEN - Some hacks by JSZ */
(function($){
    $.fn.positionCenter = function(options) {
        var settings = $.extend( {
            'position': 'absolute',
            animateposition: false
        }, options);
        var pos = {
            sTop : function() {
                return window.pageYOffset || document.documentElement && document.documentElement.scrollTop ||	document.body.scrollTop;
            },
            wHeight : function() {
                return window.innerHeight || document.documentElement && document.documentElement.clientHeight || document.body.clientHeight;
            }
        };
        return this.each(function(index) {
            if (index == 0) {
                var $this = $(this);
                var elHeight = $this.outerHeight();
                var elTop = pos.sTop() + (pos.wHeight() / 2) - (elHeight / 2);
                $this.css({
                    position: settings.position,
                    margin: '0'
                });
                // JSZ hack
                if (!settings.animateposition) {
                    $this.css({
                        top: elTop,
                        left: (($(window).width() - $this.outerWidth()) / 2) + 'px'
                    });
                } else {
                    $this.stop().animate({
                        top: elTop,
                        left: (($(window).width() - $this.outerWidth()) / 2) + 'px'
                    }, 200, 'swing');
                }
            }
        });
    };
})(jQuery);

/* PASSWORD STRENGTH */
(function($) {
    $.fn.passwordStrength = function( options ){
        return this.each(function(){
            var that = this;that.opts = {};
            that.opts = $.extend({}, $.fn.passwordStrength.defaults, options);

            that.div = $(that.opts.targetDiv);
            that.defaultClass = that.div.attr('class');

            that.percents = (that.opts.classes.length) ? 100 / that.opts.classes.length : 100;

            v = $(this)
                .keyup(function() {
                    if (typeof el == "undefined")
                        this.el = $(this);
                    var s = getPasswordStrength(this.value);
                    var p = this.percents;
                    var t = Math.floor(s / p);

                    if (100 <= s)
                        t = this.opts.classes.length - 1;

                    this.div
                        .removeAttr('class')
                        .addClass(this.defaultClass)
                        .addClass(this.opts.classes[t]);
                });
            //# Removed generate password button creation
        });

        function getPasswordStrength(H){
            var D=(H.length);

            //# Added below to make all passwords less than 4 characters show as weak
            if (D<4) { D=0 }
            if (D>5) { D=5 }
            var F=H.replace(/[0-9]/g,"");
            var G=(H.length-F.length);
            if(G>3){G=3}
            var A=H.replace(/\W/g,"");
            var C=(H.length-A.length);
            if(C>3){C=3}
            var B=H.replace(/[A-Z]/g,"");
            var I=(H.length-B.length);
            if(I>3){I=3}
            var E=((D*10)-20)+(G*10)+(C*15)+(I*10);
            if(E<0){E=0}
            if(E>100){E=100}
            return E
        }

        //# Removed generate password function
    };
})(jQuery);

// Read a page's GET URL variables and return them as an associative array.
(function($) {
    $.extend({
        getUrlVars: function () {
            var vars = [], hash;
            var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
            for (var i = 0; i < hashes.length; i++) {
                hash = hashes[i].split('=');
                vars.push(hash[0]);
                vars[hash[0]] = hash[1];
            }
            return vars;
        },
        getUrlVar: function (name) {
            return $.getUrlVars()[name];
        }
    });
})(jQuery);


/* AUTOGROWING TEXTAREA */
/**
 * TextAreaExpander plugin for jQuery
 * v1.0
 * Expands or contracts a textarea height depending on the
 * quatity of content entered by the user in the box.
 *
 * By Craig Buckler, Optimalworks.net
 *
 * As featured on SitePoint.com:
 * http://www.sitepoint.com/blogs/2009/07/29/build-auto-expanding-textarea-1/
 *
 * Please use as you wish at your own risk.
 */

/**
 * Usage:
 *
 * From JavaScript, use:
 *     $(<node>).TextAreaExpander(<minHeight>, <maxHeight>);
 *     where:
 *       <node> is the DOM node selector, e.g. "textarea"
 *       <minHeight> is the minimum textarea height in pixels (optional)
 *       <maxHeight> is the maximum textarea height in pixels (optional)
 *
 * Alternatively, in you HTML:
 *     Assign a class of "expand" to any <textarea> tag.
 *     e.g. <textarea name="textarea1" rows="3" cols="40" class="expand"></textarea>
 *
 *     Or assign a class of "expandMIN-MAX" to set the <textarea> minimum and maximum height.
 *     e.g. <textarea name="textarea1" rows="3" cols="40" class="expand50-200"></textarea>
 *     The textarea will use an appropriate height between 50 and 200 pixels.
 */
(function($) {

    // jQuery plugin definition
    $.fn.TextAreaExpander = function(minHeight, maxHeight) {

        var hCheck = !($.browser.msie || $.browser.opera);

        // resize a textarea
        function ResizeTextarea(e) {

            // event or initialize element?
            e = e.target || e;

            // find content length and box width
            var vlen = e.value.length, ewidth = e.offsetWidth;
            if (vlen != e.valLength || ewidth != e.boxWidth) {

                if (hCheck && (vlen < e.valLength || ewidth != e.boxWidth)) e.style.height = "0px";
                var h = Math.max(e.expandMin, Math.min(e.scrollHeight, e.expandMax));

                e.style.overflow = (e.scrollHeight > h ? "auto" : "hidden");
                e.style.height = h + "px";

                e.valLength = vlen;
                e.boxWidth = ewidth;
            }

            return true;
        };

        // initialize
        this.each(function() {

            // is a textarea?
            if (this.nodeName.toLowerCase() != "textarea") return;

            // set height restrictions
            var p = this.className.match(/expand(\d+)\-*(\d+)*/i);
            this.expandMin = minHeight || (p ? parseInt('0' + p[1], 10) : 0);
            this.expandMax = maxHeight || (p ? parseInt('0' + p[2], 10) : 99999);

            // initial resize
            ResizeTextarea(this);

            // zero vertical padding and add events
            if (!this.Initialized) {
                this.Initialized = true;
                $(this).css("padding-top", 0).css("padding-bottom", 0);
                $(this).bind("keyup", ResizeTextarea).bind("focus", ResizeTextarea);
            }
        });
        return this;
    };
})(jQuery);
/************************************************************************
* @name: bPopup
* @author: (c) Bjoern Klinggaard (http://dinbror.dk/bpopup)
* @version: 0.5.0.min
* Bugfixed by JSZ 2011-04-06 - the jQuery function .hide() needed a zero duration to execute
 *
 * DEFAULT VALUES:
 * amsl(Above Mean Sea Level): 150px // Vertical distance from the middle of the window, + = above, - = under
 * appendTo: 'body' // Which element the popup should append to (append to 'form' when ASP.net)
 * closeClass: 'bClose' // Class to bind the close event to
 * content: 'ajax' // [iframe, ajax, xlink] COMING SOON
 * contentContainer: null //if null, contentContainer == $(this)
 * escClose: true // Close on esc
 * fadeSpeed: 250 // Animation speed on fadeIn/out
 * follow: true // Should the popup follow the screen on scroll/resize? 
 * followSpeed: 500 // Animation speed for the popup on scroll/resize
 * loadUrl: null // External page or selection to load in popup
 * modal: true // Modal overlay
 * modalClose: true // Shold popup close on click on modal overlay?
 * modalColor: #000 // Modal overlay color
 * opacity: 0.7 // Transparency, from 0.1 to 1.0 (filled)
 * scrollBar: true // Scrollbars visible
 * vStart: null // Vertical start position for popup
 * zIndex: 9999 // Popup z-index, modal overlay = popup z-index - 1
 *
************************************************************************/
/*
(function (a) { a.fn.bPopup = function (i, k) { function u() { c.css({ left: b.scrollLeft() + g, position: "absolute", top: b.scrollTop() + e, "z-index": o.zIndex }).appendTo(o.appendTo).hide(0, function () { a.isFunction(o.onOpen) && o.onOpen.call(c); if (o.loadUrl != null) { o.contentContainer = o.contentContainer == null ? c : a(o.contentContainer); switch (o.content) { case "iframe": a('<iframe width="100%" height="100%"></iframe>').attr("src", o.loadUrl).appendTo(o.contentContainer); break; default: o.contentContainer.load(o.loadUrl); } } }).fadeIn(o.fadeSpeed, function () { a.isFunction(k) && k(); }); v(); } function j() { o.modal && a("#bModal").fadeOut(o.fadeSpeed, function () { a("#bModal").remove(); }); c.fadeOut(o.fadeSpeed, function () { o.loadUrl != null && o.content != "xlink" && o.contentContainer.empty(); }); o.scrollBar || a("html").css("overflow", "auto"); a("." + o.closeClass).die("click"); a("#bModal").die("click"); b.unbind("keydown.bPopup"); f.unbind(".bPopup"); c.data("bPopup", null); a.isFunction(o.onClose) && setTimeout(function () { o.onClose.call(c); }, o.fadeSpeed); return false; } function w() { if (m || x) { var d = [b.height(), b.width()]; return { "background-color": o.modalColor, height: d[0], left: l(), opacity: 1, position: "absolute", top: 0, width: d[1], "z-index": o.zIndex - 1 }; } else return { "background-color": o.modalColor, height: "100%", left: 0, opacity: 1, position: "fixed", top: 0, width: "100%", "z-index": o.zIndex - 1 }; } function v() { a("." + o.closeClass).live("click", j); o.modalClose && a("#bModal").live("click", j).css("cursor", "pointer"); if (o.follow[0] || o.follow[1]) f.bind("scroll.bPopup", function () { c.stop().animate({ left: o.follow[1] ? b.scrollLeft() + g : g, top: o.follow[0] ? b.scrollTop() + e : e }, o.followSpeed); }).bind("resize.bPopup", function () { if (o.modal && m) { var d = [b.height(), b.width()]; n.css({ height: d[0], width: d[1], left: l() }); } h = p(c, o.amsl); if (o.follow[0]) e = q ? b.scrollTop() + o.position[0] : b.scrollTop() + h[0]; if (o.follow[1]) g = r ? b.scrollLeft() + o.position[1] : b.scrollLeft() + h[1]; c.stop().animate({ left: g, top: e }, o.followSpeed); }); o.escClose && b.bind("keydown.bPopup", function(d) { d.which == 27 && j(); }); } function l() { return f.width() < a("body").width() ? 0 : (a("body").width() - f.width()) / 2; } function p(d, y) { var s = (f.height() - d.outerHeight(true)) / 2 - y, z = (f.width() - d.outerWidth(true)) / 2 + l(); return [s < 20 ? 20 : s, z]; } if (a.isFunction(i)) { k = i; i = null; } o = a.extend({}, a.fn.bPopup.defaults, i); o.scrollBar || a("html").css("overflow", "hidden"); var c = a(this), n = a('<div id="bModal"></div>'), b = a(document), f = a(window), h = p(c, o.amsl), q = o.position[0] != "auto", r = o.position[1] != "auto", e = q ? o.position[0] : h[0], g = r ? o.position[1] : h[1], t = navigator.userAgent.toLowerCase(), x = t.indexOf("iphone") != -1, m = /msie 6/i.test(t) && typeof window.XMLHttpRequest != "object"; this.close = function () { o = c.data("bPopup"); j(); }; return this.each(function() { if (!c.data("bPopup")) { o.modal && n.css(w()).appendTo(o.appendTo).animate({ opacity: o.opacity }, o.fadeSpeed); c.data("bPopup", o); u(); } }); }; a.fn.bPopup.defaults = { amsl: 50, appendTo: "body", closeClass: "bClose", content: "ajax", contentContainer: null, escClose: true, fadeSpeed: 250, follow: [true, true], followSpeed: 500, loadUrl: null, modal: true, modalClose: true, modalColor: "#000", onClose: null, onOpen: null, opacity: 0.7, position: ["auto", "auto"], scrollBar: true, zIndex: 9999 }; })(jQuery);
*/
/*===================================================================================================================
 * @name: bPopup
 * @type: jQuery
 * @author: (c) Bjoern Klinggaard - @bklinggaard
 * @demo: http://dinbror.dk/bpopup
 * @version: 0.9.4
 * @requires jQuery 1.4.3
 *==================================================================================================================*/
; (function ($) {
    'use strict';

    $.fn.bPopup = function (options, callback) {

        if ($.isFunction(options)) {
            callback = options;
            options = null;
        }

        // OPTIONS
        var o = $.extend({}, $.fn.bPopup.defaults, options);

        // HIDE SCROLLBAR?  
        if (!o.scrollBar)
            $('html').css('overflow', 'hidden');

        // VARIABLES	
        var $popup = this
            , d = $(document)
            , w = window
            , $w = $(w)
            , wH = windowHeight()
            , wW = windowWidth()
            , prefix = '__b-popup'
            , isIOS6X = (/OS 6(_\d)+/i).test(navigator.userAgent) // Used for a temporary fix for ios6 timer bug when using zoom/scroll 
            , buffer = 200
            , popups = 0
            , id
            , inside
            , fixedVPos
            , fixedHPos
            , fixedPosStyle
            , vPos
            , hPos
            , height
            , width
            , debounce
            ;

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC FUNCTION - call it: $(element).bPopup().close();
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////
        $popup.close = function () {
            o = this.data('bPopup');
            id = prefix + $w.data('bPopup') + '__';
            close();
        };

        return $popup.each(function () {
            if ($(this).data('bPopup')) return; //POPUP already exists?
            init();
        });

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // HELPER FUNCTIONS - PRIVATE
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////
        function init() {
            triggerCall(o.onOpen);
            popups = ($w.data('bPopup') || 0) + 1, id = prefix + popups + '__', fixedVPos = o.position[1] !== 'auto', fixedHPos = o.position[0] !== 'auto', fixedPosStyle = o.positionStyle === 'fixed', height = $popup.outerHeight(true), width = $popup.outerWidth(true);
            o.loadUrl ? createContent() : open();
        };

        function createContent() {
            o.contentContainer = $(o.contentContainer || $popup);
            switch (o.content) {
                case ('iframe'):
                    var iframe = $('<iframe class="b-iframe" ' + o.iframeAttr + '></iframe>');
                    iframe.appendTo(o.contentContainer);
                    height = $popup.outerHeight(true);
                    width = $popup.outerWidth(true);
                    open();
                    iframe.attr('src', o.loadUrl); // setting iframe src after open due IE9 bug
                    triggerCall(o.loadCallback);
                    break;
                case ('image'):
                    open();
                    $('<img />')
                        .load(function () {
                            triggerCall(o.loadCallback);
                            recenter($(this));
                        }).attr('src', o.loadUrl).hide().appendTo(o.contentContainer);
                    break;
                default:
                    open();
                    $('<div class="b-ajax-wrapper"></div>')
                        .load(o.loadUrl, o.loadData, function () {
                            triggerCall(o.loadCallback);
                            recenter($(this));
                        }).hide().appendTo(o.contentContainer);
                    break;
            }
        };

        function open() {
            // MODAL OVERLAY
            if (o.modal) {
                $('<div class="b-modal ' + id + '"></div>')
                    .css({ backgroundColor: o.modalColor, position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, opacity: 0, zIndex: o.zIndex + popups })
                    .appendTo(o.appendTo)
                    .fadeTo(o.speed, o.opacity);
            }

            // POPUP
            calPosition();
            $popup
                .data('bPopup', o).data('id', id)
                .css({
                    'left': o.transition == 'slideIn' || o.transition == 'slideBack' ? (o.transition == 'slideBack' ? d.scrollLeft() + wW : (hPos + width) * -1) : getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle))
                    , 'position': o.positionStyle || 'absolute'
                    , 'top': o.transition == 'slideDown' || o.transition == 'slideUp' ? (o.transition == 'slideUp' ? d.scrollTop() + wH : vPos + height * -1) : getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle))
                    , 'z-index': o.zIndex + popups + 1
                }).each(function () {
                    if (o.appending) {
                        $(this).appendTo(o.appendTo);
                    }
                });
            doTransition(true);
        };

        function close() {
            if (o.modal) {
                $('.b-modal.' + $popup.data('id'))
                    .fadeTo(o.speed, 0, function () {
                        $(this).remove();
                    });
            }
            // Clean up
            unbindEvents();
            // Close trasition
            doTransition();

            return false; // Prevent default
        };

        //Eksperimental
        function recenter(content) {
            var _width = content.width(), _height = content.height(), css = {};
            o.contentContainer.css({ height: _height, width: _width });

            if (_height >= $popup.height()) {
                css.height = $popup.height();
            }
            if (_width >= $popup.width()) {
                css.width = $popup.width();
            }
            height = $popup.outerHeight(true)
                , width = $popup.outerWidth(true);

            calPosition();
            o.contentContainer.css({ height: 'auto', width: 'auto' });

            css.left = getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle)),
                css.top = getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle));

            $popup
                .animate(
                css
                , 250
                , function () {
                    content.show();
                    inside = insideWindow();
                }
                );
        };

        function bindEvents() {
            $w.data('bPopup', popups);
            $popup.delegate('.bClose, .' + o.closeClass, 'click.' + id, close); // legacy, still supporting the close class bClose

            if (o.modalClose) {
                $('.b-modal.' + id).css('cursor', 'pointer').bind('click', close);
            }

            // Temporary disabling scroll/resize events on devices with IOS6+
            // due to a bug where events are dropped after pinch to zoom
            if (!isIOS6X && (o.follow[0] || o.follow[1])) {
                $w.bind('scroll.' + id, function () {
                    if (inside) {
                        $popup
                            .dequeue()
                            .animate({ 'left': o.follow[0] ? getLeftPos(!fixedPosStyle) : 'auto', 'top': o.follow[1] ? getTopPos(!fixedPosStyle) : 'auto' }, o.followSpeed, o.followEasing);
                    }
                }).bind('resize.' + id, function () {
                    wH = windowHeight();
                    wW = windowWidth();
                    inside = insideWindow();
                    if (inside) {
                        clearTimeout(debounce);
                        debounce = setTimeout(function () {
                            calPosition();
                            $popup
                                .dequeue()
                                .each(function () {
                                    if (fixedPosStyle) {
                                        $(this).css({ 'left': hPos, 'top': vPos });
                                    }
                                    else {
                                        $(this).animate({ 'left': o.follow[0] ? getLeftPos(true) : 'auto', 'top': o.follow[1] ? getTopPos(true) : 'auto' }, o.followSpeed, o.followEasing);
                                    }
                                });
                        }, 50);
                    }
                });
            }
            if (o.escClose) {
                d.bind('keydown.' + id, function (e) {
                    if (e.which == 27) {  //escape
                        close();
                    }
                });
            }
        };

        function unbindEvents() {
            if (!o.scrollBar) {
                $('html').css('overflow', 'auto');
            }
            $('.b-modal.' + id).unbind('click');
            d.unbind('keydown.' + id);
            $w.unbind('.' + id).data('bPopup', ($w.data('bPopup') - 1 > 0) ? $w.data('bPopup') - 1 : null);

            $popup.remove(); // JSZ fix - undelegate is deprecated
            //$popup.undelegate('.bClose, .' + o.closeClass, 'click.' + id, close).data('bPopup', null);
        };

        function doTransition(open) {
            switch (open ? o.transition : o.transitionClose || o.transition) {
                case "slideIn":
                    animate({
                        left: open ? getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle)) : d.scrollLeft() - (width || $popup.outerWidth(true)) - buffer
                    });
                    break;
                case "slideBack":
                    animate({
                        left: open ? getLeftPos(!(!o.follow[0] && fixedHPos || fixedPosStyle)) : d.scrollLeft() + wW + buffer
                    });
                    break;
                case "slideDown":
                    animate({
                        top: open ? getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle)) : d.scrollTop() - (height || $popup.outerHeight(true)) - buffer
                    });
                    break;
                case "slideUp":
                    animate({
                        top: open ? getTopPos(!(!o.follow[1] && fixedVPos || fixedPosStyle)) : d.scrollTop() + wH + buffer
                    });
                    break;
                default:
                    //Hardtyping 1 and 0 to ensure opacity 1 and not 0.9999998
                    $popup.stop().fadeTo(o.speed, open ? 1 : 0, function () { onCompleteCallback(open); });
            }

            function animate(css) {
                $popup
                    .css({ display: 'block', opacity: 1 })
                    .animate(css, o.speed, o.easing, function () { onCompleteCallback(open); });
            };
        };


        function onCompleteCallback(open) {
            if (open) {
                bindEvents();
                triggerCall(callback);
                if (o.autoClose) {
                    setTimeout(close, o.autoClose);
                }
            } else {
                $popup.hide();
                triggerCall(o.onClose);
                if (o.loadUrl) {
                    o.contentContainer.empty();
                    $popup.css({ height: 'auto', width: 'auto' });
                }
            }
        };

        function getLeftPos(includeScroll) {
            return includeScroll ? hPos + d.scrollLeft() : hPos;
        };

        function getTopPos(includeScroll) {
            return includeScroll ? vPos + d.scrollTop() : vPos;
        };

        function triggerCall(func) {
            $.isFunction(func) && func.call($popup);
        };

        function calPosition() {
            vPos = fixedVPos ? o.position[1] : Math.max(0, ((wH - $popup.outerHeight(true)) / 2) - o.amsl)
                , hPos = fixedHPos ? o.position[0] : (wW - $popup.outerWidth(true)) / 2
                , inside = insideWindow();
        };

        function insideWindow() {
            return wH > $popup.outerHeight(true) && wW > $popup.outerWidth(true);
        };

        function windowHeight() {
            return w.innerHeight || $w.height();
        };

        function windowWidth() {
            return w.innerWidth || $w.width();
        };
    };

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // DEFAULT VALUES
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    $.fn.bPopup.defaults = {
        amsl: 50
        , appending: true
        , appendTo: 'body'
        , autoClose: false
        , closeClass: 'b-close'
        , content: 'ajax' // ajax, iframe or image
        , contentContainer: false
        , easing: 'swing'
        , escClose: true
        , follow: [true, true] // x, y
        , followEasing: 'swing'
        , followSpeed: 500
        , iframeAttr: 'scrolling="no" frameborder="0"'
        , loadCallback: false
        , loadData: false
        , loadUrl: false
        , modal: true
        , modalClose: true
        , modalColor: '#000'
        , onClose: false
        , onOpen: false
        , opacity: 0.7
        , position: ['auto', 'auto'] // x, y,
        , positionStyle: 'absolute'// absolute or fixed
        , scrollBar: true
        , speed: 250 // open & close speed
        , transition: 'fadeIn' //transitions: fadeIn, slideDown, slideIn
        , transitionClose: false
        , zIndex: 9997 // popup gets z-index 9999, modal overlay 9998
    };
})(jQuery);


/**
* jQuery.ScrollTo - Easy element scrolling using jQuery.
* Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Dual licensed under MIT and GPL.
* Date: 5/25/2009
* @author Ariel Flesler
* @version 1.4.2
*
* http://flesler.blogspot.com/2007/10/jqueryscrollto.html
*/
; (function (d) { var k = d.scrollTo = function (a, i, e) { d(window).scrollTo(a, i, e); }; k.defaults = { axis: 'xy', duration: parseFloat(d.fn.jquery) >= 1.3 ? 0 : 1 }; k.window = function (a) { return d(window)._scrollable(); }; d.fn._scrollable = function () { return this.map(function() { var a = this, i = !a.nodeName || d.inArray(a.nodeName.toLowerCase(), ['iframe', '#document', 'html', 'body']) != -1; if (!i) return a; var e = (a.contentWindow || a).document || a.ownerDocument || a; return d.browser.safari || e.compatMode == 'BackCompat' ? e.body : e.documentElement; }); }; d.fn.scrollTo = function (n, j, b) { if (typeof j == 'object') { b = j; j = 0; } if (typeof b == 'function') b = { onAfter: b }; if (n == 'max') n = 9e9; b = d.extend({}, k.defaults, b); j = j || b.speed || b.duration; b.queue = b.queue && b.axis.length > 1; if (b.queue) j /= 2; b.offset = p(b.offset); b.over = p(b.over); return this._scrollable().each(function() { var q = this, r = d(q), f = n, s, g = { }, u = r.is('html,body'); switch (typeof f) { case 'number': case 'string': if ( /^([+-]=)?\d+(\.\d+)?(px|%)?$/ .test(f)) { f = p(f); break; } f = d(f, this); case 'object': if (f.is || f.style) s = (f = d(f)).offset(); } d.each(b.axis.split(''), function(a, i) { var e = i == 'x' ? 'Left' : 'Top', h = e.toLowerCase(), c = 'scroll' + e, l = q[c], m = k.max(q, i); if (s) { g[c] = s[h] + (u ? 0 : l - r.offset()[h]); if (b.margin) { g[c] -= parseInt(f.css('margin' + e)) || 0; g[c] -= parseInt(f.css('border' + e + 'Width')) || 0; } g[c] += b.offset[h] || 0; if (b.over[h]) g[c] += f[i == 'x' ? 'width' : 'height']() * b.over[h]; } else { var o = f[h]; g[c] = o.slice && o.slice(-1) == '%' ? parseFloat(o) / 100 * m : o; } if ( /^\d+$/ .test(g[c])) g[c] = g[c] <= 0 ? 0 : Math.min(g[c], m); if (!a && b.queue) { if (l != g[c]) t(b.onAfterFirst); delete g[c]; } }); t(b.onAfter); function t(a) { r.animate(g, j, b.easing, a && function() { a.call(this, n, b); }); } }).end(); }; k.max = function (a, i) { var e = i == 'x' ? 'Width' : 'Height', h = 'scroll' + e; if (!d(a).is('html,body')) return a[h] - d(a)[e.toLowerCase()](); var c = 'client' + e, l = a.ownerDocument.documentElement, m = a.ownerDocument.body; return Math.max(l[h], m[h]) - Math.min(l[c], m[c]); }; function p(a) { return typeof a == 'object' ? a : { top: a, left: a }; } })(jQuery);
;
