/*
 * The qunit-runner has issues with finding WRM unless we do this here, because codemirror requires it indirectly. It's not ideal,
 * but let's go with that this is test code and this solution is the easy way out without adverse impacts
 */
window.WRM = {
    data: {
        claim: function (key) {
            if (key === 'com.atlassian.bitbucket.server.config-wrm-data:attachment.upload.max.size.data') {
                return {
                    value: '10485760',
                    type: 'NUMBER'
                };
            }
        }
    }
};

define([
    'codemirror',
    'jquery',
    'lodash',
    'Squire',
    'test-helper',
    'bitbucket/util/events',
    'bitbucket/internal/feature/comments'
], function(
    CodeMirror,
    $,
    _,
    Squire,
    testHelper,
    events,
    comments
) {

    'use strict';

    // This is a bit nasty, but trying to load codemirror/meta.js via Squire
    // was nastier. This doesn't have any material impact on the tests.
    CodeMirror.findModeByExtension = sinon.stub().returns({});

    var injector = new Squire();
    injector.setDefaultDeps(['bitbucket/internal/feature/file-content/side-by-side-diff-view']);
    injector.mock('codemirror', CodeMirror);
    injector.mock('bitbucket/util/events', events);
    injector.mock('bitbucket/internal/feature/file-content/diff-hunk-map', function () {
        return function() {
            this.redraw = $.noop;
            this.diffScrolled = $.noop;
            this.destroy = $.noop;
        };
    });

    testHelper.setTemplates({
        'bitbucket.internal.feature.fileContent.conflictMessage': '<div class="CONFLICT"></div>',
        'bitbucket.internal.feature.fileContent.sideBySideDiffView.layout': '<div class="side-by-side-diff-editor-from"></div><div class="segment-connector-column" ></div><div class="side-by-side-diff-editor-to"></div>',
        'bitbucket.internal.feature.fileContent.diffView.lineNumber': '<div class="line-number"></div>',
        'bitbucket.internal.feature.fileContent.diffView.lineNumberMarker': '<div class="line-number-marker line-locator" data-file-type="" data-line-type="" data-line-number=""></div>',
        'bitbucket.internal.widget.icons.icon': ''
    });

    function makeDiffView(test, data, fileChange, SideBySideDiffView) {
        test.diffView && test.diffView.destroy();
        test.$f.empty();
        test.diff = data.diff;
        test.fileChange = fileChange;
        test.diffView = new SideBySideDiffView(data, {
            commentMode : comments.commentMode.NONE,
            $container : test.$f,
            fileChange : fileChange
        });
        // _requestWindowScrolls() is not being used in the test - we need to resolve the scroll manually
        test.diffView._requestWindowScrolls = function() {
            return $.Deferred().reject();
        };
        test.diffView.init();
        return test.diffView;
    }

    module('side-by-side-diff-view', {
        setup: function() {
            this.$f = $('#qunit-fixture');
            sinon.stub(require('bitbucket/internal/util/performance'), 'frameBatchedMap', function(fn, batchLimit, runBatch) {
                return function(arr) {
                    var deferred = $.Deferred();
                    var out;
                    runBatch(function() {
                        out = _.map(arr, fn);
                    });
                    return deferred.resolve(out);
                };
            });
            window.WRM = testHelper.mockWRM({
                'com.atlassian.bitbucket.server.config-wrm-data:attachment.upload.max.size.data': '10485760'
            });
            this._events = events.chain();
        },
        teardown: function() {
            this.diffView && this.diffView.destroy();
            this.diffView = null;
            this.diff = null;
            this.fileChange = null;
            this.$f = null;
            require('bitbucket/internal/util/performance').frameBatchedMap.restore();
            this._events.destroy();
        }
    });

    var nbsp = '\xa0';

    injector.test('Has correct lines', function (assert, SideBySideDiffView) {
        var done = assert.async();
        var self = this;
        var diffView = makeDiffView(this, getDiffJSON(), getFileChange(), SideBySideDiffView);

        // diff-view related tests require a setTimeout to wait at least one event loop so that
        // CodeMirror correctly renders all the items. Without this setTimeout the assertions will
        // fail due to elements being missing.
        setTimeout(function () {
            var $fromLines = self.$f.find('.side-by-side-diff-editor-from .CodeMirror-code > *');
            var $toLines = self.$f.find('.side-by-side-diff-editor-to .CodeMirror-code > *');
            var expectedLines = _.chain(self.diff.hunks[0].segments)
                .map(function(s) {
                    var type;
                    switch(s.type) {
                        case 'CONTEXT': type = 'context'; break;
                        case 'ADDED': type = 'added'; break;
                        case 'REMOVED': type = 'removed'; break;
                    }
                    return _.map(s.lines, function(l) {
                        return {
                            from : (s.type === 'ADDED' ? nbsp : l.source) + '',
                            to : (s.type === 'REMOVED' ? nbsp : l.destination) + '',
                            lineType : s.type,
                            text : l.line,
                            type : type,
                            isMarker : l.conflictMarker === 'MARKER',
                            isOurs : l.conflictMarker === 'OURS',
                            isTheirs : l.conflictMarker === 'THEIRS'
                        };
                    });
                })
                .flatten()
                .value()
                .reduce(function(expectedLines, line) {
                    if (line.lineType !== 'ADDED') {
                        expectedLines.FROM.push(line);
                    }
                    if (line.lineType !== 'REMOVED') {
                        expectedLines.TO.push(line);
                    }
                    return expectedLines;
                }, { FROM : [], TO : [] });

            assert.equal($fromLines.length, expectedLines.FROM.length, 'Same number of FROM lines as expected.');
            assert.equal($toLines.length, expectedLines.TO.length, 'Same number of TO lines as expected.');

            function getLineChecker(fileType, $lines) {
                var fileTypeLower = fileType.toLowerCase();
                return function eachLine(expectedLine, i) {
                    var ith = i + (i > 10 && i < 14 ? 'th' : {1:'st', 2:'nd',3:'rd'}[i % 10] || 'th'); // Sorry, it bothers me.
                    var $line = $lines.eq(i);
                    assert.ok($line.hasClass(expectedLine.type), 'The ' + ith + ' line in ' + fileType + ' should be the type "' + expectedLine.type + '"');

                    // NOTE: CodeMirror adds a zero-width space to empty lines and handles it transparently without affecting the document.
                    // NOTE: So we're testing a leaky abstraction here:
                    var zeroWidthSpace = "\u200B";
                    assert.equal($line.find('pre').text(), expectedLine.text || zeroWidthSpace, 'The ' + ith + ' line text in ' + fileType + ' should be "' + expectedLine.text + '"');

                    assert.strictEqual($line.find('.line-number-' + fileTypeLower).text(), expectedLine[fileTypeLower], 'The ' + ith + ' line number in ' + fileType + 'is correct.');
                    assert.strictEqual($line.find('.line-number-marker').attr('data-line-type'), expectedLine.lineType, 'The ' + ith + ' line type in ' + fileType + 'is correct.');

                    assert.strictEqual($line.hasClass('conflict-marker'), expectedLine.isMarker, 'The ' + ith + ' line text conflict markerness in ' + fileType + 'is correct.');
                    assert.strictEqual($line.hasClass('conflict-ours'), expectedLine.isOurs, 'The ' + ith + ' line text conflict oursness in ' + fileType + 'is correct.');
                    assert.strictEqual($line.hasClass('conflict-theirs'), expectedLine.isTheirs, 'The ' + ith + ' line text conflict theirsness in ' + fileType + 'is correct.');
                };
            }

            _.forEach(expectedLines.FROM, getLineChecker('FROM', $fromLines));
            _.forEach(expectedLines.TO, getLineChecker('TO', $toLines));
            done();
        }, 10);
    });

    injector.test('Events', function (assert, SideBySideDiffView) {
        assert.expect(6);
        var dones = [assert.async(), assert.async(), assert.async()];

        this._events.on('bitbucket.internal.feature.fileContent.diffViewDataLoaded', function(data) {
            // deprecated
            assert.ok(true, 'diffViewDataLoaded event triggered');
            assert.ok(this === window || this === null, 'context is null');
            dones[0]();
        }).on('bitbucket.internal.feature.fileContent.diffViewContentLoaded', function(data) {
            assert.ok(true, 'diffViewContentLoaded event triggered');
            assert.ok(this === window || this === null, 'context is null');
            dones[1]();
        }).on('bitbucket.internal.feature.fileContent.diffViewContentChanged', function(data) {
            assert.ok(true, 'diffViewContentChanged event triggered');
            assert.ok(this === window || this === null, 'context is null');
            dones[2]();
        });

        makeDiffView(this, getDiffJSON(), getFileChange(), SideBySideDiffView);
    });

    injector.test('Ediff regions', function (assert, SideBySideDiffView) {
        var self = this;
        var done = assert.async();
        var ediffTestJSON = {
            diff: {
                "hunks": [{
                    "sourceLine": 1, "sourceSpan": 10, "destinationLine": 1, "destinationSpan": 10,
                    "segments"  : [
                        {
                            "type"    : "REMOVED", "lines": [
                            {"source": 1, "destination": 1, "line": "Line 1", "truncated": false},
                            {
                                "source"     : 2,
                                "destination": 2,
                                "line"       : "Line 2 Hello World",
                                "truncated"  : false,
                                changed      : ['Line'],
                                deleted      : [' World']
                            },
                            {"source": 3, "destination": 3, "line": "This will be a", "truncated": false},
                            {"source": 4, "destination": 4, "line": "multi-line-change", "truncated": false},
                            {
                                "source"     : 5,
                                "destination": 6,
                                "line"       : "A line (with)(more) [complex] marking()() because-word_boundaries.can/be\\different",
                                "truncated"  : false,
                                changed      : ['with', 'more', 'complex', 'because', 'word_boundaries.', '/', '\\'],
                                deleted      : ['()']
                            }
                        ], "truncated": false
                        },
                        {
                            "type"    : "ADDED", "lines": [
                            {
                                "source"     : 1,
                                "destination": 1,
                                "line"       : "This is Line 1",
                                "truncated"  : false,
                                added        : ['This is ']
                            },
                            {
                                "source"     : 2,
                                "destination": 2,
                                "line"       : "Number 2 Hello",
                                "truncated"  : false,
                                changed      : ['Number']
                            },
                            {"source": 3, "destination": 3, "line": "This will be a", "truncated": false},
                            {
                                "source"     : 4,
                                "destination": 4,
                                "line"       : "multi-line-change going",
                                "truncated"  : false,
                                added        : [' going']
                            },
                            {
                                "source"     : 5,
                                "destination": 5,
                                "line"       : "over more than 1 line",
                                "truncated"  : false,
                                added        : ['over more than 1 line']
                            },
                            {
                                "source"     : 6,
                                "destination": 6,
                                "line"       : "A line (that has)(some) [complicated] marking(hi) because_word-boundaries'can.be/different",
                                "truncated"  : false,
                                added        : ['hi'],
                                changed      : ['that has', 'some', 'complicated', 'because_word', 'boundaries\'', '.', '/']
                            }
                        ], "truncated": false
                        }
                    ]
                }]
            }
        };

        var diffView = makeDiffView(this, ediffTestJSON, getFileChange(), SideBySideDiffView);

        // diff-view related tests require a setTimeout to wait at least one event loop so that
        // CodeMirror correctly renders all the items. Without this setTimeout the assertions will
        // fail due to elements being missing.
        setTimeout(function () {
            var fromIndex = 0;
            var toIndex = 0;
            var $fromLines = self.$f.find('.side-by-side-diff-editor-from .CodeMirror-code > *');
            var $toLines = self.$f.find('.side-by-side-diff-editor-to .CodeMirror-code > *');
            // go through the lines and check the contain the expected regions
            _.each(ediffTestJSON.diff.hunks[0].segments, function(segment) {
                // go through the lines of this segment
                if (segment.type !== 'ADDED') {
                    _.each(segment.lines, function(line) {
                        compareTokensForLine(line, $fromLines.eq(fromIndex), fromIndex + ' in FROM', assert);
                        fromIndex++;
                    });
                }
                if (segment.type !== 'REMOVED') {
                    _.each(segment.lines, function(line) {
                        compareTokensForLine(line, $toLines.eq(toIndex), toIndex + ' in TO', assert);
                        toIndex++;
                    });
                }
            });
            done();
        }, 10);
    });

    function getTextArrayForEdiffMarkers($line, className) {
        var items = [];
        _.forEach($line.find(className), function(item) {
            items.push(item.innerText);
        });
        items = items.length ? items : undefined;
        return items;
    }

    function compareTokensForLine(line, $line, lineDescription, assert) {
        //get the deleted, changed and added items in a line of code in the editor
        var changed = getTextArrayForEdiffMarkers($line, '.ediff-change');
        var deleted = getTextArrayForEdiffMarkers($line, '.ediff-delete');
        var added = getTextArrayForEdiffMarkers($line, '.ediff-add');

        assert.deepEqual(line.changed, changed, 'Changed markers for line ' + lineDescription + ' match.');
        assert.deepEqual(line.deleted, deleted, 'Deleted markers for line ' + lineDescription + ' match.');
        assert.deepEqual(line.added, added, 'Added markers for line ' + lineDescription + ' match.');
    }


    // NOTE paired with getDiffJSON below
    function getFileChange() {
        return {
            commitRange :  {
                untilRevision : {
                    'id' : "6e4aec51ba340be0cfe4e7d657a1449843584ad2",
                    'displayId' : "6e4aec5",
                    'repository' : {
                        "slug":"stash",
                        "id":2416,
                        "name":"stash",
                        "scmId":"git",
                        "state":"AVAILABLE",
                        "statusMessage":"Available",
                        "forkable":true,
                        "project":{
                            "key":"STASH",
                            "id":2443,
                            "name":"Stash",
                            "description":"Source, Source, Baby!\r\n\r\nPlease don't create test repositories in this project! \\t\\n\\r",
                            "public":false,
                            "type":"NORMAL",
                            "link":{"url":"/projects/STASH","rel":"self"},
                            "avatarUrl":"/projects/STASH/avatar.png?s=64"
                        },
                        "public":false,
                        "cloneUrl":"https://aahmed@stash.dev.internal.atlassian.com/scm/stash/stash.git",
                        "link":{"url":"/projects/STASH/repos/stash/browse","rel":"self"}
                    },
                    'message' : "Something",
                    'author' : {},
                    'authorTimestamp' : 0,
                    'parents' : null,
                    'attributes' : null
                },
                sinceRevision : {
                    'id' : "269beebf313143200f2b51561e5f84e04cf5bd92",
                    'displayId' : "269beeb",
                    'repository' : {
                        "slug":"stash",
                        "id":2416,
                        "name":"stash",
                        "scmId":"git",
                        "state":"AVAILABLE",
                        "statusMessage":"Available",
                        "forkable":true,
                        "project":{
                            "key":"STASH",
                            "id":2443,
                            "name":"Stash",
                            "description":"Source, Source, Baby!\r\n\r\nPlease don't create test repositories in this project! \\t\\n\\r",
                            "public":false,
                            "type":"NORMAL",
                            "link":{"url":"/projects/STASH","rel":"self"},
                            "avatarUrl":"/projects/STASH/avatar.png?s=64"
                        },
                        "public":false,
                        "cloneUrl":"https://aahmed@stash.dev.internal.atlassian.com/scm/stash/stash.git",
                        "link":{"url":"/projects/STASH/repos/stash/browse","rel":"self"}
                    },
                    'message' : "Something",
                    'author' : {},
                    'authorTimestamp' : 0,
                    'parents' : null,
                    'attributes' : null
                }
            },
            srcPath : null,
            path : {
                components: ["plugins","ssh","src","main","resources","atlassian-plugin.xml"],
                name: "atlassian-plugin.xml",
                extension: "xml"
            },
            type : "MODIFY",
            nodeType: "FILE",
            conflict: {
                ourChange : {
                    "srcPath" : null,
                    "path" : "plugins/ssh/src/main/resources/atlassian-plugin.xml",
                    "type" : "MODIFY"
                },
                theirChange : {
                    "srcPath" : null,
                    "path" : "plugins/ssh/src/main/resources/atlassian-plugin.xml",
                    "type" : "MODIFY"
                }
            },
            diff : null,
            srcExecutable : false,
            executable : false,
            repository : {
                project : { key: 'PROJ'},
                slug : 'REPO'
            }
        };
    }

    // NOTE paired with getFileChange above
    function getDiffJSON() {
        return {
            diff: {
                //FIXME remove isLastPage when swithcing to unified-diff-view which doesn't need this hack
                isLastPage          : true,
                relevantContextLines: 10,
                "source"            : {
                    "components": ["plugins", "ssh", "src", "main", "resources", "atlassian-plugin.xml"],
                    "parent"    : "plugins/ssh/src/main/resources",
                    "name"      : "atlassian-plugin.xml",
                    "extension" : "xml",
                    "toString"  : "plugins/ssh/src/main/resources/atlassian-plugin.xml"
                },
                "destination"       : {
                    "components": ["plugins", "ssh", "src", "main", "resources", "atlassian-plugin.xml"],
                    "parent"    : "plugins/ssh/src/main/resources",
                    "name"      : "atlassian-plugin.xml",
                    "extension" : "xml",
                    "toString"  : "plugins/ssh/src/main/resources/atlassian-plugin.xml"
                },
                "hunks"             : [{
                    "sourceLine"     : 4,
                    "sourceSpan"     : 24,
                    "destinationLine": 4,
                    "destinationSpan": 34,
                    "segments"       : [{
                        "type"     : "CONTEXT",
                        "lines"    : [{
                            "source"     : 4,
                            "destination": 4,
                            "line"       : "        <version>${project.version}</version>",
                            "truncated"  : false
                        }, {
                            "source"     : 5,
                            "destination": 5,
                            "line"       : "        <vendor name=\"${project.organization.name}\" url=\"${project.organization.url}\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 6,
                            "destination": 6,
                            "line"       : "    </plugin-info>",
                            "truncated"  : false
                        }, {
                            "source"     : 7,
                            "destination": 7,
                            "line"       : "",
                            "truncated"  : false
                        }, {
                            "source"     : 8,
                            "destination": 8,
                            "line"       : "    <!-- I18N -->",
                            "truncated"  : false
                        }, {
                            "source"     : 9,
                            "destination": 9,
                            "line"       : "",
                            "truncated"  : false
                        }, {
                            "source"     : 10,
                            "destination": 10,
                            "line"       : "    <resource type=\"i18n\" name=\"i18n\" location=\"i18n.messages\"/>",
                            "truncated"  : false
                        }, {
                            "source"     : 11,
                            "destination": 11,
                            "line"       : "",
                            "truncated"  : false
                        }, {
                            "source"     : 12,
                            "destination": 12,
                            "line"       : "    <!-- Component Imports -->",
                            "truncated"  : false
                        }, {
                            "source"     : 13,
                            "destination": 13,
                            "line"       : "",
                            "truncated"  : false
                        }],
                        "truncated": false
                    }, {
                        "type"     : "REMOVED",
                        "lines"    : [{
                            "source"     : 14,
                            "destination": 14,
                            "line"       : "    <component-import key=\"applicationPropertiesService\" interface=\"com.atlassian.stash.server.ApplicationPropertiesService\"/>",
                            "truncated"  : false
                        }],
                        "truncated": false
                    }, {
                        "type"     : "CONTEXT",
                        "lines"    : [{
                            "source"     : 15,
                            "destination": 14,
                            "line"       : "    <component-import key=\"activeObjects\" interface=\"com.atlassian.activeobjects.external.ActiveObjects\"/>",
                            "truncated"  : false
                        }],
                        "truncated": false
                    }, {
                        "type"     : "ADDED",
                        "lines"    : [{
                            "source"     : 16,
                            "destination": 15,
                            "line"       : "    <component-import key=\"applicationPropertiesService\" interface=\"com.atlassian.stash.server.ApplicationPropertiesService\"/>",
                            "truncated"  : false
                        }, {
                            "source"     : 16,
                            "destination": 16,
                            "line"       : "    <component-import key=\"authenticationController\" interface=\"com.atlassian.sal.api.auth.AuthenticationController\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 16,
                            "destination": 17,
                            "line"       : "    <component-import key=\"eventPublisher\" interface=\"com.atlassian.event.api.EventPublisher\"/>",
                            "truncated"  : false
                        }],
                        "truncated": false
                    }, {
                        "type"     : "CONTEXT",
                        "lines"    : [{
                            "source"     : 16,
                            "destination": 18,
                            "line"       : "    <component-import key=\"executorService\" interface=\"java.util.concurrent.ExecutorService\"/>",
                            "truncated"  : false
                        }],
                        "truncated": false
                    }, {
                        "type"     : "ADDED",
                        "lines"    : [{
                            "source"        : 17,
                            "destination"   : 19,
                            "line"          : "<<<<<<<",
                            "truncated"     : false,
                            "conflictMarker": "MARKER"
                        }],
                        "truncated": false
                    }, {
                        "type"     : "CONTEXT",
                        "lines"    : [{
                            "source"        : 17,
                            "destination"   : 20,
                            "line"          : "    <component-import key=\"velocityRenderer\" interface=\"com.atlassian.templaterenderer.velocity.one.six.VelocityTemplateRenderer\" />",
                            "truncated"     : false,
                            "conflictMarker": "OURS"
                        }, {
                            "source"        : 18,
                            "destination"   : 21,
                            "line"          : "    <component-import key=\"pageBuilderService\" interface=\"com.atlassian.webresource.api.assembler.PageBuilderService\"/>",
                            "truncated"     : false,
                            "conflictMarker": "OURS"
                        }],
                        "truncated": false
                    }, {
                        "type"     : "ADDED",
                        "lines"    : [{
                            "source"        : 19,
                            "destination"   : 22,
                            "line"          : "=======",
                            "truncated"     : false,
                            "conflictMarker": "MARKER"
                        }, {
                            "source"        : 19,
                            "destination"   : 23,
                            "line"          : "    <component-import key=\"i18nService\" interface=\"com.atlassian.stash.i18n.I18nService\" />",
                            "truncated"     : false,
                            "conflictMarker": "THEIRS"
                        }, {
                            "source"        : 19,
                            "destination"   : 24,
                            "line"          : "    <component-import key=\"loginUriProvider\" interface=\"com.atlassian.sal.api.auth.LoginUriProvider\" />",
                            "truncated"     : false,
                            "conflictMarker": "THEIRS"
                        }, {
                            "source"        : 19,
                            "destination"   : 25,
                            "line"          : "    <component-import key=\"navBuilder\" interface=\"com.atlassian.stash.nav.NavBuilder\" />",
                            "truncated"     : false,
                            "conflictMarker": "THEIRS"
                        }, {
                            "source"        : 19,
                            "destination"   : 26,
                            "line"          : "    <component-import key=\"permissionService\" interface=\"com.atlassian.stash.user.PermissionService\" />",
                            "truncated"     : false,
                            "conflictMarker": "THEIRS"
                        }, {
                            "source"        : 19,
                            "destination"   : 27,
                            "line"          : "    <component-import key=\"permissionValidationService\" interface=\"com.atlassian.stash.user.PermissionValidationService\" />",
                            "truncated"     : false,
                            "conflictMarker": "THEIRS"
                        }, {
                            "source"        : 19,
                            "destination"   : 28,
                            "line"          : ">>>>>>>",
                            "truncated"     : false,
                            "conflictMarker": "MARKER"
                        }],
                        "truncated": false
                    }, {
                        "type"     : "CONTEXT",
                        "lines"    : [{
                            "source"     : 19,
                            "destination": 29,
                            "line"       : "    <component-import key=\"pluginSettingsFactory\" interface=\"com.atlassian.sal.api.pluginsettings.PluginSettingsFactory\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 20,
                            "destination": 30,
                            "line"       : "    <component-import key=\"pluginAccessor\" interface=\"com.atlassian.plugin.PluginAccessor\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 21,
                            "destination": 31,
                            "line"       : "    <component-import key=\"pluginEventManager\" interface=\"com.atlassian.plugin.event.PluginEventManager\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 22,
                            "destination": 32,
                            "line"       : "    <component-import key=\"transactionTemplate\" interface=\"com.atlassian.sal.api.transaction.TransactionTemplate\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 23,
                            "destination": 33,
                            "line"       : "    <component-import key=\"soyTemplateRenderer\" interface=\"com.atlassian.soy.renderer.SoyTemplateRenderer\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 24,
                            "destination": 34,
                            "line"       : "    <component-import key=\"xsrfTokenAccessor\" interface=\"com.atlassian.sal.api.xsrf.XsrfTokenAccessor\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 25,
                            "destination": 35,
                            "line"       : "    <component-import key=\"xsrfTokenValidator\" interface=\"com.atlassian.sal.api.xsrf.XsrfTokenValidator\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 26,
                            "destination": 36,
                            "line"       : "    <component-import key=\"authenticationController\" interface=\"com.atlassian.sal.api.auth.AuthenticationController\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 27,
                            "destination": 37,
                            "line"       : "    <component-import key=\"loginUriProvider\" interface=\"com.atlassian.sal.api.auth.LoginUriProvider\" />",
                            "truncated"  : false
                        }, {
                            "source"     : 28,
                            "destination": 38,
                            "line"       : "    <component-import key=\"navBuilder\" interface=\"com.atlassian.stash.nav.NavBuilder\" />",
                            "truncated"  : false
                        }],
                        "truncated": false
                    }],
                    "truncated"      : false
                }],
                "truncated"         : false
            }
        };
    }

});
