<commit_before>var aws = require('aws-sdk')
    ,log = require('../../../libs/log')(module)
    ,config = require('../../../config/index')
    ,rootBucket = config.get('recordFile:fileRoot')
    ,SAVE_FILE_TYPE = require('../../../const/index').SAVE_FILE_TYPE
    ,fileDB = require('../../../middleware/fileDB')
    ,fs = require('fs')
    ,maskPath = config.get("recordFile:maskPath");

aws.config.update(config.get("recordFile:awsConfig"));
aws.config.httpOptions = {timeout: 5000};

// Create an S3 client
var s3 = new aws.S3();

var bucketName = (rootBucket)
        ? rootBucket.replace(/\//g,'')
        : 'webitel';

function saveToFile(file, query, res) {
    var uuid = query.id || new Date().getTime();
    var type = query.type || 'mp3';
    var keyName = uuid + '.' + type;
    var date = new Date();
    var domain = query.domain;
    var localFileTmp = file['path'];
    var fileSize = file["size"];

    var path = maskPath || '';

    path = path.replace(/\$Y/g, date.getFullYear()).replace(/\$M/g, (date.getMonth() + 1)).
        replace(/\$D/g, date.getDate()).replace(/\$H/g, date.getHours()).
        replace(/\$m/g, date.getMinutes());

    path += '/' + keyName;

//    s3.createBucket({Bucket: bucketName}, function() {
        var fileStream = fs.createReadStream(localFileTmp);
        fileStream.on('error', function (err) {
            if (err) {
                log.error(err);
                res.status(500).send(err);
            }
        });
        fileStream.on('open', function () {
            s3.putObject({
                Bucket: bucketName,
                Key: path,
                Body: fileStream,
                ContentType: res['incoming-content-type']
            }, function (err) {
                if (err) {
                    log.error(err);
                    res.status(500).send(err);
                    return;
                };
                fileDB.insertFile({
                    "uuid": uuid,
                    "path": path,
                    "domain": domain,
                    "content-type": res['incoming-content-type'],
                    "bucketName": bucketName,
                    "type": SAVE_FILE_TYPE.S3,
                    "createdOn": new Date(),
                    "size": fileSize,
                    "requestCount": 0
                }, function (err) {
                    if (err) return log.error(err.message);
                    res.send(204);
                    log.trace("save file %s - OK", path);

                    fs.unlink(localFileTmp, function (err) {
                        if (err)
                            log.error(err);
                    });
                });
            });
        });
  //  });
};

module.exports.SaveFile = function(req, res, file) {
    var query = req.query;
    saveToFile(file, query, res);
};

module.exports.getMediaStream = function (req, res, file) {
    try {
        var params = {
            Bucket: file['bucketName'],
            Key: file['path']
        };
        var url = s3.getSignedUrl('getObject', params);
        res.writeHead(302, {
            'Location': url,
            'Content-Type': file['content-type']
        });
        res.end();
    } catch (e) {
        log.error(e.message);
    };
};

module.exports.deleteFile = function (file, callback) {
    var params = {
        Bucket: file['bucketName'],
        Key: file['path']
    };
    s3.deleteObject(params, callback);
};<commit_msg>bug fix s3 transport<commit_after>var aws = require('aws-sdk')
    ,log = require('../../../libs/log')(module)
    ,config = require('../../../config/index')
    ,rootBucket = config.get('recordFile:fileRoot')
    ,SAVE_FILE_TYPE = require('../../../const/index').SAVE_FILE_TYPE
    ,fileDB = require('../../../middleware/fileDB')
    ,fs = require('fs')
    ,maskPath = config.get("recordFile:maskPath");

aws.config.update(config.get("recordFile:awsConfig"));
aws.config.httpOptions = {timeout: 5000};

// Create an S3 client
var s3 = new aws.S3();

var bucketName = (rootBucket)
        ? rootBucket.replace(/\//g,'')
        : 'webitel';

function saveToFile(file, query, res) {
    var uuid = query.id || new Date().getTime();
    var type = query.type || 'mp3';
    var keyName = uuid + '.' + type;
    var date = new Date();
    var domain = query.domain;
    var localFileTmp = file['path'];
    var fileSize = file["size"];

    var path = maskPath || '';

    path = path.replace(/\$Y/g, date.getFullYear()).replace(/\$M/g, (date.getMonth() + 1)).
        replace(/\$D/g, date.getDate()).replace(/\$H/g, date.getHours()).
        replace(/\$m/g, date.getMinutes());

    path += '/' + keyName;

//    s3.createBucket({Bucket: bucketName}, function() {
        var fileStream = fs.createReadStream(localFileTmp);
        fileStream.on('error', function (err) {
            if (err) {
                log.error(err);
                res.status(500).send(err);
            }
        });
        fileStream.on('open', function () {
            s3.putObject({
                Bucket: bucketName,
                Key: path,
                Body: fileStream,
                ContentType: res['incoming-content-type'] || 'audio/mpeg'
            }, function (err) {
                if (err) {
                    log.error(err);
                    res.status(500).send(err);
                    return;
                };
                fileDB.insertFile({
                    "uuid": uuid,
                    "path": path,
                    "domain": domain,
                    "content-type": res['incoming-content-type'] || 'audio/mpeg',
                    "bucketName": bucketName,
                    "type": SAVE_FILE_TYPE.S3,
                    "createdOn": new Date(),
                    "size": fileSize,
                    "requestCount": 0
                }, function (err) {
                    if (err) return log.error(err.message);
                    res.send(204);
                    log.trace("save file %s - OK", path);

                    fs.unlink(localFileTmp, function (err) {
                        if (err)
                            log.error(err);
                    });
                });
            });
        });
  //  });
};

module.exports.SaveFile = function(req, res, file) {
    var query = req.query;
    saveToFile(file, query, res);
};

module.exports.getMediaStream = function (req, res, file) {
    try {
        var params = {
            Bucket: file['bucketName'],
            Key: file['path']
        };
        var url = s3.getSignedUrl('getObject', params);
        res.writeHead(302, {
            'Location': url,
            'Content-Type': file['content-type'] || 'audio/mpeg'
        });
        res.end();
    } catch (e) {
        log.error(e.message);
    };
};

module.exports.deleteFile = function (file, callback) {
    var params = {
        Bucket: file['bucketName'],
        Key: file['path']
    };
    s3.deleteObject(params, callback);
};<|endoftext|>


<commit_before>import test from 'tape'
import rethink from 'rethinkdb'
import countBookmarks from '../../../helpers/countBookmarks'
import { bookmarks, connection } from '../../../config/rethinkdb'

test('countBookmarks helper', (t) => {
  t.plan(2)

  setTimeout(() => {
    countBookmarks((count) => {
      t.ok(count, 'It provides a bookmark count')

      bookmarks.insert({
        title: 'test',
        url: 'https://google.com',
        createdOn: rethink.now()
      }).run(connection, (err) => {
        if (err) {
          t.fail('It failed because: ', err)
        }

        countBookmarks((newCount) => {
          t.equal(newCount, count + 1, 'It correctly counts after a modification to the table')
          t.end()
        })
      })
    })
  }, 1000)
})
<commit_msg>Update countBookmarks test because 0 is falsy<commit_after>import test from 'tape'
import rethink from 'rethinkdb'
import countBookmarks from '../../../helpers/countBookmarks'
import { bookmarks, connection } from '../../../config/rethinkdb'

test('countBookmarks helper', (t) => {
  t.plan(2)

  setTimeout(() => {
    countBookmarks((count) => {
      t.equal(typeof count, 'number', 'It provides a bookmark count')

      bookmarks.insert({
        title: 'test',
        url: 'https://google.com',
        createdOn: rethink.now()
      }).run(connection, (err) => {
        if (err) {
          t.fail('It failed because: ', err)
        }

        countBookmarks((newCount) => {
          t.equal(newCount, count + 1, 'It correctly counts after a modification to the table')
          t.end()
        })
      })
    })
  }, 1000)
})
<|endoftext|>


<commit_before>const _ = require('lodash');
const app = require('../../../src/app');

const getAllRoutes = () => {
	// eslint-disable-next-line no-underscore-dangle
	const routes = app._router.stack.filter((x) => x.route && x.route.path && x.route.path !== '*').map((x) => x.route);
	const groupedRoutes = _.groupBy(routes, (r) => r.path.replace(/\/:__feathersId$/, ''));
	return Object.fromEntries(
		Object.entries(groupedRoutes).map(([unescapedRoute, group]) => {
			const route = unescapedRoute.replace(/\/:([^/]*)?($|\/)/, '/{$1}/').replace(/\/$/, '');
			const name = route.replace(/^\//, '');
			const methods = _.uniq(group.map((r) => Object.keys(r.methods).filter((k) => r.methods[k])).flat());

			return [name, { methods, route, name }];
		})
	);
};

module.exports = getAllRoutes;
<commit_msg>SC-6506 extract regular expressions to functions<commit_after>const _ = require('lodash');
const app = require('../../../src/app');

const removeLeadingSlash = (str) => str.replace(/^\//, '');
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
const escapeUrlParameter = (url) => url.replace(/\/:([^/]*)?($|\/)/, '/{$1}/');

const getAllRoutes = () => {
	// eslint-disable-next-line no-underscore-dangle
	const routes = app._router.stack.filter((x) => x.route && x.route.path && x.route.path !== '*').map((x) => x.route);
	const groupedRoutes = _.groupBy(routes, (r) => r.path.replace(/\/:__feathersId$/, ''));
	return Object.fromEntries(
		Object.entries(groupedRoutes).map(([unescapedRoute, group]) => {
			const route = removeTrailingSlash(escapeUrlParameter(unescapedRoute));
			const name = removeLeadingSlash(route);
			const methods = _.uniq(group.map((r) => Object.keys(r.methods).filter((k) => r.methods[k])).flat());

			return [name, { methods, route, name }];
		})
	);
};

module.exports = getAllRoutes;
<|endoftext|>


<commit_before>var util = require('util');
var twitter = require('twitter');
var NodeGeocoder = require('node-geocoder');

var gpsOptions = {
  provider: process.env.GeoCoderProvider,
  httpAdapter: 'https', // Default
  apiKey: process.env.GeoCoderApiKey, // for Mapquest, OpenCage, Google Premier
  formatter: null         // 'gpx', 'string', ...
};

var twit = new twitter({
    consumer_key: process.env.TwitterConsumerKey,
    consumer_secret: process.env.TwitterConsumerSecret,
    access_token_key: process.env.TwitterAccessTokenKey,
    access_token_secret: process.env.TwitterAccessTokenSecret
});

module.exports = function (context, message) {
    return twit.get(`statuses/show/${message.tweetid}`, { include_entities: true })
    .then(getLocation)
    .then(getImages)
    .then(getHistory)
    .then(setOutputBinding)
    .then(logTweetHistory)

    function setOutputBinding(message){
        context.bindings.out = message;
        return message;
    }

    function getLocation(message) {
    var tweetLocation = message.place.full_name + ' ' +   message.place.country_code;
    context.log(tweetLocation);

    if(message.place.country_code == 'US') {
        // Get GPS from Tweet
        if (message.coordinates != null && 2 <= message.coordinates.coordinates.length) {
            message.latitude = message.coordinates.coordinates[0];
            message.longitude = message.coordinates.coordinates[1];
            context.log('Lon: ' + message.longitude, 'Lat: ' + message.latitude);
        }
        else if (message.place.full_name != null) { // Get GPS via 3rd party GeoLocation module
        var geocoder = NodeGeocoder(gpsOptions);

        return new Promise((resolve, reject) => {
            geocoder.geocode(tweetLocation, function(err, res) {
            if (err)
                return reject(err);
            message.latitude = res[0].latitude;
            message.longitude  = res[0].longitude;
            context.log('Lon: ' + message.longitude, 'Lat: ' + message.latitude);
            resolve(message);
            });
        });
        }
    }
    return message;
    }

    function getImages(message) {
        // Add photo urls
    message.photourls = [];
    if(message.entities.media.length > 0) {
        message.entities.media.forEach(function(item) {
            message.photourls.push(item.media_url);
        });
    }

    // Print photo urls
    message.photourls.forEach(function(photourl) {
        context.log(photourl);
    });

    return message;
    }

    // Get all past tweets that contains user's handle
    function getHistory(message) {
    message.tweethistory_ids = [];
    var params = {
        q: options.screen_name,  // REQUIRED
        result_type: 'mixed',
        lang: 'en',
        max_id: message.tweetid
    };

    if (message.latitude && message.longitude) {
        params.geocode = `${message.latitude},${message.longitude},${process.env.TweetSearchRadius}km`;
    }

    return twit.get('search/tweets', params)
        .then(historyData => {
        message.tweethistory_ids = historyData.statuses
            .map(statusItem => statusItem.id_str);

        return message;
        });
    }

    function logTweetHistory(message) {
        var loggingPromises = message.tweethistory_ids.map(historyId => {
            twit.get(`statuses/show/${historyId}`, { include_entities: true })
                .then(msg => {
                context.log('tweethistory_id:' + historyId);
                context.log(msg.text);
                });
            });

        return Promise.all(loggingPromises)
            .then(() => message);
    }
};<commit_msg>formatting<commit_after>var util = require('util');
var twitter = require('twitter');
var NodeGeocoder = require('node-geocoder');

var gpsOptions = {
    provider: process.env.GeoCoderProvider,
    httpAdapter: 'https', // Default
    apiKey: process.env.GeoCoderApiKey, // for Mapquest, OpenCage, Google Premier
    formatter: null         // 'gpx', 'string', ...
};

var twit = new twitter({
    consumer_key: process.env.TwitterConsumerKey,
    consumer_secret: process.env.TwitterConsumerSecret,
    access_token_key: process.env.TwitterAccessTokenKey,
    access_token_secret: process.env.TwitterAccessTokenSecret
});

module.exports = function (context, message) {
    return twit.get(`statuses/show/${message.tweetid}`, { include_entities: true })
        .then(getLocation)
        .then(getImages)
        .then(getHistory)
        .then(setOutputBinding)
        .then(logTweetHistory)

    function setOutputBinding(message) {
        context.bindings.out = message;
        return message;
    }

    function getLocation(message) {
        var tweetLocation = message.place.full_name + ' ' + message.place.country_code;
        context.log(tweetLocation);

        if (message.place.country_code == 'US') {
            // Get GPS from Tweet
            if (message.coordinates != null && 2 <= message.coordinates.coordinates.length) {
                message.latitude = message.coordinates.coordinates[0];
                message.longitude = message.coordinates.coordinates[1];
                context.log('Lon: ' + message.longitude, 'Lat: ' + message.latitude);
            }
            else if (message.place.full_name != null) { // Get GPS via 3rd party GeoLocation module
                var geocoder = NodeGeocoder(gpsOptions);

                return new Promise((resolve, reject) => {
                    geocoder.geocode(tweetLocation, function (err, res) {
                        if (err)
                            return reject(err);
                        message.latitude = res[0].latitude;
                        message.longitude = res[0].longitude;
                        context.log('Lon: ' + message.longitude, 'Lat: ' + message.latitude);
                        resolve(message);
                    });
                });
            }
        }
        return message;
    }

    function getImages(message) {
        // Add photo urls
        message.photourls = [];
        if (message.entities.media.length > 0) {
            message.entities.media.forEach(function (item) {
                message.photourls.push(item.media_url);
            });
        }

        // Print photo urls
        message.photourls.forEach(function (photourl) {
            context.log(photourl);
        });

        return message;
    }

    // Get all past tweets that contains user's handle
    function getHistory(message) {
        message.tweethistory_ids = [];
        var params = {
            q: options.screen_name,  // REQUIRED
            result_type: 'mixed',
            lang: 'en',
            max_id: message.tweetid
        };

        if (message.latitude && message.longitude) {
            params.geocode = `${message.latitude},${message.longitude},${process.env.TweetSearchRadius}km`;
        }

        return twit.get('search/tweets', params)
            .then(historyData => {
                message.tweethistory_ids = historyData.statuses
                    .map(statusItem => statusItem.id_str);

                return message;
            });
    }

    function logTweetHistory(message) {
        var loggingPromises = message.tweethistory_ids.map(historyId => {
            twit.get(`statuses/show/${historyId}`, { include_entities: true })
                .then(msg => {
                    context.log('tweethistory_id:' + historyId);
                    context.log(msg.text);
                });
        });

        return Promise.all(loggingPromises)
            .then(() => message);
    }
};<|endoftext|>


<commit_before>/*global describe, beforeEach, module, inject, it, angular, expect */
/*jslint nomen: true */
describe('Directive: vjs.directive.js', function () {
    'use strict';

    // load the directive's module
    beforeEach(module('vjsVideoApp'));

    var nonVidStr = "<div vjs-video>",
        scope,
        $compile;

    beforeEach(inject(function ($rootScope, _$compile_) {
        scope = $rootScope.$new();
        $compile = _$compile_;
    }));

    function compileAndLink(htmlStr, s) {
        var el = angular.element(htmlStr);

        el = $compile(el)(s);
        scope.$digest();

        return el;
    }

    it('should make hidden element visible', inject(function ($compile) {

        expect(function () {
            var el = compileAndLink(nonVidStr, scope);
        }).to.throw(Error);
    }));
});
<commit_msg>Added passing tests for if not attached to a video tag<commit_after>/*global describe, beforeEach, module, inject, it, angular, expect */
/*jslint nomen: true */
describe('Directive: vjs.directive.js', function () {
    'use strict';

    // load the directive's module
    beforeEach(module('vjsVideoApp'));

    var vidStr = "<video></video>",
        nonVidStr = "<div vjs-video>",
        scope,
        $compile;

    beforeEach(inject(function ($rootScope, _$compile_) {
        scope = $rootScope.$new();
        $compile = _$compile_;
    }));

    function compileAndLink(htmlStr, s) {
        var el = angular.element(htmlStr);

        el = $compile(el)(s);
        scope.$digest();

        return el;
    }

    it('should throw an error if not attached to a video tag', function () {
        expect(function () {
            var el = compileAndLink(nonVidStr, scope);
        }).to.throw(Error);

        expect(function () {
            var el = compileAndLink(vidStr, scope);
        }).to.not.throw(Error);
    });
});
<|endoftext|>


<commit_before>import { dependencies as Inject } from "needlepoint";
import CommandModule from "../CommandModule";
import ChatApi from "../../util/ChatApi";
import Configuration from "../../util/Configuration";
import Threads from "../../util/Threads";
import MarkovChain from "markovchain";
import Bro from "brototype";
import Db from "mongodb";

@Inject(ChatApi, Configuration, Threads, Db)
export default class QuoteCommand extends CommandModule {
    constructor(api, config, threads, db) {
        super();

        this.api = api;
        this.config = config;
        this.threads = threads;
        this.db = db;

        this.escape = this.config.get("app.commandEscape");
        this.maxMarkovSentenceWordCount = this.config.get("modules.quote.maxMarkovSentenceWordCount");
    }

    getCommand() {
        return "quote";
    }

    getDescription() {
        return "Generates a quote";
    }

    getUsage() {
        return "[<person> | me]";
    }

    validate(msg, argsString) {
        return true;
    }

    execute(msg, argsString) {
        const threadId = msg.threadID;
        const targetId = this.getTargetIdFromArgsString(msg, argsString);

        if (targetId === undefined) {
            return this.api.sendMessage("Literally who?", threadId);
        }

        const dbCollection = this.db.collection(`thread-${threadId}`);

        const messagesInDbCallback = dbCollection.find({
            "$and": [
                { senderID: "fbid:" + targetId },
                { attachments: { $size: 0 } },
                { body: { $exists: true } },
                { body: { $ne: "" } },
                { body: new RegExp("^(?!" + this.escape + ").+$") } // skip messages that start with command symbol
            ]
        }).toArray();

        let start = 0;
        const markovPromise = messagesInDbCallback
            .then(history => {
                start = Date.now();
                return history;
            })
            .then(history => history.map(m => m.body))
            .then(messages => {
                const chain = new MarkovChain();
                messages.forEach(m => chain.parse(m));

                const randomWordFn = wordList => {
                    const words = Object.keys(wordList);
                    const randomIndex = Math.floor(Math.random() * words.length);
                    return words[randomIndex];
                };

                const sentence = chain
                    .start(randomWordFn)
                    .end(this.maxMarkovSentenceWordCount)
                    .process();

                const end = Date.now();
                console.info(`user ${targetId}: Built Markov chain from ${messages.length} messages in ${end - start} ms`);

                return sentence;
            });

        const userNamePromise = this.api
            .getUserInfo(targetId)
            .then(info => {
                const bro = new Bro(info);
                const prop = targetId + ".name";

                if (!bro.doYouEven(prop)) {
                    console.error("targetId", targetId);
                    console.error("userInfo", info);
                    throw new Error("Could not get name");
                }

                return bro.iCanHaz(prop);
            });

        return Promise
            .all([markovPromise, userNamePromise])
            .then(result => {
                const markovSentence = result[0];
                const name = result[1];

                return "“" + markovSentence + "”" + "\n"
                    + "– " + name;
            })
            .then(quote => this.api.sendMessage(quote, threadId));
    }

    isCommand(body) {
        return body.startsWith(this.escape);
    }

    getTargetIdFromArgsString(msg, argsString) {
        if (!argsString || argsString.length == 0) {
            return msg.senderID;
        }

        argsString = argsString.trim().toLowerCase();

        if (argsString == "me") {
            return msg.senderID;
        }

        return this.threads.getUserIdByThreadIdAndAlias(msg.threadID, argsString);
    }
}<commit_msg>QuoteCommand: Support for Markov chains based on all messages<commit_after>import { dependencies as Inject } from "needlepoint";
import CommandModule from "../CommandModule";
import ChatApi from "../../util/ChatApi";
import Configuration from "../../util/Configuration";
import Threads from "../../util/Threads";
import MarkovChain from "markovchain";
import Bro from "brototype";
import Db from "mongodb";

@Inject(ChatApi, Configuration, Threads, Db)
export default class QuoteCommand extends CommandModule {
    constructor(api, config, threads, db) {
        super();

        this.api = api;
        this.config = config;
        this.threads = threads;
        this.db = db;

        this.escape = this.config.get("app.commandEscape");
        this.maxMarkovSentenceWordCount = this.config.get("modules.quote.maxMarkovSentenceWordCount");
    }

    getCommand() {
        return "quote";
    }

    getDescription() {
        return "Generates a quote";
    }

    getUsage() {
        return "[<person> | me]";
    }

    validate(msg, argsString) {
        return true;
    }

    execute(msg, argsString) {
        const threadId = msg.threadID;
        const targetId = this.getTargetIdFromArgsString(msg, argsString);

        if (targetId === undefined) {
            return this.api.sendMessage("Literally who?", threadId);
        }

        const filterList = [];
        if (targetId !== -1) {
            filterList.push({ senderID: "fbid:" + targetId });
        } else { // all
            filterList.push({ senderID: { $ne: "fbid:" + this.api.getCurrentUserId() } }); // skip messages by the bot
        }

        filterList.push(
            { attachments: { $size: 0 } },
            { body: { $exists: true } },
            { body: { $ne: "" } },
            { body: new RegExp("^(?!" + this.escape + ").+$") } // skip messages that start with command symbol
        );

        const messagesInDbCallback = this.db
            .collection(`thread-${threadId}`)
            .find({ "$and": filterList })
            .toArray();

        let start = 0;
        const markovPromise = messagesInDbCallback
            .then(history => {
                start = Date.now();
                return history;
            })
            .then(history => history.map(m => m.body))
            .then(messages => {
                const chain = new MarkovChain();
                messages.forEach(m => chain.parse(m));

                const randomWordFn = wordList => {
                    const words = Object.keys(wordList);
                    const randomIndex = Math.floor(Math.random() * words.length);
                    return words[randomIndex];
                };

                const sentence = chain
                    .start(randomWordFn)
                    .end(this.maxMarkovSentenceWordCount)
                    .process();

                const end = Date.now();
                console.info(`targetId ${targetId}: Built Markov chain from ${messages.length} messages in ${end - start} ms`);

                return sentence;
            });

        let userNamePromise;
        if (targetId !== -1) {
            userNamePromise = this.api
                .getUserInfo(targetId)
                .then(info => {
                    const bro = new Bro(info);
                    const prop = targetId + ".name";

                    if (!bro.doYouEven(prop)) {
                        console.error("targetId", targetId);
                        console.error("userInfo", info);
                        throw new Error("Could not get name");
                    }

                    return bro.iCanHaz(prop);
                });
        } else { // all
            userNamePromise = Promise.resolve("\u{1F464}")
        }

        return Promise
            .all([markovPromise, userNamePromise])
            .then(result => {
                const markovSentence = result[0];
                const name = result[1];

                return "“" + markovSentence + "”" + "\n"
                    + "– " + name;
            })
            .then(quote => this.api.sendMessage(quote, threadId));
    }

    isCommand(body) {
        return body.startsWith(this.escape);
    }

    getTargetIdFromArgsString(msg, argsString) {
        if (!argsString || argsString.length == 0) {
            return msg.senderID;
        }

        argsString = argsString.trim().toLowerCase();

        if (argsString == "me") {
            return msg.senderID;
        }

        if (argsString == "all" || argsString == "*") {
            return -1;
        }

        return this.threads.getUserIdByThreadIdAndAlias(msg.threadID, argsString);
    }
}<|endoftext|>


<commit_before>(function(){
    'use strict';

    describe('Service: gameService', function () {

        var gameServiceInstance, colorServiceInstance, squareServiceInstance;

        // load the controller's module
        beforeEach(function(){
            module('squares');
            inject(function(gameService, colorService, squareService){
                gameServiceInstance = gameService;
                colorServiceInstance = colorService;
                squareServiceInstance = squareService;
            })

        });

        it('Should have "" by default for OutComeText', function(){

            expect(gameServiceInstance.OutcomeText).toEqual('');
        });

        it('should return an 8x8 Array of 50x50 of SquareModels when getSquares is called', function(){

            var colorCount = {};
            var result = gameServiceInstance._.getSquares(colorCount, colorServiceInstance.SimpleColors);

            expect(result.length).toBe(8);
            for(var r=0;r<8;r++){
                expect(result[r].length).toBe(8);
                for(var c=0;c<8;c++){
                    var square = result[r][c];
                    expect(square.H).toEqual(50);
                    expect(square.W).toEqual(50);
                }
            }
        });

        it('should return 1000 when getSecondScore is called with Simple', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.Simple);
            expect(result).toEqual(1000);
        });

        it('should return 1500 when getSecondScore is called with Medium', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.Medium);
            expect(result).toEqual(1500);
        });

        it('should return 2500 when getSecondScore is called with Hard', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.Hard);
            expect(result).toEqual(2500);
        });

        it('should return 5000 when getSecondScore is called with VeryHard', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.VeryHard);
            expect(result).toEqual(5000);
        });

        it('Should have "0 Wins" by default for CurrentStreak', function(){

            expect(gameServiceInstance.CurrentStreak).toEqual('0 Wins');
        });

        it('Should return "1 Win" from _getCurrentStreak, when called with 0 Wins and true', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };
            var result = gameServiceInstance._.getCurrentStreak(true, streakData);

            expect(result).toEqual('1 Win');
        });

        it('Should return "2 Wins" from _getCurrentStreak, when called with 0 Wins and true twice', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            gameServiceInstance._.getCurrentStreak(true, streakData);
            var result = gameServiceInstance._.getCurrentStreak(true, streakData);

            expect(result).toEqual('2 Wins');
        });

        it('Should return "20 Wins" from _getCurrentStreak, when called with 0 Wins and true twenty times', function(){

            var result = null;
            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            for(var i=0;i<20;i++){
                result = gameServiceInstance._.getCurrentStreak(true, streakData);
            }

            expect(result).toEqual('20 Wins');
        });

        it('Should return "1 Loss" from _getCurrentStreak, when called with 0 Wins and false', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            var result = gameServiceInstance._.getCurrentStreak(false, streakData);

            expect(result).toEqual('1 Loss');
        });

        it('Should return "2 Losses" from _getCurrentStreak, when called with 0 Wins and false twice', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            gameServiceInstance._.getCurrentStreak(false, streakData);
            var result = gameServiceInstance._.getCurrentStreak(false, streakData);

            expect(result).toEqual('2 Losses');
        });

        it('Should return "1 Loss" from _getCurrentStreak, when called with 0 Wins and 5 trues followed by 1 false', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            var result;
            for(var i=0;i<5;i++){
                result = gameServiceInstance._.getCurrentStreak(true, streakData);
            }

            expect(result).toEqual('5 Wins');

            result = gameServiceInstance._.getCurrentStreak(false, streakData);

            expect(result).toEqual('1 Loss');
        });

        it('Should return true when scoregame is called with a color with the highest color count', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 6;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1000);
        });

        it('Should return score 5000 when scoregame is called with a color with the highest color count Simple Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1000);

        });

        it('Should return score 7500 when scoregame is called with a color with the highest color count Medium Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Medium, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1500);

        });

        it('Should return score 12500 when scoregame is called with a color with the highest color count Hard Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Hard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 2500);

        });

        it('Should return score 25000 when scoregame is called with a color with the highest color count VeryHard Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.VeryHard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 5000);

        });

        it('Should return true when scoregame is called with a color with a tying high color count', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 10;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 6;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1000);
        });

        it('Should return false and 0s score when scoregame is called with a color with a lower than highest color count', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 6;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 6;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeFalsy();
            expect(result.score).toBe(0);
        });

        it('Should return score 12500/3 when scoregame is called with a color with the highest color count Hard Mode and 5 seconds and retry 2', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 2;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Hard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(Math.round((seconds * 2500)/3));

        });

        it('Should return score 12500/9 when scoregame is called with a color with the highest color count Hard Mode and 5 seconds and retry 8', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 8;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Hard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(Math.round((seconds * 2500)/9));

        });

        it('Should set foo when initGame is called with isRetry = false', function(){

            var result = gameServiceInstance._.initGame(false);

        });

    });
}());
<commit_msg>adding a scoring unit test<commit_after>(function(){
    'use strict';

    describe('Service: gameService', function () {

        var gameServiceInstance, colorServiceInstance, squareServiceInstance;

        // load the controller's module
        beforeEach(function(){
            module('squares');
            inject(function(gameService, colorService, squareService){
                gameServiceInstance = gameService;
                colorServiceInstance = colorService;
                squareServiceInstance = squareService;
            })

        });

        it('Should have "" by default for OutComeText', function(){

            expect(gameServiceInstance.OutcomeText).toEqual('');
        });

        it('should return an 8x8 Array of 50x50 of SquareModels when getSquares is called', function(){

            var colorCount = {};
            var result = gameServiceInstance._.getSquares(colorCount, colorServiceInstance.SimpleColors);

            expect(result.length).toBe(8);
            for(var r=0;r<8;r++){
                expect(result[r].length).toBe(8);
                for(var c=0;c<8;c++){
                    var square = result[r][c];
                    expect(square.H).toEqual(50);
                    expect(square.W).toEqual(50);
                }
            }
        });

        it('should return 1000 when getSecondScore is called with Simple', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.Simple);
            expect(result).toEqual(1000);
        });

        it('should return 1500 when getSecondScore is called with Medium', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.Medium);
            expect(result).toEqual(1500);
        });

        it('should return 2500 when getSecondScore is called with Hard', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.Hard);
            expect(result).toEqual(2500);
        });

        it('should return 5000 when getSecondScore is called with VeryHard', function(){
            var result = gameServiceInstance._.getSecondScore(gameServiceInstance.GameModes.VeryHard);
            expect(result).toEqual(5000);
        });

        it('Should have "0 Wins" by default for CurrentStreak', function(){

            expect(gameServiceInstance.CurrentStreak).toEqual('0 Wins');
        });

        it('Should return "1 Win" from _getCurrentStreak, when called with 0 Wins and true', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };
            var result = gameServiceInstance._.getCurrentStreak(true, streakData);

            expect(result).toEqual('1 Win');
        });

        it('Should return "2 Wins" from _getCurrentStreak, when called with 0 Wins and true twice', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            gameServiceInstance._.getCurrentStreak(true, streakData);
            var result = gameServiceInstance._.getCurrentStreak(true, streakData);

            expect(result).toEqual('2 Wins');
        });

        it('Should return "20 Wins" from _getCurrentStreak, when called with 0 Wins and true twenty times', function(){

            var result = null;
            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            for(var i=0;i<20;i++){
                result = gameServiceInstance._.getCurrentStreak(true, streakData);
            }

            expect(result).toEqual('20 Wins');
        });

        it('Should return "1 Loss" from _getCurrentStreak, when called with 0 Wins and false', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            var result = gameServiceInstance._.getCurrentStreak(false, streakData);

            expect(result).toEqual('1 Loss');
        });

        it('Should return "2 Losses" from _getCurrentStreak, when called with 0 Wins and false twice', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            gameServiceInstance._.getCurrentStreak(false, streakData);
            var result = gameServiceInstance._.getCurrentStreak(false, streakData);

            expect(result).toEqual('2 Losses');
        });

        it('Should return "1 Loss" from _getCurrentStreak, when called with 0 Wins and 5 trues followed by 1 false', function(){

            var streakData = { Count: 0, Text: 'Wins', LastGameWon: false };

            var result;
            for(var i=0;i<5;i++){
                result = gameServiceInstance._.getCurrentStreak(true, streakData);
            }

            expect(result).toEqual('5 Wins');

            result = gameServiceInstance._.getCurrentStreak(false, streakData);

            expect(result).toEqual('1 Loss');
        });

        it('Should return true when scoregame is called with a color with the highest color count', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 6;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1000);
        });

        it('Should return score 5000 when scoregame is called with a color with the highest color count Simple Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1000);

        });

        it('Should return score 7500 when scoregame is called with a color with the highest color count Medium Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Medium, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1500);

        });

        it('Should return score 12500 when scoregame is called with a color with the highest color count Hard Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Hard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 2500);

        });

        it('Should return score 25000 when scoregame is called with a color with the highest color count VeryHard Mode and 5 seconds', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.VeryHard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 5000);

        });

        it('Should return true when scoregame is called with a color with a tying high color count', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 10;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 6;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(seconds * 1000);
        });

        it('Should return false and 0s score when scoregame is called with a color with a lower than highest color count', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 6;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 6;
            retryCount = 0;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Simple, seconds, retryCount);

            expect(result.gameWon).toBeFalsy();
            expect(result.score).toBe(0);
        });

        it('Should return score 12500/3 when scoregame is called with a color with the highest color count Hard Mode and 5 seconds and retry 2', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 2;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Hard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(Math.round((seconds * 2500)/3));

        });

        it('Should return score 12500/9 when scoregame is called with a color with the highest color count Hard Mode and 5 seconds and retry 8', function(){

            var color1, color2, color3, colorCounts, chosenSquare, seconds, retryCount, result;

            color1 = colorServiceInstance.GetRandomColor();
            color2 = colorServiceInstance.GetRandomColor();
            color3 = colorServiceInstance.GetRandomColor();

            colorCounts = {};
            colorCounts[color1.GetColorId()] = 10;
            colorCounts[color2.GetColorId()] = 12;
            colorCounts[color3.GetColorId()] = 8;

            chosenSquare = squareServiceInstance.GetSquare(10, color2);

            seconds = 5;
            retryCount = 8;

            result = gameServiceInstance._.scoreGame(chosenSquare, colorCounts, gameServiceInstance.GameModes.Hard, seconds, retryCount);

            expect(result.gameWon).toBeTruthy();
            expect(result.score).toBe(Math.round((seconds * 2500)/9));

        });

        it('Should reset the colorCounts when a new game is started', function(){

            var length, lastColorCounts, squares;

            length =  getObjLength(gameServiceInstance._.colorCounts);

            expect(length).toBe(0);

            gameServiceInstance.SetGameMode(gameServiceInstance.GameModes.simple);
            squares = gameServiceInstance.StartGame();
            lastColorCounts = gameServiceInstance._.colorCounts;
            length = getObjLength(lastColorCounts);
            expect(length).toBe(4);
            expect(getColorCountsTotal(lastColorCounts)).toBe(64);
            gameServiceInstance.EndGame(squares[0][0]);
            gameServiceInstance.StartGame();
            length = getObjLength(gameServiceInstance._.colorCounts);
            expect(length).toBe(4);
            expect(gameServiceInstance._.colorCounts).not.toEqual(lastColorCounts);
            expect(getColorCountsTotal(gameServiceInstance._.colorCounts)).toBe(64);

        })

    });

    var getObjLength = function(obj) {
        var length = 0, key;
        for (key in obj) {
            if (obj.hasOwnProperty(key)) {
                length++;
            }
        }
        return length;
    };

    var getColorCountsTotal = function(colorCounts) {
        var total = 0, key;
        for (key in colorCounts) {
            if (colorCounts.hasOwnProperty(key)) {
                total+= colorCounts[key];
            }
        }
        return total;
    };
}());
<|endoftext|>


<commit_before>const { Plugin } = require('@uppy/core')
const cuid = require('cuid')
const Translator = require('@uppy/utils/lib/Translator')
const { Provider, Socket } = require('@uppy/companion-client')
const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress')
const getSocketHost = require('@uppy/utils/lib/getSocketHost')
const settle = require('@uppy/utils/lib/settle')
const limitPromises = require('@uppy/utils/lib/limitPromises')

function buildResponseError (xhr, error) {
  // No error message
  if (!error) error = new Error('Upload error')
  // Got an error message string
  if (typeof error === 'string') error = new Error(error)
  // Got something else
  if (!(error instanceof Error)) {
    error = Object.assign(new Error('Upload error'), { data: error })
  }

  error.request = xhr
  return error
}

module.exports = class XHRUpload extends Plugin {
  constructor (uppy, opts) {
    super(uppy, opts)
    this.type = 'uploader'
    this.id = 'XHRUpload'
    this.title = 'XHRUpload'

    const defaultLocale = {
      strings: {
        timedOut: 'Upload stalled for %{seconds} seconds, aborting.'
      }
    }

    // Default options
    const defaultOptions = {
      formData: true,
      fieldName: 'files[]',
      method: 'post',
      metaFields: null,
      responseUrlFieldName: 'url',
      bundle: false,
      headers: {},
      locale: defaultLocale,
      timeout: 30 * 1000,
      limit: 0,
      withCredentials: false,
      responseType: '',
      /**
       * @typedef respObj
       * @property {string} responseText
       * @property {number} status
       * @property {string} statusText
       * @property {Object.<string, string>} headers
       *
       * @param {string} responseText the response body string
       * @param {XMLHttpRequest | respObj} response the response object (XHR or similar)
       */
      getResponseData (responseText, response) {
        let parsedResponse = {}
        try {
          parsedResponse = JSON.parse(responseText)
        } catch (err) {
          console.log(err)
        }

        return parsedResponse
      },
      /**
       *
       * @param {string} responseText the response body string
       * @param {XMLHttpRequest | respObj} response the response object (XHR or similar)
       */
      getResponseError (responseText, response) {
        return new Error('Upload error')
      }
    }

    // Merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    // i18n
    this.translator = new Translator([ defaultLocale, this.uppy.locale, this.opts.locale ])
    this.i18n = this.translator.translate.bind(this.translator)
    this.i18nArray = this.translator.translateArray.bind(this.translator)

    this.handleUpload = this.handleUpload.bind(this)

    // Simultaneous upload limiting is shared across all uploads with this plugin.
    if (typeof this.opts.limit === 'number' && this.opts.limit !== 0) {
      this.limitUploads = limitPromises(this.opts.limit)
    } else {
      this.limitUploads = (fn) => fn
    }

    if (this.opts.bundle && !this.opts.formData) {
      throw new Error('`opts.formData` must be true when `opts.bundle` is enabled.')
    }
  }

  getOptions (file) {
    const overrides = this.uppy.getState().xhrUpload
    const opts = Object.assign({},
      this.opts,
      overrides || {},
      file.xhrUpload || {}
    )
    opts.headers = {}
    Object.assign(opts.headers, this.opts.headers)
    if (overrides) {
      Object.assign(opts.headers, overrides.headers)
    }
    if (file.xhrUpload) {
      Object.assign(opts.headers, file.xhrUpload.headers)
    }

    return opts
  }

  // Helper to abort upload requests if there has not been any progress for `timeout` ms.
  // Create an instance using `timer = createProgressTimeout(10000, onTimeout)`
  // Call `timer.progress()` to signal that there has been progress of any kind.
  // Call `timer.done()` when the upload has completed.
  createProgressTimeout (timeout, timeoutHandler) {
    const uppy = this.uppy
    const self = this
    let isDone = false

    function onTimedOut () {
      uppy.log(`[XHRUpload] timed out`)
      const error = new Error(self.i18n('timedOut', { seconds: Math.ceil(timeout / 1000) }))
      timeoutHandler(error)
    }

    let aliveTimer = null
    function progress () {
      // Some browsers fire another progress event when the upload is
      // cancelled, so we have to ignore progress after the timer was
      // told to stop.
      if (isDone) return

      if (timeout > 0) {
        if (aliveTimer) clearTimeout(aliveTimer)
        aliveTimer = setTimeout(onTimedOut, timeout)
      }
    }

    function done () {
      uppy.log(`[XHRUpload] timer done`)
      if (aliveTimer) {
        clearTimeout(aliveTimer)
        aliveTimer = null
      }
      isDone = true
    }

    return {
      progress,
      done
    }
  }

  createFormDataUpload (file, opts) {
    const formPost = new FormData()

    const metaFields = Array.isArray(opts.metaFields)
      ? opts.metaFields
      // Send along all fields by default.
      : Object.keys(file.meta)
    metaFields.forEach((item) => {
      formPost.append(item, file.meta[item])
    })

    if (file.name) {
      formPost.append(opts.fieldName, file.data, file.name)
    } else {
      formPost.append(opts.fieldName, file.data)
    }

    return formPost
  }

  createBareUpload (file, opts) {
    return file.data
  }

  upload (file, current, total) {
    const opts = this.getOptions(file)

    this.uppy.log(`uploading ${current} of ${total}`)
    return new Promise((resolve, reject) => {
      const data = opts.formData
        ? this.createFormDataUpload(file, opts)
        : this.createBareUpload(file, opts)

      const timer = this.createProgressTimeout(opts.timeout, (error) => {
        xhr.abort()
        this.uppy.emit('upload-error', file, error)
        reject(error)
      })

      const xhr = new XMLHttpRequest()
      if (opts.responseType !== '') {
        xhr.responseType = opts.responseType
      }

      const id = cuid()

      xhr.upload.addEventListener('loadstart', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} started`)
        // Begin checking for timeouts when loading starts.
        timer.progress()
      })

      xhr.upload.addEventListener('progress', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} progress: ${ev.loaded} / ${ev.total}`)
        timer.progress()

        if (ev.lengthComputable) {
          this.uppy.emit('upload-progress', file, {
            uploader: this,
            bytesUploaded: ev.loaded,
            bytesTotal: ev.total
          })
        }
      })

      xhr.addEventListener('load', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} finished`)
        timer.done()

        if (ev.target.status >= 200 && ev.target.status < 300) {
          const body = opts.getResponseData(xhr.responseText, xhr)
          const uploadURL = body[opts.responseUrlFieldName]

          const response = {
            status: ev.target.status,
            body,
            uploadURL
          }

          this.uppy.setFileState(file.id, { response })

          this.uppy.emit('upload-success', file, body, uploadURL)

          if (uploadURL) {
            this.uppy.log(`Download ${file.name} from ${file.uploadURL}`)
          }

          return resolve(file)
        } else {
          const body = opts.getResponseData(xhr.responseText, xhr)
          const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))

          const response = {
            status: ev.target.status,
            body
          }

          this.uppy.setFileState(file.id, { response })

          this.uppy.emit('upload-error', file, error)
          return reject(error)
        }
      })

      xhr.addEventListener('error', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} errored`)
        timer.done()

        const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
        this.uppy.emit('upload-error', file, error)
        return reject(error)
      })

      xhr.open(opts.method.toUpperCase(), opts.endpoint, true)
      // IE10 does not allow setting `withCredentials` before `open()` is called.
      xhr.withCredentials = opts.withCredentials

      Object.keys(opts.headers).forEach((header) => {
        xhr.setRequestHeader(header, opts.headers[header])
      })

      xhr.send(data)

      this.uppy.on('file-removed', (removedFile) => {
        if (removedFile.id === file.id) {
          timer.done()
          xhr.abort()
        }
      })

      this.uppy.on('cancel-all', () => {
        timer.done()
        xhr.abort()
      })
    })
  }

  uploadRemote (file, current, total) {
    const opts = this.getOptions(file)
    return new Promise((resolve, reject) => {
      const fields = {}
      const metaFields = Array.isArray(opts.metaFields)
        ? opts.metaFields
        // Send along all fields by default.
        : Object.keys(file.meta)

      metaFields.forEach((name) => {
        fields[name] = file.meta[name]
      })

      const provider = new Provider(this.uppy, file.remote.providerOptions)
      provider.post(
        file.remote.url,
        Object.assign({}, file.remote.body, {
          endpoint: opts.endpoint,
          size: file.data.size,
          fieldname: opts.fieldName,
          metadata: fields,
          headers: opts.headers
        })
      )
      .then((res) => {
        const token = res.token
        const host = getSocketHost(file.remote.serverUrl)
        const socket = new Socket({ target: `${host}/api/${token}` })

        socket.on('progress', (progressData) => emitSocketProgress(this, progressData, file))

        socket.on('success', (data) => {
          const resp = opts.getResponseData(data.response.responseText, data.response)
          const uploadURL = resp[opts.responseUrlFieldName]
          this.uppy.emit('upload-success', file, resp, uploadURL)
          socket.close()
          return resolve()
        })

        socket.on('error', (errData) => {
          const resp = errData.response
          const error = resp
            ? opts.getResponseError(resp.responseText, resp)
            : Object.assign(new Error(errData.error.message), { cause: errData.error })
          this.uppy.emit('upload-error', file, error)
          reject(error)
        })
      })
    })
  }

  uploadBundle (files) {
    return new Promise((resolve, reject) => {
      const endpoint = this.opts.endpoint
      const method = this.opts.method

      const formData = new FormData()
      files.forEach((file, i) => {
        const opts = this.getOptions(file)
        formData.append(opts.fieldName, file.data)
      })

      const xhr = new XMLHttpRequest()
      if (this.opts.responseType !== '') {
        xhr.responseType = this.opts.responseType
      }

      const timer = this.createProgressTimeout(this.opts.timeout, (error) => {
        xhr.abort()
        emitError(error)
        reject(error)
      })

      const emitError = (error) => {
        files.forEach((file) => {
          this.uppy.emit('upload-error', file, error)
        })
      }

      xhr.upload.addEventListener('loadstart', (ev) => {
        this.uppy.log('[XHRUpload] started uploading bundle')
        timer.progress()
      })

      xhr.upload.addEventListener('progress', (ev) => {
        timer.progress()

        if (!ev.lengthComputable) return

        files.forEach((file) => {
          this.uppy.emit('upload-progress', file, {
            uploader: this,
            bytesUploaded: ev.loaded / ev.total * file.size,
            bytesTotal: file.size
          })
        })
      })

      xhr.addEventListener('load', (ev) => {
        timer.done()

        if (ev.target.status >= 200 && ev.target.status < 300) {
          const resp = this.opts.getResponseData(xhr.responseText, xhr)
          files.forEach((file) => {
            this.uppy.emit('upload-success', file, resp)
          })
          return resolve()
        }

        const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error')
        error.request = xhr
        emitError(error)
        return reject(error)
      })

      xhr.addEventListener('error', (ev) => {
        timer.done()

        const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error')
        emitError(error)
        return reject(error)
      })

      this.uppy.on('cancel-all', () => {
        timer.done()
        xhr.abort()
      })

      xhr.open(method.toUpperCase(), endpoint, true)
      // IE10 does not allow setting `withCredentials` before `open()` is called.
      xhr.withCredentials = this.opts.withCredentials

      Object.keys(this.opts.headers).forEach((header) => {
        xhr.setRequestHeader(header, this.opts.headers[header])
      })

      xhr.send(formData)

      files.forEach((file) => {
        this.uppy.emit('upload-started', file)
      })
    })
  }

  uploadFiles (files) {
    const actions = files.map((file, i) => {
      const current = parseInt(i, 10) + 1
      const total = files.length

      if (file.error) {
        return () => Promise.reject(new Error(file.error))
      } else if (file.isRemote) {
        // We emit upload-started here, so that it's also emitted for files
        // that have to wait due to the `limit` option.
        this.uppy.emit('upload-started', file)
        return this.uploadRemote.bind(this, file, current, total)
      } else {
        this.uppy.emit('upload-started', file)
        return this.upload.bind(this, file, current, total)
      }
    })

    const promises = actions.map((action) => {
      const limitedAction = this.limitUploads(action)
      return limitedAction()
    })

    return settle(promises)
  }

  handleUpload (fileIDs) {
    if (fileIDs.length === 0) {
      this.uppy.log('[XHRUpload] No files to upload!')
      return Promise.resolve()
    }

    this.uppy.log('[XHRUpload] Uploading...')
    const files = fileIDs.map((fileID) => this.uppy.getFile(fileID))

    if (this.opts.bundle) {
      return this.uploadBundle(files)
    }

    return this.uploadFiles(files).then(() => null)
  }

  install () {
    if (this.opts.bundle) {
      this.uppy.setState({
        capabilities: Object.assign({}, this.uppy.getState().capabilities, {
          bundled: true
        })
      })
    }

    this.uppy.addUploader(this.handleUpload)
  }

  uninstall () {
    if (this.opts.bundle) {
      this.uppy.setState({
        capabilities: Object.assign({}, this.uppy.getState().capabilities, {
          bundled: true
        })
      })
    }

    this.uppy.removeUploader(this.handleUpload)
  }
}
<commit_msg>Postpone timeout countdown until upload has started, i.e. has left browser concurrency queue (fixes transloadit#1190)<commit_after>const { Plugin } = require('@uppy/core')
const cuid = require('cuid')
const Translator = require('@uppy/utils/lib/Translator')
const { Provider, Socket } = require('@uppy/companion-client')
const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress')
const getSocketHost = require('@uppy/utils/lib/getSocketHost')
const settle = require('@uppy/utils/lib/settle')
const limitPromises = require('@uppy/utils/lib/limitPromises')

function buildResponseError (xhr, error) {
  // No error message
  if (!error) error = new Error('Upload error')
  // Got an error message string
  if (typeof error === 'string') error = new Error(error)
  // Got something else
  if (!(error instanceof Error)) {
    error = Object.assign(new Error('Upload error'), { data: error })
  }

  error.request = xhr
  return error
}

module.exports = class XHRUpload extends Plugin {
  constructor (uppy, opts) {
    super(uppy, opts)
    this.type = 'uploader'
    this.id = 'XHRUpload'
    this.title = 'XHRUpload'

    const defaultLocale = {
      strings: {
        timedOut: 'Upload stalled for %{seconds} seconds, aborting.'
      }
    }

    // Default options
    const defaultOptions = {
      formData: true,
      fieldName: 'files[]',
      method: 'post',
      metaFields: null,
      responseUrlFieldName: 'url',
      bundle: false,
      headers: {},
      locale: defaultLocale,
      timeout: 30 * 1000,
      limit: 0,
      withCredentials: false,
      responseType: '',
      /**
       * @typedef respObj
       * @property {string} responseText
       * @property {number} status
       * @property {string} statusText
       * @property {Object.<string, string>} headers
       *
       * @param {string} responseText the response body string
       * @param {XMLHttpRequest | respObj} response the response object (XHR or similar)
       */
      getResponseData (responseText, response) {
        let parsedResponse = {}
        try {
          parsedResponse = JSON.parse(responseText)
        } catch (err) {
          console.log(err)
        }

        return parsedResponse
      },
      /**
       *
       * @param {string} responseText the response body string
       * @param {XMLHttpRequest | respObj} response the response object (XHR or similar)
       */
      getResponseError (responseText, response) {
        return new Error('Upload error')
      }
    }

    // Merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    // i18n
    this.translator = new Translator([ defaultLocale, this.uppy.locale, this.opts.locale ])
    this.i18n = this.translator.translate.bind(this.translator)
    this.i18nArray = this.translator.translateArray.bind(this.translator)

    this.handleUpload = this.handleUpload.bind(this)

    // Simultaneous upload limiting is shared across all uploads with this plugin.
    if (typeof this.opts.limit === 'number' && this.opts.limit !== 0) {
      this.limitUploads = limitPromises(this.opts.limit)
    } else {
      this.limitUploads = (fn) => fn
    }

    if (this.opts.bundle && !this.opts.formData) {
      throw new Error('`opts.formData` must be true when `opts.bundle` is enabled.')
    }
  }

  getOptions (file) {
    const overrides = this.uppy.getState().xhrUpload
    const opts = Object.assign({},
      this.opts,
      overrides || {},
      file.xhrUpload || {}
    )
    opts.headers = {}
    Object.assign(opts.headers, this.opts.headers)
    if (overrides) {
      Object.assign(opts.headers, overrides.headers)
    }
    if (file.xhrUpload) {
      Object.assign(opts.headers, file.xhrUpload.headers)
    }

    return opts
  }

  // Helper to abort upload requests if there has not been any progress for `timeout` ms.
  // Create an instance using `timer = createProgressTimeout(10000, onTimeout)`
  // Call `timer.progress()` to signal that there has been progress of any kind.
  // Call `timer.done()` when the upload has completed.
  createProgressTimeout (timeout, timeoutHandler) {
    const uppy = this.uppy
    const self = this
    let isDone = false

    function onTimedOut () {
      uppy.log(`[XHRUpload] timed out`)
      const error = new Error(self.i18n('timedOut', { seconds: Math.ceil(timeout / 1000) }))
      timeoutHandler(error)
    }

    let aliveTimer = null
    function progress () {
      // Some browsers fire another progress event when the upload is
      // cancelled, so we have to ignore progress after the timer was
      // told to stop.
      if (isDone) return

      if (timeout > 0) {
        if (aliveTimer) clearTimeout(aliveTimer)
        aliveTimer = setTimeout(onTimedOut, timeout)
      }
    }

    function done () {
      uppy.log(`[XHRUpload] timer done`)
      if (aliveTimer) {
        clearTimeout(aliveTimer)
        aliveTimer = null
      }
      isDone = true
    }

    return {
      progress,
      done
    }
  }

  createFormDataUpload (file, opts) {
    const formPost = new FormData()

    const metaFields = Array.isArray(opts.metaFields)
      ? opts.metaFields
      // Send along all fields by default.
      : Object.keys(file.meta)
    metaFields.forEach((item) => {
      formPost.append(item, file.meta[item])
    })

    if (file.name) {
      formPost.append(opts.fieldName, file.data, file.name)
    } else {
      formPost.append(opts.fieldName, file.data)
    }

    return formPost
  }

  createBareUpload (file, opts) {
    return file.data
  }

  upload (file, current, total) {
    const opts = this.getOptions(file)

    this.uppy.log(`uploading ${current} of ${total}`)
    return new Promise((resolve, reject) => {
      const data = opts.formData
        ? this.createFormDataUpload(file, opts)
        : this.createBareUpload(file, opts)

      const timer = this.createProgressTimeout(opts.timeout, (error) => {
        xhr.abort()
        this.uppy.emit('upload-error', file, error)
        reject(error)
      })

      const xhr = new XMLHttpRequest()
      if (opts.responseType !== '') {
        xhr.responseType = opts.responseType
      }

      const id = cuid()

      xhr.upload.addEventListener('loadstart', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} started`)
      })

      xhr.upload.addEventListener('progress', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} progress: ${ev.loaded} / ${ev.total}`)
        // Begin checking for timeouts when progress starts, instead of loading,
        // to avoid timing out requests on browser concurrency queue
        timer.progress()

        if (ev.lengthComputable) {
          this.uppy.emit('upload-progress', file, {
            uploader: this,
            bytesUploaded: ev.loaded,
            bytesTotal: ev.total
          })
        }
      })

      xhr.addEventListener('load', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} finished`)
        timer.done()

        if (ev.target.status >= 200 && ev.target.status < 300) {
          const body = opts.getResponseData(xhr.responseText, xhr)
          const uploadURL = body[opts.responseUrlFieldName]

          const response = {
            status: ev.target.status,
            body,
            uploadURL
          }

          this.uppy.setFileState(file.id, { response })

          this.uppy.emit('upload-success', file, body, uploadURL)

          if (uploadURL) {
            this.uppy.log(`Download ${file.name} from ${file.uploadURL}`)
          }

          return resolve(file)
        } else {
          const body = opts.getResponseData(xhr.responseText, xhr)
          const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))

          const response = {
            status: ev.target.status,
            body
          }

          this.uppy.setFileState(file.id, { response })

          this.uppy.emit('upload-error', file, error)
          return reject(error)
        }
      })

      xhr.addEventListener('error', (ev) => {
        this.uppy.log(`[XHRUpload] ${id} errored`)
        timer.done()

        const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
        this.uppy.emit('upload-error', file, error)
        return reject(error)
      })

      xhr.open(opts.method.toUpperCase(), opts.endpoint, true)
      // IE10 does not allow setting `withCredentials` before `open()` is called.
      xhr.withCredentials = opts.withCredentials

      Object.keys(opts.headers).forEach((header) => {
        xhr.setRequestHeader(header, opts.headers[header])
      })

      xhr.send(data)

      this.uppy.on('file-removed', (removedFile) => {
        if (removedFile.id === file.id) {
          timer.done()
          xhr.abort()
        }
      })

      this.uppy.on('cancel-all', () => {
        timer.done()
        xhr.abort()
      })
    })
  }

  uploadRemote (file, current, total) {
    const opts = this.getOptions(file)
    return new Promise((resolve, reject) => {
      const fields = {}
      const metaFields = Array.isArray(opts.metaFields)
        ? opts.metaFields
        // Send along all fields by default.
        : Object.keys(file.meta)

      metaFields.forEach((name) => {
        fields[name] = file.meta[name]
      })

      const provider = new Provider(this.uppy, file.remote.providerOptions)
      provider.post(
        file.remote.url,
        Object.assign({}, file.remote.body, {
          endpoint: opts.endpoint,
          size: file.data.size,
          fieldname: opts.fieldName,
          metadata: fields,
          headers: opts.headers
        })
      )
      .then((res) => {
        const token = res.token
        const host = getSocketHost(file.remote.serverUrl)
        const socket = new Socket({ target: `${host}/api/${token}` })

        socket.on('progress', (progressData) => emitSocketProgress(this, progressData, file))

        socket.on('success', (data) => {
          const resp = opts.getResponseData(data.response.responseText, data.response)
          const uploadURL = resp[opts.responseUrlFieldName]
          this.uppy.emit('upload-success', file, resp, uploadURL)
          socket.close()
          return resolve()
        })

        socket.on('error', (errData) => {
          const resp = errData.response
          const error = resp
            ? opts.getResponseError(resp.responseText, resp)
            : Object.assign(new Error(errData.error.message), { cause: errData.error })
          this.uppy.emit('upload-error', file, error)
          reject(error)
        })
      })
    })
  }

  uploadBundle (files) {
    return new Promise((resolve, reject) => {
      const endpoint = this.opts.endpoint
      const method = this.opts.method

      const formData = new FormData()
      files.forEach((file, i) => {
        const opts = this.getOptions(file)
        formData.append(opts.fieldName, file.data)
      })

      const xhr = new XMLHttpRequest()
      if (this.opts.responseType !== '') {
        xhr.responseType = this.opts.responseType
      }

      const timer = this.createProgressTimeout(this.opts.timeout, (error) => {
        xhr.abort()
        emitError(error)
        reject(error)
      })

      const emitError = (error) => {
        files.forEach((file) => {
          this.uppy.emit('upload-error', file, error)
        })
      }

      xhr.upload.addEventListener('loadstart', (ev) => {
        this.uppy.log('[XHRUpload] started uploading bundle')
        timer.progress()
      })

      xhr.upload.addEventListener('progress', (ev) => {
        timer.progress()

        if (!ev.lengthComputable) return

        files.forEach((file) => {
          this.uppy.emit('upload-progress', file, {
            uploader: this,
            bytesUploaded: ev.loaded / ev.total * file.size,
            bytesTotal: file.size
          })
        })
      })

      xhr.addEventListener('load', (ev) => {
        timer.done()

        if (ev.target.status >= 200 && ev.target.status < 300) {
          const resp = this.opts.getResponseData(xhr.responseText, xhr)
          files.forEach((file) => {
            this.uppy.emit('upload-success', file, resp)
          })
          return resolve()
        }

        const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error')
        error.request = xhr
        emitError(error)
        return reject(error)
      })

      xhr.addEventListener('error', (ev) => {
        timer.done()

        const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error')
        emitError(error)
        return reject(error)
      })

      this.uppy.on('cancel-all', () => {
        timer.done()
        xhr.abort()
      })

      xhr.open(method.toUpperCase(), endpoint, true)
      // IE10 does not allow setting `withCredentials` before `open()` is called.
      xhr.withCredentials = this.opts.withCredentials

      Object.keys(this.opts.headers).forEach((header) => {
        xhr.setRequestHeader(header, this.opts.headers[header])
      })

      xhr.send(formData)

      files.forEach((file) => {
        this.uppy.emit('upload-started', file)
      })
    })
  }

  uploadFiles (files) {
    const actions = files.map((file, i) => {
      const current = parseInt(i, 10) + 1
      const total = files.length

      if (file.error) {
        return () => Promise.reject(new Error(file.error))
      } else if (file.isRemote) {
        // We emit upload-started here, so that it's also emitted for files
        // that have to wait due to the `limit` option.
        this.uppy.emit('upload-started', file)
        return this.uploadRemote.bind(this, file, current, total)
      } else {
        this.uppy.emit('upload-started', file)
        return this.upload.bind(this, file, current, total)
      }
    })

    const promises = actions.map((action) => {
      const limitedAction = this.limitUploads(action)
      return limitedAction()
    })

    return settle(promises)
  }

  handleUpload (fileIDs) {
    if (fileIDs.length === 0) {
      this.uppy.log('[XHRUpload] No files to upload!')
      return Promise.resolve()
    }

    this.uppy.log('[XHRUpload] Uploading...')
    const files = fileIDs.map((fileID) => this.uppy.getFile(fileID))

    if (this.opts.bundle) {
      return this.uploadBundle(files)
    }

    return this.uploadFiles(files).then(() => null)
  }

  install () {
    if (this.opts.bundle) {
      this.uppy.setState({
        capabilities: Object.assign({}, this.uppy.getState().capabilities, {
          bundled: true
        })
      })
    }

    this.uppy.addUploader(this.handleUpload)
  }

  uninstall () {
    if (this.opts.bundle) {
      this.uppy.setState({
        capabilities: Object.assign({}, this.uppy.getState().capabilities, {
          bundled: true
        })
      })
    }

    this.uppy.removeUploader(this.handleUpload)
  }
}
<|endoftext|>


<commit_before>QUnit.module("controller location unit tests", {
    setup: function() {
        //App.reset();
        //Ember.run(function () { App.reset(); });
    },
    teardown: function() {
        //App.reset();
    }
});

QUnit
    .cases([
        // data provider for geographic coordinates (latitude, longtitude)
        { latitude: '49.89881', longtitude: '10.90276', expectedlatlng: '49.89881,10.90276' },
        { latitude: '53.55661', longtitude: '9.98583', expectedlatlng: '53.55661,9.98583' },
        { latitude: '48.13513', longtitude: '11.58198', expectedlatlng: '48.13513,11.58198' }
    ])
    .test("test latlng (computed property) with valid coordinates", function(coordinates) {

        var location = {
            formatted_address: 'nonsense',
            geometry: {
                location: {
                    lat: function() { return coordinates.latitude },
                    lng: function() { return coordinates.longtitude }
                }
            }
        }

        var controller = getLocationController();
        controller.setLocation(location);
        var actualLatlng = controller.get('latlng');

        equal(actualLatlng, coordinates.expectedlatlng);
    });<commit_msg>test setup fix * wrap app.reset to prevent async side effects --skip-ci<commit_after>QUnit.module("controller location unit tests", {
    setup: function() {
        Ember.run(function() {
            App.reset();
            App.deferReadiness();
        });
    },
    teardown: function() {
        App.reset();
    }
});

QUnit
    .cases([
        // data provider for geographic coordinates (latitude, longtitude)
        { latitude: '49.89881', longtitude: '10.90276', expectedlatlng: '49.89881,10.90276' },
        { latitude: '53.55661', longtitude: '9.98583', expectedlatlng: '53.55661,9.98583' },
        { latitude: '48.13513', longtitude: '11.58198', expectedlatlng: '48.13513,11.58198' }
    ])
    .test("test latlng (computed property) with valid coordinates", function(coordinates) {

        var location = {
            formatted_address: 'nonsense',
            geometry: {
                location: {
                    lat: function() { return coordinates.latitude },
                    lng: function() { return coordinates.longtitude }
                }
            }
        }

        var controller = getLocationController();
        controller.setLocation(location);
        var actualLatlng = controller.get('latlng');

        equal(actualLatlng, coordinates.expectedlatlng);
    });<|endoftext|>