<commit_before>var expect = require('expect.js');

var request = require('../../../../lib/request');
var file = require('../../../../lib/actions/file');

describe('actions/file', function() {
    var requestOptions;
    var bogusRequestModule;
    var response;
    var actualResponse;
    var errorMessage;
    var b2;
    var options;

    beforeEach(function() {
        errorMessage = undefined;
        actualResponse = undefined;

        b2 = {
            accountId: '98765',
            authorizationToken: 'unicorns and rainbows',
            apiUrl: 'https://foo',
            downloadUrl: 'https://download'
        };

        bogusRequestModule = function(options, cb) {
            requestOptions = options;
            cb(errorMessage, false, JSON.stringify(response));
        };

        request.setup(bogusRequestModule);
    });

    describe('uploadFile', function() {

        beforeEach(function() {
            options = {
                uploadUrl: 'https://uploadUrl',
                uploadAuthToken: 'uploadauthtoken',
                filename: 'foo.txt',
                data: 'some text file content'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.uploadFile(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://uploadUrl',
                    method: 'POST',
                    headers:
                    { Authorization: 'uploadauthtoken',
                        'Content-Type': 'b2/x-auto',
                        'X-Bz-File-Name': 'foo.txt',
                        'X-Bz-Content-Sha1': '332e7f863695677895a406aff6d60acf7e84ea22' },
                    body: 'some text file content'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.uploadFile(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

        describe('with info headers', function() {

            beforeEach(function(done) {
                options = {
                    uploadUrl: 'https://uploadUrl',
                    uploadAuthToken: 'uploadauthtoken',
                    filename: 'foo.txt',
                    data: 'some text file content',
                    info: {
                        foo:  'bar',
                        unicorns: 'rainbows'
                    }
                };

                file.uploadFile(b2, options).then(function() {
                    done();
                });
            });

            it('should properly add x-bz-info headers', function() {
                expect(requestOptions).to.eql({
                    url: 'https://uploadUrl',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'uploadauthtoken',
                        'Content-Type': 'b2/x-auto',
                        'X-Bz-File-Name': 'foo.txt',
                        'X-Bz-Content-Sha1': '332e7f863695677895a406aff6d60acf7e84ea22',
                        'X-Bz-Info-foo': 'bar',
                        'X-Bz-Info-unicorns': 'rainbows'
                    },
                    body: 'some text file content'
                });
            });

        });

        describe('with no mime type specified', function() {

            beforeEach(function(done) {
                options = {
                    uploadUrl: 'https://uploadUrl',
                    uploadAuthToken: 'uploadauthtoken',
                    filename: 'foo.txt',
                    data: 'some text file content',
                    info: {
                        foo:  'bar',
                        unicorns: 'rainbows'
                    }
                };

                file.uploadFile(b2, options).then(function() {
                    done();
                });
            });

            it('should default mime-type in headers', function() {
                expect(requestOptions.headers['Content-Type']).to.equal('b2/x-auto');
            });

        });

        describe('with mime type specified', function() {

            beforeEach(function(done) {
                options = {
                    uploadUrl: 'https://uploadUrl',
                    uploadAuthToken: 'uploadauthtoken',
                    filename: 'foo.txt',
                    mime: 'foo/type',
                    data: 'some text file content',
                    info: {
                        foo:  'bar',
                        unicorns: 'rainbows'
                    }
                };

                file.uploadFile(b2, options).then(function() {
                    done();
                });
            });

            it('should properly mime-type in headers', function() {
                expect(requestOptions.headers['Content-Type']).to.equal('foo/type');
            });

        });

    });

    describe('listFileNames', function() {

        beforeEach(function() {
            options = {
                bucketId: '123abc',
                startFileName: 'unicorns.png',
                maxFileCount: 200
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.listFileNames(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_list_file_names',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"bucketId":"123abc","startFileName":"unicorns.png","maxFileCount":200}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.listFileNames(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('listFileVersions', function() {

        beforeEach(function() {
            options = {
                bucketId: '123abc',
                startFileName: 'unicorns.png',
                maxFileCount: 200
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.listFileVersions(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_list_file_versions',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"bucketId":"123abc","startFileName":"unicorns.png","maxFileCount":200}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.listFileVersions(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('hideFile', function() {

        beforeEach(function() {
            options = {
                bucketId: '123abc',
                fileName: 'unicorns-and_rainbows!@#$%^&.png'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.hideFile(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response (filename to be encoded)', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_hide_file',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"bucketId":"123abc","fileName":"unicorns-and_rainbows!%40%23%24%25%5E%26.png"}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.hideFile(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('getFileInfo', function() {

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.getFileInfo(b2, 'abc123').then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_get_file_info',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"fileId":"abc123"}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.getFileInfo(b2, 'abc123').then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('downloadFileByName', function() {

        beforeEach(function() {
            options = {
                bucketName: 'unicornBox',
                fileName: 'unicorns-and_rainbows!@#$%^&.png'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = {
                    headers: {
                        'x-bz-file-id': 'fileIdAbcd1234',
                        'x-bz-file-name': 'unicorns-download.png',
                        'x-bz-content-sha1': 'file_hash'
                    },
                    statusCode: 200
                };

                bogusRequestModule = function(options, cb) {
                    requestOptions = options;
                    cb(errorMessage, response, 'file contents');
                };

                request.setup(bogusRequestModule);

                file.downloadFileByName(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response (filename to be encoded)', function() {
                expect(requestOptions).to.eql({
                    url: 'https://download/file/unicornBox/unicorns-and_rainbows!%40%23%24%25%5E%26.png',
                    encoding: null,
                    headers: {
                        Authorization: 'unicorns and rainbows'
                    }
                });
                expect(actualResponse).to.eql({
                    data: 'file contents',
                    fileId: 'fileIdAbcd1234',
                    fileName: 'unicorns-download.png',
                    contentSha1: 'file_hash'
                });
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.downloadFileByName(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('downloadFileById', function() {

        describe('with good response', function() {

            beforeEach(function(done) {
                response = {
                    headers: {
                        'x-bz-file-id': 'fileIdAbcd1234',
                        'x-bz-file-name': 'unicorns-download.png',
                        'x-bz-content-sha1': 'file_hash'
                    },
                    statusCode: 200
                };

                bogusRequestModule = function(options, cb) {
                    requestOptions = options;
                    cb(errorMessage, response, 'file contents');
                };

                request.setup(bogusRequestModule);

                file.downloadFileById(b2, 'abcd1234').then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://download/b2api/v1/b2_download_file_by_id',
                    qs: {
                        fileId: 'abcd1234'
                    },
                    headers: {
                        Authorization: 'unicorns and rainbows'
                    },
                    encoding: null
                });
                expect(actualResponse).to.eql({
                    data: 'file contents',
                    fileId: 'fileIdAbcd1234',
                    fileName: 'unicorns-download.png',
                    contentSha1: 'file_hash'
                });
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.downloadFileById(b2, '1234abcd').then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('deleteFileVersion', function() {

        beforeEach(function() {
            options = {
                fileId: 'abcd1234',
                fileName: 'unicorns-and_rainbows!@#$%^&.png'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.deleteFileVersion(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response  (filename to be encoded)', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_delete_file_version',
                    method: 'POST',
                    headers: {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"fileId":"abcd1234","fileName":"unicorns-and_rainbows!%40%23%24%25%5E%26.png"}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.deleteFileVersion(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

});
<commit_msg>Fixing broken tests<commit_after>var expect = require('expect.js');

var request = require('../../../../lib/request');
var file = require('../../../../lib/actions/file');

describe('actions/file', function() {
    var requestOptions;
    var bogusRequestModule;
    var response;
    var actualResponse;
    var errorMessage;
    var b2;
    var options;

    beforeEach(function() {
        errorMessage = undefined;
        actualResponse = undefined;

        b2 = {
            accountId: '98765',
            authorizationToken: 'unicorns and rainbows',
            apiUrl: 'https://foo',
            downloadUrl: 'https://download'
        };

        bogusRequestModule = function(options, cb) {
            requestOptions = options;
            cb(errorMessage, false, JSON.stringify(response));
        };

        request.setup(bogusRequestModule);
    });

    describe('uploadFile', function() {

        beforeEach(function() {
            options = {
                uploadUrl: 'https://uploadUrl',
                uploadAuthToken: 'uploadauthtoken',
                filename: 'foo.txt',
                data: 'some text file content'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.uploadFile(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://uploadUrl',
                    method: 'POST',
                    headers:
                    { Authorization: 'uploadauthtoken',
                        'Content-Type': 'b2/x-auto',
                        'X-Bz-File-Name': 'foo.txt',
                        'X-Bz-Content-Sha1': '332e7f863695677895a406aff6d60acf7e84ea22' },
                    body: 'some text file content'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.uploadFile(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

        describe('with info headers', function() {

            beforeEach(function(done) {
                options = {
                    uploadUrl: 'https://uploadUrl',
                    uploadAuthToken: 'uploadauthtoken',
                    filename: 'foo.txt',
                    data: 'some text file content',
                    info: {
                        foo:  'bar',
                        unicorns: 'rainbows'
                    }
                };

                file.uploadFile(b2, options).then(function() {
                    done();
                });
            });

            it('should properly add x-bz-info headers', function() {
                expect(requestOptions).to.eql({
                    url: 'https://uploadUrl',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'uploadauthtoken',
                        'Content-Type': 'b2/x-auto',
                        'X-Bz-File-Name': 'foo.txt',
                        'X-Bz-Content-Sha1': '332e7f863695677895a406aff6d60acf7e84ea22',
                        'X-Bz-Info-foo': 'bar',
                        'X-Bz-Info-unicorns': 'rainbows'
                    },
                    body: 'some text file content'
                });
            });

        });

        describe('with no mime type specified', function() {

            beforeEach(function(done) {
                options = {
                    uploadUrl: 'https://uploadUrl',
                    uploadAuthToken: 'uploadauthtoken',
                    filename: 'foo.txt',
                    data: 'some text file content',
                    info: {
                        foo:  'bar',
                        unicorns: 'rainbows'
                    }
                };

                file.uploadFile(b2, options).then(function() {
                    done();
                });
            });

            it('should default mime-type in headers', function() {
                expect(requestOptions.headers['Content-Type']).to.equal('b2/x-auto');
            });

        });

        describe('with mime type specified', function() {

            beforeEach(function(done) {
                options = {
                    uploadUrl: 'https://uploadUrl',
                    uploadAuthToken: 'uploadauthtoken',
                    filename: 'foo.txt',
                    mime: 'foo/type',
                    data: 'some text file content',
                    info: {
                        foo:  'bar',
                        unicorns: 'rainbows'
                    }
                };

                file.uploadFile(b2, options).then(function() {
                    done();
                });
            });

            it('should properly mime-type in headers', function() {
                expect(requestOptions.headers['Content-Type']).to.equal('foo/type');
            });

        });

    });

    describe('listFileNames', function() {

        beforeEach(function() {
            options = {
                bucketId: '123abc',
                startFileName: 'unicorns.png',
                maxFileCount: 200
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.listFileNames(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_list_file_names',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"bucketId":"123abc","startFileName":"unicorns.png","maxFileCount":200","prefix":"","delimiter":""}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.listFileNames(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('listFileVersions', function() {

        beforeEach(function() {
            options = {
                bucketId: '123abc',
                startFileName: 'unicorns.png',
                maxFileCount: 200
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.listFileVersions(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_list_file_versions',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"bucketId":"123abc","startFileName":"unicorns.png","maxFileCount":200}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.listFileVersions(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('hideFile', function() {

        beforeEach(function() {
            options = {
                bucketId: '123abc',
                fileName: 'unicorns-and_rainbows!@#$%^&.png'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.hideFile(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response (filename to be encoded)', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_hide_file',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"bucketId":"123abc","fileName":"unicorns-and_rainbows!%40%23%24%25%5E%26.png"}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.hideFile(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('getFileInfo', function() {

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.getFileInfo(b2, 'abc123').then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_get_file_info',
                    method: 'POST',
                    headers:
                    {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"fileId":"abc123"}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.getFileInfo(b2, 'abc123').then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('downloadFileByName', function() {

        beforeEach(function() {
            options = {
                bucketName: 'unicornBox',
                fileName: 'unicorns-and_rainbows!@#$%^&.png'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = {
                    headers: {
                        'x-bz-file-id': 'fileIdAbcd1234',
                        'x-bz-file-name': 'unicorns-download.png',
                        'x-bz-content-sha1': 'file_hash'
                    },
                    statusCode: 200
                };

                bogusRequestModule = function(options, cb) {
                    requestOptions = options;
                    cb(errorMessage, response, 'file contents');
                };

                request.setup(bogusRequestModule);

                file.downloadFileByName(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response (filename to be encoded)', function() {
                expect(requestOptions).to.eql({
                    url: 'https://download/file/unicornBox/unicorns-and_rainbows!%40%23%24%25%5E%26.png',
                    encoding: null,
                    headers: {
                        Authorization: 'unicorns and rainbows'
                    }
                });
                expect(actualResponse).to.eql({
                    data: 'file contents',
                    fileId: 'fileIdAbcd1234',
                    fileName: 'unicorns-download.png',
                    contentSha1: 'file_hash'
                });
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.downloadFileByName(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('downloadFileById', function() {

        describe('with good response', function() {

            beforeEach(function(done) {
                response = {
                    headers: {
                        'x-bz-file-id': 'fileIdAbcd1234',
                        'x-bz-file-name': 'unicorns-download.png',
                        'x-bz-content-sha1': 'file_hash'
                    },
                    statusCode: 200
                };

                bogusRequestModule = function(options, cb) {
                    requestOptions = options;
                    cb(errorMessage, response, 'file contents');
                };

                request.setup(bogusRequestModule);

                file.downloadFileById(b2, 'abcd1234').then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response', function() {
                expect(requestOptions).to.eql({
                    url: 'https://download/b2api/v1/b2_download_file_by_id',
                    qs: {
                        fileId: 'abcd1234'
                    },
                    headers: {
                        Authorization: 'unicorns and rainbows'
                    },
                    encoding: null
                });
                expect(actualResponse).to.eql({
                    data: 'file contents',
                    fileId: 'fileIdAbcd1234',
                    fileName: 'unicorns-download.png',
                    contentSha1: 'file_hash'
                });
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.downloadFileById(b2, '1234abcd').then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

    describe('deleteFileVersion', function() {

        beforeEach(function() {
            options = {
                fileId: 'abcd1234',
                fileName: 'unicorns-and_rainbows!@#$%^&.png'
            };
        });

        describe('with good response', function() {

            beforeEach(function(done) {
                response = { foo: '1234' };

                file.deleteFileVersion(b2, options).then(function(response) {
                    actualResponse = response;
                    done();
                });
            });

            it('should set correct options and resolve with good response  (filename to be encoded)', function() {
                expect(requestOptions).to.eql({
                    url: 'https://foo/b2api/v1/b2_delete_file_version',
                    method: 'POST',
                    headers: {
                        Authorization: 'unicorns and rainbows'
                    },
                    body: '{"fileId":"abcd1234","fileName":"unicorns-and_rainbows!%40%23%24%25%5E%26.png"}'
                });
                expect(actualResponse).to.eql(response);
            });
        });

        describe('with error response', function() {

            beforeEach(function(done) {
                errorMessage = 'Something went wrong';

                file.deleteFileVersion(b2, options).then(null, function(error) {
                    actualResponse = error;
                    done();
                });
            });

            it('Should respond with an error and reject promise', function() {
                expect(actualResponse).to.be(errorMessage);
            });
        });

    });

});
<|endoftext|>


<commit_before>/*! Overthrow v.0.1.0. An overflow:auto polyfill for responsive design. (c) 2012: Scott Jehl, Filament Group, Inc. https://filamentgroup.github.com/overthrow/license.txt */
(function( w, undefined ){

	var doc = w.document,
		docElem = doc.documentElement,
		classtext = "overthrow-enabled",

		// Touch events are used in the polyfill, and thus are a prerequisite
		canBeFilledWithPoly = "ontouchmove" in doc,

		// The following attempts to determine whether the browser has native overflow support
		// so we can enable it but not polyfill
		overflowProbablyAlreadyWorks =
			// Features-first. iOS5 overflow scrolling property check - no UA needed here. thanks Apple :)
			"WebkitOverflowScrolling" in docElem.style ||
			// Touch events aren't supported and screen width is greater than X
			// ...basically, this is a loose "desktop browser" check.
			// It may wrongly opt-in very large tablets with no touch support.
			( !canBeFilledWithPoly && w.screen.width > 1200 ) ||
			// Hang on to your hats.
			// Whitelist some popular, overflow-supporting mobile browsers for now and the future
			// These browsers are known to get overlow support right, but give us no way of detecting it.
			(function(){
				var ua = w.navigator.userAgent,
					// Webkit crosses platforms, and the browsers on our list run at least version 534
					webkit = ua.match( /AppleWebKit\/([0-9]+)/ ),
					wkversion = webkit && webkit[1],
					wkLte534 = webkit && wkversion >= 534;

				return (
					/* Android 3+ with webkit gte 534
					~: Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13 */
					ua.match( /Android ([0-9]+)/ ) && RegExp.$1 >= 3 && wkLte534 ||
					/* Blackberry 7+ with webkit gte 534
					~: Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0 Mobile Safari/534.11+ */
					ua.match( / Version\/([0-9]+)/ ) && RegExp.$1 >= 0 && w.blackberry && wkLte534 ||
					/* Blackberry Playbook with webkit gte 534
					~: Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+ */
					ua.indexOf( /PlayBook/ ) > -1 && RegExp.$1 >= 0 && wkLte534 ||
					/* Firefox Mobile (Fennec) 4 and up
					~: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:2.1.1) Gecko/ Firefox/4.0.2pre Fennec/4.0. */
					ua.match( /Fennec\/([0-9]+)/ ) && RegExp.$1 >= 4 ||
					/* WebOS 3 and up (TouchPad too)
					~: Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.48 Safari/534.6 TouchPad/1.0 */
					ua.match( /wOSBrowser\/([0-9]+)/ ) && RegExp.$1 >= 233 && wkLte534 ||
					/* Nokia Browser N8
					~: Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/012.002; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.0 Mobile Safari/533.4 3gpp-gba
					~: Note: the N9 doesn't have native overflow with one-finger touch. wtf */
					ua.match( /NokiaBrowser\/([0-9\.]+)/ ) && parseFloat(RegExp.$1) === 7.3 && webkit && wkversion >= 533
				);
			})(),

		// Easing can use any of Robert Penner's equations (http://www.robertpenner.com/easing_terms_of_use.html). By default, overthrow includes ease-out-cubic
		// arguments: t = current iteration, b = initial value, c = end value, d = total iterations
		// use w.overthrow.easing to provide a custom function externally, or pass an easing function as a callback to the toss method
		defaultEasing = function (t, b, c, d) {
			return c*((t=t/d-1)*t*t + 1) + b;
		},

		enabled = false,

		// Keeper of intervals
		timeKeeper,

		/* toss scrolls and element with easing

		// elem is the element to scroll
		// options hash:
			* left is the desired horizontal scroll. Default is "+0". For relative distances, pass a string with "+" or "-" in front.
			* top is the desired vertical scroll. Default is "+0". For relative distances, pass a string with "+" or "-" in front.
			* duration is the number of milliseconds the throw will take. Default is 100.
			* easing is an optional custom easing function. Default is w.overthrow.easing. Must follow the easing function signature
		*/
		toss = function( elem, options ){
			var i = 0,
				sLeft = elem.scrollLeft,
				sTop = elem.scrollTop,
				// Toss defaults
				o = {
					top: "+0",
					left: "+0",
					duration: 100,
					easing: w.overthrow.easing
				},
				endLeft, endTop;

			// Mixin based on predefined defaults
			if( options ){
				for( var j in o ){
					if( options[ j ] !== undefined ){
						o[ j ] = options[ j ];
					}
				}
			}

			// Convert relative values to ints
			// First the left val
			if( typeof o.left === "string" ){
				o.left = parseFloat( o.left );
				endLeft = o.left + sLeft;
			}
			else {
				endLeft = o.left;
				o.left = o.left - sLeft;
			}
			// Then the top val
			if( typeof o.top === "string" ){
				o.top = parseFloat( o.top );
				endTop = o.top + sTop;
			}
			else {
				endTop = o.top;
				o.top = o.top - sTop;
			}

			timeKeeper = setInterval(function(){
				if( i++ < o.duration ){
					elem.scrollLeft = o.easing( i, sLeft, o.left, o.duration );
					elem.scrollTop = o.easing( i, sTop, o.top, o.duration );
				}
				else{
					if( endLeft !== elem.scrollLeft ){
						elem.scrollLeft = endLeft;
					}
					if( endTop !== elem.scrollTop ){
						elem.scrollTop = endTop;
					}
					intercept();
				}
			}, 1 );

			// Return the values, post-mixin, with end values specified
			return { top: endTop, left: endLeft, duration: o.duration, easing: o.easing }
		},

		// find closest overthrow (elem or a parent)
		closest = function( target, ascend ){
			return !ascend && target.className && target.className.indexOf( "overthrow" ) > -1 && target || closest( target.parentNode );
		},

		// Intercept any throw in progress
		intercept = function(){
			clearInterval( timeKeeper );
		},

		// Enable and potentially polyfill overflow
		enable = function(){

			// If it's on,
			if( enabled ){
				return;
			}
			// It's on.
			enabled = true;

			// If overflowProbablyAlreadyWorks or at least the element canBeFilledWithPoly, add a class to cue CSS that assumes overflow scrolling will work (setting height on elements and such)
			if( overflowProbablyAlreadyWorks || canBeFilledWithPoly ){
				docElem.className += " " + classtext;
			}

			// Destroy everything later. If you want to.
			w.overthrow.forget = function(){
				// Strip the class name from docElem
				docElem.className = docElem.className.replace( classtext, "" );
				// Remove touch binding (check for method support since this part isn't qualified by touch support like the rest)
				if( doc.removeEventListener ){
					doc.removeEventListener( "touchstart", start, false );
				}
				// reset easing to default
				w.overthrow.easing = defaultEasing;

				// Let 'em know
				enabled = false;
			};

			// If overflowProbablyAlreadyWorks or it doesn't look like the browser canBeFilledWithPoly, our job is done here. Exit viewport left.
			if( overflowProbablyAlreadyWorks || !canBeFilledWithPoly ){
				return;
			}

			// Fill 'er up!
			// From here down, all logic is associated with touch scroll handling
				// elem references the overthrow element in use
			var elem,

				// The last several Y values are kept here
				lastTops = [],

				// The last several X values are kept here
				lastLefts = [],

				// lastDown will be true if the last scroll direction was down, false if it was up
				lastDown,

				// lastRight will be true if the last scroll direction was right, false if it was left
				lastRight,

				// For a new gesture, or change in direction, reset the values from last scroll
				resetVertTracking = function(){
					lastTops = [];
					lastDown = null;
				},

				resetHorTracking = function(){
					lastLefts = [];
					lastRight = null;
				},

				// After releasing touchend, throw the overthrow element, depending on momentum
				finishScroll = function(){
					// Come up with a distance and duration based on how
					// Multipliers are tweaked to a comfortable balance across platforms
					var top = ( lastTops[ 0 ] - lastTops[ lastTops.length -1 ] ) * 8,
						left = ( lastLefts[ 0 ] - lastLefts[ lastLefts.length -1 ] ) * 8,
						duration = Math.max( Math.abs( left ), Math.abs( top ) ) / 8;

					// Make top and left relative-style strings (positive vals need "+" prefix)
					top = ( top > 0 ? "+" : "" ) + top;
					left = ( left > 0 ? "+" : "" ) + left;

					// Make sure there's a significant amount of throw involved, otherwise, just stay still
					if( !isNaN( duration ) && duration > 0 && ( Math.abs( left ) > 80 || Math.abs( top ) > 80 ) ){
						toss( elem, { left: left, top: top, duration: duration } );
					}
				},

				// On webkit, touch events hardly trickle through textareas and inputs
				// Disabling CSS pointer events makes sure they do, but it also makes the controls innaccessible
				// Toggling pointer events at the right moments seems to do the trick
				// Thanks Thomas Bachem http://stackoverflow.com/a/5798681 for the following
				inputs,
				setPointers = function( val ){
					inputs = elem.querySelectorAll( "textarea, input" );
					for( var i = 0, il = inputs.length; i < il; i++ ) {
						inputs[ i ].style.pointerEvents = val;
					}
				},

				// For nested overthrows, changeScrollTarget restarts a touch event cycle on a parent or child overthrow
				changeScrollTarget = function( startEvent, ascend ){
					if( doc.createEvent ){
						var newTarget = ( !ascend || ascend === undefined ) && elem.parentNode || elem.touchchild || elem,
							tEnd;

						if( newTarget !== elem ){
							tEnd = doc.createEvent( "HTMLEvents" );
							tEnd.initEvent( "touchend", true, true );
							elem.dispatchEvent( tEnd );
							newTarget.touchchild = elem;
							elem = newTarget;
							newTarget.dispatchEvent( startEvent );
						}
					}
				},

				// Touchstart handler
				// On touchstart, touchmove and touchend are freshly bound, and all three share a bunch of vars set by touchstart
				// Touchend unbinds them again, until next time
				start = function( e ){

					// Stop any throw in progress
					intercept();

					// Reset the distance and direction tracking
					resetVertTracking();
					resetHorTracking();

					elem = closest( e.target );

					if( !elem || elem === docElem || e.touches.length > 1 ){
						return;
					}

					setPointers( "none" );
					var touchStartE = e,
						scrollT = elem.scrollTop,
						scrollL = elem.scrollLeft,
						height = elem.offsetHeight,
						width = elem.offsetWidth,
						startY = e.touches[ 0 ].pageY,
						startX = e.touches[ 0 ].pageX,
						scrollHeight = elem.scrollHeight,
						scrollWidth = elem.scrollWidth,

						// Touchmove handler
						move = function( e ){

							var ty = scrollT + startY - e.touches[ 0 ].pageY,
								tx = scrollL + startX - e.touches[ 0 ].pageX,
								down = ty >= ( lastTops.length ? lastTops[ 0 ] : 0 ),
								right = tx >= ( lastLefts.length ? lastLefts[ 0 ] : 0 );

							// If there's room to scroll the current container, prevent the default window scroll
							if( ( ty > 0 && ty < scrollHeight - height ) || ( tx > 0 && tx < scrollWidth - width ) ){
								e.preventDefault();
							}
							// This bubbling is dumb. Needs a rethink.
							else {
								changeScrollTarget( touchStartE );
							}

							// If down and lastDown are inequal, the y scroll has changed direction. Reset tracking.
							if( lastDown && down !== lastDown ){
								resetVertTracking();
							}

							// If right and lastRight are inequal, the x scroll has changed direction. Reset tracking.
							if( lastRight && right !== lastRight ){
								resetHorTracking();
							}

							// remember the last direction in which we were headed
							lastDown = down;
							lastRight = right;

							// set the container's scroll
							elem.scrollTop = ty;
							elem.scrollLeft = tx;

							lastTops.unshift( ty );
							lastLefts.unshift( tx );

							if( lastTops.length > 3 ){
								lastTops.pop();
							}
							if( lastLefts.length > 3 ){
								lastLefts.pop();
							}
						},

						// Touchend handler
						end = function( e ){
							// Apply momentum based easing for a graceful finish
							finishScroll();
							// Bring the pointers back
							setPointers( "auto" );
							setTimeout( function(){
								setPointers( "none" );
							}, 450 );
							elem.removeEventListener( "touchmove", move, false );
							elem.removeEventListener( "touchend", end, false );
						};

					elem.addEventListener( "touchmove", move, false );
					elem.addEventListener( "touchend", end, false );
				};

			// Bind to touch, handle move and end within
			doc.addEventListener( "touchstart", start, false );
		};

	// Expose overthrow API
	w.overthrow = {
		set: enable,
		forget: function(){},
		easing: defaultEasing,
		toss: toss,
		intercept: intercept,
		closest: closest,
		support: overflowProbablyAlreadyWorks ? "native" : canBeFilledWithPoly && "polyfilled" || "none"
	};

	// Auto-init
	enable();

})( this );<commit_msg>missing semicolon - thx jshint :)<commit_after>/*! Overthrow v.0.1.0. An overflow:auto polyfill for responsive design. (c) 2012: Scott Jehl, Filament Group, Inc. https://filamentgroup.github.com/overthrow/license.txt */
(function( w, undefined ){

	var doc = w.document,
		docElem = doc.documentElement,
		classtext = "overthrow-enabled",

		// Touch events are used in the polyfill, and thus are a prerequisite
		canBeFilledWithPoly = "ontouchmove" in doc,

		// The following attempts to determine whether the browser has native overflow support
		// so we can enable it but not polyfill
		overflowProbablyAlreadyWorks =
			// Features-first. iOS5 overflow scrolling property check - no UA needed here. thanks Apple :)
			"WebkitOverflowScrolling" in docElem.style ||
			// Touch events aren't supported and screen width is greater than X
			// ...basically, this is a loose "desktop browser" check.
			// It may wrongly opt-in very large tablets with no touch support.
			( !canBeFilledWithPoly && w.screen.width > 1200 ) ||
			// Hang on to your hats.
			// Whitelist some popular, overflow-supporting mobile browsers for now and the future
			// These browsers are known to get overlow support right, but give us no way of detecting it.
			(function(){
				var ua = w.navigator.userAgent,
					// Webkit crosses platforms, and the browsers on our list run at least version 534
					webkit = ua.match( /AppleWebKit\/([0-9]+)/ ),
					wkversion = webkit && webkit[1],
					wkLte534 = webkit && wkversion >= 534;

				return (
					/* Android 3+ with webkit gte 534
					~: Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13 */
					ua.match( /Android ([0-9]+)/ ) && RegExp.$1 >= 3 && wkLte534 ||
					/* Blackberry 7+ with webkit gte 534
					~: Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0 Mobile Safari/534.11+ */
					ua.match( / Version\/([0-9]+)/ ) && RegExp.$1 >= 0 && w.blackberry && wkLte534 ||
					/* Blackberry Playbook with webkit gte 534
					~: Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+ */
					ua.indexOf( /PlayBook/ ) > -1 && RegExp.$1 >= 0 && wkLte534 ||
					/* Firefox Mobile (Fennec) 4 and up
					~: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:2.1.1) Gecko/ Firefox/4.0.2pre Fennec/4.0. */
					ua.match( /Fennec\/([0-9]+)/ ) && RegExp.$1 >= 4 ||
					/* WebOS 3 and up (TouchPad too)
					~: Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.48 Safari/534.6 TouchPad/1.0 */
					ua.match( /wOSBrowser\/([0-9]+)/ ) && RegExp.$1 >= 233 && wkLte534 ||
					/* Nokia Browser N8
					~: Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/012.002; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.0 Mobile Safari/533.4 3gpp-gba
					~: Note: the N9 doesn't have native overflow with one-finger touch. wtf */
					ua.match( /NokiaBrowser\/([0-9\.]+)/ ) && parseFloat(RegExp.$1) === 7.3 && webkit && wkversion >= 533
				);
			})(),

		// Easing can use any of Robert Penner's equations (http://www.robertpenner.com/easing_terms_of_use.html). By default, overthrow includes ease-out-cubic
		// arguments: t = current iteration, b = initial value, c = end value, d = total iterations
		// use w.overthrow.easing to provide a custom function externally, or pass an easing function as a callback to the toss method
		defaultEasing = function (t, b, c, d) {
			return c*((t=t/d-1)*t*t + 1) + b;
		},

		enabled = false,

		// Keeper of intervals
		timeKeeper,

		/* toss scrolls and element with easing

		// elem is the element to scroll
		// options hash:
			* left is the desired horizontal scroll. Default is "+0". For relative distances, pass a string with "+" or "-" in front.
			* top is the desired vertical scroll. Default is "+0". For relative distances, pass a string with "+" or "-" in front.
			* duration is the number of milliseconds the throw will take. Default is 100.
			* easing is an optional custom easing function. Default is w.overthrow.easing. Must follow the easing function signature
		*/
		toss = function( elem, options ){
			var i = 0,
				sLeft = elem.scrollLeft,
				sTop = elem.scrollTop,
				// Toss defaults
				o = {
					top: "+0",
					left: "+0",
					duration: 100,
					easing: w.overthrow.easing
				},
				endLeft, endTop;

			// Mixin based on predefined defaults
			if( options ){
				for( var j in o ){
					if( options[ j ] !== undefined ){
						o[ j ] = options[ j ];
					}
				}
			}

			// Convert relative values to ints
			// First the left val
			if( typeof o.left === "string" ){
				o.left = parseFloat( o.left );
				endLeft = o.left + sLeft;
			}
			else {
				endLeft = o.left;
				o.left = o.left - sLeft;
			}
			// Then the top val
			if( typeof o.top === "string" ){
				o.top = parseFloat( o.top );
				endTop = o.top + sTop;
			}
			else {
				endTop = o.top;
				o.top = o.top - sTop;
			}

			timeKeeper = setInterval(function(){
				if( i++ < o.duration ){
					elem.scrollLeft = o.easing( i, sLeft, o.left, o.duration );
					elem.scrollTop = o.easing( i, sTop, o.top, o.duration );
				}
				else{
					if( endLeft !== elem.scrollLeft ){
						elem.scrollLeft = endLeft;
					}
					if( endTop !== elem.scrollTop ){
						elem.scrollTop = endTop;
					}
					intercept();
				}
			}, 1 );

			// Return the values, post-mixin, with end values specified
			return { top: endTop, left: endLeft, duration: o.duration, easing: o.easing };
		},

		// find closest overthrow (elem or a parent)
		closest = function( target, ascend ){
			return !ascend && target.className && target.className.indexOf( "overthrow" ) > -1 && target || closest( target.parentNode );
		},

		// Intercept any throw in progress
		intercept = function(){
			clearInterval( timeKeeper );
		},

		// Enable and potentially polyfill overflow
		enable = function(){

			// If it's on,
			if( enabled ){
				return;
			}
			// It's on.
			enabled = true;

			// If overflowProbablyAlreadyWorks or at least the element canBeFilledWithPoly, add a class to cue CSS that assumes overflow scrolling will work (setting height on elements and such)
			if( overflowProbablyAlreadyWorks || canBeFilledWithPoly ){
				docElem.className += " " + classtext;
			}

			// Destroy everything later. If you want to.
			w.overthrow.forget = function(){
				// Strip the class name from docElem
				docElem.className = docElem.className.replace( classtext, "" );
				// Remove touch binding (check for method support since this part isn't qualified by touch support like the rest)
				if( doc.removeEventListener ){
					doc.removeEventListener( "touchstart", start, false );
				}
				// reset easing to default
				w.overthrow.easing = defaultEasing;

				// Let 'em know
				enabled = false;
			};

			// If overflowProbablyAlreadyWorks or it doesn't look like the browser canBeFilledWithPoly, our job is done here. Exit viewport left.
			if( overflowProbablyAlreadyWorks || !canBeFilledWithPoly ){
				return;
			}

			// Fill 'er up!
			// From here down, all logic is associated with touch scroll handling
				// elem references the overthrow element in use
			var elem,

				// The last several Y values are kept here
				lastTops = [],

				// The last several X values are kept here
				lastLefts = [],

				// lastDown will be true if the last scroll direction was down, false if it was up
				lastDown,

				// lastRight will be true if the last scroll direction was right, false if it was left
				lastRight,

				// For a new gesture, or change in direction, reset the values from last scroll
				resetVertTracking = function(){
					lastTops = [];
					lastDown = null;
				},

				resetHorTracking = function(){
					lastLefts = [];
					lastRight = null;
				},

				// After releasing touchend, throw the overthrow element, depending on momentum
				finishScroll = function(){
					// Come up with a distance and duration based on how
					// Multipliers are tweaked to a comfortable balance across platforms
					var top = ( lastTops[ 0 ] - lastTops[ lastTops.length -1 ] ) * 8,
						left = ( lastLefts[ 0 ] - lastLefts[ lastLefts.length -1 ] ) * 8,
						duration = Math.max( Math.abs( left ), Math.abs( top ) ) / 8;

					// Make top and left relative-style strings (positive vals need "+" prefix)
					top = ( top > 0 ? "+" : "" ) + top;
					left = ( left > 0 ? "+" : "" ) + left;

					// Make sure there's a significant amount of throw involved, otherwise, just stay still
					if( !isNaN( duration ) && duration > 0 && ( Math.abs( left ) > 80 || Math.abs( top ) > 80 ) ){
						toss( elem, { left: left, top: top, duration: duration } );
					}
				},

				// On webkit, touch events hardly trickle through textareas and inputs
				// Disabling CSS pointer events makes sure they do, but it also makes the controls innaccessible
				// Toggling pointer events at the right moments seems to do the trick
				// Thanks Thomas Bachem http://stackoverflow.com/a/5798681 for the following
				inputs,
				setPointers = function( val ){
					inputs = elem.querySelectorAll( "textarea, input" );
					for( var i = 0, il = inputs.length; i < il; i++ ) {
						inputs[ i ].style.pointerEvents = val;
					}
				},

				// For nested overthrows, changeScrollTarget restarts a touch event cycle on a parent or child overthrow
				changeScrollTarget = function( startEvent, ascend ){
					if( doc.createEvent ){
						var newTarget = ( !ascend || ascend === undefined ) && elem.parentNode || elem.touchchild || elem,
							tEnd;

						if( newTarget !== elem ){
							tEnd = doc.createEvent( "HTMLEvents" );
							tEnd.initEvent( "touchend", true, true );
							elem.dispatchEvent( tEnd );
							newTarget.touchchild = elem;
							elem = newTarget;
							newTarget.dispatchEvent( startEvent );
						}
					}
				},

				// Touchstart handler
				// On touchstart, touchmove and touchend are freshly bound, and all three share a bunch of vars set by touchstart
				// Touchend unbinds them again, until next time
				start = function( e ){

					// Stop any throw in progress
					intercept();

					// Reset the distance and direction tracking
					resetVertTracking();
					resetHorTracking();

					elem = closest( e.target );

					if( !elem || elem === docElem || e.touches.length > 1 ){
						return;
					}

					setPointers( "none" );
					var touchStartE = e,
						scrollT = elem.scrollTop,
						scrollL = elem.scrollLeft,
						height = elem.offsetHeight,
						width = elem.offsetWidth,
						startY = e.touches[ 0 ].pageY,
						startX = e.touches[ 0 ].pageX,
						scrollHeight = elem.scrollHeight,
						scrollWidth = elem.scrollWidth,

						// Touchmove handler
						move = function( e ){

							var ty = scrollT + startY - e.touches[ 0 ].pageY,
								tx = scrollL + startX - e.touches[ 0 ].pageX,
								down = ty >= ( lastTops.length ? lastTops[ 0 ] : 0 ),
								right = tx >= ( lastLefts.length ? lastLefts[ 0 ] : 0 );

							// If there's room to scroll the current container, prevent the default window scroll
							if( ( ty > 0 && ty < scrollHeight - height ) || ( tx > 0 && tx < scrollWidth - width ) ){
								e.preventDefault();
							}
							// This bubbling is dumb. Needs a rethink.
							else {
								changeScrollTarget( touchStartE );
							}

							// If down and lastDown are inequal, the y scroll has changed direction. Reset tracking.
							if( lastDown && down !== lastDown ){
								resetVertTracking();
							}

							// If right and lastRight are inequal, the x scroll has changed direction. Reset tracking.
							if( lastRight && right !== lastRight ){
								resetHorTracking();
							}

							// remember the last direction in which we were headed
							lastDown = down;
							lastRight = right;

							// set the container's scroll
							elem.scrollTop = ty;
							elem.scrollLeft = tx;

							lastTops.unshift( ty );
							lastLefts.unshift( tx );

							if( lastTops.length > 3 ){
								lastTops.pop();
							}
							if( lastLefts.length > 3 ){
								lastLefts.pop();
							}
						},

						// Touchend handler
						end = function( e ){
							// Apply momentum based easing for a graceful finish
							finishScroll();
							// Bring the pointers back
							setPointers( "auto" );
							setTimeout( function(){
								setPointers( "none" );
							}, 450 );
							elem.removeEventListener( "touchmove", move, false );
							elem.removeEventListener( "touchend", end, false );
						};

					elem.addEventListener( "touchmove", move, false );
					elem.addEventListener( "touchend", end, false );
				};

			// Bind to touch, handle move and end within
			doc.addEventListener( "touchstart", start, false );
		};

	// Expose overthrow API
	w.overthrow = {
		set: enable,
		forget: function(){},
		easing: defaultEasing,
		toss: toss,
		intercept: intercept,
		closest: closest,
		support: overflowProbablyAlreadyWorks ? "native" : canBeFilledWithPoly && "polyfilled" || "none"
	};

	// Auto-init
	enable();

})( this );<|endoftext|>


<commit_before>//
// lazy load image
//
define('private/lazy-load', function (require, exports, module) {
    "use strict";

    if (!location.href.match(/private\/detail\.html/i)) {
        return;
    }

    function applyLazyLoad () {
        $('.lazy-load-img').lazyload();

        $(document).magnificPopup({
            delegate: '.magic-popup-link',
            gallery: {
                enabled: true
            },
            image: {
                verticalFit: false,
                titleSrc: function (mfp) {
                    return 'GO';
                }
            },
            type: 'image'
        })
        .on('click', 'a.private-img-go', function () {
            $.magnificPopup.close();
        });
    }

    function applyAffix () {
        $('#private-affix-main').affix();
    }

    var $ = require('jquery'),
        purl = require('purl'),
        $main = $('
' +
                  '

' +
                  '

' +
                  '
'),
        $header = $main.find('.private-header'),
        $content = $main.find('.private-content'),
        $sidebar = $main.find('.private-sidebar'),
        $navs = $('
' +
                      '
' +
                      '
' +
                      '返回顶部' +
                  '
'),
        url = purl(location.href).param('url');

    function init () {
        $.ajax({
            url: url,
            type: 'get',
            timeout: 30 * 1000,
            dataType: 'text'
        })
        .done(function (data) {
            var html = data.replace(/src=/ig, 'data-src='),
                $html = $($.parseHTML(html)),
                $title = $html.filter('title'),
                title = $.trim($title.text()),
                $body = $html.filter('#main'),
                body = $body.find('#content').html() || '',
                names = [],
                index = 0,
                sections = [];

            sections = body.split(/
\n
\n/).map(function (section) {
                var matches = section.match(/^(.*)
/i),
                    repl = '',
                    name = '';
                if (matches && matches.length) {
                    name = matches[1];
                    repl = '
' + name + '
';
                    section = section.replace(/^.*
/i, repl);
                    index += 1;
                    names.push(name);
                }
                section = '
' + section + '
';
                return section;
            });

            body = sections.join('
\n');
            $body = $body.html($.parseHTML(body));

            $body.find('img').each(function (i) {
                var $img = $(this),
                    src = $img.data('src'),
                    $link = $('');

                $img.attr('data-original', src);
                $img.addClass('lazy-load-img');
                $img.attr('id', 'private-img-' + i);
                $link.attr('href', src);
                $link.addClass('magic-popup-link');
                $link.insertBefore($img);
                $link.append($img);
            });

            var navs = '';
            $.each(names, function (i, name) {
                if (!i) {
                    navs += '
' + name + '
';
                } else {
                    navs += '
' + name + '
';
                }
            });
            $navs.find('.private-sidenav').html(navs);
            $sidebar.html($navs);

            $header.text(title);
            document.title = title;
            $content.html($body.html());

            applyLazyLoad();
            applyAffix();
        })
        .fail(function () {
            $content.html('error');
        });

        $('html').show();
        document.title = 'loading';
        $('body').html($main);
    }

    module.exports = {
        init: init
    };
});
<commit_msg>refactor lazy-load<commit_after>//
// lazy load image
//
define('private/lazy-load', function (require, exports, module) {
    "use strict";

    if (!location.href.match(/private\/detail\.html/i)) {
        return;
    }

    function applyLazyLoad () {
        $('.lazy-load-img').lazyload();

        $(document).magnificPopup({
            delegate: '.magic-popup-link',
            gallery: {
                enabled: true
            },
            image: {
                verticalFit: false,
                titleSrc: function (mfp) {
                    return 'GO';
                }
            },
            type: 'image'
        })
        .on('click', 'a.private-img-go', function () {
            $.magnificPopup.close();
        });
    }

    function applyAffix () {
        $('#private-affix-main').affix();
    }

    var $ = require('jquery'),
        purl = require('purl'),
        $main = $('
' +
                  '

' +
                  '

' +
                  '
'),
        $header = $main.find('.private-header'),
        $content = $main.find('.private-content'),
        $sidebar = $main.find('.private-sidebar'),
        $navs = $('
' +
                      '
' +
                      '
' +
                      '返回顶部' +
                  '
'),
        url = purl(location.href).param('url');

    function init () {
        $.ajax({
            url: url,
            type: 'get',
            timeout: 30 * 1000,
            dataType: 'text'
        })
        .done(function (data) {
            var html = data.replace(/src=/ig, 'data-src='),
                $html = $($.parseHTML(html)),
                $title = $html.filter('title'),
                title = $.trim($title.text()),
                $body = $html.filter('#main'),
                body = $body.find('#content').html() || '',
                names = [],
                index = 0,
                sections = [];

            sections = body.split(/
\n
\n/).map(function (section) {
                var matches = section.match(/^(.*)
/i),
                    repl = '',
                    name = '';
                if (matches && matches.length) {
                    name = matches[1];
                    repl = '
' + name + '
';
                    section = section.replace(/^.*
/i, repl);
                    index += 1;
                    names.push(name);
                }
                section = '
' + section + '
';
                return section;
            });

            body = sections.join('
\n');
            $body = $body.html($.parseHTML(body));

            $body.find('img').each(function (i) {
                var $img = $(this),
                    src = $img.data('src'),
                    $link = $('');

                $img.attr('data-original', src);
                $img.addClass('lazy-load-img');
                $img.attr('id', 'private-img-' + i);
                $link.attr('href', src);
                $link.addClass('magic-popup-link');
                $link.insertBefore($img);
                $link.append($img);
            });

            var navs = '';
            $.each(names, function (i, name) {
                if (!i) {
                    navs += '
' + name + '
';
                } else {
                    navs += '
' + name + '
';
                }
            });
            $navs.find('.private-sidenav').html(navs);
            $sidebar.html($navs);

            $header.text(title);
            document.title = title;
            $content.html($body.html());

            applyLazyLoad();
            applyAffix();
        })
        .fail(function () {
            $content.html('error');
        });

        document.title = 'loading';
        $('body').append($main);
    }

    module.exports = {
        init: init
    };
});
<|endoftext|>


<commit_before>angular.module('futurism')
	.directive('abominationAnim', function($, maths, animFns) {
		'use strict';

		return {
			restrict: 'A',
			link: function(scope, boardElem) {

				scope.$on('event:abom', function(srcScope, update) {

					var targetChain = update.data;

					var abom = animFns.makeAnimTarget(update, targetChain[0]);
					var victim = animFns.makeAnimTarget(update, targetChain[1]);

					var abomElem = $('.card-small.cid-'+abom.target.card.cid);
					var victimElem = $('.card-small.cid-'+victim.target.card.cid);

					var abomOffset = animFns.relativeOffset(abomElem, boardElem);
					var victimOffset = animFns.relativeOffset(victimElem, boardElem);

					var cloneElem = victimElem.clone(false);

					cloneElem.css({
						position: 'absolute',
						top: victimOffset.top,
						left: victimOffset.left,
						'z-index': 99
					});

					makeGrabbers(4, abomOffset, victimOffset, function() {
						$('.grabber-effect')
							.animate({
								width: 1
							}, function() {
								$('.grabber-effect').remove();
							});

						cloneElem
							.animate({
								top: abomOffset.top,
								left: abomOffset.left
							}, 'slow')
							.animate({
								opacity: 0
							}, function() {
								cloneElem.remove();
							});
					});

					boardElem.append(cloneElem);
				});

				var makeGrabbers = function(count, abomOffset, victimOffset, callback) {
					var done = _.after(count, function() {
						callback();
					});

					_.times(count, function(n) {
						_.delay(function() {
							makeGrabber(abomOffset, victimOffset, done);
						}, n*200);
					});
				};

				var makeGrabber = function(abomOffset, victimOffset, callback) {
					console.log('make grabber');
					var p1 = {
						x: abomOffset.left + (Math.random() * animFns.cardWidth),
						y: abomOffset.top + (Math.random() * animFns.cardHeight)
					};
					var p2 = {
						x: victimOffset.left + (Math.random() * animFns.cardWidth),
						y: victimOffset.top + (Math.random() * animFns.cardHeight)
					};
					var distX = p1.x - p2.x;
					var distY = p1.y - p2.y;
					var distTot = Math.sqrt(distX*distX + distY*distY);
					var angleRad = Math.atan2(distY, distX) + Math.PI;

					var grabber = $('

');
					boardElem.append(grabber);
					grabber
						.css({
							left: p1.x,
							top: p1.y,
							width: 1,
							transform: 'rotate('+angleRad+'rad)'
						})
						.animate({
							width: distTot
						}, callback)
				};
			}
		};
	});<commit_msg>update abomination animation<commit_after>angular.module('futurism')
	.directive('abominationAnim', function($, maths, animFns) {
		'use strict';

		return {
			restrict: 'A',
			link: function(scope, boardElem) {

				scope.$on('event:abom', function(srcScope, update) {

					var animTargets = animFns.chainedAnimTargets(update, update.data);
					var abom = animTargets[0];
					var victim = animTargets[1];

					var cloneElem = victim.elem.find('.card-small').clone();

					cloneElem.css({
						position: 'absolute',
						top: victim.offset.top,
						left: victim.offset.left,
						'z-index': 99
					});

					makeGrabbers(4, abom.offset, victim.offset, function() {
						$('.grabber-effect')
							.animate({
								width: 1
							}, function() {
								$('.grabber-effect').remove();
							});

						cloneElem
							.animate({
								top: abom.offset.top,
								left: abom.offset.left
							}, 'slow')
							.animate({
								opacity: 0
							}, function() {
								cloneElem.remove();
							});
					});

					boardElem.append(cloneElem);
				});

				var makeGrabbers = function(count, abomOffset, victimOffset, callback) {
					var done = _.after(count, function() {
						callback();
					});

					_.times(count, function(n) {
						_.delay(function() {
							makeGrabber(abomOffset, victimOffset, done);
						}, n*200);
					});
				};

				var makeGrabber = function(abomOffset, victimOffset, callback) {
					console.log('make grabber');
					var p1 = {
						x: abomOffset.left + (Math.random() * animFns.cardWidth),
						y: abomOffset.top + (Math.random() * animFns.cardHeight)
					};
					var p2 = {
						x: victimOffset.left + (Math.random() * animFns.cardWidth),
						y: victimOffset.top + (Math.random() * animFns.cardHeight)
					};
					var distX = p1.x - p2.x;
					var distY = p1.y - p2.y;
					var distTot = Math.sqrt(distX*distX + distY*distY);
					var angleRad = Math.atan2(distY, distX) + Math.PI;

					var grabber = $('

');
					boardElem.append(grabber);
					grabber
						.css({
							left: p1.x,
							top: p1.y,
							width: 1,
							transform: 'rotate('+angleRad+'rad)'
						})
						.animate({
							width: distTot
						}, callback)
				};
			}
		};
	});<|endoftext|>


<commit_before>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

$(document).ready(function () {
  if ( $('.inspection-new-express').length > 0 ) {
    enableExpressInspectionDropdowns()
  }
});

function enableExpressInspectionDropdowns () {

  $('.inspection-supercategories').on('change', function () {
    $.getJSON('/api/v1/inspection_types?supercategory='+this.value, function (inspection_types) {
      inspection_types = _.uniqBy(inspection_types, function (i) { return i.inspection_category + i.inspection_name; })

      $field = $('.inspection-names')

      if ($('.chosen-container').length) {
        $field.chosen("destroy"); // only do this if the element is present - creates a phantom Chosen box if none exists
      }

      $field.show().html( $("<option>") ) // start with a blank
      $(inspection_types).each(function (index, type) {
        $field.append( $("<option>", { value: type.id, html: (type.inspection_category_name+' - '+type.inspection_name), "data-comments": type.comments }) )
      })
      $field.chosen({
        allow_single_deselect: true,
        no_results_text: 'No results matched',
        width: '300px',
      });
    })
  })

  $('.inspection-names').on('change', function () {
    $('#inspection_inspection_type_id').val( $(this).val() );
    $('.inspection-type-comments').empty().text($(this).find(':selected').data('comments'))
  })

}
<commit_msg>Scope your JS variables, folks. This ain't Ruby.<commit_after>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

$(document).ready(function () {
  if ( $('.inspection-new-express').length > 0 ) {
    enableExpressInspectionDropdowns()
  }
});

function enableExpressInspectionDropdowns () {

  $('.inspection-supercategories').on('change', function () {
    $.getJSON('/api/v1/inspection_types?supercategory='+this.value, function (inspection_types) {
      var inspection_types = _.uniqBy(inspection_types, function (i) { return i.inspection_category + i.inspection_name; })

      var $field = $('.inspection-names')

      if ($('.chosen-container').length) {
        $field.chosen("destroy"); // only do this if the element is present - creates a phantom Chosen box if none exists
      }

      $field.show().html( $("<option>") ) // start with a blank
      $(inspection_types).each(function (index, type) {
        $field.append( $("<option>", { value: type.id, html: (type.inspection_category_name+' - '+type.inspection_name), "data-comments": type.comments }) )
      })
      $field.chosen({
        allow_single_deselect: true,
        no_results_text: 'No results matched',
        width: '300px',
      });
    })
  })

  $('.inspection-names').on('change', function () {
    $('#inspection_inspection_type_id').val( $(this).val() );
    $('.inspection-type-comments').empty().text($(this).find(':selected').data('comments'))
  })

}
<|endoftext|>


<commit_before>import React from 'react';

class TextContainer extends React.PureComponent {
  render() {
    const { idyll, children, ...props } = this.props;
    return (

{this.props.children}

    );
  }
}

export default TextContainer;
<commit_msg>remove container component<commit_after><|endoftext|>


<commit_before>/* global Flash */
import Service from '../services/repo_service';
import Store from '../stores/repo_store';
import '../../flash';

const RepoHelper = {
  getDefaultActiveFile() {
    return {
      active: true,
      binary: false,
      extension: '',
      html: '',
      mime_type: '',
      name: 'loading...',
      plain: '',
      size: 0,
      url: '',
      raw: false,
      newContent: '',
      changed: false,
      loading: false,
    };
  },

  key: '',

  isTree(data) {
    return Object.hasOwnProperty.call(data, 'blobs');
  },

  Time: window.performance
  && window.performance.now
  ? window.performance
  : Date,

  getBranch() {
    return $('button.dropdown-menu-toggle').attr('data-ref');
  },

  getLanguageIDForFile(file, langs) {
    const ext = file.name.split('.').pop();
    const foundLang = RepoHelper.findLanguage(ext, langs);

    return foundLang ? foundLang.id : 'plaintext';
  },

  getFilePathFromFullPath(fullPath, branch) {
    return fullPath.split(branch)[1];
  },

  findLanguage(ext, langs) {
    return langs.find(lang => lang.extensions && lang.extensions.indexOf(`.${ext}`) > -1);
  },

  setDirectoryOpen(tree) {
    const file = tree;
    if (!file) return undefined;

    file.opened = true;
    file.icon = 'fa-folder-open';
    RepoHelper.toURL(file.url, file.name);
    return file;
  },

  isKindaBinary() {
    const okExts = ['md', 'svg'];
    return okExts.indexOf(Store.activeFile.extension) > -1;
  },

  setBinaryDataAsBase64(file) {
    Service.getBase64Content(file.raw_path)
    .then((response) => {
      Store.blobRaw = response;
      file.base64 = response; // eslint-disable-line no-param-reassign
    })
    .catch(RepoHelper.loadingError);
  },

  toggleFakeTab(loading, file) {
    if (loading) return Store.addPlaceholderFile();
    return Store.removeFromOpenedFiles(file);
  },

  setLoading(loading, file) {
    if (Service.url.indexOf('blob') > -1) {
      Store.loading.blob = loading;
      return RepoHelper.toggleFakeTab(loading, file);
    }

    if (Service.url.indexOf('tree') > -1) Store.loading.tree = loading;

    return undefined;
  },

  getNewMergedList(inDirectory, currentList, newList) {
    const newListSorted = newList.sort(this.compareFilesCaseInsensitive);
    if (!inDirectory) return newListSorted;
    const indexOfFile = currentList.findIndex(file => file.url === inDirectory.url);
    if (!indexOfFile) return newListSorted;
    return RepoHelper.mergeNewListToOldList(newListSorted, currentList, inDirectory, indexOfFile);
  },

  mergeNewListToOldList(newList, oldList, inDirectory, indexOfFile) {
    newList.reverse().forEach((newFile) => {
      const fileIndex = indexOfFile + 1;
      const file = newFile;
      file.level = inDirectory.level + 1;
      oldList.splice(fileIndex, 0, file);
    });

    return oldList;
  },

  compareFilesCaseInsensitive(a, b) {
    const aName = a.name.toLowerCase();
    const bName = b.name.toLowerCase();
    if (a.level > 0) return 0;
    if (aName < bName) { return -1; }
    if (aName > bName) { return 1; }
    return 0;
  },

  isRoot(url) {
    // the url we are requesting -> split by the project URL. Grab the right side.
    const isRoot = !!url.split(Store.projectUrl)[1]
    // remove the first "/"
    .slice(1)
    // split this by "/"
    .split('/')
    // remove the first two items of the array... usually /tree/master.
    .slice(2)
    // we want to know the length of the array.
    // If greater than 0 not root.
    .length;
    return isRoot;
  },

  getContent(treeOrFile, cb) {
    let file = treeOrFile;
    // const loadingData = RepoHelper.setLoading(true);
    return Service.getContent()
    .then((response) => {
      const data = response.data;
      // RepoHelper.setLoading(false, loadingData);
      if (cb) cb();
      Store.isTree = RepoHelper.isTree(data);
      if (!Store.isTree) {
        if (!file) file = data;
        Store.binary = data.binary;

        if (data.binary) {
          Store.binaryMimeType = data.mime_type;
          // file might be undefined
          RepoHelper.setBinaryDataAsBase64(data);
          Store.setViewToPreview();
        } else if (!Store.isPreviewView()) {
          Service.getRaw(data.raw_path)
            .then((rawResponse) => {
              Store.blobRaw = rawResponse.data;
              data.plain = rawResponse.data;

              RepoHelper.setFile(data, file);
            }).catch(RepoHelper.loadingError);
        }

        if (Store.isPreviewView()) RepoHelper.setFile(data, file);

        // if the file tree is empty
        if (Store.files.length === 0) {
          const parentURL = Service.blobURLtoParentTree(Service.url);
          Service.url = parentURL;
          RepoHelper.getContent();
        }
      } else {
        // it's a tree
        if (!file) Store.isRoot = RepoHelper.isRoot(Service.url);
        file = RepoHelper.setDirectoryOpen(file);
        const newDirectory = RepoHelper.dataToListOfFiles(data);
        Store.addFilesToDirectory(file, Store.files, newDirectory);
        Store.prevURL = Service.blobURLtoParentTree(Service.url);
      }
    }).catch((e) => {
      console.log(e);
      RepoHelper.loadingError();
    });
  },

  setFile(data, file) {
    const newFile = data;

    newFile.url = file.url || location.pathname;
    newFile.url = file.url;
    newFile.newContent = '';

    Store.addToOpenedFiles(newFile);
    Store.setActiveFiles(newFile);
  },

  toFA(icon) {
    return `fa-${icon}`;
  },

  serializeBlob(blob) {
    const simpleBlob = RepoHelper.serializeRepoEntity('blob', blob);
    simpleBlob.lastCommitMessage = blob.last_commit.message;
    simpleBlob.lastCommitUpdate = blob.last_commit.committed_date;
    simpleBlob.loading = false;

    return simpleBlob;
  },

  serializeTree(tree) {
    return RepoHelper.serializeRepoEntity('tree', tree);
  },

  serializeSubmodule(submodule) {
    return RepoHelper.serializeRepoEntity('submodule', submodule);
  },

  serializeRepoEntity(type, entity) {
    const { url, name, icon, last_commit } = entity;
    return {
      type,
      name,
      url,
      lastCommitUrl: `${Store.projectUrl}/commit/${last_commit.id}`,
      icon: RepoHelper.toFA(icon),
      level: 0,
      loading: false,
    };
  },

  scrollTabsRight() {
    // wait for the transition. 0.1 seconds.
    setTimeout(() => {
      const tabs = document.getElementById('tabs');
      if (!tabs) return;
      tabs.scrollLeft = 12000;
    }, 200);
  },

  dataToListOfFiles(data) {
    const a = [];

    // push in blobs
    data.blobs.forEach((blob) => {
      a.push(RepoHelper.serializeBlob(blob));
    });

    data.trees.forEach((tree) => {
      a.push(RepoHelper.serializeTree(tree));
    });

    data.submodules.forEach((submodule) => {
      a.push(RepoHelper.serializeSubmodule(submodule));
    });

    return a;
  },

  genKey() {
    return RepoHelper.Time.now().toFixed(3);
  },

  getStateKey() {
    return RepoHelper.key;
  },

  setStateKey(key) {
    RepoHelper.key = key;
  },

  toURL(url, title) {
    const history = window.history;

    RepoHelper.key = RepoHelper.genKey();

    history.pushState({ key: RepoHelper.key }, '', url);

    if (title) {
      document.title = `${title} · GitLab`;
    }
  },

  findOpenedFileFromActive() {
    return Store.openedFiles.find(openedFile => Store.activeFile.url === openedFile.url);
  },

  loadingError() {
    Flash('Unable to load the file at this time.');
  },
};

export default RepoHelper;
<commit_msg>Remove console.log<commit_after>/* global Flash */
import Service from '../services/repo_service';
import Store from '../stores/repo_store';
import '../../flash';

const RepoHelper = {
  getDefaultActiveFile() {
    return {
      active: true,
      binary: false,
      extension: '',
      html: '',
      mime_type: '',
      name: 'loading...',
      plain: '',
      size: 0,
      url: '',
      raw: false,
      newContent: '',
      changed: false,
      loading: false,
    };
  },

  key: '',

  isTree(data) {
    return Object.hasOwnProperty.call(data, 'blobs');
  },

  Time: window.performance
  && window.performance.now
  ? window.performance
  : Date,

  getBranch() {
    return $('button.dropdown-menu-toggle').attr('data-ref');
  },

  getLanguageIDForFile(file, langs) {
    const ext = file.name.split('.').pop();
    const foundLang = RepoHelper.findLanguage(ext, langs);

    return foundLang ? foundLang.id : 'plaintext';
  },

  getFilePathFromFullPath(fullPath, branch) {
    return fullPath.split(branch)[1];
  },

  findLanguage(ext, langs) {
    return langs.find(lang => lang.extensions && lang.extensions.indexOf(`.${ext}`) > -1);
  },

  setDirectoryOpen(tree) {
    const file = tree;
    if (!file) return undefined;

    file.opened = true;
    file.icon = 'fa-folder-open';
    RepoHelper.toURL(file.url, file.name);
    return file;
  },

  isKindaBinary() {
    const okExts = ['md', 'svg'];
    return okExts.indexOf(Store.activeFile.extension) > -1;
  },

  setBinaryDataAsBase64(file) {
    Service.getBase64Content(file.raw_path)
    .then((response) => {
      Store.blobRaw = response;
      file.base64 = response; // eslint-disable-line no-param-reassign
    })
    .catch(RepoHelper.loadingError);
  },

  toggleFakeTab(loading, file) {
    if (loading) return Store.addPlaceholderFile();
    return Store.removeFromOpenedFiles(file);
  },

  setLoading(loading, file) {
    if (Service.url.indexOf('blob') > -1) {
      Store.loading.blob = loading;
      return RepoHelper.toggleFakeTab(loading, file);
    }

    if (Service.url.indexOf('tree') > -1) Store.loading.tree = loading;

    return undefined;
  },

  getNewMergedList(inDirectory, currentList, newList) {
    const newListSorted = newList.sort(this.compareFilesCaseInsensitive);
    if (!inDirectory) return newListSorted;
    const indexOfFile = currentList.findIndex(file => file.url === inDirectory.url);
    if (!indexOfFile) return newListSorted;
    return RepoHelper.mergeNewListToOldList(newListSorted, currentList, inDirectory, indexOfFile);
  },

  mergeNewListToOldList(newList, oldList, inDirectory, indexOfFile) {
    newList.reverse().forEach((newFile) => {
      const fileIndex = indexOfFile + 1;
      const file = newFile;
      file.level = inDirectory.level + 1;
      oldList.splice(fileIndex, 0, file);
    });

    return oldList;
  },

  compareFilesCaseInsensitive(a, b) {
    const aName = a.name.toLowerCase();
    const bName = b.name.toLowerCase();
    if (a.level > 0) return 0;
    if (aName < bName) { return -1; }
    if (aName > bName) { return 1; }
    return 0;
  },

  isRoot(url) {
    // the url we are requesting -> split by the project URL. Grab the right side.
    const isRoot = !!url.split(Store.projectUrl)[1]
    // remove the first "/"
    .slice(1)
    // split this by "/"
    .split('/')
    // remove the first two items of the array... usually /tree/master.
    .slice(2)
    // we want to know the length of the array.
    // If greater than 0 not root.
    .length;
    return isRoot;
  },

  getContent(treeOrFile, cb) {
    let file = treeOrFile;
    // const loadingData = RepoHelper.setLoading(true);
    return Service.getContent()
    .then((response) => {
      const data = response.data;
      // RepoHelper.setLoading(false, loadingData);
      if (cb) cb();
      Store.isTree = RepoHelper.isTree(data);
      if (!Store.isTree) {
        if (!file) file = data;
        Store.binary = data.binary;

        if (data.binary) {
          Store.binaryMimeType = data.mime_type;
          // file might be undefined
          RepoHelper.setBinaryDataAsBase64(data);
          Store.setViewToPreview();
        } else if (!Store.isPreviewView()) {
          Service.getRaw(data.raw_path)
            .then((rawResponse) => {
              Store.blobRaw = rawResponse.data;
              data.plain = rawResponse.data;

              RepoHelper.setFile(data, file);
            }).catch(RepoHelper.loadingError);
        }

        if (Store.isPreviewView()) RepoHelper.setFile(data, file);

        // if the file tree is empty
        if (Store.files.length === 0) {
          const parentURL = Service.blobURLtoParentTree(Service.url);
          Service.url = parentURL;
          RepoHelper.getContent();
        }
      } else {
        // it's a tree
        if (!file) Store.isRoot = RepoHelper.isRoot(Service.url);
        file = RepoHelper.setDirectoryOpen(file);
        const newDirectory = RepoHelper.dataToListOfFiles(data);
        Store.addFilesToDirectory(file, Store.files, newDirectory);
        Store.prevURL = Service.blobURLtoParentTree(Service.url);
      }
    }).catch(RepoHelper.loadingError);
  },

  setFile(data, file) {
    const newFile = data;

    newFile.url = file.url || location.pathname;
    newFile.url = file.url;
    newFile.newContent = '';

    Store.addToOpenedFiles(newFile);
    Store.setActiveFiles(newFile);
  },

  toFA(icon) {
    return `fa-${icon}`;
  },

  serializeBlob(blob) {
    const simpleBlob = RepoHelper.serializeRepoEntity('blob', blob);
    simpleBlob.lastCommitMessage = blob.last_commit.message;
    simpleBlob.lastCommitUpdate = blob.last_commit.committed_date;
    simpleBlob.loading = false;

    return simpleBlob;
  },

  serializeTree(tree) {
    return RepoHelper.serializeRepoEntity('tree', tree);
  },

  serializeSubmodule(submodule) {
    return RepoHelper.serializeRepoEntity('submodule', submodule);
  },

  serializeRepoEntity(type, entity) {
    const { url, name, icon, last_commit } = entity;
    return {
      type,
      name,
      url,
      lastCommitUrl: `${Store.projectUrl}/commit/${last_commit.id}`,
      icon: RepoHelper.toFA(icon),
      level: 0,
      loading: false,
    };
  },

  scrollTabsRight() {
    // wait for the transition. 0.1 seconds.
    setTimeout(() => {
      const tabs = document.getElementById('tabs');
      if (!tabs) return;
      tabs.scrollLeft = 12000;
    }, 200);
  },

  dataToListOfFiles(data) {
    const a = [];

    // push in blobs
    data.blobs.forEach((blob) => {
      a.push(RepoHelper.serializeBlob(blob));
    });

    data.trees.forEach((tree) => {
      a.push(RepoHelper.serializeTree(tree));
    });

    data.submodules.forEach((submodule) => {
      a.push(RepoHelper.serializeSubmodule(submodule));
    });

    return a;
  },

  genKey() {
    return RepoHelper.Time.now().toFixed(3);
  },

  getStateKey() {
    return RepoHelper.key;
  },

  setStateKey(key) {
    RepoHelper.key = key;
  },

  toURL(url, title) {
    const history = window.history;

    RepoHelper.key = RepoHelper.genKey();

    history.pushState({ key: RepoHelper.key }, '', url);

    if (title) {
      document.title = `${title} · GitLab`;
    }
  },

  findOpenedFileFromActive() {
    return Store.openedFiles.find(openedFile => Store.activeFile.url === openedFile.url);
  },

  loadingError() {
    Flash('Unable to load the file at this time.');
  },
};

export default RepoHelper;
<|endoftext|>