/* Minification failed. Returning unminified contents.
(8220,21-22): run-time error JS1195: Expected expression: )
(8220,24-25): run-time error JS1195: Expected expression: >
(8222,10-11): run-time error JS1195: Expected expression: ,
(8236,5-6): run-time error JS1002: Syntax error: }
(8238,68-69): run-time error JS1004: Expected ';': {
(8269,17-18): run-time error JS1195: Expected expression: )
(8269,20-21): run-time error JS1195: Expected expression: >
(8272,5-6): run-time error JS1002: Syntax error: }
(8275,91-92): run-time error JS1004: Expected ';': {
(8277,6-7): run-time error JS1195: Expected expression: )
(8298,12-13): run-time error JS1195: Expected expression: )
(8298,14-15): run-time error JS1004: Expected ';': {
(8608,3-4): run-time error JS1195: Expected expression: )
(8608,4-5): run-time error JS1004: Expected ';': )
(8609,25-27): run-time error JS1197: Too many errors. The file might not be a JavaScript file: ||
 */
window.OGT = window.OGT || {};

window.OGT.FilterTextArea = (function () {

    var lang = {
        tokenTypes: {
            stop: "Hållplats",
            stopPoint: "Hållplatsläge",
            trafficType: "Trafiktyp",
            line: "Linje"

        }
    },

    KeyCodes = {
        Left: 37,
        Right: 39,
        Down: 40,
        Up: 38,
        Enter: 13,
        Esc: 27,
        BackSpace: 8,
        Delete: 46
    },

    MAX_SUGGESTION_COUNT_IN_CATEGORY = 25;


    function indexIsGreaterThan(otherToken) {
        return this.index > otherToken.index;
    }

    var TokenClasses = (function () {

        var FullTokenBase = (function () {

            function T() {
            }

            T.prototype.postInit = function (settings) {
                this.index = settings.index;
                this.$dom = $('<div class="tag ' + this.getOuterCssClass() + '">'
                    + this.getAdditionalHtml() + '<div class="augment__text label">' + this.getText() + '<div class="close float-right"><i class="ogt-close-16 icon--xsmall"></i></div></div></div>');
            };

            T.prototype.getLabel = function () {
                return lang.tokenTypes[this.langKey] || "";
            };

            T.prototype.isGreaterThan = function (otherToken) {
                return otherToken.getText() < this.getText();
            };

            T.prototype.equals = function (otherToken) {
                return this.constructor == otherToken.constructor && this.getText() == otherToken.getText();
            };

            return T;

        }());


        var ShieldTokenBase = (function () {

            function T() {
            }
            OGT.Util.extend(T, FullTokenBase);

            var makeShieldHtml = function (shieldOptions) {

                shieldOptions.scale = 0.7;
                shieldOptions.coloursAndHighlights = [{ colour: OGT.CONSTANTS.COLOURS.DEFAULT_RED }];

                var icon = OGT.Util.OGT.Icon.get(shieldOptions);

                return icon.getImgHtml({
                    cssClass: "icon--tiny-map shield"
                });

            };

            T.prototype.getText = function () {
                return "";
            };

            T.prototype.getAdditionalHtml = function () {

                return makeShieldHtml(this.shieldSettings);

            };

            return T;

        }());


        var EverythingToken = (function () {

            function T(settings) {

                this.label = settings.label;
                this.text = settings.term;

                this.postInit.apply(this, arguments);

            }
            OGT.Util.extend(T, FullTokenBase);

            T.prototype.getText = function () {
                return this.text;
            };

            T.prototype.getLabel = function () {
                return this.label;
            };

            T.prototype.getOuterCssClass = function () {
                return 'everything';
            };

            T.prototype.getAdditionalHtml = function () {
                return '';
            };

            return T;

        }());


        var StopToken = (function () {

            function T(settings) {

                this.stop = settings.stop;

                this.shieldSettings = {
                    type: "stop"
                };

                this.postInit.apply(this, arguments);

            }
            OGT.Util.extend(T, ShieldTokenBase);

            T.prototype.langKey = "stop";

            T.prototype.isGreaterThan = indexIsGreaterThan;

            T.prototype.getText = function () {
                return this.stop.PlaceName;
            };

            T.prototype.getOuterCssClass = function () {
                return 'stop';
            };

            T.prototype.equals = function (otherToken) {
                return T.superclass.equals.apply(this, arguments) && this.stop.Id == otherToken.stop.Id;
            };

            return T;

        }());

        var StopPointToken = (function () {

            function T(settings) {

                var stopPoint = this.stopPoint = settings.stopPoint;

                this.shieldSettings = {
                    type: "stopPoint",
                    stopPointAlias: stopPoint.OgtStopPointData.stopPointAlias
                };

                this.postInit.apply(this, arguments);

            }
            OGT.Util.extend(T, ShieldTokenBase);

            T.prototype.langKey = "stopPoint";

            T.prototype.getText = function () {

                return OGT.Util.OGT.StopPointAlias.textFrom({
                    stopPointAlias: this.stopPoint.OgtStopPointData.stopPointAlias,
                    upperCase: true,
                    alwaysAcceptAlias: true
                });

            };

            T.prototype.getOuterCssClass = function () {
                return 'stop-point';
            };

            T.prototype.isGreaterThan = function (otherToken) {
                return otherToken.stopPoint.OgtStopPointData.stopPointAlias < this.stopPoint.OgtStopPointData.stopPointAlias;
            };

            T.prototype.equals = function (otherToken) {
                if (!T.superclass.equals.apply(this, arguments)) {
                    return false;
                }
                var thisStopPoint = this.stopPoint,
                    otherStopPoint = otherToken.stopPoint;
                return thisStopPoint.Id == otherStopPoint.Id && thisStopPoint.OgtStopPointData.stopPointId == otherStopPoint.OgtStopPointData.stopPointId;
            };

            return T;

        }());


        var TrafficTypeToken = (function () {

            function T(settings) {

                this.trafficTypeGroup = settings.trafficTypeGroup;

                this.postInit.apply(this, arguments);

            }
            OGT.Util.extend(T, FullTokenBase);

            T.prototype.langKey = "trafficType";

            T.prototype.getText = function () {
                return this.trafficTypeGroup.label;
            };

            T.prototype.getOuterCssClass = function () {
                return 'traffic-type-group';
            };

            T.prototype.getAdditionalHtml = function () {
                return '<div class="augment traffic-type-group__icon ' + this.trafficTypeGroup.OgtTrafficTypeGroupCssClass + ' selected">';
            };

            return T;

        }());


        var LineToken = (function () {

            function T(settings) {

                this.line = settings.line;

                this.postInit.apply(this, arguments);

            }
            OGT.Util.extend(T, FullTokenBase);

            T.prototype.getLabel = function () {
                return OGT.Util.OGT.Format.lineNameWithNameClashDefiningAttribute(this.line.Ogt);
            };

            T.prototype.getText = function () {
                return "";
            };

            T.prototype.getOuterCssClass = function () {
                return 'line';
            };

            T.prototype.getAdditionalHtml = function () {
                return OGT.Util.OGT.Format.line(this.line, {
                    noLink: true,
                    useTextOnGraphicalIcon: true,
                    icon: {
                        cssClass: "augment"
                    }
                });
            };

            T.prototype.equals = function (otherToken) {
                return T.superclass.equals.apply(this, arguments) && this.line.LineNrReal == otherToken.line.LineNrReal;
            };

            T.prototype.isGreaterThan = indexIsGreaterThan;

            return T;

        }());

        return {
            Base: {
                FullTokenBase: FullTokenBase,
                ShieldTokenBase: ShieldTokenBase
            },

            EverythingToken: EverythingToken,
            StopToken: StopToken,
            StopPointToken: StopPointToken,
            TrafficTypeToken: TrafficTypeToken,
            LineToken: LineToken
        };

    }());





    var SuggestionSourceClasses = (function () {

        function getAllSentences(terms) {

            return terms.reduceRight(function (collected, term) {

                var len = collected.length,
                    sentence = term.toLowerCase() + (len ? collected[len - 1] : "");

                collected.push(sentence);

                return collected;

            }, []);

        }

        function isFullMatchOrWordMatch(needle, haystack) {
            return haystack.indexOf(needle) == 0 || haystack.split(" ").some(function (haystackWord) { return haystackWord.indexOf(needle) == 0; });
        }

        var Base = (function () {

            function B() {

            }

            B.prototype.cancel = function () {
            };

            return B;

        }());

        var Everything = (function () {

            function S(options) {
                options = options || {};
                this.term = options.term || "All";
                this.label = options.label || "";
            }
            OGT.Util.extend(S, Base);

            S.prototype.words = function () {
                return ["allt", "alla", "all", "everything", "allting", this.term.toLowerCase()];
            }

            S.prototype._getReturnObject = function (usedTokens, index) {

                return {
                    score: 1000,
                    index: index,
                    TokenClass: TokenClasses.EverythingToken,
                    usedTokens: usedTokens,
                    term: this.term,
                    label: this.label
                };

            }

            S.prototype.fetchUnguided = function (callback) {

                callback([this._getReturnObject(0, 0)], true);

            };

            S.prototype.fetch = function (terms, callback) {

                var sentences = getAllSentences(terms),
                    results = [];

                this.words().some(function (word, loopIndex) {

                    var name = word.toLowerCase(),
                        foundTokenIndex = sentences.findIndexRight(function (sentence) {
                            return isFullMatchOrWordMatch(sentence, name);
                        });

                    if (foundTokenIndex != -1) {
                        results.push(this._getReturnObject(foundTokenIndex + 1, loopIndex));
                        return true;
                    }
                    return false;

                }, this);

                callback(results);

            };

            return S;

        }());



        var Stop = (function () {

            function S(presetStops, filterUnguidedStopsFunction) {
                this.presetStops = presetStops;
                this.filterUnguidedStopsFunction = filterUnguidedStopsFunction || function () { return true; };
            }
            OGT.Util.extend(S, Base);

            function getReturnObject(stop, usedTokens, index) {

                return {
                    stop: stop,
                    score: 10,
                    index: index,
                    TokenClass: TokenClasses.StopToken,
                    usedTokens: usedTokens
                };

            }

            S.prototype._getApplicableStops = function () {
                var presetStops = this.presetStops;
                return presetStops.length < 2 ? [] : presetStops;
            };

            S.prototype.fetchUnguided = function (callback) {

                callback(this._getApplicableStops().filter(this.filterUnguidedStopsFunction).slice(0, MAX_SUGGESTION_COUNT_IN_CATEGORY).map(function (stop, loopIndex) {
                    return getReturnObject(stop, 0, loopIndex);
                }));

            };

            S.prototype.fetch = function (terms, callback) {

                var sentences = getAllSentences(terms),
                    results = [];

                this._getApplicableStops().forEach(function (stop, loopIndex) {

                    var name = stop.PlaceName.toLowerCase(),
                        foundTokenIndex = sentences.findIndexRight(function (sentence) {
                            return isFullMatchOrWordMatch(sentence, name);
                        });

                    if (foundTokenIndex != -1 && results.length < MAX_SUGGESTION_COUNT_IN_CATEGORY) {
                        results.push(getReturnObject(stop, foundTokenIndex + 1, loopIndex));
                    }

                });

                callback(results);

            };

            return S;

        }());

        var StopPoint = (function () {

            function S(presetStopPoints) {
                this.presetStopPoints = presetStopPoints;
            }
            OGT.Util.extend(S, Base);

            function getReturnObject(stopPoint, usedTokens, index) {

                return {
                    stopPoint: stopPoint,
                    score: 20,
                    index: index,
                    TokenClass: TokenClasses.StopPointToken,
                    usedTokens: usedTokens
                };

            }

            S.prototype._getApplicableStopPoints = function () {
                var presetStopPoints = this.presetStopPoints;
                return presetStopPoints.length < 2 ? [] : presetStopPoints;
            };

            S.prototype.fetchUnguided = function (callback) {

                callback(this._getApplicableStopPoints().slice(0, MAX_SUGGESTION_COUNT_IN_CATEGORY).map(function (stopPoint, loopIndex) {
                    return getReturnObject(stopPoint, 0, loopIndex);
                }));

            };

            S.prototype.fetch = function (terms, callback) {

                var sentences = getAllSentences(terms),
                    results = [];

                this._getApplicableStopPoints().forEach(function (stopPoint, loopIndex) {

                    var stopPointAlias = stopPoint.OgtStopPointData.stopPointAlias,
                        stopPointAliasLowerCase = stopPointAlias.toLowerCase(),
                        fullTextLowerCase = OGT.Util.OGT.StopPointAlias.textFrom({ stopPointAlias: stopPointAlias, upperCase: true }).toLowerCase(),
                        foundTokenIndex = sentences.findIndexRight(function (sentence) {
                            return isFullMatchOrWordMatch(sentence, stopPointAliasLowerCase) || isFullMatchOrWordMatch(sentence, fullTextLowerCase);
                        });



                    if (foundTokenIndex != -1 && results.length < MAX_SUGGESTION_COUNT_IN_CATEGORY) {
                        results.push(getReturnObject(stopPoint, foundTokenIndex + 1, loopIndex));
                    }

                });

                callback(results);

            };

            return S;

        }());

        var TrafficType = (function () {

            function S(presetTrafficTypeGroups) {
                this.presetTrafficTypeGroups = presetTrafficTypeGroups;
            }
            OGT.Util.extend(S, Base);

            function getReturnObject(trafficTypeGroup, usedTokens) {

                return {
                    trafficTypeGroup: trafficTypeGroup,
                    score: 30,
                    TokenClass: TokenClasses.TrafficTypeToken,
                    usedTokens: usedTokens
                };

            }

            S.prototype._getApplicableTrafficTypeJourneySearchGroups = function () {

                var presetTrafficTypeGroups = this.presetTrafficTypeGroups;
                return presetTrafficTypeGroups.length < 2 ? [] : presetTrafficTypeGroups;

            };

            S.prototype.fetchUnguided = function (callback) {

                callback(this._getApplicableTrafficTypeJourneySearchGroups().slice(0, MAX_SUGGESTION_COUNT_IN_CATEGORY).map(function (trafficTypeGroup, loopIndex) {
                    return getReturnObject(trafficTypeGroup, 0, loopIndex);
                }));

            };


            S.prototype.fetch = function (terms, callback) {

                var sentences = getAllSentences(terms),
                    results = [];

                this._getApplicableTrafficTypeJourneySearchGroups().forEach(function (trafficTypeGroup, loopIndex) {

                    var ttgLabel = trafficTypeGroup.label.toLowerCase(),
                        foundTokenIndex = sentences.findIndexRight(function (sentence) {
                            return isFullMatchOrWordMatch(sentence, ttgLabel);
                        });
                    if (foundTokenIndex != -1 && results.length < MAX_SUGGESTION_COUNT_IN_CATEGORY) {
                        results.push(getReturnObject(trafficTypeGroup, foundTokenIndex + 1, loopIndex));
                    }
                });

                callback(results);

            };

            return S;

        }());

        var Line = (function () {

            function S(presetLines) {

                this.presetLines = presetLines;

            }
            OGT.Util.extend(S, Base);

            function getReturnObject(line, usedTokens, index) {

                return {
                    line: line,
                    score: 18,
                    index: index,
                    TokenClass: TokenClasses.LineToken,
                    usedTokens: usedTokens
                };
            }

            S.prototype._getApplicableLines = function () {
                var presetLines = this.presetLines;
                return presetLines.length < 2 ? [] : presetLines;

            };

            S.prototype.fetchUnguided = function (callback) {

                callback(this._getApplicableLines().slice(0, MAX_SUGGESTION_COUNT_IN_CATEGORY).map(function (line, loopIndex) {
                    return getReturnObject(line, 0, loopIndex);
                }));

            };

            S.prototype.fetch = function (terms, callback) {

                var sentences = getAllSentences(terms),
                    results = [];

                this._getApplicableLines().forEach(function (line, loopIndex) {
                    var name = line.LineName.toLowerCase(),
                        namePreformatted = line.Ogt.OgtLineNamePreformatted.toLowerCase(),
                        lineNrReal = line.LineNrReal,
                        foundTokenIndex = sentences.findIndexRight(function (sentence) {
                            return name.indexOf(sentence) == 0 ||
                                namePreformatted.indexOf(sentence) == 0 ||
                                lineNrReal == sentence;
                        });
                    if (foundTokenIndex != -1 && results.length < MAX_SUGGESTION_COUNT_IN_CATEGORY) {
                        results.push(getReturnObject(line, foundTokenIndex + 1, loopIndex));
                    }
                });

                callback(results);

            };

            return S;

        }());


        return {
            Everything: Everything,
            Stop: Stop,
            StopPoint: StopPoint,
            TrafficType: TrafficType,
            Line: Line
        };

    }());



    var Suggestioner = (function () {

        var TEMPLATE = '<div class="suggestion clearfix"><div class="suggestion__item"></div><div class="suggestion__label"></div></div>',
            SUGGESTION_ACTIVE_CLASS = 'active',
            INLINE_SUGGESTIONER_CLASS = 'inline';

        function S($target, parentFilterTextArea, sources, useInlineSuggestioner) {

            this.$target = $target;
            this.target = $target.get(0);
            this.parentFilterTextArea = parentFilterTextArea;

            if (useInlineSuggestioner) {
                $target.addClass(INLINE_SUGGESTIONER_CLASS);
            }

            this.sources = sources;

            $target.on("mousedown mouseup touchstart touchend click", function (event) {
                event.stopPropagation();
            });

        }

        S.prototype.guided = function (searchString) {

            this.clear();

            var terms = searchString.split(' ');

            this.sources.forEach(function (source) {

                var that = this;

                source.fetch(terms, function (suggestions) {

                    that.addSuggestions(suggestions);

                });


            }, this);

            this.$target.show().scrollTop(0);

        };

        S.prototype.unguided = function () {

            this.clear();

            this.sources.forEach(function (source) {

                var that = this;

                source.fetchUnguided(function (suggestions, oneIsOkay) {

                    if ((oneIsOkay ? 0 : 1) < suggestions.length) {
                        that.addSuggestions(suggestions);
                    }

                });

            }, this);

            this.$target.show().scrollTop(0);

        };

        S.prototype.addSuggestions = function (suggestions) {

            suggestions.slice(0, MAX_SUGGESTION_COUNT_IN_CATEGORY).forEach(function (suggestion) {
                this.addSuggestion(suggestion);
            }, this);

        };

        S.prototype.addSuggestion = function (suggestion) {

            var that = this,
                token = new suggestion.TokenClass(suggestion),
                augmenterTokens = this.parentFilterTextArea.currentAugmenterTokens;

            if (!augmenterTokens.some(function (otherToken) {
                return token.equals(otherToken);
            })) {

                var $dom = $(TEMPLATE),
                    dom = $dom.get(0);

                dom.token = token;
                dom.suggestion = suggestion;

                $dom.find(".suggestion__item").append(token.$dom);
                $dom.find(".suggestion__label").append(token.getLabel());
                $dom.on("click", function (event) {
                    event.stopPropagation();
                    that.apply(token, suggestion.usedTokens);
                });

                OGT.Util.Splice.spliceInElementSorted(this.target, dom, function (toInsertElement, otherElement) {

                    var toInsertSuggestion = toInsertElement.suggestion,
                        otherSuggestion = otherElement.suggestion,
                        toInsertScore = toInsertSuggestion.score,
                        otherScore = otherSuggestion.score,
                        toInsertToken = toInsertElement.token,
                        otherToken = otherElement.token;

                    if (toInsertScore < otherScore) {
                        return true;
                    }

                    if (toInsertScore == otherScore) {

                        if (toInsertToken.constructor == otherToken.constructor) {
                            if (toInsertToken.isGreaterThan(otherToken)) {
                                return true;
                            }
                        } else {
                            if (otherToken.getLabel() < toInsertToken.getLabel()) {
                                return true;
                            }
                        }

                    }

                    return null;

                });

            }

        };


        S.prototype.useTheKeyEvent = function (event) {

            var keyCode = event.keyCode;

            if (keyCode == KeyCodes.Up || keyCode == KeyCodes.Down || keyCode == KeyCodes.Esc || keyCode == KeyCodes.Enter) {

                if (this.active()) {

                    if (keyCode == KeyCodes.Esc) {
                        this.clear();
                        event.preventDefault();
                        return true;
                    }

                    var $target = this.$target,
                        $suggestions = $target.children();
                    if ($suggestions.length) {

                        event.preventDefault();

                        if (event.type == "keydown") {

                            var $active = $suggestions.filter("." + SUGGESTION_ACTIVE_CLASS),
                                $newActive = null;

                            if (keyCode == KeyCodes.Enter) {
                                var $hit;
                                if ($active.length) {
                                    $hit = $active;
                                } else {
                                    $hit = $suggestions.first();
                                }
                                $hit.click();
                                return true;
                            }

                            if (keyCode == KeyCodes.Up) {
                                $newActive = $active.prev();
                                if (!$newActive.length) {
                                    $newActive = $suggestions.last();
                                }

                            } else if (keyCode == KeyCodes.Down) {
                                $newActive = $active.next();
                                if (!$newActive.length) {
                                    $newActive = $suggestions.first();
                                }
                            }

                            if ($newActive && $newActive.length) {
                                $suggestions.removeClass(SUGGESTION_ACTIVE_CLASS);
                                $newActive.addClass(SUGGESTION_ACTIVE_CLASS);

                                var unitHeight = $newActive.height() + 1;
                                $target.scrollTop(($newActive.position().top - $suggestions.position().top) - ($target.height() - unitHeight) / 2);
                            }

                        }
                        return true;

                    }

                }

            }

            return false;

        };

        S.prototype.apply = function (token, replaceTokensCount) {

            var firstTokenIndex = this.tokenIndex + 1 - replaceTokensCount;

            this.parentFilterTextArea.addToken(
                token,
                {
                    isInteractive: true,
                    doSetCaretPosition: true,
                    firstTokenIndex: firstTokenIndex,
                    replaceTokensCount: replaceTokensCount
                });

            this.parentFilterTextArea.input.value = "";

            this.clear();

        };

        S.prototype.clear = function () {

            this.sources.forEach(function (source) {
                source.cancel();
            });
            this.$target.hide().empty();

        };

        S.prototype.active = function () {
            return this.$target.is(":visible");
        }


        return S;

    }());

    var FilterTextArea = (function () {

        function F2(options) {

            this.changeListeners = [];

            var that = this,
                $target = options.$target,
                suggestionSources = options.suggestionSources,
                useInlineSuggestioner = options.useInlineSuggestioner;

            this.$tagTarget = $('<div class="tag-target tags clearfix"></div>').appendTo($target);

            var $inputWrapper = $('<label class="state-visible-hide"></label>').appendTo($target),
                $input = this.$input = $('<input type="text" placeholder="' + $target.data('placeholder') + '" autocomplete="off" spellcheck="false" />').appendTo($inputWrapper);

            $target.show();

            var $suggester = $('<div class="suggester tags"></div>').appendTo($target);

            this.suggester = new Suggestioner($suggester, this, suggestionSources, useInlineSuggestioner);

            this.currentAugmenterTokens = [];
            this.input = $input.get(0);

            $target.on("click", function (event) {

                event.stopPropagation();

                if (OGT.Util.OGT.getLayoutWidth() != OGT.Util.OGT.layoutWidths.mobile) {
                    that.suggester.unguided();
                }

            });

            $input.on("focus mouseup touchend", function (event) {

                event.stopPropagation();

                if (!this.readOnly) {
                    that._handleFocus(event);
                    $target.addClass("has-focus");
                }
            });

            $input.on("keydown", function (event) {
                that.handleKeyDown(event);
            });

            $input.on("keyup", function (event) {
                that.handleKeyUp(event);
            });


            $input.on("input cut paste drop", function (event) {
                that._handleInput(event);
                $target.addClass("typing");
            });

            $(document).on("click", function (event) {
                event.stopPropagation();
                $input.blur();
                that.closeSuggestions();
            });

        }

        F2.prototype._handleFocus = function (event) {

            if (event.view) {
                if (OGT.Util.OGT.getLayoutWidth() != OGT.Util.OGT.layoutWidths.mobile) {
                    this.suggester.unguided();
                }
            }

        };

        F2.prototype.closeSuggestions = function () {
            this.input.value = "";
            this.suggester.clear();
        };

        F2.prototype.clear = function (options) {

            var augmenterTokens = this.currentAugmenterTokens;
            augmenterTokens.length = 0;
            this.render(options);

        };

        F2.prototype.addToken = function (insertToken, options) {

            options = options || {};

            var augmenterTokens = this.currentAugmenterTokens;

            augmenterTokens.push(insertToken);

            this.render({ isInteractive: options.isInteractive });

        };

        F2.prototype.handleKeyDown = function (event) {

            var suggester = this.suggester;

            if (suggester.useTheKeyEvent(event)) {
                return;
            }

            var keyCode = event.keyCode;

            if (keyCode == KeyCodes.BackSpace || keyCode == KeyCodes.Delete || keyCode == KeyCodes.Left || keyCode == KeyCodes.Right || keyCode == KeyCodes.Up || keyCode == KeyCodes.Down) {

                suggester.clear();

                if (keyCode == KeyCodes.Up) {

                    this.suggester.unguided();
                    return;

                }

                if (keyCode == KeyCodes.Down) {

                    this.suggester.unguided();
                    return;

                }

            }

        };

        F2.prototype.handleKeyUp = function (event) {

            var suggester = this.suggester;

            if (suggester.useTheKeyEvent(event)) {
                return;
            }

            var keyCode = event.keyCode;

            if (keyCode == KeyCodes.Up || keyCode == KeyCodes.Down) {

                suggester.clear();

                var caretPosition = this.getCaretPosition();
                if (caretPosition === false) {
                    return;
                }

                switch (keyCode) {
                    case KeyCodes.Up:
                        this.setCaretPosition(this.findNextTokenBoundry(caretPosition, "closest", false), event);
                        break;
                    case KeyCodes.Down:
                        this.setCaretPosition(this.findNextTokenBoundry(caretPosition, "closest", false), event);
                        break;

                }

            }

        };

        F2.prototype._handleInput = function (event) {

            var suggester = this.suggester,
                $input = this.$input,
                currentInputValue = $input.val();

            if (0 < currentInputValue.length) {
                suggester.guided(currentInputValue);
            }

        };

        F2.prototype._remove = function (token, options) {

            var currentAugmenterTokens = this.currentAugmenterTokens,
                index = currentAugmenterTokens.indexOf(token);
            if (-1 < index) {
                currentAugmenterTokens.splice(index, 1);
            }
            this.render(options);

        };

        F2.prototype.render = function (options) {

            options = options || {};

            var that = this,
                $tagTarget = this.$tagTarget,
                isInteractive = options.isInteractive;

            $tagTarget.empty();

            this.currentAugmenterTokens.forEach(function (token) {
                var $dom = token.$dom;
                $dom.on("click", function (event) {
                    event.stopPropagation();
                    that._remove(token, { isInteractive: true });
                });
                $tagTarget.append($dom);
            });

            if (isInteractive) {
                this.changeListeners.forEach(function (listener) {
                    listener(isInteractive);
                });
            }

        };




        F2.prototype.readonly = function (doReadonly) {

            if (doReadonly) {
                this.suggester.clear();
            }
            this.$input.prop("readonly", doReadonly);

        };

        F2.prototype.listenForChange = function (callback) {

            this.changeListeners.push(callback);

        };

        F2.prototype.onClick = function (callback) {

            this.$input.click(callback);

        };

        return F2;

    }());

    function initAndGet(options) {
        return new FilterTextArea(options);
    }


    return {
        initAndGet: initAndGet,
        canBeUsed: function () {
            var browserDetect = OGT.Util.BrowserDetect;
            return !browserDetect.isIe() || 8 < browserDetect.getVersion();
        },
        SuggestionSourceClasses: SuggestionSourceClasses,
        TokenClasses: TokenClasses
    };

}());
;
window.OGT = window.OGT || {};


window.OGT.UpcomingDepartures = (function () {

    var lang = {
        reload: "Vänligen ladda om sidan",
        clickForVehicleOnMap: "Klicka för att visa fordonet på kartan.",
        interuption: "Tillfälligt avbrott",
        pageOfPages: "Sida {0}/{1}",
        explanationRealtime: "Tiden är en prognos",
        explanationSchedule: "Tidtabellstid",
        departures: "Kommande avgångar",
        showInMap: "Se bussen i kartan",
        omittedTime: "Invänta tid",
        towards: "mot",
        position: "Läge"
    },

        DEVIATION_ROW_HEIGHT = 1.9,
        DEVIATION_ROWS = 1,
        DEVIATION_FIELD = "ShortText",

        fillUpcomingDepartures = (function () {

            var columnTemplate = '<div class="result__column" style="width: {0}%; margin-left: {1}%;" role="marquee"></div>',

                upcomingDepartureHeaderContentTemplate = [
                    '{0}',
                    '<div class="item text notranslate">' + lang.towards + ' {1}</div>',
                    '<div class="item details">' + lang.position + ' {2}</div>',
                    '<div class="item time">{6}<span class="time__label {7}">{3}</span> <i class="ogt-{4}-16 timingicon" title="{5}" aria-hidden="true">&nbsp;</i></div>'
                ].join(''),
                upcomingDepartureContentContentTemplate = [
                    '<button class="openinmap button slim rounded" title="{0}" aria-hidden="true" tabindex="-1">' + lang.showInMap + '</button>'
                ].join(''),

                newDayTemplate = '<div class="accordion_list__item departure__item clearfix"><div class="separator--dom">{0}</div></div>',
                deviationTemplate = '<div class="deviation text"></div>',
                COLUMN_DISTANCE_PERCENT = 2,

                firstLoadVersionNumber = null;

            return function (options) {

                function setInteruption() {
                    $container.addClass("interruption");
                }

                var $container = options.$container,
                    $list = options.$list,
                    $deviationsWindow = options.$deviationsTarget,
                    $hideWhenDeviations = options.$hideWhenDeviations,
                    $deviationsSlider = $deviationsWindow && $deviationsWindow.find(".deviations__slider--container"),

                    ajaxRequest = $container.data("ajaxRequest"),

                    stopId = options.stopId,
                    date = options.date || "",
                    lines = options.lines || [],
                    stopPoints = options.stopPoints || [],
                    trafficTypeGroups = options.trafficTypeGroups || [],
                    sortOrder = options.sortOrder,
                    delay = options.delay || 0,
                    departuresPerColumnCount = options.departuresPerColumnCount || 4,
                    columnsPerPageCount = options.columnsPerPageCount || 1,
                    pagesCount = options.pagesCount || 1,

                    useDaySeparator = options.useDaySeparator,

                    nonInteractive = options.nonInteractive,
                    noComingStopsFunctionality = nonInteractive || options.noComingStopsFunctionality,

                    successCallback = options.successCallback,
                    failureCallback = options.failureCallback,

                    animationCompleteCallback = options.animationCompleteCallback,
                    animationStepCallback = options.animationStepCallback,

                    $messageTarget = options.$messageTarget,

                    trackingPointFragment = options.trackingPointFragment;

                if (ajaxRequest) {
                    ajaxRequest.abort();
                }

                $container.data("ajaxRequest", OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.STOP_DEPARTURES + "/departures", {

                    data: {
                        stopAreaId: stopId,
                        date: date,
                        delay: delay,
                        maxNumberOfResultPerColumn: departuresPerColumnCount,
                        columnsPerPageCount: columnsPerPageCount,
                        pagesCount: pagesCount,
                        lines: lines.map(function (line) { return line.LineNrReal; }).join(','),
                        trafficTypes: trafficTypeGroups.map(function (trafficTypeGroup) { return trafficTypeGroup.shortKey; }).join(','),
                        stopPoints: stopPoints.map(function (stopPoint) { return stopPoint.OgtStopPointData.stopPointAliasId; }).join(','),
                        sortOrder: sortOrder,
                        useDaySeparator: useDaySeparator
                    },

                    failureCallback: function () {

                        $container.data("ajaxRequest", null);

                        setInteruption();
                        if (failureCallback) {
                            failureCallback.apply(this, arguments);
                        }

                        if (trackingPointFragment) {
                            OGT.Tracking.DeparturesSearchEngine.DeparturesSearch[trackingPointFragment + "Failure"]();
                        }

                    },

                    successCallback: function (result) {

                        $container.data("ajaxRequest", null);

                        Date.updateLoadTime(result.serverLoadTime);

                        if (trackingPointFragment) {
                            OGT.Tracking.DeparturesSearchEngine.DeparturesSearch[trackingPointFragment + "Success"]();
                        }

                        var proxyNs = OGT.Proxy,
                            vehiclesProxy = proxyNs && proxyNs.vehicles,
                            vehiclesSelectedItemsIo = vehiclesProxy && vehiclesProxy.selected,
                            mapProxy = proxyNs && proxyNs.map,

                            versionNumber = result.versionNumber,
                            groups = result.groups,
                            allDeviations = [];

                        groups.forEach(function (group) {
                            group.forEach(function (item) {

                                var line = item.Line;

                                if (!line) {
                                    return;
                                }

                                (line.Deviations || []).forEach(function (deviation) {
                                    var text = deviation[DEVIATION_FIELD].trim(),
                                        existingDeviation = allDeviations.find(function (otherDeviation) { return text == otherDeviation.text; });
                                    if (!existingDeviation) {
                                        existingDeviation = {
                                            text: text,
                                            departures: []
                                        };
                                        allDeviations.push(existingDeviation);
                                    }
                                    existingDeviation.departures.push(line);
                                });


                            });
                        });

                        if (firstLoadVersionNumber == null) {
                            firstLoadVersionNumber = versionNumber;
                        } else if (firstLoadVersionNumber < versionNumber) {
                            if ($messageTarget) {
                                $messageTarget.html(lang.reload).show();
                            }
                        } else {
                            if ($messageTarget) {
                                $messageTarget.hide();
                            }
                        }

                        if (!groups) {
                            setInteruption();
                            return;
                        }

                        $container.removeClass("interruption");

                        $list.empty().hide();


                        var actualColumnCount = groups.length,
                            actualPagesCount = ((1 < actualColumnCount ? 1 : 0) + Math.ceil(actualColumnCount / columnsPerPageCount)),
                            columnDistancePercentNormalized = COLUMN_DISTANCE_PERCENT / actualPagesCount,
                            columnWidth = 100 / (actualPagesCount * columnsPerPageCount) - ((columnsPerPageCount - 1) * columnDistancePercentNormalized / columnsPerPageCount);

                        if (actualColumnCount) {

                            groups.forEach(function (group, index) {

                                var $column = $(OGT.Util.String.format(columnTemplate, columnWidth, (index % columnsPerPageCount ? columnDistancePercentNormalized : "0")))
                                    .appendTo($list);

                                group.forEach(function (item) {

                                    var newDay = item.DateTime,
                                        departure = item.Line;

                                    if (newDay) {
                                        $column.append(OGT.Util.String.format(newDayTemplate, Date.fromSwedishDateTime(newDay).getFancyFormattedDate().capitalizeFirstLetter()));
                                    }

                                    if (departure) {

                                        var iconOnIcon = false,
                                            deviations = departure.Deviations;
                                        if (deviations && deviations.length) {

                                            var journeyNs = OGT.Util.OGT.Journey;

                                            iconOnIcon = {
                                                graphics: "warning",
                                                tooltip: journeyNs ? journeyNs.getDeviationsText(1) : ""
                                            };

                                        }

                                        var iCanHasPosRoi = vehiclesProxy && mapProxy && departure.HasPosRoi,
                                            hasRealtime = OGT.Util.OGT.Format.Realtime.hasRoutelinkRealTime(departure),
                                            resourceString = hasRealtime ? lang.explanationRealtime : lang.explanationSchedule,
                                            clickForVehiclesOnMapFragment = resourceString + (iCanHasPosRoi ? (". " + lang.clickForVehicleOnMap) : ""),
                                            isCanceled = OGT.Util.OGT.Format.Realtime.getRouteOrPointLinkIsCanceled(departure),
                                            timeIsOmitted = OGT.Util.OGT.Format.Realtime.checkIfRealtimeIsOmitted(departure);

                                        $row = $(OGT.Util.OGT.AccordionList.getAccordionListItem({
                                            root: {
                                                additionalCssClasses: OGT.Util.String.format(
                                                    'departure__item main clearfix{0}{1}',
                                                    (iCanHasPosRoi ? " additional_controls_enabled" : ""),
                                                    (isCanceled ? " depature__canceled" : "")
                                                ),
                                                additionalAttributes: OGT.Util.String.format(
                                                    'data-routelinkkey="{0}" data-depdatetime="{1}"',
                                                    departure.RouteLinkKey,
                                                    departure.JourneyDateTime
                                                )
                                            },
                                            header: {
                                                htmlContent: OGT.Util.String.format(
                                                    upcomingDepartureHeaderContentTemplate,
                                                    OGT.Util.OGT.Format.line(departure, {
                                                        noLink: nonInteractive,
                                                        icon: {
                                                            iconOnIcon: iconOnIcon
                                                        }
                                                    }),
                                                    departure.Towards,
                                                    departure.StopPointAliasFormatted,
                                                    timeIsOmitted ? lang.omittedTime : departure.OgtTimeUntilDepOrArr,
                                                    timeIsOmitted ? "" : hasRealtime ? "forecast" : "timetable",
                                                    clickForVehiclesOnMapFragment,
                                                    isCanceled ? OGT.Util.OGT.Format.Realtime.canceledHeader.replace(/span/g, "div") : "",
                                                    timeIsOmitted ? "time-omitted" : ""
                                                )
                                            },

                                            content: {
                                                additionalCssClasses: "item__additional_controls",
                                                htmlContent: OGT.Util.String.format(
                                                    upcomingDepartureContentContentTemplate,
                                                    clickForVehiclesOnMapFragment
                                                ),
                                                additionalContent: isCanceled ? OGT.Util.OGT.Format.Realtime.canceledInfoTemplate : null
                                            }
                                        }));


                                        departure.$row = $row;

                                        if (iCanHasPosRoi) {

                                            var mapVisibleIo = mapProxy.visible,
                                                mapFullscreenIo = mapProxy.fullscreen;

                                            $row.find('.openinmap').click(function (event) {
                                                event.stopPropagation();

                                                var zoomToDone = false;
                                                function visibilityListener(inValueOptions, options) {

                                                    if (!zoomToDone && mapVisibleIo.get().ise && mapFullscreenIo.get().ise !== false) {

                                                        zoomToDone = true;

                                                        mapVisibleIo.stopListening(visibilityListener);
                                                        mapFullscreenIo.stopListening(visibilityListener);

                                                        vehiclesProxy.zoomTo.set(vehiclesSelectedItemsIo.get(), options);
                                                        return options.allIsDoneCallback;
                                                    }
                                                    return null;

                                                }

                                                var runPlaceTypes = vehiclesProxy.runPlaceTypes,
                                                    vehicleHighlightTypes = vehiclesProxy.vehicleHighlightTypes;

                                                vehiclesSelectedItemsIo.set([{ type: vehicleHighlightTypes.run, id: departure.LineNrReal, run: departure.RunNo, runPlaces: [{ type: runPlaceTypes.from, place: { OgtType: "stop", Id: stopId, OgtStopPointData: departure.OgtStopPointData } }], highlitLevel: OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.VEHICLES_SHOW_DEPARTURE_ON_MAP }]);

                                                mapVisibleIo.listen({
                                                    newValue: visibilityListener
                                                });
                                                mapFullscreenIo.listen({
                                                    newValue: visibilityListener
                                                });


                                                mapFullscreenIo.set({
                                                    doe: true
                                                });
                                                mapVisibleIo.set({
                                                    doe: true
                                                });

                                            });

                                        }

                                        $row.data('deviationMessage', departure.Deviations);
                                        $column.append($row);

                                    }

                                });

                            });

                        }

                        if ($deviationsWindow) {
                            $deviationsSlider.empty();

                            var deviationWindowHeight = DEVIATION_ROWS * DEVIATION_ROW_HEIGHT + "em";

                            if (allDeviations.length) {

                                $deviationsWindow.height(deviationWindowHeight).show();
                                $hideWhenDeviations.addClass("hidden");

                                allDeviations.forEach(function (deviation) {

                                    var $deviationContainer = null,
                                        tokens = deviation.text.split(' '),
                                        tokensLastIndex = tokens.length,
                                        textFragment = '',
                                        prevHeight,
                                        rowCount;

                                    tokens.push('');

                                    tokens.forEach(function (token, index) {

                                        var last = (index == tokensLastIndex),
                                            toAdd = token + ' ';

                                        if (!$deviationContainer) {
                                            $deviationContainer = $(deviationTemplate);
                                            $deviationsSlider.append($deviationContainer);
                                            prevHeight = 0;
                                            rowCount = 0;
                                        }

                                        $deviationContainer.data("deviation", deviation);

                                        $deviationContainer.html(textFragment + toAdd);

                                        var currentHeight = $deviationContainer.outerHeight();

                                        if (prevHeight < currentHeight || last) {
                                            rowCount++;

                                            if (DEVIATION_ROWS < rowCount || last) {
                                                $deviationContainer.empty();

                                                $('<span/>', {
                                                    text: textFragment
                                                }).appendTo($deviationContainer);

                                                $deviationContainer.height(deviationWindowHeight);

                                                $deviationContainer = null;
                                                textFragment = '';
                                            } else {
                                                prevHeight = currentHeight;
                                            }
                                        }

                                        textFragment += toAdd;

                                    });

                                });

                            } else {

                                $deviationsWindow.hide();
                                $hideWhenDeviations.removeClass("hidden");

                            }

                        }


                        if (!noComingStopsFunctionality) {
                            attachComingStopsFunctionality($list, stopId);
                        }

                        if (successCallback) {
                            successCallback.apply(this, arguments);
                        }

                        $list.show();

                        $container.slideDown({
                            duration: "slow",
                            complete: function () {
                                // Animation complete.
                                if (animationCompleteCallback) {
                                    animationCompleteCallback();
                                }
                            },
                            step: animationStepCallback
                        });

                        $container.find('a').click(function (event) {
                            event.stopPropagation();
                        });

                    }
                }));

                function close() {
                    var ajaxRequest = $container.data("ajaxRequest");
                    if (ajaxRequest) {
                        ajaxRequest.abort();
                        $container.data("ajaxRequest", null);
                    }
                    $container.slideUp();
                }

                return {
                    stopId: stopId,
                    trafficTypeGroups: trafficTypeGroups,
                    close: close
                }

            }
        }());


    var attachComingStopsFunctionality = (function () {

        var expandedTemplate = '<div class="item__content stops__coming clearfix"></div>',
            comingStopTemplate =
                '<div class="coming__stop clearfix">' +
                '<div class="stop--info clearfix">' +
                '<div class="journey__tracer">' +
                '<div class="relative"></div>' +
                '</div>' +
                '<div class="item time {6}">{1} ' +
                '<span class="ogt-{2}-16 timingicon" title="{3}">&nbsp;</span>' +
                '</div>' +
                '<div class="info__icon">' +
                '<i class="icon--intermediate"></i>' +
                '</div>' +
                '<div class="item text notranslate {5}">{0}{4}</div>' +
                '<div class="details"></div>' +
                '</div>' +
                '</div>',
            expandedClass = 'expandable--active';

        function toggleComingStops(routelinkKey, depDateTime, deviationMessages, stopId, parentDepartureItem, $parentRoot) {

            var $parentDepartureItem = $(parentDepartureItem),
                $expandedContainer = parentDepartureItem.$expandedContainer,
                timeIsOmitted = false;

            if (!$expandedContainer) {

                parentDepartureItem.$expandedContainer = "loading";

                $expandedContainer = parentDepartureItem.$expandedContainer = $(expandedTemplate);

                (deviationMessages || []).forEach(function (messageData) {

                    // Check if traffic-errand is of type 'Invänta tid/TIMEOMITTED'
                    if (messageData.Categories && messageData.Categories.CustomCategory == "TIMEOMITTED") {
                        timeIsOmitted = true;
                    }

                    var messageArea = OGT.Util.OGT.makeMessageArea();

                    $expandedContainer.append(messageArea.$root);

                    messageArea.show(messageData.Details, true);

                });

                $parentDepartureItem.append($expandedContainer);

                OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.FS_TRAFFIC_INFO + "/getLineStops", {
                    data: {
                        routelinkKey: routelinkKey,
                        onlyFuture: false
                    },
                    successCallback: function (stopDatas) {

                        var isSamePlace = OGT.Util.OGT.isSamePlace,
                            stop = {
                                Id: stopId,
                                OgtType: "stop"
                            },
                            depDateTimeParsed = Date.fromSwedishDateTime(depDateTime),
                            indexFirst = stopDatas.findIndex(function (stopData) {
                                return isSamePlace(stop, stopData.Place, true) && (depDateTimeParsed.isSameAs(Date.fromSwedishDateTime(stopData.DepDateTime)));
                            });

                        if (indexFirst === -1) {
                            return;
                        }

                        $parentDepartureItem.append(OGT.Util.OGT.AccordionList.get$NavigationBarForJourneySearch({
                            start: stopDatas[indexFirst].Place.PlaceName,
                            end: lang.departures,
                            date: depDateTimeParsed.getFormattedDate("yyyy-MM-dd"),
                            click: function () {
                                toggleComingStops(routelinkKey, depDateTime, deviationMessages, stopId, parentDepartureItem, $parentRoot);
                            }
                        }));

                        var reducedStopDatas = stopDatas.slice(indexFirst);

                        if (reducedStopDatas.length) {

                            reducedStopDatas.forEach(function (stopData) {

                                var place = stopData.Place,
                                    hasRealtime = OGT.Util.OGT.Format.Realtime.hasRoutelinkRealTime(stopData),
                                    resourceString = hasRealtime ? lang.explanationRealtime : lang.explanationSchedule,
                                    isCanceled = OGT.Util.OGT.Format.Realtime.getRouteOrPointLinkIsCanceled(stopData);

                                $expandedContainer.append(OGT.Util.String.format(
                                    comingStopTemplate,
                                    OGT.Util.Html.spanifyOrLinkify("", place.PlaceName, OGT.Util.OGT.getStopPageUrl(place)),
                                    timeIsOmitted ? lang.omittedTime : stopData.OgtTimeUntilDeparture,
                                    timeIsOmitted ? "" : hasRealtime ? "forecast" : "timetable",
                                    resourceString,
                                    isCanceled ? OGT.Util.OGT.Format.Realtime.canceledPointTemplate : "",
                                    isCanceled ? OGT.Util.OGT.Format.Realtime.canceledDepartmentTntermediateItemName : "",
                                    timeIsOmitted ? "time-omitted" : ""
                                ));
                            });

                        }

                        $expandedContainer.find("a").click(function (event) {
                            event.stopPropagation();
                        });
                        $expandedContainer.addClass("loaded");

                        window.setTimeout(function () {
                            toggleComingStops(routelinkKey, depDateTime, deviationMessages, stopId, parentDepartureItem, $parentRoot);
                        }, 50);

                    }
                });

                return;

            }

            if ($expandedContainer == "loading") {
                return;
            }

            var wasOpen = $parentDepartureItem.hasClass(expandedClass);

            if (!wasOpen) {
                $parentDepartureItem.addClass(expandedClass);
                $parentDepartureItem.find(".item__header-controls").attr("aria-expanded", "true");
            }
            else {
                $parentDepartureItem.removeClass(expandedClass);
                $parentDepartureItem.find(".item__header-controls").attr("aria-expanded", "false");
            }
        }

        return function ($root, stopId) {

            $root.find('.departure__item').addClass('expandable').click(function (event) {

                event.preventDefault();
                event.stopPropagation();

                var $this = $(this),
                    dataset = this.dataset;

                toggleComingStops(dataset.routelinkkey, dataset.depdatetime, $this.data("deviationMessage"), stopId, this, $root);

                //window.setTimeout(function () { // Trigger this right after css animation (500 ms)
                //    OGT.Util.scrollToElement($this);
                //}, 550);

            });

        };

    }());


    var wireUp = function (options) {

        var stopId = options.stopId,
            nonInteractive = options.nonInteractive,
            autoUpdate = options.autoUpdate,
            autoUpdateInterval = options.autoUpdateInterval || 60,
            useCookie = options.useCookie,
            useQueryString = options.useQueryString,
            useDaySeparator = options.useDaySeparator,

            trackingPointFragment = options.trackingPointFragment,

            defaultDeparturesPerColumn = options.defaultDeparturesPerColumn,

            $currentTimeTarget = options.$currentTimeTarget,
            $settingsDialog = options.$settingsDialog,
            $showSettingsButton = options.$showSettingsButton,
            $activeFiltersTarget = options.$activeFiltersTarget,
            $activePageTarget = options.$activePageTarget,
            $messageTarget = options.$messageTarget,
            inputFields = options.inputFields || {},
            $fullscreenButton = inputFields.$fullscreenButton,
            $date = inputFields.$date,
            $time = inputFields.$time,
            $filterTextArea = inputFields.$filterTextArea,
            $form = inputFields.$form,
            $filterButton = inputFields.$filterButton,
            $departuresCount = inputFields.$departuresCount,
            $delay = inputFields.$delay,
            $deviationsTime = inputFields.$deviationsTime,
            $sortOrder = inputFields.$sortOrder,
            $columnCount = inputFields.$columnCount,
            $pagesCount = inputFields.$pagesCount,
            $pageTime = inputFields.$pageTime,
            $textSize = inputFields.$textSize,
            $look = inputFields.$look,
            $hideFilters = inputFields.$hideFilters,
            loadedLinesCallback = options.loadedLinesCallback,

            $departuresContainer = options.$departuresContainer,
            $maskWindow = $('<div class="departures__window clearfix"></div>').prependTo($departuresContainer),
            $slider = $('<div class="departures__slider--container clearfix"></div>').appendTo($maskWindow),
            $deviationsWindow = options.$deviationsTarget,
            $deviationsSlider = $deviationsWindow && $deviationsWindow.find(".deviations__slider--container"),
            $hideWhenDeviations = options.$hideWhenDeviations;

        $departuresContainer.append(OGT.Util.String.format(
            '<div class="deviations__interruption"><div class="mask"></div><div class="text">{0}</div></div>',
            lang.interuption
        ));


        var constants = window.OGT.CONSTANTS,
            queryStringKeys = constants.QUERY_STRING.STOP_PAGE,
            trafficTypesQueryStringKey = queryStringKeys.trafficType,
            linesQueryStringKey = queryStringKeys.lines,
            stopPointsQueryStringKey = queryStringKeys.stopPoint,

            departuresPerColumnQueryStringKey = queryStringKeys.departuresCount,
            textSizeQueryStringKey = queryStringKeys.textSize,
            sortOrderQueryStringKey = queryStringKeys.sortOrder,
            strippedQueryStringKey = queryStringKeys.stripped,
            delayQueryStringKey = queryStringKeys.delay,
            deviationsTimeQueryStringKeys = queryStringKeys.deviationsTime,
            columnCountPerPageQueryStringKey = queryStringKeys.columnsPerPageCount,
            pagesCountQueryStringKey = queryStringKeys.pagesCount,
            pageTimeQueryStringKey = queryStringKeys.pageTime,
            lookQueryStringKey = queryStringKeys.look,
            hideFiltersQueryStringKey = queryStringKeys.hideFilters,

            allQueryStringKeys = [trafficTypesQueryStringKey, linesQueryStringKey, stopPointsQueryStringKey, departuresPerColumnQueryStringKey, textSizeQueryStringKey, strippedQueryStringKey, delayQueryStringKey, deviationsTimeQueryStringKeys, sortOrderQueryStringKey, columnCountPerPageQueryStringKey, pagesCountQueryStringKey, pageTimeQueryStringKey, lookQueryStringKey, hideFiltersQueryStringKey],
            numberQueryStringKeys = [departuresPerColumnQueryStringKey, delayQueryStringKey, deviationsTimeQueryStringKeys, columnCountPerPageQueryStringKey, pagesCountQueryStringKey, pageTimeQueryStringKey],
            arrayQueryStringKeys = [trafficTypesQueryStringKey, linesQueryStringKey, stopPointsQueryStringKey],
            booleanQueryStringKeys = [strippedQueryStringKey, hideFiltersQueryStringKey],
            inputsToQueryStringKeyMapping = [
                { $select: $departuresCount, qsKey: departuresPerColumnQueryStringKey },
                { $select: $columnCount, qsKey: columnCountPerPageQueryStringKey },
                { $select: $pagesCount, qsKey: pagesCountQueryStringKey },
                { $select: $pageTime, qsKey: pageTimeQueryStringKey },
                { $select: $textSize, qsKey: textSizeQueryStringKey },
                { $select: $delay, qsKey: delayQueryStringKey },
                { $select: $deviationsTime, qsKey: deviationsTimeQueryStringKeys },
                { $select: $sortOrder, qsKey: sortOrderQueryStringKey },
                { $select: $look, qsKey: lookQueryStringKey },
                { $checkbox: $hideFilters, qsKey: hideFiltersQueryStringKey }
            ],
            cookieKeyTemplate = constants.COOKIE.KEYS.DEPARTURE_FILTER;

        var defaultFilterValues = allQueryStringKeys.reduce(function (prev, key) {
            var val = null;
            if (arrayQueryStringKeys.includes(key)) {
                val = [];
            } else if (booleanQueryStringKeys.includes(key)) {
                val = false;
            } else {
                switch (key) {
                    case departuresPerColumnQueryStringKey:
                        val = defaultDeparturesPerColumn;
                        break;
                    case columnCountPerPageQueryStringKey:
                        val = 1;
                        break;
                    case pagesCountQueryStringKey:
                        val = 1;
                        break;
                    case pageTimeQueryStringKey:
                        val = 10;
                        break;
                    case textSizeQueryStringKey:
                        val = "normal";
                        break;
                    case lookQueryStringKey:
                        val = "normal";
                        break;
                    case sortOrderQueryStringKey:
                        val = "DepartureTime";
                        break;
                    case deviationsTimeQueryStringKeys:
                        val = 5;
                        break;
                    case delayQueryStringKey:
                        val = 0;
                        break;
                }
            }
            prev[key] = val;
            return prev;
        }, {});

        var selectedFilters = (function () {
            var result = {};
            for (var key in defaultFilterValues) {
                var value = defaultFilterValues[key];
                if (Array.isArray(value)) {
                    value = value.splice();
                }
                result[key] = value;
            }
            return result;
        }());


        var allTrafficTypeJourneySearchGroups = window.trafficTypeJourneySearchGroups;

        var loadedLines = [],
            loadedStopPoints = [],
            loadedTrafficTypes = [],
            loadedStops = [],
            hideSettings;

        function initSettingsButton() {

            if ($settingsDialog && $showSettingsButton) {

                if ($fullscreenButton && $fullscreenButton.length && OGT.Util.FullScreen.enabled()) {
                    $fullscreenButton.show().click(function (event) {
                        event.stopPropagation();

                        OGT.Util.FullScreen.toggle();

                        hideSettings();
                    });

                }

                var visibleTimer,
                    blockedTimer;

                $settingsDialog.on("click touchstart", function (event) {
                    event.stopPropagation();

                    if (filterTextArea) {
                        filterTextArea.closeSuggestions();
                    }

                });

                $showSettingsButton.on("click dblclick", function (event) {
                    event.stopPropagation();
                    $settingsDialog.show();
                    $(".settings__overlay").addClass("active");
                    if (filterTextArea) {
                        filterTextArea.render();
                    }
                });

                function showShowSettingsButton() {

                    if (blockedTimer) {
                        return;
                    }

                    $showSettingsButton.addClass("visible");
                    if (visibleTimer) {
                        window.clearTimeout(visibleTimer);
                        visibleTimer = null;
                    }
                    visibleTimer = window.setTimeout(hideSettings, 10000);


                }

                hideSettings = function () {
                    if (visibleTimer) {
                        window.clearTimeout(visibleTimer);
                        visibleTimer = null;
                    }
                    $showSettingsButton.removeClass("visible");
                    $settingsDialog.hide();
                    $(".settings__overlay").removeClass("active");

                    blockedTimer = window.setTimeout(function () {
                        blockedTimer = null;
                    }, 100);
                }

                $settingsDialog.find('.action--close').click(function () {
                    hideSettings();
                });

                $(document).on("mousemove keyup keydown click touchstart", function () {
                    showShowSettingsButton();
                });

                $(document).click(function () {
                    hideSettings();
                });

                $(".settings__overlay").on("click touch touchstart", function () {
                });
            }

        };

        function initVisibleMessages() {

            if (OGT.Util.BrowserDetect.isIe()) {
                $('.ie__message').removeClass('hidden');
            }
            else if (OGT.Util.BrowserDetect.isIos()) {
                $('.ios__message').removeClass('hidden');
            }
        }

        function setFilterSettingsFromQueryStringOrCookie() {

            var newSettings = getQueryStringSettings();

            if (!newSettings) {
                newSettings = getCookieSettings();
            }

            if (newSettings) {
                setFilterSettings(newSettings);
            }

        }


        function trafficTypeGroupKeysToTrafficTypeGroups(trafficTypeGroupKeys) {
            return (trafficTypeGroupKeys || []).map(function (shortKey) {
                shortKey = "" + (shortKey || "");
                return allTrafficTypeJourneySearchGroups.find(function (trafficTypeGroup) { return shortKey.toLowerCase() == trafficTypeGroup.shortKey.toLowerCase(); });
            }).filter(OGT.Util.OGT.Filters.hasValue);
        }

        function lineUrlSegmentsToLines(lineUrlSegments) {
            return (lineUrlSegments || []).map(function (lineUrlSegment) { return loadedLines.find(function (line) { return lineUrlSegment == line.Ogt.OgtLineUrlSegment; }); }).filter(OGT.Util.OGT.Filters.hasValue);
        }

        function stopPointAliasesToStopPoints(stopPointAliasIds) {
            return (stopPointAliasIds || []).map(function (stopPointAliasId) {
                return loadedStopPoints.find(function (stopPoint) {
                    return stopPointAliasId == stopPoint.OgtStopPointData.stopPointAliasId;
                });
            }).filter(OGT.Util.OGT.Filters.hasValue);
        }


        function refreshActiveFiltersTarget() {

            if ($activeFiltersTarget) {

                var texts = [];

                texts.spliceArray(0, 0, (selectedFilters[trafficTypesQueryStringKey] || []).map(function (trafficTypeGroup) {
                    return trafficTypeGroup.label;
                }).sort());

                texts.spliceArray(0, 0, (selectedFilters[stopPointsQueryStringKey] || []).map(function (stopPoint) {
                    return OGT.Util.OGT.StopPointAlias.textFrom({ stopPointAlias: stopPoint.OgtStopPointData.stopPointAlias, upperCase: true, alwaysAcceptAlias: true });
                }).sort());

                texts.spliceArray(0, 0, (selectedFilters[linesQueryStringKey] || []).map(function (line) {
                    return line.Ogt.OgtLineNamePreformatted;
                }).sort());

                $activeFiltersTarget.html(texts.join(", "));

            }

        }


        function setFilterSettings(newFilters) {

            for (var id in newFilters) {

                selectedFilters[id] = newFilters[id];
            }

            refreshActiveFiltersTarget();
            fillFilterInputs(selectedFilters);

        }


        function updateFilterSettings() {

            if (filterTextArea) {

                var stopPoints = [],
                    trafficTypeGroups = [],
                    lines = [];

                filterTextArea.currentAugmenterTokens.forEach(function (token) {

                    switch (token.constructor) {

                        case OGT.FilterTextArea.TokenClasses.StopPointToken:
                            stopPoints.push(token.stopPoint);
                            break;

                        case OGT.FilterTextArea.TokenClasses.TrafficTypeToken:
                            trafficTypeGroups.push(token.trafficTypeGroup);
                            break;

                        case OGT.FilterTextArea.TokenClasses.LineToken:
                            lines.push(token.line);
                            break;
                    }

                });

                selectedFilters[trafficTypesQueryStringKey] = trafficTypeGroups;
                selectedFilters[linesQueryStringKey] = lines;
                selectedFilters[stopPointsQueryStringKey] = stopPoints;

            }

            inputsToQueryStringKeyMapping.forEach(function (pair) {
                var $select = pair.$select,
                    $checkbox = pair.$checkbox,
                    key = pair.qsKey;
                if ($select) {
                    var value = $select.val();
                    if (numberQueryStringKeys.includes(key)) {
                        value = parseInt(value);
                    }
                    selectedFilters[key] = value;
                } else if ($checkbox) {
                    selectedFilters[key] = $checkbox.is(':checked');
                }
            });

        }

        function getCookieSettings() {

            var cookie = useCookie && OGT.Util.Cookie.get(OGT.Util.String.format(cookieKeyTemplate, stopId));
            if (cookie) {

                var newSettings = {};

                allQueryStringKeys.forEach(function (key) {

                    var value = cookie[key];

                    if (arrayQueryStringKeys.includes(key)) {
                        value = value || [];
                    }

                    newSettings[key] = value;

                });

                return fromHalfParsed(newSettings);

            }

            return false;

        }


        function getQueryStringSettings() {

            if (useQueryString) {

                var locationSearch = window.decodeURIComponent(location.search),
                    newSettings = {};

                allQueryStringKeys.forEach(function (key) {

                    var value = OGT.Util.QueryString.getParameter(locationSearch, key);

                    if (value != null) {

                        if (arrayQueryStringKeys.includes(key)) {

                            value = value ? value.split(',') : [];

                        } else if (booleanQueryStringKeys.includes(key)) {

                            value = !!value;

                        } else if (numberQueryStringKeys.includes(key)) {

                            value = parseInt(value);

                        }

                    }

                    newSettings[key] = value;


                });

                return fromHalfParsed(newSettings);

            }

            return false;

        }


        function fromHalfParsed(settings) {

            var newSettings = {},
                anyThing = false;

            allQueryStringKeys.forEach(function (key) {

                var value = settings[key];

                if (typeof value != "undefined" && value != null) {
                    switch (key) {
                        case trafficTypesQueryStringKey:
                            value = trafficTypeGroupKeysToTrafficTypeGroups(value);
                            anyThing |= value.length;
                            break;
                        case linesQueryStringKey:
                            value = lineUrlSegmentsToLines(value);
                            anyThing |= value.length;
                            break;
                        case stopPointsQueryStringKey:
                            value = stopPointAliasesToStopPoints(value);
                            anyThing |= value.length;
                            break;
                        default:
                            if (numberQueryStringKeys.includes(key)) {
                                value = parseInt(value);
                            } else if (booleanQueryStringKeys.includes(key)) {
                                value = !!value;
                            }
                            anyThing |= true;
                    }
                }

                if (typeof value != "undefined" && value != null) {
                    newSettings[key] = value;
                }


            });

            if (anyThing) {
                return newSettings;
            }
            return false;

        }



        function fillFilterInputs(settings) {

            if (filterTextArea) {

                filterTextArea.clear();

                (settings[trafficTypesQueryStringKey] || []).forEach(function (trafficTypeGroup) {

                    filterTextArea.addToken(new OGT.FilterTextArea.TokenClasses.TrafficTypeToken({
                        trafficTypeGroup: trafficTypeGroup
                    }));

                });

                (settings[stopPointsQueryStringKey] || []).forEach(function (stopPoint) {

                    filterTextArea.addToken(new OGT.FilterTextArea.TokenClasses.StopPointToken({
                        stopPoint: stopPoint
                    }));

                });

                (settings[linesQueryStringKey] || []).forEach(function (line) {

                    filterTextArea.addToken(new OGT.FilterTextArea.TokenClasses.LineToken({
                        line: line
                    }));

                });

                filterTextArea.render();

            }

            inputsToQueryStringKeyMapping.forEach(function (pair) {
                var $select = pair.$select,
                    $checkbox = pair.$checkbox,
                    key = pair.qsKey,
                    value = settings[key];
                if ($select) {
                    if (typeof value != "undefined" && value != null) {
                        var $toSelect = $select.find('option[value="' + value + '"]');
                        if ($toSelect.length) {
                            $toSelect.prop('selected', true);
                        }
                    }
                } else if ($checkbox) {
                    $checkbox.attr('checked', value);
                }
            });

        }


        var startAutoUpdate,
            stopAutoUpdate;

        (function () {

            var intervalId;

            function doRefresh() {

                if (autoUpdate) {

                    if ($date && $time) {

                        var now = Date.adjusted();

                        $date.val(now.getFormattedDate());
                        $time.val(now.getFormattedTime());
                    }

                    doDeparturesSearch(false);
                }

            }


            startAutoUpdate = function () {

                function doAutoUpdate() {
                    doRefresh();

                    intervalId = window.setInterval(function () {

                        doRefresh();

                    }, (autoUpdateInterval * 1000));
                }


                if (autoUpdate) {
                    if (!OGT.Util.addBrowserVisibilityListener(function (isVisible) {
                        if (isVisible && autoUpdate) {
                            doAutoUpdate();
                        } else {
                            stopAutoUpdate();
                        }
                    })) {
                        // No browser visibility support, just start updating
                        doAutoUpdate();
                    }
                }
            }

            stopAutoUpdate = function () {

                if (intervalId) {
                    window.clearInterval(intervalId);
                }

            }

        }());


        var startClock = (function () {

            function refreshClock() {

                if ($currentTimeTarget) {

                    var n = Date.adjusted().getFormattedTime();

                    if (n != $currentTimeTarget.html()) {

                        $currentTimeTarget.html(n);

                    }

                }

            }

            return function () {

                if ($currentTimeTarget) {

                    refreshClock();

                    window.setInterval(refreshClock, 1000);

                }

            }

        }());


        var startAutoAdvancePages = (function () {

            var timer,
                currentInterval,
                currentPageIndex = 0,
                currentPagesCount = 0;

            function advancePage(offset) {

                currentPageIndex = currentPagesCount && (currentPageIndex + offset) % currentPagesCount;

                $slider.css("left", (-100 * currentPageIndex) + "%");
                if ($activePageTarget) {
                    if (1 < currentPagesCount) {
                        $activePageTarget.html(OGT.Util.String.format(lang.pageOfPages, (currentPageIndex + 1), currentPagesCount));
                    } else {
                        $activePageTarget.html("");
                    }
                }

            }

            return function () {

                var columnsCountPerPage = selectedFilters[columnCountPerPageQueryStringKey];
                var interval = selectedFilters[pageTimeQueryStringKey];

                var usedColumns = $slider.children().length,
                    pagesCount = Math.ceil(usedColumns / columnsCountPerPage),
                    doRun = interval && 1 < pagesCount;

                if (timer && (!doRun || currentInterval != interval)) {
                    window.clearInterval(timer);
                    timer = null;
                }

                currentInterval = interval;
                currentPagesCount = pagesCount;

                $slider.width(((pagesCount + (1 < usedColumns ? 1 : 0)) * 100) + "%");
                advancePage(0);

                if (doRun) {

                    if (!timer) {
                        timer = window.setInterval(function () {
                            advancePage(1);
                        }, interval * 1000);
                    }
                }
            }

        }());


        var updateDeviations = (function () {

            var HIGHLIGHT_DEVIATIONS_CLASS = "highlightDeviation",
                i = 0;

            return function (doProgress) {

                var $rows = $deviationsSlider.children(),
                    rowCount = $rows.length;

                if (!rowCount) {
                    return;
                }

                if (doProgress) {

                    if (i < rowCount - 1) {
                        i++;
                    } else {
                        i = 0; // reset 
                    }

                }

                var pullUp = -i * DEVIATION_ROWS * DEVIATION_ROW_HEIGHT;
                $deviationsSlider.css("top", pullUp + "em");


                var deviation = $rows.eq(i).data("deviation"),
                    departures = deviation.departures,
                    $allItems = $departuresContainer.find(".departure__item");

                $allItems.each(function () {
                    var $item = $(this),
                        element = $item.get(0);
                    if (departures.some(function (departure) { return element === departure.$row.get(0); })) {
                        $item.addClass(HIGHLIGHT_DEVIATIONS_CLASS);
                    } else {
                        $item.removeClass(HIGHLIGHT_DEVIATIONS_CLASS);
                    }
                });

            }
        }());

        var startAutoAdvanceDeviations = (function () {

            var timer,
                currentInterval;

            return function () {

                if ($deviationsWindow) {

                    updateDeviations(false);

                    var interval = selectedFilters[deviationsTimeQueryStringKeys];

                    if (timer && (currentInterval != interval)) {
                        window.clearInterval(timer);
                        timer = null;
                    }

                    currentInterval = interval;

                    if (!timer) {
                        timer = window.setInterval(function () { updateDeviations(true); }, interval * 1000);
                    }

                }
            }

        }());


        function getFilterSettingsKeyValues() {

            var keyValuesForCookie = {},
                keyValuesForQueryString = {};
            allQueryStringKeys.forEach(function (key) {

                var value = selectedFilters[key];

                if (typeof value != "undefined" && value != null) {

                    var defaultValue = defaultFilterValues[key];
                    if (Array.isArray(defaultValue) && Array.isArray(value)) {
                        if (value.sort().join("") == defaultValue.sort().join("")) {
                            value = null;
                        }
                    } else {
                        if (defaultValue == value) {
                            value = null;
                        }
                    }

                    if (value) {
                        switch (key) {
                            case trafficTypesQueryStringKey:
                                value = value.map(function (trafficTypeGroup) { return trafficTypeGroup.shortKey.toLowerCase(); });
                                break;
                            case linesQueryStringKey:
                                value = value.map(function (line) { return line.Ogt.OgtLineUrlSegment; });
                                break;
                            case stopPointsQueryStringKey:
                                value = value.map(function (stopPoint) {
                                    return stopPoint.OgtStopPointData.stopPointAliasId;
                                });
                                break;
                        }
                    }
                }

                if (typeof value != "undefined" && value != null) {
                    keyValuesForCookie[key] = value;
                }
                keyValuesForQueryString[key] = Array.isArray(value) ? value.join(",") : value;

            });

            return {
                forCookie: keyValuesForCookie,
                forQueryString: keyValuesForQueryString
            };

        }


        function getUrl(baseUrl, keyValuesForQueryString) {

            baseUrl = (baseUrl || window.currentPagePath) + window.decodeURIComponent(location.search);

            if (!keyValuesForQueryString) {
                updateFilterSettings();
                keyValuesForQueryString = getFilterSettingsKeyValues().forQueryString;
            }

            return OGT.Util.QueryString.updateWithDict(keyValuesForQueryString, baseUrl);

        }
        ns.getUrl = getUrl;


        function doDeparturesSearch(isInteractive, completeCallback) {

            var datetime = null;

            if ($date && $time) {
                $date.blur();
                $time.blur();
                datetime = $date.val() + " " + $time.val();
            }

            updateFilterSettings();

            refreshActiveFiltersTarget();

            var $body = $("body"),
                $bodyAndHtml = $("html,body"),
                textSize = selectedFilters[textSizeQueryStringKey],
                look = selectedFilters[lookQueryStringKey],
                stripped = selectedFilters[strippedQueryStringKey];

            $bodyAndHtml.removeClass(function (index, className) {
                return className.split(' ').filter(function (c) {
                    return c.indexOf('departures') == 0;
                }).join(' ');
            });

            if (textSize) {
                $body.addClass("departures__textsize--" + textSize);
            }
            if (look) {
                $bodyAndHtml.addClass("departures__look--" + look);
            }
            if (stripped) {
                $body.addClass("departures__stripped");
            }


            if ($activeFiltersTarget) {
                if (selectedFilters[hideFiltersQueryStringKey]) {
                    $activeFiltersTarget.hide();
                } else {
                    $activeFiltersTarget.show();
                }
            }


            var trafficTypeGroups = selectedFilters[trafficTypesQueryStringKey],
                stopPoints = selectedFilters[stopPointsQueryStringKey],
                lines = selectedFilters[linesQueryStringKey];

            if (isInteractive) {

                var keyValues = getFilterSettingsKeyValues(),
                    keyValuesForCookie = keyValues.forCookie,
                    keyValuesForQueryString = keyValues.forQueryString;

                if (useCookie) {
                    OGT.Util.Cookie.set(OGT.Util.String.format(cookieKeyTemplate, stopId), keyValuesForCookie, 365, window.currentPagePath);
                }

                if (useQueryString && window.history.pushState) {

                    try {

                        window.history.pushState({ settings: keyValuesForCookie }, "", getUrl(null, keyValuesForQueryString));

                    } catch (err) { }
                }
            }

            fillUpcomingDepartures({
                nonInteractive: nonInteractive,
                $container: $departuresContainer,
                $deviationsTarget: $deviationsWindow,
                $hideWhenDeviations: $hideWhenDeviations,
                $list: $slider,
                $messageTarget: $messageTarget,
                stopId: stopId,
                date: datetime,
                sortOrder: selectedFilters[sortOrderQueryStringKey],
                useDaySeparator: useDaySeparator,
                delay: selectedFilters[delayQueryStringKey],
                departuresPerColumnCount: selectedFilters[departuresPerColumnQueryStringKey],
                columnsPerPageCount: selectedFilters[columnCountPerPageQueryStringKey],
                pagesCount: selectedFilters[pagesCountQueryStringKey],
                pageTime: selectedFilters[pageTimeQueryStringKey],
                stopPoints: stopPoints,
                trafficTypeGroups: trafficTypeGroups,
                lines: lines,
                trackingPointFragment: trackingPointFragment,
                successCallback: function () {
                    startAutoAdvanceDeviations();
                    startAutoAdvancePages();
                    if (completeCallback) {
                        completeCallback();
                    }
                },
                failureCallback: completeCallback,
                isInteractive: isInteractive
            });

        }

        var filterTextArea;
        if ($filterTextArea) {

            if (OGT.FilterTextArea.canBeUsed()) {

                filterTextArea = OGT.FilterTextArea.initAndGet({
                    $target: $filterTextArea,
                    onlyFullTokensAtBlur: true,
                    suggestionSources: [
                        new OGT.FilterTextArea.SuggestionSourceClasses.StopPoint(loadedStopPoints),
                        new OGT.FilterTextArea.SuggestionSourceClasses.TrafficType(loadedTrafficTypes),
                        new OGT.FilterTextArea.SuggestionSourceClasses.Line(loadedLines)
                    ]
                });

            }
        }


        var loadingMask = OGT.Util.OGT.getLoadingMask("red");

        function search(e) {
            e.stopPropagation();
            e.preventDefault();
            e.returnValue = false;
            loadingMask.show();
            doDeparturesSearch(true, function () {
                loadingMask.hide();
            });
        }
        if ($form) {
            $form.on('submit', search);
            $form.append(loadingMask.$root);
        }
        if ($filterButton) {
            $filterButton.click(function (e) {
                search(e);
                if (hideSettings && (OGT.Util.OGT.getLayoutWidth() != OGT.Util.OGT.layoutWidths.desktop)) {
                    hideSettings();
                }
            });
        }

        setFilterSettingsFromQueryStringOrCookie();

        startClock();

        startAutoAdvancePages();

        if ($time && $date) {

            $time.change(stopAutoUpdate);
            $date.change(stopAutoUpdate);
        }


        OGT.GeoServer.fetchMainStopsAndStopPointsWithIds({
            ids: [stopId],
            //timeTablePeriodId: timeTablePeriodId,
            successCallback: function (result) {

                OGT.GeoServer.Util.convertMainAndStopPointArrayFromGeoServerData(result);

                var stopWithTheMostLines;
                result.forEach(function (mainOrSub) {

                    var ogtStopPointData = mainOrSub.OgtStopPointData;
                    if (ogtStopPointData) {

                        var stopPointAliasId = ogtStopPointData.stopPointAliasId;

                        if (!loadedStopPoints.find(function (otherStopPoint) {
                            return stopPointAliasId == otherStopPoint.stopPointAliasId;
                        })) {

                            loadedStopPoints.push(mainOrSub);
                        }

                    } else {

                        if (!stopWithTheMostLines || stopWithTheMostLines.lineIds.length < mainOrSub.lineIds.length) {
                            stopWithTheMostLines = mainOrSub;
                        }
                    }
                });


                if (stopWithTheMostLines) {

                    loadedStops.push(stopWithTheMostLines);

                    OGT.Util.OGT.AsyncAjax.getStopAndLines(stopWithTheMostLines.Id, stopWithTheMostLines.lineIds,
                        {
                            successCallback: function (extraDetails) {

                                extraDetails.lines.forEach(function (line) {
                                    if (!loadedLines.find(function (otherLine) {
                                        return line.LineNrReal == otherLine.LineNrReal;
                                    })) {
                                        var ogtTrafficTypeGroupCssClass = line.Ogt.OgtTrafficTypeGroupCssClass,
                                            ttType = allTrafficTypeJourneySearchGroups.find(function (ttg) { return ogtTrafficTypeGroupCssClass == ttg.OgtTrafficTypeGroupCssClass; });
                                        if (ttType) {
                                            loadedTrafficTypes.pushIfNotInArray(ttType);
                                        }
                                        loadedLines.push(line);
                                    }
                                });

                                setFilterSettingsFromQueryStringOrCookie();

                                startAutoUpdate();
                                initSettingsButton();
                                initVisibleMessages();

                                window.onpopstate = function (event) {

                                    if (event) {
                                        var state = event.state;
                                        if (state) {
                                            var settings = state.settings;
                                            if (settings) {

                                                setFilterSettings(settings);

                                                doDeparturesSearch(false);
                                                return;
                                            }
                                        }
                                    }

                                    setFilterSettingsFromQueryStringOrCookie();

                                    doDeparturesSearch(false);

                                };

                                if (loadedLinesCallback) {
                                    loadedLinesCallback(loadedLines);
                                }

                            }
                        });
                }
            }
        });

    };


    var ns = {
        fill: fillUpcomingDepartures,
        attachComingStopsFunctionality: attachComingStopsFunctionality,
        wireUp: wireUp
    };

    return ns;

}());
;
window.OGT = window.OGT || {};

window.OGT.Proxy = window.OGT.Proxy || {};

window.OGT.Proxy.Util = (function () {

    var highlightHandler = (function () {

        var listeners = [],
            currentlyHighlitByLevel = { 1: null, 2: null, 3: null, 4: null },
            currentHighestHitlitLevel = 0;

        function clear(toClear) {

            toClear.items.forEach(function (item) {
                item.highlitLevel = 0;
            });
            toClear.io.refresh({
                context: highlightHandler
            });

        }

        function addIo(options) {

            var io = options.io;

            io.listen({
                context: highlightHandler,
                newValue: function (value) {

                    if (!Array.isArray(value)) {
                        value = value ? [value] : [];
                    }

                    var newHighlightsByLevel = value.reduce(function (agg, selectedItem) {

                        var highlightLevel = selectedItem.highlitLevel || 0;
                        if (highlightLevel === 0) {
                            return agg;
                        }

                        var currentlyHighlits = currentlyHighlitByLevel[highlightLevel];
                        if (!currentlyHighlits || !currentlyHighlits.items.includes(selectedItem)) {
                            var target = agg[highlightLevel];
                            if (!target) {
                                target = agg[highlightLevel] = [];
                            }
                            target.push(selectedItem);
                        }
                        return agg;

                    }, []);


                    if (newHighlightsByLevel.length) {

                        var wasCurrentlyHighlit = [],
                            lowestHighlitLevelInNewHighlights = newHighlightsByLevel.findIndex(function (h) { return h; });

                        if (lowestHighlitLevelInNewHighlights == 2) {
                            lowestHighlitLevelInNewHighlights = 1;
                        }

                        for (var levelKey in currentlyHighlitByLevel) {
                            levelKey = parseInt(levelKey);
                            if (lowestHighlitLevelInNewHighlights <= levelKey) {
                                wasCurrentlyHighlit.pushIfHasValue(currentlyHighlitByLevel[levelKey]);
                                currentlyHighlitByLevel[levelKey] = null;
                            }
                            var newHighlights = newHighlightsByLevel[levelKey];
                            if (newHighlights) {
                                currentlyHighlitByLevel[levelKey] = {
                                    items: newHighlights,
                                    io: io
                                }
                            }
                        }

                        if (wasCurrentlyHighlit.length) {

                            wasCurrentlyHighlit.forEach(function (w) {
                                clear(w);
                            });

                        }


                        var highestHighlitLevel = 0;
                        for (var keyLevel in currentlyHighlitByLevel) {
                            if (currentlyHighlitByLevel[keyLevel] && highestHighlitLevel < parseInt(keyLevel)) {
                                highestHighlitLevel = parseInt(keyLevel);
                            }
                        }


                        if (currentHighestHitlitLevel !== highestHighlitLevel) {
                            currentHighestHitlitLevel = highestHighlitLevel;
                            callAllListeners();
                        }

                    } else {

                        var rightNowHighestLevel = 0;
                        for (var i = 1; ; i++) {

                            var currentlyHighlit = currentlyHighlitByLevel[i];

                            if (typeof currentlyHighlit == "undefined") {
                                break;
                            }

                            if (currentlyHighlit) {

                                var v = currentlyHighlit.io.get();

                                if (!Array.isArray(v)) {
                                    v = v ? [v] : [];
                                }

                                var rightNowHighlits = v.filter(function (selectedItem) { return selectedItem.highlitLevel; });

                                if (rightNowHighlits.length) {

                                    currentlyHighlit.items = rightNowHighlits;
                                    rightNowHighestLevel = i;

                                } else {
                                    currentlyHighlitByLevel[i] = null;
                                }

                            }

                        }

                        if (currentHighestHitlitLevel !== rightNowHighestLevel) {
                            currentHighestHitlitLevel = rightNowHighestLevel;
                            callAllListeners();
                        }

                    }

                }
            });

        }

        function getHighestHighlitLevel() {
            return currentHighestHitlitLevel;
        }

        function unhighlightCurrentlyHighlit() {

            if (getHighestHighlitLevel()) {

                for (var key in currentlyHighlitByLevel) {
                    if (currentlyHighlitByLevel[key]) {
                        clear(currentlyHighlitByLevel[key]);
                        currentlyHighlitByLevel[key] = null;
                    }
                }

                currentHighestHitlitLevel = 0;
                callAllListeners();
            }

        }

        function callAllListeners() {
            listeners.forEach(function (listener) {
                listener(currentHighestHitlitLevel);
            });
        }

        function listenForAnyHighlightChange(listener) {
            listeners.push(listener);
        }

        return {
            addIo: addIo,
            getHighestHighlitLevel: getHighestHighlitLevel,
            unhighlightCurrentlyHighlit: unhighlightCurrentlyHighlit,
            listenForAnyHighlightChange: listenForAnyHighlightChange

        };

    }());


    function getColourForFunc() {

        var mapping = {};

        return function (id) {

            var colour = mapping[id];

            if (!colour) {
                mapping[id] = colour = OGT.Proxy.ColourPool.take();
            }

            return colour;

        };

    }

    var getColourForLine = getColourForFunc();

    var getColourForStop = getColourForFunc();

    return {
        HighlightHandler: highlightHandler,
        getColourForLine: getColourForLine,
        getColourForStop: getColourForStop

    };

}());;
window.OGT = window.OGT || {};

window.OGT.Proxy = window.OGT.Proxy || {};

/**
* The I/O-concept is very central to how the ÖGT-webpage is working. It is a take on the pub/sub-pattern.
* Each I/O-object can be thought of as an observable variable with plenty of extra functionality.
* It handles serialization/deserialization to querystring, to cookies and to history.
* It handles validation of values.
* It handles reformatting of incoming and outgoing values.
*
* Listening can be done to an I/O in two different ways. Either one can listen to value changes in a normal
* manner. Or one can listen for the consequences of value changes having played out.
* If for example a new search is done, and the URL is to be updated depending on the result of this search,
* then it's vital that the URL updating is not done until all asyncronos requests have been run. Then listening
* for "allIsDone" is helpful.
* 
* For the "allIsDone" functionality to work those listening for regular changes in a value have a reponsiblity
* to either say "I'm done as soon as I return" or "please wait, and I'll call you back". The first option is
* accomplished by simply returning when registering a listener. The second option by returning a callback which
* will be called when all is done. It is vital that any method returning a callback will call it, no matter
* what happens. If a callback is left uncalled it can block history pushstate for instance.
* 
*/


window.OGT.Proxy.Io = (function () {

    // If it is a function, run it and return the result. Otherwise return it as is.
    function valueOrFunctionResult(thingie, funcContext) {
        if (typeof thingie == "function") {
            return thingie.call(funcContext);
        }
        return thingie;
    };

    /**
    * Transient I/O:s do not save their value. They simply takes what comes in and distributes it to its
    * listeners. Consequently it can not be serialized. It does however accept to be deserialized to, for
    * instance from querystring.
    */
    var TransientIo = (function () {

        function Io(options) {
            this._init.apply(this, arguments);
        }

        Io.prototype._init = function (parent, name, options) {
            options = options || {};
            this.parent = parent;
            this.name = name;

            this.isValid = options.isValid || function () { return true; };
            this.reformatValidInValue = options.reformatValidInValue || function (value, prevValue) { return value; };
            this.reformatOutValue = options.reformatOutValue || function (value) { return value; };

            var serializeOptions = options.serialize || {};
            this.serializeInflateFunc = serializeOptions.funcs && serializeOptions.funcs.inflate || function (value) { return value; };

            var querystringOptions = options.querystring || {};
            this.shortQuerystringKey = valueOrFunctionResult(querystringOptions.shortKey, this);
            this.querystringSerializeFromFunc = querystringOptions.serializeFuncs && querystringOptions.serializeFuncs.from || function () { return this.getQuerystringValue(); };

            this.getDefaultValue = options.getDefaultValue;
            this.getReplacementValueForInvalidValue = options.getReplacementValueForInvalidValue || function () { return null; };
            this.getInitValue = options.getInitValue;
            this.initDataOverridesQuerystringAndCookie = options.initDataOverridesQuerystringAndCookie;

            this.reset({ doNotCallNewValueListeners: true, doNotStoreInCookie: true });

        };

        Io.prototype.set = function (inValue, options) {

            options = options || {};

            if (options.inflate) {
                inValue = this.serializeInflateFunc(inValue);
            }

            var isValid = options.suppressAllValidation || this.isValid.call(this, inValue);
            if (isValid === true) {
                inValue = this.reformatValidInValue(inValue, this.value);
            } else {
                var errorIo = this.parent.error;
                if (errorIo) {
                    errorIo.set(isValid);
                } else {
                    alert(isValid);
                }
                inValue = this.getReplacementValueForInvalidValue();
                options.context = null;
            }
            this._finalSet(inValue, options);
            return isValid;
        };

        Io.prototype._finalSet = function (inValue, options) {
            var allIsDoneCallback = options.allIsDoneCallback;
            if (!options.doNotCallNewValueListeners) {
                this._callAllListeners(this.reformatOutValue.call(this, inValue), options);
                return;
            }
            if (allIsDoneCallback) {
                allIsDoneCallback.call(context);
            }
        };


        Io.prototype._callAllListeners = function (value, options) {
            options = options || {};

            var newValueListeners = (this.newValueListeners || []).slice(), // copy with slice so that all will always be call even if the listeners array is changed mid call
                parentCallbacks = (this.allDoneListeners || []).concat([options.allIsDoneCallback || function () { }]),
                allIsDoneMultiplexer = AllIsDoneMultiplexer({ parentCallbacks: parentCallbacks, doneValueOrFunc: value, label: "_callAllListeners " + this.name });

            newValueListeners.forEach(function (listener) {

                var childOptions = {};
                for (var key in options) {
                    childOptions[key] = (key == "allIsDoneCallback") ? allIsDoneMultiplexer.getChildCallback() : options[key];
                }

                this._callListener(listener, value, childOptions);

            }, this);

            allIsDoneMultiplexer.arm();

        };

        Io.prototype._callListener = function (listener, value, options) {

            options = options || {};

            options.allIsDoneCallback = options.allIsDoneCallback || function () { };

            var allIsDoneCallback = options.allIsDoneCallback,
                callerContext = options.context,
                theListenersContext = listener.c;
            if (!callerContext || theListenersContext !== callerContext) {
                var willCallBackCallback = listener.l.call(theListenersContext, value, options); // Will return the provided callback if it intends to callback
                if (willCallBackCallback === allIsDoneCallback) {
                    return;
                }
            }
            allIsDoneCallback.call(theListenersContext); // Did not get any callback back, so it will never call back. Mark it as done by calling immediately

        };

        Io.prototype.listen = function (options, ignoreAllIsDone) {
            options = options || {};

            var allIsDoneCallback = options.allIsDoneCallback,
                newValueListener = options.newValue,
                allDoneListener = options.allDone,
                context = options.context;
            if (newValueListener) {
                this.newValueListeners = this.newValueListeners || [];
                this.newValueListeners.push({ c: context, l: newValueListener });
            }
            if (allDoneListener) {
                this.allDoneListeners = this.allDoneListeners || [];
                this.allDoneListeners.push(allDoneListener);
            }

            if (allIsDoneCallback && !ignoreAllIsDone) {
                allIsDoneCallback.call(context);
            }

        };

        Io.prototype.stopListening = function (listener) {

            var listeners = this.newValueListeners || [],
                i = listeners.findIndex(function (otherListener) { return listener == otherListener.l; });
            if (0 <= i) {
                listeners.splice(i, 1);
                return;
            }

            listeners = this.allDoneListeners || [];
            i = listeners.findIndex(function (otherListener) { return listener == otherListener; });
            if (0 <= i) {
                listeners.splice(i, 1);
                return;
            }

        };

        Io.prototype.getBaseFullPathKey = function () {
            var parent = this.parent;
            return this.shortQuerystringKey || ((parent ? (parent.getKey() + "_") : "") + this.name);
        };

        Io.prototype.getQuerystringValue = function (key) {
            key = key || this.getBaseFullPathKey();
            return OGT.Util.QueryString.getParameter(window.mapStartQueryString || window.decodeURIComponent(location.search), key);
        };

        Io.prototype.getCookieKey = function () {
            return "io_" + this.getBaseFullPathKey();
        };
        Io.prototype.getCookieValue = function () {

            return OGT.Util.Cookie.get(this.getCookieKey());

        };

        Io.prototype.reset = function (options) {

            options = options || {};
            var toInitState = options.toInitState,
                optionsForSet = {
                    suppressAllValidation: true,
                    allIsDoneCallback: options.allIsDoneCallback,
                    doNotCallNewValueListeners: options.doNotCallNewValueListeners,
                    doNotStoreInCookie: options.doNotStoreInCookie || toInitState,
                    forceUpdate: toInitState
                };

            var value;

            if (toInitState) {

                var getInitValueFunc = this.getInitValue;

                if (valueOrFunctionResult(this.initDataOverridesQuerystringAndCookie, this) && getInitValueFunc) {
                    value = getInitValueFunc.call(this);
                    if (typeof value != "undefined" && value != null) {
                        this.set(value, optionsForSet);
                        return;
                    }
                }

                value = this.querystringSerializeFromFunc();
                if (typeof value != "undefined" && value != null) {
                    optionsForSet.inflate = true;
                    this.set(value, optionsForSet);
                    return;
                }

                value = this.getCookieValue();
                if (typeof value != "undefined" && value != null) {
                    optionsForSet.inflate = true;
                    this.set(value, optionsForSet);
                    return;
                }

                if (getInitValueFunc) {
                    value = getInitValueFunc.call(this);
                    if (typeof value != "undefined" && value != null) {
                        this.set(value, optionsForSet);
                        return;
                    }
                }

            }

            var getDefaultValueFunc = this.getDefaultValue;

            if (getDefaultValueFunc) {
                value = getDefaultValueFunc.call(this);
                if (typeof value != "undefined" && value != null) {
                    this.set(value, optionsForSet);
                    return;
                }
            }

            this.set(null, optionsForSet);

        };

        return Io;

    }());


    /**
    * Persistent I/O:s are as Transient I/O:s, except they also store their value. For this reason
    * the can be asked what their value is (get), and they can be serialized in various ways.
    */
    var allPersistentIos = [],
        PersistentIo = (function () {

            function Io() {
                this._init.apply(this, arguments);
            }

            OGT.Util.extend(Io, TransientIo);

            Io.prototype._init = function (parent, name, options) {
                options = options || {};

                Io.superclass._init.apply(this, arguments);

                this.dontUpdateIfNoValueChange = options.dontUpdateIfNoValueChange;

                var serializeOptions = options.serialize || {};
                this.doSerialize = typeof serializeOptions.doe == "undefined" || serializeOptions.doe;
                this.serializeDeflateFunc = serializeOptions.funcs && serializeOptions.funcs.deflate || function (value) { return value; };
                this.serializeDefaultValue = serializeOptions.doForDefaultValue;

                this.storeInCookie = options.storeInCookie;

                var querystringOptions = options.querystring || {};
                this.querystringDoSerialize = typeof querystringOptions.doSerialize == "undefined" || querystringOptions.doSerialize;
                this.querystringSerializeToFunc = querystringOptions.serializeFuncs && querystringOptions.serializeFuncs.to || function (result, value) {
                    result[this.getBaseFullPathKey()] = value;
                };

                allPersistentIos.push(this);

            }
            Io.prototype.get = function (options) {

                options = options || {};

                var value = this.reformatOutValue.call(this, this.value);
                if (options.deflate) {
                    value = this.serializeDeflateFunc(value);
                }

                return value;
            };

            Io.prototype.refresh = function (options) {

                this.set(this.get(), options);

            };

            Io.prototype._finalSet = function (inValue, options) {

                var allIsDoneCallback = options.allIsDoneCallback;

                if (options.forceUpdate || !valueOrFunctionResult(this.dontUpdateIfNoValueChange, this) || !Object.equals(this.value, inValue)) {
                    this.value = inValue;
                    if (this.storeInCookie && !options.doNotStoreInCookie) {
                        this.setCookieValue();
                    }
                    if (!options.doNotCallNewValueListeners) {
                        this._callAllListeners(this.get(), options);
                        return;
                    }
                }
                if (allIsDoneCallback) {
                    allIsDoneCallback.call(options.context);
                }

            };

            Io.prototype.listen = function (options) {

                Io.superclass.listen.call(this, options, true);

                options = options || {};

                var allIsDoneCallback = options.allIsDoneCallback,
                    newValueListener = options.newValue,
                    context = options.context;
                if (newValueListener && !options.suppressImmediateCallback) {
                    this._callListener({ l: newValueListener, c: context }, this.get(), { allIsDoneCallback: allIsDoneCallback });
                    return;
                }
                if (allIsDoneCallback) {
                    allIsDoneCallback.call(context);
                }

            };

            Io.prototype.okForSerialization = function () {
                if (valueOrFunctionResult(this.doSerialize, this)) {
                    var value = this.get();
                    if (typeof value != "undefined" && ((this.serializeDefaultValue || !Object.equals(value, this.getDefaultValue())))) {
                        //if (typeof value != "undefined" && ((this.serializeDefaultValue || !Object.equals(value, this.getDefaultValue())) || (this.storeInCookie && !Object.equals(value, this.getCookieValue())))) {
                        return true;
                    }
                }
                return false;
            };

            Io.prototype.setCookieValue = function () {
                OGT.Util.Cookie.set(this.getCookieKey(), this.get({ deflate: true }), 365);
            };

            Io.prototype.addForQuerystring = function (result) {
                if (this.okForSerialization() && valueOrFunctionResult(this.querystringDoSerialize, this)) {
                    this.querystringSerializeToFunc(result, this.get({ deflate: true }));
                }
            };


            return Io;

        }());

    /**
    * This is a helper classed, used when one needs to multiplex a number of callbacks together. It could be
    * several different AJAX-calls which must all callback before the common callback is called.
    *
    * The common callback happens in two instances: 
    * - The arm-method is called, and every child-callback has already called back.
    * - A child callback calls backs, the arm method has already been run and no other child callback
    *   has not yet returned.
    * 
    * Consequently the arm-method is important, as callback will never happen unless it has been called.
    * Arm should be called when each child-callback needed has been retrieved. In certain fringe cases
    * it's okay to retrieve child callbacks after arm has been called. This is when one can be certain that
    * a previously retrieved child callback has not yet called back. Then it's safe to extend the number of
    * child callbacks.
    * 
    */
    var AllIsDoneMultiplexer = (function () {

        function allIsDoneMultiplexer(options) {
            this.parentCallbacks = options.parentCallbacks;
            this.doneValueOrFunc = options.doneValueOrFunc;
            this.label = options.label; //For debug purposes
            this.childCallbackStatuses = [];
        }

        // Gets a callback function which must eventually be called
        allIsDoneMultiplexer.prototype.getChildCallback = function (label) {
            var that = this,
                childCallbackStatuses = this.childCallbackStatuses,
                index = childCallbackStatuses.length;
            childCallbackStatuses[index] = label || false; // Label for debug purposes
            return function () {
                // Register this callback as being done
                childCallbackStatuses[index] = true;
                that.maybeDone();
            };
        };

        // If we have armed it, we have not previously called the parent callback, and each and every child callback has called back, then call the parent callbacks.
        allIsDoneMultiplexer.prototype.maybeDone = function () {
            var parentCallbacks = this.parentCallbacks;
            if (this.armed && !this.done && (parentCallbacks.length == 0 || this.childCallbackStatuses.every(function (status) { return status === true; }))) {

                // For debugging purposes
                //window.clearTimeout(this.giveUpTimer);

                this.done = true;
                var value = valueOrFunctionResult(this.doneValueOrFunc, this);
                parentCallbacks.forEach(function (parentCallback) {
                    parentCallback(value);
                });

                return true;
            }
            return false;
        };

        // Make ready to call back. It could be that all child callbacks have already called back. If so, the parent callbacks will run immediately.
        allIsDoneMultiplexer.prototype.arm = function () {
            this.armed = true;

            this.maybeDone();

            // For debugging purposes
            //if (!this.maybeDone()) {
            //    var that = this;
            //    this.giveUpTimer = window.setTimeout(function() {
            //        console.log("give up", that.label, that.childCallbackStatuses);
            //    }, 1000);
            //}

        };

        return function (options) {
            return new allIsDoneMultiplexer(options);
        };

    }());

    var onlyDigitsRegex = /^\d+$/;

    // Functions to rewrite a valid in-value to some internal representation.
    var ReformatValidInValueNs = (function () {

        function getIdItemReformatter(options) {

            options = options || {};

            var idIsAlwaysNumber = options.idIsAlwaysNumber;
            var minHighlitLevel = options.minHighlitLevel;
            var getColourFunc = options.getColourFunc;

            return function (idOrOptions) {

                if (idOrOptions) {

                    if (typeof idOrOptions != "object") {
                        if (idIsAlwaysNumber && typeof idOrOptions == "string") {
                            idOrOptions = parseInt(idOrOptions);
                        }
                        idOrOptions = { id: idOrOptions };
                    }

                    if (getColourFunc) {
                        idOrOptions.colour = getColourFunc(idOrOptions.id);
                    }

                    if (minHighlitLevel) {
                        idOrOptions.highlitLevel = Math.max(idOrOptions.highlitLevel || 0, minHighlitLevel);
                    }

                }

                return idOrOptions;

            }

        }

        function getIdItemsReformatter(formatter) {

            return function (inValue) {

                inValue.forEach(function (idOrOptions, index) {
                    inValue[index] = formatter(idOrOptions);
                });

                return inValue;

            }

        }

        function getStopReformatter(options) {

            options = options || {};

            options.getColourFunc = OGT.Proxy.Util.getColourForStop;
            options.idIsAlwaysNumber = true;

            return getIdItemReformatter(options);

        }

        function getLineReformatter(options) {

            options = options || {};

            options.getColourFunc = OGT.Proxy.Util.getColourForLine;
            options.idIsAlwaysNumber = true;

            return getIdItemReformatter(options);

        }

        return {

            getIdItemFunc: getIdItemReformatter,

            getStopFunc: getStopReformatter,

            stops: getIdItemsReformatter(getStopReformatter()),

            getLineFunc: getLineReformatter,

            lines: getIdItemsReformatter(getLineReformatter()),

            // To fetch a colour
            getColourFunc: function (options) {

                options = options || {};

                var takeColour = options.takeColour,
                    propertiesDecidingEquality = options.propertiesDecidingEquality || ["id"],
                    handBackColourWhenNotUsedBeforeRequestingNewColours = options.handBackColourWhenNotUsedBeforeRequestingNewColours,
                    handBackColourWhenNotUsedAfterRequestingNewColours = options.handBackColourWhenNotUsedAfterRequestingNewColours;

                function getAggregatedId(options) {
                    return propertiesDecidingEquality.reduce(function (acc, propName) { return acc + "_" + (options[propName] || ""); }, "");
                }

                function handBackUnusedColours(usedItems, colourMapping) {

                    for (var id in colourMapping) {

                        if (!usedItems.some(function (itemOptions) {
                            return id == getAggregatedId(itemOptions);
                        })) {
                            OGT.Proxy.ColourPool.handBack(colourMapping[id]);
                            delete colourMapping[id];
                        };
                    }

                }

                return function (inValue) {

                    var colourMapping = this.colourMapping = this.colourMapping || {};

                    inValue.forEach(function (idOrOptions, index) {
                        if (typeof idOrOptions != "object") {
                            if (typeof idOrOptions == "string") {
                                if (onlyDigitsRegex.test(idOrOptions)) {
                                    idOrOptions = parseInt(idOrOptions);
                                }
                            }
                            inValue[index] = { id: idOrOptions };
                        }
                    });

                    if (handBackColourWhenNotUsedBeforeRequestingNewColours) {
                        handBackUnusedColours(inValue, colourMapping);
                    }

                    var result = inValue.map(function (options) {

                        var id = getAggregatedId(options),
                            colour = colourMapping[id];

                        if (!colour) {

                            colour = OGT.Proxy.ColourPool.take((typeof takeColour == "function") ? takeColour(options) : takeColour);
                            colourMapping[id] = colour;

                        }

                        options.colour = colour;

                        return options;

                    }, this);

                    if (handBackColourWhenNotUsedAfterRequestingNewColours) {
                        handBackUnusedColours(inValue, colourMapping);
                    }

                    return result;

                }
            }
        }
    }());


    // Functions for serializing/deserializing values for Querystring. This in some cases involves multiple querystring values turning to one I/O
    // and one I/O truning to multiple querystring values.
    var Querystring = {
        Serializers: {
            ll: {
                from: function () {
                    var value = this.getQuerystringValue();
                    if (value) {
                        return value.split(',').map(function (s) { return parseFloat(s); });
                    }
                    return null;
                },
                to: function (result, ll) {
                    if (ll) {
                        result[this.getBaseFullPathKey()] = OGT.Util.latLngArrayToUrlValue(ll);
                    }
                }
            },
            array: {
                from: function () {
                    var value = this.getQuerystringValue();
                    if (value) {
                        return value.split(',');
                    }
                    return null;
                },
                to: function (result, value) {
                    if (value && value.length) {
                        result[this.getBaseFullPathKey()] = value.join(",");
                    }
                }
            },
            number: {
                from: function () {
                    var value = this.getQuerystringValue();
                    if (value) {
                        return parseInt(value);
                    }
                    return null;
                },
                to: function (result, value) {
                    if (typeof value == "number") {
                        result[this.getBaseFullPathKey()] = "" + value;
                    }
                }
            },
            boolean: {
                from: function () {
                    var querystringValue = this.getQuerystringValue();
                    switch (querystringValue) {
                        case "true":
                            return true;
                        case "false":
                            return false;
                        default:
                            return null;
                    }
                },
                to: function (result, value) {
                    if (typeof value != 'undefined' && value != null) {
                        result[this.getBaseFullPathKey()] = (value ? "true" : "false");
                    }
                }
            },
            flatDictionary: {
                from: function () {
                    var qValue = this.getQuerystringValue();
                    if (qValue) {
                        var result = {};
                        qValue.split(",").forEach(function (keyValueString) {
                            var keyValue = keyValueString.split(":");
                            result[keyValue[0]] = keyValue[1];
                        });
                        return result;
                    }
                    return null;
                },
                to: function (result, dict) {
                    var out = [];
                    for (var key in dict) {
                        out.push(key + ":" + dict[key]);
                    }
                    out = out.join(",");
                    if (out.length) {
                        result[this.getBaseFullPathKey()] = out;
                    }
                }
            },
            flatBooleanDictionary: {
                from: function () {
                    var qValue = this.getQuerystringValue();
                    if (qValue) {
                        var result = {};
                        qValue.split(",").forEach(function (key) {
                            result[key] = true;
                        });
                        return result;
                    }
                    return null;
                },
                to: function (result, dict) {
                    var out = [];
                    for (var key in dict) {
                        if (dict[key]) {
                            out.push(key);
                        }
                    }
                    out = out.join(",");
                    if (out.length) {
                        result[this.getBaseFullPathKey()] = out;
                    }
                }
            },
            place: {
                from: function () {
                    var id = this.getQuerystringValue(),
                        rawLl = this.getQuerystringValue(this.getBaseFullPathKey() + "Ll"),
                        result = {};

                    if (!id && !rawLl) {
                        return null;
                    }

                    if (id) {
                        result.Id = id;
                    }
                    result.PlaceName = this.getQuerystringValue(this.getBaseFullPathKey() + "Name") || OGT.Language.translate('/searchjourney/placetypes/custom');
                    result.OgtType = this.getQuerystringValue(this.getBaseFullPathKey() + "Type") || (id ? "stop" : "custom");

                    if (rawLl) {
                        result.Ll = rawLl.split(',').map(function (s) { return parseFloat(s); });
                    }

                    return result;

                },
                to: function (result, value, fullPathKey) {
                    if (value) {
                        var type = value.OgtType,
                            baseQueryStringKey = fullPathKey || this.getBaseFullPathKey(),
                            typeQueryStringKey = baseQueryStringKey + "Type",
                            nameQueryStringKey = baseQueryStringKey + "Name",
                            llQueryStringKey = baseQueryStringKey + "Ll";

                        delete result[baseQueryStringKey];
                        delete result[typeQueryStringKey];
                        delete result[nameQueryStringKey];
                        delete result[llQueryStringKey];


                        if (type == "stop" && value.OgtStopUrlSegment) {

                            result[baseQueryStringKey] = value.OgtStopUrlSegment;

                        } else {

                            if (value.Id) {
                                result[baseQueryStringKey] = value.Id;
                            }

                            result[typeQueryStringKey] = type;

                            result[nameQueryStringKey] = value.PlaceName;
                            var ll = value.Ll;
                            if (ll) {
                                result[llQueryStringKey] = OGT.Util.latLngArrayToUrlValue(ll);
                            }
                        }
                    }
                }
            },
            time: { // As : is not allowed in URL-segments
                from: function () {
                    var qValue = this.getQuerystringValue();
                    if (qValue) {
                        return qValue.replace(".", ":");
                    }
                    return null;
                },
                to: function (result, value, fullPathKey) {
                    if (value) {
                        result[fullPathKey || this.getBaseFullPathKey()] = value.replace(":", ".");
                    }
                }
            },
            selectedItems: {
                from: function () {
                    var value = this.getQuerystringValue();
                    if (value) {
                        return value.split(',').map(function (id) {

                            var highlitLevel = 0;
                            while (id.substr(0, 1) == "!") {
                                highlitLevel++;
                                id = id.substr(1);
                            }

                            if (onlyDigitsRegex.test(id)) {
                                id = parseInt(id);
                            }

                            return {
                                id: id,
                                highlitLevel: highlitLevel
                            };
                        });
                    }
                    return null;
                },
                to: function (result, value) {

                    if (value && value.length) {

                        result[this.getBaseFullPathKey()] = value.map(function (p) {
                            return [].fillAndCreate("!", p.highlitLevel).join('') + p.id;
                        }).join(",");

                    }
                }
            }

        }

    }

    // Functions used for basic serialization/deserialization of values. Used for history, cookie and before querystring.
    var Serialize = {

        Funcs: {

            idType: {
                deflate: function (value) {
                    return value.map(function (item) {
                        return {
                            id: OGT.Util.OGT.Filters.extractLowerCaseId(item),
                            highlitLevel: item.highlitLevel
                        };
                    });
                }
            },
            intAsString: {
                deflate: function (value) {
                    return parseInt(value);
                },
                inflate: function (value) {
                    return "" + value;
                }
            },
            place: {
                deflate: (function () {

                    var acceptableTypes = ["FsType", "Id", "Ll", "Municipality", "NameClashDefiningAttribute", "OgtStopUrlSegment", "OgtType", "PlaceName", "TaxeZone", "priority", "priority"];

                    return function (value) {

                        var result = {};

                        for (var key in value) {
                            if (acceptableTypes.includes(key)) {
                                result[key] = value[key];
                            }
                        }

                        return result;

                    };

                }())
            }

        }

    };

    return {
        Persistent: PersistentIo,
        Transient: TransientIo,
        Util: {
            Querystring: Querystring,
            Serialize: Serialize,
            ReformatValidInValue: ReformatValidInValueNs
        },
        AllIsDoneMultiplexer: AllIsDoneMultiplexer
    };

}());
;
window.OGT = window.OGT || {};

window.OGT.Proxy = window.OGT.Proxy || {};


/**
* A common pool of colours used by different parts of the map.
*/

window.OGT.Proxy.ColourPool = (function () {

    //var availableColours = ["#ff3333", "#489797", "#f17b14", "#d043cb", "#3871e6", "#ff8585", "#91c1c1", "#f7b072", "#e38ee0", "#88aaf0", "#cc2929", "#3a7979", "#c16210", "#a636a2", "#2d5ab8"].map(function (colourCode) {
    //var availableColours = ["#ff3333", "#489797", "#f17b14", "#d043cb", "#3871e6"].map(function (colourCode) {

    var byName = {
        orange: "#EC6B00",
        blue: "#00009B",
        green: "#00BD00",
        purple: "#BD006B",
        red: "#ff3333"
    };


    var availableColours = [
        byName.orange,
        byName.blue,
        byName.green,
        byName.purple,
        "#057571",
        "#d80d10",
        "#7f2600",
        "#d615a9",
        "#868c0f",
        "#7a17a5",
        "#2d70bc",
        "#db811a",
        "#b21c35",
        "#a05add",
        "#28af82",
        "#ea6478",
        "#b54e34",
        "#6454f2",
        "#cc3f43",
        "#19aa64",
        "#7929ce",
        "#fc7844",
        "#ea56cf",
        "#4087ad",
        "#b76d31",
        "#144de8",
        byName.red,
        "#c88500",
        "#d15373",
        "#4e6bed",
        "#68af21",
        "#d16ded",
        "#e8921b",
        "#0a5b99",
        "#e51266",
        "#047a39",
        "#729fff",
        "#f46461",
        "#008e8e",
        "#7e52af",
        "#889b09",
        "#ea79d0",
        "#916dff",
        "#0dd84a",
        "#fa05ff"
    ].map(function (colourCode) {
        return {
            code: colourCode,
            takenCount: 0
        };
    }),
    bearableTakenNumber = 0;

    function take(takeColour) {

        if (takeColour) {

            var takeColourObject = availableColours.find(function (aColourObject) { return takeColour == aColourObject.code; });

            if (takeColourObject) {
                takeColourObject.takenCount++;
            }
            return takeColour;

        }

        do {

            var colourObject = availableColours.find(function (aColourObject) { return aColourObject.takenCount <= bearableTakenNumber; });

            if (colourObject) {
                colourObject.takenCount++;
                return colourObject.code;
            }

        } while (++bearableTakenNumber)

        return null;

    }

    function handBack(colour) {

        var colourObject = availableColours.find(function (aColourObject) { return colour === aColourObject.code; });
        if (colourObject) {
            colourObject.takenCount = bearableTakenNumber;
        }

    }

    return {
        take: take,
        handBack: handBack
    };

}());
;
window.OGT = window.OGT || {};

window.OGT.Proxy = window.OGT.Proxy || {};

/**
* This handles the manipulation of the browser history, in web browsers which handles it.
*/

window.OGT.Proxy.History = (function () {

    var suppressPushState = true, // Suppressed when the web page is starting and when poping a state, to avoid poping or initing in turn push history state
        popInProgress = false, // To stop multiple pops to happen at once and destroy for each other
        nextPop = null; // Waiting pop-operation, if a pop is currently in progress.

    function pushOrReplace(isPush) {

        if (window.manipulateHistory && !suppressPushState) {

            var updatedUrl = OGT.Proxy.updateQuerystring(window.currentPagePathWithoutExtraSegments),
                decodedCurrentHref = decodeURI(location.href);

            if ((decodedCurrentHref.substr(decodedCurrentHref.length - updatedUrl.length) != updatedUrl)) {

                //fippla med urlen om det går, fallbacka till att ladda om sidan med querystrings på.
                if (isPush ? window.history.pushState : window.history.replaceState) {

                    try {

                        if (isPush) {
                            window.history.pushState({ url: updatedUrl, iosSerialized: OGT.Proxy.serialize() }, "", updatedUrl);
                        } else {
                            window.history.replaceState({ url: updatedUrl, iosSerialized: OGT.Proxy.serialize() }, "", updatedUrl);
                        }

                        //method({ url: updatedUrl, iosSerialized: OGT.Proxy.serialize() }, "", updatedUrl);

                    } catch (err) {
                        //Fallback
                        if (isPush) {
                            window.location = updatedUrl;
                        }
                    }
                } else if (isPush) {
                    // IE fallback
                    window.location = updatedUrl;
                }

            }
        }
        
    }

    function push() {
        pushOrReplace(true);
    }

    function replace() {
        pushOrReplace(false);
    }

    function suppressPush(doSuppress, stillAllowAnimations) {
        suppressPushState = doSuppress;
        $.fx.off = doSuppress && !stillAllowAnimations;
    }


    if (window.manipulateHistory) {

        function stateRestoringDone() {

            popInProgress = false;
            suppressPush(false);

            doPop();
        }

        function doPop() {

            if (!popInProgress && nextPop) {

                popInProgress = true;

                var event = nextPop.event,
                    planB = true;

                nextPop = false;
                suppressPush(true);

                if (event) {
                    var state = event.state;
                    if (state) {
                        var iosSerialized = state.iosSerialized;
                        if (iosSerialized) {

                            // deserialize, has state

                            OGT.Proxy.deserialize(iosSerialized, {
                                allIsDoneCallback: stateRestoringDone
                            });

                            planB = false;
                        }
                    }
                }

                if (planB) {

                    // reset, as we have no state (propably we are in the beginning of history)

                    OGT.Proxy.resetToInitState(stateRestoringDone);

                }

            }

        }


        window.onpopstate = function (event) {

            nextPop = { event: event };
            doPop();

        };

    }

    return {
        push: push,
        replace: replace,
        suppressPush: suppressPush
    };

}());
;
window.OGT = window.OGT || {};

window.OGT.Proxy = window.OGT.Proxy || {};

/**
* The proxy and its modules is used as a cross connect and state holder for various values used
* throughout the site. Its purpose was originally to hold settings needed by the map-functionality
* before the map-functionality was loaded, but now it is also used for serialization, reseting and
* similar tasks.
*/


(function () {

    var thisNameSpace = window.OGT.Proxy;


    var modules = [];

    function initAllModules() {

        modules.forEach(function (modu) {
            modu._init();
        });

        resetToInitState(function () {
            //Start pushing
            OGT.Proxy.History.suppressPush(false);
        });

        modules.forEach(function (modu) {
            modu.connect();
        });

    }


    var resetToInitState = (function () {

        function doIt(parentAllIsDoneCallback) {

            resetAllIos({
                toInitState: true,
                allIsDoneCallback: function () {

                    var journeyProxy = OGT.Proxy.journey;
                    if (journeyProxy) {
                        journeyProxy.search.set({
                            doe: {
                                action: "doe",
                                waitForIt: true
                            }
                        },
                        {
                            allIsDoneCallback: parentAllIsDoneCallback
                        });
                        return;
                    }

                    parentAllIsDoneCallback();

                }
            });

        }

        return function (parentAllIsDoneCallback) {

            var mapAdapter = OGT.MapAdapter;

            if (mapAdapter && mapAdapter.whileDoingThisConsiderMapCenterAndZoomAsBeingNonInteractive) {

                mapAdapter.whileDoingThisConsiderMapCenterAndZoomAsBeingNonInteractive(function () {

                    doIt.call(this, parentAllIsDoneCallback);

                }, this);


            } else {

                doIt.call(this, parentAllIsDoneCallback);

            }

        }

    }());


    function addModule(moduleClass) {

        var key = moduleClass.getKey();

        if (thisNameSpace[key]) {
            console.error("this key is already in use");
            return;
        }

        var moduleInstance = new moduleClass();
        moduleInstance.ofClass = moduleClass;
        thisNameSpace[key] = moduleInstance;
        modules.push(moduleInstance);

    }

    function allModules() {
        return modules;
    }

    function serialize() {
        var result = {};

        modules.forEach(function (modu) {
            modu.serialize(result);
        });

        return result;
    }

    function deserialize(data, options) {

        var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({
            parentCallbacks: [options.allIsDoneCallback],
            label: "Proxy serialize"
        });

        modules.forEach(function (modu) {

            var childOptions = OGT.Util.dictClone(options);
            childOptions.allIsDoneCallback = allIsDoneMultiplexer.getChildCallback(modu.getKey());

            modu.deserialize(data, childOptions);

        });

        allIsDoneMultiplexer.arm();

    }


    function updateQuerystring(url, keyValues) {

        keyValues = keyValues || getForQuerystring();

        (window.querystringToPathReplacementsEvaluationFunctions || []).forEach(function (qsMoveFunc) {

            qsMoveFunc(keyValues).forEach(function (moveKey) {

                url += keyValues[moveKey] + "/";
                delete keyValues[moveKey];

            });

        });

        return OGT.Util.QueryString.updateWithDict(keyValues, url);

    }


    function getForQuerystring() {
        var result = {};

        modules.forEach(function (modu) {
            modu.addForQuerystring(result);
        });

        return result;
    }

    function resetAllIos(options) {

        var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({
            parentCallbacks: [options.allIsDoneCallback],
            label: "Proxy resetAllIos"
        });

        modules.forEach(function (modu) {

            var childOptions = OGT.Util.dictClone(options);
            childOptions.allIsDoneCallback = allIsDoneMultiplexer.getChildCallback(modu.getKey());

            modu.resetAllIos(childOptions);
        });

        allIsDoneMultiplexer.arm();

    }


    thisNameSpace._init = initAllModules;
    thisNameSpace.resetToInitState = resetToInitState;
    thisNameSpace.addModule = addModule;
    thisNameSpace.allModules = allModules;
    thisNameSpace.serialize = serialize;
    thisNameSpace.deserialize = deserialize;
    thisNameSpace.updateQuerystring = updateQuerystring;
    thisNameSpace.getForQuerystring = getForQuerystring;
    thisNameSpace.resetAllIos = resetAllIos;

    return thisNameSpace;

}());

;
window.OGT.Proxy.ProxyModuleBase = (function () {

    /**
    * Base class for proxy modules
    */
    function ProxyModuleBase() {
    }

    ProxyModuleBase.prototype.postInit = function (options) {
    };

    ProxyModuleBase.prototype.postConnect = function () {
    };

    ProxyModuleBase.prototype.getKey = function () {
        console.error("getKey must be overridden in derived class");
    };

    // Convencience function for creating I/O
    ProxyModuleBase.prototype.makeAndAddPersistentIo = function (name, options) {
        var io = new OGT.Proxy.Io.Persistent(this, name, options);
        this[name] = io;
        this.persistantIosByName = this.persistantIosByName || {};
        this.persistantIosByName[name] = io;



        //io.listen({
        //    allDone: function(value) {
        //        //console.log(name + " is all done " + value + " " + OGT.Proxy.updateQuerystring(window.currentPagePathWithoutExtraSegments));

        //        OGT.Proxy.History.replace();
        //    }
        //});

        return io;
    };

    // Convencience function for creating I/O
    ProxyModuleBase.prototype.makeAndAddTransientIo = function (name, options) {
        var io = new OGT.Proxy.Io.Transient(this, name, options);
        this[name] = io;
        return io;
    };

    /*
    * Convenience function for creating an I/O which aggregates a number of different value into one.
    * Internally it will keep track of all values, but when get is called it will return an aggregated
    * version of the values.
    * General info about PersistingAggregatingIo:
    * The value to set on the IO is a simple object consisting of two parts: "action" and "data".
    * "action" can have the values: "add", "remove", "reset" and "refresh".
    * "add" will add the value given in "data" to the internal array.
    * "remove" will remove the value given in "data" from the internal array. Please note
    * that the value in "data" is compared using the === operator. That means that if the
    * value in the internal array is a primitive type such as a number or string it suffice
    * that they look the same, whereas if the value is a real object the object reference must be
    * the same. That is, the object which has once been added must also be the one passed when
    * removing. You need to store a reference to it.
    * "reset" will reset the internal array to a new empty array.
    * "refresh" will trigger the mechanics evaluating whether the values have changed and should
    * be propagated to those listening. This is useful when the values in the internal array
    * have been changed externally and one wants these changes to propagate.
    * The value got when doing a get or listening is an aggregation of all the values in the
    * internal array. This aggregation is done using the aggregator function passed when the
    * I/O is created.
    */

    ProxyModuleBase.prototype.makeAndAddPersistentAggregatingIo = function(name, aggregatorFunction, options) {

        options = options || {};

        options.getDefaultValue = function() { return {}; };

        options.reformatValidInValue = function(commandAndData, internalValues) {
            this.dirty = true;

            internalValues = internalValues || [];

            var commandDatas = commandAndData.datas;

            switch (commandAndData.action) {
                case "add":
                    if (!internalValues.some(function(otherValue) {
                        return otherValue === commandDatas;
                    })) {
                        internalValues.push(commandDatas);
                    }
                    break;
                case "remove":
                    var i = internalValues.indexOf(commandDatas);
                    if (0 <= i) {
                        internalValues.splice(i, 1);
                    }
                    break;
                case "reset":
                    internalValues = [];
                    break;
                case "refresh":
                    break;
            }

            return internalValues;
        };

        var originalReformatOutValue = options.reformatOutValue || function(value) { return value; };
        options.reformatOutValue = function(internalValues) {

            if (this.dirty || !this.cachedResult) {

                internalValues = internalValues || [];

                var result = internalValues.reduce(function(agg, curr) {
                     return agg.concat(curr);
                }, []).reduce(aggregatorFunction, []);

                result = originalReformatOutValue(result);

                this.dirty = false;
                this.cachedResult = result;

            }

            return this.cachedResult;
        };


        return this.makeAndAddPersistentIo(name, options);

    };


    ProxyModuleBase.prototype.isMapVisible = function () {

        var mapProxy = OGT.Proxy.map;
        if (!mapProxy) {
            return false;
        }

        var visible = mapProxy.visible.get(),
            fullscreen = mapProxy.fullscreen.get();

        return (visible.ise || visible.doe) && (fullscreen.ise !== false);

    };


    ProxyModuleBase.prototype.serialize = function (outerResult) {

        var result = {},
            empty = true,
            persistantIosByName = this.persistantIosByName || {};
        for (var name in persistantIosByName) {
            var io = persistantIosByName[name];
            if (io.okForSerialization()) {
                empty = false;
                result[name] = io.get({
                    deflate: true
                });
            }
        }
        if (!empty) {
            outerResult[this.getKey()] = result;
        }

    };

    ProxyModuleBase.prototype.deserialize = function (data, options) {
        var key = this.getKey(),
            forThis = data[key];

        if (forThis) {

            var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({ parentCallbacks: [options.allIsDoneCallback], label: key }),
                persistantIosByName = this.persistantIosByName || {};
            for (var name in persistantIosByName) {
                var io = persistantIosByName[name],
                    value = forThis[name],
                    allIsDoneChildCallback = allIsDoneMultiplexer.getChildCallback(name);
                if (typeof value == "undefined") {
                    io.reset({
                        allIsDoneCallback: allIsDoneChildCallback
                    });
                } else {
                    io.set(value, {
                        suppressAllValidation: true,
                        inflate: true,
                        allIsDoneCallback: allIsDoneChildCallback
                    });
                }
            }

            allIsDoneMultiplexer.arm();

        } else {
            this.resetAllIos(options);
        }

    };

    ProxyModuleBase.prototype.addForQuerystring = function (result) {

        var persistantIosByName = this.persistantIosByName || {};
        for (var name in persistantIosByName) {
            persistantIosByName[name].addForQuerystring(result);
        }

    };

    ProxyModuleBase.prototype.getForQuerystring = function () {
        var result = {};
        this.addForQuerystring(result);
        return result;
    }

    ProxyModuleBase.prototype.updateQuerystring = function (url, specialHandler) {

        var keyValues = this.getForQuerystring();

        if (specialHandler) {
            url = specialHandler(url, keyValues);
        }

        for (var key in keyValues) {
            url = OGT.Util.QueryString.update(key, keyValues[key], url);
        }
        return url;

    }


    ProxyModuleBase.prototype.resetAllIos = function(options) {

        var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({
                parentCallbacks: [options.allIsDoneCallback],
                label: this.getKey()
            }),
            persistantIosByName = this.persistantIosByName || {};

        for (var name in persistantIosByName) {
            var childOptions = OGT.Util.dictClone(options);
            childOptions.allIsDoneCallback = allIsDoneMultiplexer.getChildCallback(name);
            persistantIosByName[name].reset(childOptions);
        }
        allIsDoneMultiplexer.arm();

    };


    return ProxyModuleBase;

}());
;
window.OGT.Proxy.ProxyModuleHideableBase = (function () {

    /**
    * Base class for proxy modules
    */
    function ProxyModuleHideableBase() {
    }
    OGT.Util.extend(ProxyModuleHideableBase, OGT.Proxy.ProxyModuleBase);

    ProxyModuleHideableBase.prototype.postInit = function () {

        ProxyModuleHideableBase.superclass.postInit.apply(this, arguments);

        /**
        * This I/O is used for hiding all stops when in their default state
        * that is, not highlit or something like that
        */
        this.makeAndAddPersistentIo("hidden", {
            getDefaultValue: function () { return false; },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean
            },
            doSerialize: this.isMapVisible
        });

    };

    return ProxyModuleHideableBase;

}());
;
window.OGT.Proxy = window.OGT.Proxy || {};

window.OGT.Proxy.ProxyModuleLinesAndNartrafikBase = (function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleHideableBase);

    Proxy.prototype._init = function () {

        /**
        * This I/O is used for showing lines on the map. 
        * The value is an array of simple objects on the form
        * { id: X, colour: Y, highlitLevel: Z }
        * where 
        *   id is the line id
        *   colour is a colour object. It is automatically assigned if not explicitly stated.
        *   highlitLevel is a number showing if the line should be extra highlit.
        */
        this.makeAndAddPersistentIo("selected", {
            getDefaultValue: function () { return []; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.lines,
            serialize: {
                funcs: OGT.Proxy.Io.Util.Serialize.Funcs.idType
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.selectedItems,
                doSerialize: this.isMapVisible
            }
        });

        /**
         */
        this.makeAndAddPersistentIo("extraHighlit", {

            getDefaultValue: function () { return null; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getLineFunc({ minHighlitLevel: OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.MAP_MENU_CONTEXT.LINE }),
            querystring: {
                doSerialize: false
            }

        });

        /**
        * This I/O is used for hiding all stops when in their default state
        * that is, not highlit or something like that
        */
        this.makeAndAddPersistentIo("showStops", {
            getDefaultValue: function () { return true; },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean
            },
            doSerialize: this.isMapVisible
        });

        /**
        * Through this I/O command to zoom in on a line is communicated. Value is a line id.
        */
        this.makeAndAddTransientIo("zoomTo");

        this.postInit();

    };

    Proxy.prototype.connect = function () {

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: this.selected
        });

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: this.extraHighlit
        });

        this.postConnect();

    };

    return Proxy;

}());
;
window.OGT.Proxy.ProxyModuleSimpleMarkerLayerBase = (function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleHideableBase);

    Proxy.prototype._init = function () {

        var that = this;

        this.makeAndAddPersistentIo("selected", {

            getDefaultValue: function () { return []; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getColourFunc({
                takeColour: function (options) {
                    return that.getAllTypes().find(function (type) { return options.id == type.key; }).colour;
                }
            }),
            serialize: {
                funcs: OGT.Proxy.Io.Util.Serialize.Funcs.idType
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.selectedItems,
                doSerialize: this.isMapVisible
            }
        });

        /**
        */
        this.makeAndAddPersistentIo("extraHighlit", {

            getDefaultValue: function () { return null; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getIdItemFunc({ minHighlitLevel: OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.MAP_MENU_CONTEXT.SIMPLE_MARKER_LAYER }),
            querystring: {
                doSerialize: false
            }

        });

        this.postInit();
    };

    Proxy.prototype.connect = function () {

        var highlightHandler = OGT.Proxy.Util.HighlightHandler;

        highlightHandler.addIo({
            proxy: this,
            io: this.selected
        });

        highlightHandler.addIo({
            proxy: this,
            io: this.extraHighlit
        });


        this.postConnect();

    };

    return Proxy;

}());
;
window.OGT.Proxy = window.OGT.Proxy || {};

window.OGT.Proxy.ProxyModuleZonesBase = (function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleHideableBase);

    Proxy.prototype._init = function () {

        /**
        * This I/O is used for showing zones on the map.
        * The value is an array of simple objects on the form
        * { id: X, colour: Y, highlitLevel: Z }
        * where 
        *   id is the id
        *   colour is a colour object. It is automatically assigned if not explicitly stated.
        *   highlitLevel is a number showing if the zone should be extra highlit.
        */
        this.makeAndAddPersistentIo("selected", {
            getDefaultValue: function () { return []; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getColourFunc(),
            serialize: {
                funcs: OGT.Proxy.Io.Util.Serialize.Funcs.idType
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.selectedItems,
                doSerialize: this.isMapVisible
            }
        });


        /**
        */
        this.makeAndAddPersistentIo("extraHighlit", {

            getDefaultValue: function () { return null; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getIdItemFunc({ minHighlitLevel: OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.MAP_MENU_CONTEXT.ZONE }),
            querystring: {
                doSerialize: false
            }

        });


        /**
        * Through this I/O command to zoom in on a zone is communicated. Value is a id.
        */
        this.makeAndAddTransientIo("zoomTo");

        this.postInit();
    };

    Proxy.prototype.connect = function () {

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: this.selected
        });

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: this.extraHighlit
        });

        this.postConnect();

    };

    return Proxy;

}());;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleHideableBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "stops";
    };

    Proxy.prototype._init = function () {

        /**
        * This I/O is used for highlighting stops on the map. 
        * The value is an array of simple objects on the form
        * { id: X, colour: Y, highlitLevel: Z }
        * where 
        *   id is the stop id
        *   colour is a colour object. It is automatically assigned if not explicitly stated.
        *   highlitLevel is a number showing if the stop should be extra highlit.
        */
        this.makeAndAddPersistentIo("selected", {

            getDefaultValue: function () { return []; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.stops,
            serialize: {
                funcs: OGT.Proxy.Io.Util.Serialize.Funcs.idType
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.selectedItems,
                doSerialize: this.isMapVisible
            }

        });

        /**
         */
        this.makeAndAddPersistentIo("extraHighlit", {

            getDefaultValue: function () { return null; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getStopFunc({ minHighlitLevel: OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.MAP_MENU_CONTEXT.STOP}),
            querystring: {
                doSerialize: false
            }

        });

        /**
        * This I/O is used for enabling and disabling the zoom filter of stops.
        */
        this.makeAndAddPersistentIo("filter", {

            getDefaultValue: function () { return true; },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean,
                doSerialize: this.isMapVisible
            }

        });

        function aggregateStopPoints(aggregateTarget, currentStopData) {

            var currentStopPointIds = currentStopData.stopPointIds || [];

            if (currentStopPointIds == "all" || aggregateTarget.stopPointIds == "all") {

                aggregateTarget.stopPointIds = "all";

            } else {

                var usedStopStopPointIds = aggregateTarget.stopPointIds = aggregateTarget.stopPointIds || [];

                currentStopPointIds.forEach(function (stopPointId) {
                    usedStopStopPointIds.pushIfNotInArray(stopPointId);
                });

            }

        }

        /**
        * This I/O is used to highlight stops on the map. Where the "selected" I/O is
        * used only to highlight invididual stops the I/O is used for more advanced
        * highlighting. This is for instance when an entire journey shall be highlit. In
        * that case the highlighting is a mix of the journeypath and the stops being
        * highlit on the map.
        * 
        * Please read the documentation on "makeAndAddPersistentAggregatingIo"
        * 
        * The "data" values you supply when adding or removing are in this case arrays
        * of stop-highlighting details. Se the I/O "selected" for details on this.
        * The aggregated value got when getting or listening is an aggregated array
        * of highlight details, without duplicates, and with the most specific settings
        * for colour and highlit being used.
        */
        this.makeAndAddPersistentAggregatingIo("selectedItemsAggregated",

            function (aggregated, currentStopData) {

                var id = currentStopData.id,
                    existingStopData = aggregated.find(function (otherStopData) {
                        return id == otherStopData.id;
                    });

                if (!existingStopData) {
                    existingStopData = {
                        id: id,
                        coloursAndHighlights: []
                    };
                    aggregated.push(existingStopData);
                }

                var coloursAndHighlights = existingStopData.coloursAndHighlights,
                    colour = currentStopData.colour,
                    existingColourAndHighlight;

                if (colour) {

                    existingColourAndHighlight = coloursAndHighlights.find(function (cAndF) {
                        return colour == cAndF.colour;
                    });

                    if (!existingColourAndHighlight) {
                        existingColourAndHighlight = {
                            colour: colour
                        };
                        coloursAndHighlights.push(existingColourAndHighlight);
                    }

                    existingColourAndHighlight.highlitLevel = Math.max(existingColourAndHighlight.highlitLevel || 0, currentStopData.highlitLevel || 0);

                    aggregateStopPoints(existingColourAndHighlight, currentStopData);

                }

                coloursAndHighlights.sort(function (left, right) {

                    var leftHighlitLevel = left.highlitLevel || 0,
                        rightHighlitLevel = right.highlitLevel || 0;

                    return leftHighlitLevel === rightHighlitLevel ? 0 : (leftHighlitLevel < rightHighlitLevel ? 1 : -1);

                });

                if (coloursAndHighlights.length && !existingStopData.showStopPointIdExplicit && currentStopData.showStopPointExplicit) {

                    var firstStopPointId = coloursAndHighlights[0].stopPointIds;
                    if (firstStopPointId != "all") {
                        existingStopData.showStopPointIdExplicit = coloursAndHighlights[0].stopPointIds;
                    }
                }

                return aggregated;

            },
            {
                serialize: {
                    doe: false
                }
            }

        );


        /**
        * Through this I/O command to zoom in on a stop is communicated. Value is a stop id.
        */
        this.makeAndAddTransientIo("zoomTo");

        this.postInit();
    };

    Proxy.prototype.connect = function () {

        var selectedItemsForSelectedItemDetails = [],
            extraHighlitForSelectedItemDetails = [],
            selectedItemsAggregatedIo = this.selectedItemsAggregated,
            selectedItemsIo = this.selected,
            extraHighlitIo = this.extraHighlit,
            hiddenIo = this.hidden;

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: selectedItemsIo
        });

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: extraHighlitIo
        });

        function selectedItemChangeListener(dropV, options) {

            var selectedItems;

            if (hiddenIo.get()) {
                selectedItems = [];
            } else {
                selectedItems = selectedItemsIo.get();
            }

            selectedItemsForSelectedItemDetails.length = 0;

            var parentAllIsDoneCallback = options.allIsDoneCallback,
                allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({ parentCallbacks: [parentAllIsDoneCallback] });


            selectedItems.forEach(function (stopSelectedItem) {

                var stopId = stopSelectedItem.id,
                    colour = stopSelectedItem.colour,
                    highlitLevel = stopSelectedItem.highlitLevel,
                    stopPointIds = stopSelectedItem.stopPointIds;

                selectedItemsForSelectedItemDetails.push({
                    id: stopId,
                    colour: colour,
                    highlitLevel: highlitLevel,
                    stopPointIds: stopPointIds || "all"
                });

            });

            selectedItemsAggregatedIo.set({ action: "refresh" }, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback() });

            allIsDoneMultiplexer.arm();

            return parentAllIsDoneCallback;

        }

        selectedItemsIo.listen({
            newValue: selectedItemChangeListener
        });
        hiddenIo.listen({
            newValue: selectedItemChangeListener
        });

        extraHighlitIo.listen({
            newValue: function (stop, options) {

                extraHighlitForSelectedItemDetails.length = 0;

                if (stop) {
                    extraHighlitForSelectedItemDetails.push(OGT.Util.dictClone(stop));
                }

                selectedItemsAggregatedIo.set({ action: "refresh" }, { allIsDoneCallback: options.allIsDoneCallback });

            }
        });

        selectedItemsAggregatedIo.set({ action: "add", datas: selectedItemsForSelectedItemDetails });
        selectedItemsAggregatedIo.set({ action: "add", datas: extraHighlitForSelectedItemDetails });

        this.postConnect();
    };

    OGT.Proxy.addModule(Proxy);

}());
;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleHideableBase);

    var routeColours = Proxy.routeColours = {};

    routeColours.walkFromAndToAndAllTheWay = OGT.CONSTANTS.COLOURS.WALK_LINE_COLOR;
    routeColours.walkSignificantly = OGT.CONSTANTS.COLOURS.WALK_LINE_COLOR;

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "journey";
    };

    Proxy.prototype.getSelectedItemKeyFromJourney = function (journey) {
        return journey.ogtDepartureDateTime.getFormattedDateTime("_");
    };

    Proxy.prototype.acceptableChangeTimeToMinutes = {
        "0": 2,
        "1": 5,
        "2": 10
    };

    Proxy.prototype._init = function () {

        var thisProxy = this;

        function getInitData(key) {
            var plannerInitData = window.plannerInitData;
            return plannerInitData && plannerInitData[key] || null;
        }

        function getOtherPlaceIsTheSameValidator(proxy, ioName) {
            return function (thisPlace) {
                var other = proxy[ioName];
                if (other) {
                    var otherPlace = other.get();
                    if (thisPlace && otherPlace) {
                        var thisId = thisPlace.Id,
                            otherId = otherPlace.Id,
                            thisType = thisPlace.OgtType,
                            otherType = otherPlace.OgtType,
                            thisLl = thisPlace.Ll,
                            otherLl = otherPlace.Ll;
                        if ((thisId && otherId && thisType && otherType && thisId == otherId && thisType == otherType) || (thisLl && otherLl && thisLl[0] == otherLl[0] && thisLl[1] == otherLl[1])) {
                            return OGT.Language.translate("/searchjourney/errormessages/sameStartEnd");
                        }
                    }
                }
                return true;
            }
        }

        function hasSearchResultOrWillSearch(value) {
            var searchValue = value || searchIo.get(),
                result = !!(searchValue && (searchValue.ise || searchValue.doe.action));
            return result;
        }

        var isMapVisible = this.isMapVisible;

        /**
        * The value of these I/Os will be a place-object, or null if empty.
        */
        function makeStartEndIo(thisIoName, otherIoName, initDataKey, qsShortKey) {
            this.makeAndAddPersistentIo(thisIoName, {
                getDefaultValue: function () { return null; },
                getInitValue: function () { return getInitData(initDataKey); },
                initDataOverridesQuerystringAndCookie: true,
                isValid: getOtherPlaceIsTheSameValidator(this, otherIoName),
                serialize: {
                    funcs: OGT.Proxy.Io.Util.Serialize.Funcs.place
                },
                querystring: {
                    shortKey: qsShortKey,
                    serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.place,
                    doSerialize: function (v) { return hasSearchResultOrWillSearch(v) || isMapVisible(); }
                }
            });

        }

        makeStartEndIo.call(this, "start", "end", "Start", OGT.CONSTANTS.QUERY_STRING.JOURNEY_FROM);
        makeStartEndIo.call(this, "end", "start", "End", OGT.CONSTANTS.QUERY_STRING.JOURNEY_TO);

        /**
        * The value of this I/O will be a data-string in the format yyyy-MM-dd
        */
        this.makeAndAddPersistentIo("date", {
            getInitValue: function () { return getInitData("Date"); },
            initDataOverridesQuerystringAndCookie: true,
            querystring: {
                shortKey: OGT.CONSTANTS.QUERY_STRING.JOURNEY_DATE,
                doSerialize: hasSearchResultOrWillSearch
            },
            getDefaultValue: function () {
                var ogtProxyJourneyDateDefaultValue = window.ogtProxyJourneyDateDefaultValue;
                if (typeof ogtProxyJourneyDateDefaultValue == "undefined") {
                    return null;
                } else {
                    return ogtProxyJourneyDateDefaultValue;
                }
            },
            getReplacementValueForInvalidValue: function () {
                return "";
            },
            serialize: {
                doForDefaultValue: true
            },
            isValid: function (date) {
                if (date === null || OGT.Util.Is.date(date)) {
                    return true;
                }
                return OGT.Language.translate("/searchjourney/errormessages/invaliddate");
            },
            dontUpdateIfNoValueChange: true
        });


        var nowPlaceHolder = OGT.Language.translate("/searchjourney/now").toLowerCase();

        /**
        * The value of this I/O will be a time-string in the format HH:mm
        * In QueryStrings it's formatted as HH.mm to also be allowed as URL-segment.
        */
        this.makeAndAddPersistentIo("time", {
            getInitValue: function () { return getInitData("Time"); },
            initDataOverridesQuerystringAndCookie: true,
            querystring: {
                shortKey: OGT.CONSTANTS.QUERY_STRING.JOURNEY_TIME,
                doSerialize: hasSearchResultOrWillSearch,
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.time
            },
            getDefaultValue: function () {
                var ogtProxyJourneyTimeDefaultValue = window.ogtProxyJourneyTimeDefaultValue;
                if (typeof ogtProxyJourneyTimeDefaultValue == "undefined") {
                    return null;
                } else {
                    return ogtProxyJourneyTimeDefaultValue;
                }
            },
            getReplacementValueForInvalidValue: function () {
                return "";
            },
            serialize: {
                doForDefaultValue: true
            },
            isValid: function (time) {

                if (time === null || (typeof time == "string" && time.toLowerCase() == nowPlaceHolder) || OGT.Util.String.getNiceTimeOrFalse(time)) {
                    return true;
                }
                return OGT.Language.translate("/searchjourney/errormessages/invalidtime");
            },
            reformatValidInValue: function (time) {
                return (typeof time == "string" && time.toLowerCase() == nowPlaceHolder) ? null : (OGT.Util.String.getNiceTimeOrFalse(time) || (time ? "" : time));
            },
            dontUpdateIfNoValueChange: true
        });

        /**
        * The value of this I/O is a number representing an enum-value
        */
        this.makeAndAddPersistentIo("direction", {
            querystring: {
                shortKey: OGT.CONSTANTS.QUERY_STRING.JOURNEY_DIRECTION,
                doSerialize: hasSearchResultOrWillSearch
            },
            getDefaultValue: function () { return "0"; },
            isValid: function (value) {
                if (value == 0 || value == 1) {
                    return true;
                }
                return "Felaktigt värde i direction";
            }
        });

        /**
        * The value of this I/O is a number representing an enum-value
        */
        this.makeAndAddPersistentIo("trafficTypes", {
            querystring: {
                shortKey: OGT.CONSTANTS.QUERY_STRING.JOURNEY_TRAFIC_TYPES,
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.array,
                doSerialize: hasSearchResultOrWillSearch
            },
            getDefaultValue: function () {
                return [];
            },
            reformatValidInValue: function (value) {
                return value.map(function (i) { return parseInt(i); });
            }

        });

        /**
        * The value of this I/O is a number representing an enum-value
        */
        this.makeAndAddPersistentIo("acceptableChangeTime", {
            storeInCookie: true,
            querystring: {
                shortKey: OGT.CONSTANTS.QUERY_STRING.JOURNEY_CHANGE_TIME,
                doSerialize: hasSearchResultOrWillSearch
            },
            getDefaultValue: function () { return "0"; }
        });




        /**
        * The value of this I/O is a number representing an enum-value
        */
        this.makeAndAddPersistentIo("priority", {
            storeInCookie: true,
            querystring: {
                shortKey: OGT.CONSTANTS.QUERY_STRING.JOURNEY_PRIORITY,
                doSerialize: hasSearchResultOrWillSearch
            },
            getDefaultValue: function () { return "0"; }
        });

        /**
        * The value of this I/O is a bool
        */
        this.makeAndAddPersistentIo("walkAcceptable", {
            storeInCookie: true,
            querystring: {
                shortKey: OGT.CONSTANTS.QUERY_STRING.JOURNEY_WALK_ACCEPTABLE,
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean,
                doSerialize: hasSearchResultOrWillSearch
            },
            getDefaultValue: function () { return true; },
            reformatValidInValue: function (value) {
                return !!value;
            }
        });


        /**
        * Through this I/O errors are communicated. The value is a string, or null if clearing.
        */
        this.makeAndAddTransientIo("error");

        /**
        *
        * This I/O represents commandos to the journey search engine and the search result.
        * It consists of a simple object with two parts: "ise" and "doe"
        *   The value "ise" is an array with journeys. If no journeys the array is empty.
        *   When setting the value:
        *     if "ise" evaluates to false it will clear the array
        *     if "ise" is an array it will merge these journeys with current search result
        *       in such a way that there will be no duplicates and they are sorted.
        *   The value "doe" represents commands to the search engine and the status of the search.
        *     "action" in doe can be "doe", "inprogress" or null. 
        *       "doe" is the command to commence a search
        *       "inprogress" means a search is in progress. It is set internally
        *       All other values evaluating to false means the search is done or cancelled.
        *     "waitForIt" is a flag representing whether the search engine should wait for 
        *       all paramenters needed for search to be completed. This can be useful when
        *       certain parameters are waiting for async calls.
        *     "track" is a flag representing whether the search should be traced.
        *     "sendToMainSearchPage" is a flag which when set means the search should be
        *       forwarded to the main search page.
        *     "doPaging" is a string representing whether the search is a fresh search or a
        *       paging search.
        *       Any value evaluating to false means no paging.
        *       "before" means page earlier journeys.
        *       "after" means page later journeys.
        *
        */
        var searchIo = this.makeAndAddPersistentIo("search", {
            getDefaultValue: function () {
                return {
                    ise: null,
                    doe: {
                        action: false
                    }
                };
            },
            getInitValue: function () {
                return {
                    ise: null,
                    doe: {
                        action: false
                    }
                };
            },
            reformatValidInValue: function (inValue, oldValue) {

                if (!oldValue) {
                    return inValue;
                }

                var inValueIs = inValue.ise,
                    inValueDo = inValue.doe;

                if (typeof inValueIs != "undefined") {

                    if (inValueIs) {

                        if (!oldValue.ise) {

                            oldValue.ise = inValueIs;

                        } else {

                            inValueIs.Journeys.forEach(function (journey) {
                                OGT.Util.Splice.spliceInSorted(oldValue.ise.Journeys, journey, function (toInsertJourneyObject, otherJourneyObject) {

                                    if (otherJourneyObject.invalid) {
                                        return null;
                                    }

                                    if (toInsertJourneyObject.ogtKey == otherJourneyObject.ogtKey) {
                                        return false;
                                    }

                                    if (otherJourneyObject.ogtDepartureDateTime < toInsertJourneyObject.ogtDepartureDateTime) {
                                        return true;
                                    }

                                    return null;

                                });
                            });

                        }

                    } else {

                        oldValue.ise = null;

                    }

                }

                if (typeof inValueDo != "undefined") {
                    if (typeof inValueDo != "object") {
                        inValueDo = {
                            action: inValueDo ? "doe" : null
                        }
                    }
                    oldValue.doe = inValueDo;
                }

                return oldValue;
            },
            serialize: {
                funcs: {
                    inflate: function (value) {
                        return {
                            ise: null,
                            doe: (value ? true : false)
                        };
                    },
                    deflate: function (value) {
                        return hasSearchResultOrWillSearch(value);
                    }
                }
            },
            querystring: {
                serializeFuncs: {
                    from: function () {
                        var value = this.getQuerystringValue();
                        if (value) {
                            switch (value) {
                                case "true":
                                    return { "doe": true };
                                case "false":
                                    return { "doe": false };
                            }
                        }
                        return null;
                    },
                    to: function (result, value) {

                        // Intentionally blank

                    }
                }
            }
        });

        /**
        * Through this I/O commands to swap the places of to- and from is communicated. No value.
        */
        this.makeAndAddTransientIo("swapToAndFrom");

        /**
        * This I/O is used for showing journeys on the map. 
        * The value is an array of simple objects on the form
        * { id: X, colour: Y, highlitLevel: Z }
        * where 
        *   id is a generated identifier for a journey
        *   colour is a colour object. It is automatically assigned if not explicitly stated.
        *   highlitLevel is a number showing if the journey should be extra highlit.
        //* Values a reformatted when get so that only journeys loaded are included.
        */
        this.makeAndAddPersistentIo("selected", {
            getDefaultValue: function () { return []; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getColourFunc({
                handBackColourWhenNotUsedBeforeRequestingNewColours: true
            }),
            //reformatOutValue: function (selectedItems) {

            //    var searchValue = searchIo.get();

            //    if (!searchValue) {
            //        return [];
            //    }

            //    var journeySearchResults = searchValue.ise;
            //    if (!journeySearchResults) {
            //        return [];
            //    }

            //    var journeys = journeySearchResults.Journeys || [];

            //    return selectedItems.filter(function (selectedItem) {

            //        return journeys.some(function (journey) {
            //            return selectedItem.id == thisProxy.getSelectedItemKeyFromJourney(journey);
            //        }, this);

            //    }, this);

            //},
            serialize: {
                funcs: OGT.Proxy.Io.Util.Serialize.Funcs.idType
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.selectedItems,
                doSerialize: this.isMapVisible
            }
        });

        /**
         */
        this.makeAndAddPersistentIo("extraHighlit", {

            getDefaultValue: function () { return null; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getIdItemFunc({ minHighlitLevel: OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.MAP_MENU_CONTEXT.JOURNEY }),
            querystring: {
                doSerialize: false
            }

        });

        /**
        * Through this I/O command to zoom in on the start marker is communicated. No value.
        */
        this.makeAndAddTransientIo("zoomToStartMarker"),

        /**
        * Through this I/O command to zoom in on the end marker is communicated. No value.
        */
        this.makeAndAddTransientIo("zoomToEndMarker"),

        /**
        * Through this I/O command to zoom in on a combination of stops and route segments is communicated.
        * The value is an object on the form:
        * { selectedItemId: X, fullJourney: B, pathSegmentIndexes: [I...], places: [P...] }
        * where
        *   selectedItemId is the generated id of a highlit journey
        *   fullJourney is a boolean which when set will set the zoom to the entire journey
        *   pathSegmentIndexes is an array of indexes relating to the segments in the highlit path
        *   places is an array of places-objects.
        *    
        */
        this.makeAndAddTransientIo("zoomTo"),

        this.postInit();

    };

    Proxy.prototype.connect = function () {

        var highlightHandler = OGT.Proxy.Util.HighlightHandler;

        highlightHandler.addIo({
            proxy: this,
            io: this.selected
        });

        highlightHandler.addIo({
            proxy: this,
            io: this.extraHighlit
        });

        highlightHandler.addIo({
            proxy: this,
            io: this.start
        });

        highlightHandler.addIo({
            proxy: this,
            io: this.end
        });

        var whenAllDonePushHistory = {
            allDone: function () {
                OGT.Proxy.History.push();
            }
        },
        whenAllDoneReplaceHistory = {
            allDone: function () {
                OGT.Proxy.History.replace();
            }
        };

        var that = this,
            dateIo = this.date,
            timeIo = this.time;

        dateIo.listen({
            context: that,
            newValue: function (newDate) {
                if (newDate) {
                    if (timeIo.get() === null) {
                        timeIo.set(Date.adjusted().getFormattedTime(), { context: that });
                    }
                } else {
                    if (newDate === null) {
                        timeIo.set(null, { context: that });
                    }
                }
            }
        });

        timeIo.listen({
            context: that,
            newValue: function (newTime) {
                if (newTime) {
                    if (dateIo.get() === null) {
                        dateIo.set(Date.adjusted().getFormattedDate(), { context: that });
                    }
                } else {
                    if (newTime === null) {
                        dateIo.set(null, { context: that });
                    }
                }
            }
        });


        this.search.listen(whenAllDonePushHistory);
        this.swapToAndFrom.listen(whenAllDonePushHistory);

        //this.zoomToStartMarker.listen(whenAllDoneReplaceHistory);
        //this.zoomToEndMarker.listen(whenAllDoneReplaceHistory);
        //this.zoomTo.listen(whenAllDoneReplaceHistory);

        this.zoomToStartMarker.listen(whenAllDonePushHistory);
        this.zoomToEndMarker.listen(whenAllDonePushHistory);
        this.zoomTo.listen(whenAllDonePushHistory);

        //OGT.Proxy.map.visible.listen({
        //    context: this,
        //    newValue: function (inValueOptions, options) {

        //        if (inValueOptions.ise === false) {

        //            this.selected.set([], options); // Chain
        //            return options.allIsDoneCallback;

        //        }
        //        return null;

        //    }
        //});

        Proxy.addSearchFunctionality();

        this.postConnect();

    };

    OGT.Proxy.addModule(Proxy);

}());
;
(function () {

    var journeyProxy = OGT.Proxy.journey;

    if (journeyProxy) {

        var Proxy = journeyProxy.ofClass,
            giveUpTimer = false,
            activeFindJourney = false;

        Proxy.addSearchFunctionality = function () {

            var searchIo = journeyProxy.search,
                startIo = journeyProxy.start,
                endIo = journeyProxy.end;


            addZoomToMarkerHandling(journeyProxy.zoomToStartMarker, startIo);
            addZoomToMarkerHandling(journeyProxy.zoomToEndMarker, endIo);

            [
                            journeyProxy.date,
                            journeyProxy.time,
                            journeyProxy.direction,
                            journeyProxy.trafficTypes,
                            journeyProxy.acceptableChangeTime,
                            journeyProxy.priority,
                            journeyProxy.walkAcceptable
            ].forEach(function (io) {
                io.listen({
                    suppressImmediateCallback: true,
                    newValue: function (inValueDrop, options) {

                        var allIsDoneCallback = options.allIsDoneCallback;

                        searchParameterChanged(allIsDoneCallback);

                        return allIsDoneCallback;
                    }
                });
            });

            function addStartOrEndPlaceChangedHandling(io, searchResultsField) {

                io.listen({
                    newValue: function (newPlace, options) {

                        var searchResults = searchIo.get().ise;
                        if (searchResults) {

                            var oldPlace = searchResults[searchResultsField];

                            if (!OGT.Util.OGT.isSamePlace(newPlace, oldPlace, false)) {

                                var allIsDoneCallback = options.allIsDoneCallback;
                                searchParameterChanged(allIsDoneCallback);
                                return allIsDoneCallback;

                            }
                        }

                        return null;

                    }
                });

            }

            addStartOrEndPlaceChangedHandling(startIo, "RequestedStartPlace");
            addStartOrEndPlaceChangedHandling(endIo, "RequestedEndPlace");

            searchIo.listen({
                newValue: function (inValueSearchOptionsDrop, options) {

                    var allIsDoneCallback = options.allIsDoneCallback;

                    search(allIsDoneCallback);

                    return allIsDoneCallback;
                }
            });


            journeyProxy.swapToAndFrom.listen({
                newValue: function (inValueDrop, options) {

                    var allIsDoneCallback = options.allIsDoneCallback;

                    swapToAndFrom(allIsDoneCallback);

                    return allIsDoneCallback;

                }
            });

        }

        function addZoomToMarkerHandling(zoomToIo, placeIo) {
            zoomToIo.listen({
                context: journeyProxy,
                newValue: function (inValueDrop, options) {
                    var markerValue = placeIo.get();
                    if (markerValue) {
                        OGT.Proxy.map.zoomTo.set(markerValue.Ll, options); // Chain
                    } else {
                        OGT.Proxy.map.reset.set(null, options); // Chain
                    }
                    return options.allIsDoneCallback;
                }
            });
        }

        function saveToCookie(fromPlace, toPlace) {
            OGT.Util.OGT.RecentSearches.addPlaceToRecentPlacesQueue(fromPlace, OGT.CONSTANTS.COOKIE.KEYS.RECENT_FROM_SEARCH);
            OGT.Util.OGT.RecentSearches.addPlaceToRecentPlacesQueue(toPlace, OGT.CONSTANTS.COOKIE.KEYS.RECENT_TO_SEARCH);
        }

        function swapToAndFrom(allIsDoneCallback) {

            var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({ parentCallbacks: [allIsDoneCallback], label: "swapToAndFrom" }),
                searchIo = journeyProxy.search,
                startIo = journeyProxy.start,
                endIo = journeyProxy.end,
                hadSearchResult = searchIo.get().ise,

                previousStartPlace = startIo.get(),
                previousEndPlace = endIo.get();

            OGT.Proxy.History.suppressPush(true, true);

            endIo.set(previousStartPlace, { suppressAllValidation: true, allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("start -> end") });
            startIo.set(previousEndPlace, { suppressAllValidation: true, allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("end -> start") });

            OGT.Proxy.History.suppressPush(false);

            if (hadSearchResult && OGT.Util.OGT.getLayoutWidth() !== OGT.Util.OGT.layoutWidths.mobile) {
                searchIo.set({
                    doe: {
                        action: "doe",
                        track: true
                    }
                }, {
                    allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("new search")
                });
            }

            allIsDoneMultiplexer.arm();

        }

        function setGiveUpTimer(callback) {
            var searchIo = journeyProxy.search;
            giveUpTimer = {
                callback: callback,
                timer: window.setTimeout(function () {
                    // Fock it, we should have received data by now, cancel 
                    // so it does not autosearch when user interacts
                    searchIo.set({ doe: false }, { allIsDoneCallback: callback });
                }, 500)
            };
        };

        function clearGiveUpTimer() {

            if (giveUpTimer) {
                window.clearTimeout(giveUpTimer.timer);
                giveUpTimer.callback();
            }
            giveUpTimer = null;

        };

        function searchParameterChanged(allIsDoneCallback) {

            var searchIo = journeyProxy.search,
                searchIoValue = searchIo.get();
            if (searchIoValue) {
                var doAction = searchIoValue.doe.action;
                if (doAction != "inprogress") {
                    if (doAction == "doe") {
                        search(allIsDoneCallback);
                    } else {
                        searchIo.set({ ise: null }, { allIsDoneCallback: allIsDoneCallback });
                    }
                }
                return;
            }

            allIsDoneCallback();

        };

        function search(allIsDoneCallback) {

            var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({ parentCallbacks: [allIsDoneCallback], label: "proxy journey search search" }),
                searchIo = journeyProxy.search,
                searchOptions = searchIo.get(),
                searchDoOptions = searchOptions.doe,
                doTrack = searchDoOptions.track,
                waitForIt = searchDoOptions.waitForIt,
                doAction = searchDoOptions.action;

            if (doAction != "doe") {
                allIsDoneMultiplexer.arm();
                return;
            }

            clearGiveUpTimer();

            if (waitForIt) {
                setGiveUpTimer(allIsDoneMultiplexer.getChildCallback("give up timeout"));
            }

            var from = journeyProxy.start.get(),
                to = journeyProxy.end.get(),
                date = journeyProxy.date.get(),
                time = journeyProxy.time.get(),
                errorIo = journeyProxy.error;

            //Clear any error messages
            errorIo.set(null, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("Clear errors") });

            //If no from/to selected, show error message
            if (!from || from.inprogress) {
                if (!waitForIt) {
                    searchIo.set({ doe: false }, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No from, cancel searching") });
                    errorIo.set(OGT.Language.translate("/searchjourney/errormessages/nofromselected"), { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No from set error") });
                }

                allIsDoneMultiplexer.arm();
                return;

            }

            if (!to || to.inprogress) {
                if (!waitForIt) {
                    searchIo.set({ doe: false }, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No to, cancel searching") });
                    errorIo.set(OGT.Language.translate("/searchjourney/errormessages/notoselected"), { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No to set error") });
                }

                allIsDoneMultiplexer.arm();
                return;

            }

            if (!time && time !== null) {

                if (!waitForIt) {
                    searchIo.set({ doe: false }, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No time, cancel searching") });
                    errorIo.set(OGT.Language.translate("/searchjourney/errormessages/invalidtime"), { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No time set error") });
                }

                allIsDoneMultiplexer.arm();
                return;

            }

            if (!date && date !== null) {

                if (!waitForIt) {
                    searchIo.set({ doe: false }, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No date, cancel searching") });
                    errorIo.set(OGT.Language.translate("/searchjourney/errormessages/invaliddate"), { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("No date set error") });
                }

                allIsDoneMultiplexer.arm();
                return;

            }

            clearGiveUpTimer();
            if (searchDoOptions.sendToMainSearchPage) {

                var mainSearchPageUrl = window.mainSearchPageUrl;
                if (mainSearchPageUrl && mainSearchPageUrl != window.currentPagePathWithoutExtraSegments) {

                    OGT.Proxy.History.suppressPush(true);

                    if (doTrack) {
                        OGT.Tracking.JourneySearchEngine.JourneySearch.StartPage();
                    }

                    document.location = OGT.Proxy.updateQuerystring(mainSearchPageUrl);

                    allIsDoneMultiplexer.arm();
                    return;

                }

            }


            var direction,
                searchSpan;

            if (searchDoOptions.doPaging) {

                var currentJourneySearchResults = searchIo.get().ise;
                if (currentJourneySearchResults === null) {

                    allIsDoneMultiplexer.arm();
                    return;

                }

                var currentJourneys = currentJourneySearchResults.Journeys;

                if (!currentJourneys || !currentJourneys.length) {

                    allIsDoneMultiplexer.arm();
                    return;

                }

                var beforeOrAfter = searchDoOptions.doPaging;

                var pageDate;
                if (beforeOrAfter === "before") {
                    pageDate = currentJourneys[0].ogtDepartureDateTime;
                } else {
                    pageDate = currentJourneys[currentJourneys.length - 1].ogtDepartureDateTime;
                }

                date = pageDate.getFormattedDate();
                time = pageDate.getFormattedTime();
                direction = "0";
                searchSpan = beforeOrAfter;

                searchIo.set({
                    doe: {
                        action: "inprogress",
                        track: doTrack
                    }
                },
                    {
                        allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("paging search")
                    });


            } else {

                direction = journeyProxy.direction.get();
                searchSpan = "default";

                searchIo.set({
                    ise: null,
                    doe: {
                        action: "inprogress",
                        track: doTrack
                    }
                },
                    {
                        allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("clear previous search")
                    });

            }


            if (doTrack) {
                OGT.Tracking.JourneySearchEngine.JourneySearch.JourneySearchPage();
            }

            if (activeFindJourney) {
                activeFindJourney.$xhr.abort();
                activeFindJourney.allIsDoneCallback();
            }


            var tt = journeyProxy.trafficTypes.get().reduce(function (acc, oneTrafficType) {
                acc += parseInt(oneTrafficType);
                return acc;
            }, 0);


            var searchIsExecutedCallback = allIsDoneMultiplexer.getChildCallback("search is executed");

            //Post search query
            activeFindJourney = {
                allIsDoneCallback: allIsDoneCallback,
                $xhr: OGT.Util.OGT.AsyncAjax.doRequest(
                    OGT.CONSTANTS.AJAX_ENDPOINTS.JOURNEY + "/Find",
                    {
                        data:
                        {
                            startId: from.Id || null,
                            endId: to.Id || null,
                            startType: from.OgtType,
                            endType: to.OgtType,
                            startLl: OGT.Util.latLngArrayToUrlValue(from.Ll),
                            endLl: OGT.Util.latLngArrayToUrlValue(to.Ll),
                            startName: from.PlaceName,
                            endName: to.PlaceName,
                            date: date,
                            time: time,
                            direction: direction,
                            span: searchSpan,
                            traffictypes: tt,
                            changetime: journeyProxy.acceptableChangeTime.get(),
                            priority: journeyProxy.priority.get(),
                            walk: journeyProxy.walkAcceptable.get()
                        },
                        successCallback: function (journeySearchResult) {

                            activeFindJourney = false;

                            //If result is null, show error message
                            if (!journeySearchResult) {

                                errorIo.set(OGT.Language.translate("/searchjourney/errormessages/nosearchpossible"), { allIsDoneCallback: searchIsExecutedCallback });
                                return;

                            }

                            var errorText = journeySearchResult.ErrorText;
                            if (errorText) {
                                displayErrorText(errorText)
                                return;
                            }


                            var searchIsAllDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({ parentCallbacks: [searchIsExecutedCallback], label: "search is all done" });

                            OGT.Proxy.map.timetableperiod.set(OGT.Proxy.map.getTimeTablePeriodCoveringDate(journeySearchResult.Date), {
                                allIsDoneCallback: searchIsAllDoneMultiplexer.getChildCallback("timetableperiod")
                            });

                            searchIo.set({
                                doe: false,
                                ise: journeyProxy.Util.augmentSearchResult(journeySearchResult)
                            }, {
                                forceUpdate: true,
                                allIsDoneCallback: searchIsAllDoneMultiplexer.getChildCallback("search object updated")
                            });

                            //selectedItemBestHit(searchIsAllDoneMultiplexer.getChildCallback("selected item best hit"));

                            searchIsAllDoneMultiplexer.arm();

                            saveToCookie(from, to);

                        },
                        failureCallback: function (journeySearchResult) {

                            displayErrorText(journeySearchResult.ErrorText);
                            searchIsExecutedCallback();

                        }

                    })
            }


            allIsDoneMultiplexer.arm();
            return;

        };

        function displayErrorText(errorText) {
            journeyProxy.error.set(errorText || OGT.Language.translate("/searchjourney/errormessages/genericerror"));
        }



        //function selectedItemBestHit(allIsDoneCallback) { //todo

        //    var journeySearchResult = journeyProxy.search.get().ise;

        //    if (!journeySearchResult) {
        //        allIsDoneCallback();
        //        return;
        //    }

        //    var journeys = journeySearchResult.Journeys;

        //    if (!journeys || !journeys.length) {
        //        allIsDoneCallback();
        //        return;
        //    }

        //    if (journeyProxy.selected.get().length) {
        //        journeyProxy.selected.refresh();
        //        allIsDoneCallback();
        //        return;
        //    }

        //    var now = Date.adjusted(),
        //        selectedJourney = journeys.find(function (journey) {
        //            return now <= journey.ogtDepartureDateTime;
        //        });

        //    if (!selectedJourney) {
        //        selectedJourney = journeys[0];
        //    }

        //    var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({ parentCallbacks: [allIsDoneCallback], label: "selectedItemBestHit" });

        //    var selectedItemKey = journeyProxy.getSelectedItemKeyFromJourney(selectedJourney);

        //    journeyProxy.selected.set(
        //        [{
        //            id: selectedItemKey,
        //            highlitLevel: OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.JOURNEY_BEST_HIT
        //        }],
        //        {
        //            allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("selectedItems")
        //        });

        //    journeyProxy.zoomTo.set({ fullJourney: true, selectedItemId: selectedItemKey }, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback("zoomto") });

        //    allIsDoneMultiplexer.arm();

        //}

    }

}());
;
(function () {

    var proxyInstance = OGT.Proxy.journey;

    if (proxyInstance) {

        var addEventHandlingToJourneySearchInput = (function () {

            function addOrRemoveErrorBasedOnSetStatus(status) {
                if (status === true) {
                    OGT.Proxy.journey.error.set(null);
                    return;
                }
                OGT.Proxy.journey.error.set(status);
            }

            return function ($inputOrSelect, endActionEventName, io, options) {

                if (!$inputOrSelect || !$inputOrSelect.length) {
                    return;
                }

                options = options || {};

                var $emptyPlaceHolder = options.$emptyPlaceHolder,
                    $textboxClear = options.$textboxClear,
                    clearOnFocus = options.clearOnFocus,
                    ignoreFocusBlurTimer = false,
                    ignoreNextFocus = false;

                function onUserEvent(originalEvent, delayEvent) {
                    if (delayEvent) {
                        window.setTimeout(function () {
                            onUserEvent(originalEvent, false);
                        }, 250);
                        return;
                    }

                    if (clearOnFocus && $inputOrSelect.val() == "") {
                        if (ignoreFocusBlurTimer) {
                            ignoreNextFocus = true;
                        } else {
                            setFunc($inputOrSelect, io.get());
                        }
                    } else {
                        var val = getFunc($inputOrSelect);
                        refreshEmptyPlaceHolder(val);
                        addOrRemoveErrorBasedOnSetStatus(io.set(val, { context: context, originatedWithEnter: OGT.Util.Event.isEnter(originalEvent) }));
                    };
                }

                function refreshEmptyPlaceHolder(value) {
                    var empty = value === null;
                    if ($emptyPlaceHolder) {
                        if (empty) {
                            $emptyPlaceHolder.show();
                        } else {
                            $emptyPlaceHolder.hide();
                        }
                    }

                    if ($textboxClear) {
                        if (empty) {
                            $textboxClear.hide();
                        } else {
                            $textboxClear.show();
                        }
                    }
                }

                var getFunc = options.getFunc || function ($elem) {
                    return $elem.val();
                },
                    setFunc = options.setFunc || function ($elem, value) {
                        if (value) {
                            value = value.trim();
                        }
                        //$elem.val(value || "");
                        refreshEmptyPlaceHolder(value);
                    },
                    context;

                if (options.contextFunc) {
                    context = options.contextFunc();
                } else {
                    context = $inputOrSelect.get(0);
                }

                $inputOrSelect.on(endActionEventName, function (event) {
                    onUserEvent(event, options.delayEvent);
                });
                io.listen({
                    newValue: function (inValue) {
                        setFunc($inputOrSelect, inValue);
                    }
                });

                $inputOrSelect.focus(function () {

                    if ($emptyPlaceHolder) {
                        $emptyPlaceHolder.hide();
                    }

                    if (clearOnFocus) {

                        if (ignoreNextFocus) {

                            ignoreNextFocus = false;

                        } else {

                            $inputOrSelect.val("");

                            // To counter double event behaviour in Android mobiles
                            var fieldType = this.type;
                            if (fieldType == "time" || fieldType == "date" || fieldType == "datetime") {
                                ignoreFocusBlurTimer = true;
                                window.setTimeout(function () {
                                    ignoreFocusBlurTimer = false;
                                }, 500);
                            }
                        }
                    }
                });

                if ($textboxClear) {
                    $textboxClear.click(function (event) {
                        event.stopPropagation();
                        io.set(null);
                        $inputOrSelect.focus();
                    });
                }
            }
        }());

        var wireUpNs = (function () {

            function getPostSearchFilteringFunc(otherPlaceIo) {
                var isSamePlace = OGT.Util.OGT.isSamePlace;

                return function (results) {
                    var otherPlace = otherPlaceIo.get();
                    return otherPlace ? results.filter(function (result) {
                        return !isSamePlace(result.data, otherPlace, true);
                    }) : results;
                }
            }

            function from($textbox, $textboxClear, errorIo, onlySearchForPointType) {
                var checkinput = document.getElementById("inputSearchlocal");

                if(checkinput){
                    OGT.Util.OGT.addEventsToPlaceTextbox({
                        $textbox: $textbox,
                        $textboxClear: $textboxClear,
                        io: proxyInstance.start,
                        errorIo: errorIo,
                        //recentSearchCookieKey: OGT.CONSTANTS.COOKIE.KEYS.RECENT_FROM_SEARCH,
                        onlySearchForPointType: "address",
                        //postSearchFiltering: getPostSearchFilteringFunc(proxyInstance.end)
                    });
                }
                else {
                    OGT.Util.OGT.addEventsToPlaceTextbox({
                        $textbox: $textbox,
                        $textboxClear: $textboxClear,
                        io: proxyInstance.start,
                        errorIo: errorIo,
                        recentSearchCookieKey: OGT.CONSTANTS.COOKIE.KEYS.RECENT_FROM_SEARCH,
                        onlySearchForPointType: onlySearchForPointType,
                        postSearchFiltering: getPostSearchFilteringFunc(proxyInstance.end)
                    });
                }
            }

            function to($textbox, $textboxClear, errorIo, onlySearchForPointType) {
                OGT.Util.OGT.addEventsToPlaceTextbox({
                    $textbox: $textbox,
                    $textboxClear: $textboxClear,
                    io: proxyInstance.end,
                    errorIo: errorIo,
                    recentSearchCookieKey: OGT.CONSTANTS.COOKIE.KEYS.RECENT_TO_SEARCH,
                    onlySearchForPointType: onlySearchForPointType,
                    postSearchFiltering: getPostSearchFilteringFunc(proxyInstance.start)
                });
            }

            function date($textbox, $textboxClear, $emptyPlaceHolder) {
                addEventHandlingToJourneySearchInput($textbox, "blur", OGT.Proxy.journey.date, {
                    delayEvent: $textbox.prop("type") === "text",
                    clearOnFocus: false,
                    $textboxClear: $textboxClear,
                    $emptyPlaceHolder: $emptyPlaceHolder
                });
            }

            function time($textbox, $textboxClear, $emptyPlaceHolder) {
                addEventHandlingToJourneySearchInput($textbox, "blur", OGT.Proxy.journey.time, {
                    clearOnFocus: false,
                    $textboxClear: $textboxClear,
                    $emptyPlaceHolder: $emptyPlaceHolder
                });
            }

            function direction($select) {
                addEventHandlingToJourneySearchInput($select, "change", OGT.Proxy.journey.direction);
            }

            function trafficTypes($root, idPrefix) {
                addEventHandlingToJourneySearchInput($root, "change", OGT.Proxy.journey.trafficTypes, {
                    getFunc: function ($trafficTypes) {
                        var tt = [];
                        $trafficTypes.each(function () {
                            if (this.checked) {
                                tt.push(this.value);
                            }
                        });
                        return tt;
                    },
                    setFunc: function ($trafficTypes, ttArray) {
                        var ttIdArray = typeof window.trafficTypeJourneySearchGroups == "undefined" ? [] : window.trafficTypeJourneySearchGroups.map(function (trafficTypeGroup) { return trafficTypeGroup.aggregatedId; }); //TrafikTypknapparnas värden/id:n hämtade från sidan

                        ttIdArray.forEach(function (id) {
                            var checked = ttArray.find(function (otherId) { return id == otherId; }), //Se om den aktuella knappen finns i arrayen av valda knappar
                                $thisTrafficType = $("#" + idPrefix + id),
                                $label = $thisTrafficType.parent().find("label");

                            $thisTrafficType.attr('checked', checked);
                            if (checked) {
                                $label.addClass("selected");
                            } else {
                                $label.removeClass("selected");
                            }
                        });
                    },
                    contextFunc: function () {
                        return null;
                    }
                });
            }

            function changeTime($select) {
                addEventHandlingToJourneySearchInput($select, "change", OGT.Proxy.journey.acceptableChangeTime, {
                    getFunc: function ($stuff) {
                        var resultValue = 0;
                        $stuff.each(function () {
                            if (this.checked) {
                                resultValue = this.value;
                            }
                        });

                        return resultValue;
                    },
                    setFunc: function ($stuff, nyttVärde) {
                        $stuff.each(function () {
                            if ($(this).val() == nyttVärde) {
                                $(this).prop('checked', true);
                            }
                        });
                    }
                });
            }

            function priority($select) {
                addEventHandlingToJourneySearchInput($select, "change", OGT.Proxy.journey.priority, {
                    getFunc: function ($stuff) {
                        var resultValue = 0;
                        $stuff.each(function () {
                            if (this.checked) {
                                resultValue = this.value;
                            }
                        });

                        return resultValue;
                    },
                    setFunc: function ($stuff, nyttVärde) {
                        $stuff.each(function () {
                            if ($(this).val() == nyttVärde) {
                                $(this).prop('checked', true);
                            }
                        });
                    }
                });
            }

            function walkAcceptable($checkbox) {
                addEventHandlingToJourneySearchInput($checkbox, "change", OGT.Proxy.journey.walkAcceptable, {
                    getFunc: function ($elems) {
                        var resultValue = false;

                        $elems.each(function () {
                            if (this.checked) {
                                resultValue = (this.value == "true");
                            }
                        });

                        return resultValue;
                    },
                    setFunc: function ($elems, value) {
                        $elems.each(function () {
                            if (this.value == "" + value) {
                                $(this).prop('checked', true);
                            }
                        });
                    }
                });
            }

            return {
                from: from,
                to: to,
                date: date,
                time: time,
                direction: direction,
                trafficTypes: trafficTypes,
                changeTime: changeTime,
                priority: priority,
                walkAcceptable: walkAcceptable
            };
        }());

        var augmentSearchResult = (function () {

            function augment(newSearchResult) {

                var journeys = null;

                if (newSearchResult.Journeys) {
                    journeys = newSearchResult.Journeys;
                } else if (newSearchResult.length) {
                    journeys = newSearchResult;
                }

                if (journeys) {
                    journeys.forEach(function (journey) {
                        augmentJourney(journey);
                    });
                }
               
                return newSearchResult;
            }

            function getTravelTime(journeyData) {
                var travelTime = journeyData.TravelTime;
                if (travelTime) {
                    if (travelTime.substr(0, 1) == "0") {
                        return travelTime.substr(1);
                    }
                    return travelTime;
                }
                return "";
            }

            function augmentJourney(journey) {

                journey.ogtKey = getJourneyKey(journey);
                journey.ogtArrivalDateTime = Date.fromSwedishDateTime(journey.Arrival);
                journey.ogtDepartureDateTime = Date.fromSwedishDateTime(journey.Departure);

                var routelinks = journey.Routelinks,
                    len = routelinks.length;

                if (routelinks && len) {
                    augmentRoutelinks(routelinks, journey);
                    journey.ogtDepArrHtml = OGT.Util.OGT.Format.Realtime.formattedDepArr({
                        routelinkDep: routelinks[0],
                        routelinkRealtime: routelinks[journey.FirstSignificantRoutelinkIndex],
                        realTimeConsiderPrefix: "Dep",
                        routelinkArr: routelinks[routelinks.length - 1]
                    });
                    journey.ogtTravelTime = getTravelTime(journey);
                    journey.ogtTravelCanceled = OGT.Util.OGT.Format.Realtime.getRouteOrPointLinkIsCanceled(routelinks[routelinks.length - 1]);
                }
            }

            var MAIN_TEMPLATE = '<span class="main {1}">{0}</span>',
                WITH_STOP_POINT_TEMPLATE = '{0} <span class="point--stop notranslate">({1})</span>';

            function formatPlace(place, className, trafficTypeId) {

                var name = OGT.Util.String.format(MAIN_TEMPLATE, place.PlaceName, (OGT.Util.OGT.placeTranslatable(place) ? '' : 'notranslate')),
                    ogtStopPointData = place.OgtStopPointData,
                    stopPoint = OGT.Util.OGT.StopPointAlias.textFrom({ stopPointAlias: ogtStopPointData && ogtStopPointData.stopPointAlias, trafficTypeId: trafficTypeId });

                if (stopPoint) {
                    name = OGT.Util.String.format(WITH_STOP_POINT_TEMPLATE, name, stopPoint);
                }

                return OGT.Util.Html.spanifyOrLinkify(className,
                    name,
                    OGT.Util.OGT.getStopPageUrl(place));
            }

            function augmentRoutelinks(routelinks, journey) {

                var from = proxyInstance.start.get() || journey.ReturnedStartPlace,
                    to = proxyInstance.end.get() || journey.ReturnedEndPlace,
                    previousRoutelink,
                    previousVehicleRoutelink,

                    len = routelinks.length,
                    i = len,
                    routelink;

                while (i--) {

                    routelink = routelinks[i];

                    if (previousRoutelink) {
                        previousRoutelink.ogtPrevRoutelink = routelink;
                        routelink.ogtNextRoutelink = previousRoutelink;
                    }

                    basicAugmentRoutelink(routelink);

                    if (routelink.Line.IsWalk) {

                        if (i == 0) {
                            if (len == 1) {
                                augmentWalkAllTheWayRoutelink(routelink, from, to);
                            } else {
                                augmentWalkToStartRoutelink(routelink, routelinks[1], from);
                            }
                        } else if (i == (len - 1)) {
                            augmentWalkFromEndRoutelink(routelink, routelinks[i - 1], to);
                        } else {
                            augmentWalkSignificantlyRoutelink(routelink, routelinks[i - 1]);
                        }
                    } else {

                        var thisIsFirstVehicleLink = (i == 0),
                            nextI = i - 1, // Next = previous as we move backwards
                            nextRouteLinkIsWalk = null;

                        if (0 <= nextI) {
                            nextRouteLinkIsWalk = (routelinks[nextI].Line.LineTypeId == 0);
                        }

                        if (nextRouteLinkIsWalk != null && (i == 1) && nextRouteLinkIsWalk) {
                            thisIsFirstVehicleLink = true;
                        }

                        augmentVehicleRoutelink(routelink, thisIsFirstVehicleLink);

                        if (previousVehicleRoutelink) {
                            previousVehicleRoutelink.ogtPrevVehicleRoutelink = routelink;
                            routelink.ogtNextVehicleRoutelink = previousVehicleRoutelink;
                        }
                        previousVehicleRoutelink = routelink;

                        if (nextRouteLinkIsWalk != null && !nextRouteLinkIsWalk) {
                            routelinks.splice(i, 0, getWalkInsignificantlyRoutelink());
                        }
                    }

                    previousRoutelink = routelink;
                }
            }

            function basicAugmentRoutelink(routelink) {
                routelink.ogtDepDateTime = Date.fromSwedishDateTime(routelink.DepDateTime);
                routelink.ogtArrDateTime = Date.fromSwedishDateTime(routelink.ArrDateTime);
            }

            function formatPlaceForWalk(place) {

                var placeName = formatPlace(place);

                if (place.OgtType == "stop") {
                    placeName = OGT.Util.String.format(OGT.Language.translate("/searchjourney/thestop"), placeName);
                }

                return placeName;
            }

            function augmentWalkRoutelink(routelink, from, to, langStringFrom, langStringTo) {

                var formatNs = OGT.Util.OGT.Format,
                    realtimeNs = formatNs.Realtime,

                    stringFormatArgs = [
                        formatPlaceForWalk(from, "from"),
                        formatPlaceForWalk(to, "to"),
                        OGT.Util.Html.spanify("duration", formatNs.minutes(routelink.Duration))
                    ];

                routelink.ogtTexts = {
                    stepOn: {
                        time: realtimeNs.formattedDepArr({ routelinkDep: routelink }),
                        instruction: OGT.Util.String.format(OGT.Language.translate(langStringFrom), stringFormatArgs),
                        shortFormat: formatPlace(from, "from")
                    },
                    stepOff: {
                        time: realtimeNs.formattedDepArr({ routelinkArr: routelink }),
                        instruction: OGT.Util.String.format(OGT.Language.translate(langStringTo), stringFormatArgs),
                        shortFormat: formatPlace(to, "to")
                    }
                }
            }

            function augmentWalkAllTheWayRoutelink(routelink, from, to) {

                routelink.ogtType = "walkFromAndToAndAllTheWay";

                routelink.From = from;
                routelink.To = to;
                augmentWalkRoutelink(routelink, from, to, "/searchjourney/walkalltheway", "");
            }

            function adjustWalkDeviationToVehicleRoutelink(routelink, vehicleRoutelink, depArr) {

                var realtimeNs = OGT.Util.OGT.Format.Realtime;

                if (!realtimeNs.hasRoutelinkRealTime(routelink) && realtimeNs.hasRoutelinkRealTime(vehicleRoutelink)) {

                    var nextRealTime = realtimeNs.getRoutelinkOrPointRealTime(vehicleRoutelink),
                        timeDeviation = nextRealTime[depArr + 'TimeDeviation'],
                        deviationAffect = nextRealTime[depArr + 'DeviationAffect'];

                    routelink.RealTime = {
                        RealTimeInfo: [{
                            DepTimeDeviation: timeDeviation,
                            DepDeviationAffect: deviationAffect,
                            ArrTimeDeviation: timeDeviation,
                            ArrDeviationAffect: deviationAffect
                        }]
                    };
                }
            }

            function augmentWalkToStartRoutelink(routelink, nextRoutelink, from) {

                adjustWalkDeviationToVehicleRoutelink(routelink, nextRoutelink, "Dep");

                routelink.ogtType = "walkFromAndToAndAllTheWay";
                routelink.From = from;
                augmentWalkRoutelink(routelink, from, routelink.To, "/searchjourney/walkstart", "/searchjourney/walksignificantly/endtemplate");
            }

            function augmentWalkFromEndRoutelink(routelink, previousRoutelink, to) {

                adjustWalkDeviationToVehicleRoutelink(routelink, previousRoutelink, "Arr");

                routelink.ogtType = "walkFromAndToAndAllTheWay";
                routelink.To = to;
                augmentWalkRoutelink(routelink, routelink.From, to, "/searchjourney/walkend", "/searchjourney/walksignificantly/endtemplate");
            }

            function augmentWalkSignificantlyRoutelink(routelink, previousRoutelink) {

                adjustWalkDeviationToVehicleRoutelink(routelink, previousRoutelink, "Arr");

                routelink.ogtType = "walkSignificantly";

                augmentWalkRoutelink(routelink, routelink.From, routelink.To, "/searchjourney/walksignificantly/starttemplate", "/searchjourney/walksignificantly/endtemplate");
            }

            function getWalkInsignificantlyRoutelink() {
                return {
                    ogtType: "walkInsignificantly"
                };
            }

            function augmentVehicleRoutelink(routelink, isFirst) {

                routelink.ogtType = "vehicle";

                var formatNs = OGT.Util.OGT.Format,
                    from = routelink.From,
                    to = routelink.To,
                    line = routelink.Line,
                    trafficTypeId = line.LineTypeId,
                    formattedFrom = formatPlace(from, "from", trafficTypeId),
                    formattedTo = formatPlace(to, "to", trafficTypeId);

                var stringFormatArgs = [
                        formattedFrom,
                        OGT.Language.translate(isFirst ? "/searchjourney/youtake" : "/searchjourney/youswitch"),
                        OGT.Util.Html.spanifyOrLinkify("lineName",
                            formatNs.line(line),
                            OGT.Util.OGT.getLinePageUrl(line.Ogt)),
                        OGT.Util.Html.spanify("towards",
                            line.Towards),
                        formattedTo
                    ],
                    stepOnInstruction = OGT.Util.String.format(OGT.Language.translate("/searchjourney/stepondirectionstemplate"), stringFormatArgs),
                    stepOffInstruction = OGT.Util.String.format(OGT.Language.translate("/searchjourney/stepoffdirectionstemplate"), stringFormatArgs);

                routelink.ogtTexts = {
                    stepOn: {
                        time: formatNs.Realtime.formattedDepArr({ routelinkDep: routelink }),
                        instruction: stepOnInstruction,
                        shortFormat: formattedFrom
                    },
                    stepOff: {
                        time: formatNs.Realtime.formattedDepArr({ routelinkArr: routelink }),
                        instruction: stepOffInstruction,
                        shortFormat: formattedTo
                    }
                };

                augmentIntermediateStops(line.PointsOnRouteLink);
                augmentDeviations(routelink);
            }

            function augmentIntermediateStops(intermediateStops) {

                intermediateStops.forEach(function (intermediateStop) {
                    intermediateStop.ogtTexts = {
                        time: OGT.Util.OGT.Format.Realtime.timingPointFormatted("Arr", intermediateStop, 0, true),
                        instruction: OGT.Util.Html.spanifyOrLinkify("", intermediateStop.PlaceName, OGT.Util.OGT.getStopPageUrl(intermediateStop))
                    }
                });
            }

            var augmentDeviations = (function () {

                function getDeviationHash(deviation) {
                    var hash = deviation.Header + deviation.Details,
                        scopes = deviation.DeviationScopes || [],
                        i = scopes.length;
                    while (i--) {
                        var scope = scopes[i];
                        hash += scope.FromDateTime + scope.ToDateTime;
                    }
                    return hash;
                }

                return function (routelink) {

                    var deviations = routelink.Deviations,
                        nextRoutelink = routelink.ogtNextRoutelink;

                    if (nextRoutelink) {
                        var nextRoutelinkIsWalk = nextRoutelink.Line.IsWalk,
                            nextNextRoutelink = nextRoutelink.ogtNextRoutelink;

                        if (!nextRoutelinkIsWalk || nextNextRoutelink) {

                            var proxyNs = OGT.Proxy,
                                journeyProxy = proxyNs.journey,
                                acceptableSwitchTimeMs = journeyProxy.acceptableChangeTimeToMinutes[journeyProxy.acceptableChangeTime.get()] * 60 * 1000;

                            if (nextRoutelinkIsWalk) {
                                acceptableSwitchTimeMs = Math.max(acceptableSwitchTimeMs, nextRoutelink.Duration * 60 * 1000);
                                nextRoutelink = nextNextRoutelink;
                            }

                            var realtimeNs = OGT.Util.OGT.Format.Realtime,

                                plannedArrivalDateTime = routelink.ogtArrDateTime,
                                plannedDepartureDateTime = nextRoutelink.ogtDepDateTime,
                                arrivalDeviation = realtimeNs.getTimeDeviation("Arr", routelink),
                                departureDeviation = realtimeNs.getTimeDeviation("Dep", nextRoutelink),
                                delayedArrivalDateTime = plannedArrivalDateTime.clone().addMinutes(arrivalDeviation || 0),
                                delayedDepartureDateTime = plannedDepartureDateTime.clone().addMinutes(departureDeviation || 0),

                                plannedSwitchTimeMs = plannedDepartureDateTime - plannedArrivalDateTime,
                                delayedSwitchTimeMs = delayedDepartureDateTime - delayedArrivalDateTime,
                                templateLangKey;

                            acceptableSwitchTimeMs = Math.min(acceptableSwitchTimeMs, plannedSwitchTimeMs);

                            if (routelink.Line.FootNotes != null && !nextRoutelink.CallTrip) {

                                if (delayedSwitchTimeMs < acceptableSwitchTimeMs) {

                                    if (delayedSwitchTimeMs < 0) {
                                        templateLangKey = "/searchjourney/shortSwitchTime/impossible";
                                    } else {
                                        templateLangKey = "/searchjourney/shortSwitchTime/tight";
                                    }
                                }
                            }

                            if (templateLangKey) {

                                if (!deviations) {
                                    deviations = routelink.Deviations = [];
                                }

                                var serializersNs = proxyNs.Io.Util.Querystring.Serializers,
                                    placeSerializer = serializersNs.place,
                                    timeSerializer = serializersNs.time,

                                    delayedToPlace = routelink.To,
                                    delayedArrivalTime = delayedArrivalDateTime.getFormattedTime(),
                                    destination = journeyProxy.end.get(),

                                    nextRoutelinkLine = nextRoutelink.Line,

                                    nextRoutelinkLineOgt = nextRoutelinkLine.Ogt,

                                    newSearchUrlArgs = proxyNs.getForQuerystring();

                                if (destination) { //possibly we are re-populating groupjourney data and destination can then not be found from the journeyProxy

                                    placeSerializer.to(newSearchUrlArgs, delayedToPlace, "from");
                                    placeSerializer.to(newSearchUrlArgs, destination, "to");
                                    timeSerializer.to(newSearchUrlArgs, delayedArrivalTime, "time");

                                    var newSearchLink = proxyNs.updateQuerystring(window.currentPagePathWithoutExtraSegments, newSearchUrlArgs),

                                        header = OGT.Util.String.format(
                                            OGT.Language.translate(templateLangKey),
                                            OGT.Util.Html.spanifyOrLinkify("lineName",
                                                nextRoutelinkLineOgt.OgtLineNamePreformatted,
                                                OGT.Util.OGT.getLinePageUrl(nextRoutelinkLineOgt)),
                                            nextRoutelinkLine.Towards),

                                        message = OGT.Util.String.format(
                                            OGT.Language.translate("/searchjourney/shortSwitchTime/search"),
                                            formatPlace(delayedToPlace, "from"),
                                            formatPlace(destination, "to"),
                                            delayedArrivalTime,
                                            newSearchLink);

                                    deviations.push(
                                    {
                                        Details: message,
                                        Header: header,
                                        PublicNote: message,
                                        ShortText: message,
                                        Summary: message
                                    });
                                }
                            }
                        }
                    }

                    if (deviations) {
                        deviations.forEach(function (deviation) {
                            deviation.ogtHash = getDeviationHash(deviation);
                            deviation.ogtTexts = {
                                header: deviation.Header,
                                details: deviation.Details
                            }
                        });
                    }
                }
            }());

            function getJourneyKey(journeyData) {
                return journeyData.JourneyKey + journeyData.Departure + journeyData.Arrival;
            }

            return augment;
        }());


        proxyInstance.Util = {
            addEventHandlingToJourneySearchInput: addEventHandlingToJourneySearchInput,
            WireUpInput: wireUpNs,
            augmentSearchResult: augmentSearchResult
        };
    }
}());
;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleLinesAndNartrafikBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "lines";
    };

    OGT.Proxy.addModule(Proxy);

}());
;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleSimpleMarkerLayerBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "servicePoints";
    };

    Proxy.prototype.getAllTypes = (function () {

        var types = [
            {
                key: "salespoint",
                colour: "#DE2824",
                icon: "SALESPOINT"
            },
            {
                key: "automat",
                colour: "#8DD036",
                icon: "AUTOMAT"
            }
        ];
        types.forEach(function (type) {
            type.text = OGT.Language.translate("/searchjourney/map/" + type.key).replace("&shy;", "");
        });

        return function () {
            return types;
        };
    }());

    OGT.Proxy.addModule(Proxy);

}());
;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleZonesBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "zonesCity";
    };

    OGT.Proxy.addModule(Proxy);

}());;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleLinesAndNartrafikBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "nartrafik";
    };

    Proxy.prototype._init = function () {

        this.makeAndAddPersistentIo("address", {
            getDefaultValue: function() {
                return null;
            },
            querystring: {
                doSerialize: function() { return false; }
            }
        });

        this.makeAndAddTransientIo("error");

        Proxy.superclass._init.apply(this, arguments);
    };

    OGT.Proxy.addModule(Proxy);

}());
;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "vehicles";
    };

    var vehicleHighlightTypes = Proxy.prototype.vehicleHighlightTypes =
    {
        "line": 1,
        "stop": 2,
        "run": 3
        //"line": "line",
        //"stop": "stop",
        //"run": "run"
    };

    var runPlaceTypes = Proxy.prototype.runPlaceTypes =
    {
        //"by": "by",
        //"to": "to",
        //"from": "from",
        //"toOnlyReproach": "toOnlyReproach",
        //"fromOnlyApproach": "fromOnlyApproach"
        "by": 1,
        "to": 2,
        "from": 3,
        "toOnlyReproach": 4,
        "fromOnlyApproach": 5
    };

    Proxy.prototype.runPlaceTypeGroups = {
        "allFrom": [runPlaceTypes.fromOnlyApproach, runPlaceTypes.from],
        "allTo": [runPlaceTypes.toOnlyReproach, runPlaceTypes.to],
        "allFromTo": [runPlaceTypes.fromOnlyApproach, runPlaceTypes.from, runPlaceTypes.toOnlyReproach, runPlaceTypes.to]
    };


    var mergeRunPlaces = Proxy.prototype.mergeRunPlaces = (function () {

        var priorities = {};

        priorities[runPlaceTypes.by] = 1;
        priorities[runPlaceTypes.toOnlyReproach] = 2;
        priorities[runPlaceTypes.fromOnlyApproach] = 3;
        priorities[runPlaceTypes.to] = 2;
        priorities[runPlaceTypes.from] = 3;

        return function (toAddTo, toAdd) {

            if (!toAdd) {
                return;
            }

            toAdd.forEach(function (toAddRunPlace) {

                var toAddPlace = toAddRunPlace.place,
                    existingRunPlaceIndex = toAddTo.findIndex(function (r) {
                        return OGT.Util.OGT.isSamePlace(toAddPlace, r.place, true);
                    });

                if (existingRunPlaceIndex === -1) {
                    toAddTo.push(toAddRunPlace);
                    return;
                }

                var existingPrio = priorities[toAddTo[existingRunPlaceIndex].type],
                    toAddPrio = priorities[toAddRunPlace.type];

                if (existingPrio < toAddPrio || existingPrio === toAddPrio && toAddPlace.OgtStopPointData) {
                    toAddTo[existingRunPlaceIndex] = toAddRunPlace;
                }

            });

        }

    }());

    Proxy.prototype._init = function () {

        var primaryPropertyNames = ["type", "id", "run"],
            propType = { "type": "string", "id": "int", "run": "int", "to": "arrayOfInts" },
            typesToSerialize = [vehicleHighlightTypes.line, vehicleHighlightTypes.stop],
            typePriorities = {};

        typePriorities[vehicleHighlightTypes.line] = 1;
        typePriorities[vehicleHighlightTypes.stop] = 2;
        typePriorities[vehicleHighlightTypes.run] = 3;

        function onlySerializableFilter(item) { return typesToSerialize.includes(item.type); }

        this.makeAndAddPersistentIo("all", {
            getDefaultValue: function () { return false; },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean,
                doSerialize: this.isMapVisible
            }
        });

        this.makeAndAddPersistentIo("selected", {
            getDefaultValue: function () { return []; },
            reformatValidInValue: OGT.Proxy.Io.Util.ReformatValidInValue.getColourFunc({ propertiesDecidingEquality: primaryPropertyNames }),
            serialize: {
                funcs: {
                    deflate: function (value) {
                        return value
                            .filter(onlySerializableFilter)
                            .map(function (item) {
                                var r = {};
                                primaryPropertyNames.forEach(function (propName) {
                                    var value = item[propName];
                                    if (value) {
                                        r[propName] = value;
                                    }
                                });
                                r.highlitLevel = item.highlitLevel || 0;
                                return r;
                            });
                    }
                }
            },
            querystring: {
                serializeFuncs: {
                    from: function () {
                        var value = this.getQuerystringValue();
                        if (value) {
                            return value.split(';').map(function (grp) {

                                var highlitLevel = 0;
                                while (grp.substr(0, 1) == "!") {
                                    highlitLevel++;
                                    grp = grp.substr(1);
                                }

                                var r = {
                                    highlitLevel: highlitLevel
                                },
                                    s = grp.split(":");

                                primaryPropertyNames.forEach(function (propName, index) {

                                    if (index < s.length) {

                                        var value = s[index];
                                        switch (propType[propName]) {
                                            case "int":
                                                value = parseInt(value);
                                                break;
                                            case "arrayOfInts":
                                                value = value.split(',').map(parseInt);
                                                break;
                                        }
                                        if (value) {
                                            r[propName] = value;
                                        }

                                    }

                                });
                                return r;
                            });
                        }
                        return null;
                    },
                    to: function (result, value) {

                        if (value && value.length) {

                            result[this.getBaseFullPathKey()] = value
                                .filter(onlySerializableFilter)
                                .map(function (p) {
                                    return [].fillAndCreate("!", p.highlitLevel).join('') +
                                    primaryPropertyNames.reduce(function (r, propName) {
                                        var v = p[propName];
                                        if (v) {
                                            r.push(v);
                                        }
                                        return r;
                                    }, []).join(":");

                                }).join(";");

                        }
                    }
                },
                doSerialize: this.isMapVisible
            }
        });

        this.makeAndAddPersistentAggregatingIo("selectedItemsAggregated",
            function (aggregated, currentSelectedItem) {

                var existingSelectedItem = aggregated.find(function (other) {
                    return primaryPropertyNames.every(function (propName) {
                        return currentSelectedItem[propName] == other[propName];
                    });
                });

                if (!existingSelectedItem) {

                    existingSelectedItem = { runPlaces: [] };

                    primaryPropertyNames.forEach(function (propName) {
                        existingSelectedItem[propName] = currentSelectedItem[propName];
                    });

                    aggregated.push(existingSelectedItem);

                }

                mergeRunPlaces(existingSelectedItem.runPlaces, currentSelectedItem.runPlaces);

                existingSelectedItem.highlitLevel = Math.max(existingSelectedItem.highlitLevel || 0, currentSelectedItem.highlitLevel || 0);

                return aggregated;

            },
            {
                reformatOutValue: function (aggregatedList) {

                    aggregatedList.sort(function (left, right) {

                        var leftPriority = typePriorities[left.type],
                            rightPriority = typePriorities[right.type];

                        if (leftPriority === rightPriority) {

                            var leftHasToFrom = left.to || left.from,
                                rightHasToFrom = right.to || right.from;

                            if (leftHasToFrom === rightHasToFrom) {
                                return 0;
                            }

                            return !leftHasToFrom && rightHasToFrom ? 1 : -1;

                        }

                        return leftPriority < rightPriority ? 1 : -1;

                    });

                    return aggregatedList;
                },
                serialize: {
                    doe: false
                }
            }
        );

        /**
        */
        this.makeAndAddPersistentIo("extraHighlit", {

            getDefaultValue: function () { return null; },
            querystring: {
                doSerialize: false
            }

        });

        this.makeAndAddTransientIo("zoomTo");

        this.postInit();

    };

    Proxy.prototype.connect = function () {

        var selectedItemsIo = this.selected,
            extraHighlitIo = this.extraHighlit,
            showAllIo = this.all,
            selectedItemsAggregatedIo = this.selectedItemsAggregated,
            selectedItemsForSelectedItemsDetails = [],
            extraHighlitForSelectedItemDetails = [],
            linesProxy = OGT.Proxy.lines,
            lineSelectedItemsForSelectedItemsAggregated = [],
            stopsProxy = OGT.Proxy.stops,
            stopSelectedItemsForSelectedItemsAggregated = [];

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: selectedItemsIo
        });

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: extraHighlitIo
        });

        selectedItemsAggregatedIo.set({ action: "add", datas: selectedItemsForSelectedItemsDetails });
        selectedItemsAggregatedIo.set({ action: "add", datas: extraHighlitForSelectedItemDetails });
        selectedItemsAggregatedIo.set({ action: "add", datas: lineSelectedItemsForSelectedItemsAggregated });
        selectedItemsAggregatedIo.set({ action: "add", datas: stopSelectedItemsForSelectedItemsAggregated });


        function selectedItemsChangeListener(dropV, options) {

            var selectedItems = selectedItemsIo.get();

            selectedItemsForSelectedItemsDetails.length = 0;

            var parentAllIsDoneCallback = options.allIsDoneCallback,
                allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({ parentCallbacks: [parentAllIsDoneCallback] });

            selectedItems.forEach(function (fOptions) {

                selectedItemsForSelectedItemsDetails.push(fOptions);

            });

            selectedItemsAggregatedIo.set({ action: "refresh" }, { allIsDoneCallback: allIsDoneMultiplexer.getChildCallback() });

            allIsDoneMultiplexer.arm();

            return parentAllIsDoneCallback;

        }

        selectedItemsIo.listen({
            newValue: selectedItemsChangeListener
        });
        showAllIo.listen({
            newValue: selectedItemsChangeListener
        });

        extraHighlitIo.listen({
            newValue: function (v, options) {

                extraHighlitForSelectedItemDetails.length = 0;

                if (v) {
                    extraHighlitForSelectedItemDetails.push(OGT.Util.dictClone(v));
                }

                selectedItemsAggregatedIo.set({ action: "refresh" }, { allIsDoneCallback: options.allIsDoneCallback });

            }
        });

        if (linesProxy) {

            var linesSelectedItemsIo = linesProxy.selected,
                linesLayerHiddenIo = linesProxy.hidden;

            function linesChangeListener() {

                var lineSelectedItems;
                if (linesLayerHiddenIo.get()) {
                    lineSelectedItems = [];
                } else {
                    lineSelectedItems = linesSelectedItemsIo.get();
                }

                lineSelectedItemsForSelectedItemsAggregated.length = 0;

                lineSelectedItems.forEach(function (lineSelectedItem) {
                    var highlitLevel = lineSelectedItem.highlitLevel;
                    if (highlitLevel) {
                        lineSelectedItemsForSelectedItemsAggregated.push({
                            type: vehicleHighlightTypes.line,
                            id: parseInt(lineSelectedItem.id),
                            highlitLevel: highlitLevel
                        });
                    }
                });

                selectedItemsAggregatedIo.set({ action: "refresh" });
            }

            linesSelectedItemsIo.listen({
                newValue: linesChangeListener
            });
            linesLayerHiddenIo.listen({
                newValue: linesChangeListener
            });

        }

        if (stopsProxy) {

            var stopsSelectedItemsIo = stopsProxy.selected,
                stopsSelectedItemsAggregatedIo = stopsProxy.selectedItemsAggregated,
                stopsExtraHighlitIo = stopsProxy.extraHighlit,
                stopsLayerHiddenIo = stopsProxy.hidden;

            function stopsSelectedItemsListener() {

                var stopsSelectedItems;
                if (stopsLayerHiddenIo.get()) {
                    stopsSelectedItems = [];
                } else {
                    stopsSelectedItems = stopsSelectedItemsIo.get();
                }

                var extraHighlitStop = stopsExtraHighlitIo.get();

                stopSelectedItemsForSelectedItemsAggregated.length = 0;
                if (extraHighlitStop) {
                    stopsSelectedItems = stopsSelectedItems.concat([extraHighlitStop]);
                }

                stopsSelectedItems.forEach(function (stopSelectedItem) {
                    var highlitLevel = stopSelectedItem.highlitLevel;
                    if (highlitLevel) {
                        stopSelectedItemsForSelectedItemsAggregated.push({
                            type: vehicleHighlightTypes.stop,
                            id: parseInt(stopSelectedItem.id),
                            highlitLevel: highlitLevel
                        });
                    }
                });

                selectedItemsAggregatedIo.set({ action: "refresh" });
            }

            stopsSelectedItemsIo.listen({
                newValue: stopsSelectedItemsListener
            });
            stopsLayerHiddenIo.listen({
                newValue: stopsSelectedItemsListener
            });
            stopsExtraHighlitIo.listen({
                newValue: stopsSelectedItemsListener
            });

            var vehicleSelectedRunToFromsForStopsSelectedItemsAggregated = [];

            stopsSelectedItemsAggregatedIo.set({ action: "add", datas: vehicleSelectedRunToFromsForStopsSelectedItemsAggregated });

            selectedItemsIo.listen({
                newValue: function (selectedVehicleItems) {

                    vehicleSelectedRunToFromsForStopsSelectedItemsAggregated.length = 0;

                    selectedVehicleItems
                        .filter(function (item) { return item.type == vehicleHighlightTypes.run && (item.from || item.to); })
                        .forEach(function (item) {

                            var colour = OGT.Proxy.Util.getColourForLine(item.id),
                                highlitLevel = item.highlitLevel;

                            item.to.forEach(function (place) {

                                var ogtStopPointData = place.OgtStopPointData,
                                    stopPointId = ogtStopPointData && ogtStopPointData.stopPointId;

                                vehicleSelectedRunToFromsForStopsSelectedItemsAggregated.push({
                                    id: place.Id,
                                    showStopPointExplicit: true,
                                    stopPointIds: stopPointId ? [stopPointId.toLowerCase()] : "all",
                                    colour: colour,
                                    highlitLevel: highlitLevel
                                });

                            });

                        });

                    stopsSelectedItemsAggregatedIo.set({ action: "refresh" });

                }
            });

        }

        OGT.Proxy.map.visible.listen({
            newValue: function (inValue, options) {
                if (inValue.ise === false) {
                    selectedItemsIo.set([]);
                }
            }
        });

        this.postConnect();

    };

    OGT.Proxy.addModule(Proxy);

}());
;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleSimpleMarkerLayerBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "commuterParkings";
    };

    Proxy.prototype.getAllTypes = (function () {

        var types = [
            {
                key: "pendpark",
                text: "Parkering",
                colour: "#177EC3",
                icon: "PARKING"
            }
        ];
        
        return function () {
            return types;
        };
    }());

    OGT.Proxy.addModule(Proxy);

}());;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "userLocation";
    };

    var userLocationModes = {
        none: null,
        track: "track",
        trackAndFollow: "trackAndFollow"
    };
    Proxy.prototype.userLocationModes = userLocationModes;

    Proxy.prototype.nextUserLocationMode = function (currentMode) {
        switch (currentMode) {
            case userLocationModes.none:
                return userLocationModes.trackAndFollow;
            case userLocationModes.track:
                if (this.position.get() && !(this.isNearCenter() && this.isZoomedIn())) {
                    return userLocationModes.trackAndFollow;
                }
                return userLocationModes.none;
            default:
                return userLocationModes.none;
        }
    };


    Proxy.prototype.isNearCenter = function () {
        var mapAdapter = OGT.MapAdapter,
            position = this.position.get();
        if (position && mapAdapter) {
            return OGT.Util.OGT.Geo.roughRelativeDistance(mapAdapter.llToPx(OGT.Proxy.map.ll.get()), mapAdapter.llToPx(position.ll), 1) < 35;
        }
        return null;
    };

    Proxy.prototype.isZoomedIn = function () {
        return OGT.CONSTANTS.MAP.TRACK_AND_FOLLOW_ZOOM - 2 < OGT.Proxy.map.zoom.get();
    };


    Proxy.prototype._init = function () {

        /**
        * This I/O is used for setting modes for user location function.
        */
        this.makeAndAddPersistentIo("mode", {
            storeInCookie: true,
            getDefaultValue: function () { return userLocationModes.none },
            querystring: {
                doSerialize: false
            }
        });

        this.makeAndAddPersistentIo("position", {
            storeInCookie: false,
            getDefaultValue: function () { return null },
            querystring: {
                doSerialize: false
            }
        });

        this.makeAndAddPersistentIo("compass", {
            storeInCookie: false,
            getDefaultValue: function () { return null },
            querystring: {
                doSerialize: false
            }
        });

        this.postInit();

    };

    Proxy.prototype.connect = function () {

        this.postConnect();

    };

    OGT.Proxy.addModule(Proxy);

}());
;
(function () {

    if (OGT.Util.OGT.isKcMode()) {

        function Proxy() {
        }
        OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleSimpleMarkerLayerBase);

        Proxy.getKey = Proxy.prototype.getKey = function () {
            return "activationPoints";
        };

        Proxy.prototype.getAllTypes = function () {
            return [
                {
                    key: "activationPoint",
                    text: "Aktiveringspunkt",
                    colour: "#000",
                    icon: "TRAFFIC_LIGHT"
                }
            ];
        };

        OGT.Proxy.addModule(Proxy);

    }

}());;
(function () {

    function Proxy() {
    }
    OGT.Util.extend(Proxy, OGT.Proxy.ProxyModuleBase);

    Proxy.getKey = Proxy.prototype.getKey = function () {
        return "map";
    };

    function getTimeTablePeriodCoveringDate(date) {
        if (typeof date == "string") {
            date = Date.fromSwedishDate(date);
        }
        return (window.timetablePeriods || []).find(function (timeTablePeriod) { return timeTablePeriod.From <= date && date < timeTablePeriod.To; }) || null;
    };

    Proxy.prototype.getTimeTablePeriodCoveringDate = getTimeTablePeriodCoveringDate;

    Proxy.prototype._init = function () {

        function hasBounds() {
            return boundsIo.get();
        }

        /**
        * This I/O is used for showing and hiding the map. It does also trigger the loading
        * of the map scripts.
        * It consists of a simple object with two parts: "ise" and "doe"
        * The value "ise" is a boolean showing if the map is currently visible.
        * The value "doe" is a boolean working as a command to change the visiblity.
        * The two values will not always be the same - do can for instance be true, while the
        * map has not yet changed state into visible.
        */
        this.makeAndAddPersistentIo("visible", {
            getDefaultValue: function () { return { ise: null, doe: false }; },
            serialize:
                {
                    funcs: {
                        inflate: function (value) {
                            return {
                                doe: value
                            };
                        },
                        deflate: function (value) {
                            return (value.ise || value.doe) ? true : null;
                        }
                    }
                },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean,
                doSerialize: function () {
                    return fullscreenIo.get().ise !== false;
                }
            }
        });



        /**
        * This I/O is used for toggling the map between showing in a window on the page
        * and taking the entire browser window.
        * It consists of a simple object with two parts: "ise" and "doe"
        * The value "ise" tells if the map is in fullscreen mode. null shows that the map
        * can not be toggled between the two modes. This happens when it is only shown
        * in fullscreen mode.
        * The value "doe" is a boolean working as a command to change the visibility.
        */
        var fullscreenIo = this.makeAndAddPersistentIo("fullscreen", {
            getDefaultValue: function () { return { ise: null }; },
            serialize: {
                funcs: {
                    inflate: function (value) {
                        return {
                            doe: value
                        };
                    },
                    deflate: function (value) {
                        return value.ise ? true : null;
                    }
                }
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean,
                doSerialize: this.isMapVisible
            }
        });

        /**
        * This I/O represents the center point of the map. The value is an Lat-Long Array.
        */
        this.makeAndAddPersistentIo("ll", {
            getDefaultValue: function () { return OGT.CONSTANTS.MAP.START_POSITION; },
            serialize: {
                doe: function () {
                    return !hasBounds();
                }
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.ll,
                doSerialize: this.isMapVisible
            }
        });


        (function () {

            function getTimeTablePeriodWithId(id) {
                return (window.timetablePeriods || []).find(function (timeTablePeriod) { return timeTablePeriod.Id == id; }) || null;
            }

            /**
            * This I/O represents what time table period is shown. Not all parts of the map care about this.
            */
            this.makeAndAddPersistentIo("timetableperiod", {
                serialize: {
                    funcs: {
                        deflate: function (value) {
                            return value.Id;
                        },
                        inflate: function (value) {
                            return getTimeTablePeriodWithId(value);
                        }
                    }
                },
                getDefaultValue: function () {
                    return getTimeTablePeriodCoveringDate(Date.adjusted());
                },
                querystring: {
                    serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.number,
                    doSerialize: this.isMapVisible
                },
                reformatValidInValue: function (timeTablePeriodOrId, oldValue) {
                    if (!timeTablePeriodOrId) {
                        return oldValue;
                    }
                    if (typeof timeTablePeriodOrId != "object") {
                        timeTablePeriodOrId = getTimeTablePeriodWithId(timeTablePeriodOrId);
                    }
                    return timeTablePeriodOrId;
                }
            });

        }.call(this));

        /**
        * This I/O represents the zoom level of the map. The value is a number.
        */
        this.makeAndAddPersistentIo("zoom", {
            getDefaultValue: function () {
                return OGT.CONSTANTS.MAP.START_ZOOM;
            },
            serialize: {
                doe: function () {
                    return !hasBounds();
                }
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.number,
                doSerialize: this.isMapVisible
            }
        });

        /**
        * This I/O represents the bounds of the map. The value is a bounds Array.
        */
        var boundsIo = this.makeAndAddPersistentIo("bounds", {
            getDefaultValue: function () {
                return null;
            },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.ll,
                doSerialize: this.isMapVisible
            },
            reformatValidInValue: function (bounds) {
                if (bounds && !Array.isArray(bounds)) {
                    bounds = bounds.toBoundsArray();
                }
                return bounds;
            }
        });


        /**
        * This I/O is used for changing guiSize on map.
        */
        this.makeAndAddPersistentIo("guiSize", {
            storeInCookie: true,
            getDefaultValue: function () { return OGT.Util.OGT.getLayoutWidth() === OGT.Util.OGT.layoutWidths.mobile ? OGT.CONSTANTS.MAP.SMALL_ICON_SCALE_FACTOR : 1 },
            querystring: {
                doSerialize: false
            }
        });


        /**
        * This I/O is used for showing labels on places.
        */
        this.makeAndAddPersistentIo("labels", {
            storeInCookie: true,
            getDefaultValue: function () { return false; },
            querystring: {
                doSerialize: false
            }
        });


        /**
        * This I/O is used for showing stops related to lines.
        */
        this.makeAndAddPersistentIo("associatedPlaces", {
            storeInCookie: true,
            getDefaultValue: function () { return true; },
            querystring: {
                doSerialize: false
            }
        });



        this.makeAndAddPersistentIo("lineWeb", {
            getDefaultValue: function () { return false; },
            querystring: {
                serializeFuncs: OGT.Proxy.Io.Util.Querystring.Serializers.boolean,
                doSerialize: this.isMapVisible
            }
        });


        this.makeAndAddPersistentIo("menuOpen", {
            getDefaultValue: function () { return null; },
            querystring: {
                serializeFuncs: {
                    from: function () {
                        var querystringValue = this.getQuerystringValue();
                        if (querystringValue == "false") {
                            return false;
                        }
                        return querystringValue;
                    },
                    to: function (result, value) {
                        if (typeof value != 'undefined' && value != null) {
                            result[this.getBaseFullPathKey()] = value;
                        }
                    }
                },
                doSerialize: this.isMapVisible
            }
        });

        this.makeAndAddPersistentIo("extraHighlit", {

            getDefaultValue: function () { return null; },
            querystring: {
                doSerialize: false
            }

        });


        /**
        * Through this I/O command to zoom in on a location is communicated. Value is Lat-Long Array.
        */
        this.makeAndAddTransientIo("zoomTo");

        /**
        * Through this I/O command to reset ll and zoom on the map is communciated. No value.
        */
        this.makeAndAddTransientIo("reset");

        this.postInit();
    };

    Proxy.prototype.connect = function () {

        this.zoomTo.listen({
            context: this,
            newValue: function (inValueLl) {
                OGT.Proxy.map.ll.set(inValueLl);
                OGT.Proxy.map.zoom.set(OGT.CONSTANTS.MAP.ZOOM_TO_POSITION_ZOOM);
            }
        });

        this.reset.listen({
            context: this,
            newValue: function (inValueDrop) {
                OGT.Proxy.map.ll.reset();
                OGT.Proxy.map.zoom.reset();
            }
        });

        var visibleIo = this.visible;

        function initiateMapLoadListener(inValueOptions, options) {

            inValueOptions = inValueOptions || {};

            if (inValueOptions.doe) {

                visibleIo.stopListening(initiateMapLoadListener);

                var allIsDoneCallback = options.allIsDoneCallback;

                if (window.mapBundleUrl) {

                    $.getScript(window.mapBundleUrl, function () {

                        OGT.MapHandler._init(allIsDoneCallback);

                    });

                } else {
                    OGT.MapHandler._init(allIsDoneCallback);
                }

                return allIsDoneCallback;

            }

            return null;
        }

        visibleIo.listen({
            context: this,
            newValue: initiateMapLoadListener,
            allDone: function (inValueOptions) {
                if (inValueOptions.ise === false) {
                    OGT.Proxy.History.push();
                }
            }
        });


        this.timetableperiod.listen({
            allDone: function () {
                OGT.Proxy.History.push();
            }
        });


        this.fullscreen.listen({
            allDone: function (inValueOptions) {
                if (inValueOptions.ise !== null) {
                    OGT.Proxy.History.push();
                }
            }
        });

        OGT.Proxy.Util.HighlightHandler.addIo({
            proxy: this,
            io: this.extraHighlit
        });

        this.postConnect();

    };

    OGT.Proxy.addModule(Proxy);

}());
;
/**   
* Anonymous auto-eval function to load all required extensions/patches/patches
*/
(function () {

    var LEGEND_TEMPLATE = '<li class="legend">{0}</li>',
        LEGEND_PLACETYPE_TEMPLATE = '<span class="item {3}"><span class="icon--placetype legend {0}"></span><span class="show-for-medium">{1}</span><span class="hide-for-medium">{2}</span></span>',
        RESULT_ITEM_TEMPLATE = '<li><a><span class="item {1}">{0}</span></a></li>',
        ITEM_TEMPLATE = '<span class="icon--placetype {0}"></span>{1}';

    //Monkey patch to override jQuery UI Autocomplete rendering behaviour to show different images for stops and addresses in search result
    $.ui.autocomplete.prototype._renderItemData = function (ul, item) {
        var data = item.data;

        return $(OGT.Util.String.format(RESULT_ITEM_TEMPLATE,
            OGT.Util.String.format(ITEM_TEMPLATE, item.getIcon ? item.getIcon(data) : "", item.label),
            item.isTranslatable && item.isTranslatable(data) ? "" : "notranslate"))
            .data("ui-autocomplete-item", item)
            .appendTo(ul);
    };

    //Patches for jQuery UI Autocomplete to show legend as final item of search result list.
    $.ui.autocomplete.prototype._renderLegend = function (ul, item) {

        var labels = item.labels;

        //Remove jQuery Ui class
        setTimeout(() => {
            $(".legend").removeClass("ui-menu-item");
        }, "100");

        if (labels.length) {
            $(OGT.Util.String.format(LEGEND_TEMPLATE,
                labels.map(function (placeType) {
                    var isLine = (placeType == "line"),
                        longVersion = OGT.Language.translate('/searchjourney/placetypes/' + placeType),
                        shortVersion = OGT.Language.translate('/searchjourney/placetypes/' + placeType + 'Short', longVersion);
                    return OGT.Util.String.format(LEGEND_PLACETYPE_TEMPLATE, OGT.Util.OGT.getIconCssClassForOgtPlaceType(placeType, true), longVersion, shortVersion, (isLine ? "line" : "place"));
                }).join('')
            ))
                .data("ui-autocomplete-item", {})
                .appendTo(ul);
        }
    };

    $.ui.autocomplete.prototype._renderMenu = function (ul, items) {

        var that = this;
        var stopLength = 0;

        $.each(items, function (index, item) {
            if (item.ogtType == 'legend') {
                that._renderLegend(ul, item);
            } else {
                that._renderItemData(ul, item);
                stopLength++; // Increment stopLength for non-legend items

            }
        });

        // Update screen reader text on how many stops there is in autocomplete
        var prefix = $(".updated-suggestions").attr("data-prefix");
        var sufix = $(".updated-suggestions").attr("data-sufix");

        var msg = prefix + stopLength + sufix;
        // Add period if msg is the same. Used to make screen reader anncounce text even if the text is identical
        if (msg == $(".updated-suggestions").text()) {
            msg = msg + "."
        }

        if (stopLength >= 0) {
            $(".updated-suggestions").text(msg);
        }
    };

    //Remove tabindex from humany FAQ container
    setTimeout(() => {
        $(".humany-guide-list").removeAttr("tabindex");
        $(".humany-component.humany-contact-link").attr("href", "/kundservice/hur-kan-vi-hjalpa-dig/alla-kontaktvagar/");
    }, "1000");

    //Add listener to humany contact-link
    $("body").on("mouseover focus", ".humany-component.humany-contact-link", function (e) {
        e.target.href = "/kundservice/hur-kan-vi-hjalpa-dig/alla-kontaktvagar/";
    });

    //jQuery UI autocomplete patches to combat Chrome/Safari Width/Jump bugs
    //NOTE: this should ideally be removed once jQuery solves these issues.
    //$.ui.autocomplete.prototype._suggest = function (items) {
    //    var ul = this.menu.element
    //        .empty()
    //        .zIndex(this.element.zIndex() + 1);

    //    this._renderMenu(ul, items);

    //    this.menu.refresh();

    //    this.menu.element.show().position($.extend({
    //        of: this.element //<-pos bug Chrome ??
    //    }, this.options.position));

    //    ul.outerWidth(400); //<-- fixed width on search suggestion box
    //};
}());
;
(function () {

    /*
     * This thing was connected to Google Analytics, until ÖGT switched to Google Tag Manager, which made it not work.
     * However, it might be helpful to still have these things connected, and also I do not dare delete all the 
     * tracking points incase I would accidentaly destroy something. //Linus 2019-11-08
     */


    function track(options) {

        //console.log(arguments);

        var completeCallback = options.completeCallback;

        //google analytics
        if (typeof ga == "undefined") {

            if (completeCallback) {
                completeCallback();
            }

        } else {

            var opts = {
                hitType: 'event',
                eventCategory: options.category,
                eventAction: options.action,
                eventLabel: options.label,
                eventValue: options.value
            };

            if (completeCallback) {
                opts.hitCallback = completeCallback;
            }

            window.setTimeout(completeCallback, 1000);

            ga('send', opts);

        }

    }

    var events = {
        UserLogin: {
            SuccessStepUp: null,
            FailureStepUp: null,
            Success: null,
            Failure: null,
            Logout: null
        },

        UserEdit: {
            Addresses: {
                Update: null,
                Delete: null,
                Add: null
            },
            TelephoneNumber: {
                Update: null,
                Delete: null,
                Add: null
            },
            ExternalAuthentication: {
                Update: null,
                Delete: null,
                Add: null
            },
            RealName: {
                Update: null
            },
            RomeDetails: {
                Update: null
            },
            UserHardDelete: {
                Update: null
            },
            LockAccount: {
                Update: null,
                RequestReactivate: null
            },
            Password: {
                Update: null,
                Create: null,
                Reset: null,
                ResetAfterFindMyAccount: null
            },
            UserSoftDelete: {
                Update: null
            },
            SSN: {
                Update: null
            },
            DirectAdvertising: {
                Update: null
            },
            EmailAddress: {
                Update: null
            },
            Identity: {
            
            },
            TrafficInformation: {
                Update: null,
                UpdateFromStopOrLinePage: null
            },
            NotificationSettings: {
                Update: null
            },
            ValidationEmail: {
                ResendRegister: null
            },
            FindMyAccount: {
                FindMyAccountSearch: null,
                FindMyAccountSuccess: null,
                FindMyAccountNoHits: null,
                FindMyAccountFailure: null,
                FindMyAccountShowLogin: null,
                FindMyAccountSendResetPasswordMail: null
            }
        },
        GroupTravel: {
            Add: null,
            AddFail: null,
            Delete: null,
            Update: null,
            UpdateAdminData: null
        },
        Compensation: {
            Add: null,
            AddFail: null
        },
        DeparturesSearchEngine: {
            DeparturesSearch: {
                JourneySearchFormSuccess: null,
                JourneySearchFormFailure: null,
                StopPageSuccess: null,
                StopPageFailure: null,
                DepartureBoardSuccess: null,
                DepartureBoardFailure: null,
                MapStopMenuSuccess: null,
                MapStopMenuFailure: null
            }
        },
        JourneySearchEngine: {
            JourneySearch: {
                StartPage: null,
                JourneySearchPage: null
            },

            SwapToAndFrom: null,

            DateChanged: null,
            TimeChanged: null,
            FitArrivalOrDepartureChanged: null,
            TrafficTypesChanged: null,
            ChangeTimeChanged: null,
            PriorityChanged: null,
            WalkAcceptableChanged: null,
            ShowIntermediateStops: null,
            Print: {
                FullResult: null,
                Journey: null
            },
            ShowJourneyDetails: null,
            MoreEarlier: null,
            MoreLater: null
        },
        Map: {
            ZoomOnStart: {
                StartPage: null,
                MapMenu: null
            },
            ZoomOnEnd: {
                StartPage: null,
                MapMenu: null
            },
            ZoomOnSwitch: {
                WalkStart: null,
                JourneyStart: null,
                JourneyEnd: null,
                WalkEnd: null,
                Switch: null
            },
            ZoomOnLeg: {
                Vehicle: null,
                Walk: null,
                WalkStart: null,
                WalkEnd: null
            },
            ShowFullScreen: null
        },
        MapMenu: {
            ExpandJourneySearch: null,
            ExpandTimeTablePeriod: null,
            ExpandStops: null,
            ExpandLines: null,
            ExpandTaxeZones: null,
            ExpandCityZones: null,
            ExpandNaromraden: null,
            ExpandServicePoints: null,
            ExpandLinesByTrafficType: null,
            ExpandShare: null,
            ExpandAdvanced: null,
            ExpandCommuterParkings: null
        },
        TimeTable: {
            TimeTableSearch: {
                TimeTablePage: null
            },
            DownloadPdf: null
        },
        Stop: {
            StopSearch: {
                StopSearchPage: null
            }
        },
        TrafficInformation: {
            Expand: null
        },
        Notifications: {
            NotificationsHeader: {
                NotificationsHeaderExpand: null,
                NotificationsHeaderLinkClick: null
            }
        },
        InformationBoard: {
            userSelectorClickSwitchSlide: null,
            userSelectorClickGlobalTimerReset: null,
            userDocumentClickGlobalTimerReset: null,
            userContentInteractionBlockExpand: null,
            userContentInteractionBlockTimerReset: null,
            userContentInteractionBlockActive: null,
            userToggleButtonBlockExpand: null,
            userToggleButtonBlockClose: null,
            userDocumentClickBlockClose: null,
            timeoutBlockClose: null,

        }
    };

    (function () {

        for (var eventCategory in events) {

            var eventActions = events[eventCategory];

            for (var eventAction in eventActions) {

                var eventLabels = eventActions[eventAction];

                if (eventLabels) {

                    for (var eventLabel in eventLabels) {

                        eventLabels[eventLabel] = (function (category, action, label) {

                            return function (options) {

                                options = options || {};

                                options.category = category;
                                options.action = action;
                                options.label = label;

                                track(options);
                            };

                        }(eventCategory, eventAction, eventLabel));

                    }

                } else {

                    eventActions[eventAction] = (function (category, action) {

                        return function (options) {

                            options = options || {};

                            options.category = category;
                            options.action = action;

                            track(options);

                        };

                    }(eventCategory, eventAction));

                }

            }

        }

        events.SetCustomerId = function(customerId) {

            if (typeof ga == "undefined") {
                return;
            }
            ga('set', 'userId', customerId);

        };

    }());

    window.OGT = window.OGT || {};
    window.OGT.Tracking = events;

}());;
window.OGT = window.OGT || {};
window.OGT.Util = window.OGT.Util || {};
window.OGT.Util.OGT = window.OGT.Util.OGT || {};

//MainMenu
$(document).ready(function () {


    function calculateDurationFromHeight(height) {
        return height;
    }

    var $mainMenuItems = $("#menu__main .nav li.menu__top"),
        $mainMenuItemButtons = $mainMenuItems.find("button.menu__item--top"),
        $subMenuContainer = $("#submenu__container");


    $.fn.moveAndMeasureSubMenus = function () {
        this.each(function () {

            var $this = $(this);

            var $submenus = $subMenuContainer.find(".submenu__area");

            if ($this.siblings().length > 0) {
                menuInit($this);
            }
            else if ($submenus.length > 0) {
                $submenus.each(function () {
                    $(this).height("auto");
                    var measuredHeight = $(this).outerHeight();
                    $(this)
                        .attr("measuredHeight", measuredHeight)
                        .css("height", 0);
                    this.style.transition = "height " + calculateDurationFromHeight(measuredHeight) + "ms";
                });
            }
        });
    }


    function menuInit(elem) {
        var $parent = elem.parent();
        var $subMenuArea = $parent.prop("subMenuAreaRef");

        if (!$subMenuArea) {
            $subMenuArea = elem.siblings();
            $parent.prop("subMenuAreaRef", $subMenuArea);

            $subMenuArea
                .appendTo("#submenu__container")
                .addClass("submenu__area--measure");

            $subMenuArea.find("ul.sub__menu > li").equalHeights();

            var measuredHeight = $subMenuArea.outerHeight();

            $subMenuArea.
                attr("measuredHeight", measuredHeight)
                .css("height", 0)
                .removeClass("submenu__area--measure");

            $subMenuArea.get(0).style.transition = "height " + calculateDurationFromHeight(measuredHeight) + "ms";

            $subMenuContainer.find(".sub__item a").attr("tabindex", "-1");
        }
    }

    function openSubMenuArea($subMenuArea, delayMs, $button) {

        var willBeOpenedStyle = $subMenuArea.get(0).style;
        willBeOpenedStyle.transitionDelay = delayMs + "ms";
        willBeOpenedStyle.height = $subMenuArea.attr("measuredHeight") + "px";

        var MENU_OPEN_CSS_CLASS = "submenu__area--open";

        $subMenuArea.addClass(MENU_OPEN_CSS_CLASS);

        $subMenuArea.find(".sub__item > a").attr("tabindex", "0");
        $button.find("button.menu__item--top").attr("aria-expanded", true);

        OGT.Search.showHideMainMenuSearch(false);

        var $openMenuChildrenLinks = $(".submenu__area--open .sub__item > a");

        // Set up tabindex

        $('.menu__item--top').on("keydown", function (e) {
            if (e.keyCode === 9) {

                if (e.shiftKey) { // If reverse tab, go to prev item and close menu
                    closeSubMenuArea($subMenuContainer.find(".submenu__area--open"));
                }
                else if ($(this).parent().hasClass("open")) {
                    e.preventDefault();
                    e.stopPropagation();
                    $(".submenu__area--open .sub__item > a").first().trigger('focus');
                }
            }
        });

        $openMenuChildrenLinks.on("keydown", function (e) {
            e.stopPropagation();
            if (e.keyCode === 9) {

                if (e.shiftKey) {
                    if ($(this).closest(".sub__menu ").find(".sub__item > a").first().is($(this))) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(".menu__top.open > button").first().trigger('focus');
                    }
                } else {
                    if ($openMenuChildrenLinks.last().is($(this))) {
                        e.preventDefault();
                        e.stopPropagation();
                        var $nextTopMenuItem = $(".menu__top.open").first().next();

                        window.OGT.Util.scrollToElement($nextTopMenuItem);
                        var link = $nextTopMenuItem.find("a");

                        if (link.length == 0) {
                            link = $nextTopMenuItem.find("button");
                        }

                        link.first().trigger('focus');

                        closeSubMenuArea($subMenuContainer.find(".submenu__area--open"));
                    }
                }
            }
        });

        $(document).on("keyup", function (e) {
            e.preventDefault();
            e.stopPropagation();

        });

    }


    function closeSubMenuArea($subMenuArea) {

        if ($subMenuArea.length) {
            var wasOpenedStyle = $subMenuArea.get(0).style;

            // The two lines below are somewhat weird, but are necessary to reset the transition-delay
            // in all web browsers. (Ie requires 0, the rest seems to be fine with null.)
            wasOpenedStyle.transitionDelay = null;
            wasOpenedStyle.transitionDelay = 0;

            wasOpenedStyle.height = 0;
        }

        $subMenuArea.removeClass("submenu__area--open");

        $subMenuArea.find(".sub__item > a").attr("tabindex", "-1");

        $mainMenuItems.removeClass('open');
        $mainMenuItems.find("button.menu__item--top").attr("aria-expanded", false);

    }

    $mainMenuItemButtons.moveAndMeasureSubMenus();

    $subMenuContainer.addClass("initialized");

    window.addEventListener('resize', function (event) {
        if (OGT.Util.OGT.getLayoutWidth() == OGT.Util.OGT.layoutWidths.desktop) {
            $mainMenuItemButtons.moveAndMeasureSubMenus();
        }
    });

    $mainMenuItems.bind('click', function (e) {
        //$mainMenuItems.bind('click touchstart', function (e) {
        var $this = $(this),
            isVisible = $this.hasClass('open');

        if (isVisible) {
            e.preventDefault();
            e.stopPropagation();
            closeSubMenuArea($subMenuContainer.find(".submenu__area.submenu__area--open"));
        }
        else {

            var $willBeOpened = $this.prop("subMenuAreaRef");
            if ($willBeOpened) { //Om det finns en undermeny 

                e.preventDefault();
                e.stopPropagation(); //Make all touch events stop at the #filter1 container element

                var $wasOpened = $willBeOpened.siblings(".submenu__area--open");

                var durationOfPrevious = calculateDurationFromHeight($wasOpened.attr("measuredHeight") || 0);

                closeSubMenuArea($wasOpened);
                openSubMenuArea($willBeOpened, durationOfPrevious, $this);

                $this.addClass('open').siblings().removeClass('open');

            } else {

                $mainMenuItems.removeClass('open');
                closeSubMenuArea($subMenuContainer.find(".submenu__area.submenu__area--open"));

            }
        }
    });

    //Klicka utanför menyn för att dölja undermenyer
    $(document).click(function (event) {
        if (!$(event.target).closest('.menu__wrapper').length || $(event.target).hasClass("closeMenuIcon")) {

            $mainMenuItems.removeClass('open');
            closeSubMenuArea($subMenuContainer.find(".submenu__area.submenu__area--open"));
        }
    });
});;
window.OGT = window.OGT || {};

window.OGT.Login = window.OGT.Login || {};

(function () {

    var isKc = OGT.Util.OGT.isKcMode(),

        $loginFormWrapper = $("#form--login-js"),
        $loginFormInputs = $("#form--login-js input"),
        loginFormWrapper = $loginFormWrapper.get(0),

        $loginForm = $loginFormWrapper.find('.login-js__form'),

        $headingText = $loginForm.find('.heading'),

        $authRow = $loginForm.find(".login-js__auth-source"),

        $authSources = $authRow.find(".auth-source__item"),

        $authSourcesById = {},

        selectedAuthSourceId,

        $messageContainers = $loginForm.find(".messageContainer"),
        $messageContainerLoggedInButNeedStepUp = $messageContainers.filter(".need-step-up"),

        $overlay = $loginForm.find(".overlay"),

        loadingMask = OGT.Util.OGT.getLoadingMask("red"),
        $loadingMaskRoot = loadingMask.$root,

        $loggedInUserOtherMethodQuestion = $overlay.filter(".login__other-methods"),
        $loggedInUserOtherMethodQuestionMustMessage = $messageContainers.filter(".must"),
        $loggedInUserOtherMethodQuestionCanMessage = $messageContainers.filter(".can"),

        $noAccountConnected = $overlay.filter(".login__no-account-connected"),
        $noAccountConnectedConnectAccount = $noAccountConnected.find(".action--connect-account"),
        $accountType = $loginForm.find(".no-account-connected__type"),

        $hideOverlay = $loginForm.find(".buttons--hide-overlay"),

        $usernameNormal = $loginForm.find(".username-normal"),
        $usernameOgt = $loginForm.find(".username-ogt"),

        $redirectResetUrl = $loginForm.find(".other-actions--password-reset"),

        $userName = $loginForm.find(".fields__username"),
        $password = $loginForm.find(".fields__password"),
        $impersonateUserName = $loginForm.find(".fields__impersonate"),
        $publicComputerCheckbox = $loginForm.find(".checkbox--public-computer"),
        $publicComputerLabel = $loginForm.find(".label--public-computer"),

        $submit = $loginForm.find(".submit"),

        $loginDialogLogoutLink = $loginForm.find(".other-actions--logout"),

        $close = $loginForm.find(".action--close"),

        currentLoginSuccessfulCallbacks = [],
        currentAbandonCallbacks = [],
        currentCompleteCallbacks = [],

        currentNeededForAccess,
        currentIsStepUp = false,

        currentLoginRequest = null,

        loginChangeListeners = [],

        $createAccount = $loginFormWrapper.find('.other-actions--register'),
        registerUrl = $createAccount.data('user-register-url') + "Partial",
        $registerForm = $("#form--register-js"),

        $passwordToggleButton = $loginForm.find(".js-password-toggle"),
        $passwordInput = $loginForm.find("#login-password-input"),
        $capsLockToolTip = $loginForm.find(".js-tooltip-close"),


        locationSearch = location.search,
        initAuthSource = OGT.Util.QueryString.decodeUriComponentRespectNull(OGT.Util.QueryString.getParameter(locationSearch, OGT.CONSTANTS.QUERY_STRING.LOGIN_AUTH_SOURCE)),
        initUserName = OGT.Util.QueryString.decodeUriComponentRespectNull(OGT.Util.QueryString.getParameter(locationSearch, OGT.CONSTANTS.QUERY_STRING.LOGIN_USER_NAME)),
        initImpersonateUserName = OGT.Util.QueryString.decodeUriComponentRespectNull(OGT.Util.QueryString.getParameter(locationSearch, OGT.CONSTANTS.QUERY_STRING.LOGIN_IMPERSONATE_USER_NAME));

    initAuthSource = (typeof initAuthSource == 'string' && !parseInt(initAuthSource)) ? window.allAuthenticationSources.find(function (authSource) { return initAuthSource.toLowerCase() == authSource.key.toLowerCase() }).id : initAuthSource;


    var messageArea = OGT.Util.OGT.makeMessageArea();

    $loginForm.find('.login__error__message__target').replaceWith(messageArea.$root);
    hideLoginForm();

    function focusOnFirstUnfilledField() {

        if (!$userName.val()) {
            $userName.focus();
            return;
        }
        if (!$password.val()) {
            $password.focus();
            return;
        }
        if (!$impersonateUserName.val() && $impersonateUserName.parent("label").is(':visible')) {
            $impersonateUserName.focus();
            return;
        }

        $userName.focus();

    }

    $loginForm.append($loadingMaskRoot);

    $hideOverlay.click(function () {
        $overlay.hide();
    });

    function presetUserNameOrEmail() {
        var hrefTemplate = $(this).data("hreftemplate");
        this.href = OGT.Util.String.format(hrefTemplate, $userName.val());
    }

    $redirectResetUrl.click(presetUserNameOrEmail);

    $("a.logout__link").click(function (event) {
        event.preventDefault();

        var href = this.href,
            label = "Logout source " + this.dataset.logoutsource,
            loggedInUserData = OGT.Login.loggedInUserData,
            minimumIdentifyLoggedInUser = loggedInUserData && loggedInUserData.minimumIdentifyLoggedInUser;

        if (minimumIdentifyLoggedInUser) {

            var customer = minimumIdentifyLoggedInUser.Customer,
                impersonator = minimumIdentifyLoggedInUser.Impersonator,
                customerMemSource = window.membershipAndRoleSourcesById[customer.Provider];

            if (customerMemSource) {
                label += " Mem source " + customerMemSource.key;
            }

            if (impersonator) {
                var impersonatorMemSource = window.membershipAndRoleSourcesById[impersonator.Provider];
                if (impersonatorMemSource) {
                    label += " Impersonator Mem source " + impersonatorMemSource.key;
                }
            }

        }

        OGT.Tracking.UserLogin.Logout({
            label: label,
            completeCallback: function () {
                location.href = href;
            }
        });

    });


    $(document).keypress(function (event) {
        if (isVisible()) {
            switch (event.keyCode) {
                case 13:
                    submitForm();
                    break;
                case 27:
                    abandon();
                    break;
            }
        }
    });

    $loginFormWrapper.click(abandon);

    $loginForm.click(function (event) {
        event.stopPropagation();
    });

    $noAccountConnectedConnectAccount.click(function (event) {
        event.preventDefault();
        location.href = $(this).data("url");
    });


    $authSources.each(function () {

        var $authSource = $(this),
            id = $authSource.data("id");

        $authSourcesById[id] = $authSource;

        $authSource.click(function () {
            selectAuthSource(id);

            focusOnFirstUnfilledField();
        });

    });

    $passwordToggleButton.click(togglePasswordVisibility);

    $passwordInput.on("keyup", detectCapsLock);

    $capsLockToolTip.click(closeTooltip);


    function selectAuthSource(authSourceId) {

        selectedAuthSourceId = authSourceId;

        $authSources.removeClass("selected");
        $authSourcesById[authSourceId].addClass("selected");
        
        var source = window.authenticationSourcesById[authSourceId];

        loginFormWrapper.className = "auth-source__item" + source.key;

        refreshLoginButtonText();

        //$usernameNormal.show();
        //$usernameOgt.hide();

        $redirectResetUrl.hide();

        switch (authSourceId) {
            case window.authenticationSourcesByKey.Rome.id:
                $redirectResetUrl.show();
                break;
        }


        switch (authSourceId) {
            case window.authenticationSourcesByKey.Rome.id:

                //$usernameNormal.hide();
                //$usernameOgt.show();
                // Intentionally no break

            case window.authenticationSourcesByKey.Epi.id:
            case window.authenticationSourcesByKey.Win.id:

                $userName.parent().show();
                $password.parent().show();
                $impersonateUserName.parent("label").hide();
                $publicComputerLabel.show();
                break;

            case window.authenticationSourcesByKey.EpiRome.id:

                $userName.parent().show();
                $password.parent().show();
                $impersonateUserName.parent("label").show();
                $publicComputerLabel.hide();
                break;
        }

    }

    function refreshLoginButtonText() {
        var labelTemplate = $submit.data((currentIsStepUp ? 'revalidate' : 'login') + (selectedAuthSourceId == window.authenticationSourcesByKey.Rome.id ? "normal" : "explicitmethod") + 'labeltext'),
            source = window.authenticationSourcesById[selectedAuthSourceId];

        $submit.val(OGT.Util.String.format(labelTemplate, source.label));
    }

    function showIfResponseContainsNeededForAccess(response, options) {

        var neededForAccess = response && response.data && response.data.neededForAccess,
            abandonCallback = options.abandonCallback,
            completeCallback = options.completeCallback;

        if (neededForAccess) {

            show({
                neededForAccess: neededForAccess,
                loginSuccessfulCallback: options.loginSuccessfulCallback,
                abandonCallback: abandonCallback,
                completeCallback: completeCallback
            });

        } else {

            options.noUsefulDataDidNotShowLoginDialogCallback(response);
            if (completeCallback) {
                completeCallback();
            }

        }

    }


    function show(options) {

        messageArea.hide();

        $messageContainers.hide();
        $overlay.hide();
        $authRow.show();

        options = options || {};

        var preFilledUserName = options.preFilledUserName,
            neededForAccess = options.neededForAccess || {},
            wasDeniedAccessToSomething = neededForAccess.wasDeniedAccessToSomething,
            usableAuthSourceIds = neededForAccess.usableAuthenticationSources,
            usableAuthenticationSourceForStepUp = wasDeniedAccessToSomething && neededForAccess.usableAuthenticationSourceForStepUp,
            lessPublicAuthenticationSources = !isKc && (neededForAccess.lessPublicAuthenticationSources || window.lessPublicAuthenticationSources) || [],
            limitedUserInfo = neededForAccess.limitedUserInfo,
            impersonatingUserLimitedUserInfo = neededForAccess.impersonatingUserLimitedUserInfo,
            keepMeLimitedLoggedIn = neededForAccess.keepMeLimitedLoggedIn;

        if (usableAuthSourceIds && usableAuthSourceIds.every(function (usableAuthSourceId) { return lessPublicAuthenticationSources.includes(usableAuthSourceId); })) {
            // All usable sources covered. So we use them anyhow.
            lessPublicAuthenticationSources = [];
        }


        keepMeLimitedLoggedIn = typeof (keepMeLimitedLoggedIn) == "undefined" || keepMeLimitedLoggedIn;

        $headingText.html($headingText.data((usableAuthenticationSourceForStepUp ? 'revalidate' : 'login') + 'label'));

        if (usableAuthenticationSourceForStepUp) {
            $messageContainerLoggedInButNeedStepUp.show();
            $authRow.hide();
        } else if (limitedUserInfo && !isKc) {
            $loggedInUserOtherMethodQuestion.show();

            if (wasDeniedAccessToSomething) {
                $loggedInUserOtherMethodQuestionMustMessage.show();
            } else {
                $loggedInUserOtherMethodQuestionCanMessage.show();
            }
        } else if (!isKc) {
            $authRow.hide();
        }

        $userName.val(preFilledUserName || initUserName || (impersonatingUserLimitedUserInfo || limitedUserInfo || { UserName: "" }).UserName);
        $impersonateUserName.val(initImpersonateUserName || (impersonatingUserLimitedUserInfo ? limitedUserInfo.UserName : ""));

        $publicComputerCheckbox.prop('checked', !keepMeLimitedLoggedIn);


        if (options.loginSuccessfulCallback) {
            currentLoginSuccessfulCallbacks.push(options.loginSuccessfulCallback);
        }
        if (options.abandonCallback) {
            currentAbandonCallbacks.push(options.abandonCallback);
        }
        if (options.completeCallback) {
            currentCompleteCallbacks.push(options.completeCallback);
        }


        currentNeededForAccess = neededForAccess;
        currentIsStepUp = usableAuthenticationSourceForStepUp;

        $userName.prop('disabled', usableAuthenticationSourceForStepUp);
        $impersonateUserName.prop('disabled', usableAuthenticationSourceForStepUp);
        $publicComputerCheckbox.prop('disabled', usableAuthenticationSourceForStepUp);

        var firstSourceId = null;
        $authSources.each(function () {

            var $authSource = $(this),
                id = $authSource.data("id");

            if (lessPublicAuthenticationSources.every(function (otherId) { return id !== otherId; }) && (!usableAuthSourceIds || usableAuthSourceIds.includes(id) && !usableAuthenticationSourceForStepUp) || usableAuthenticationSourceForStepUp === id || initAuthSource == id) {
                $authSource.show();
                firstSourceId = initAuthSource || firstSourceId || id;
            } else {
                $authSource.hide();
            }

        });

        if (firstSourceId) {

            selectAuthSource(firstSourceId);
            $loginFormWrapper.show();
            showLoginForm();
            focusOnFirstUnfilledField();


        } else {

            alert("You can not see this content");

        }

        refreshLoginButtonText();

        popupVisible();
    }

    function popupVisible() {
        if (isVisible()) {
            $('body').css('overflow', 'hidden');
        } else {
            $('body').css('overflow', 'visible');
        }
    }

    function abandon() {

        currentAbandonCallbacks.forEach(function (callback) {
            callback();
        });

        close();
    }

    function close() {

        if (currentLoginRequest) {
            currentLoginRequest.abort();
            currentLoginRequest = null;
        }

        currentCompleteCallbacks.forEach(function (callback) {
            callback();
        });

        currentNeededForAccess = null;
        currentIsStepUp = false;

        currentLoginSuccessfulCallbacks = [];
        currentAbandonCallbacks = [];
        currentCompleteCallbacks = [];

        hideLoginForm();

        popupVisible();
    }

    function showLoginForm() {
        $loginFormInputs.show();
        $loginFormInputs.prop("disabled", false);
        $loginFormInputs.prop("readonly", false);
        $loginFormWrapper.show();
    }

    function hideLoginForm() {
        // Hide all form inputs to stop browsers (hello chrome) from autocompleting them...
        $loginFormInputs.hide();
        $loginFormInputs.prop("readonly", true);
        $loginFormInputs.prop("disabled", true);
        $loginFormWrapper.hide();
    }

    function isVisible() {
        return $loginFormWrapper.is(":visible");
    }

    $close.click(function () {
        abandon();
    });

    $submit.click(function (event) {

        event.stopPropagation();

        submitForm();

    });

    function submitForm() {

        var authSource = selectedAuthSourceId;

        loadingMask.show();

        attempt({
            authSource: authSource,
            userName: $userName.val(),
            password: $password.val(),
            impersonateUserName: $impersonateUserName.val(),
            keepMeLimitedLoggedIn: !$publicComputerCheckbox.is(':checked'),
            neededForAccess: currentNeededForAccess,
            isStepUp: currentIsStepUp,
            successCallback: function (response) {
                currentLoginSuccessfulCallbacks.forEach(function (callback) {
                    callback(response);
                });
                currentLoginSuccessfulCallbacks = [];
                close();
                loadingMask.hide();
            },
            failureCallback: function (response) {

                loadingMask.hide();

                if (!response) {
                    return;
                }

                var responseData = response.data;

                var connectUrl = responseData && responseData.connectUrl;

                if (responseData && responseData.status == "NOT_FOUND" && connectUrl) {

                    $accountType.html(responseData.authSourceLabel);

                    $overlay.hide();
                    $noAccountConnected.show();
                    $noAccountConnectedConnectAccount.data("url", connectUrl);

                } else {

                    messageArea.showResponse(response, true);

                }
            }
        });

        $password.val("");

    }

    function togglePasswordVisibility() {

        var $passwordInput = $(".password input"),
            $passwordToggleVisibility = $(".js-password-toggle"),
            $passwordToggleVisibilityIcon = $passwordToggleVisibility.find(".password-toggle__icon"),
            $passwordToggleVisibilityText = $passwordToggleVisibility.find(".password-toggle__text");

        if ($passwordInput.attr('type') === "password") {
            $passwordInput.attr('type', "text");

            $passwordToggleVisibilityIcon.removeClass('ogt-show-16').addClass("ogt-hide-16");
            $passwordToggleVisibilityText.html(OGT.Language.translate("/general/hide"));
        } else {
            $passwordInput.attr('type', 'password');
            $passwordToggleVisibilityIcon.removeClass("ogt-hide-16").addClass("ogt-show-16");
            $passwordToggleVisibilityText.html(OGT.Language.translate("/general/show"));
        }
    }

    function detectCapsLock(e) {

        var $passwordCapsLockWarning = $(".js-password-caps-lock-warning"),
            $passwordInput = $(".password input");

        if (e.originalEvent instanceof KeyboardEvent) {
            if (e.originalEvent.getModifierState("CapsLock")) {
                $passwordCapsLockWarning.addClass("password-caps-lock-warning--visible");
                $passwordInput.addClass("caps-lock--active");
            } else {
                $passwordCapsLockWarning.removeClass("password-caps-lock-warning--visible");
                $passwordInput.removeClass("caps-lock--active");
            }
        }
    }

    function closeTooltip() {
        var $tooltip = $(".js-password-caps-lock-warning__tooltip");

        $tooltip.addClass("password-caps-lock-warning__tooltip--hidden");
    }

    var refreshLoginStatus = (function () {
        var LOGGED_IN_CSS_CLASS = 'status--logged-in',
            LOGGED_OUT_CSS_CLASS = 'status--logged-out',
            $body = $("body"),
            $headerLoginLink = $(".action--login"),
            $headerLogoutLink = $(".action--logout"),
            $headerPresentationUserName = $(".header__username"),
            $landingPageLink = $("a.landing-page"),
            $landingPageLinkText = $("a.landing-page .text").not(".header__username"),
            $landingPageLinkContainer = $("a.landing-page").parent().not(".user__info");

        return function (response) {
            var data = response && response.data;

            OGT.Login.loggedInUserData = data;

            loginChangeListeners.forEach(function (listener) {
                listener(OGT.Login.loggedInUserData);
            });

            if (data) {
                var presentationsUserName = data.presentationUserName,
                    minimumIdentifyLoggedInUser = data.minimumIdentifyLoggedInUser,
                    customer = minimumIdentifyLoggedInUser.Customer,
                    provider = customer.Provider,
                    impersonator = minimumIdentifyLoggedInUser.Impersonator;

                if (impersonator) {
                    presentationsUserName += " [" + impersonator.UserName + "]";
                }

                OGT.Tracking.SetCustomerId(provider + "_" + customer.ProviderUserId);

                $loginDialogLogoutLink.show();

                $headerLoginLink.hide();
                $headerLogoutLink.show();
                $headerPresentationUserName.html(presentationsUserName);

                if (data.loginLandingPage && data.loginLandingPage.title) {
                    $landingPageLink.attr("href", data.loginLandingPage.url);
                    $landingPageLinkText.html(data.loginLandingPage.title);

                    $landingPageLinkContainer.show();
                } else {
                    $landingPageLink.attr("href", "#");
                    $landingPageLinkText.html("");

                    $landingPageLinkContainer.hide();
                }

                $body.addClass(LOGGED_IN_CSS_CLASS);
                $body.removeClass(LOGGED_OUT_CSS_CLASS);
            } else {
                $loginDialogLogoutLink.hide();

                $headerLoginLink.show();
                $headerLogoutLink.hide();
                $headerPresentationUserName.html("");

                $body.removeClass(LOGGED_IN_CSS_CLASS);
                $body.addClass(LOGGED_OUT_CSS_CLASS);
            }
        };
    }());


    function attempt(options) {

        var authSource = options.authSource,
            token = options.token,
            isStepUp = options.isStepUp,
            data = {
                authSource: authSource,
                keepMeLimitedLoggedIn: options.keepMeLimitedLoggedIn,
                userName: options.userName,
                password: options.password,
                impersonateUserName: options.impersonateUserName,
                token: token
            },
            successCallback = options.successCallback,
            failureCallback = options.failureCallback;

        var authForTracking = "Login Auth " + window.authenticationSourcesById[authSource].key;

        currentLoginRequest = OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.LOGIN + "/Attempt", {
            data: data,
            sendAsJsonPost: true,
            successCallback: function (response) {

                refreshLoginStatus(response);

                (isStepUp ? OGT.Tracking.UserLogin.SuccessStepUp : OGT.Tracking.UserLogin.Success)({
                    label: authForTracking,
                    completeCallback: function () {
                        successCallback(response);
                    }
                });

            },
            failureCallback: function (response) {

                (isStepUp ? OGT.Tracking.UserLogin.FailureStepUp : OGT.Tracking.UserLogin.Failure)({ label: authForTracking });

                if (failureCallback) {
                    failureCallback(response);
                }

            },
            completeCallback: function () {
                currentLoginRequest = null;
                OGT.Login.ViewEditSave.refreshLoginState();
            }
        });


    }

    function listenForLogonChange(listener) {
        loginChangeListeners.push(listener);
        listener(OGT.Login.loggedInUserData);
    }


    var augmentFormFieldWithCleverAutoComplete = (function () {

        function email($emailField) {
            OGT.Login.listenForLogonChange(function (loginData) {
                if (loginData)
                    OGT.Util.OGT.AsyncAjax.UserRomeEdit.Details.get({
                        successCallback: function (response) {
                            if (!$emailField.val().length) $emailField.val(response.data.email || "");
                        }
                    });
            });
        }

        // If only one field is set, prefill firstname and lastname in firstnamefield
        function realName($firstNameField, $lastNameField) {
            // om inte inloggad, gör inget
            //Vid inloggning, om ifylld pilla inte
            // Om inte ifyllda fyll i
            OGT.Login.listenForLogonChange(function (loginData) {
                //Inloggad
                if (loginData) {

                    OGT.Util.OGT.AsyncAjax.UserRomeEdit.Details.get({

                        successCallback: function (response) {
                            var nameData = response.data;
                            var firstName = nameData.firstName || "";
                            var lastName = nameData.lastName || "";
                            if ($lastNameField) {
                                if (!$firstNameField.val().length) $firstNameField.val(firstName);
                                if (!$lastNameField.val().length) $lastNameField.val(lastName);
                            } else {
                                if (!$firstNameField.val().length) $firstNameField.val(firstName + ' ' + lastName);
                            }
                        }
                    });
                }
            });
        }

        return {
            realName: realName,
            email: email
        };

    }());


    function init() {

        $(".action-login").click(function () {

            OGT.Login.show({
                loginSuccessfulCallback: function () {
                    location.href = OGT.Login.loggedInUserData.loginLandingPage.url;
                }
            });

        });

        OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.LOGIN + "/GetLoggedInUserInfo", {
            successCallback: function (response) {

                refreshLoginStatus(response);

            },
            failureCallback: function () {

                refreshLoginStatus();

            }
        });

        $createAccount.click(function (event) {

            event.preventDefault();
            loadingMask.show();

            $.ajax({
                url: registerUrl,
                type: 'GET',
                success: function (result) {
                    hideLoginForm();
                    $registerForm.html(result);
                    $registerForm.show();
                    loadingMask.hide();
                    
                }
            });

        });

    }

    init();


    var viewEditSaveNs = (function () {

        var states = {
            notvisible: "notvisible",
            visible: "visible",
            editable: "editable",
            editinprogress: "editinprogress"
        },
            allViewEditSaves = [];

        function setup(options) {

            return new ViewEditSave(options);

        }

        function refreshLoginState() {
            allViewEditSaves.forEach(function (aViewEditSave) {
                aViewEditSave.tryToReachYourGoal(true);
            });
        }


        var makeButtonRow = (function () {

            var buttonRowTemplate = [
                '<div class="vieweditsave">',
                '<button class="right cancel button rounded secondary" type="button"><i class="ogt-arrow-left-16 icon--small"></i>' + OGT.Language.translate("/viewEditSave/cancel") + '</button>',
                '<button class="right edit button rounded" type="button"><i class="ogt-edit-16 icon--small"></i>' + OGT.Language.translate("/viewEditSave/edit") + '</button>',
                '<button class="right view button rounded" type="button"><i class="icon--lock ogt-lock-16 icon--small"></i><i class="icon--unlock ogt-unlock-16 icon--small"></i>' + OGT.Language.translate("/viewEditSave/view") + '</button>',
                '<button class="right save button rounded" type="submit"><i class="ogt-checkmark icon--small"></i>' + OGT.Language.translate("/viewEditSave/save") + '</button>',
                '<button class="right button--clear button rounded secondary" type="reset"><i class="ogt-clear-16 icon--small"></i>' + OGT.Language.translate("/viewEditSave/clear") + '</button>',
                '</div>'
            ].join('');

            function br(getItemsFunc, options) {

                options = options || {};

                var canOnlyView = this.canOnlyView = options.canOnlyView,
                    canClear = this.canClear = options.canClear;

                var $buttonRow = this.$root = $(buttonRowTemplate),
                    $view = $buttonRow.find('.view');

                if (canOnlyView) {

                    $buttonRow.addClass('vieweditsave--ineligible');

                } else {

                    $buttonRow.addClass('vieweditsave--eligible');

                    var $cancel = $buttonRow.find('.cancel'),
                        $edit = $buttonRow.find('.edit'),
                        $save = $buttonRow.find('.save'),
                        $clear = $buttonRow.find('.button--clear');

                    $cancel.click(function (event) {

                        event.stopPropagation();
                        getItemsFunc().forEach(function (item) {
                            item.tryView();
                        });

                    });

                    $edit.click(function (event) {

                        event.stopPropagation();
                        getItemsFunc().forEach(function (item) {
                            item.tryEdit();
                        });

                    });

                    $save.click(function (event) {

                        event.stopPropagation();
                        getItemsFunc().forEach(function (item) {
                            item.trySave();
                        });

                    });

                    if (canClear) {

                        $buttonRow.addClass('vieweditsave--allow-clear');

                        $clear.click(function (event) {

                            event.stopPropagation();
                            getItemsFunc().forEach(function (item) {
                                item.tryClear();
                            });

                        });

                    }

                }

                $view.click(function (event) {

                    event.stopPropagation();
                    getItemsFunc().forEach(function (item) {
                        item.tryView();
                    });

                });

            };

            br.prototype.setState = function (state) {

                this.$root.removeClass(function () {
                    return this.className.split(' ').find(function (token) { return token.indexOf('state-') == 0; }) || '';
                }).addClass('state-' + state);

            };


            return function (f, c) {

                return new br(f, c);

            };

        }());



        var ViewEditSave = (function () {


            function v(options) {

                var that = this,
                    group = options.group;

                this.stateChangeListeners = [];

                this.populateFunc = options.populateFunc;
                this.viewFunc = options.viewFunc;
                var saveFunc = this.saveFunc = options.saveFunc;

                var neverLock = this.neverLock = options.neverLock;

                var clearModel = this.clearModel = options.clearModel;

                this.confirmSaveFunc = options.confirmSaveFunc || function () { return true; };

                var messageArea = this.messageArea = OGT.Util.OGT.makeMessageArea();
                options.$target.append(messageArea.$root);

                if (!group && !neverLock) {

                    var buttonRow = this.buttonRow = makeButtonRow(function () {
                        return [that];
                    },
                        {
                            canOnlyView: !saveFunc,
                            canClear: clearModel
                        });

                    options.$target.append(buttonRow.$root);

                }

                if (!group && options.$maskTarget) {
                    var loadingMask = this.loadingMask = OGT.Util.OGT.getLoadingMask("red");
                    options.$maskTarget.append(loadingMask.$root);
                }

                this._setState(states.notvisible);


                if (!options.startHidden) {
                    this.tryView({ silent: true });
                }


                allViewEditSaves.push(this);

                if (group) {
                    this.group = group;
                    group.addMember(this);
                }

            }

            v.prototype.tryToReachYourGoal = function (silent) {

                if (this.state == states.notvisible) {
                    this.tryView({ silent: silent });
                }

            };

            v.prototype.tryToReachNextState = function () {

                switch (this.state) {

                    case states.notvisible:

                        this.tryView();
                        break;

                    case states.visible:

                        this.tryEdit();
                        break;

                    case states.editinprogress:

                        this.trySave();
                        break;

                    case states.editable:

                        this.trySave({ isPreflight: true });
                        break;

                }

            };

            v.prototype._setState = function (state) {

                if (this.state == state) {
                    return;
                }

                this.state = state;

                var buttonRow = this.buttonRow;
                if (buttonRow) {
                    buttonRow.setState(state);
                }

                this._callStateChangeListeners();
            };

            v.prototype._callStateChangeListeners = function () {
                this.stateChangeListeners.forEach(function (listener) {
                    listener();
                });
            };

            v.prototype.listenForStateChange = function (callback) {
                this.stateChangeListeners.push(callback);
                callback(this.state);
            };

            v.prototype.setLoading = function (doLoading) {

                this.isLoading = doLoading;
                var loadingMask = this.loadingMask;
                if (loadingMask) {
                    loadingMask.show(doLoading);
                }
                this._callStateChangeListeners();
            };

            v.prototype._tryForState = function (func, targetState, options) {

                options = options || {};

                var that = this,
                    orgArgs = arguments,
                    isPreFlight = options.isPreflight || (targetState == states.editable && this.state == states.visible);

                this.setLoading(true);

                func.call(this, {
                    successCallback: function (response) {

                        that._setMessage(false);

                        that._setState(targetState);

                        that.populateFunc(response);

                        var message = OGT.Util.OGT.AsyncAjax.MetaData.translateToString(response);

                        if (message) {
                            that._setMessage(true, false, message);
                        }

                        if (options.successCallback) {
                            options.successCallback(response.data);
                        }

                        that.setLoading(false);

                    },
                    failureCallback: function (response) {

                        if (!options.silent) {

                            showIfResponseContainsNeededForAccess(
                                response,
                                {
                                    successCallback: function () {
                                        that._tryForState.apply(that, orgArgs);
                                    },
                                    noUsefulDataDidNotShowLoginDialogCallback: function () {
                                        that._setMessage(true, true, OGT.Util.OGT.AsyncAjax.MetaData.translateToString(response));
                                    }
                                });

                        }

                        if (options.failureCallback) {
                            options.failureCallback(response);
                        }

                        that.setLoading(false);

                    }
                }, isPreFlight);

            }

            v.prototype.tryView = function (options) {

                options = options || {};

                var group = this.group;

                if (options.tellTheRestInTheSameGroup && group) {

                    delete options.tellTheRestInTheSameGroup;

                    group.members.forEach(function (member) {
                        member.tryView(OGT.Util.dictClone(options));
                    });

                    return;

                }

                this._tryForState(this.viewFunc, states.visible, options);

            };

            v.prototype.tryEdit = function (options) {

                options = options || {};

                var group = this.group;

                if (options.tellTheRestInTheSameGroup && group) {

                    delete options.tellTheRestInTheSameGroup;

                    group.members.forEach(function (member) {
                        member.tryEdit(OGT.Util.dictClone(options));
                    });

                    return;

                }

                var saveFunc = this.saveFunc;
                if (saveFunc) {
                    this._tryForState(saveFunc, states.editable, options);
                }

            };

            v.prototype.setEditInProgress = function () {
                this._setState(states.editinprogress);
            };

            v.prototype.trySave = function (options) {

                options = options || {};

                var saveFunc = this.saveFunc;
                if (this.saveFunc) {

                    if ((options.isPreflight || this.confirmSaveFunc()) && (this.neverLock || this.state == states.editinprogress)) {
                        this._tryForState(saveFunc, states.visible, options);
                    } else {
                        this.tryView();
                    }

                }

            };

            v.prototype.tryClear = function (options) {

                this.populateFunc({ data: this.clearModel });
                this.setEditInProgress();
                this.trySave(options);

            };

            v.prototype.clearMessage = function () {

                this._setMessage(false);

            };

            v.prototype._setMessage = function (doShow, isError, message) {

                var messageArea = this.messageArea;
                if (doShow) {
                    messageArea.show(message, isError);
                } else {
                    messageArea.hide();
                }

            };

            return v;

        }());

        return {
            setup: setup,
            states: states,
            refreshLoginState: refreshLoginState,
            makeButtonRow: makeButtonRow
        };

    }());

    OGT.Login.show = show;
    OGT.Login.showIfResponseContainsNeededForAccess = showIfResponseContainsNeededForAccess;
    OGT.Login.attempt = attempt;
    OGT.Login.listenForLogonChange = listenForLogonChange;
    OGT.Login.ViewEditSave = viewEditSaveNs;
    OGT.Login.augmentFormFieldWithCleverAutoComplete = augmentFormFieldWithCleverAutoComplete;
    OGT.Login.detectCapsLock = detectCapsLock;
    OGT.Login.togglePasswordVisibility = togglePasswordVisibility;
    OGT.Login.closeTooltip = closeTooltip;


}());;
(function () {

    var $createAccount = $("#form--login-js").find('.other-actions--register');

    var $registerForm = $("#form--register-js"),

        loadingMask = OGT.Util.OGT.getLoadingMask("red"),

        currentRegisterSuccessfulCallbacks = [],
        currentRegisterRequest = null,

        registerAccountUrl = $createAccount.data('user-register-url') + "PartialIndex";

    function init() {

        $registerForm.on("keyup", "#RegistrationPassword", OGT.Login.detectCapsLock);

        $registerForm.on("keyup", "#RegistrationPassword", validatePassword);

        $registerForm.on("click", ".js-password-toggle", OGT.Login.togglePasswordVisibility);

        $registerForm.on("click", ".js-tooltip-close", OGT.Login.closeTooltip);

        $registerForm.on("click", ".js-register-close", closeRegisterForm);

        $registerForm.on("mouseup", function(e) {
            var jsForm = $registerForm.find(".js--register");

            if (!jsForm.is(e.target) && jsForm.has(e.target).length === 0) {
                closeRegisterForm();
            }
        });
       

        $(document).keyup(function (event) {
            if (registerIsVisible()) {
                switch (event.key) {
                    case "Escape":
                        closeRegisterForm();
                        break;
                }
            }
        });

        $registerForm.on("click", "#login", function (event) {
            event.preventDefault();

            submitRegisterForm();
         
        });
    }

    init();

    function validatePassword() {

        var $passwordInput = $registerForm.find("#RegistrationPassword"),
            $passwordValidationLetter = $registerForm.find(".js-password-validation__letter"),
            $passwordValidationNumber = $registerForm.find(".js-password-validation__number"),
            $passwordValidationLength = $registerForm.find(".js-password-validation__length"),
            $checkmarkForLetter = $registerForm.find('.js-checkmark__letter'),
            $checkmarkForNumber = $registerForm.find('.js-checkmark__number'),
            $checkmarkForLength = $registerForm.find('.js-checkmark__length'),
            password = $passwordInput.val();

        var letters = /[a-zA-Z]/g;
        if (password.match(letters)) {
            $checkmarkForLetter.removeClass("hidden");
            $passwordValidationLetter.removeClass("password-validation__item--invalid").addClass("password-validation__item--valid");
        } else {
            $checkmarkForLetter.addClass("hidden");
            $passwordValidationLetter.removeClass("password-validation__item--valid").addClass("password-validation__item--invalid");
        }

        var numbers = /[0-9]/g;
        if (password.match(numbers)) {
            $checkmarkForNumber.removeClass("hidden");
            $passwordValidationNumber.removeClass("password-validation__item--invalid").addClass("password-validation__item--valid");
        } else {
            $checkmarkForNumber.addClass("hidden");
            $passwordValidationNumber.removeClass("password-validation__item--valid").addClass("password-validation__item--invalid");
        }

        if (password.length >= 8) {
            $checkmarkForLength.removeClass("hidden");
            $passwordValidationLength.removeClass("password-validation__item--invalid").addClass("password-validation__item--valid");
        } else {
            $checkmarkForLength.addClass("hidden");
            $passwordValidationLength.removeClass("password-validation__item--valid").addClass("password-validation__item--invalid");
        }
        
    }

    function closeRegisterForm() {

        $registerForm.hide();

    }

    function registerIsVisible() {

        return $registerForm.is(":visible");
    }

    function submitRegisterForm() {

        $registerForm.find(".js--register").append(loadingMask.$root);

        var $RequestVerificationToken = $registerForm.find('input[name="__RequestVerificationToken"]'),
            $RegistrationEmailUserName = $registerForm.find("#RegistrationEmailUserName"),
            $RegistrationPassword = $registerForm.find("#RegistrationPassword"),
            $RegistrationFirstName = $registerForm.find("#RegistrationFirstName"),
            $RegistrationLastName = $registerForm.find("#RegistrationLastName"),
            $RegistrationAcceptance = true,
            $V3Token = $registerForm.find("#V3Token");

        loadingMask.show();

        attempt({
            __RequestVerificationToken: $RequestVerificationToken.val(),
            RegistrationEmailUserName: $RegistrationEmailUserName.val(),
            RegistrationPassword: $RegistrationPassword.val(),
            RegistrationFirstName: $RegistrationFirstName.val(),
            RegistrationLastName: $RegistrationLastName.val(),
            RegistrationAcceptance: $RegistrationAcceptance,
            V3Token: $V3Token.val(),
            
            successCallback: function (response) {
                currentRegisterSuccessfulCallbacks.forEach(function (callback) {
                    callback(response);
                });
                currentRegisterSuccessfulCallbacks = [];

                $("#form--register-js .register-body").html(response);

                loadingMask.hide();

            },
            failureCallback: function (response) {

                if (!response) {
                    return;
                }
                loadingMask.hide();
            }
        });

    }

    function attempt(options) {

        var rt = options.__RequestVerificationToken;
        var data = {
                __RequestVerificationToken: rt,
                RegistrationEmailUserName: options.RegistrationEmailUserName,
                RegistrationPassword: options.RegistrationPassword,
                RegistrationFirstName: options.RegistrationFirstName,
                RegistrationLastName: options.RegistrationLastName,
                RegistrationAcceptance: options.RegistrationAcceptance,
                V3Token: options.V3Token
            },
            successCallback = options.successCallback,
            failureCallback = options.failureCallback;

        currentRegisterRequest = $.ajax({
            url: registerAccountUrl,
            type:"POST",
            data: data,
            success: function (response) {

                if (successCallback) {
                    successCallback(response);
                }

            },
            error: function (response) {

                if (failureCallback) {
                    failureCallback(response);
                }

            },
            complete: function () {

                currentRegisterRequest = null;   
            }
        });
    }

}());;
(function() {

    var $resetForm = $("#form--reset-js");

    function init() {

        $resetForm.on("keyup", "#NewPassword", detectCapsLock);

        $resetForm.on("keyup", "#NewPassword", validatePassword);

        $resetForm.on("click", ".js-password-toggle", toggleResetPasswordVisibility);

        $resetForm.on("click", ".js-tooltip-close", closeTooltip);

    }

    function toggleResetPasswordVisibility() {

        var $passwordInput = $("#NewPassword"),
            $passwordToggleVisibility = $(".js-password-toggle"),
            $passwordToggleVisibilityIcon = $passwordToggleVisibility.find(".password-toggle__icon"),
            $passwordToggleVisibilityText = $passwordToggleVisibility.find(".password-toggle__text");

        if ($passwordInput.attr('type') === "password") {
            $passwordInput.attr('type', "text");

            $passwordToggleVisibilityIcon.removeClass('ogt-show-16').addClass("ogt-hide-16");
            $passwordToggleVisibilityText.html(OGT.Language.translate("/general/hide"));
        } else {
            $passwordInput.attr('type', 'password');
            $passwordToggleVisibilityIcon.removeClass("ogt-hide-16").addClass("ogt-show-16");
            $passwordToggleVisibilityText.html(OGT.Language.translate("/general/show"));
        }
    }

    function detectCapsLock(e) {

        var $passwordCapsLockWarning = $(".js-password-caps-lock-warning"),
            $passwordInput = $("#NewPassword");

        if (e.originalEvent instanceof KeyboardEvent) {
            if (e.originalEvent.getModifierState("CapsLock")) {
                $passwordCapsLockWarning.addClass("password-caps-lock-warning--visible");
                $passwordInput.addClass("caps-lock--active");
            } else {
                $passwordCapsLockWarning.removeClass("password-caps-lock-warning--visible");
                $passwordInput.removeClass("caps-lock--active");
            }
        }
    }

    function closeTooltip() {
        var $tooltip = $(".js-password-caps-lock-warning__tooltip");

        $tooltip.addClass("password-caps-lock-warning__tooltip--hidden");
    }

    function validatePassword() {

        var $passwordInput = $resetForm.find("#NewPassword"),
            $passwordValidationLetter = $resetForm.find(".js-password-validation__letter"),
            $passwordValidationNumber = $resetForm.find(".js-password-validation__number"),
            $passwordValidationLength = $resetForm.find(".js-password-validation__length"),
            $checkmarkForLetter = $resetForm.find('.js-checkmark__letter'),
            $checkmarkForNumber = $resetForm.find('.js-checkmark__number'),
            $checkmarkForLength = $resetForm.find('.js-checkmark__length'),
            password = $passwordInput.val();

        var letters = /[a-zA-Z]/g;
        if (password.match(letters)) {
            $checkmarkForLetter.removeClass("hidden");
            $passwordValidationLetter.removeClass("password-validation__item--invalid").addClass("password-validation__item--valid");
        } else {
            $checkmarkForLetter.addClass("hidden");
            $passwordValidationLetter.removeClass("password-validation__item--valid").addClass("password-validation__item--invalid");
        }

        var numbers = /[0-9]/g;
        if (password.match(numbers)) {
            $checkmarkForNumber.removeClass("hidden");
            $passwordValidationNumber.removeClass("password-validation__item--invalid").addClass("password-validation__item--valid");
        } else {
            $checkmarkForNumber.addClass("hidden");
            $passwordValidationNumber.removeClass("password-validation__item--valid").addClass("password-validation__item--invalid");
        }

        if (password.length >= 8) {
            $checkmarkForLength.removeClass("hidden");
            $passwordValidationLength.removeClass("password-validation__item--invalid").addClass("password-validation__item--valid");
        } else {
            $checkmarkForLength.addClass("hidden");
            $passwordValidationLength.removeClass("password-validation__item--valid").addClass("password-validation__item--invalid");
        }

    }

    init();
}());;
(function () {

    var HEADER_TEMPLATE = [
            '<span class="topbar-item__title show-for-large">',
                '<span class="notifications-icon__container">',
                    '<i class="ogt-mail icon--medium icon--primary"></i>',
                    '<span class="notifications-icon__count"></span>',
                '</span>',
                '<span class="text">Meddelanden</span>',
            '</span>'
    ].join(''),
    DROPDOWN_TEMPLATE = [
        '<div class="dropdown">',
            '<div class="dropdown__content">',
                '<div class="items">',
                '</div>',
            '</div>',
        '</div>'
    ].join(''),
    SUB_HEADING_TEMPLATE = [
        '<h3 class="subheading">',
            '{0}',
        '</h3>'
    ].join('');
    NO_MESSAGES_TEMPLATE = [
        '<div class="no-messages">',
            '{0}',
        '</div>',
        '<div class="notifications__explanation">{1}</div>'
    ].join('');
    ITEM_TEMPLATE = [
        '<{2} {3} class="item">',
            '<span class="item__left"><i class="{1} icon--small icon--primary"></i></span>',
            '<span class="item__right text">{0}</span>',
            '<i class="ogt-chevron-right-16 icon--small icon--highlight"></i>',
        '</{2}>'
    ].join(''),
    HAS_NOTIFICATIONS_CLASS = 'has-notifications';


    var $root,
        $header,
        $iconContainer,
        $iconCount,
        $dropDown,
        $dropDownContentTarget,
        ajaxGetRequest,
        topBarItem,
        loadingMask = OGT.Util.OGT.getLoadingMask("red"),
        currentUnreadCount = 0;

    var startNotificationsInHeader = (function () {

        $root = $('.notifications.topbar-item');
        
        var autoRefreshInterval = 60 * 1000,
            updateTimer;

        function start() {

            if (updateTimer) {
                return;
            }

            updateTimer = window.setInterval(function () {
                refreshNotificationsCount();
            }, autoRefreshInterval);

            refreshNotificationsCount();

        }

        function stop() {

            window.clearInterval(updateTimer);
            updateTimer = null;

        }

        return function () {

            if (!$root.length || !OGT.Login.loggedInUserData) {
                return;
            }

            if (!OGT.Util.addBrowserVisibilityListener(function (isVisible) {
                if (isVisible) {
                    start();
            } else {
                    stop();
            }
            })) {
                // No browser visibility support, just start updating
                start();
            }

        }
    }());


    function refreshNotificationsCount() {

        OGT.Util.OGT.AsyncAjax.User.Notification.getCountForHeader({
            successCallback: function (response) {

                setUnread(response.data.unreadCount);
                rerenderUnread();

            }
        });

    }


    function setUnread(unreadCount) {

        currentUnreadCount = unreadCount;

    }

    function rerenderUnread() {

        if (!$header) {

            $header = $(HEADER_TEMPLATE);

            $dropDown = $(DROPDOWN_TEMPLATE);

            $root.append($dropDown);

            topBarItem = OGT.Util.OGT.setUpClickEventsForTopBarItem({

                $root: $root,
                delayShowDropdown: true,
                openCallback: function () {

                    refreshNotifications(function () {

                        OGT.Tracking.Notifications.NotificationsHeader.NotificationsHeaderExpand();

                        topBarItem.showDropdown();
                    });

                },
                closeCallback: function () {

                    rerenderUnread();

                    if (ajaxGetRequest) {
                        ajaxGetRequest.abandon();
                    }

                }

            });

            $root.prepend($header);

            $iconContainer = $('.notifications .notifications-icon__container').css('visibility', 'visible');
            $iconCount = $('.notifications .notifications-icon__count');

            $dropDownContentTarget = $('.notifications .dropdown__content .items');

            $dropDownContentTarget.on('click', function(event) {
            //$dropDownContentTarget.on('click touchend', function(event) {
                event.stopPropagation();
            });

            $root.append(loadingMask.$root);

            $root.removeClass('hidden');

        }

        if (currentUnreadCount) {
            $iconCount.html(currentUnreadCount).show();
            $iconContainer.addClass(HAS_NOTIFICATIONS_CLASS);
        } else {
            $iconCount.hide();
            $iconContainer.removeClass(HAS_NOTIFICATIONS_CLASS);
        }

    }


    function refreshNotifications(successCallback) {

        var originalThis = this,
            originalArguments = arguments;

        loadingMask.show();

        if (ajaxGetRequest) {
            ajaxGetRequest.abandon();
        }

        $dropDownContentTarget.empty();

        ajaxGetRequest = OGT.Util.OGT.AsyncAjax.User.Notification.getForHeader({

            successCallback: function (response) {

                loadingMask.hide();

                var data = response.data,
                    notifications = data.notifications,
                    unreadCount = data.unreadCount;

                setUnread(unreadCount);
                rerenderUnread();

                if (notifications.length > 0) {
                    $dropDownContentTarget.append(OGT.Util.String.format(
                        SUB_HEADING_TEMPLATE,
                        'Nya meddelanden'
                    ));
                }

                if (currentUnreadCount === 0) {
                    $dropDownContentTarget.append(OGT.Util.String.format(
                        NO_MESSAGES_TEMPLATE,
                        notifications.length > 0 ? "Inga nya meddelanden" : 'Du har inga meddelanden',
                        notifications.length > 0 ? "" : window.messageNoNotifications
                    ));

                }

                notifications.forEach(function (notificationData, index) {

                    if (index === currentUnreadCount) {

                        $dropDownContentTarget.append(OGT.Util.String.format(
                            SUB_HEADING_TEMPLATE,
                            'Äldre meddelanden'
                        ));

                    }

                    var url = notificationData.url,
                        $html = $(OGT.Util.String.format(
                            ITEM_TEMPLATE,
                            notificationData.shortMessage,
                            OGT.Util.OGT.getSeverityIconCssClass(notificationData.severity),
                            url ? 'a' : 'span',
                            url ? 'href="' + url + '"' : ''));

                    $html.filter('a').click(function (event) {

                        var opts = {};
                        if (!OGT.Util.Event.hasModifier(event)) {
                            var href = this.href;
                            opts.completeCallback = function () {
                                location.href = href;
                            };
                            event.preventDefault();
                        }
                        OGT.Tracking.Notifications.NotificationsHeader.NotificationsHeaderLinkClick(opts);
                    });

                    $dropDownContentTarget.append($html);

                });


                if (currentUnreadCount && notifications.length) {

                    var latestTime = notifications[0].timeStamp;

                    OGT.Util.OGT.AsyncAjax.User.Notification.setEventHorizonForHeader({
                        data: {
                            Timestamp: latestTime
                        },
                        successCallback: function (response) {
                            setUnread(0);
                        }
                    });

                }

                if (successCallback) {
                    successCallback();
                }

            },

            failureCallback: function (response) {

                OGT.Login.showIfResponseContainsNeededForAccess(response, {
                    loginSuccessfulCallback: function () {
                        refreshNotifications.apply(originalThis, originalArguments);
                    },
                    abandonCallback: function () {
                        loadingMask.hide();
                        topBarItem.close();
                    },
                    noUsefulDataDidNotShowLoginDialogCallback: function () {
                        loadingMask.hide();
                        topBarItem.close();
                    }
                });

            }

        });

    }

    window.OGT.Util.OGT.startNotificationsInHeader = startNotificationsInHeader;

}());;
window.OGT = window.OGT || {};
window.OGT.MyPages = window.OGT.MyPages || {};

(function () {

    function lockFieldsFuncReadonlyInputs($target, doLock) {

        $target.find('input[type=text],input[type=email],input[type=password],input[type=date],input[type=datetime],input[type=time],input[type=number]').each(function () {
            var $element = $(this),
                isLocked = $element.prop('readonly');
            if (doLock != isLocked) {
                var hadFocus = $element.is(':focus');
                $element.prop('readonly', doLock);
                if (hadFocus && OGT.Util.BrowserDetect.isIe()) {
                    $element.select();
                }
            }
        });

    }

    function lockFieldsFuncCover($target, doLock) {

        $target.find('input').each(function () {

            var $input = $(this),
                $mask = $input.siblings('.maskarea');
            if (!$mask.length && doLock) {
                $input.after('<div class="maskarea"></div>');
            }
            if (doLock) {
                $mask.show();
            } else {
                $mask.hide();
            }

        });

    }

    function lockFieldsFuncNone() { }



    function multiProp(setupOptions) {

        var $block = setupOptions.$block,
            $container = setupOptions.$container,
            userSettingsGroup = setupOptions.userSettingsGroup,
            lockFieldsFunc = setupOptions.lockFieldsFunc || lockFieldsFuncReadonlyInputs,
            confirmSaveFunc = setupOptions.confirmSaveFunc,
            preflightCrudFunc = setupOptions.preflightCrudFunc || setupOptions.updateAsync,
            clearModel = setupOptions.clearModel,
            itemIsEmptyFunc = setupOptions.itemIsEmptyFunc || function (data) {
                for (var propName in data) {
                    if (data[propName]) {
                        return false;
                    }
                }
                return true;
            },
            neverLock = setupOptions.neverLock,
            alwaysAtLeastOneItem = setupOptions.alwaysAtLeastOneItem,
            getItemIdentifierFunc = setupOptions.getItemIdentifierFunc || function (data) { return data.dbId; },
            setItemIdentiferFunc = setupOptions.setItemIdentifierFunc || function (target, id) { target.dbId = id; },
            emptyMessage = setupOptions.emptyMessage,
            allIsDoneCallback = setupOptions.allIsDoneCallback || function () { },
            $itemTarget = $container.find('.item__target').first(),
            $templateItem = setupOptions.$templateItem || $container.find('.item__template').remove(),
            $itemsArray = [],
            $addButton = $container.find('.add'),
            trackingRoot = setupOptions.trackingRoot,
            itemCounter = 0;


        if (!window.backstop) {
            window.backstop = true;
            $(document).unbind('keydown').bind('keydown', function (event) {
                if (event.keyCode === 8) {
                    var doPrevent = true;
                    var types = ["text", "password", "file", "search", "email", "number", "date", "color", "datetime", "datetime-local", "month", "range", "search", "tel", "time", "url", "week"];
                    var d = $(event.srcElement || event.target);
                    var disabled = d.prop("readonly") || d.prop("disabled");
                    if (!disabled) {
                        if (d[0].isContentEditable) {
                            doPrevent = false;
                        } else if (d.is("input")) {
                            var type = d.attr("type");
                            if (type) {
                                type = type.toLowerCase();
                            }
                            if (types.indexOf(type) > -1) {
                                doPrevent = false;
                            }
                        } else if (d.is("textarea")) {
                            doPrevent = false;
                        }
                    }
                    if (doPrevent) {
                        event.preventDefault();
                        return false;
                    }
                }
            });
        }


        if (!neverLock) {
            $block.click(function (event) {
                event.stopPropagation();
                switch (viewEditSave.state) {
                    case OGT.Login.ViewEditSave.states.visible:
                        viewEditSave.tryEdit({ tellTheRestInTheSameGroup: true });
                        break;
                    case OGT.Login.ViewEditSave.states.notvisible:
                        viewEditSave.tryView({ tellTheRestInTheSameGroup: true });
                        break;
                }
            });
        }


        function addItem(data) {

            itemCounter++;

            data = data || {};


            var id = getItemIdentifierFunc(data),
                $item = $templateItem.clone(),
                $textBoxes = $item.find('input[type=text],textarea');

            $itemsArray.push($item);

            $item.attr("id", id);

            setupOptions.mappToDom($item, data, itemCounter);

            $itemTarget.append($item);

            $textBoxes.on('change input', function () {

                viewEditSave.setEditInProgress();
            });

            $item.find('.delete').click(function () {
                if ($item.siblings().length < 1 && $item.parent().parent().hasClass('group__template')) {
                    $item.parent().parent().remove();
                }
                $item.remove();

                viewEditSave.setEditInProgress();
            });

            refreshState();
        }

        $addButton.click(function () {
            addItem();
        });


        var viewEditSave = OGT.Login.ViewEditSave.setup({
            $target: $block,
            $maskTarget: $block,
            confirmSaveFunc: confirmSaveFunc,
            allIsDoneCallback: allIsDoneCallback,
            clearModel: clearModel,
            group: userSettingsGroup,
            neverLock: neverLock,
            populateFunc: function (response) {

                itemCounter = 0;

                var datas = response.data;
                if (datas) {

                    $itemTarget.empty();
                    $itemsArray = [];
                    //$groups = {};

                    if (datas.length) {

                        datas.forEach(function (data) {
                            addItem(data);
                        });

                    } else {

                        if (alwaysAtLeastOneItem) {
                            addItem();
                        }
                        if (emptyMessage) {
                            viewEditSave.messageArea.show(emptyMessage);
                        }

                        if (!setupOptions.saveAsync) {
                            viewEditSave.buttonRow.$root.hide();
                        }

                    }

                    allIsDoneCallback();
                }

            },

            viewFunc: function (options) {

                setupOptions.getAsync({
                    successCallback: function (response) {
                        options.successCallback(response);
                    }, failureCallback: function (response) {
                        options.failureCallback(response);
                    }
                });

            },

            saveFunc: function (options, isPreFlight) {

                if (isPreFlight) {

                    (preflightCrudFunc || function () { options.successCallback({}); })({
                        data: null,
                        successCallback: function (response) {
                            options.successCallback(response);
                        },
                        failureCallback: function (response) {
                            options.failureCallback(response);
                        }
                    });

                    return;

                }


                var updateItems = [],
                    createItems = [],
                    deleteItems = [];

                $itemsArray.forEach(function ($item) {

                    var id = $item.attr('id'),
                        obj = setupOptions.mappToModel($item);

                    if (typeof id !== "undefined") {

                        if (!itemIsEmptyFunc(obj) && $item.parent().length) {
                            updateItems.push(obj);
                        }
                        else {
                            deleteItems.push(obj);
                        }

                        setItemIdentiferFunc(obj, id);

                    } else {
                        createItems.push(obj);
                    }

                });


                var updateCount = updateItems.length,
                    createCount = createItems.length,
                    deleteCount = deleteItems.length;


                if (createCount || updateCount || deleteCount) {

                    var allIsDoneMultiplexer = OGT.Proxy.Io.AllIsDoneMultiplexer({
                        parentCallbacks: [function () {

                            if (lastFailureResponse) {
                                options.failureCallback(lastFailureResponse);
                            } else {
                                options.successCallback(lastSuccessResponse);
                            }

                        }]
                    }),
                    lastSuccessResponse = null,
                    lastFailureResponse = null;

                    function innerSuccessCallback(response) {
                        lastSuccessResponse = response;
                    }
                    function innerFailureCallback(response) {
                        lastFailureResponse = response;
                    }


                    if (updateCount && setupOptions.updateAsync) {
                        if (trackingRoot) {
                            trackingRoot.Update();
                        }
                        setupOptions.updateAsync({
                            data: updateItems,
                            successCallback: function () {
                                if (trackingRoot) {
                                    trackingRoot.Update();
                                }
                                innerSuccessCallback.apply(this, arguments);
                            },
                            failureCallback: innerFailureCallback,
                            completeCallback: allIsDoneMultiplexer.getChildCallback("update")
                        });

                    }
                    if (createCount && setupOptions.saveAsync) {

                        setupOptions.saveAsync({
                            data: createItems,
                            successCallback: function () {
                                if (trackingRoot) {
                                    trackingRoot.Add();
                                }
                                innerSuccessCallback.apply(this, arguments);
                            },
                            failureCallback: innerFailureCallback,
                            completeCallback: allIsDoneMultiplexer.getChildCallback("create")
                        });

                    }
                    if (deleteCount && setupOptions.removeAsync) {

                        setupOptions.removeAsync({
                            data: deleteItems,
                            successCallback: function () {
                                if (trackingRoot) {
                                    trackingRoot.Delete();
                                }
                                innerSuccessCallback.apply(this, arguments);
                            },
                            failureCallback: innerFailureCallback,
                            completeCallback: allIsDoneMultiplexer.getChildCallback("remove")
                        });

                    }

                    allIsDoneMultiplexer.arm();

                }
            }
        });

        function refreshState() {

            var allStates = OGT.Login.ViewEditSave.states;

            var newState = viewEditSave.state;

            var isEditState = allStates.editable == newState || allStates.editinprogress == newState;

            if (!neverLock) {
                lockFieldsFunc($container, !isEditState);
            }

            for (var stateId in allStates) {
                $block.removeClass("state-" + allStates[stateId]);
            }
            $block.addClass("state-" + newState);

        }

        viewEditSave.listenForStateChange(function () {

            refreshState();

        });

        $container.show();

        return viewEditSave;

    }


    function singleProp(setupOptions) {

        var $block = setupOptions.$block,
            $container = setupOptions.$container,
            userSettingsGroup = setupOptions.userSettingsGroup,
            lockFieldsFunc = setupOptions.lockFieldsFunc || lockFieldsFuncReadonlyInputs,
            neverLock = setupOptions.neverLock,
            confirmSaveFunc = setupOptions.confirmSaveFunc,
            saveSuccessCallback = setupOptions.saveSuccessCallback,
            redirectToUrlAtSuccess = setupOptions.redirectToUrlAtSuccess,

            trackingRoot = setupOptions.trackingRoot,
            clearModel = setupOptions.clearModel,
            $textBoxes = $container.find('input'),

            viewEditSave = OGT.Login.ViewEditSave.setup({
                $target: $block,
                $maskTarget: $block,
                confirmSaveFunc: confirmSaveFunc,
                clearModel: clearModel,
                group: userSettingsGroup,
                neverLock: neverLock,
                populateFunc: function (response) {

                    if (response) {


                        var data = response.data;

                        if (data !== null) {

                            setupOptions.mappToDom($container, data);

                        }

                    }

                },

                viewFunc: function (options) {

                    setupOptions.getAsync({
                        successCallback: function (response) {
                            options.successCallback(response);
                        }, failureCallback: function (response) {
                            options.failureCallback(response);
                        }
                    });

                },

                saveFunc: (setupOptions.saveAsync ? function (options, isPreFlight) {

                    var userData = isPreFlight ? null : setupOptions.mappToModel($container);

                    setupOptions.saveAsync({

                        data: userData,
                        successCallback: function (response) {
                            options.successCallback(response);

                            if (trackingRoot) {
                                trackingRoot.Update();
                            }

                            if (!isPreFlight && saveSuccessCallback) {
                                saveSuccessCallback(response);
                            }

                        },
                        failureCallback: function (response) {
                            options.failureCallback(response);
                        }

                    });
                } : null)
            });

        $textBoxes.on('change input', function () {
            viewEditSave.setEditInProgress();
        });

        if (!neverLock) {
            $block.click(function (event) {
                event.stopPropagation();
                switch (viewEditSave.state) {
                    case OGT.Login.ViewEditSave.states.visible:
                        viewEditSave.tryEdit({ tellTheRestInTheSameGroup: true });
                        break;
                    case OGT.Login.ViewEditSave.states.notvisible:
                        viewEditSave.tryView({ tellTheRestInTheSameGroup: true });
                        break;
                }
            });
        }

        viewEditSave.listenForStateChange(function () {

            var newState = viewEditSave.state,
                allStates = OGT.Login.ViewEditSave.states;

            if (!neverLock) {
                lockFieldsFunc($container, allStates.editable != newState && allStates.editinprogress != newState);
            }

            for (var stateId in allStates) {
                $block.removeClass("state-" + allStates[stateId]);
            }
            $block.addClass("state-" + newState);

        });

        $container.show();

        return viewEditSave;

    }


    window.OGT.MyPages = {

        multiProp: multiProp,
        singleProp: singleProp,
        lockFieldsFunc: {
            readonlyInputs: lockFieldsFuncReadonlyInputs,
            cover: lockFieldsFuncCover,
            none: lockFieldsFuncNone
        }

    };


}());;
/******************************
/* Search
/*/
(function () {

    var $allSearchInputs = $(".search-site__input"),
        $headerSearchArea = $("#search__area"),
        $mainMenuSearchToggle = $("#menu__main__search-button");

    $allSearchInputs.blur(function () {
        var $searchInput = $(this);
        if ($searchInput.val().length == 0) {
            $(".search__center").removeClass("active");
        } else {
            $allSearchInputs.val($searchInput.val());
        }
    });

    //snabbsök
    $headerSearchArea.find(".search__icon_id").click(function () {
        $headerSearchArea.find("form").get(0).submit();
    });



    // Close on click outside
    $(".menuwrapper").click(function (e) {
        if ($(e.target).parents(".search__form").length === 0) {
            $allSearchInputs.blur();
        }
    });


    $allSearchInputs.each(function () {

        var $searchInput = $(this);

        $searchInput.keyup(function (e) {

            if (e.keyCode === 13) {

                var isEmpty = $searchInput.val() === "";
                if (!isEmpty) {
                    $searchInput.closest("form").get(0).submit();
                }
            }
        });

    });

    (function () {

        $allSearchInputs.autocomplete({
            selectFirst: true,
            minLength: 2,
            source: function (request, response) {

                OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.SITE_SEARCH + "/GetAutocomplete", {
                    data: {
                        q: request.term
                    },
                    successCallback: function (data) {
                        response(data.map(function (item) {
                            return { label: item.Query };
                        }));

                    }
                });

            },
            focus: function (event, ui) {
                // Set the focused items value to its label (e.g 'Linköpings Resecentrum') in order to make screen readers announce the focused item
                ui.item.value = ui.item.label;
            },
            select: function (event, ui) { //Selection callback

                if (event.keyCode != 13) {
                    this.value = ui.item.label;
                    $(this).closest("form").get(0).submit();
                }

                var data = ui.item.data;
                if (!data) return false;

                ui.item = null; // Kill this, and let the I/O handling handle this from here

                handleSelectedValue(data, this, OGT.Util.Event.isEnter(event), event);

                return false;
            }
        });

    }());

    $(".search__icon, .search__button").click(function (e) {

        var $searchInput = $(this).parent().find(".search-site__input");
        if ($searchInput.val() === "") {
            e.preventDefault();
        }

        $(".search__center").addClass("active");

        if ($searchInput.length > 0) {
            $searchInput.focus();
        }
    });



    function showHideMainMenuSearch(doShow) {

        var $searchInput = $headerSearchArea.find(".search-site__input"),
            $searchButton = $headerSearchArea.find(".search__icon_id > button");

        if (doShow) {
            $mainMenuSearchToggle.addClass("search--active");
            $headerSearchArea.addClass("active");

            $searchInput.attr("tabindex", "0");
            $searchButton.attr("tabindex", "0");
            $searchInput.focus();

        } else {
            $mainMenuSearchToggle.removeClass("search--active");
            $headerSearchArea.removeClass("active");

            $searchInput.attr("tabindex", "-1");
            $searchButton.attr("tabindex", "-1");
        }

    }

    $mainMenuSearchToggle.click(function () {
        showHideMainMenuSearch(!$headerSearchArea.hasClass("active"));
    });

    $(document).click(function (event) {
        // Click outside to hide search area
        var $eventTarget = $(event.target);
        if (!$eventTarget.closest('#search__area').length && !$eventTarget.closest("#menu__main__search-button").length) {
            showHideMainMenuSearch(false);
        }
    });

    OGT.Search = {
        showHideMainMenuSearch: showHideMainMenuSearch
    };

}());;
window.OGT = window.OGT || {};


window.OGT.initCompensationPage = function () {

    var boxesById = {},
        allBoxesInOrder = [],
        loadingMask = OGT.Util.OGT.getLoadingMask("red");

    (function () {

        var BaseBox = (function () {

            function baseBox() {
            }

            baseBox.prototype.init = function (rootElementId) {
                this.$root = $("#" + rootElementId);
                this.messageTemplate = this.$root.data("messagetemplate");
                this.setVisible(true);
            };
            baseBox.prototype.setVisible = function (doShow) {
                this.visible = doShow;
                if (doShow) {
                    this.$root.show();
                } else {
                    this.$root.hide();
                }
            };
            baseBox.prototype.getTextForMessage = function () {
                return this.getBoxTextForMessage();
            };
            baseBox.prototype.boxIsValid = function () {
                if (this.visible) {
                    return this.isValid();
                } else {
                    return true;
                }
            };
            return baseBox;
        }());


        var BaseSubBox = (function () {

            function baseSubBox() {

            }
            OGT.Util.extend(baseSubBox, BaseBox);

            baseSubBox.prototype.init = function (rootElementId) {
                baseSubBox.superclass.init.apply(this, arguments);

            };
            baseSubBox.prototype.getBoxTextForMessage = function () {
                return this.getSubBoxTextForMessage();
            };
            baseSubBox.prototype.isValid = function () {
                if (this.visible) {
                    return this.subBoxIsValid();
                } else {
                    return true;
                }
            };
            return baseSubBox;
        }());

        var TransportationServiceBox = (function () {

            function transportationServiceBox(rootElementId) {
                this.init.apply(this, arguments);

                this.$transportationServiceSelector = this.$root.find("input:radio[name='TransportationService']");
                this.$infoTexts = this.$root.find(".text--info");
                this.$transportationServiceInfo = this.$root.find(".text--info.transportation");
                this.$regularJourneyInfo = this.$root.find(".text--info.regular");

                var that = this;

                this.$transportationServiceSelector.change(function () {
                    that.refreshTransportationServiceMode();
                });
                $(document).ready(function () {
                    that.refreshTransportationServiceMode();
                });

            }
            OGT.Util.extend(transportationServiceBox, BaseBox);

            transportationServiceBox.prototype.getBoxTextForMessage = function () {
                return this.boxText;
            };

            transportationServiceBox.prototype.setBoxTextForMessage = function () {
                if (this.isTransportationService()) {
                    this.boxText = "Jag har åkt med färdtjänst eller sjukresa.";
                } else {
                    this.boxText = "Jag har åkt med allmän kollektivtrafik.";
                }
            }

            transportationServiceBox.prototype.refreshTransportationServiceMode = function () {

                this.setBoxTextForMessage();

                if (this.isTransportationService()) {
                    contactBox.identifyWithPersonalId(true);
                    departureBox.setTransportationServiceMode(true);

                    ticketBox.setVisible(false);
                    compensationTypeBox.setVisible(false);

                    var availablePaymentTypes = paymentBox.availableTypes;
                    paymentBox.showPaymentParts([availablePaymentTypes.bank]);

                    this.$regularJourneyInfo.hide();
                    this.$transportationServiceInfo.show();
                } else {
                    contactBox.identifyWithPersonalId(false);
                    departureBox.setTransportationServiceMode(false);

                    ticketBox.setVisible(true);
                    compensationTypeBox.setVisible(true);

                    this.$transportationServiceInfo.hide();
                    this.$regularJourneyInfo.show();
                }
            };

            transportationServiceBox.prototype.isTransportationService = function () {
                return this.$transportationServiceSelector.filter(":checked").val() == "yes";
            };

            transportationServiceBox.prototype.isValid = function () {
                return true;
            };

            return transportationServiceBox;
        }());

        var ContactBox = (function () {

            function contactBox(rootElementId) {
                this.init.apply(this, arguments);

                this.$textboxPersonalId = this.$root.find("#field--ssn");
                this.$textboxFirstName = this.$root.find("#field--firstname");
                this.$textboxLastName = this.$root.find("#field--lastname");
                this.$textboxAddress = this.$root.find("#field--address");
                this.$textboxPostalCode = this.$root.find("#field--postal-code");
                this.$textboxArea = this.$root.find("#field--area");
                this.$textboxPhone = this.$root.find("#field--phone");
                this.$textboxEmail = this.$root.find("#field--email");
                this.identifyWithPersonalId(false);
            }
            OGT.Util.extend(contactBox, BaseBox);

            contactBox.prototype.identifyWithPersonalId = function (isTrue) {

                var compulsoryFields = this.compulsoryFields = [
                        this.$textboxFirstName,
                        this.$textboxLastName,
                        this.$textboxEmail
                ];

                if (isTrue) {
                    compulsoryFields.push(this.$textboxPersonalId);
                } else {
                    compulsoryFields.push(this.$textboxAddress);
                    compulsoryFields.push(this.$textboxPostalCode);
                    compulsoryFields.push(this.$textboxArea);
                    compulsoryFields.push(this.$textboxPhone);
                }

                this.$root.find(".star").remove();
                this.$root.find("input").attr("required", false);

                this.compulsoryFields.forEach(function (field) {
                    var $thisFieldId = field[0].id;
                    var $thisLabel = this.$root.find("label[for=" + $thisFieldId + "]");
                    $thisLabel.append("<span class='star'>*</span>");
                    $("input#" + $thisFieldId).attr("required", true);
                }, this);
            };

            contactBox.prototype.addPersonalDetailsToDataObject = function (data) {
                data.firstName = this.$textboxFirstName.val();
                data.lastName = this.$textboxLastName.val();
                data.address = this.$textboxAddress.val();
                data.postalcode = this.$textboxPostalCode.val();
                data.area = this.$textboxArea.val();
                data.phone = this.$textboxPhone.val();
                data.email = this.$textboxEmail.val();
                data.ssn = this.$textboxPersonalId.val();
            };

            contactBox.prototype.getBoxTextForMessage = function () {

                return "";

            };

            contactBox.prototype.isValid = function () {
                return this.compulsoryFields.reduce(function (allOkThisFar, $field) {
                    return hasValue($field) && allOkThisFar;
                }, true);
            };
            return contactBox;
        }());


        var DepartureBox = (function () {

            function departureBox(rootElementId) {

                this.init.apply(this, arguments);

                var plannedRegularJourney = this.plannedRegularJourney = new PlannedRegularJourneySubBox("compensation--planned-regular");
                var plannedTransportationService = this.plannedTransportationService = new PlannedTransportationServiceSubBox("compensation--planned-transportation");
                var whatHappened = this.whatHappened = new WhatHappenedSubBox("compensation--whathappened");

                this.allSubBoxes = [plannedRegularJourney, plannedTransportationService, whatHappened];
                this.setTransportationServiceMode(false);

            }
            OGT.Util.extend(departureBox, BaseBox);

            departureBox.prototype.setTransportationServiceMode = function (doTransportationServiceMode) {
                this.plannedRegularJourney.setVisible(!doTransportationServiceMode);
                this.plannedTransportationService.setVisible(doTransportationServiceMode);
                this.whatHappened.showOrHideFields(doTransportationServiceMode);
            };

            departureBox.prototype.getBoxTextForMessage = function () {
                return this.allSubBoxes.filter(function (subBox) {
                    return subBox.visible;
                }).map(function (subBox) {
                    return subBox.getBoxTextForMessage();
                }).join("<br/>");

            };

            departureBox.prototype.isValid = function () {
                return this.allSubBoxes.reduce(function (allOkThisFar, $box) {
                    return $box.isValid() && allOkThisFar;
                }, true);
            };

            var PlannedRegularJourneySubBox = (function () {
                function plannedRegularJourneySubBox() {
                    this.init.apply(this, arguments);

                    this.selectedJourney = null;

                    var $root = this.$root;

                    this.$searchfields = $root.find("#compensation__search-journey");
                    this.$resultContainer = $root.find(".container--result");
                    this.$noselectedjourney = $root.find("#selected-journey--none");
                    this.selectedJourneyDisplay = $root.find("#selected-journey").get(0);

                    this.plannedRegularJourneyMessageTemplate = $root.data("plannedregularjourneymessagetemplate");
                    this.journeyMessageTemplate = $root.data("journeymessagetemplate");
                    this.routelinkMessageTemplate = $root.data("routelinkmessagetemplate");

                    this.$textboxFrom = $root.find(".journey__from-input");
                    this.$textboxTo = $root.find(".journey__to-input");

                    var $textboxTime = this.$textboxTime = $root.find("#journey__time_regular"),
                        $textboxDate = this.$textboxDate = $root.find("#journey__date_regular");


                    var messageArea = OGT.Util.OGT.makeMessageArea();

                    $root.find(".message-target").replaceWith(messageArea.$root); //Error message container


                    var journeyProxy = OGT.Proxy.journey,
                        errorIo = journeyProxy.error;

                    errorIo.listen({
                        newValue: function (inValueMessage) {
                            if (inValueMessage) {
                                messageArea.show(inValueMessage, true);
                            } else {
                                messageArea.hide();
                            }
                        }
                    });

                    //Set up handlers for focus/blur on from/to boxes
                    var wireUpJourneyInputNs = journeyProxy.Util.WireUpInput;

                    wireUpJourneyInputNs.from(this.$textboxFrom, null, errorIo);
                    wireUpJourneyInputNs.to(this.$textboxTo, null, errorIo);

                    wireUpJourneyInputNs.date($textboxDate);
                    OGT.Util.Html.convertInputToDateIfConditions($textboxDate);

                    wireUpJourneyInputNs.time($textboxTime);
                    OGT.Util.Html.convertInputToTimeIfConditions($textboxTime);

                    // Add a button to each journey
                    var that = this;

                    OGT.JourneySearchHandler.SearchResultList.addButtonToEachJourney({
                        langKey: "/searchjourney/select",
                        clickCallback: function (journeyData) {
                            that.selectJourney(journeyData);

                            var $compensationElem = $('#compensation__search-journey');

                            if ($compensationElem.length) {
                                OGT.Util.scrollToElement($compensationElem);
                            }

                        }
                    });

                    journeyProxy.walkAcceptable.reset({ doNotStoreInCookie: true });
                    journeyProxy.acceptableChangeTime.reset({ doNotStoreInCookie: true });
                    journeyProxy.priority.reset({ doNotStoreInCookie: true });
                }
                OGT.Util.extend(plannedRegularJourneySubBox, BaseSubBox);


                plannedRegularJourneySubBox.prototype.selectJourney = function (journeyData) {
                    removeErrorMessage(boxesById.departure.plannedRegularJourney.$resultContainer);
                    this.selectedJourney = journeyData;
                    this.clearAllFields();
                    boxesById.departure.plannedRegularJourney.$searchfields.hide();
                    this.refreshSelection();
                    return true;
                };

                plannedRegularJourneySubBox.prototype.refreshSelection = function () {
                    if (this.selectedJourney != null) {
                        this.$noselectedjourney.hide();
                        this.clearSelection();

                        var that = this;
                        OGT.JourneySearchHandler.SearchResultList.renderJourney(this.selectedJourney,
                        {
                            targetElement: this.selectedJourneyDisplay,
                            useListJourneyHeader: true,
                            extraButtonOnEachJourneyOptions: {
                                langKey: "/searchjourney/cancel",
                                clickCallback: function(journeyData) {
                                    that.removeJourney();
                                }
                            }
                        });
                    } else {
                        this.clearSelection();
                        this.$noselectedjourney.show();
                    }
                };

                plannedRegularJourneySubBox.prototype.clearSelection = function () {
                    $(this.selectedJourneyDisplay).empty();
                };

                plannedRegularJourneySubBox.prototype.removeJourney = function () {
                    this.selectedJourney = null;
                    this.refreshSelection();
                    this.$searchfields.show();
                };

                plannedRegularJourneySubBox.prototype.getSubBoxTextForMessage = function () {

                    var customPlaceText = OGT.Language.translate("/searchjourney/placetypes/custom"),
                        from = (this.selectedJourney.UsedStartPlace.PlaceName || customPlaceText) + " " +
                        Date.fromSwedishDateTime(this.selectedJourney.Departure).getFormattedDateTime(),
                        to = (this.selectedJourney.UsedEndPlace.PlaceName || customPlaceText) + " " +
                        Date.fromSwedishDateTime(this.selectedJourney.Arrival).getFormattedDateTime(),
                        route = "";

                    this.selectedJourney.Routelinks.forEach(function (routelink, index, routelinks) {
                        if (routelink.ogtType != "walkInsignificantly") {
                            route += OGT.Util.String.format(this.routelinkMessageTemplate, routelink.Line.LineName + ((routelink.Line.TrainNo == 0) ? '' : ' (#' + routelink.Line.TrainNo + ')'));
                            if (index < routelinks.length - 1) {
                                route += ", ";
                            }
                        }
                    }, this);

                    var text = OGT.Util.String.format(this.plannedRegularJourneyMessageTemplate, from, to, route);

                    return text;
                };

                plannedRegularJourneySubBox.prototype.subBoxIsValid = function () {
                    if (this.selectedJourney != null) {
                        removeErrorMessage(this.$textboxTime);
                        return true;
                    } else {
                        setErrorMessage(this.$textboxTime, "Du måste söka fram din resa");
                        return false;
                    }
                };

                plannedRegularJourneySubBox.prototype.clearAllFields = function () {
                    OGT.Proxy.journey.resetAllIos({ allIsDoneCallback: function () { removeErrorMessage(boxesById.departure.plannedRegularJourney.$resultContainer); } });
                };

                return plannedRegularJourneySubBox;
            }());

            var PlannedTransportationServiceSubBox = (function () {

                function plannedTransportationServiceSubBox() {
                    this.init.apply(this, arguments);

                    this.plannedTransportationServiceMessageTemplate = this.$root.data("plannedtransportationservicemessagetemplate");

                    this.$journey__time = this.$root.find("#journey__time_transportation");
                    this.$journeyDate = this.$root.find("#journey__date_transportation");
                }
                OGT.Util.extend(plannedTransportationServiceSubBox, BaseSubBox);

                plannedTransportationServiceSubBox.prototype.getSubBoxTextForMessage = function () {
                    var time = this.$journey__time.val(),
                        date = this.$journeyDate.val();
                    var text = OGT.Util.String.format(this.plannedTransportationServiceMessageTemplate, time, date);
                    return text;
                };

                plannedTransportationServiceSubBox.prototype.subBoxIsValid = function () {
                    return hasValue(this.$journey__time) && hasValue(this.$journeyDate);
                };

                return plannedTransportationServiceSubBox;
            }());

            var WhatHappenedSubBox = (function () {

                function whatHappenedSubBox() {
                    this.init.apply(this, arguments);

                    this.whatHappenedMessageTemplateRegularOnly = this.$root.data("whathappenedmessagetemplateregularonly");
                    this.whatHappenedMessageTemplate = this.$root.data("whathappenedmessagetemplate");
                    this.realTimesMessageTemplate = this.$root.data("realtimesmessagetemplate");
                    var maxCharsInDescribeWhatHappened = this.maxCharsInDescribeWhatHappened = this.$root.data("maxcharsindescribewhathappened");

                    this.$realDepTime = this.$root.find(".realtime--departure");
                    this.$realArrTime = this.$root.find(".realtime--arrival");
                    this.$realTimeFields = this.$root.find(".realtime");

                    var $describeWhatHappened = this.$describeWhatHappened = this.$root.find("#field--describe-what-happened");

                    var $charsLeft = this.$root.find(".info--chars-left"),
                        charsLeftTemplate = OGT.Language.translate("/Compensation/ForJs/Departure/MaxCharsInDescribeWhatHappened");
                    $describeWhatHappened.on("keyup blur", function () {
                        var charsLeft = Math.max(0, maxCharsInDescribeWhatHappened - $describeWhatHappened.val().length);
                        if (charsLeft) {
                            $charsLeft.removeClass("info--too-much");
                        } else {
                            $charsLeft.addClass("info--too-much");
                        }
                        $charsLeft.html(OGT.Util.String.format(charsLeftTemplate, "" + charsLeft));
                    });

                    this.$realTimeFields.blur(function () {
                        if (OGT.Util.String.getNiceTimeOrFalse(this.value)) {
                            this.value = OGT.Util.String.getNiceTimeOrFalse(this.value);
                        }
                    });

                }
                OGT.Util.extend(whatHappenedSubBox, BaseSubBox);

                whatHappenedSubBox.prototype.showOrHideFields = function (doTransportationServiceMode) {
                    var $hiddenForTransportationServiceMode = this.$root.find(".hidden--transportation-service-mode");
                    if (doTransportationServiceMode) {
                        $hiddenForTransportationServiceMode.hide();
                    } else {
                        $hiddenForTransportationServiceMode.show();
                    }
                }

                whatHappenedSubBox.prototype.getSubBoxTextForMessage = function () {
                    var realDepTime = this.$realDepTime,
                        realArrTime = this.$realArrTime,
                        realTimesString = OGT.Util.String.format(this.realTimesMessageTemplate, realDepTime.val(), realArrTime.val()),
                        describeWhatHappened = this.$describeWhatHappened;

                    var thisHappened = describeWhatHappened.val();

                    var text = "";

                    if (!boxesById.transportationServiceBox.isTransportationService()) {
                        text += OGT.Util.String.format(this.whatHappenedMessageTemplateRegularOnly, realTimesString);
                    }
                    text += OGT.Util.String.format(this.whatHappenedMessageTemplate, thisHappened);

                    return text;
                };

                whatHappenedSubBox.prototype.subBoxIsValid = function () {
                    var maxCharsInDescribeWhatHappened = this.maxCharsInDescribeWhatHappened,
                        describeWhatHappenedMessage = this.$describeWhatHappened.val();
                    if ((this.$realDepTime.val() && this.$realArrTime.val()) || describeWhatHappenedMessage) {
                        if (describeWhatHappenedMessage && maxCharsInDescribeWhatHappened < describeWhatHappenedMessage.length) {
                            setErrorMessage(this.$describeWhatHappened, OGT.Util.String.format(OGT.Language.translate("/Compensation/ForJs/Departure/ErrorMaxCharsInDescribeWhatHappened"), maxCharsInDescribeWhatHappened));
                            return false;
                        }
                        removeErrorMessage(this.$describeWhatHappened);
                        return true;
                    } else {
                        setErrorMessage(this.$describeWhatHappened, OGT.Language.translate("/Compensation/ForJs/DescriptionOfInput"));
                        return false;
                    }
                };

                return whatHappenedSubBox;

            }());

            return departureBox;
        }());

        var TicketBox = (function () {

            function ticketBox(rootElementId) {
                this.init.apply(this, arguments);

                var $root = this.$root,
                    $ticketRadio = this.$ticketRadio = $root.find("input[name='ticket']"),
                    $travelCardForm = this.$travelCardForm = $root.find("#form--travelcard"),
                    $debitCardForm = this.$debitCardForm = $root.find("#form--debitcard"),
                    $phoneForm = this.$phoneForm = $root.find("#form--phone"),
                    $paperslipForm = this.$paperslipForm = $root.find("#form--paper-slip");
                    $travelPlusForm = this.$travelPlusForm = $root.find("#form--travel-plus");

                $ticketRadio.change(function () {

                    $(".form--ticket").hide();

                    switch (this.value) {
                        case "Reskort":
                            $travelCardForm.show();
                            break;
                        case "Bankkort":
                            $debitCardForm.show();
                            break;
                        case "Biljett i app":
                            $phoneForm.show();
                            break;
                        case "Pappersbiljett":
                            $paperslipForm.show();
                            break;
                        case "Resplus":
                            $travelPlusForm.show();
                            break;
                        default:
                            break;
                    }
                });
            }
            OGT.Util.extend(ticketBox, BaseBox);

            ticketBox.prototype.getCheckedRadioValue = function () {
                return this.$ticketRadio.filter(":checked").val();
            };

            ticketBox.prototype.getBoxTextForMessage = function () {
                var selectedTicket = this.getCheckedRadioValue(),
                    info = "",
                    travelCardNo = this.$travelCardForm.find("#field--travelcard-no").val(),
                    debitCardNo = this.$debitCardForm.find("#field--debitcard-no").val(),
                    phoneNo = this.$phoneForm.find("#field--phone-no").val(),
                    bookingNo = this.$travelPlusForm.find("#field--travel-plus").val();

                if (selectedTicket == "Reskort" && travelCardNo != null && travelCardNo.length > 0) {
                    info = " #" + travelCardNo;
                }
                if (selectedTicket == "Bankkort" && debitCardNo != null && debitCardNo.length > 0) {
                    info = ", ************" + debitCardNo;
                }
                if (selectedTicket == "Biljett i app" && phoneNo != null && phoneNo.length > 0) {
                    info = ", Mobiltelefonnumer: " + phoneNo;
                }
                if (selectedTicket == "Pappersbiljett") {
                    info = ", " + this.$paperslipForm.data("mailinstructions");
                }
                if (selectedTicket == "Resplus") {
                    info = ", Bokningsnummer: " + bookingNo;
                }
                var text = OGT.Util.String.format(this.messageTemplate, selectedTicket, info);

                return text;
            };

            ticketBox.prototype.isValid = function () {
                var selectedTicket = this.getCheckedRadioValue();

                var travelCardNo = this.$travelCardForm.find("#field--travelcard-no"),
                    debitCardNo = this.$debitCardForm.find("#field--debitcard-no"),
                    phoneNo = this.$phoneForm.find("#field--phone-no"),
                    bookingNo = this.$travelPlusForm.find("#field--travel-plus");

                var travelCardNoPattern = /^\d{12}$/,
                    debitCardNoPattern = /^\d{4}$/,
                    bookingNoPattern = /^\d{8}$/;

                var travelCardNoOk = hasValidValue(travelCardNo, travelCardNoPattern, "Reskortsnumret måste bestå av 12 siffror"),
                    debitCardNoOk = hasValidValue(debitCardNo, debitCardNoPattern, "Fältet måste innehålla fyra siffror"),
                    phoneNoOk = hasValue(phoneNo),
                    bookingNoOk = hasValidValue(bookingNo, bookingNoPattern, "Bokningsnumret måste bestå av 8 siffror");

                switch (selectedTicket) {
                    case "Reskort":
                        return travelCardNoOk;
                    case "Bankkort":
                        return debitCardNoOk;
                    case "Biljett i app":
                        return phoneNoOk;
                    case "Resplus":
                        return bookingNoOk;
                    default:
                        return true;
                }
            };

            ticketBox.prototype.emptyForm = function () {
            };

            return ticketBox;
        }());

        var CompensationTypeBox = (function () {

            function compensationTypeBox(rootElementId) {
                this.init.apply(this, arguments);
                this.$compensation = this.$root.find("input:radio[name='compensation']");

                var that = this;

                this.$compensation.change(function () {
                    that.findOutWhatPartsOfThePaymentInfoYouNeed();
                });

                this.findOutWhatPartsOfThePaymentInfoYouNeed();

            }
            OGT.Util.extend(compensationTypeBox, BaseBox);

            compensationTypeBox.prototype.getCheckedRadio = function () {
                if (this.$compensation) {
                    return this.$compensation.filter(":checked");
                }
                return null;
            };

            compensationTypeBox.prototype.findOutWhatPartsOfThePaymentInfoYouNeed = function () {
                var parts = [];
                var $compensation = this.getCheckedRadio();

                if ($compensation) {
                    var availablePaymentTypes = paymentBox.availableTypes;

                    if ($compensation.hasClass("car")) {
                        parts.push(availablePaymentTypes.car);
                    }
                    if ($compensation.hasClass("card-no")) {
                        parts.push(availablePaymentTypes.cardNo);
                    }
                    if ($compensation.hasClass("bank")) {
                        parts.push(availablePaymentTypes.bank);
                    }
                    if ($compensation.hasClass("taxi")) {
                        parts.push(availablePaymentTypes.taxi);
                    }
                }
                paymentBox.showPaymentParts(parts);
            };

            compensationTypeBox.prototype.setVisible = function (doShow) {
                compensationTypeBox.superclass.setVisible.apply(this, arguments);
                if (doShow) {
                    this.findOutWhatPartsOfThePaymentInfoYouNeed();
                }
            };

            compensationTypeBox.prototype.getBoxTextForMessage = function () {

                //Hämtar texten från formulärsfälten
                var checkedRadio = this.getCheckedRadio();
                var delay = "";
                var choice = checkedRadio.val();

                if (checkedRadio.hasClass("late2039")) {
                    delay = "20-39 min";
                }
                if (checkedRadio.hasClass("late4059")) {
                    delay = "40-59 minuter";
                }
                if (checkedRadio.hasClass("lateMoreThan60")) {
                    delay = "> 60 minuter";
                }
                var text = OGT.Util.String.format(this.messageTemplate, delay, choice);

                return text;
            };

            compensationTypeBox.prototype.isValid = function () {
                return somethingIsChecked("compensation");
            };

            compensationTypeBox.prototype.emptyForm = function () {
                //Töm alla formulärsfält
            };
            return compensationTypeBox;
        }());

        var PaymentBox = (function () {

            function paymentBox(rootElementId) {
                this.init.apply(this, arguments);

                var carBox = new CarBox("compensation--car"),
                    cardNoBox = new CardNoBox("compensation--card-no"),
                    bankBox = new BankBox("compensation--bank"),
                    taxiBox = new TaxiBox("compensation--taxi");

                this.subBoxesById = this.availableTypes = {
                    car: carBox,
                    cardNo: cardNoBox,
                    bank: bankBox,
                    taxi: taxiBox
                };

                this.subBoxesInOrder = [
                    carBox,
                    cardNoBox,
                    bankBox,
                    taxiBox
                ];

                this.showPaymentParts([]);
            }
            OGT.Util.extend(paymentBox, BaseBox);

            paymentBox.prototype.showPaymentParts = function (parts) {

                this.subBoxesInOrder.forEach(function (box) {
                    box.setVisible(parts.includes(box));
                });
            };

            paymentBox.prototype.getBoxTextForMessage = function () {
                return this.subBoxesInOrder.filter(function (subBox) {
                    return subBox.visible;
                }).map(function (subBox) {
                    return subBox.getBoxTextForMessage();
                }).join("<br/>");
            };

            paymentBox.prototype.isValid = function () {
                return this.subBoxesInOrder.filter(function (subBox) {
                    return subBox.visible;
                }).reduce(function (allOkThisFar, $box) {
                    return $box.isValid() && allOkThisFar;
                }, true);
            };

            paymentBox.prototype.emptyForm = function () {
                //Töm alla formulärsfält
            };

            var CarBox = (function () {

                function carBox(rootElementId) {
                    this.init.apply(this, arguments);

                    this.$fieldDistance = this.$root.find("#field--distance");
                    
                    this.$from = this.$root.find("#field--from-input");
                    this.$to = this.$root.find("#field--to-input");
                    this.$registrationno = this.$root.find("#field--registration-no");
                }
                OGT.Util.extend(carBox, BaseSubBox);

                carBox.prototype.getSubBoxTextForMessage = function () {

                    var distance = this.$fieldDistance.val();
                    var from = this.$from.val();
                    var to = this.$to.val();
                    var registrationno = this.$registrationno.val();

                    var text = OGT.Util.String.format(this.messageTemplate, distance, from, to, registrationno);

                    return text;
                };

                carBox.prototype.subBoxIsValid = function () {
                    
                    var compulsoryFields = [this.$fieldDistance,
                                            this.$from,
                                            this.$to,
                                            this.$registrationno
                    ];
                    return compulsoryFields.reduce(function (allOkThisFar, $field) {
                        return hasValue($field) && allOkThisFar;
                    }, true);
                };

                carBox.prototype.emptyForm = function () {
                    //Töm alla formulärsfält
                };
                return carBox;
            }());

            var CardNoBox = (function () {

                function cardNoBox(rootElementId) {
                    this.init.apply(this, arguments);
                    this.$fieldTravelCard = this.$root.find("#field--travelcard-no");
                }
                OGT.Util.extend(cardNoBox, BaseSubBox);

                cardNoBox.prototype.getSubBoxTextForMessage = function () {

                    var travelCardNo = this.$fieldTravelCard.val();
                    var text = OGT.Util.String.format(this.messageTemplate, travelCardNo);
                    return text;
                };

                cardNoBox.prototype.subBoxIsValid = function () {
                    var travelCardNoPattern = /^\d{12}$/;
                    var travelCardNoOk = hasValidValue(this.$fieldTravelCard, travelCardNoPattern, "Reskortsnumret måste bestå av 12 siffror");
                    return travelCardNoOk;
                };

                cardNoBox.prototype.emptyForm = function () {
                    //Töm alla formulärsfält
                };
                return cardNoBox;
            }());

            var BankBox = (function () {

                function bankBox(rootElementId) {
                    this.init.apply(this, arguments);

                    this.$bank = this.$root.find("#field--bank");
                    this.$clearing = this.$root.find("#field--clearing");
                    this.$account = this.$root.find("#field--account");
                }
                OGT.Util.extend(bankBox, BaseSubBox);

                bankBox.prototype.getSubBoxTextForMessage = function () {

                    var bank = this.$bank.val();
                    var clearing = this.$clearing.val();
                    var account = this.$account.val();

                    var text = OGT.Util.String.format(this.messageTemplate, bank, clearing, account);
                    return text;
                };

                bankBox.prototype.subBoxIsValid = function () {
                    var compulsoryFields = [this.$bank,
                        this.$clearing,
                        this.$account
                    ];
                    return compulsoryFields.reduce(function (allOkThisFar, $field) {
                        return hasValue($field) && allOkThisFar;
                    }, true);
                };

                bankBox.prototype.emptyForm = function () {
                    //Töm alla formulärsfält
                };
                return bankBox;
            }());

            var TaxiBox = (function () {

                function taxiBox(rootElementId) {
                    this.init.apply(this, arguments);
                }
                OGT.Util.extend(taxiBox, BaseSubBox);

                taxiBox.prototype.getSubBoxTextForMessage = function () {
                    var text = OGT.Util.String.format(this.messageTemplate);
                    return text;
                };

                taxiBox.prototype.subBoxIsValid = function () {
                    return true;
                };

                taxiBox.prototype.emptyForm = function () {
                    //Töm alla formulärsfält
                };
                return taxiBox;
            }());
            
            return paymentBox;
        }());


        var transportationServiceBox = new TransportationServiceBox("compensation--transportation"),
            contactBox = new ContactBox("compensation--contact"),
            departureBox = new DepartureBox("compensation--departurebox"),
            ticketBox = new TicketBox("compensation--ticket"),
            paymentBox = new PaymentBox("compensation--payment"),
            compensationTypeBox = new CompensationTypeBox("compensation--type");


        boxesById.transportationServiceBox = transportationServiceBox;
        boxesById.contact = contactBox;
        boxesById.departure = departureBox;
        boxesById.ticket = ticketBox;
        boxesById.compensationtype = compensationTypeBox;
        boxesById.payment = paymentBox;

        allBoxesInOrder.push(transportationServiceBox);
        allBoxesInOrder.push(contactBox);
        allBoxesInOrder.push(departureBox);
        allBoxesInOrder.push(ticketBox);
        allBoxesInOrder.push(compensationTypeBox);
        allBoxesInOrder.push(paymentBox);

    }());

    function getTextForMessageFromAllBoxes() {

        var boxesWithMessagesInThem = allBoxesInOrder.filter(function (aBox) {
            if (aBox.getTextForMessage() && aBox.visible) {
                return true;
            }
            return false;
        });

        var message = boxesWithMessagesInThem.map(function (aBox) {
            return aBox.getTextForMessage();
        }).join("<br /><br />");

        return message;
    }

    function getMessageArea($aField) {
        var messageArea = $aField.data("messageArea");
        if (!messageArea) {
            messageArea = OGT.Util.OGT.makeMessageArea();
            $aField.data("messageArea", messageArea);
        }
        return messageArea;
    }

    function setErrorMessage($aField, aMessage) {
        var messageArea = getMessageArea($aField);
        $aField.parent().append(messageArea.$root);
        messageArea.show(aMessage, true);
    }

    function removeErrorMessage($aField) {
        getMessageArea($aField).hide();
    }

    function hasValue($aField) {
        var labelName = $aField.closest(".container--compensation").find("label[for='" + $aField.attr('id') + "']").html();
        if ($aField.val()) {
            removeErrorMessage($aField);
            return true;
        } else {
            setErrorMessage($aField, labelName + " är obligatoriskt");
            return false;
        }
    }

    function hasValidValue($aField, aRexExp, anErrorMessage) {
        if (hasValue($aField)) {
            if (aRexExp.test($aField.val())) {
                removeErrorMessage($aField);
                return true;
            } else {
                setErrorMessage($aField, anErrorMessage);
                return false;
            }
        }
        return false;
    }

    function somethingIsChecked(aNameOfARadioBtnGroup) {

        var $group = $("input[type='radio'][name='" + aNameOfARadioBtnGroup + "']:last").parent();

        if ($("input[type='radio'][name='" + aNameOfARadioBtnGroup + "']:checked").length > 0) {
            removeErrorMessage($group);
            return true;
        } else {
            setErrorMessage($group, "Något måste väljas");
            return false;
        }
    }

    function validateAllBoxes() {

        var acceptOk = $("#field--accept:checked").length,
            $container = $("#field--accept");

        if (acceptOk) {
            removeErrorMessage($container);
        } else {
            setErrorMessage($container, "Du måste acceptera villkoren.");
        }

        return acceptOk && allBoxesInOrder.reduce(function (allOkThisFar, aBox) {
            return aBox.boxIsValid() && allOkThisFar;
        }, true);

    }

    var $form = $("form.form--compensation"),
        $sendButton = $form.find(".button--send");

    function sendJson() {
        var data = {};
        boxesById.contact.addPersonalDetailsToDataObject(data);
        var messageFromAllBoxes = getTextForMessageFromAllBoxes();
        messageFromAllBoxes = messageFromAllBoxes.replace(/"/g, '\\"');
        data.message = encodeURIComponent(messageFromAllBoxes);
        data.isTransportationService = boxesById.transportationServiceBox.isTransportationService();

        loadingMask.show();

        OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.COMPENSATION + "/SendDemand", {
            data: data,
            sendAsJsonPost: true,
            successCallback: function (response) {

                OGT.Tracking.Compensation.Add({
                    completeCallback: function() {
                        location.href = $form.attr("action");
                    }
                });
            },
            failureCallback: function (response) {
                OGT.Tracking.Compensation.AddFail();
                alert(response.Message || response.statusText || response);
                $sendButton.prop("disabled", false);
            },
            completeCallback: function () {
                loadingMask.hide();
            }
        });
    }

    $form.append(loadingMask.$root);

    $form.on("submit",
        function (event) {
            event.preventDefault();
            trySend();
        });

    $sendButton.click(function (event) {
        event.preventDefault();
        trySend();
    });

    function trySend() {

        // Set button to disabled to prevent multiple sendings
        $sendButton.prop("disabled", true);

        if (validateAllBoxes()) {
            sendJson();
        } else {
            $sendButton.prop("disabled", false);
        }

    }

};
;
window.OGT = window.OGT || {};


window.OGT.initAdditionalFeePage = function () {

    var boxesById = {},
        allBoxesInOrder = [],
        loadingMask = OGT.Util.OGT.getLoadingMask("red");

    (function () {

        var BaseBox = (function () {

            function baseBox() {
            }

            baseBox.prototype.init = function (rootElementId) {
                this.$root = $("#" + rootElementId);
                this.messageTemplate = this.$root.data("messagetemplate");
                this.setVisible(true);
            };
            baseBox.prototype.setVisible = function (doShow) {
                this.visible = doShow;
                if (doShow) {
                    this.$root.show();
                } else {
                    this.$root.hide();
                }
            };
            baseBox.prototype.getTextForMessage = function () {
                return this.getBoxTextForMessage();
            };
            baseBox.prototype.boxIsValid = function () {
                if (this.visible) {
                    return this.isValid();
                } else {
                    return true;
                }
            };
            return baseBox;
        }());


        var ContactBox = (function () {

            function contactBox(rootElementId) {
                this.init.apply(this, arguments);

                this.$textboxFirstName = this.$root.find("#field--firstname");
                this.$textboxLastName = this.$root.find("#field--lastname");
                this.$textboxPhone = this.$root.find("#field--phone");
                this.$textboxEmail = this.$root.find("#field--email");
                this.$textboxGuardianFirstName = this.$root.find("#field--firstname--guardian");
                this.$textboxGuardianLastName = this.$root.find("#field--lastname--guardian");
                this.$textboxGuardianPhone = this.$root.find("#field--phone--guardian");
                this.$textboxGuardianEmail = this.$root.find("#field--email--guardian");

                this.compulsoryFields = [
                    this.$textboxFirstName,
                    this.$textboxLastName,
                    this.$textboxPhone,
                    this.$textboxEmail
                ];

            }
            OGT.Util.extend(contactBox, BaseBox);

            contactBox.prototype.addPersonalDetailsToDataObject = function (data) {

                data.firstName = this.$textboxFirstName.val();
                data.lastName = this.$textboxLastName.val();
                data.phone = this.$textboxPhone.val();
                data.email = this.$textboxEmail.val();
                data.reporterFirstName = this.$textboxGuardianFirstName.val();
                data.reporterLastName = this.$textboxGuardianLastName.val();
                data.reporterPhone = this.$textboxGuardianPhone.val();
                data.reporterEmail = this.$textboxGuardianEmail.val();
            };

            contactBox.prototype.getBoxTextForMessage = function () {

                var text = OGT.Util.String.format(this.messageTemplate,
                    this.$textboxFirstName.val(),
                    this.$textboxLastName.val(),
                    this.$textboxPhone.val(),
                    this.$textboxEmail.val(),
                    this.$textboxGuardianFirstName.val(),
                    this.$textboxGuardianLastName.val(),
                    this.$textboxGuardianPhone.val(),
                    this.$textboxGuardianEmail.val());

                return text;

            };

            contactBox.prototype.isValid = function () {
                return this.compulsoryFields.reduce(function (allOkThisFar, $field) {
                    return hasValue($field) && allOkThisFar;
                }, true);
            };
            return contactBox;
        }());

        var AdditionalFeeNumberBox = (function () {

            function additionalFeeNumberBox() {
                this.init.apply(this, arguments);
                var $root = this.$root;
                this.$penaltyTicketNumber = $root.find("#field--additionalfeenumber");
                var $issuedDate = this.$issuedDate = $root.find("#issued__date");
                var $showImageClick = $root.find("#show--additionalfeenumber-image");
                $showImageClick.click(function (event) {
                    boxesById.image.toggle();
                });

                OGT.Util.Html.convertInputToDateIfConditions($issuedDate);

            }
            OGT.Util.extend(additionalFeeNumberBox, BaseBox);

            additionalFeeNumberBox.prototype.addDetailsToDataObject = function (data) {

                data.ogtNumber = this.$penaltyTicketNumber.val();
                data.issuedDate = this.$issuedDate.val();

            };

            additionalFeeNumberBox.prototype.getBoxTextForMessage = function () {
                return "";
            };

            additionalFeeNumberBox.prototype.isValid = function () {

                var penaltyTicketNumberMaxLength = 12;
                var penaltyTicketNumberMinLength = 7;
                var penaltyTicketNumberPattern = /^(((Ö)(T)[0-9]{5})|((47)(45)[0-9]{8}))$/;

                var penaltyTicketNumberValid = hasValidLength(this.$penaltyTicketNumber, penaltyTicketNumberMinLength, penaltyTicketNumberMaxLength)
                    && hasValidValue(this.$penaltyTicketNumber, penaltyTicketNumberPattern, (getFieldLabelText(this.$penaltyTicketNumber) + " måste vara i korrekt format: (ÖT + fem siffror) eller (4745 + åtta siffror)"));

                var issuedDateValid = hasValidDate(this.$issuedDate, "Du måste ange datum då tilläggsavgiften utfärdades på formatet ÅÅÅÅ-MM-DD.");

                return penaltyTicketNumberValid
                    && issuedDateValid;

            };

            return additionalFeeNumberBox;

        }());

        var ImageBox = (function () {

            function imageBox() {
                this.init.apply(this, arguments);
                this.$root.hide();

            }
            OGT.Util.extend(imageBox, BaseBox);

            imageBox.prototype.toggle = function () {
                this.$root.toggle();
            };

            imageBox.prototype.getBoxTextForMessage = function () {
                return "";
            };

            imageBox.prototype.isValid = function () {
                return true;
            };

            return imageBox;
        }());



        var TicketBox = (function () {

            function ticketBox(rootElementId) {
                this.init.apply(this, arguments);

                var $root = this.$root,
                    $ticketRadio = this.$ticketRadio = $root.find("input[name='ticket']"),
                    $travelCardForm = this.$travelCardForm = $root.find("#form--travelcard"),
                    $debitCardForm = this.$debitCardForm = $root.find("#form--debitcard"),
                    $phoneForm = this.$phoneForm = $root.find("#form--phone"),
                    $paperslipForm = this.$paperslipForm = $root.find("#form--paper-slip");

                $ticketRadio.change(function () {

                    $(".form--ticket").hide();

                    switch (this.value) {
                        case "Reskort":
                            $travelCardForm.show();
                            break;
                        case "Bankkort":
                            $debitCardForm.show();
                            break;
                        case "Biljett i app":
                            $phoneForm.show();
                            break;
                        case "Pappersbiljett":
                            $paperslipForm.show();
                            break;
                        default:
                            break;
                    }
                });
            }
            OGT.Util.extend(ticketBox, BaseBox);

            ticketBox.prototype.getCheckedRadioValue = function () {
                return this.$ticketRadio.filter(":checked").val();
            };

            ticketBox.prototype.addTicketNumberToDataObject = function (data) {

                var selectedTicket = this.getCheckedRadioValue(),
                    travelTokenNumber = "";

                switch (selectedTicket) {
                    case "Reskort":
                        var travelCardNo = this.$travelCardForm.find("#field--travelcard-no").val();
                        travelTokenNumber = travelCardNo;
                        break;
                    case "Bankkort":
                        var debitCardNo = this.$debitCardForm.find("#field--debitcard-no").val();
                        travelTokenNumber = debitCardNo;
                        break;
                    case "Biljett i app":
                        var phoneNo = this.$phoneForm.find("#field--phone-no").val();
                        travelTokenNumber = phoneNo;
                        break;
                    case "Pappersbiljett":
                        travelTokenNumber = "Pappersbiljett";
                        break;

                }

                data.travelTokenNumber = travelTokenNumber;

            };

            ticketBox.prototype.getBoxTextForMessage = function () {
                var selectedTicket = this.getCheckedRadioValue(),
                    info = "";

                switch (selectedTicket) {
                    case "Reskort":
                        var travelCardNo = this.$travelCardForm.find("#field--travelcard-no").val();
                        info = " #" + travelCardNo;
                        break;
                    case "Bankkort":
                        var debitCardNo = this.$debitCardForm.find("#field--debitcard-no").val();
                        info = ", ************" + debitCardNo;
                        break;
                    case "Biljett i app":
                        var phoneNo = this.$phoneForm.find("#field--phone-no").val();
                        info = ", Mobiltelefonnumer: " + phoneNo;
                        break;
                    case "Pappersbiljett":
                        info = ", " + this.$paperslipForm.data("mailinstructions");
                        break;

                }

                var text = OGT.Util.String.format(this.messageTemplate, selectedTicket, info);

                return text;
            };

            ticketBox.prototype.isValid = function () {
                var selectedTicket = this.getCheckedRadioValue();

                var travelCardNo = this.$travelCardForm.find("#field--travelcard-no"),
                    debitCardNo = this.$debitCardForm.find("#field--debitcard-no"),
                    phoneNo = this.$phoneForm.find("#field--phone-no");

                var travelCardNoPattern = /^\d{12}$/,
                    debitCardNoPattern = /^\d{4}$/;

                var travelCardNoOk = hasValidValue(travelCardNo, travelCardNoPattern, "Reskortsnumret måste bestå av 12 siffror"),
                    debitCardNoOk = hasValidValue(debitCardNo, debitCardNoPattern, "Fältet måste innehålla fyra siffror"),
                    phoneNoOk = hasValue(phoneNo);

                switch (selectedTicket) {
                    case "Reskort":
                        return travelCardNoOk;
                    case "Bankkort":
                        return debitCardNoOk;
                    case "Biljett i app":
                        return phoneNoOk;
                    default:
                        return true;
                }
            };

            ticketBox.prototype.emptyForm = function () {
            };

            return ticketBox;
        }());

        var MessageBox = (function () {

            function messageBox() {
                this.init.apply(this, arguments);

                var maxCharsInDescribeWhatHappened = this.maxCharsInDescribeWhatHappened = this.$root.data("maxcharsindescribewhathappened");

                var $describeWhatHappened = this.$describeWhatHappened = this.$root.find("#field--describe-what-happened");

                var $charsLeft = this.$root.find(".info--chars-left"),
                    charsLeftTemplate = OGT.Language.translate("/Compensation/ForJs/Departure/MaxCharsInDescribeWhatHappened");
                $describeWhatHappened.on("keyup blur", function () {
                    var charsLeft = Math.max(0, maxCharsInDescribeWhatHappened - $describeWhatHappened.val().length);
                    if (charsLeft) {
                        $charsLeft.removeClass("info--too-much");
                    } else {
                        $charsLeft.addClass("info--too-much");
                    }
                    $charsLeft.html(OGT.Util.String.format(charsLeftTemplate, "" + charsLeft));
                });

            }
            OGT.Util.extend(messageBox, BaseBox);

            messageBox.prototype.getBoxTextForMessage = function () {
                return OGT.Util.String.format(this.messageTemplate, this.$describeWhatHappened.val());
            };

            messageBox.prototype.isValid = function () {
                var maxCharsInDescribeWhatHappened = this.maxCharsInDescribeWhatHappened,
                    describeWhatHappenedMessage = this.$describeWhatHappened.val();

                if (describeWhatHappenedMessage && maxCharsInDescribeWhatHappened < describeWhatHappenedMessage.length) {
                    setErrorMessage(this.$describeWhatHappened, OGT.Util.String.format(OGT.Language.translate("/Compensation/ForJs/Departure/ErrorMaxCharsInDescribeWhatHappened"), maxCharsInDescribeWhatHappened));
                    return false;
                }
                removeErrorMessage(this.$describeWhatHappened);
                return true;
            };

            return messageBox;

        }());

        var contactBox = new ContactBox("additionalfee--contact"),
            additionalFeeNumber = new AdditionalFeeNumberBox("additionalfee--number"),
            imageBox = new ImageBox("block--additionalfeenumber-image"),
            ticketBox = new TicketBox("additionalfee--ticket"),
            messageBox = new MessageBox("additionalfee--message");

        boxesById.contact = contactBox;
        boxesById.additionalFeeNumber = additionalFeeNumber;
        boxesById.ticket = ticketBox;
        boxesById.image = imageBox;
        boxesById.message = messageBox;

        allBoxesInOrder.push(contactBox);
        allBoxesInOrder.push(additionalFeeNumber);
        allBoxesInOrder.push(imageBox);
        allBoxesInOrder.push(ticketBox);
        allBoxesInOrder.push(messageBox);

    }());

    function getTextForMessageFromAllBoxes() {

        var boxesWithMessagesInThem = allBoxesInOrder.filter(function (aBox) {
            if (aBox.getTextForMessage() && aBox.visible) {
                return true;
            }
            return false;
        });

        var message = boxesWithMessagesInThem.map(function (aBox) {
            return aBox.getTextForMessage();
        }).join("<br /><br />");

        return message;
    }

    function getMessageArea($aField) {
        var messageArea = $aField.data("messageArea");
        if (!messageArea) {
            messageArea = OGT.Util.OGT.makeMessageArea();
            $aField.data("messageArea", messageArea);
        }
        return messageArea;
    }

    function setErrorMessage($aField, aMessage) {
        var messageArea = getMessageArea($aField);
        $aField.parent().append(messageArea.$root);
        messageArea.show(aMessage, true);
    }

    function removeErrorMessage($aField) {
        getMessageArea($aField).hide();
    }

    function getFieldLabelText($aField) {
        var $label = $aField.closest(".container--compensation").find("label[for='" + $aField.attr('id') + "']");
        return $label.contents().not($label.children(".addition, .extrainfo, .star")).text();
    }

    function hasValue($aField) {
        var labelName = getFieldLabelText($aField);
        if ($aField.val()) {
            removeErrorMessage($aField);
            return true;
        } else {
            setErrorMessage($aField, labelName + " är obligatoriskt");
            return false;
        }
    }

    function hasValidValue($aField, aRexExp, anErrorMessage) {
        if (hasValue($aField)) {
            if (aRexExp.test($aField.val())) {
                removeErrorMessage($aField);
                return true;
            } else {
                setErrorMessage($aField, anErrorMessage);
                return false;
            }
        }
        return false;
    }

    function hasValidMaxLength($aField, aMaxLength, anErrorMessage) {
        if (hasValue($aField)) {
            if ($aField.val().length <= aMaxLength) {
                removeErrorMessage($aField);
                return true;
            } else {
                setErrorMessage($aField, anErrorMessage || (getFieldLabelText($aField) + " får vara max " + aMaxLength + " tecken"));
                return false;
            }
        }
        return false;
    }

    function hasValidMinLength($aField, aMinLength, anErrorMessage) {
        if (hasValue($aField)) {
            if ($aField.val().length >= aMinLength) {
                removeErrorMessage($aField);
                return true;
            } else {
                setErrorMessage($aField, anErrorMessage || (getFieldLabelText($aField) + " måste vara minst " + aMinLength + " tecken"));
                return false;
            }
        }
        return false;
    }

    function hasValidLength($aField, aMinLength, aMaxLength, anErrorMessage) {
        if (hasValue($aField)) {
            var res = true;
            if (aMinLength)
                res &&= hasValidMinLength($aField, aMinLength, anErrorMessage);
            if (aMaxLength)
                res &&= hasValidMaxLength($aField, aMaxLength, anErrorMessage);
            return res;
        }
        return false;
    }

    function hasValidDate($aField, anErrorMessage) {
        if (hasValue($aField)) {
            if (OGT.Util.Is.date($aField.val())) {
                removeErrorMessage($aField);
                return true;
            } else {
                setErrorMessage($aField, anErrorMessage);
                return false;
            }
        }
        return false;
    }

    function validateAllBoxes() {

        var acceptOk = $("#field--accept:checked").length,
            $container = $("#field--accept");

        if (acceptOk) {
            removeErrorMessage($container);
        } else {
            setErrorMessage($container, "Du måste acceptera villkoren.");
        }

        return acceptOk && allBoxesInOrder.reduce(function (allOkThisFar, aBox) {
            return aBox.boxIsValid() && allOkThisFar;
        }, true);

    }

    var $form = $("form.form--additionalFee"),
        $sendButton = $form.find(".button--send");

    function sendJson() {
        var data = {};
        boxesById.contact.addPersonalDetailsToDataObject(data);
        boxesById.additionalFeeNumber.addDetailsToDataObject(data);
        boxesById.ticket.addTicketNumberToDataObject(data);

        var messageFromAllBoxes = getTextForMessageFromAllBoxes();
        messageFromAllBoxes = messageFromAllBoxes.replace(/"/g, '\\"');
        data.message = encodeURIComponent(messageFromAllBoxes);
        data.description = encodeURIComponent(boxesById.message.getTextForMessage().replace(/"/g, '\\"'));

        loadingMask.show();

        OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.ADDITIONALFEE + "/SendDemand", {
            data: data,
            sendAsJsonPost: true,
            successCallback: function (response) {

                OGT.Tracking.Compensation.Add({
                    completeCallback: function () {
                        location.href = $form.attr("action");
                    }
                });
            },
            failureCallback: function (response) {
                OGT.Tracking.Compensation.AddFail();
                alert(response.Message || response.statusText || response);
                $sendButton.prop("disabled", false);
            },
            completeCallback: function () {
                loadingMask.hide();
            }
        });
    }

    $form.append(loadingMask.$root);

    $form.on("submit",
        function (event) {
            event.preventDefault();
            trySend();
        });

    $sendButton.click(function (event) {
        event.preventDefault();
        trySend();
    });

    function trySend() {

        // Set button to disabled to prevent multiple sendings
        $sendButton.prop("disabled", true);

        if (validateAllBoxes()) {
            sendJson();
        } else {
            $sendButton.prop("disabled", false);
        }
    }
};
;
window.OGT = window.OGT || {};

var files = [];
var fileObject = {};
var index;
var test = "";
var guardian;
var checkimage = false;
var travelChoseList = [];
var base64ImgExtra;
var fileNameExtra;
var extensionExtra;


function uploadFile(inputElement) {
    var fileextra = inputElement.files[0];
    var reader2 = new FileReader();
    reader2.onloadend = function () {
        fileNameExtra = inputElement.files[0].name;;
        fileNameExtra = fileNameExtra.split(/(\\|\/)/g).pop()
        extensionExtra = fileNameExtra.split('.').pop();
       // base64ImgExtra = reader2.result.split(',')[1];
        base64ImgExtra = reader2.result.match(/^data:.+\/(.+);base64,(.*)$/)[2]


    }
    reader2.readAsDataURL(fileextra);
}


//upload image and PDF and list files in list
function uploadFilereceipt(inputElement) {
    var filename = [];

        
        var filereceipt = inputElement.files[0];
        var reader = new FileReader();
        reader.onloadend = function () {
            fileName = inputElement.files[0].name;
            fileName = fileName.split(/(\\|\/)/g).pop()
            extension = fileName.split('.').pop();
         // let base64Img = reader.result.split(',')[1];
            var base64Img = reader.result.match(/^data:.+\/(.+);base64,(.*)$/)[2]
            // var base64Img = reader.result;

            var fileObject = {
                base64Img: base64Img,
                fileName: fileName,
                extension: extension
            }
            files.push(fileObject);
            checkimage = true;



       

            var newrow = document.createElement("tr");
            newrow.style.background = "#f2f2f2";

            var imageNameColumn = createRowColumn(newrow);
            var removeColumn = createRowColumn(newrow);

            var labelImage = document.createElement('label');
            labelImage.id = filename;
            labelImage.textContent = fileName;
            labelImage.style.fontSize = '20px';
            imageNameColumn.appendChild(labelImage);

            var remove = document.createElement("button");
            //remove.setAttribute("type", "button");
            //remove.setAttribute("value", "Radera");
            remove.setAttribute("onClick", "deleteRowImage(this)");
            remove.style.backgroundColor = "#f2f2f2";
            remove.style.alignItems = "left";
            remove.innerHTML = "<i class=\"ogt-close-32 icon--medium icon--primary\"></i>"
            removeColumn.appendChild(remove);

            // var remove = document.createElement("button");


            //var iconremove = document.createElement("i");
            //iconremove.setAttribute("class", "ogt-search     icon--medium icon--primary");
            //removeColumn.appendChild(iconremove);


            var table = document.getElementById('t2');
            var tbody = table.querySelector('tbody') || table;
            var count = tbody.getElementsByTagName('tr').length -1;
            newrow.id = count;
            tbody.appendChild(newrow);

            document.getElementById("field--receiptCheck").innerHTML = "";

        }
        reader.readAsDataURL(filereceipt);
       
  
   
    
}


function createRowColumn(row) {
    var column = document.createElement("td");
    //column.style.background = "white";
    //column.style.fontSize = "25px";
    row.appendChild(column)
    return column;
}

var klinikList = [];
var kliniktObject = {};

function addKlinik() {

     var $uploadImageCheck = this.$uploadImageCheck = document.getElementById("field--receipt1");
    if (checkimage) {

        var newrow = document.createElement("tr");

        newrow.style.background = "white";
        //newrow.style.fontSize = "25px";

        var emptyColumn = createRowColumn(newrow);
        var fromdate = createRowColumn(newrow);
        var todate = createRowColumn(newrow);
        var removeColumn = createRowColumn(newrow);
        var editMedicalTravelColumn = createRowColumn(newrow);
        var numericColumn = createRowColumn(newrow);

        var checkbox = document.createElement("input");
        checkbox.setAttribute("type", "checkbox");
        // checkColumn.appendChild(checkbox);

        var datebox = document.createElement("input");
        datebox.setAttribute("type", "date");


        var labelKlinik = document.createElement('label');
        labelKlinik.textContent = document.getElementById('field--nameklinik').value;
        labelKlinik.style.fontSize = '25px';
        emptyColumn.appendChild(labelKlinik);

        emptyColumn.setAttribute("contenteditable", "true");

        var remove = document.createElement("input");
        remove.setAttribute("type", "button");
        remove.setAttribute("value", "Ta bort");
        remove.setAttribute("onClick", "deleteRow(this)");
        remove.title = "Ta bort sjukresa";
        remove.className = "button rounded float-right";
        remove.style.fontSize = "15px";
        //remove.innerHTML = "<i class=\"ogt-close-32 icon--medium icon--primary\"></i>"
        removeColumn.appendChild(remove);


        var edit = document.createElement("input");
        edit.setAttribute("type", "button");
        edit.setAttribute("value", "Ändra");
        edit.setAttribute("onClick", "MedicaltravelEdit(this)");
        edit.title = "Ändra sjukresa";
        edit.className = "button rounded float-right";
        edit.style.fontSize = "15px";
        //edit.innerHTML = "<i class=\"ogt-expand-32 icon--medium icon--primary\"></i>"
        editMedicalTravelColumn.appendChild(edit);

        var checkedBoxes = document.querySelectorAll('input[name=ch]:checked');

        var newlist =  [];
        var travelChoseList = Array.from(checkedBoxes)
        for (var c = 0; c < travelChoseList.length; c++) {

            var box = {
                name : travelChoseList[c].id
            }
            newlist.push(box);
         //   console.log(travelChoseList[c].id);
            
        }

       // console.log(newlist);

       


        checkimage = true;
   

        var table = document.getElementById('t1');
        var tbody = table.querySelector('tbody') || table;
        var count = tbody.getElementsByTagName('tr').length;
        newrow.id = count;
        //numericColumn.innerText = count.toLocaleString() ;

        tbody.appendChild(newrow);

        var kliniktObject = {
            klinikName: document.getElementById('field--nameklinik').value,
            dateTo: document.getElementById('field--Datetoklinik').value,
            dateFrom: document.getElementById('field--Datefromklinik').value,
            travelChoseList: newlist,
            files: files,
            id: count
        }

        klinikList.push(kliniktObject);
       // console.log(klinikList);
      

        var x = document.getElementById("MedicaltravelAddMore");
        x.style.display = "block";

        var y = document.getElementById("MedicaltravelEditFromList");
        y.style.display = "none";

        var z = document.getElementById("MedicaltravelNew");
        z.style.display = "none";

        checkimage = false;
        
    }
    else {
  
        document.getElementById("field--receiptCheck").innerHTML = "  Minst ett kvitto måste laddas upp  ";

    }
    
}

function editKlinik(button) {

    var x = document.getElementById("MedicaltravelAddMore");
    x.style.display = "block";

    var z = document.getElementById("MedicaltravelEditFromList");
    z.style.display = "none";

  
  
    
} 

function deleteRow(button) {
    var row = button.parentNode.parentNode;
    var tbody = row.parentNode;
    tbody.removeChild(row);
    var rows = tbody.getElementsByTagName("tr");

}

function deleteRowImage(button) {
    var row = button.parentNode.parentNode;
    var tbody = row.parentNode;
    tbody.removeChild(row);
    var rows = tbody.getElementsByTagName("tr");
    var rowId = event.target.parentNode.parentNode.id;

    files.splice(rowId, 1);

}


function MedicaltravelEdit(button) {
    var x = document.getElementById("MedicaltravelEditFromList");
    x.style.display = "block";
    var y = document.getElementById("MedicaltravelAddMore");
    y.style.display = "none";
    var z = document.getElementById("MedicaltravelNew");
    z.style.display = "none";

    var Table2 = document.getElementById("t3");
    Table2.innerHTML = "";

   // console.log(klinikList);

    var rowId = button.parentNode.parentNode.id;
    console.log()


    for (var i in klinikList) {
        if (klinikList[i].id == rowId) {
            index = i;
            document.getElementById("field--nameklinikedit").value = klinikList[i].klinikName;
            document.getElementById("field--Datetoklinikedit").value = klinikList[i].dateTo;
            document.getElementById("field--Datefromklinikedit").value = klinikList[i].dateFrom;


            for (var c = 0; c < (klinikList[i].travelChoseList).length; c++) { 

                 if (klinikList[i].travelChoseList[c].name == "carChecbox") {
                    document.getElementById("carChecboxedit").checked = true;
                }
                 if (klinikList[i].travelChoseList[c].name == "trainChecbox") {
                     document.getElementById("trainChecboxedit").checked = true;
                }
                 if (klinikList[i].travelChoseList[c].name == "busChecbox") {
                     document.getElementById("busChecboxedit").checked = true;
                }
                 if (klinikList[i].travelChoseList[c].name == "taxiChecbox") {
                     document.getElementById("taxiChecboxedit").checked = true;
                }
                 if (klinikList[i].travelChoseList[c].name == "airChecbox") {
                     document.getElementById("airChecboxedit").checked = true;
                }


            }

            for (var f = 0; f < (klinikList[index].files).length; f++) {
             
                var newrow = document.createElement("tr");
                newrow.style.background = "#f2f2f2";

                var imageNameColumn = createRowColumn(newrow);
                var removeColumn = createRowColumn(newrow);

                var labelImage = document.createElement('label');
                labelImage.textContent = klinikList[index].files[f].fileName;
                labelImage.style.fontSize = '20px';
                imageNameColumn.appendChild(labelImage);

                //var remove = document.createElement("input");
                //remove.setAttribute("type", "button");
                //remove.setAttribute("value", "X");
                //remove.setAttribute("onClick", "deleteRowImage(this)");
                //remove.style.backgroundColor = "#f2f2f2"
                //remove.style.alignItems = "left";
                //removeColumn.appendChild(remove);

                var remove = document.createElement("button");
                //remove.setAttribute("type", "button");
                //remove.setAttribute("value", "Radera");
                remove.setAttribute("onClick", "deleteRowImage(this)");
                remove.style.backgroundColor = "#f2f2f2";
                remove.style.alignItems = "left";
                remove.innerHTML = "<i class=\"ogt-close-32 icon--medium icon--primary\"></i>"
                removeColumn.appendChild(remove);

                var table = document.getElementById('t3');
                var tbody = table.querySelector('tbody') || table;
                var count = tbody.getElementsByTagName('tr').length;

                tbody.appendChild(newrow);
            }

            break; 
        }
    }
    
   
 

}

function removerecipt(e) {
    var el = e.target;
    el.parentNode.remove();
}

function addmedicaltravelmore() {
    var x = document.getElementById("MedicaltravelNew");
    x.style.display = "block";

    document.getElementById("field--nameklinik").value = "";
    document.getElementById("field--Datetoklinik").value = "";
    document.getElementById("field--Datefromklinik").value = "";
    document.getElementById("carChecbox").checked = false;
    document.getElementById("trainChecbox").checked = false;
    document.getElementById("busChecbox").checked = false;
    document.getElementById("taxiChecbox").checked = false;
    document.getElementById("airChecbox").checked = false;

    files = [];
    
    var Table = document.getElementById("t2");
    Table.innerHTML = "";
}

function saveMedicalTravelEdit() {


    var x = document.getElementById("MedicaltravelEditFromList");
    x.style.display = "none";
    var y = document.getElementById("MedicaltravelAddMore");
    y.style.display = "block";
    var z = document.getElementById("MedicaltravelNew");
    z.style.display = "none";


    klinikList[index].klinikName = document.getElementById("field--nameklinikedit").value;
    klinikList[index].dateTo = document.getElementById("field--Datetoklinikedit").value;
    klinikList[index].dateFrom = document.getElementById("field--Datefromklinikedit").value;

    var checkedBoxes = document.querySelectorAll('input[name=ch]:checked');
    var newlist = [];
    klinikList[index].travelChoseList = [];
    var travelChoseList = Array.from(checkedBoxes)
    for (var c = 0; c < travelChoseList.length; c++) {

        var box = {
            name: travelChoseList[c].id
        }
        newlist.push(box);

    }


    //update list 
    var Table = document.getElementById("t1");
    Table.innerHTML = "";

 
    for (var i = 0; i < klinikList.length; i++) {

        var newrow = document.createElement("tr");  

        newrow.style.background = "white";

        var emptyColumn = createRowColumn(newrow);
        var fromdate = createRowColumn(newrow);
        var todate = createRowColumn(newrow);
        var removeColumn = createRowColumn(newrow);
        var editMedicalTravelColumn = createRowColumn(newrow);
        var numericColumn = createRowColumn(newrow);

        var checkbox = document.createElement("input");
        checkbox.setAttribute("type", "checkbox");

        var datebox = document.createElement("input");
        datebox.setAttribute("type", "date");

        var labelKlinik = document.createElement('label');
        labelKlinik.textContent = klinikList[i].klinikName;
        labelKlinik.style.fontSize = '25px';
        emptyColumn.appendChild(labelKlinik);

        emptyColumn.setAttribute("contenteditable", "true");


        var remove = document.createElement("input");
        remove.setAttribute("type", "button");
        remove.setAttribute("value", "Ta bort");
        remove.setAttribute("onClick", "deleteRow(this)");
        remove.title = "Ta bort sjukresa";
        remove.className = "button rounded";
        remove.style.fontSize = "15px";
        removeColumn.appendChild(remove);

        var edit = document.createElement("input");
        edit.setAttribute("type", "button");
        edit.setAttribute("value", "Ändra");
        edit.setAttribute("onClick", "MedicaltravelEdit(this)");
        edit.title = "Ändra sjukresa";
        edit.style.fontSize = "15px";
        edit.className = "button rounded";
        editMedicalTravelColumn.appendChild(edit);
        
   

    var table = document.getElementById('t1');
    var tbody = table.querySelector('tbody') || table;
    var count = tbody.getElementsByTagName('tr').length;
    newrow.id = klinikList[i].id;

    tbody.appendChild(newrow);
    }

}

function addOnEditMedicalTravelredcipt() {

    //var Table = document.getElementById("t3");
    //Table.innerHTML = "";

}






window.OGT.initMedicalTravelPage = function (extraeceipt) {

 


    var boxesById = {},
            allBoxesInOrder = [],
            loadingMask = OGT.Util.OGT.getLoadingMask("red");

    (function () {

        var BaseBox = (function () {

            function baseBox() {
            }

            baseBox.prototype.init = function (rootElementId) {
                this.$root = $("#" + rootElementId);
                this.messageTemplate = this.$root.data("messagetemplate");
                this.setVisible(true);
            };
            baseBox.prototype.setVisible = function (doShow) {
                this.visible = doShow;
                if (doShow) {
                    this.$root.show();
                } else {
                    this.$root.hide();
                }
            };
            baseBox.prototype.getTextForMessage = function () {
                return this.getBoxTextForMessage();
            };
            baseBox.prototype.boxIsValid = function () {
                if (this.visible) {
                    return this.isValid();
                } else {
                    return true;
                }
            };
            return baseBox;
        }());

        var SsnBox = (function () {


            function ssnBox() {
                this.init.apply(this, arguments);
                var $root = this.$root;
                var $textboxPersonalID = this.$textboxPersonalID = $root.find("#field--personalid");
              

            };

            OGT.Util.extend(ssnBox, BaseBox);
            

 

            ssnBox.prototype.addSsnDetailsToDataObject = function (data) {
                data.ssn = this.$textboxPersonalID.val();
                
            };

            ssnBox.prototype.getBoxTextForMessage = function () {

                return "";
            };

       
            ssnBox.prototype.isValid = function () {

                var checkssn = isValidSwedishSSN(this.$textboxPersonalID.val());
                var ssnHasvalue = hasValue(this.$textboxPersonalID);

                if (ssnHasvalue == true){
                removeErrorMessage(this.$textboxPersonalID);

                if (checkssn == false) {
                    setErrorMessage(this.$textboxPersonalID, "Felaktigt personnummer");
                   }
                }
                return ssnHasvalue && checkssn;
            };
            return ssnBox;

         

        }());


        var ContactBox = (function () {

      

            function contactBox(rootElementId) {
                this.init.apply(this, arguments);
                this.$textboxFirstName = this.$root.find("#field--firstname");
                this.$textboxLastName = this.$root.find("#field--lastname");
                this.$textboxPhone = this.$root.find("#field--phone");
                this.$textboxEmail = this.$root.find("#field--email");

                this.$textboxGuardianpersonalid = this.$root.find("#field--personalid--guardian");
                this.$textboxGuardianFirstName = this.$root.find("#field--firstname--guardian");
                this.$textboxGuardianLastName = this.$root.find("#field--lastname--guardian");
                this.$textboxGuardianPhone = this.$root.find("#field--phone--guardian");
                this.$textboxGuardianEmail = this.$root.find("#field--email--guardian");
                this.$radiobuttonGuardian = this.$root.find("#transportation__radio--no");
     
             
            

                this.compulsoryFields = [,
                       this.$textboxFirstName,
                       this.$textboxLastName,
                       this.$textboxPhone,             

                ];

            }


            OGT.Util.extend(contactBox, BaseBox);

            contactBox.prototype.addPersonalDetailsToDataObject = function (data) {

              
                data.firstName = this.$textboxFirstName.val();
                data.lastName = this.$textboxLastName.val();
                data.phone = this.$textboxPhone.val();
                data.email = this.$textboxEmail.val();
                data.reporterSsn = this.$textboxGuardianpersonalid.val();
                data.reporterFirstName = this.$textboxGuardianFirstName.val();
                data.reporterLastName = this.$textboxGuardianLastName.val();
                data.reporterPhone = this.$textboxGuardianPhone.val();
                data.reporterEmail = this.$textboxGuardianEmail.val();
            };

            contactBox.prototype.getBoxTextForMessage = function () {

                return "";

            };

            contactBox.prototype.isValid = function () {
               
                var checkMailPattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
                var checkmail = hasValidValue(this.$textboxEmail, checkMailPattern, "");
                var checkmailGuardian = true;
                var hasValueEmail = hasValue(this.$textboxEmail);
                var checkssnGuardian = true;
                var personalIdGuardian = true;
                var firstNameGuardian = true;
                var LastNameGuardian = true;
                var PhoneGuardian = true;
                var EmailGuardian = true;

                if (guardian == true) {
                    personalIdGuardian = hasValue(this.$textboxGuardianpersonalid);
                    firstNameGuardian = hasValue(this.$textboxGuardianFirstName);
                    LastNameGuardian = hasValue(this.$textboxGuardianLastName);
                    PhoneGuardian = hasValue(this.$textboxGuardianPhone);
                    EmailGuardian = hasValue(this.$textboxGuardianEmail);
                    checkmailGuardian = hasValidValue(this.$textboxGuardianEmail, checkMailPattern, "");
                    checkssnGuardian = isValidSwedishSSN(this.$textboxGuardianpersonalid.val());
                }
        
                

                if (personalIdGuardian) {
                    removeErrorMessage(this.$textboxGuardianpersonalid);

                    if (checkssnGuardian == false) {
                        setErrorMessage(this.$textboxGuardianpersonalid, "Felaktigt personnummer");
                    }
                }


                if (hasValueEmail)
                {
                    removeErrorMessage(this.$textboxEmail);
                    if (checkmail == false) {
                       setErrorMessage(this.$textboxEmail, "Felaktigt mailadress");
                    }
                }

                if (EmailGuardian) {
                    removeErrorMessage(this.$textboxGuardianEmail);
                    if (checkmailGuardian == false) {
                        setErrorMessage(this.$textboxGuardianEmail, "Felaktigt mailadress");
                    }
                }

                return this.compulsoryFields.reduce(function (allOkThisFar, $field) {
                    return (hasValue($field) && allOkThisFar && hasValueEmail && EmailGuardian && personalIdGuardian && firstNameGuardian && LastNameGuardian && PhoneGuardian && checkmailGuardian && checkssnGuardian);
                    //return (hasValue($field) && allOkThisFar );
                }, true);
            };
            return contactBox;
        }());



    

        var ImageBox = (function () {

        
          
            function imageBox() {
                this.init.apply(this, arguments);
                var $root = this.$root;
                var $textboxDatetoklinik = this.$textboxDatetoklinik = $root.find("#field--Datetoklinik");
                var $textboxDatefromklinik = this.$textboxDatefromklinik = $root.find("#field--Datefromklinik");
                var $textboxDateofoutlay = this.$textboxDateOfOutlay = $root.find("#field--dateofoutlay");
                var $travelChose = this.$travelChose = $root.find("#field--travelchose");
                var $typeofoutlay = this.$typeofoutlay = $root.find("#field--typeofoutlay");
                var $textboxNameKlinik = this.$textboxNameKlinik = $root.find("#field--nameklinik");
                var $uploadImage = this.$uploadImage = $root.find("#field--receipt1");
 
                var $ch = this.$ch = $root.find("#ch");

                //console.log($travelChose);
                //console.log($ch);

                OGT.Util.Html.convertInputToDateIfConditions(this.$textboxDatetoklinik);
                OGT.Util.Html.convertInputToDateIfConditions(this.$textboxDatefromklinik);
                OGT.Util.Html.convertInputToDateIfConditions(this.$textboxDateOfOutlay);

            }

    


            OGT.Util.extend(imageBox, BaseBox);


            imageBox.prototype.addDetailsToDataObject = function (data) {

   
               

                data.dateOfutlay = this.$textboxDateOfOutlay.val();
                data.typeofoutlay = this.$typeofoutlay.val();
                data.klinikList = klinikList;


            };

            imageBox.prototype.getBoxTextForMessage = function () {

                return "";
            };



            imageBox.prototype.isValid = function () {
                var textboxDatetoklinikDateValid = hasValidDate(this.$textboxDatetoklinik, "Du måste ange datum i formatet ÅÅÅÅ-MM-DD.");
                var textboxDatefromklinikDateValid = hasValidDate(this.$textboxDatefromklinik, "Du måste ange datum i formatet ÅÅÅÅ-MM-DD.");
                var textboxNameKlinik = hasValue(this.$textboxNameKlinik);
          
                if (klinikList.length > 0) {
                

                }
 
                //removeErrorMessage(this.$uploadImage);
                //if(checkimage == false) {
                //    setErrorMessage(this.$uploadImage, "Minst ett kvitto måste laddas upp");
                //}
               


                var checkboxs = document.getElementsByName("ch");
                    var okayCheckbox = false;
                    for (var i = 0, l = checkboxs.length; i < l; i++) {
                        if (checkboxs[i].checked) {
                            okayCheckbox = true;
                            break;
                        }               
                    }

                    removeErrorMessage(this.$travelChose);

                    if (okayCheckbox == false) {
                        setErrorMessage(this.$travelChose, "Något färdsätt måste väljas");
                    }


                      var z = document.getElementById("MedicaltravelNew");
                      if (z.style.display == "none") {
                      
                         // return textboxDatetoklinikDateValid && textboxDatefromklinikDateValid && okayCheckbox && textboxNameKlinik && checkimage;
                          return textboxDatetoklinikDateValid && textboxDatefromklinikDateValid && okayCheckbox && textboxNameKlinik;
                      }
                      else {
                          return true;
                      }
 
                    
            };
            return imageBox;
        }());



        var MessageBox = (function () {

            function messageBox() {
                this.init.apply(this, arguments);

                var maxCharsInDescribeWhatHappened = this.maxCharsInDescribeWhatHappened = this.$root.data("maxcharsindescribewhathappened");

                var $describeWhatHappened = this.$describeWhatHappened = this.$root.find("#field--describe-what-happened");
                

                var $charsLeft = this.$root.find(".info--chars-left"),
                    charsLeftTemplate = OGT.Language.translate("/Compensation/ForJs/Departure/MaxCharsInDescribeWhatHappened");
                $describeWhatHappened.on("keyup blur", function () {
                    var charsLeft = Math.max(0, maxCharsInDescribeWhatHappened - $describeWhatHappened.val().length);
                    if (charsLeft) {
                        $charsLeft.removeClass("info--too-much");
                    } else {
                        $charsLeft.addClass("info--too-much");
                    }
                    $charsLeft.html(OGT.Util.String.format(charsLeftTemplate, "" + charsLeft));
                });

            }
            OGT.Util.extend(messageBox, BaseBox);

            messageBox.prototype.getBoxTextForMessage = function () {
                return OGT.Util.String.format(this.messageTemplate, this.$describeWhatHappened.val());
            };

            messageBox.prototype.isValid = function () {
                var maxCharsInDescribeWhatHappened = this.maxCharsInDescribeWhatHappened,
                    describeWhatHappenedMessage = this.$describeWhatHappened.val();

                if (describeWhatHappenedMessage && maxCharsInDescribeWhatHappened < describeWhatHappenedMessage.length) {
                    setErrorMessage(this.$describeWhatHappened, OGT.Util.String.format(OGT.Language.translate("/Compensation/ForJs/Departure/ErrorMaxCharsInDescribeWhatHappened"), maxCharsInDescribeWhatHappened));
                    return false;
                }
                removeErrorMessage(this.$describeWhatHappened);
                return true;
            };

            return messageBox;

        }());

        var contactBox = new ContactBox("medicaltravel--contact"),
       //receiptBox = new ReceiptBox("MedicalTravel--receipt"),
         ssnBox = new SsnBox("MedicalTravel--ssn"),
         imageBox = new ImageBox("MedicalTravel--receipt"),      
         messageBox = new MessageBox("MedicalTravel--message");

        boxesById.contact = contactBox;
        //  boxesById.receipt = receiptBox;
        boxesById.ssn = ssnBox;
        boxesById.image = imageBox;
        boxesById.message = messageBox;

        allBoxesInOrder.push(contactBox);
        // allBoxesInOrder.push(receiptBox);
        allBoxesInOrder.push(ssnBox);
        allBoxesInOrder.push(imageBox);
        allBoxesInOrder.push(messageBox);

    }());

    function getTextForMessageFromAllBoxes() {

        var boxesWithMessagesInThem = allBoxesInOrder.filter(function (aBox) {
            if (aBox.getTextForMessage() && aBox.visible) {
                return true;
            }
            return false;
        });

        var message = boxesWithMessagesInThem.map(function (aBox) {
            return aBox.getTextForMessage();
        }).join("<br /><br />");

        return message;
    }

    function getMessageArea($aField) {
        var messageArea = $aField.data("messageArea");
        if (!messageArea) {
            messageArea = OGT.Util.OGT.makeMessageArea();
            $aField.data("messageArea", messageArea);
        }
        return messageArea;
    }

    function setErrorMessage($aField, aMessage) {
        var messageArea = getMessageArea($aField);
        $aField.parent().append(messageArea.$root);
        messageArea.show(aMessage, true);
    }

    function removeErrorMessage($aField) {
        getMessageArea($aField).hide();
    }

    function hasValue($aField) {
        var labelName = $aField.closest(".container--compensation").find("label[for='" + $aField.attr('id') + "']").html();
        if ($aField.val()) {
            removeErrorMessage($aField);
            return true;
        } else {
            setErrorMessage($aField, labelName + " är obligatoriskt");
            return false;
        }
    }

    function hasValidValue($aField, aRexExp, anErrorMessage) {
        if (hasValue($aField)) {
            if (aRexExp.test($aField.val())) {
                removeErrorMessage($aField);
                return true;
            } else {
                setErrorMessage($aField, anErrorMessage);
                return false;
            }
        }
        return false;
    }

    function hasValidDate($aField, anErrorMessage) {
        if (hasValue($aField)) {
            if (OGT.Util.Is.date($aField.val())) {
                removeErrorMessage($aField);
                return true;
            } else {
                setErrorMessage($aField, anErrorMessage);
                return false;
            }
        }
        return false;
    }

    function validateAllBoxes() {
        var acceptOk = $("#field--accept:checked").length,
            $container = $("#field--accept");

        if (acceptOk) {
            removeErrorMessage($container);
        } else {
            setErrorMessage($container, "Du måste acceptera villkoren.");
        }

        return acceptOk && allBoxesInOrder.reduce(function (allOkThisFar, aBox) {
            return aBox.boxIsValid() && allOkThisFar;
        }, true);

    }

    function isValidSwedishSSN(ssn) {
        ssn = ssn
            .replace(/\D/g, "")     // strip out all but digits
            .split("")              // convert string to array
            .reverse()              // reverse order for Luhn
            .slice(0, 10);          // keep only 10 digits (i.e. 1977 becomes 77)

        if (ssn.length != 10) {
            return false;
        }

        var sum = ssn
          
            .map(function (n) {
                return Number(n);
            })
           
            .reduce(function (previous, current, index) {
              
                if (index % 2) current *= 2;
                if (current > 9) current -= 9;
                return previous + current;
            });

       return 0 === sum % 10;
    };

    $(function () {
        $('form').submit(function () {
            $('body').append('<br>Result: ' + isValidSwedishSSN($('input[type="text"]').val()));
            return false;
        });
    });




    var $form = $("form.form--medicaltravel"),
         $sendButton = $form.find(".button--send");


        
    function sendJson() {
        console.log("send Jason");
       // console.log(klinikList);

        var data = {};
        boxesById.ssn.addSsnDetailsToDataObject(data);
        boxesById.contact.addPersonalDetailsToDataObject(data);
        boxesById.image.addDetailsToDataObject(data);

         var messageFromAllBoxes = getTextForMessageFromAllBoxes();
         messageFromAllBoxes = messageFromAllBoxes.replace(/"/g, '\\"');
         data.message = encodeURIComponent(messageFromAllBoxes);
         data.message = document.getElementById("field--describe-what-happened").value;
         data.message = data.message.replace(/"/g, '\\"');
         data.description = encodeURIComponent(boxesById.message.getTextForMessage());

         console.log("base64ImgExtra: " + base64ImgExtra);
         console.log(fileNameExtra);
         console.log(extensionExtra);
         if (base64ImgExtra != null) {
             var fileObjectExtra = {
                 base64Img: base64ImgExtra,
                 fileName: fileNameExtra,
                 extension: extensionExtra
             }

             klinikList[0].files.push(fileObjectExtra);
             console.log(klinikList);
         }

        // console.log(data.message);

         loadingMask.show();

         OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.MEDICALTRAVEL + "/SendDemand", {

            data: data,
            sendAsJsonPost: true,
            successCallback: function (response) {

                OGT.Tracking.Compensation.Add({
                    completeCallback: function () {
                        location.href = $form.attr("action");
                    }
                });
            },
            failureCallback: function (response) {
                OGT.Tracking.Compensation.AddFail();
                alert(response.Message || response.statusText || response);
                $sendButton.prop("disabled", false);
            },
            completeCallback: function () {
                loadingMask.hide();

            }
        });

    }

    $form.append(loadingMask.$root);

    $form.on("submit",
        function (event) {
            event.preventDefault();
            trySend();
        });

    $sendButton.click(function (event) {
        if (document.getElementById('divbutton1').style.display == 'block') {
            guardian = true;
        }
        else {
            guardian = false;
        }

        event.preventDefault();
        trySend();


    });

   

    function trySend() {

        // Set button to disabled to prevent multiple sendings
        $sendButton.prop("disabled", true);

        if (validateAllBoxes()) {
            sendJson();
        }
        else {
            $sendButton.prop("disabled", false);
        }
    }






};
;
window.OGT = window.OGT || {};

window.OGT.initGrouptravelPage = function () {

    var maxTravelersOnClassTicket = 30;

    var boxesById = {},
        allBoxesInOrder = [],
        messageArea = OGT.Util.OGT.makeMessageArea(),
        loadingMask = OGT.Util.OGT.getLoadingMask("red");

    (function () {

        function getSimplifiedList(inList) {

            return inList.map(function (jData) {

                var routeLinks = jData.Routelinks.filter(function (rlink) { return rlink.ogtType == "vehicle"; }).map(function (rlink) {
                    var stopIds = rlink.Line.PointsOnRouteLink.map(function (pointOnRouteLink) {
                        return {
                            'stopId': pointOnRouteLink.Id
                        };
                    });
                    return {
                        'DepDateTime': rlink.DepDateTime,
                        'LineNrReal': rlink.Line.LineNrReal,
                        'LineName': rlink.Line.LineName,
                        'RouteLinkGroupType': rlink.Line.Ogt.OgtTrafficTypeGroupKey,
                        'RunNo': rlink.Line.RunNo,
                        'RouteLinkKey': rlink.RouteLinkKey,
                        'StopIds': stopIds
                    };
                });
                return {
                    'RawDateOfJourney': jData.Departure,
                    'JourneyKey': jData.JourneyKey,
                    'Time': jData.Departure,
                    'From': { 'PlaceId': jData.ReturnedStartPlace.Id, 'PlaceName': jData.ReturnedStartPlace.PlaceName },
                    'To': { 'PlaceId': jData.ReturnedEndPlace.Id, 'PlaceName': jData.ReturnedEndPlace.PlaceName },
                    'RouteLinks': routeLinks
                };
            });
        };

        var BaseBox = (function () {

            function baseBox() {
            }

            baseBox.prototype.init = function (rootElementId) {
                this.$root = $("#" + rootElementId);
                this.messageTemplate = this.$root.data("messagetemplate");
                this.setVisible(true);
            };
            baseBox.prototype.setVisible = function (doShow) {
                this.visible = doShow;
                if (doShow) {
                    this.$root.show();
                } else {
                    this.$root.hide();
                }
            };
            baseBox.prototype.boxIsValid = function () {
                if (this.visible) {
                    return this.isValid();
                } else {
                    return true;
                }
            };
            return baseBox;
        }());

        var BaseSubBox = (function () {

            function baseSubBox() {
            }

            OGT.Util.extend(baseSubBox, BaseBox);

            baseSubBox.prototype.init = function (rootElementId) {
                baseSubBox.superclass.init.apply(this, arguments);
            };
            baseSubBox.prototype.isValid = function () {
                if (this.visible) {
                    return this.subBoxIsValid();
                } else {
                    return true;
                }
            };
            return baseSubBox;
        }());

        var NumberOfTravelersBox = (function () {

            function numberOfTravelersBox(rootElementId) {
                this.init.apply(this, arguments);

                var $root = this.$root,
                    that = this;

                this.$classticketSelector = $root.find("input:radio[name='ClassTickets']");
                this.$classticketInfo = $root.find(".text--info.classticket");
                this.$numberOfTravelers = $root.find("#numberoftravelers");
                this.$numberOfWheelchairs = $root.find("#numberofwheelchairs");
                this.$numberOfBabycarriages = $root.find("#numberofbabycarriages");
                this.$numberOfTravelersInfoText = $root.find("#numtravelersinfotext");
                this.$additionalGearInfoText = $root.find("#info--additionalgear");

                this.$classticketSelector.change(function () {
                    that.refreshClassticketSelection();
                    that.refreshNumberOfTravelers();
                });
                $(document).ready(function () {
                    that.refreshClassticketSelection();
                });

                var numChangeTimer;
                this.$numberOfTravelers.change(function () {
                    clearTimeout(numChangeTimer);
                    numChangeTimer = setTimeout(function () {
                        that.refreshNumberOfTravelers();
                    }, 600);
                });

                var showAdditionalGearInfo = function () {
                    that.$additionalGearInfoText.show();
                }

                this.$numberOfWheelchairs.change(showAdditionalGearInfo);
                this.$numberOfBabycarriages.change(showAdditionalGearInfo);
            }

            OGT.Util.extend(numberOfTravelersBox, BaseBox);

            numberOfTravelersBox.prototype.refreshNumberOfTravelers = function () {

                var numTravelers = this.getNumberOfTravelers();
                var numOnClassticket = this.getNumberOfTravelersOnClassTicket();
                var numFullPrice = this.getNumberOfTravelersOnFullPrice();

                var $numberOfTravelersInfoText = this.$numberOfTravelersInfoText;
                $numberOfTravelersInfoText.parent().show();
                $numberOfTravelersInfoText.html(OGT.Util.String.format(this.messageTemplate,
                    numTravelers + "",
                    numOnClassticket + "",
                    numFullPrice + ""));

                if (OGT.Proxy.journey.search.get().ise) {
                    OGT.Proxy.journey.search.set({ doe: { action: "doe" } });
                }
            };

            numberOfTravelersBox.prototype.getNumberOfTravelers = function () {
                return Math.max(0, this.$numberOfTravelers.val());
            };

            numberOfTravelersBox.prototype.getNumberOfTravelersOnClassTicket = function () {
                return this.isClassticket() ? Math.min(this.getNumberOfTravelers(), maxTravelersOnClassTicket) : 0;
            };

            numberOfTravelersBox.prototype.getNumberOfTravelersOnFullPrice = function () {
                return this.getNumberOfTravelers() - this.getNumberOfTravelersOnClassTicket();
            };

            numberOfTravelersBox.prototype.refreshClassticketSelection = function () {
                (this.isClassticket()) ? this.$classticketInfo.show() : this.$classticketInfo.hide();
            };

            numberOfTravelersBox.prototype.getNumberOfTravelersInWheelChair = function () {
                return this.$numberOfWheelchairs.val();
            };

            numberOfTravelersBox.prototype.getNumberOfTravelersWithBabycarriages = function () {
                return this.$numberOfBabycarriages.val();
            };


            numberOfTravelersBox.prototype.isClassticket = function () {
                return this.$classticketSelector.filter(":checked").val() == "yes";
            };

            numberOfTravelersBox.prototype.isValid = function () {
                var numtravelers = this.getNumberOfTravelers();
                if (numtravelers > 0) {
                    removeErrorMessage(this.$numberOfTravelers);
                    return true;
                } else {
                    setErrorMessage(this.$numberOfTravelers, OGT.Language.translate("/Grouptravel/ForJs/NumTravelers/ValidationError"));
                    return false;
                }
            };

            return numberOfTravelersBox;
        }());

        var ContactBox = (function () {

            function contactBox(rootElementId) {
                this.init.apply(this, arguments);

                this.$textboxContactName = this.$root.find("#field--contact-name");
                this.$textboxOrganization = this.$root.find("#field--organization");
                this.$textboxPhoneOrganization = this.$root.find("#field--phone-organization");
                this.$textboxPhoneContact = this.$root.find("#field--phone-contact");
                this.$textboxEmail = this.$root.find("#field--email");

                this.compulsoryFields = [
                    this.$textboxContactName,
                    this.$textboxOrganization,
                    this.$textboxPhoneOrganization,
                    this.$textboxPhoneContact,
                    this.$textboxEmail
                ];
            }

            OGT.Util.extend(contactBox, BaseBox);

            contactBox.prototype.addPersonalDetailsToDataObject = function (data) {
                data.ContactName = this.$textboxContactName.val();
                data.OrganisationName = this.$textboxOrganization.val();
                data.PhoneOrganisation = this.$textboxPhoneOrganization.val();
                data.PhoneContact = this.$textboxPhoneContact.val();
                data.Email = this.$textboxEmail.val();
            };

            contactBox.prototype.isValid = function () {
                return this.compulsoryFields.reduce(function (allOkThisFar, $field) {
                    return hasValue($field) && allOkThisFar;
                }, true);
            };
            return contactBox;
        }());

        var DisclaimerBox = (function () {

            function disclaimerBox(rootElementId) {
                this.init.apply(this, arguments);
                this.$disclaimerCheckBox = this.$root.find("input:checkbox[name='disclaimerCheckbox']");
                this.$disclaimerNoticesCheckBox = this.$root.find("input:checkbox[name='disclaimerNoticesCheckbox']");
                this.$disclaimerPersonalDataCheckBox = this.$root.find("input:checkbox[name='disclaimerPersonalDataCheckbox']");
            }

            OGT.Util.extend(disclaimerBox, BaseBox);

            disclaimerBox.prototype.isValid = function () {
                if (this.$disclaimerCheckBox.prop("checked") && this.$disclaimerNoticesCheckBox.prop("checked") && this.$disclaimerPersonalDataCheckBox.prop("checked")) {
                    removeErrorMessage(this.$disclaimerNoticesCheckBox);
                    return true;
                } else {
                    setErrorMessage(this.$disclaimerNoticesCheckBox, OGT.Language.translate("/Grouptravel/ForJs/Disclaimer/ValidationError"));
                    return false;
                }
            };

            return disclaimerBox;
        }());

        var TravelplanBox = (function () {

            function travelplanBox(rootElementId) {
                this.init.apply(this, arguments);
                var findJourney = this.findJourney = new FindJourneySubBox("grouptravel__find-journey");
                var addToList = this.addToList = new AddToListSubBox("grouptravel__add-to-list");

                this.allSubBoxes = [findJourney, addToList];
            }

            OGT.Util.extend(travelplanBox, BaseBox);

            travelplanBox.prototype.isValid = function () {
                return this.allSubBoxes.reduce(function (allOkThisFar, $box) {
                    return $box.isValid() && allOkThisFar;
                }, true);
            };

            var FindJourneySubBox = (function () {

                //Local times, defaults should fetching fail.
                var groupticketLimits = {};

                OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.GROUP_TRAVEL + "/GetLimits", {
                    successCallback: function (response) {
                        if (response.data)
                            groupticketLimits = response.data;
                    }
                });

                var GROUPTYPES = OGT.CONSTANTS.TRAFFIC_TYPE_GROUPS;
                function inTimeBounds(dateTime, bounds) {
                    
                    var hour = dateTime.getHours(), // Adjust to Swedish
                        minute = dateTime.getMinutes(),
                        min = bounds.min,
                        max = bounds.max,
                        minHour = min.hour,
                        maxHour = max.hour;

                    return (minHour < hour || minHour == hour && min.minute <= minute) && (hour < maxHour || hour == maxHour && minute <= max.minute);
                }

                function timeBoundaryText(boundary) {
                    return (boundary.hour < 10 ? "0" : "") + boundary.hour + ":" + (boundary.minute < 10 ? "0" : "") + boundary.minute;
                }

                function findJourneySubBox() {
                    this.init.apply(this, arguments);

                    this.partialjourneys = [];

                    var $root = this.$root;

                    this.$resultContainer = $root.find(".container--result");

                    this.$textboxFrom = $root.find(".journey__from-input");
                    this.$textboxTo = $root.find(".journey__to-input");

                    var $textboxTime = this.$textboxTime = $root.find("#journey__time"),
                        $textboxDate = this.$textboxDate = $root.find("#journey__date");

                    this.$trafficTypes = $("#traffic-types").find('input[type=checkbox]');
                    this.$changetime = $(".changetime__item[name=changetime]");
                    this.$priority = $(".priority__item[name=priority]");
                    this.$walk = $(".walk__item[name=walk]");

                    var $submitButton = $root.find("#journey__submit");

                    $submitButton.click(function (event) {
                        event.preventDefault();
                        OGT.Proxy.journey.search.set({ doe: { action: 'doe' } });
                    });

                    var messageArea = OGT.Util.OGT.makeMessageArea(),
                        loadingMask = OGT.Util.OGT.getLoadingMask("red");

                    $root.find(".message-target").replaceWith(messageArea.$root);
                    $root.append(loadingMask.$root);

                    var journeyProxy = OGT.Proxy.journey,
                        errorIo = journeyProxy.error;

                    errorIo.listen({
                        newValue: function (inValueMessage) {
                            if (inValueMessage) {
                                messageArea.show(inValueMessage, true);
                            } else {
                                messageArea.hide();
                            }
                        }
                    });

                    ////Set up handlers for focus/blur on from/to boxes
                    var wireUpJourneyInputNs = journeyProxy.Util.WireUpInput;

                    wireUpJourneyInputNs.from(this.$textboxFrom, null, errorIo, "stop");
                    wireUpJourneyInputNs.to(this.$textboxTo, null, errorIo, "stop");

                    // Fix english time format, limit to current and future dates, fix zindex
                    //this.$textboxDate.datepicker({
                    //    dateFormat: "yy-mm-dd",
                    //    minDate: 7,
                    //    beforeShow: function () {
                    //        setTimeout(function () { $(".ui-datepicker").css("z-index", 99999999999999); }, 0);
                    //    }
                    //});

                    wireUpJourneyInputNs.date($textboxDate);
                    OGT.Util.Html.convertInputToDateIfConditions($textboxDate);

                    wireUpJourneyInputNs.time($textboxTime);
                    OGT.Util.Html.convertInputToTimeIfConditions($textboxTime);

                    wireUpJourneyInputNs.trafficTypes(this.$trafficTypes);
                    wireUpJourneyInputNs.changeTime(this.$changetime);
                    wireUpJourneyInputNs.priority(this.$priority);
                    wireUpJourneyInputNs.walkAcceptable(this.$walk);

                    // Add a button to each journey
                    OGT.JourneySearchHandler.SearchResultList.addButtonToEachJourney({
                        langKey: "/searchjourney/selecttravel",
                        clickCallback: function (journeyData) {
                            boxesById.travelplan.addToList.addJourneyToList(journeyData);
                            boxesById.travelplan.addToList.refreshJourneyList();

                            var $groupTravelElem = $('#grouptravel__add-to-list');

                            if ($groupTravelElem.length) {
                                OGT.Util.scrollToElement($groupTravelElem);
                            }
                        }
                    });


                    OGT.JourneySearchHandler.SearchResultList.setPreParseJourneySearchResultFunc(function (journeySearchResult, callback) {

                        var journeys = journeySearchResult.Journeys;

                        var partialJourneys = getSimplifiedList(journeys);

                        loadingMask.show();

                        OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.GROUP_TRAVEL + "/FindConflicts", {
                            data: partialJourneys,
                            sendAsJsonPost: true,
                            successCallback: function(conflictItems) {

                                var isClassticket = boxesById.numberoftravelers.isClassticket(),
                                    day = new Date(OGT.Proxy.journey.search.parent.date.get()).getDay(),
                                    isWeekend = (day === 6) || (day === 0);

                                conflictItems.map(function(conflictItem) {
                                    journeys.filter(function(resultItem) {
                                        return resultItem.JourneyKey == conflictItem.c.JourneyKey;
                                    }).filter(function (resultItem) {

                                        resultItem.hasNonStop = resultItem.Routelinks.some(function (routelink) {
                                            var from = routelink.From,
                                                to = routelink.To;
                                            return from && from.OgtType != 'stop' || to && to.OgtType != 'stop';
                                        });
                                        
                                        resultItem.hasConflict = ((conflictItem.i - boxesById.numberoftravelers.getNumberOfTravelers()) < 0);

                                        resultItem.violatesConstraint = (isClassticket && conflictItem.c.RouteLinks.some(function (rl) {

                                            var depDate = Date.fromSwedishDateTime(rl.DepDateTime),
                                                landViolation = rl.RouteLinkGroupType == GROUPTYPES.GRPLAND && !inTimeBounds(depDate, groupticketLimits.land),
                                                tatoViolation = (rl.RouteLinkGroupType == GROUPTYPES.GRPTÄTO || rl.RouteLinkGroupType == GROUPTYPES.GRPTÅG || rl.RouteLinkGroupType == GROUPTYPES.GRPEXPRESS) && !inTimeBounds(depDate, groupticketLimits.tato);
                                            return landViolation || tatoViolation;

                                        }));

                                        resultItem.classTicketNonAcceptingLine = (isClassticket && conflictItem.c.RouteLinks.some(function (rl) {
                                            return rl.RouteLinkGroupType == GROUPTYPES.GRPTÅG && groupticketLimits.nonAcceptingLineNames.some(function (nonAccept) {
                                                return rl.LineName.replace('.', '').includes(nonAccept) && (resultItem.classTicketNonAcceptingName = nonAccept);
                                            });
                                        }));

                                        resultItem.alreadySelected = (boxesById.travelplan.addToList.selectedpartialjourneys.some(function (selectedItem) {
                                            return selectedItem.JourneyKey == resultItem.JourneyKey;
                                        }));

                                        resultItem.notSameTrafficDayAsSelected = (!boxesById.travelplan.addToList.selectedpartialjourneys.every(function (selectedItem) {
                                            return selectedItem.ogtDepartureDateTime.isSameTrafficDayAs(resultItem.ogtDepartureDateTime);
                                        }));

                                        resultItem.classTicketOnWeekend = (isClassticket && isWeekend);

                                        return resultItem.classTicketOnWeekend || resultItem.hasNonStop || resultItem.hasConflict || resultItem.violatesConstraint || resultItem.classTicketNonAcceptingLine || resultItem.alreadySelected || resultItem.notSameTrafficDayAsSelected;

                                    }).map(function (iseObj) {

                                        // Disable conflicting journey (non desctructive)
                                        iseObj.postProcess$Dom = function ($dom) {
                                            $dom.addClass("disabled");
                                            var tp = $dom.find("span.travel__plan"), message = "";
                                            tp.hide();
                                            if (iseObj.hasNonStop) message = OGT.Language.translate("/Grouptravel/ForJs/FindJourney/HasNonStop");
                                            if (iseObj.violatesConstraint) message = OGT.Util.String.format(OGT.Language.translate("/Grouptravel/ForJs/FindJourney/ViolatesConstraint"), timeBoundaryText(groupticketLimits.land.min), timeBoundaryText(groupticketLimits.land.max));
                                            if (iseObj.classTicketNonAcceptingLine) message = OGT.Util.String.format(OGT.Language.translate("/Grouptravel/ForJs/FindJourney/NonAcceptingLine"), iseObj.classTicketNonAcceptingName);
                                            if (iseObj.hasConflict) message = OGT.Util.String.format(OGT.Language.translate("/Grouptravel/ForJs/FindJourney/JourneyFull"), "" + conflictItem.i);
                                            if (iseObj.alreadySelected) message = OGT.Language.translate("/Grouptravel/ForJs/FindJourney/AlreadySelected");
                                            if (iseObj.notSameTrafficDayAsSelected) message = OGT.Language.translate("/Grouptravel/ForJs/FindJourney/NotSameTrafficDayAsSelected");

                                            if (iseObj.classTicketOnWeekend) message = OGT.Language.translate("/Grouptravel/ForJs/FindJourney/NoClassTicketOnWeekend");

                                            $('<span class="item">' + message + "</span>").insertAfter(tp);

                                            $dom.find("button.travel__selection").prop("disabled", true);
                                        };
                                    });
                                });
                                callback(journeySearchResult);
                                removeErrorMessage(boxesById.travelplan.findJourney.$resultContainer);
                            },
                            failureCallback: function() {
                                callback(journeys);
                            },
                            completeCallback: function () {
                                loadingMask.hide();
                            }
                        });
                    });
                }

                OGT.Util.extend(findJourneySubBox, BaseSubBox);

                findJourneySubBox.prototype.subBoxIsValid = function () {
                    return true;
                };

                findJourneySubBox.prototype.clearAllFields = function () {
                    OGT.Proxy.journey.search.set({ ise: null });
                    removeErrorMessage(boxesById.travelplan.findJourney.$resultContainer);
                };

                findJourneySubBox.prototype.clearAllInputData = function () {
                    OGT.Proxy.journey.resetAllIos({ allIsDoneCallback: function () { } });
                };

                return findJourneySubBox;
            }());


            var AddToListSubBox = (function () {

                function addToListSubBox() {
                    this.init.apply(this, arguments);
                    this.selectedpartialjourneys = [];
                    this.addedJourneysList = this.$root.find("#journey__list--added").get(0);
                    this.noaddedjourneys = this.$root.find("#journey__list--none-added");
                    this.addjourneyButton = this.$root.find("#journey__add");
                    var that = this;
                    this.addjourneyButton.click(function (event) {
                        event.preventDefault();
                        boxesById.travelplan.findJourney.$root.show();
                        that.addjourneyButton.hide();
                    });
                }

                OGT.Util.extend(addToListSubBox, BaseSubBox);

                addToListSubBox.prototype.showOrHideFields = function (activeSearchResult) {
                    (activeSearchResult) ? this.$root.show() : this.$root.hide();
                };

                addToListSubBox.prototype.addJourneyToList = function (journeyData) {
                    if (this.selectedpartialjourneys.length > 0) {
                        if (!this.selectedpartialjourneys.every(function (existingJourney) {
                            return existingJourney.ogtDepartureDateTime.isSameTrafficDayAs(journeyData.ogtDepartureDateTime);
                        })) {
                            setErrorMessage(boxesById.travelplan.findJourney.$resultContainer, OGT.Language.translate("Err, not same traffic day"));
                            return false;
                        }
                    }
                    removeErrorMessage(boxesById.travelplan.findJourney.$resultContainer);
                    this.selectedpartialjourneys.push(journeyData);
                    this.sortTravelPlan();
                    boxesById.travelplan.findJourney.clearAllInputData();
                    boxesById.travelplan.findJourney.clearAllFields();
                    boxesById.travelplan.findJourney.$root.hide();
                    this.addjourneyButton.show();
                    return true;
                };

                addToListSubBox.prototype.refreshJourneyList = function () {
                    if (this.selectedpartialjourneys.length > 0) {
                        this.noaddedjourneys.hide();
                        this.clearList();
                        var that = this;
                        this.selectedpartialjourneys.map(function (journeyData) {
                            OGT.JourneySearchHandler.SearchResultList.renderJourney(journeyData,
                            {
                                targetElement: that.addedJourneysList,
                                useListJourneyHeader: true,
                                extraButtonOnEachJourneyOptions: {
                                    langKey: "/searchjourney/cancel",
                                    clickCallback: function (journeyData) {
                                        that.removeJourney(journeyData.JourneyKey);
                                    }
                                }
                            });
                        });
                    } else {
                        this.clearList();
                        this.noaddedjourneys.show();
                        boxesById.travelplan.findJourney.$root.show();
                        this.addjourneyButton.hide();
                    }
                };
                addToListSubBox.prototype.clearList = function () {
                    $(this.addedJourneysList).empty();
                };
                addToListSubBox.prototype.getSimplifiedList = function () {
                    return getSimplifiedList(this.selectedpartialjourneys);

                };
                addToListSubBox.prototype.removeJourney = function (journeyKey) {
                    var index = this.selectedpartialjourneys.map(function (j) { return j.JourneyKey; }).indexOf(journeyKey);
                    this.selectedpartialjourneys.splice(index, 1);
                    boxesById.travelplan.findJourney.clearAllFields();
                    boxesById.travelplan.findJourney.$root.hide();
                    this.refreshJourneyList();
                };

                addToListSubBox.prototype.sortTravelPlan = function () {
                    this.selectedpartialjourneys.sort(function (a, b) {
                        return a.ogtDepartureDateTime - b.ogtDepartureDateTime;
                    });
                };

                addToListSubBox.prototype.subBoxIsValid = function () {
                    if (this.selectedpartialjourneys.length) {
                        removeErrorMessage($(this.addedJourneysList));
                        return true;
                    } else {
                        setErrorMessage($(this.addedJourneysList), OGT.Language.translate("/Grouptravel/ForJs/AddToList/ValidationError"));
                        return false;
                    }
                };

                return addToListSubBox;

            }());

            return travelplanBox;
        }());

        var NotesBox = (function () {

            function notesBox(rootElementId) {
                this.init.apply(this, arguments);

                var $root = this.$root;

                this.maxCharsInNote = $root.data("maxcharsinnote");
                this.$notesTextArea = $root.find("#textarea--notes");

                this.charsLeft = this.$root.find(".info--chars-left");
                this.charsLeftTemplate = OGT.Language.translate("/Grouptravel/ForJs/Note/MaxCharsInNote");
                this.noCharsLeftTemplate = OGT.Language.translate("/Grouptravel/ForJs/Note/ErrorMaxCharsInNotes");

                var that = this;
                this.$notesTextArea.on("keyup blur", function () {
                    that.isValid();
                });
            }

            OGT.Util.extend(notesBox, BaseBox);

            notesBox.prototype.getNoteText = function () {
                return this.$notesTextArea.val();
            };

            notesBox.prototype.isValid = function () {
                var numCharsLeft = Math.max(0, this.maxCharsInNote - this.getNoteText().length);
                if (numCharsLeft) {
                    removeErrorMessage(this.charsLeft);
                    this.charsLeft.show();
                    this.charsLeft.html(OGT.Util.String.format(this.charsLeftTemplate, "" + numCharsLeft));
                    return true;
                } else {
                    setErrorMessage(this.charsLeft, OGT.Util.String.format(this.noCharsLeftTemplate, "" + this.maxCharsInNote));
                    this.charsLeft.hide();
                    return false;
                }
            };

            return notesBox;
        }());

        var contactBox = new ContactBox("container--contact"),
            numberOfTravelersBox = new NumberOfTravelersBox("container--number-of-travelers"),
            travelplanBox = new TravelplanBox("container--travel-plan"),
            notesBox = new NotesBox("container--notes"),
            disclaimerBox = new DisclaimerBox("container--disclaimer");

        boxesById.contact = contactBox;
        boxesById.numberoftravelers = numberOfTravelersBox;
        boxesById.travelplan = travelplanBox;
        boxesById.notes = notesBox;
        boxesById.disclaimerbox = disclaimerBox;

        allBoxesInOrder.push(contactBox);
        allBoxesInOrder.push(numberOfTravelersBox);
        allBoxesInOrder.push(travelplanBox);
        allBoxesInOrder.push(notesBox);
        allBoxesInOrder.push(disclaimerBox);
    }());

    function getErrorMessageArea($aField) {
        var localMessageArea = $aField.data("messageArea");
        if (!localMessageArea) {
            localMessageArea = OGT.Util.OGT.makeMessageArea();
            $aField.data("messageArea", localMessageArea);
            $aField.parent().append(localMessageArea.$root);
        }
        return localMessageArea;
    }

    function setErrorMessage($aField, aMessage) {
        getErrorMessageArea($aField).show(aMessage, true);
    }

    function removeErrorMessage($aField) {
        getErrorMessageArea($aField).hide();
    }

    function hasValue($aField) {
        var labelName = $aField.parent().parent().find("label[for='" + $aField.attr("id") + "']").html();
        if ($aField.val()) {
            removeErrorMessage($aField);
            return true;
        } else {
            setErrorMessage($aField, labelName + " " + OGT.Language.translate("/Grouptravel/ForJs/Contact/IsObligatory"));
            return false;
        }
    }

    function validateAllBoxes() {
        return allBoxesInOrder.reduce(function (allOkThisFar, aBox) {
            return aBox.boxIsValid() && allOkThisFar;
        }, true);
    }

    function sendJson() {
        var data = {};
        boxesById.contact.addPersonalDetailsToDataObject(data);
        data.NumTravelers = boxesById.numberoftravelers.getNumberOfTravelers();
        data.NumWheelchairs = boxesById.numberoftravelers.getNumberOfTravelersInWheelChair();
        data.NumBabycarriages = boxesById.numberoftravelers.getNumberOfTravelersWithBabycarriages();
        data.HasClassTicket = boxesById.numberoftravelers.isClassticket();
        data.Note = boxesById.notes.getNoteText();
        data.PartialJourneys = boxesById.travelplan.addToList.getSimplifiedList();

        var sendButton = $(".button--send").after(messageArea.$root);

        loadingMask.show();

        OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.GROUP_TRAVEL + "/SendDemand", {
            data: data,
            sendAsJsonPost: true,
            successCallback: function (response) {
                OGT.Tracking.GroupTravel.Add({
                    completeCallback: function () {
                        location.href = $("form.grouptravel__form").attr("action");
                    }
                });
            },
            failureCallback: function (response) {
                OGT.Login.showIfResponseContainsNeededForAccess(response, {
                    loginSuccessfulCallback: function () {
                        sendJson();
                    },
                    abandonCallback: function () {
                        loadingMask.hide();
                        messageArea.show(OGT.Language.translate("/Grouptravel/ForJs/LoginNeededForSubmission"), true);
                        OGT.Util.scrollToElement(messageArea.$root);
                        sendButton.prop("disabled", false);
                    },
                    noUsefulDataDidNotShowLoginDialogCallback: function (noTryResponse) {
                        loadingMask.hide();
                        messageArea.showResponse(noTryResponse, true);
                        OGT.Util.scrollToElement(messageArea.$root);
                        OGT.Tracking.GroupTravel.AddFail();
                        sendButton.prop("disabled", false);
                    }
                });
            }
        });
    }

    $(".grouptravel__form").append(loadingMask.$root);

    $(".button--send").after(messageArea.$root).click(function (event) {
        event.preventDefault();

        // Set button to disabled to prevent multiple sendings
        $(this).prop("disabled", true);

        if (validateAllBoxes()) {
            sendJson();
        } else {
            alert(OGT.Language.translate("/Grouptravel/ForJs/ValidationErrorAlert"));
            $(this).prop("disabled", false);
        }
    });
};;
window.OGT = window.OGT || {};
window.OGT.Util = window.OGT.Util || {};
window.OGT.Util.OGT = window.OGT.Util.OGT || {};

(function () {

    function initSlideshow () {
        // Set variable
        var $slideshow = $('.slideshow');

        var $newsblockslideshow = $('.newsblock-slideshow');

        // If matchMedia is supported
        if (typeof (matchMedia) != 'undefined' && matchMedia) {
            var mq = window.matchMedia("(max-width: 767px)");
            mq.addListener(widthChange);
            widthChange(mq);
        }

        function widthChange(mq) {
            if (mq.matches) {
                // media query match
                $slideshow.cycle('destroy'); // destroy slideshow
                $newsblockslideshow.cycle('destroy');
            } else {
                // media query does not match
                // init slideshow
                $slideshow.cycle({
                    speed: 500, // speed if triggered by automatic sliding
                    manualSpeed: 500, // speed if manually clicked
                    paused: false, // automatic sliding or not
                    timeout: 10000, // time between automatic slidings
                    swipe: true, // allow swipe
                    slides: '.slideshow__item', // element we want to have as slide
                    fx: 'scrollHorz',
                    log: false, // log in console
                    easing: 'linear' // type of easing
                });

                $newsblockslideshow.cycle({
                    speed: 500, // speed if triggered by automatic sliding
                    manualSpeed: 500, // speed if manually clicked
                    paused: true, // automatic sliding or not
                    timeout: 10000, // time between automatic slidings
                    swipe: true, // allow swipe
                    slides: '.slideshow__item', // element we want to have as slide
                    fx: 'scrollHorz',
                    log: false, // log in console
                    easing: 'linear', // type of easing,
                    allowWrap: false,
                    disabledClass: "disabled"
                });
            }

        }
    };

    var ogtUtil = window.OGT.Util.OGT;

    ogtUtil.initSlideshow = initSlideshow;
}());;
window.OGT = window.OGT || {};
window.OGT.Util = window.OGT.Util || {};
window.OGT.Util.OGT = window.OGT.Util.OGT || {};

(function () {

    var selectableElements = [];

    // Initialize the menu and perform setup
    function initMenu() {

        var $visibleNode = $(".menu__item--onsub");

        if ($visibleNode.length > 0) { // If we're on a sub page

            var added = false;

            if ($visibleNode.find("> a").hasClass("menu__container")) {
                // Page is a menu container, hence a valid sub menu component
            } else {
                // Page is not a menu container, set parent as active node
                $visibleNode.parent().addClass("active-chain");
                $visibleNode.parent().addClass("active");
                added = true;
            }

            if ($visibleNode.parent().hasClass("menu__toplevel") && !$visibleNode.hasClass("menu__item--no-children")) {
                // Page is directly beneath start node
                $visibleNode.parent().addClass("active-chain");
                $visibleNode.parent().addClass("active");

            } else if (!added) {
                $visibleNode.addClass("active-chain"); // Add to active chain
                $visibleNode.find("> ul").addClass("active"); // Set children of current to be the active menu list
            }

            $(".menu .active").parent().find("> .item__link").addClass("is-parent"); // Set as parent navigator
            $(".menu .active").parent().find("> .item__link").addClass("menu__item--parent"); // Set as parent link

            $("li.active-chain").addClass("active-chain"); // Add to active chain

            selectableElements = $visibleNode.find("> a");
            selectableElements.push($visibleNode.find("ul > li > a"));

        } else { // If we're on start page
            $(".menu").addClass("active"); // Set root level to active
            selectableElements = $(".menu--mobile").find("> li > a");
        }

        setupMenuClickEvents();

    }


    function setupMenuKeyPressEvents() {
        var KEY_TAB = 9;
        var KEY_ESC = 27;
        var toggleMenuElem = $(".toggle--menu").get(0);
        var searchElem = $("#search__bar--mobile .search-site__input").get(0);

        function handleBackwardTab(e) {

            if (document.activeElement === selectableElements[0]) {
                e.preventDefault();
                toggleMenuElem.focus();
            } else if (document.activeElement === searchElem) {
                e.preventDefault();
                selectableElements[selectableElements.length - 1].focus();
            } else if (document.activeElement === toggleMenuElem) {
                e.preventDefault();
                searchElem.focus();
            }

        }

        function handleForwardTab(e) {

            if (document.activeElement === selectableElements[selectableElements.length - 1]) {
                e.preventDefault();
                searchElem.focus();
            } else if (document.activeElement === searchElem) {
                e.preventDefault();
                toggleMenuElem.focus();
            } else if (document.activeElement === toggleMenuElem) {
                e.preventDefault();
                selectableElements[0].focus();
            }
        }

        $("#mobile--menu--wrapper .menu, .toggle--menu").on("keydown",function (e) {

            switch (e.keyCode) {
                case KEY_TAB:
                    if (selectableElements.length === 1) {
                        e.preventDefault();
                        break;
                    }

                    if (e.shiftKey) {
                        handleBackwardTab(e);
                    } else {
                        handleForwardTab(e);
                    }

                    break;
                case KEY_ESC:
                    $(".toggle--menu").click();
                    $("#mobile--menu--wrapper .menu, .toggle--menu").off("keydown");
                    $(".toggle--menu").focus();
                        break;
                default:
                    break;
            } 
        }); 

    }

    function setupMenuClickEvents() {
        $(".item__link.menu__container").click(function (e) {
            e.preventDefault();
            e.stopPropagation();

            if ($(this).hasClass("menu__item--parent")) {
                goToParent($(this)); // go up to parent item
            } else {
                goToSubItem($(this)); // go down one level
            }
        });

        // Toggle menu
        $(".toggle--menu").click(function (e) {
            
            var $htmlElement = $("html");
            var $mobileMenuWrapper = $("#mobile--menu--wrapper");

            if ($htmlElement.hasClass("menu--open")) { // hide menu
                $htmlElement.removeClass("menu--open"); // remove overflow hidden from html
                $(this).removeClass("open"); // remove class 'active' of menu toggler
                $mobileMenuWrapper.addClass("hidden--toggle"); // hide menu to inactivate unnecessary tabbing
                $("#mobile--menu--wrapper .menu, .toggle--menu").off("keydown");
                $(this).attr("aria-expanded", "false");
                $("#content__main").attr("aria-hidden", "false");
            } else { // show it
                $htmlElement.addClass("menu--open"); // add overflow hidden to html
                $(this).addClass("open"); // add class 'active' to menu toggler
                $mobileMenuWrapper.removeClass("hidden--toggle"); // unhide menu
                $(this).attr("aria-expanded", "true");
                $("#content__main").attr("aria-hidden", "true");
                setupMenuKeyPressEvents();
                
                setTimeout(function () { $("#mobile--menu--wrapper .active a:first").focus(); }, 1000); // focus can only be set to visible elements. 

            }
        });

        // Toggle click event if 'enter' is pressed
        var $menuToggle = $(".toggle--menu");
        $menuToggle.on("keypress", function (event) {
            if (event.keyCode == 13) {
                event.preventDefault();
                $menuToggle.click();
            }
        });
    }



    function goToSubItem(element) {
        $(".is-parent").removeClass("is-parent"); // Remove current parent item from active
        $(".menu__item--parent").removeClass("menu__item--parent"); // Remove current parent link from active
        $(".menu__submenu").removeClass("active"); // Hide current menu list
        //$(".menu__submenu").attr("aria-expanded", "false"); //Menu expaned for screenreaders

        element.parent().addClass("active-chain"); // Add current to chain before going to next
        element.addClass("menu__item--parent"); // Set the current link as the new parent value
        element.addClass("is-parent"); // Set the current item as the new parent value

        element.next(".menu__submenu").addClass("active"); // Set directly descending menu list as active
        element.attr("aria-expanded", "true"); //Menu expaned for screenreaders

        element.next(".menu__submenu").find("a:first").focus();
        element.parent().siblings().addClass("hidden-item"); // Hide siblings
        $(".menu > .menu__item--top").removeClass("active"); // Remove active from top level if it was set

        selectableElements = element.parent().find(":focusable");


    }

    function goToParent(element) {

        element.parent().find("> ul").removeClass("active"); // Remove active status from current menu item
        element.attr("aria-expanded", "false");
        element.closest("li.active-chain").parent().addClass("active"); // Set parent item as current item

        element.closest("li.active-chain").parent().find("a:first").focus();

        element.closest("li.active-chain").removeClass("active-chain"); // Remove current item from active chain

        element.closest(".active-chain").find("> .menu__back").addClass("is-parent");
        element.closest(".active-chain").find("> .item__link").addClass("menu__item--parent");

        element.parent().siblings().removeClass("hidden-item"); // Remove hidden status from parent's siblings
        element.removeClass("menu__item--parent");
        element.removeClass("is-parent");

        if (element.closest("li.active-chain").length > 0) {
            selectableElements = element.closest("li.active-chain").find(":focusable");
        } else {
            selectableElements = element.closest("li").parent().find(":focusable");
        } 
        
    }

    window.OGT.Util.OGT.initMenu = initMenu;

}());;
"use strict";

(function () {

    var totalUnits = 24,
        compactUnits = 3,
        pixelsOnOneUnit = 71.17,
        pixelsGutter = 28.8,
        slideShiftTimeMs = 1000,
        timeOutDelay = 30;

    function unitsToBlockContentHeightPx(units) {
        return pixelsOnOneUnit * units - pixelsGutter;
    }


    var SelectorButton = (function () {

        function selectorButton(parent, id) {
            this.parent = parent;

            var $selectorButton = $("#selector_" + id);

            this.$countDown = $selectorButton.find(".countDown");
            this.$label = $selectorButton.find(".label");
            this.$selectorButton = $selectorButton;
            this.$spinnerButton = $selectorButton.find(".logo");

            // Svg without load
            var $spinner = this.$spinner = $selectorButton.find(".spinner__circle"),
                spinner = this.spinner = $spinner.get(0),
                strokeLength = this.strokeLength = spinner.getTotalLength();

            $spinner
                .css("stroke-dasharray", strokeLength)
                .css("stroke-dashoffset", strokeLength);

            $selectorButton.get(0).addEventListener(clickEvent, function () {

                event.preventDefault();
                if (!parent.isActive()) {
                    event.stopPropagation();
                    OGT.Tracking.InformationBoard.userSelectorClickSwitchSlide();
                    parent.setActive(true);
                } else {
                    OGT.Tracking.InformationBoard.userSelectorClickGlobalTimerReset();
                }
            }, { passive: false });
        }

        selectorButton.prototype.timerReset = function (isPaused, durationMs) {
            var $spinner = this.$spinner,
                that = this;

            $spinner
                .css("transition", "none")
                .css("stroke-dashoffset", 0);

            if (isPaused) {
                return;
            }

            window.setTimeout(function () {
                $spinner
                    .css("transition", "stroke-dashoffset " + (durationMs - timeOutDelay) + "ms linear")
                    .css("stroke-dashoffset", that.strokeLength);
            }, timeOutDelay);

        };

        selectorButton.prototype.timerPause = function (isPause, remainderMs) {

            var $spinner = this.$spinner,
                computedStyle = $spinner.css('stroke-dashoffset'),
                that = this;

            $spinner.css('stroke-dashoffset', computedStyle);

            if (!isPause) {

                setTimeout(function () {
                    $spinner
                        .css("transition", "stroke-dashoffset " + (remainderMs - timeOutDelay) + "ms linear")
                        .css('stroke-dashoffset', that.strokeLength);
                }, timeOutDelay);
            }

        };


        selectorButton.prototype.showTimer = function (doShow) {

            var $selectorButton = this.$selectorButton,
                $spinnerButton = this.$spinnerButton;

            if (doShow) { // Display load
                $selectorButton.addClass("active");
                $spinnerButton.addClass("button--loading");
            } else { // Hide load
                $selectorButton.removeClass("active");
                $spinnerButton.removeClass("button--loading");
            }

        };

        return selectorButton;

    }());




    function blocksFromInitData(initData) {

        return initData.blocks.map(function (blockInitData) {

            var cls;
            switch (blockInitData.type) {
                case "Toggle":
                    cls = ToggleBlock;
                    break;
                case "SlideShow":
                    cls = SlideShowBlock;
                    break;
                case "General":
                    cls = GeneralBlock;
                    break;
                case "Movie":
                    cls = MovieBlock;
                    break;
                case "Map":
                    cls = MapBlock;
                    break;

            }


            return new cls(this, blockInitData);

        }, this);

    }

    var BlockBase = (function () {

        function blockBase() { }

        function parseIntOrNull(v) {
            return v ? parseInt(v) : null;
        }

        blockBase.prototype.init = function (parent, initData) {

            var that = this;

            this.parent = parent;
            this.type = initData.type;

            this.currentMode = blockModes.normal;

            var $root = this.$root = parent.$root.find("#" + initData.id);

            this.viewTime = parseIntOrNull($root.data("overrideviewtime"));
            this.expandedViewTime = parseIntOrNull($root.data("expandedviewtime"));

            var expandedUnits = this.expandedUnits = (totalUnits - (parent.blockCount - 1) * compactUnits);

            $root.addClass("units__mode--expanded__" + expandedUnits);

            var $multiButton = $root.find(".multibutton");

            if ($multiButton.length) {

                var $toggleButton = $multiButton.find(".togglebutton");
                this.$smallSpinner = $multiButton.find(".spinner__circle");

                $root.get(0).addEventListener(clickEvent, function (event) {

                    event.preventDefault();
                    event.stopPropagation();

                    var trackingPoint;
                    if (that.currentMode == blockModes.normal) {
                        trackingPoint = OGT.Tracking.InformationBoard.userContentInteractionBlockExpand;
                    } else {
                        trackingPoint = OGT.Tracking.InformationBoard.userContentInteractionBlockTimerReset;
                    }

                    that.setMode(blockModes.activeExpanded, trackingPoint);
                }, { passive: false });


                $toggleButton.get(0).addEventListener(clickEvent, function (event) {

                    event.preventDefault();
                    event.stopPropagation();

                    if (that.currentMode == blockModes.normal || that.currentMode == blockModes.compact) {
                        that.setMode(blockModes.activeExpanded, OGT.Tracking.InformationBoard.userToggleButtonBlockExpand);
                    } else {
                        that.setMode(blockModes.normal, OGT.Tracking.InformationBoard.userToggleButtonBlockClose);
                    }

                }, { passive: false });

            }

        }

        blockBase.prototype.parentVisible = function () { };

        blockBase.prototype.setVisible = function (doVisible) {
            if (doVisible) {
                this.$root.show();
            } else {
                this.$root.hide();
            }
        };

        blockBase.prototype.getViewTime = function () {
            return this.viewTime;
        };

        blockBase.prototype.setMode = (function () {

            function resetAndActiveTimer() {

                window.clearTimeout(this.timeout);

                var expandedViewTimeMs = this.expandedViewTime * 1000;

                var that = this,
                    $smallerSpinner = this.$smallSpinner;

                documentClickKidnapper = function () {
                    that.setMode(blockModes.normal, OGT.Tracking.InformationBoard.userDocumentClickBlockClose);
                };

                GlobalTimer.pause();

                if ($smallerSpinner) {

                    // Svg without load                
                    var strokeLength = this.strokeLength = $smallerSpinner.get(0).getTotalLength();


                    // Reset animation/svg
                    $smallerSpinner
                        .css("transition", "none")
                        .css("stroke-dashoffset", 0)
                        .css("stroke-dasharray", strokeLength);

                    // start the animation
                    setTimeout(function () {
                        $smallerSpinner
                                    .css("transition", "stroke-dashoffset " + (expandedViewTimeMs - timeOutDelay) + "ms linear")
                                .css("stroke-dashoffset", that.strokeLength);
                    }, timeOutDelay);

                }

                this.timeout = setTimeout(function () {

                    that.setMode(blockModes.normal, OGT.Tracking.InformationBoard.timeoutBlockClose);
                    stopTimer.call(that);


                }, expandedViewTimeMs);

            }

            function stopTimer() {

                if (this.timeout) {

                    window.clearTimeout(this.timeout);
                    this.timeout = null;

                    GlobalTimer.play();

                    documentClickKidnapper = null;

                }

            }


            return function (mode, trackingPoint) {

                if (trackingPoint) {
                    trackingPoint();
                }

                var currentMode = this.currentMode;

                if (mode == currentMode) {

                    if (mode == blockModes.activeExpanded || mode == blockModes.active) {
                        resetAndActiveTimer.call(this);
                    }
                    return;

                }



                if (currentMode == blockModes.activeExpanded || currentMode == blockModes.active) {
                    stopTimer.call(this);
                }

                var $root = this.$root,
                    parent = this.parent;

                $root.removeClass(currentMode);
                this.currentMode = mode;

                $root.addClass(mode);

                switch (mode) {
                    case blockModes.activeExpanded:
                        parent.blocks.forEach(function (otherBlock) {
                            if (otherBlock.type === "Toggle") {
                                return;
                            }
                            if (this !== otherBlock) {
                                otherBlock.setMode(blockModes.compact);
                            }
                        }, this);
                        break;
                    case blockModes.active:
                    case blockModes.normal:
                        parent.blocks.forEach(function (otherBlock) {
                            if (this !== otherBlock) {
                                otherBlock.setMode(blockModes.normal);
                            }
                        }, this);
                        break;
                }

                if (mode == blockModes.activeExpanded || mode == blockModes.active) {
                    resetAndActiveTimer.call(this);
                }
            }
        }());

        return blockBase;

    }());

    var MapBlock = (function () {

        function b(parent, initData) {
            this.init.apply(this, arguments);

            var that = this,
                expandedHeightPx = unitsToBlockContentHeightPx(this.expandedUnits),
                $mapTarget = this.$mapTarget = this.$root.find("#map__target--portion");

            $mapTarget
                .css({
                    height: expandedHeightPx,
                    top: "calc(50% - " + expandedHeightPx / 2 + "px)"
                })
                .on(clickEvent, function (event) {
                    event.stopPropagation();
                })
                .on("touchstart", function (event) {
                    event.stopPropagation();
                    that.setMode(blockModes.active, OGT.Tracking.InformationBoard.userContentInteractionBlockActive);
                });

            function visibilityListener(inValueOptions, options) {

                if (inValueOptions.ise === true) {
                    OGT.Proxy.map.visible.stopListening(visibilityListener);

                    OGT.MapAdapter.listenForAnyInteraction(function () {
                        that.setMode(blockModes.active, OGT.Tracking.InformationBoard.userContentInteractionBlockActive);
                    });

                    return options.allIsDoneCallback;
                }
                return null;
            };

            OGT.Proxy.map.visible.listen({
                newValue: visibilityListener
            });

        }
        OGT.Util.extend(b, BlockBase);

        b.prototype.setMode = function (mode) {

            var currentMode = this.currentMode;
            if (mode != currentMode && currentMode == blockModes.activeExpanded) {
                OGT.Proxy.resetToInitState();
            }

            b.superclass.setMode.apply(this, arguments);
        };

        b.prototype.parentVisible = function (isActive) {

            this.$mapTarget.css("visibility", isActive ? "visible" : "hidden");

            if (isActive) {
                OGT.Proxy.map.visible.set({ doe: true });
            } else {
                OGT.Proxy.resetToInitState();
            }
        };

        return b;

    }());

    var MovieBlock = (function () {

        var ytLoaded = false,
            ytLoadCallbacks = [];

        function loadYouTubeApi(callback) {

            if (ytLoaded) {
                callback();
                return;
            }

            ytLoadCallbacks.push(callback);

            if (1 < ytLoadCallbacks.length) {
                return;
            }

            var tag = document.createElement('script');

            tag.src = "https://www.youtube.com/iframe_api";
            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

        }

        window.onYouTubeIframeAPIReady = function () {
            ytLoaded = true;
            ytLoadCallbacks.forEach(function (callback) {
                callback();
            });
        };

        function b(parent, initData) {
            this.init.apply(this, arguments);

            var that = this;

            loadYouTubeApi(function () {

                var player = that.player = new YT.Player(that.$root.find(".target").get(0), {
                    playerVars: {
                        showinfo: 0,
                        controls: 0,
                        modestbranding: 1,
                        rel: 0
                    },
                    height: '100%',
                    width: '100%',
                    videoId: initData.additional.movieId,
                    events: {
                        'onReady': function (e) {
                            player.setLoop(true);
                            e.target.mute();
                            that.refreshPlaying();
                        },
                        'onStateChange': function (e) {
                            // on done, replay video
                            if (e.data == YT.PlayerState.ENDED) {
                                player.seekTo(0);
                            }
                        }
                    }
                });

            });

        }
        OGT.Util.extend(b, BlockBase);

        b.prototype.refreshPlaying = function () {
            var player = this.player;
            if (player) {

                var compact = this.currentMode === blockModes.compact;

                if (this.parentIsVisible && !compact) {
                    if (player.playVideo) {
                        player.playVideo();
                    }
                } else {
                    if (player.pauseVideo) {
                        player.pauseVideo();
                        if (!compact) {
                            player.seekTo(0);
                        }
                    }

                }
            }
        };

        b.prototype.setMode = function () {
            b.superclass.setMode.apply(this, arguments);
            this.refreshPlaying();
        };

        b.prototype.parentVisible = function (isActive) {
            this.parentIsVisible = isActive;
            this.refreshPlaying();
        };

        b.prototype.getViewTime = function () {

            var player = this.player;

            if (player && player.getDuration) {

                var duration = player.getDuration();

                if (duration) {
                    return duration;
                }

            }

            return b.superclass.getViewTime.apply(this, arguments);

        };

        return b;

    }());

    var ToggleBlock = (function () {

        function b(parent, initData) {
            this.init.apply(this, arguments);

            this.blocks = blocksFromInitData.call(parent, initData.additional);

            this.blockStepIndex = -1;
            this.step();

        }
        OGT.Util.extend(b, BlockBase);

        b.prototype.getCurrent = function () {
            return this.blocks[this.blockStepIndex];
        };

        b.prototype.parentVisible = function (isActive) {
            if (!isActive) {
                this.step();
            }
            this.blocks.forEach(function (block) {
                block.parentVisible(isActive);
            });
        };

        b.prototype.step = function () {
            var blockStepIndex = this.blockStepIndex,
                blocks = this.blocks;
            blockStepIndex = this.blockStepIndex = (blockStepIndex + 1) % blocks.length;
            blocks.forEach(function (block, index) {
                var selected = blockStepIndex === index;
                if (selected) {
                    block.$root.removeClass("toggle-block--active");
                } else {
                    block.$root.addClass("toggle-block--active");
                }
                block.setVisible(selected);
            }, this);

        };

        b.prototype.getViewTime = function () {
            return this.getCurrent().getViewTime();
        };

        b.prototype.setMode = function (mode, trackingPoint) {
            this.getCurrent().setMode(mode, trackingPoint);
        };

        return b;

    }());

    var GeneralBlock = (function () {

        function b(parent, initData) {
            this.init.apply(this, arguments);
        }
        OGT.Util.extend(b, BlockBase);


        return b;

    }());

    var SlideShowBlock = (function () {

        function b(parent, initData) {
            this.init.apply(this, arguments);

            var that = this;

            var $slider = this.$slider = this.$root.find(".informationboard__slideshow"),
                additional = initData.additional,
                imageCount = this.imageCount = additional.imageCount,
                imageViewTimeSeconds = this.imageViewTimeSeconds = additional.imageViewTimeSeconds;

            this.$slides = $slider.find(".informationboard__slideshow__item");

            if (imageCount === 0) {
                return;
            }

            var oneRoundTime = imageCount * imageViewTimeSeconds,
                defaultViewTime = this.parent.defaultViewTime;

            this.viewTime = oneRoundTime * Math.round(defaultViewTime / oneRoundTime);

            if (imageCount === 2) {
                var $clones = this.$slides.clone();
                $slider.append($clones);
                this.$slides = $slider.find(".informationboard__slideshow__item");
                this.imageCount = this.$slides.length;
            }

            if (imageCount === 1) {
                that.stop();
            }

            this.$slides.each(function (index) {

                var $slide = $(this);

                if (index == 0) {
                    $slide.addClass("slideshow__item--active");
                    that.$activeSlide = $slide;
                }
                if (index == 1) {
                    $slide.addClass("slideshow__item--next");
                }
                if (index == that.$slides.length - 1) {
                    $slide.addClass("slideshow__item--prev");
                }
            });

            this._setUpSwipeEvents();

        }
        OGT.Util.extend(b, BlockBase);

        b.prototype.getViewTime = function () {
            return this.viewTime;
        };

        b.prototype.play = function () {

            var that = this;
            this.counter = typeof (this.counter) == 'undefined' ? 0 : this.counter;
            this.ticker = window.setInterval(function () { that.tick(true); }, this.imageViewTimeSeconds * 1000);

        };

        b.prototype.stop = function () {

            window.clearInterval(this.ticker);

        };

        b.prototype.parentVisible = function (isActive) {

            if (isActive) {
                this.play();
            } else {
                this.stop();
            }

        };

        b.prototype.tick = function (forward) {

            var imageCount = this.imageCount,
                counter = this.counter,
                $slider = this.$slider,
                $slides = this.$slides,
                that = this;

            var $nextSlide = this.$activeSlide.next(),
                $prevSlide = this.$activeSlide.prev();

            if (typeof (forward) != "undefined" && !forward) { // swipe right

                $nextSlide = this.$activeSlide;

                if (counter <= 0) {
                    // if first, go to last
                    this.$activeSlide = $slides.last();

                    $prevSlide = this.$activeSlide.prev();

                    counter = imageCount - 1;
                } else {
                    this.$activeSlide = $prevSlide;

                    // if first slide, set last as next
                    if (counter - 1 <= 0) {
                        $prevSlide = $slides.last();
                    } else {
                        $prevSlide = this.$activeSlide.prev();
                    }

                    counter--;
                }

                $nextSlide.addClass("higher-opacity");
                this.$activeSlide.addClass("higher-opacity");

            } else if (forward) { // swipe left
                $prevSlide = this.$activeSlide;

                // No more pictures, start over
                if (counter >= imageCount - 1) { // 
                    this.$activeSlide = $slides.first();
                    $nextSlide = this.$activeSlide.next();

                    counter = 0;
                } else {
                    this.$activeSlide = $nextSlide;

                    // if last slide, set first as next
                    if (counter + 1 >= imageCount - 1) {
                        $nextSlide = $slides.first();
                    } else {
                        $nextSlide = this.$activeSlide.next();
                    }

                    counter++;
                }

                $prevSlide.addClass("higher-opacity");
                this.$activeSlide.addClass("higher-opacity");
            }

            setTimeout(function () {
                $(".higher-opacity").removeClass("higher-opacity");
            }, 500);

            this.counter = counter;

            $slider.find(".slideshow__item--active").removeClass("slideshow__item--active");
            that.$activeSlide.addClass("slideshow__item--active");
            $slider.find(".slideshow__item--next").removeClass("slideshow__item--next");
            $slider.find(".slideshow__item--prev").removeClass("slideshow__item--prev");
            $nextSlide.addClass("slideshow__item--next");
            $prevSlide.addClass("slideshow__item--prev");
        }


        b.prototype._setUpSwipeEvents = function () {
            var that = this,
               swipeDistance,
               touchStartPoint = 0,
               touchEndPoint = 0,
               swipeThreshold = 200; // px

            that.$slider.get(0).addEventListener('touchstart', function (event) {
                that.stop(); // pause interval
                event.stopPropagation();
                event.preventDefault();
                var e = event.originalEvent;

                if (typeof (e) == 'undefined') {
                    e = event;
                }

                touchStartPoint = e.changedTouches[0].pageX;
            }, { passive: false });

            that.$slider.bind('touchmove', function (event) {
                event.stopPropagation();
                event.preventDefault();
                var e = event.originalEvent;

                if (typeof (e) == 'undefined') {
                    e = event;
                }

                var currentPosition = e.changedTouches[0].pageX;

                var difference = currentPosition - touchStartPoint;

                $(this).css("left", 0 + difference + "px");
            });

            that.$slider.bind('touchend', function (event) {
                $(this).css("left", 0);

                event.stopPropagation();
                event.preventDefault();
                var e = event.originalEvent;

                if (typeof (e) == 'undefined') {
                    e = event;
                }

                touchEndPoint = e.changedTouches[0].pageX;

                swipeDistance = touchEndPoint - touchStartPoint;

                if (swipeDistance > swipeThreshold) { // swiped right
                    that.tick(false);
                } else if (swipeDistance < -(swipeThreshold)) { // swiped left
                    that.tick(true);
                }

                that.play();
            });
        }

        return b;

    }());

    var Slide = (function () {

        var activeCssClass = "active",
            changeInProgress = false;

        function slide(initData, index) {

            var id = initData.id;

            this.$root = $("#" + id);
            this.selectorButton = new SelectorButton(this, id);

            this.id = initData.id;
            this.index = index;
            this.defaultViewTime = initData.defaultViewTime;

            this.blockCount = initData.blocks.length;

            this.blocks = blocksFromInitData.call(this, initData);

        }

        slide.prototype.getViewTime = function () {

            var blockViewOverRideTime = this.blocks.reduce(function (acc, block) {
                var val = block.getViewTime();
                if (val && (!acc || acc < val)) {
                    return val;
                }
                return acc;
            }, null);

            if (blockViewOverRideTime) {
                return blockViewOverRideTime;
            }

            return this.defaultViewTime;

        };

        slide.prototype.isActive = function () {

            return activeSlide === this;

        };


        slide.prototype.setActive = function (doActive) {

            var that = this;

            if (doActive) {

                if (changeInProgress) {
                    return;
                }

                window.clearTimeout(this.animationDelayTimeout);

                if (this.isActive()) {

                    GlobalTimer.reset();

                } else {

                    changeInProgress = true;

                    activeSlide = this;

                    slidesInOrder.forEach(function (slide) {

                        if (that !== slide) {
                            slide.setActive(false);
                        }

                    });

                    this.animationDelayTimeout = window.setTimeout(function () {

                        GlobalTimer.set(that.getViewTime());
                        GlobalTimer.play();

                        that.selectorButton.showTimer(true);

                        that.blocks.forEach(function (block) {
                            block.parentVisible(true);
                        });

                        changeInProgress = false;


                    }, slideShiftTimeMs);

                    this.blocks.forEach(function (block) {
                        block.setMode(blockModes.normal);
                    });

                    this.$root.addClass(activeCssClass);

                    $mainSlider.css("top", -(this.index * 100) + "%");

                }

            } else {

                this.$root.removeClass(activeCssClass);
                this.selectorButton.showTimer(false);

                that.blocks.forEach(function (block) {
                    block.parentVisible(false);
                });

                //this.animationDelayTimeout = window.setTimeout(function () {
                //}, slideShiftTimeMs);

            }

        };

        slide.prototype.timerReset = function () {
            var selectorButton = this.selectorButton;
            selectorButton.timerReset.apply(selectorButton, arguments);
        };
        slide.prototype.timerPause = function () {
            var selectorButton = this.selectorButton;
            selectorButton.timerPause.apply(selectorButton, arguments);
        };

        return slide;

    }());


    var GlobalTimer = (function () {

        var timer,
            timesUpTimeStamp,
            countingDownFromSeconds,
            pausedWithMillisecondsLeft = 0,

            debouceTimer;

        function set(seconds) {
            countingDownFromSeconds = seconds;
            reset();
        }

        function reset() {

            var wasPaused = isPaused();

            window.clearTimeout(timer);

            pausedWithMillisecondsLeft = countingDownFromSeconds * 1000;

            if (!wasPaused) {
                play();
            }

            if (activeSlide) {
                activeSlide.timerReset(wasPaused, pausedWithMillisecondsLeft);
            }

        }

        function play() {

            window.clearTimeout(debouceTimer);

            debouceTimer = window.setTimeout(function () {

                if (isPaused()) {

                    timesUpTimeStamp = new Date().addMilliseconds(pausedWithMillisecondsLeft);

                    var leftMilliseconds = pausedWithMillisecondsLeft;

                    timer = window.setTimeout(function () {

                        pause();
                        activeSlide.next.setActive(true);

                    }, pausedWithMillisecondsLeft);

                    pausedWithMillisecondsLeft = null;

                    notify(leftMilliseconds);

                }

            }, timeOutDelay);

        }

        function pause() {

            window.clearTimeout(debouceTimer);

            debouceTimer = window.setTimeout(function () {

                if (isPaused()) {
                    return;
                }

                window.clearTimeout(timer);

                pausedWithMillisecondsLeft = timesUpTimeStamp.getTime() - new Date().getTime();

                notify(pausedWithMillisecondsLeft);

            }, timeOutDelay);

        }

        function notify(remainingMilliseconds) {
            if (activeSlide) {
                activeSlide.timerPause(isPaused(), remainingMilliseconds);
            }
        }

        function isPaused() {
            return pausedWithMillisecondsLeft !== null;
        }

        return {
            set: set,
            reset: reset,
            play: play,
            pause: pause
        };


    }());


    var blockModes = {
        compact: "compact",
        normal: "normal",
        active: "active",
        activeExpanded: "expanded"
    };

    var clickEvent,
        documentClickKidnapper = null,
        $mainSlider,
        currentVersion,
        urlSegment,
        slidesInOrder = [],
        activeSlide;


    function initDateObject($elemDate, $elemTime) {

        var now;

        function refreshClock() {
            
            now = Date.adjusted();

            $elemDate.html(now.getFancyFormattedDate(false));
            $elemTime.html(now.getFormattedTime());

        }

        refreshClock();

        window.setTimeout(function () {

            refreshClock();

            window.setInterval(refreshClock, 60 * 1000);

        }, 60000 - (now - now.stripSeconds()));

    }

    function init($root, initData) {

        var touchModeCookieKey = OGT.CONSTANTS.COOKIE.KEYS.INFORMATION_BOARD_TOUCH_MODE,
            isTouchMode = OGT.Util.QueryString.getParameter(location.search, OGT.CONSTANTS.QUERY_STRING.INFORMATION_BOARD_TOUCH_MODE);

        if (isTouchMode) {
            OGT.Util.Cookie.set(touchModeCookieKey, isTouchMode, 365);
        } else {
            isTouchMode = OGT.Util.Cookie.get(touchModeCookieKey);
        }

        clickEvent = isTouchMode ? "touchend" : "click";

        $mainSlider = $root.find(".slidemask");

        currentVersion = initData.version;
        urlSegment = initData.urlSegment;

        initData.slides.forEach(function (slideInitData, index) {

            var slide = new Slide(slideInitData, index);
            slidesInOrder.push(slide);

        });

        slidesInOrder.forEach(function (slide, index) {
            slide.next = slidesInOrder[(index + 1) % slidesInOrder.length];
        });

        slidesInOrder[0].setActive(true);

        document.addEventListener(clickEvent, function (event) {
            event.preventDefault();
            event.stopImmediatePropagation();

            if (documentClickKidnapper) {
                documentClickKidnapper();
                return;
            }

            OGT.Tracking.InformationBoard.userDocumentClickGlobalTimerReset();

            activeSlide.setActive(true);

        }, { passive: false });

        if (isTouchMode) {
            document.addEventListener('contextmenu', function (event) { event.preventDefault() });
        }

        var versionRequest,
            clientIdCookieKey = OGT.Util.String.format(OGT.CONSTANTS.COOKIE.KEYS.INFORMATION_BOARD_CLIENT_ID, urlSegment),
            clientId = OGT.Util.QueryString.getParameter(location.search, OGT.CONSTANTS.QUERY_STRING.INFORMATION_BOARD_CLIENT_ID);

        if (clientId) {
            OGT.Util.Cookie.set(clientIdCookieKey, clientId, 365);
        } else {
            clientId = OGT.Util.Cookie.get(clientIdCookieKey);
        }

        function ping() {

            if (versionRequest) {
                return;
            }

            versionRequest = OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.PHYSICAL_STOP + "/Ping", {
                data: {
                    urlSegment: urlSegment,
                    clientId: clientId,
                    clientVersion: currentVersion
                },
                successCallback: function () {
                    // All is fine, do nothing...
                },
                failureCallback: function (response) {
                    var serverVersion = response.serverVersion;
                    if (typeof serverVersion != "undefined" && serverVersion != null && typeof serverVersion == "number" && currentVersion != serverVersion) {
                        window.location.reload();
                    }
                },
                completeCallback: function (response) {
                    versionRequest = null;
                    Date.updateLoadTime(response.serverLoadTime);
                }
            });

        }

        window.setInterval(ping, 60000);
        ping();

    };

    window.OGT.InformationBoard = {
        init: init,
        initDateObject: initDateObject
    };


}());;
/**
* All tillgänglig kod på denna webbplats är © Copyright AB Östgötatrafiken
*/

if (typeof OGT != "undefined" && OGT.Proxy) {
    OGT.Proxy._init();
}

$(document).ready(function () {

    // For CSS
    OGT.Util.OGT.setUpIsTouchDeviceFlag();

    // Init event triggers
    OGT.Util.OGT.setUpClickEvents();

    // Init DOM manipulation methods
    OGT.Util.OGT.domManipulation();

    // Init slideshow
    OGT.Util.OGT.initSlideshow();

    // Init image resizing fallback
    //OGT.Util.OGT.imageSizingFallbackIe();

    // Init mobile menu
    OGT.Util.OGT.initMenu();

    // Check for cookies
    OGT.Util.OGT.validateCookie(window.alertMessageId);

    // Apply negative tabindex to hidden elements
    OGT.Util.OGT.applyTabIndexToHiddenElements();

    // Apply aria hidden to hidden elements
    OGT.Util.OGT.applyAriaHiddenToHiddenElements();

    // Init focus function
    OGT.Util.OGT.setUpFocus();

    // Init skip link
    OGT.Util.OGT.setupSkipLink();
   
});
;
(function () {

    var $messageTargets = $(".journey__message__target"), //Error message container
        messageAreas = [],
        journeyProxy = OGT.Proxy.journey;

    if (!journeyProxy) {
        return;
    }

    $messageTargets.each(function () {

        var messageArea = OGT.Util.OGT.makeMessageArea();
        $(this).replaceWith(messageArea.$root);
        messageAreas.push(messageArea);

    });


    journeyProxy.error.listen({
        newValue: function (inValueMessage) {
            messageAreas.forEach(function (messageArea) {
                if (inValueMessage) {
                    messageArea.show(inValueMessage, true);
                } else {
                    messageArea.hide();
                }
            });
        }
    });

}());;
(function () {

    var proxyNs = OGT.Proxy,
        journeyProxy = proxyNs.journey,

        $journeySearchResultMasterContainers = $('.journey__search-result-master-container'),
        $journeySearchResultContainers = $journeySearchResultMasterContainers.find(".journey__search__result-container"),

        $journeyCurrentlySearchedFindNewButtons = $(".journey__find-new"),

        $pagingEarlierButtons = $journeySearchResultContainers.find(".paging__earlier"),
        $pagingLaterButtons = $journeySearchResultContainers.find(".paging__later"),
        $printAllButtons = $journeySearchResultContainers.find('.action__print-all');


    $journeyCurrentlySearchedFindNewButtons.click(function () {
        journeyProxy.search.set({ ise: null });
    });


    var showEarlierCount = 0;
    $pagingEarlierButtons.click(function (event) {
        event.preventDefault();
        event.stopPropagation();

        OGT.Tracking.JourneySearchEngine.MoreEarlier({ value: showEarlierCount++ });

        OGT.Proxy.journey.search.set({
            doe: {
                action: "doe",
                pushState: true,
                doPaging: "before"
            }
        });
    });


    var showLaterCount = 0;
    $pagingLaterButtons.click(function (event) {
        event.preventDefault();
        event.stopPropagation();

        OGT.Tracking.JourneySearchEngine.MoreLater({ value: showLaterCount++ });

        OGT.Proxy.journey.search.set({
            doe: {
                action: "doe",
                pushState: true,
                doPaging: "after"
            }
        });
    });


    $printAllButtons.click(function (event) {
        event.preventDefault();
        event.stopPropagation();

        OGT.Tracking.JourneySearchEngine.Print.FullResult();
        window.print();

    });

}());;
(function () {

    var proxyNs = OGT.Proxy,
        journeyProxy = proxyNs.journey,

        hasJourneySearchResultCssClass = "has-journey-search-result",

        $journeySearchFormMasterContainers = $('.search__journey-form-master-container'),

        $journeySearchResultMasterContainers = $('.journey__search-result-master-container'),
        $journeySearchResultContainers = $journeySearchResultMasterContainers.find(".journey__search__result-container"),

        $journeySearchFormContainers = $('.journey-search-form-container'),

        $journeyCurrentlySearchedContainers = $journeySearchFormMasterContainers.find(".journey__currently-searched"),
        $journeyCurrentlySearchedFroms = $journeyCurrentlySearchedContainers.find(".from"),
        $journeyCurrentlySearchedTos = $journeyCurrentlySearchedContainers.find(".to"),
        $journeyCurrentlySearchedDates = $journeyCurrentlySearchedContainers.find(".date"),

        $searchResultTopBars = $journeySearchResultContainers.find(".heading__fromto"),

        $stopTripFroms = $searchResultTopBars.find(".stop--trip-from"),
        $stopTripTos = $searchResultTopBars.find(".stop--trip-to"),
        $stopTripFromTexts = $stopTripFroms.find(".stop--trip-from-text"),
        $stopTripToTexts = $stopTripTos.find(".stop--trip-to-text"),
        $stopTripFromIcons = $stopTripFroms.find("i"),
        $stopTripToIcons = $stopTripTos.find("i");

    if (journeyProxy) {

        journeyProxy.search.listen({
            newValue: function (inValueSearch) {

                var ise = inValueSearch.ise;

                if (ise) {

                    var start = ise.RequestedStartPlace,
                        end = ise.RequestedEndPlace,
                        date = ise.Date;

                    if (start) {

                        var iconStart = OGT.Util.OGT.getIconCssClassForOgtPlaceType(start.OgtType);

                        $stopTripFroms.off("click");
                        if (start.OgtType == "stop") {
                            $stopTripFroms.click(function (event) {
                                event.stopPropagation();
                            });
                        } else {
                            $stopTripFroms.removeClass("clickable");
                        }

                        if (OGT.Util.OGT.placeTranslatable(start)) {
                            $stopTripFromTexts.removeClass("notranslate");
                        } else {
                            $stopTripFromTexts.addClass("notranslate");
                        }

                        $stopTripFromTexts.text(start.PlaceName);

                        $stopTripFromIcons.removeClass(function () { return this.className; }).addClass(iconStart);

                        $journeyCurrentlySearchedFroms.html(start.PlaceName);

                    }

                    if (end) {

                        var iconEnd = OGT.Util.OGT.getIconCssClassForOgtPlaceType(end.OgtType);

                        $stopTripTos.off("click");
                        if (end.OgtType == "stop") {
                            $stopTripTos.click(function (event) {
                                event.stopPropagation();
                            });
                        } else {
                            $stopTripTos.removeClass("clickable");
                        }

                        if (OGT.Util.OGT.placeTranslatable(end)) {
                            $stopTripToTexts.removeClass("notranslate");
                        } else {
                            $stopTripToTexts.addClass("notranslate");
                        }

                        $stopTripToTexts.text(end.PlaceName);

                        $stopTripToIcons.removeClass(function () { return this.className; }).addClass(iconEnd);

                        $journeyCurrentlySearchedTos.html(end.PlaceName);

                    }

                    if (date) {

                        $journeyCurrentlySearchedDates.html(date);

                    }

                    $journeySearchFormContainers.addClass(hasJourneySearchResultCssClass);
                    $journeyCurrentlySearchedContainers.addClass(hasJourneySearchResultCssClass);
                    $journeySearchResultContainers.addClass(hasJourneySearchResultCssClass);

                } else {

                    $journeySearchFormContainers.removeClass(hasJourneySearchResultCssClass);
                    $journeyCurrentlySearchedContainers.removeClass(hasJourneySearchResultCssClass);
                    $journeySearchResultContainers.removeClass(hasJourneySearchResultCssClass);

                }

            }

        });

    }

}());
;
(function () {

    var $journeySearchFormMasterContainers = $('.search__journey-form-master-container');


    var connectJourneySearchForm = (function () {

        return function (options) {

            var proxyNs = OGT.Proxy,
                journeyProxy = proxyNs.journey;

            if (!journeyProxy) {
                return;
            }

            var mapProxy = proxyNs.map,
                startIo = journeyProxy.start,
                endIo = journeyProxy.end,
                errorIo = journeyProxy.error,

                wireUpJourneyInputNs = journeyProxy.Util.WireUpInput,

                $journeySearchFormContainer = options.$journeySearchFormContainer,
                $journeySearchFormOptionsArea = options.$journeySearchFormOptionsArea,
                $journeySearchFormButtonArea = options.$journeySearchFormButtonArea,
                $journeyAdvancedOptionsContainer = options.$journeyAdvancedOptionsContainer,

                $journeyFromAndToContainer = $journeySearchFormContainer.find('.journey__from-and-to-container'),

                $journeyFromContainer = $journeyFromAndToContainer.find('.journey__from-container'),
                $textboxFrom = $journeyFromContainer.find(".journey__from-input"), //Travel from textbox
                $textboxFromClear = $journeyFromContainer.find(".field__clear"), //Clear from textbox
                $textboxFromHidden = $journeyFromContainer.find(".inputSearchlocal"), //Travel from textbox


                $journeyToContainer = $journeyFromAndToContainer.find('.journey__to-container'),
                $textboxTo = $journeyToContainer.find(".journey__to-input"), //Travel to textbox
                $textboxToClear = $journeyToContainer.find(".field__clear"), //Clear to textbox

                $textboxTimeContainer = $journeySearchFormContainer.find(".journey__time"),
                $textboxTime = $textboxTimeContainer.find("input"), //Travel time selection textbox startpage-time-input
                $textboxTimeEmptyPlaceHolder = $textboxTimeContainer.find(".empty-placeholder"),
                $textboxTimeClear = $textboxTimeContainer.find(".field__clear"), //Clear from textbox

                $textboxDateContainer = $journeySearchFormContainer.find(".journey__date"),
                $textboxDate = $textboxDateContainer.find("input"), //Travel date selection
                $textboxDateEmptyPlaceHolder = $textboxDateContainer.find(".empty-placeholder"),
                $textboxDateClear = $textboxDateContainer.find(".field__clear"), //Clear from textbox

                $sokresaDdl = $journeySearchFormContainer.find(".journey__direction select"), //Travel direction selection
                $swapButton = $journeySearchFormContainer.find(".swap button"),
                $swapUpdateElement = $swapButton.siblings(".changed-text"),
                swapBtnText = $swapUpdateElement.attr("data-updated"),


                $trafficTypesContainer = $journeySearchFormContainer.find('.advanced__traffic-types'),
                $trafficTypes = $trafficTypesContainer.find('input'),
                $changetime = $journeySearchFormContainer.find(".advanced__item--changetime input"),
                $priority = $journeySearchFormContainer.find(".advanced__item--priority input"),
                $walk = $journeySearchFormContainer.find(".advanced__item--walk input"),
                $submit = $journeySearchFormContainer.find(".journey__submit"),

                $textlocaltracffc = $journeySearchFormContainer.find(".textlocaltraffic"),

                $heroImageContainer = $('.hero-image'),
                $heroImage = $heroImageContainer.find(".image__fallback-image"),

                $journeyAdvancedButton = $journeySearchFormContainer.find(".journey__advanced"),
                $startPageResult = $('.startpage-result'),

                //  $inputLocaltraffic = $inputLocaltraffic.find(".journey__from-input_localtraffic"),


                $openMapButton = $journeySearchFormContainer.find('.action--open-map');


            wireUpJourneyInputNs.from($textboxFrom, $textboxFromClear, errorIo);
            wireUpJourneyInputNs.to($textboxTo, $textboxToClear, errorIo);

            wireUpJourneyInputNs.date($textboxDate);
            //OGT.Util.Html.convertInputToDateIfConditions($textboxDate);

            wireUpJourneyInputNs.time($textboxTime, $textboxTimeClear, $textboxTimeEmptyPlaceHolder);
            OGT.Util.Html.convertInputToTimeIfConditions($textboxTime);

            wireUpJourneyInputNs.direction($sokresaDdl);
            wireUpJourneyInputNs.trafficTypes($trafficTypes, $trafficTypesContainer.data("idprefix"));
            wireUpJourneyInputNs.changeTime($changetime);
            wireUpJourneyInputNs.priority($priority);
            wireUpJourneyInputNs.walkAcceptable($walk);

            $textboxDate.click(function () {
                //OGT.Tracking.JourneySearchEngine.DateChanged();
            });
            $textboxTime.click(function () {
                OGT.Tracking.JourneySearchEngine.TimeChanged();
            });
            $sokresaDdl.change(function () {
                OGT.Tracking.JourneySearchEngine.FitArrivalOrDepartureChanged();
            });
            $trafficTypes.click(function () {
                OGT.Tracking.JourneySearchEngine.TrafficTypesChanged();
            });
            $changetime.change(function () {
                OGT.Tracking.JourneySearchEngine.ChangeTimeChanged();
            });
            $priority.change(function () {
                OGT.Tracking.JourneySearchEngine.PriorityChanged();
            });
            $walk.change(function () {
                OGT.Tracking.JourneySearchEngine.WalkAcceptableChanged();
            });

            $swapButton.click(function (event) {
                event.preventDefault();
                event.stopPropagation();

                $swapButton.toggleClass("spin");

                OGT.Tracking.JourneySearchEngine.SwapToAndFrom();

                journeyProxy.swapToAndFrom.set();

                // Display explanatory text for screen reader when switching destinations
                $swapUpdateElement.html(swapBtnText);

                // Empty span so the DOM will be updated every time the btn is click/pressed
                setTimeout(function () {
                    $swapUpdateElement.empty();
                }, 200);

            });

            $submit.click(function (event) {
                event.preventDefault();
                event.stopPropagation();
                journeyProxy.search.set({ doe: { action: 'doe', track: true } });
            });


            $journeyAdvancedButton.click(function (event) {
                event.preventDefault();
                event.stopPropagation();

                if ($journeyAdvancedButton.attr("aria-expanded") === "false") {
                    $journeyAdvancedButton.attr("aria-expanded", true);
                } else {
                    $journeyAdvancedButton.attr("aria-expanded", false);
                }


                $journeyAdvancedButton.toggleClass("advanced--active");
                $startPageResult.toggleClass("advanced--active");
                $heroImageContainer.toggleClass("advanced--active");

                $journeyAdvancedButton.find("i,span").toggleClass("hide");

                $journeyAdvancedOptionsContainer.toggleClass("hide");

            });

            $openMapButton.click(function () {

                var $that = $(this);

                function visibilityListener(inValueOptions, options) {

                    if (inValueOptions.ise === true) {

                        mapProxy.visible.stopListening(visibilityListener);

                        OGT.MapAdapter.MapMenu.Visibility.showInMobileMode(false);

                        if ($that.hasClass("action--zoom-from")) {

                            journeyProxy.zoomToStartMarker.set(true, options); // Chain

                        } else if ($that.hasClass("action--zoom-to")) {

                            journeyProxy.zoomToEndMarker.set(true, options); // Chain

                        }

                        return options.allIsDoneCallback;

                    }

                    return null;

                }

                //journeyProxy.selected.set([]);

                var io = null;
                if ($that.hasClass("action--zoom-from")) {

                    io = OGT.Proxy.journey.start;

                    OGT.Tracking.Map.ZoomOnStart.StartPage();

                } else if ($that.hasClass("action--zoom-to")) {

                    io = OGT.Proxy.journey.end;

                    OGT.Tracking.Map.ZoomOnEnd.StartPage();

                }

                if (io) {
                    var v = io.get();
                    if (v) {
                        v.highlitLevel = OGT.CONSTANTS.MAP.HIGHLIT_LEVELS.JOURNEY_CLICK_ZOOM_TO_START_OR_END;
                        io.refresh();
                    }
                }

                mapProxy.visible.listen({ newValue: visibilityListener });

                mapProxy.fullscreen.set({ doe: true });
                mapProxy.visible.set({ doe: true });

                $("#map__target--fullscreen").focus();
                modalTabLoop(".modal");



            });

            function showSearchCustomization() {

                //$journeySearchFormOptionsArea.show();
                //$journeySearchFormButtonArea.show();

                //$journeyFromAndToContainer.addClass("swap-enabled").find(".swap.swap-desktop button").attr("tabindex", "0");
            }

            $textboxFrom.focus(function () {
                showSearchCustomization();
            });

            $textboxTo.focus(function () {
                showSearchCustomization();
            });

            function startAndEndListener() {

                var startPlace = startIo.get(),
                    endPlace = endIo.get();

                if (startPlace || endPlace) {
                    showSearchCustomization();
                }

            }

            startIo.listen({
                newValue: startAndEndListener
            });
            endIo.listen({
                newValue: startAndEndListener
            });

            /**
            * Journey search related eventhandlers
            */

            //Hitting enter on keydown in from textbox should "tab" to the to-textbox

            (function () {

                var inOrder = [
                    { $field: $textboxFrom, io: OGT.Proxy.journey.start },
                    { $field: $textboxTo, io: OGT.Proxy.journey.end },
                    { $field: $textboxTime, io: OGT.Proxy.journey.time },
                    { $field: $textboxDate, io: OGT.Proxy.journey.date }
                ];

                function trySearch() {

                    OGT.Proxy.journey.search.set({
                        doe: {
                            action: "doe",
                            waitForIt: true,
                            sendToMainSearchPage: false
                        }
                    });

                }

                function getSelectNextEmptyFieldIfOriginatedWithUser($originatorField) {
                    var originatorField = $originatorField.get(0);
                    return function (inValue, options) {


                        if (originatorField === options.context && options.originatedWithEnter) {

                            var frontierIndex = inOrder.findIndex(function (fieldAndIo) { return fieldAndIo.$field === $originatorField; });
                            if (0 <= frontierIndex) {
                                var nextEmpty = inOrder.slice(frontierIndex + 1).concat(inOrder.slice(0, frontierIndex)).find(function (fieldAndTo) { return !fieldAndTo.io.get(); });
                                if (nextEmpty) {

                                    // Focus on same input
                                    //$field.focus();

                                } else {

                                    // All are filled, search
                                    trySearch();
                                }
                            }

                        }

                    }
                }

                inOrder.forEach(function (fieldAndIo) {

                    var $field = fieldAndIo.$field;
                    $field.keydown(function (ev) {
                        if (OGT.Util.Event.isEnter(ev)) {
                            ev.preventDefault();

                            if (OGT.Util.BrowserDetect.isIe()) {

                                // Ie kills the type forward if you use blur
                                var field = $field.get(0),
                                    onBlurBypass = field.onBlurBypass;

                                if (onBlurBypass) {
                                    onBlurBypass.call(field, ev, { originatedWithEnter: true });
                                }

                                $submit.focus();

                            } else {
                                $field.trigger("blur", { originatedWithEnter: true });
                            }
                        }
                    });

                    fieldAndIo.io.listen({
                        suppressImmediateCallback: true,
                        newValue: getSelectNextEmptyFieldIfOriginatedWithUser($field)
                    });

                });


            }());

        };

    }());


    var connectNartrafik = (function () {

        var linkTemplate = "{1}?{0}={3}&{0}Type={2}&{0}Name={5}&{0}Ll={4}&lines_selected={6}&map_ll={4}&map_zoom=9";

        var startAjaxHandle,
            endAjaxHandle;

        function getStopLabel(stop) {
            var url = OGT.Util.OGT.getStopPageUrl(stop);
            return '<' + (url ? 'a' : 'span') + ' href="' + url + '" class="gray-place' + (url ? ' clickable' : '') + '">' +
                '<i class="ogt-pin-stop-16 icon--small" aria-hidden="true"></i>' +
                '<span class="text text--small text--medium">' +
                stop.PlaceName +
                '</span>' +
                '</' + (url ? 'a' : 'span') + '>';
        }

        function addStopList($root, stops) {
            var $listRoot = $('<div class="list"></div>');

            stops
                .reduce(function (acc, stop) {
                    var stopId = stop.Id;
                    if (acc.every(function (s) { return stopId != s.Id; })) {
                        acc.push(stop);
                    }
                    return acc;
                }, [])
                .forEach(function (stop) {

                    var $listItem = $('<div style="border: none" class="list__item"></div>');

                    $listItem.append(getStopLabel(stop));
                    $listRoot.append($listItem);
                });
            $root.empty().append($listRoot);
        }

        function reduceLinesToUniqueStopIds(lines) {
            return lines.reduce(function (acc, line) {
                line.hplllista
                    .forEach(function (stop) {
                        acc.pushIfNotInArray(stop.id);
                    });

                return acc;
            }, []);
        }

        function fetchPlace(place, successCallback) {

            if (place) {
                var ogtType = place.OgtType;
                if (ogtType == "poi" || ogtType == "address") {
                    return OGT.GeoServer.fetchLinesCoveringCoord({
                        coords: place.Ll,
                        successCallback: function (lines) {

                            lines = lines.filter(function (line) {
                                return line.trafiktyp.split(" ").some(function (tt) { return tt == OGT.CONSTANTS.TRAFFIC_TYPES.NARTRAFIK; });
                            });

                            var stopIds = reduceLinesToUniqueStopIds(lines);

                            if (stopIds.length) {

                                OGT.Util.OGT.AsyncAjax.getStops(stopIds, {
                                    successCallback: function (stops) {

                                        var remaingStopIds = stopIds.filter(function (stopId) {

                                            return stops.every(function (stop) { return stopId !== stop.Id });

                                        });


                                        OGT.GeoServer.fetchMainStopsWithIds({
                                            ids: remaingStopIds,
                                            successCallback: function (geoStops) {

                                                OGT.GeoServer.Util.convertMainAndStopPointArrayFromGeoServerData(geoStops);

                                                stops.pushArray(geoStops);

                                                successCallback(place, lines, stops);

                                            }
                                        });

                                    }
                                });
                            }


                        }
                    });
                }
            }

            return null;

        }

        return function (options) {

            var proxyNs = OGT.Proxy,
                journeyProxy = proxyNs.journey;

            if (!journeyProxy) {
                return;
            }


            var startIo = journeyProxy.start,
                endIo = journeyProxy.end,
                errorIo = journeyProxy.error,

                $nartrafikContainer = options.$nartrafikContainer,

                $journeySearchFormOptionsArea = options.$journeySearchFormOptionsArea,
                $journeySearchFormButtonArea = options.$journeySearchFormButtonArea,
                $journeyAdvancedOptionsContainer = options.$journeyAdvancedOptionsContainer,

                $fromNartrafikInfo = $nartrafikContainer.find(".fromNartrafik-info__container"),
                $toNartrafikInfo = $nartrafikContainer.find(".toNartrafik-info__container"),
                $fromNartrafikStops = $fromNartrafikInfo.find(".nartrafik-info__result-list"),
                $toNartrafikStops = $toNartrafikInfo.find(".nartrafik-info__result-list"),
                $fromNartrafikMoreInfo = $fromNartrafikInfo.find(".more-nartrafik-info"),
                $toNartrafikMoreInfo = $toNartrafikInfo.find(".more-nartrafik-info"),
                moreInfoHref = $fromNartrafikMoreInfo.attr("href"),
                $localtraffictext = document.getElementById("localtraffictext"),

                $idlocaltraffic = document.getElementById("idlocaltraffic"),

                $fromNartrafikInfoShow = $nartrafikContainer.find(".fromNartrafik-info__containerShow");
            $Nartrafikinfo__containerShowStarttext = $nartrafikContainer.find(".Nartrafik-info__containerShowStarttext");

            function showNartrafikInformation(place, lines, stops, isFromNartrafik) {

                $journeySearchFormOptionsArea.hide();
                $journeySearchFormButtonArea.hide();
                $journeyAdvancedOptionsContainer.hide();

                errorIo.set(null);

                var $nartrafikStops = isFromNartrafik ? $fromNartrafikStops : $toNartrafikStops;

                addStopList($nartrafikStops, stops);


                // document.getElementById("localtraffictext").innerHTML = "Det är inte möjligt att resa med närtrafik till eller från den här adressen.";

                $Nartrafikinfo__containerShowStarttext.hide();

                (isFromNartrafik ? $fromNartrafikInfo : $toNartrafikInfo).show();
                if (isFromNartrafik) {
                    $fromNartrafikInfoShow.hide();


                    // document.getElementById("localtraffictext").innerHTML = "Hit kan du resa med närtrafiken. Se närtrafikområdet i kartan. Du kan resa till eller från:";
                }





                var $moreNartrafikInfo = (isFromNartrafik ? $fromNartrafikMoreInfo : $toNartrafikMoreInfo);


                $moreNartrafikInfo.attr("href", OGT.Util.String.format(linkTemplate,
                    isFromNartrafik ? "from" : "to",
                    moreInfoHref,
                    place.OgtType,
                    place.Id,
                    place.Ll,
                    place.PlaceName,
                    lines.map(function (line) { return line.linje })
                ));

            }

            function showFromNartrafikInformation(place, lines, stops) {
                showNartrafikInformation(place, lines, stops, true);

            }

            function showToNartrafikInformation(place, lines, stops) {
                showNartrafikInformation(place, lines, stops, false);
            }

            function startAndEndAndPlaceNartrafikListener() {

                var startPlace = startIo.get(),
                    endPlace = endIo.get();

                $fromNartrafikInfo.hide();
                $Nartrafikinfo__containerShowStarttext.show();




                $toNartrafikInfo.hide();

                $journeyAdvancedOptionsContainer.show();


                if (startAjaxHandle) {
                    startAjaxHandle.abort();
                }
                startAjaxHandle = fetchPlace(startPlace, showFromNartrafikInformation);


                if (startAjaxHandle == null) {
                    $fromNartrafikInfoShow.hide();
                }
                else {
                    $fromNartrafikInfoShow.show();
                    $Nartrafikinfo__containerShowStarttext.hide();
                }


                if (endAjaxHandle) {
                    endAjaxHandle.abort();
                }
                endAjaxHandle = fetchPlace(endPlace, showToNartrafikInformation);
            }

            startIo.listen({
                newValue: startAndEndAndPlaceNartrafikListener
            });
            endIo.listen({
                newValue: startAndEndAndPlaceNartrafikListener
            });

        };

    }());


    $journeySearchFormMasterContainers.each(function () {

        var $journeySearchFormMasterContainer = $(this),

            $nartrafikContainer = $journeySearchFormMasterContainer.find(".nartrafik__container"),

            $journeySearchFormContainer = $journeySearchFormMasterContainer.find(".journey-search-form-container"),

            $journeySearchFormOptionsArea = $journeySearchFormContainer.find(".options__area"),
            $journeySearchFormButtonArea = $journeySearchFormContainer.find(".button__area"),
            $journeyAdvancedOptionsContainer = $journeySearchFormContainer.find(".journey__advanced-options");

        connectJourneySearchForm({
            $journeySearchFormContainer: $journeySearchFormContainer,
            $journeySearchFormOptionsArea: $journeySearchFormOptionsArea,
            $journeySearchFormButtonArea: $journeySearchFormButtonArea,
            $journeyAdvancedOptionsContainer: $journeyAdvancedOptionsContainer
        });

        if ($nartrafikContainer.length) {

            connectNartrafik({
                $nartrafikContainer: $nartrafikContainer,
                $journeySearchFormOptionsArea: $journeySearchFormOptionsArea,
                $journeySearchFormButtonArea: $journeySearchFormButtonArea,
                $journeyAdvancedOptionsContainer: $journeyAdvancedOptionsContainer
            });

        }

    });

}());
;
(function () {

    var isListPage = window._OGT_SearchHandler_IsListPage,
        isSmallScreen = window.matchMedia("screen and (max-width: 480px)").matches,
        utilNs = OGT.Util,

        proxyNs = OGT.Proxy,
        journeyProxy = proxyNs.journey;

    if (!journeyProxy) {
        return;
    }

    var startIo = journeyProxy.start,
        endIo = journeyProxy.end,
        errorIo = journeyProxy.error,

        $journeySearchResultMasterContainers = $('.journey__search-result-master-container'),

        extraButtonOnEachJourneyOptions = null,
        specialPreParseJourneySearchFunc = null,

        languageTranslate = OGT.Language.translate; //Local pointer to Language.translate to minimize property lookup since translate is heavily used

    // Adds screen reader text to depature
    function insertDepSrText() {
        $(".deparr .dep, .deparr .dep-deviated").each(function (index) {
            if ($(this).children(".show-for-sr").length === 0) {
                if ($(this).hasClass("dep")) {
                    if ($(this).parent().hasClass("deviated--no-longer-valid")) {
                        $(this).prepend("<span class='show-for-sr'>Ursprunglig avgång</span>");
                    }
                    else {
                        $(this).prepend("<span class='show-for-sr'>Avgång</span>");
                    }
                }
                else if ($(this).hasClass("dep-deviated")) {
                    $(this).prepend("<span class='show-for-sr'>Ny avgång</span>");
                }
            }
        });
    }

    // Adds screen reader text to arrival
    function insertArrSrText() {

        $(".deparr .arr, .deparr .arr-deviated").each(function (index) {
            if ($(this).children(".show-for-sr").length === 0) {
                if ($(this).hasClass("arr")) {
                    if ($(this).parent().hasClass("deviated--no-longer-valid")) {
                        $(this).prepend("<span class='show-for-sr'>Ursprunglig ankomst</span>");
                    }
                    else {
                        $(this).prepend("<span class='show-for-sr'>Ankomst</span>");
                    }
                }
                else if ($(this).hasClass("arr-deviated")) {
                    $(this).prepend("<span class='show-for-sr'>Ny ankomst</span>");
                }
            }
        });
    }

    var ResultRowBaseClass = (function () {

        function resultRowBaseClass() {
        }

        resultRowBaseClass.prototype.getKey = function () {
            return null;
        };
        resultRowBaseClass.prototype.preInit = function (options) {

            options = options || {};

            var div = document.createElement("div"),
                $div = $(div);

            this.div = div;
            this.$div = $div;
            div.className = "departure__item accordion_list__item clearfix expandable " + (options.cssClass || "");
            div.backReference = this;
        };

        resultRowBaseClass.prototype.getDomElement = function () {
            return this.div;
        };

        resultRowBaseClass.prototype.postInit = function () {
            if (!isListPage) this.$div.slideDown();
        };
        resultRowBaseClass.prototype.invalidate = function () {
            this.invalid = true;
        };
        resultRowBaseClass.prototype.destroy = function () {
            var that = this;
            if (!isListPage) {
                this.$div.slideUp(function () {
                    that.$div.remove();
                    // Kill the back reference, just to be sure it will be garbage collected
                    that.div.backReference = null;
                });
            } else {
                that.$div.remove();
                that.div.backReference = null;
            }
        };
        return resultRowBaseClass;
    }());

    var JourneyClass = (function () {

        var headerContentTemplate = [
            '<span class="item time">{0}</span>',
            '<span class="item start-and-end">{3} - {4}</span>',
            '<span class="item traveltime">',
            '<span class="traveltime__switch">',
            '<span class="screen-reader-text">' + languageTranslate("/searchjourney/timeisforecast") + '. ' + languageTranslate("/searchjourney/traveltime") + ': </span>',
            ' {1} <span class="show-for-sr"> timme. </span>',
            '</span>',
            '</span>',
            '<span class="item switches">',
            '<span class="switch">',
            '<span class="show-for-sr">Byten: </span>{2}',
            '</span>',
            '</span>',
            '<span class="show-for-sr">. Transportslag: </span><span class="item travel__plan"></span>'
        ].join(''),

            additionalButtonTemplate = [
                '<button type="button" class="button alternative rounded item travel__selection additional">' +
                '<span>{0}</span>' +
                '<span>Välj</span>' +
                '</button>'
            ].join('');

        function journeyClass(journeyData, options) {
            journeyClass.superclass.preInit.call(this, { cssClass: "main" });

            options = options || {};

            var that = this,
                $div = this.$div,
                additionalButtonOptions = options.extraButtonOnEachJourneyOptions,
                additionalButtonHtml = '';

            this.journeyData = journeyData;

            if (additionalButtonOptions) {
                additionalButtonHtml = utilNs.String.format(additionalButtonTemplate,
                    languageTranslate(additionalButtonOptions.langKey));
            }

            var $itemHeader = $(OGT.Util.OGT.AccordionList.getAccordionListItemHeader({
                noTextOnButtons: additionalButtonOptions,
                htmlContent: utilNs.String.format(headerContentTemplate,
                    journeyData.ogtDepArrHtml,
                    journeyData.ogtTravelTime,
                    OGT.Util.OGT.Format.switches(journeyData.Switch),
                    journeyData.ReturnedStartPlace.PlaceName,
                    journeyData.ReturnedEndPlace.PlaceName,
                    journeyData.JourneyKey
                ),
                additionalHtmlButtons: additionalButtonHtml
            }));

            if (journeyData.ogtTravelCanceled) {
                $div.addClass("depature__canceled");
            }

            $div.append($itemHeader);

            if (journeyData.postProcess$Dom) {
                journeyData.postProcess$Dom($div);
            }

            this._addTravelPlan();

            function toggleDetails(event) {

                event.preventDefault();
                event.stopPropagation();

                that.toggleDetails();

                var $elem = $(this);

                //// Wait for css animation to complete first
                //setTimeout(function () {
                //    OGT.Util.scrollToElement($elem);
                //}, 550);
            }

            $itemHeader.on("click keypress", toggleDetails);

            $itemHeader.find(".traffic-type-group__icon.COLLECTIVE").click(toggleDetails);

            $itemHeader.find(".travel__plan").click(function (event) {
                event.stopPropagation();
            });

            if (additionalButtonOptions) {
                $div.find(".additional").click(function (event) {

                    event.stopPropagation();

                    additionalButtonOptions.clickCallback(journeyData, $div);

                });
            }

            errorIo.set(null);

            journeyClass.superclass.postInit.call(this);

            insertDepSrText();
            insertArrSrText();

        }

        OGT.Util.extend(journeyClass, ResultRowBaseClass);

        journeyClass.prototype.destroy = function () {

            this.hideDetails();

            journeyClass.superclass.destroy.call(this);

        };

        journeyClass.prototype._addTravelPlan = function () {

            OGT.Util.OGT.Journey.addTravelPlan(this.journeyData, this.$div.find(".travel__plan"));

        };

        journeyClass.prototype.getDepartureDateTime = function () {
            return this.journeyData.ogtDepartureDateTime;
        };

        journeyClass.prototype.getKey = function () {
            return this.journeyData.ogtKey;
        };

        journeyClass.prototype.zoomTo = function (routelinkIndex, places) {
            OGT.Util.OGT.Journey.zoomToRoutelinksAndPlaces(this.journeyData, routelinkIndex, places);
        };

        journeyClass.prototype.showDetails = function () {

            var $div = this.$div,
                $detailsDiv = this.$detailsDiv;

            if (!$detailsDiv) {

                OGT.Tracking.JourneySearchEngine.ShowJourneyDetails();

                var detailsResult = this._makeDetails();
                this.tabSet = detailsResult.tabSet;

                $detailsDiv = this.$detailsDiv = detailsResult.$expandedDiv;
                $div.append($detailsDiv);


                $div.append(OGT.Util.OGT.AccordionList.get$NavigationBarForJourneySearch(this._getTopBarArguments()));

            }

            insertDepSrText();
            insertArrSrText();

            $detailsDiv.removeAttr("hidden");
            $div.find(".item__header-controls").attr('aria-expanded', true);

            // Set Overview as default view when expanding
            this.tabSet[0].show();

            setTimeout(function () {
                $div.addClass("expandable--active");
                if (isSmallScreen) {
                    OGT.Util.OGT.AccordionList.setupAccordionKeyPressEvents($div);
                }

            }, 50);

            return;

        };

        journeyClass.prototype.hideDetails = function () {

            var $div = this.$div,
                $detailsDiv = this.$detailsDiv;

            if (!$detailsDiv) {
                return;
            }

            this.tabSet.forEach(function (tab) {
                tab.hide();
            });

            $div.removeClass('expandable--active'); // Clear all rows
            $detailsDiv.attr("hidden", true);
            $div.find(".item__header-controls").attr('aria-expanded', false);
            OGT.Util.OGT.AccordionList.removeAccordionKeyPressEvents($div);

        };

        journeyClass.prototype.toggleDetails = function () {

            var $div = this.$div;

            if ($div.hasClass('expandable--active')) { // Is open, close it
                this.hideDetails();
            } else {
                this.showDetails();
            }

        };

        journeyClass.prototype._getTopBarArguments = function () {

            var that = this,
                customPlaceText = OGT.Language.translate("/searchjourney/placetypes/custom"),
                journeyData = this.journeyData;

            return {
                date: journeyData.ogtDepartureDateTime.getFormattedDate("yyyy-MM-dd"),
                start: journeyData.UsedStartPlace.PlaceName || customPlaceText,
                end: journeyData.UsedEndPlace.PlaceName || customPlaceText,
                click: function () {
                    that.hideDetails();
                }
            };

        };

        journeyClass.prototype._makeDetails = (function () {

            var expandedDivTemplate = [
                '<ul data-tabs role="tablist" class="tabs"></ul>',
                '<div class="tabContents clearfix"></div>',
            ].join('');

            function doIt() {

                var journeyData = this.journeyData,
                    $expandedDiv = $(OGT.Util.OGT.AccordionList.getAccordionListItemContent({
                        htmlContent: expandedDivTemplate,
                        additionalContent: journeyData.ogtTravelCanceled ? OGT.Util.OGT.Format.Realtime.canceledInfoTemplate : null
                    })),
                    $tabs = $expandedDiv.find('.tabs'),
                    $tabContents = $expandedDiv.find('.tabContents'),
                    tabSet = [];

                [
                    {
                        langKey: "overview",
                        makeFunc: getRenderOverviewOrDetailsFunc(false)
                    },
                    {
                        langKey: "details",
                        makeFunc: getRenderOverviewOrDetailsFunc(true)
                    },
                    {
                        langKey: "map",
                        makeFunc: _makeMapTabContent,
                        hideFunc: _hideMapTab,
                        alwaysRunMakeContent: true
                    },
                    {
                        langKey: "prices",
                        makeFunc: _makePricesTabContent
                    }
                ].forEach(function (t, i) {

                    t.tabSet = tabSet;
                    t.$tabs = $tabs;
                    t.$tabContents = $tabContents;
                    t.name = languageTranslate("/searchjourney/searchresulttabs/" + t.langKey);
                    t.journeyData = journeyData;

                    var tab = _makeTab.call(this, t);
                    if (i == 0) {
                        tab.show();
                    }

                });

                return {
                    $expandedDiv: $expandedDiv,
                    tabSet: tabSet
                };
            };


            var _makeTab = (function () {

                var tabTemplate = '<button aria-selected="false" tabindex="-1" role="tab" aria-controls="tabpanel-{1}" class="tabs__tab {1} button rounded secondary">{0}</button>',
                    passiveCssClass = 'secondary';

                function t(options) {

                    var tabSet = this.tabSet = options.tabSet;
                    this.alwaysRunMakeContent = options.alwaysRunMakeContent;
                    this.makeContentFunc = options.makeFunc;
                    this.hideFunc = options.hideFunc || function () { };
                    this.$tabContents = options.$tabContents;
                    this.journeyData = options.journeyData;

                    tabSet.push(this);

                    var that = this,
                        $tab = this.$tab = $(OGT.Util.String.format(tabTemplate, options.name, options.langKey));

                    $tab.click(function (event) {
                        event.preventDefault();
                        event.stopPropagation();
                        that.show();
                        setTimeout(() => {
                            insertDepSrText();
                            insertArrSrText();
                        }, 200);

                    }).appendTo(options.$tabs);

                    $tab.keydown(function (event) {
                        onKeydown(event);
                    });

                    // Keyboard control of tabs
                    function onKeydown(event) {
                        var tgt = event.currentTarget;

                        switch (event.key) {
                            case 'ArrowLeft':
                                $(tgt).prev().focus();
                                break;

                            case 'ArrowRight':
                                $(tgt).next().focus();
                                break;

                            case 'Home':
                                event.preventDefault();
                                $(".tabs__tab:first-child").focus();
                                break;

                            case 'End':
                                event.preventDefault();
                                $(".tabs__tab:last-child").focus();
                                break;

                            default:
                                break;
                        }
                    };

                }

                t.prototype.show = function () {

                    if (this.visible) {
                        return;
                    }

                    this.tabSet.forEach(function (tS) {
                        tS.hide();
                    });

                    this.$tab.removeClass(passiveCssClass);
                    this.$tab.attr("tabindex", 0);
                    this.$tab.attr("aria-selected", true);

                    if (this.alwaysRunMakeContent || !this.$tabContent) {
                        this.$tabContent = this.makeContentFunc(this.$tabContents, this.journeyData);
                    }

                    this.$tabContent.show();

                    this.visible = true;

                };

                t.prototype.hide = function () {

                    this.$tab.addClass(passiveCssClass);
                    this.$tab.attr("tabindex", -1);
                    this.$tab.attr("aria-selected", false);

                    if (!this.visible) {
                        return;
                    }

                    this.visible = false;

                    this.hideFunc();

                    if (this.$tabContent) {
                        this.$tabContent.hide();
                    }

                };


                return function (makeContentFunc) {
                    return new t(makeContentFunc);
                };

            }());

            function _makeMapTabContent($parent, journeyData) {

                var $target = OGT.MapAdapter && OGT.MapAdapter.getPortionTarget && $(OGT.MapAdapter.getPortionTarget());

                if (!$target || !$target.length) {
                    $target = $('<div id="map__target--portion" class="map__target"></div>');
                }
                $parent.append($target);

                OGT.Proxy.map.fullscreen.set({ doe: false });
                OGT.Proxy.map.visible.set({ doe: true });

                OGT.Util.OGT.Journey.showJourneyOnMap(journeyData);

                removeTabIndexingForMapPartial();

                return $target;

            };

            function _hideMapTab() {

                var $target = OGT.MapAdapter && OGT.MapAdapter.getPortionTarget && $(OGT.MapAdapter.getPortionTarget(true));
                if ($target) {
                    //To ensure we don't loose click events it must stay attached somewhere
                    $('body').append($target);
                }
                OGT.Proxy.map.visible.set({ doe: false });

            };

            function _makePricesTabContent($parent, journeyData) {

                var costDetailTemplate = [
                    '<div class="block pricing">',
                    '<div class="pricing__table"><div class="pricing__text">{0}</div><div class="pricing__text">{1}</div></div>',
                    '</div>'
                ].join(""),
                    costMessage1 = typeof window.costMessage1 === "undefined" ? "" : window.costMessage1,
                    costMessage2 = typeof window.costMessage2 === "undefined" ? "" : window.costMessage2;


                var $result = $(utilNs.String.format(costDetailTemplate, costMessage1, costMessage2));
                $parent.append($result);
                return $result;

            };

            return doIt;


        }());
        return journeyClass;
    }());

    var DaySeparatorClass = (function () {

        var daySeparatorTemplate = [
            '<div class="separator--dom">',
            '{0}',
            '</div>'
        ].join("");


        function daySeparatorClass(theDateTime, theNextSibling, options) {
            daySeparatorClass.superclass.preInit.call(this);

            var localResultDiv = options.targetElement,
                theDate = theDateTime.stripTime();

            this.departureDateTime = theDate;
            this.$div.append(utilNs.String.format(daySeparatorTemplate, theDate.getFancyFormattedDate().capitalizeFirstLetter()));

            localResultDiv.insertBefore(this.div, theNextSibling);

            daySeparatorClass.superclass.postInit.call(this);

        }

        OGT.Util.extend(daySeparatorClass, ResultRowBaseClass);

        daySeparatorClass.prototype.getDepartureDateTime = function () {
            return this.departureDateTime;
        };

        return daySeparatorClass;
    }());

    var ListJourneyHeaderClass = (function () {

        var listJourneyHeaderTemplate = [
            '<div class="separator--dom">',
            '{0}&nbsp;<i class="ogt-arrow-right-16 icon--xsmall"></i>&nbsp;{1}, <span class="travel-date">{2}</span>',
            '</div>'
        ].join("");

        function listJourneyHeaderClass(theBackReference, theNextSibling) {
            listJourneyHeaderClass.superclass.preInit.call(this);

            var $parentDiv = listJourneyHeaderClass.superclass.getDomElement.apply(this);
            $parentDiv.className = "journey__item--header clearfix expandable ";

            var theDate = theBackReference.getDepartureDateTime().stripTime(),
                journeyData = theBackReference.journeyData,
                customPlaceText = OGT.Language.translate("/searchjourney/placetypes/custom");

            this.$div.append(utilNs.String.format(listJourneyHeaderTemplate,
                journeyData.ReturnedStartPlace.PlaceName || customPlaceText,
                journeyData.ReturnedEndPlace.PlaceName || customPlaceText,
                theDate.getFancyFormattedDate()));

            theNextSibling.insertBefore(this.div, theNextSibling.firstChild);

            listJourneyHeaderClass.superclass.postInit.call(this);

        }

        OGT.Util.extend(listJourneyHeaderClass, ResultRowBaseClass);

        return listJourneyHeaderClass;
    }());

    var NoJourneySeparatorClass = (function () {

        var noJourneyTemplate = [
            '<div class="journey__empty-for-day">',
            '<div class="inner">',
            '<b>',
            languageTranslate("/searchjourney/noJourneyOnSelectedDateHeading"),
            '</b>',
            '<br />',
            languageTranslate("/searchjourney/noJourneyOnSelectedDateText"),
            '</div>',
            '</div>'
        ].join("");

        function noJourneySeparatorClass(theDateTime, theNextSibling, options) {
            noJourneySeparatorClass.superclass.preInit.call(this);

            var localResultDiv = options.targetElement,
                theDate = theDateTime.stripTime();

            this.departureDateTime = theDate;
            this.$div.append(utilNs.String.format(noJourneyTemplate, theDate.getFancyFormattedDate()));

            localResultDiv.insertBefore(this.div, theNextSibling);

            noJourneySeparatorClass.superclass.postInit.call(this);
        }

        OGT.Util.extend(noJourneySeparatorClass, ResultRowBaseClass);

        noJourneySeparatorClass.prototype.getDepartureDateTime = function () {
            return this.departureDateTime;
        };

        return noJourneySeparatorClass;
    }());

    function addButtonToEachJourney(inExtraButtonOnEachJourneyOptions) {
        extraButtonOnEachJourneyOptions = inExtraButtonOnEachJourneyOptions;
    }

    function setPreParseJourneySearchResultFunc(inSpecialPreParseJourneySearchFunc) {
        specialPreParseJourneySearchFunc = inSpecialPreParseJourneySearchFunc;
    }


    var getRenderOverviewOrDetailsFunc = (function () {

        var journeyZoomMapLegTemplate = [
            '<a href="#" class="map__zoom--leg hide-for-print" title="' + languageTranslate("/searchjourney/zoomto/leg") + '">',
            '<i class="ogt-zoom-to-16 icon--small map__zoom--icon" aria-hidden="true"></i>',
            '</a>'
        ].join(''),
            journeyZoomMapSwitchTemplate = [
                '<a href="#" class="map__zoom--switch hide-for-print {0}" title="{1}">',
                '<i class="ogt-zoom-to-16 icon--small map__zoom--icon" aria-hidden="true"></i>',
                '</a>'
            ].join('');

        var LegBaseClass = (function () {

            var journeyLegDetailTemplate =
                '<div class="journey__leg {0}">' +
                '<div class="journey__tracer {3}" style="{2}">' +
                '<div class="relative">' +
                '<i class="marker {1} icon--big" aria-hidden="true"></i> ' +
                '</div>' +
                '</div>' +
                '</div>',
                journeyLegPartTemplate =
                    '<div class="journey__leg__part {0} clearfix">' +
                    '<div class="part__time">{1}</div>' +
                    '<div class="part__icon">{2}</div>' +
                    '<div class="part__info">{3}</div>' +
                    '<div class="part__info part__info--short">{4}</div>' +
                    '</div>';

            function l() {
            }

            l.prototype._init = function (parent, options) {

                var isOdd = options.isOdd,
                    isFirst = options.isFirst,
                    isLast = options.isLast,
                    isForMap = this.isForMap = options.isForMap,
                    routelink = this.routelink = options.routelink,
                    type = routelink.ogtType,
                    typeIsVehicle = (type == "vehicle");

                this.parent = parent;
                this.index = options.index;
                this.isFirst = isFirst;
                this.isLast = options.isLast;
                this.isOdd = isOdd;

                var mapSelectedColour = null;

                if (this.isForMap) {

                    mapSelectedColour = typeIsVehicle ? OGT.Proxy.Util.getColourForLine(routelink.Line.LineNrReal) : OGT.Proxy.journey.ofClass.routeColours[type];
                }

                var $journeyLegDetail = this.$journeyLegDetail = $(utilNs.String.format(
                    journeyLegDetailTemplate,
                    ((isFirst ? " first" : "") + (isLast ? " last" : "") + (isForMap ? " journey__leg--is-for-map" : "")),
                    OGT.Util.OGT.getIconCssClassForOgtPlaceType(routelink.To.OgtType, true),
                    mapSelectedColour ? "border-color: " + mapSelectedColour : "",
                    typeIsVehicle ? "is-vehicle" : "is-not-vehicle"
                ));

                this.mapSelectedColour = mapSelectedColour;

                options.$root.append($journeyLegDetail);

            };

            l.prototype._makePushGet$LegPart = function (className, depArrHtml, iconHtml, mainHtml, shortFormatInfo) {

                var $dom = $(utilNs.String.format(journeyLegPartTemplate,
                    className,
                    depArrHtml,
                    iconHtml,
                    mainHtml,
                    shortFormatInfo));

                this.$journeyLegDetail.append($dom);

                return $dom;
            }

            l.prototype._addTravelInstructionsFrom = function () {

                var journeyNs = OGT.Util.OGT.Journey,
                    routelink = this.routelink,
                    line = routelink.Line,
                    ogtTexts = routelink.ogtTexts,
                    stepOnTexts = ogtTexts.stepOn,

                    deviationsCount = journeyNs.getDeviations([routelink]).length,
                    iconOnIcon = false;

                // We currently do not support calltrip and deviation at the same time. This does not happen very often
                if (deviationsCount > 0) {

                    iconOnIcon = {
                        graphics: "warning",
                        tooltip: journeyNs.getDeviationsText(deviationsCount, true)
                    }

                } else if (routelink.Line.FootNotes != null) {

                    iconOnIcon = {
                        graphics: "infosquare",
                        tooltip: OGT.Language.translate("/searchjourney/journeyhasimportantinfo")
                    }

                }

                var iconHtml = "";
                if (this.isForMap) {

                    var isFirst = this.isFirst;
                    var type = routelink.ogtType;
                    var typeIsVehicle = (type == "vehicle");

                    if (isFirst || typeIsVehicle) {

                        var ogtDetails = line.Ogt;

                        var trafficTypeId = line && line.LineTypeId;

                        var colour = this.mapSelectedColour;

                        var placeIcon = OGT.MapHandler.journey.getIcon(routelink.From, null, trafficTypeId, null, { coloursAndHighlights: [{ colour: this.mapSelectedColour }] });

                        iconHtml = placeIcon.getImgHtml();

                        if (typeIsVehicle) {

                            var specialGraphics = ogtDetails.OgtTrafficTypeIconReplaceLineName && ogtDetails.OgtTrafficTypeIconCssClass;

                            var vehicleIcon = OGT.Util.OGT.Icon.get({
                                type: "vehiclePointer",

                                specialGraphics: specialGraphics,

                                rotation: 180,

                                text: specialGraphics ? "" : line.LineName,
                                coloursAndHighlights: [
                                    {
                                        colour: colour
                                    }
                                ]
                            });

                            iconHtml += vehicleIcon.getImgHtml();

                        }

                    }

                } else {

                    iconHtml = OGT.Util.OGT.Format.line(line, {
                        icon: {
                            cssClass: "traffic-type-group__icon",
                            iconOnIcon: iconOnIcon
                        }
                    });

                }

                this._makePushGet$LegPart(
                    "travel__instruction from",
                    stepOnTexts.time,
                    iconHtml,
                    stepOnTexts.instruction,
                    stepOnTexts.shortFormat);
            };

            l.prototype._addTravelInstructionsTo = function () {

                var routelink = this.routelink,
                    line = routelink.Line,
                    ogtTexts = routelink.ogtTexts,
                    stepOffTexts = ogtTexts.stepOff;

                var iconHtml = "";
                if (this.isForMap) {

                    var isLast = this.isLast;
                    var type = routelink.ogtType;
                    var typeIsVehicle = (type == "vehicle");

                    if (isLast || typeIsVehicle) {

                        var trafficTypeId = line && line.LineTypeId;
                        var placeIcon = OGT.MapHandler.journey.getIcon(routelink.To, null, trafficTypeId, null, { coloursAndHighlights: [{ colour: this.mapSelectedColour }] });

                        iconHtml = placeIcon.getImgHtml();

                    }

                }

                this._makePushGet$LegPart(
                    "travel__instruction to",
                    stepOffTexts.time,
                    iconHtml,
                    stepOffTexts.instruction,
                    stepOffTexts.shortFormat);
            };

            return l;

        }());

        var WalkToAndFromAndAllTheWayLegClass = (function () {

            function walkToAndFromAndAllTheWayLegClass(parent, options) {

                this._init.apply(this, arguments);

                this._addTravelInstructionsFrom();

                this._addTravelInstructionsTo();

                this._addZooms();

            }

            OGT.Util.extend(walkToAndFromAndAllTheWayLegClass, LegBaseClass);

            walkToAndFromAndAllTheWayLegClass.prototype._addZooms = function () {

                var that = this,
                    $journeyLegDetail = this.$journeyLegDetail,
                    routelink = this.routelink,
                    previousRoutelink = routelink.ogtPrevRoutelink,
                    nextRoutelink = routelink.ogtNextRoutelink;


                $(journeyZoomMapLegTemplate).addClass('withIcon').appendTo($journeyLegDetail).click(function (event) {
                    event.stopPropagation();
                    event.preventDefault();


                    if (that.isFirst) {

                        OGT.Tracking.Map.ZoomOnLeg.WalkStart();

                        var places = [routelink.From, routelink.To],
                            nextR = routelink.ogtNextRoutelink;
                        if (nextR) {
                            places.push(nextR);
                        }
                        that.parent.zoomTo(that.index, places);
                    } else {

                        OGT.Tracking.Map.ZoomOnLeg.WalkEnd();

                        that.parent.zoomTo(that.index, [routelink.ogtPrevRoutelink.To, routelink.From, routelink.To]);
                    }
                });

                if (this.isFirst) {

                    $(utilNs.String.format(journeyZoomMapSwitchTemplate, "top", languageTranslate("/searchjourney/zoomto/walkstart"))).appendTo($journeyLegDetail).click(function (event) {
                        event.stopPropagation();
                        event.preventDefault();

                        OGT.Tracking.Map.ZoomOnSwitch.WalkStart();

                        that.parent.zoomTo(null, [routelink.From]);
                    });

                    if (!this.isLast) {

                        $(utilNs.String.format(journeyZoomMapSwitchTemplate, "bottom", languageTranslate("/searchjourney/zoomto/journeystart"))).appendTo($journeyLegDetail).click(function (event) {
                            event.stopPropagation();
                            event.preventDefault();

                            OGT.Tracking.Map.ZoomOnSwitch.JourneyStart();

                            that.parent.zoomTo(null, [routelink.To, nextRoutelink.From]);
                        });

                    }

                } else {

                    $(utilNs.String.format(journeyZoomMapSwitchTemplate, "top", languageTranslate("/searchjourney/zoomto/journeyend"))).appendTo($journeyLegDetail).click(function (event) {
                        event.stopPropagation();
                        event.preventDefault();

                        OGT.Tracking.Map.ZoomOnSwitch.JourneyEnd();

                        that.parent.zoomTo(null, [previousRoutelink.To, routelink.From]);
                    });

                }

            };


            return walkToAndFromAndAllTheWayLegClass;

        }());

        var WalkSignificantlyLegClass = (function () {

            function walkSignificantlyLegClass(parent, options) {

                this._init.apply(this, arguments);


                this._addTravelInstructionsFrom();

                this._addTravelInstructionsTo();


                this._addZooms();

            }

            OGT.Util.extend(walkSignificantlyLegClass, LegBaseClass);

            walkSignificantlyLegClass.prototype._addZooms = function () {

                var that = this,
                    $journeyLegDetail = this.$journeyLegDetail,
                    routelink = this.routelink,
                    previousRoutelink = routelink.ogtPrevRoutelink,
                    nextRoutelink = routelink.ogtNextRoutelink;

                $(journeyZoomMapLegTemplate).addClass('withIcon').appendTo($journeyLegDetail).click(function (event) {
                    event.stopPropagation();
                    event.preventDefault();

                    OGT.Tracking.Map.ZoomOnLeg.Walk();

                    that.parent.zoomTo(that.index, [previousRoutelink.To, routelink.From, routelink.To, nextRoutelink.From]);
                });

                if (previousRoutelink) {

                    $(utilNs.String.format(journeyZoomMapSwitchTemplate, "top", languageTranslate("/searchjourney/zoomto/walkstart"))).appendTo($journeyLegDetail).click(function (event) {
                        event.stopPropagation();
                        event.preventDefault();

                        OGT.Tracking.Map.ZoomOnSwitch.WalkStart();

                        that.parent.zoomTo(null, [previousRoutelink.To]);
                    });
                }

                if (nextRoutelink) {

                    $(utilNs.String.format(journeyZoomMapSwitchTemplate, "bottom", languageTranslate("/searchjourney/zoomto/walkend"))).appendTo($journeyLegDetail).click(function (event) {
                        event.stopPropagation();
                        event.preventDefault();

                        OGT.Tracking.Map.ZoomOnSwitch.WalkEnd();

                        that.parent.zoomTo(null, [nextRoutelink.From]);
                    });
                }

            };

            return walkSignificantlyLegClass;

        }());

        var VehicleLegClass = (function () {

            function vehicleLegClass(parent, options) {

                this._init.apply(this, arguments);

                this._addTravelInstructionsFrom();

                this._addDeviations();

                this._addCalltrip();

                if (options.withDetails) {
                    this._addIntermediateStops();
                }

                this._addTravelInstructionsTo();

                this._addZooms();


                this.$journeyLegDetail.find(".traffic-type-group__icon").click(function (event) {
                    event.stopPropagation();
                });

            }

            OGT.Util.extend(vehicleLegClass, LegBaseClass);


            (function () {


                var journeyLegPartDeviationsTemplate = [
                    '<{0} class="deviation__item deviation__header message warning">',
                    '<div class="clearfix icon">',
                    '<div class="deviation__item deviation__item--icon">' +
                    OGT.Util.OGT.Format.stackedIcon({ graphics: "warning" }) +
                    '</div>',
                    '</div>',
                    '<div class="clearfix text">',
                    '<div class="deviation__item deviation__item--header">{1} </div>',
                    '<div class="deviation__item deviation__item--description">{2} </div>',
                    '<div class="deviation__item deviation__item--subtext"><span>{3} </div>',
                    '</div>',
                    '</{0}>'
                ].join('');

                vehicleLegClass.prototype._addDeviations = function () {

                    OGT.Util.OGT.Journey.getDeviations([this.routelink]).forEach(function (deviationData) {
                        this._addDeviation(deviationData);
                    }, this);

                };

                vehicleLegClass.prototype._addDeviation = function (deviationData) {

                    var details = deviationData.details;

                    this._makePushGet$LegPart(
                        "deviations",
                        '',
                        '',
                        utilNs.String.format(
                            journeyLegPartDeviationsTemplate,
                            details ? "div" : "span",
                            deviationData.header,
                            deviationData.details,
                            deviationData.scopes)
                    );

                };

            }());


            (function () {

                var journeyLegPartCallTripTemplate = [
                    '<div class="journey__calltrip">',
                    '<div class="calltrip__icon">' +
                    OGT.Util.OGT.Format.stackedIcon({ graphics: "infosquare" }) +
                    '</div>',
                    '<div class="calltrip__text">{0}</div>',
                    '</div>'
                ].join('');

                vehicleLegClass.prototype._addCalltrip = function () {
                    var htmlstring = "";
                    var routelink = this.routelink;
                    if (routelink.Line.FootNotes != null) {

                        for (var i = 0; i < routelink.Line.FootNotes.length; i++) {
                            htmlstring += utilNs.String.format(journeyLegPartCallTripTemplate, routelink.Line.FootNotes[i].Text);
                        };

                        this._makePushGet$LegPart(
                            "calltrip",
                            '',
                            '',
                            htmlstring
                        );
                    }
                };

            }());


            (function () {

                var intermediateStopContainerTemplate = [
                    '<div class="container__intermediate-stop">',
                    '</div>'
                ].join(""),
                    intermediateStopTemplate = [
                        '<div class="stop__intermediate travel__instruction clearfix">',
                        '<div class="intermediate__item intermediate__item--time float-left">{0}</div>',
                        '<div class="intermediate__icon"><i class="icon--intermediate icon--big"></i></div><div class="intermediate__item intermediate__item--info float-left notranslate {3}">{1}{2}</div>',
                        '</div>'
                    ].join("");

                vehicleLegClass.prototype._addIntermediateStops = function () {


                    var that = this,
                        routelink = this.routelink,
                        pointsOnRouteLink = routelink.Line.PointsOnRouteLink;
                    if (pointsOnRouteLink && pointsOnRouteLink.length > 1) {

                        var $root = this._makePushGet$LegPart(
                            "intermediate__stops",
                            '',
                            '',
                            ''
                        );

                        OGT.Util.OGT.AsyncAjax.doRequest(OGT.CONSTANTS.AJAX_ENDPOINTS.FS_TRAFFIC_INFO + "/getLineStops", {
                            data: {
                                routelinkKey: routelink.RouteLinkKey,
                                onlyFuture: false
                            },
                            successCallback: function (stopDatas) {

                                that._addAllStopPoints($root,
                                    chopOutPartBetween(stopDatas, routelink.From, routelink.To, routelink.DepDateTime),
                                    OGT.Util.OGT.Format.Realtime.checkIfRealtimeIsOmitted(routelink));
                            }
                        });


                    }

                };

                function chopOutPartBetween(stopDatas, from, to, depDateTime) {

                    // Some routeLinks contain the same stop more than once. Therefore the date must also be taken into account.
                    var isSamePlace = OGT.Util.OGT.isSamePlace,
                        indexFirst = stopDatas.findIndex(function (stopData) {
                            return isSamePlace(from, stopData.Place, true) && (depDateTime == stopData.DepDateTime);
                        }) + 1;


                    stopDatas = stopDatas.slice(indexFirst);

                    var indexLast = stopDatas.findIndex(function (stopData) {
                        return isSamePlace(to, stopData.Place, true);
                    });

                    return stopDatas.slice(0, indexLast);

                }


                vehicleLegClass.prototype._addAllStopPoints = function ($root, stopDatas, timeIsOmitted) {

                    var $detailsDom = $(intermediateStopContainerTemplate).appendTo($root);

                    stopDatas.forEach(function (point) {

                        var place = point.Place;
                        var isCanceled = OGT.Util.OGT.Format.Realtime.getRouteOrPointLinkIsCanceled(point);

                        $detailsDom.append(utilNs.String.format(
                            intermediateStopTemplate,
                            OGT.Util.OGT.Format.Realtime.formattedDepArr({
                                routelinkDep: point,
                                alwaysShowRealTimeIcon: false,
                                isStopPoint: true,
                                isPointOnTimeOmittedRoutelink: timeIsOmitted
                            }),
                            OGT.Util.Html.spanifyOrLinkify("", place.PlaceName, OGT.Util.OGT.getStopPageUrl(place)),
                            isCanceled ? OGT.Util.OGT.Format.Realtime.canceledPointTemplate : "",
                            isCanceled ? OGT.Util.OGT.Format.Realtime.canceledDepartmentTntermediateItemName : ""
                        ));
                    });

                    $detailsDom.find('a').click(function (event) {
                        event.stopPropagation();
                    });

                };

            }());


            vehicleLegClass.prototype._addZooms = function () {

                var that = this,
                    $journeyLegDetail = this.$journeyLegDetail,
                    routelink = this.routelink,
                    previousRoutelink = routelink.ogtPrevRoutelink,
                    nextRoutelink = routelink.ogtNextRoutelink;


                $(journeyZoomMapLegTemplate).appendTo($journeyLegDetail).click(function (event) {
                    event.stopPropagation();
                    event.preventDefault();

                    OGT.Tracking.Map.ZoomOnLeg.Vehicle();

                    that.parent.zoomTo(that.index, [routelink.From, routelink.To]);
                });


                if (!previousRoutelink) {

                    $(utilNs.String.format(journeyZoomMapSwitchTemplate, "top", languageTranslate("/searchjourney/zoomto/switch"))).appendTo($journeyLegDetail).click(function (event) {
                        event.stopPropagation();
                        event.preventDefault();

                        OGT.Tracking.Map.ZoomOnSwitch.Switch();

                        that.parent.zoomTo(null, [routelink.From]);
                    });

                }

                if (nextRoutelink) {

                    if (nextRoutelink.ogtType == "vehicle") {
                        $(utilNs.String.format(journeyZoomMapSwitchTemplate, "bottom", languageTranslate("/searchjourney/zoomto/switch"))).appendTo($journeyLegDetail).click(function (event) {
                            event.stopPropagation();
                            event.preventDefault();

                            OGT.Tracking.Map.ZoomOnSwitch.Switch();

                            that.parent.zoomTo(that.index + 1, [routelink.To, nextRoutelink.From]);
                        });
                    }

                }

            };

            return vehicleLegClass;

        }());


        return function (withDetails, isForMap) {

            return function ($parent, journeyData) {

                var routelinks = journeyData.Routelinks,
                    lastIndex = routelinks.length - 1,
                    odd = true,
                    $root = $('<div class="clearfix"></div>').appendTo($parent);

                routelinks.forEach(function (routelink, index) {

                    var isFirst = index === 0,
                        isLast = index === lastIndex,
                        options = {
                            routelink: routelink,
                            $root: $root,
                            index: index,
                            isFirst: isFirst,
                            isLast: isLast,
                            odd: odd,
                            withDetails: withDetails,
                            isForMap: isForMap
                        };

                    switch (routelink.ogtType) {
                        case "vehicle":
                            new VehicleLegClass(this, options);
                            odd = !odd;
                            break;
                        case "walkFromAndToAndAllTheWay":
                            new WalkToAndFromAndAllTheWayLegClass(this, options);
                            odd = !odd;
                            break;
                        case "walkSignificantly":
                            new WalkSignificantlyLegClass(this, options);
                            odd = !odd;
                            break;
                    }

                }, this);

                return $root;

            };

        }

    }());

    function renderJourney(journeyData, options) {
        var journey = new JourneyClass(journeyData, options);

        var localResultDiv = options.targetElement,
            journeyDiv = journey.div,
            spliceResult = OGT.Util.Splice.spliceInElementSorted(localResultDiv, journeyDiv, function (toInsertElement, otherElement) {

                var toInsertJourneyObject = toInsertElement.backReference,
                    otherElementJourneyObject = otherElement.backReference;

                if (otherElementJourneyObject.invalid) {
                    return null;
                }

                if (toInsertJourneyObject.getKey() == otherElementJourneyObject.getKey()) {
                    return false;
                }

                if (options.useListJourneyHeader || otherElementJourneyObject.getDepartureDateTime() < toInsertJourneyObject.getDepartureDateTime()) {
                    return true;
                }

                return null;

            });

        if (spliceResult !== true) {
            return;
        }


        //Insert a separator if needed
        if (options.useListJourneyHeader) {
            new ListJourneyHeaderClass(journeyDiv.backReference, journeyDiv);
        } else {
            var newDepartureDate = journeyDiv.backReference.getDepartureDateTime(),
                searchedDate = Date.fromSwedishDate(options.journeySearchResult.Date, true),
                previousDiv = journeyDiv.previousSibling,
                searchedDateIsSameAsNewDepartureDate = !searchedDate || searchedDate.isSameDayAs(newDepartureDate);
            if (previousDiv) {
                var previousJourneyObject = previousDiv.backReference;
                // If it has journeyData it must be a journey, not a label
                if (previousJourneyObject.journeyData) {
                    var previousDepartureDate = previousJourneyObject.getDepartureDateTime();
                    if (!newDepartureDate.isSameDayAs(previousDepartureDate)) { //Do add a label
                        if (previousDepartureDate < searchedDate && searchedDate < newDepartureDate && !searchedDateIsSameAsNewDepartureDate) { // The previous and the new spans the searched date, while neither of them is on the searched date.
                            new NoJourneySeparatorClass(searchedDate, journeyDiv, options);
                        }
                        new DaySeparatorClass(newDepartureDate, journeyDiv, options);
                    }
                }
            } else {
                if (searchedDate && !(searchedDateIsSameAsNewDepartureDate && searchedDate.isSameDayAs(Date.adjusted()))) {
                    if (localResultDiv.childNodes.length < 2 && !searchedDateIsSameAsNewDepartureDate && searchedDate < newDepartureDate) { // The list was empty, and we did not find a journey on the searched date
                        new NoJourneySeparatorClass(searchedDate, journeyDiv, options);
                    }
                    new DaySeparatorClass(newDepartureDate, journeyDiv, options);
                }
            }
        }


    }
    // (Accessibility/Screenreader) 
    // Remove tabindexing on links that are inside the map partialview.
    // Apparently some of the links are injected at a later point in time than others (the "Terms of Use" link for example),
    // so we have to wait and check for these links to be loaded into the map before disabling the tabindex for them.
    function removeTabIndexingForMapPartial() {

        document.querySelectorAll('#map__target--portion').forEach(function (item) {
            item.setAttribute('aria-hidden', 'true');
        });

        var timesRun = 0;
        var checkIfLinksInMapAreLoaded = setInterval(function () {

            timesRun += 1;

            // Timeout after 5 checks.
            if (timesRun == 5) {
                clearInterval(checkIfLinksInMapAreLoaded);
            }

            if (document.querySelectorAll('#map__target--portion a').length) {
                document.querySelectorAll('#map__target--portion a').forEach(function (item) {
                    if (item.href.indexOf("/help/terms_maps.html") > -1) {

                        [].slice.apply(document.querySelectorAll('#map__target--portion a')).forEach(function (item) {
                            item.setAttribute('tabindex', '-1');
                        });

                        clearInterval(checkIfLinksInMapAreLoaded);
                    }
                });
            }
        }, 1000); // check every 1000ms
    }

    function renderJourneySearchResults(journeySearchResult, options) {

        options = options || {};

        if (extraButtonOnEachJourneyOptions) {
            options.extraButtonOnEachJourneyOptions = extraButtonOnEachJourneyOptions;
        }

        var targetElement = options.targetElement;

        var journeyDatas = journeySearchResult && journeySearchResult.Journeys;

        if (journeyDatas && journeyDatas.length) {

            options.journeySearchResult = journeySearchResult;

            journeyDatas.forEach(function (journeyData) {
                renderJourney(journeyData, options);
            }, this);

        } else {

            clearResultList(targetElement);

        }

    }

    function clearResultList(targetElement) {

        function forAllBackReferences(func) {
            var i = allChildren.length;
            while (i--) {
                var child = allChildren[i];
                if (child) {
                    var backReference = child.backReference;
                    if (backReference) {
                        func(backReference);
                    }
                }
            };

        }

        var allChildren = targetElement && targetElement.childNodes;
        if (allChildren) {

            forAllBackReferences(function (backReference) {
                backReference.invalidate();
            });
            forAllBackReferences(function (backReference) {
                backReference.destroy();
            });

        }

    }


    function connectJourneySearchResults(options) {

        var searchResultTarget = options.searchResultTarget,
            $departuresContainer = options.$departuresContainer;

        journeyProxy.search.listen({
            newValue: function (inValueSearch) {
                var ise = inValueSearch.ise,
                    preParseJourneySearchResult = (ise && specialPreParseJourneySearchFunc) || function (result, callback) { callback(result); };

                preParseJourneySearchResult(ise, function (preParsedResult) {

                    if (preParsedResult && $departuresContainer) {
                        $departuresContainer.hide();
                    }

                    renderJourneySearchResults(preParsedResult, {
                        targetElement: searchResultTarget
                    });

                });
            }
        });

    }


    function connectDepartures(options) {

        var currentFillObject = null,
            $departuresContainer = options.$departuresContainer,
            $departuresList = $departuresContainer.find(".departures__result-list"),
            trafficTypesIo = journeyProxy.trafficTypes,
            searchIo = journeyProxy.search;

        if ($departuresContainer.length) {

            var $stopUpcoming = $departuresContainer.find(".stop--upcoming"),
                $stopUpcomingText = $stopUpcoming.find('.stop--upcoming-text');

            function aggFunc(acc, ttg) {
                return acc + ttg.aggregatedId;
            }

            function showUpcomingDepartures() {

                var stop = startIo.get(),
                    stopId = stop.Id,
                    selectedTrafficTypesAggregatedIds = trafficTypesIo.get(),
                    selectedTrafficTypes = window.trafficTypeJourneySearchGroups.filter(function (ttg) { return selectedTrafficTypesAggregatedIds.includes(ttg.aggregatedId); });

                if (currentFillObject &&
                    currentFillObject.stopId == stopId &&
                    currentFillObject.trafficTypeGroups.reduce(aggFunc, 0) === selectedTrafficTypes.reduce(aggFunc, 0)) {
                    return;
                }

                hideUpcomingDepartures();

                $stopUpcoming.click(function (event) {

                    event.stopPropagation();
                    location.href = OGT.Util.OGT.getStopPageUrl(stop);

                });
                $stopUpcomingText.text(stop.PlaceName);


                currentFillObject = OGT.UpcomingDepartures.fill({
                    $container: $departuresContainer,
                    $list: $departuresList,
                    stopId: stopId,
                    departuresPerColumnCount: 8,
                    isInteractive: true,
                    noComingStopsFunctionality: false,
                    trafficTypeGroups: selectedTrafficTypes,
                    trackingPointFragment: "JourneySearchForm"
                });

                errorIo.set(null);

            }

            function hideUpcomingDepartures() {

                if (currentFillObject) {
                    currentFillObject.close();
                    currentFillObject = null;
                }

            }


            function startAndEndAndTrafficTypeAndSearchListener() {

                var startPlace = startIo.get(),
                    endPlace = endIo.get(),
                    searchValue = searchIo.get();

                if (!searchValue.ise && !searchValue.doe.action && startPlace && startPlace.OgtType == "stop" && !endPlace) {
                    showUpcomingDepartures();
                } else {
                    hideUpcomingDepartures();
                }

            }

            startIo.listen({
                newValue: startAndEndAndTrafficTypeAndSearchListener
            });
            endIo.listen({
                newValue: startAndEndAndTrafficTypeAndSearchListener
            });
            trafficTypesIo.listen({
                newValue: startAndEndAndTrafficTypeAndSearchListener
            });
            searchIo.listen({
                newValue: startAndEndAndTrafficTypeAndSearchListener
            });

        }

    };


    $journeySearchResultMasterContainers.each(function () {

        var $journeySearchResultMasterContainer = $(this),
            $journeySearchResultContainer = $journeySearchResultMasterContainer.find(".journey__search__result-container"),
            searchResultTarget = $journeySearchResultContainer.find(".journey__search-result-target").get(0),
            $departuresContainer = $journeySearchResultMasterContainer.find(".departures__upcoming");

        if ($journeySearchResultContainer.length) {

            connectJourneySearchResults({
                searchResultTarget: searchResultTarget,
                $departuresContainer: $departuresContainer
            });

        }

        if ($departuresContainer.length) {

            connectDepartures({
                $departuresContainer: $departuresContainer
            });

        }

    });


    window.OGT.JourneySearchHandler = window.OGT.JourneySearchHandler || {};

    window.OGT.JourneySearchHandler.SearchResultList = {
        renderJourneySearchResults: renderJourneySearchResults,
        renderJourney: renderJourney,
        addButtonToEachJourney: addButtonToEachJourney,
        setPreParseJourneySearchResultFunc: setPreParseJourneySearchResultFunc,
        getRenderOverviewOrDetailsFunc: getRenderOverviewOrDetailsFunc

    };

}());;
