<commit_before>/**
 * @flow
 */

import * as Immutable from 'immutable'

export type Updater<T> = (t: T) => T;

const registeredConstructors = {};

/**
 * A "lens", as defined here, is a path down an existing object with the ability
 * to do an immutable replacement deep in the object tree. All generated value
 * types know how to create a type-specific lens to assist in navigating through
 * them, and for other types, we
 */
export class Lens<T, Result> {
    value: T;
    replace: (t: T) => Result;

    constructor(value: T, replace: (t: T) => Result) {
        this.value = value;
        this.replace = replace;
    }

    update(updater: Updater<T>): Result {
        return this.replace(updater(this.value));
    }
}

export const makeLens = function<T, Result>(
        value: T, replace: (t: T) => Result): Lens<T, Result> {
    if (value != null && typeof value.makeLens === 'function') {
        return value.makeLens(replace);
    }
    return new Lens(value, replace);
};

const withName = (name) => {
    return 'with' + name[0].toUpperCase() + name.slice(1);
};

const updateName = (name) => {
    return 'update' + name[0].toUpperCase() + name.slice(1);
};

/**
 * Construct a class with properly-named fields.
 */
export const buildValueClass = (
        className: string, fieldNames: Array<string>): any => {
    class CustomLens extends Lens {}
    const customLensPrototype: any = CustomLens.prototype;
    for (const name of fieldNames) {
        customLensPrototype[name] = function() {
            const replaceChild = newChildVal =>
                this.replace(this.value[withName(name)](newChildVal));
            return makeLens(this.value[name], replaceChild);
        };
    }

    class ValueClass {
        constructor(fields) {
            // Attach the class name for easier debugging.
            (this: any).className = className;
            for (const fieldName of fieldNames) {
                (this: any)[fieldName] = fields[fieldName];
            }
        }
        static make(...args) {
            const constructorArg = {};
            for (let i = 0; i < fieldNames.length; i++) {
                constructorArg[fieldNames[i]] = args[i];
            }
            return new ValueClass(constructorArg);
        }
        serialize() {
            const result = {};
            result.__SERIALIZED_CLASS = className;
            for (const name of fieldNames) {
                result[name] = serialize((this: any)[name]);
            }
            return result;
        }
        lens() {
            return this.makeLens(newValue => newValue);
        }
        makeLens(replace) {
            return new CustomLens(this, replace);
        }
        equals(other) {
            for (const name of fieldNames) {
                if (!Immutable.is((this: any)[name], other[name])) {
                    return false;
                }
            }
            return true;
        }
        hashCode() {
            return Immutable.Map(this).hashCode();
        }
        toString() {
            return Immutable.Map(this).toString();
        }
    }
    for (const name of fieldNames) {
        (ValueClass: any).prototype[withName(name)] = function(newVal) {
            const newArgs = {};
            for (const copyName of fieldNames) {
                newArgs[copyName] = this[copyName];
            }
            newArgs[name] = newVal;
            return new ValueClass(newArgs);
        };
        (ValueClass: any).prototype[updateName(name)] = function(updater) {
            return this[withName(name)](updater(this[name]));
        };
    }
    registeredConstructors[className] = ValueClass;
    return ValueClass;
};

export const buildUnionCaseClass = (
        caseName: string, fieldNames: Array<string>): any => {
    const defaults: any = {};
    for (const name of fieldNames) {
        defaults[name] = undefined;
    }
    defaults.type = undefined;
    class UnionCaseClass {
        constructor(fields) {
            (this: any).type = fields.type;
            for (const fieldName of fieldNames) {
                (this: any)[fieldName] = fields[fieldName];
            }
        }
        static make(...args) {
            const constructorArg = {};
            constructorArg.type = caseName;
            for (let i = 0; i < fieldNames.length; i++) {
                constructorArg[fieldNames[i]] = args[i];
            }
            return new UnionCaseClass(constructorArg);
        }
        match(visitor) {
            return visitor[caseName](this);
        }
        serialize() {
            const result = {};
            result.__SERIALIZED_CLASS = caseName;
            for (const name of fieldNames) {
                result[name] = serialize((this: any)[name]);
            }
            result.type = (this: any).type;
            return result;
        }
        equals(other) {
            for (const name of fieldNames) {
                if (!Immutable.is((this: any)[name], other[name])) {
                    return false;
                }
            }
            return true;
        }
        hashCode() {
            return Immutable.Map(this).hashCode();
        }
        toString() {
            return Immutable.Map(this).toString();
        }
    }
    for (const name of fieldNames) {
        (UnionCaseClass: any).prototype[withName(name)] = function(newVal) {
            const newArgs = {};
            for (const copyName of fieldNames) {
                newArgs[copyName] = this[copyName];
            }
            newArgs.type = this.type;
            newArgs[name] = newVal;
            return new UnionCaseClass(newArgs);
        };
        (UnionCaseClass: any).prototype[updateName(name)] = function(updater) {
            return this[withName(name)](updater(this[name]));
        };
    }
    registeredConstructors[caseName] = UnionCaseClass;
    return UnionCaseClass;
};

const serialize = (obj: any): any => {
    if (obj == null || typeof obj !== 'object') {
        return obj;
    }
    if (typeof obj.serialize === 'function') {
        return obj.serialize();
    }
    return obj;
};

// TODO: Handle immutable maps. Currently it just doesn't serialize them.
export const deserialize = (obj: any): any => {
    if (obj == null || typeof obj !== 'object') {
        return obj;
    }
    const className = obj.__SERIALIZED_CLASS;
    if (className == null) {
        return obj;
    }
    const constructorArg = {};
    for (const name of Object.keys(obj)) {
        if (name !== '__SERIALIZED_CLASS') {
            constructorArg[name] = deserialize(obj[name]);
        }
    }
    const constructor = registeredConstructors[className];
    return new constructor(constructorArg);
};

export const serializeActionsMiddleware = (store: any) => (next: any) => (action: any) => {
    if (action != null &&
            typeof action === 'object' &&
            typeof action.serialize === 'function') {
        action = action.serialize();
    }
    return next(action);
};<commit_msg>Fix bug in type equality<commit_after>/**
 * @flow
 */

import * as Immutable from 'immutable'

export type Updater<T> = (t: T) => T;

const registeredConstructors = {};

/**
 * A "lens", as defined here, is a path down an existing object with the ability
 * to do an immutable replacement deep in the object tree. All generated value
 * types know how to create a type-specific lens to assist in navigating through
 * them, and for other types, we
 */
export class Lens<T, Result> {
    value: T;
    replace: (t: T) => Result;

    constructor(value: T, replace: (t: T) => Result) {
        this.value = value;
        this.replace = replace;
    }

    update(updater: Updater<T>): Result {
        return this.replace(updater(this.value));
    }
}

export const makeLens = function<T, Result>(
        value: T, replace: (t: T) => Result): Lens<T, Result> {
    if (value != null && typeof value.makeLens === 'function') {
        return value.makeLens(replace);
    }
    return new Lens(value, replace);
};

const withName = (name) => {
    return 'with' + name[0].toUpperCase() + name.slice(1);
};

const updateName = (name) => {
    return 'update' + name[0].toUpperCase() + name.slice(1);
};

/**
 * Construct a class with properly-named fields.
 */
export const buildValueClass = (
        className: string, fieldNames: Array<string>): any => {
    class CustomLens extends Lens {}
    const customLensPrototype: any = CustomLens.prototype;
    for (const name of fieldNames) {
        customLensPrototype[name] = function() {
            const replaceChild = newChildVal =>
                this.replace(this.value[withName(name)](newChildVal));
            return makeLens(this.value[name], replaceChild);
        };
    }

    class ValueClass {
        constructor(fields) {
            // Attach the class name for easier debugging.
            (this: any).className = className;
            for (const fieldName of fieldNames) {
                (this: any)[fieldName] = fields[fieldName];
            }
        }
        static make(...args) {
            const constructorArg = {};
            for (let i = 0; i < fieldNames.length; i++) {
                constructorArg[fieldNames[i]] = args[i];
            }
            return new ValueClass(constructorArg);
        }
        serialize() {
            const result = {};
            result.__SERIALIZED_CLASS = className;
            for (const name of fieldNames) {
                result[name] = serialize((this: any)[name]);
            }
            return result;
        }
        lens() {
            return this.makeLens(newValue => newValue);
        }
        makeLens(replace) {
            return new CustomLens(this, replace);
        }
        equals(other) {
            if (!(other instanceof ValueClass)) {
                return false;
            }
            for (const name of fieldNames) {
                if (!Immutable.is((this: any)[name], other[name])) {
                    return false;
                }
            }
            return true;
        }
        hashCode() {
            return Immutable.Map(this).hashCode();
        }
        toString() {
            return Immutable.Map(this).toString();
        }
    }
    for (const name of fieldNames) {
        (ValueClass: any).prototype[withName(name)] = function(newVal) {
            const newArgs = {};
            for (const copyName of fieldNames) {
                newArgs[copyName] = this[copyName];
            }
            newArgs[name] = newVal;
            return new ValueClass(newArgs);
        };
        (ValueClass: any).prototype[updateName(name)] = function(updater) {
            return this[withName(name)](updater(this[name]));
        };
    }
    registeredConstructors[className] = ValueClass;
    return ValueClass;
};

export const buildUnionCaseClass = (
        caseName: string, fieldNames: Array<string>): any => {
    const defaults: any = {};
    for (const name of fieldNames) {
        defaults[name] = undefined;
    }
    defaults.type = undefined;
    class UnionCaseClass {
        constructor(fields) {
            (this: any).type = fields.type;
            for (const fieldName of fieldNames) {
                (this: any)[fieldName] = fields[fieldName];
            }
        }
        static make(...args) {
            const constructorArg = {};
            constructorArg.type = caseName;
            for (let i = 0; i < fieldNames.length; i++) {
                constructorArg[fieldNames[i]] = args[i];
            }
            return new UnionCaseClass(constructorArg);
        }
        match(visitor) {
            return visitor[caseName](this);
        }
        serialize() {
            const result = {};
            result.__SERIALIZED_CLASS = caseName;
            for (const name of fieldNames) {
                result[name] = serialize((this: any)[name]);
            }
            result.type = (this: any).type;
            return result;
        }
        equals(other) {
            if (!(other instanceof UnionCaseClass)) {
                return false;
            }
            if (!Immutable.is((this: any).type, other.type)) {
                return false;
            }
            for (const name of fieldNames) {
                if (!Immutable.is((this: any)[name], other[name])) {
                    return false;
                }
            }
            return true;
        }
        hashCode() {
            return Immutable.Map(this).hashCode();
        }
        toString() {
            return Immutable.Map(this).toString();
        }
    }
    for (const name of fieldNames) {
        (UnionCaseClass: any).prototype[withName(name)] = function(newVal) {
            const newArgs = {};
            for (const copyName of fieldNames) {
                newArgs[copyName] = this[copyName];
            }
            newArgs.type = this.type;
            newArgs[name] = newVal;
            return new UnionCaseClass(newArgs);
        };
        (UnionCaseClass: any).prototype[updateName(name)] = function(updater) {
            return this[withName(name)](updater(this[name]));
        };
    }
    registeredConstructors[caseName] = UnionCaseClass;
    return UnionCaseClass;
};

const serialize = (obj: any): any => {
    if (obj == null || typeof obj !== 'object') {
        return obj;
    }
    if (typeof obj.serialize === 'function') {
        return obj.serialize();
    }
    return obj;
};

// TODO: Handle immutable maps. Currently it just doesn't serialize them.
export const deserialize = (obj: any): any => {
    if (obj == null || typeof obj !== 'object') {
        return obj;
    }
    const className = obj.__SERIALIZED_CLASS;
    if (className == null) {
        return obj;
    }
    const constructorArg = {};
    for (const name of Object.keys(obj)) {
        if (name !== '__SERIALIZED_CLASS') {
            constructorArg[name] = deserialize(obj[name]);
        }
    }
    const constructor = registeredConstructors[className];
    return new constructor(constructorArg);
};

export const serializeActionsMiddleware = (store: any) => (next: any) => (action: any) => {
    if (action != null &&
            typeof action === 'object' &&
            typeof action.serialize === 'function') {
        action = action.serialize();
    }
    return next(action);
};<|endoftext|>


<commit_before>var Roles = require('./role');
var config = require('../serverconfig');

var defaultVoteObj = function(){
	return {
		like: [],
		dislike: [],
		grab: []
	};
};

function makeSongObj(input){
	if (!input) return input;

	delete input.thumbnail;
	return input;
}

/* DJ Queue Object Start */
function djqueue(room){
	if (!room){
		return;
	}

	this.room = room;
	this.users = [];
	this.lastdj = null;
	this.lastsong = null;
	this.currentdj = null;
	this.currentsong = null;
	this.songstart = null;
	this.lasttimer = null;
	this.limit = config.room.queue.limit;
	this.cycle = config.room.queue.cycle;
	this.lock = config.room.queue.lock;
	this.votes = new defaultVoteObj;
}

djqueue.prototype.add = function(sock, position){
	var res = this._add(sock, position);
	if(res.success){

		if(this.users.length == 1 && this.currentdj == null)
			this.advance();
	}
	return res;
};

djqueue.prototype._add = function(sock, position){
	var res = {success: false};

	if(sock.user && this.checkQueuePos(sock) == -1 && (this.currentdj == null ? null : this.currentdj.user.uid) != sock.user.uid){
		res.success = true;
		position = parseInt(position);

		if (isNaN(position) || position < 0 || position >= this.users.length){
			this.users.push(sock);
		}else{
			res.position = position;
			this.users.splice(position, 0, sock);
		}

		this.room.sendAll({
			type: "userJoinedQueue",
			data: {
				queueList: this.makeClientObj()
			}
		});
	}
	return res;
};

djqueue.prototype.replaceSocket = function(sock_old, sock_new){
	var qpos = this.users.indexOf(sock_old);

	if (qpos >= 0) this.users[qpos] = sock_new;
	if (this.currentdj == sock_old) this.currentdj = sock_new;

	var lpos = this.votes.like.indexOf(sock_old);
	var dpos = this.votes.dislike.indexOf(sock_old);
	var gpos = this.votes.grab.indexOf(sock_old);

	if (lpos >= 0) this.votes.like[lpos] = sock_new;
	if (dpos >= 0) this.votes.dislike[dpos] = sock_new;
	if (gpos >= 0) this.votes.grab[gpos] = sock_new;
};

djqueue.prototype.remove = function(sock, type){
	var index = this.checkQueuePos(sock);

	// Remove their vote
	for (var i in this.votes){
		if (this.votes[i].indexOf(sock) > -1) this.vote(i, sock, true);
	}

	if(index != -1){
		this.users.splice(index, 1);

		this.room.sendAll({
			type: type == undefined ? "userLeftQueue" : type,
			data: {
				queueList: this.makeClientObj()
			}
		});

		return true;
	} else if (sock.user && this.currentdj && this.currentdj.user.uid == sock.user.uid){
		this.advance( true );
		return true;
	}

	return false;
};

djqueue.prototype.move = function(sock, to){
	var from = this.checkQueuePos(sock);
	var res = {success: false};

	if (from == to){
		//TODO: Return error to sender, might need to add a callback
		return res;
	}

	if(to < this.users.length && to >= 0 && from != -1 && sock != this.currentdj){
		this.users.splice(from, 1);
		var pos = (to > from+1 ? to-1 : to);
		this.users.splice( pos, 0, sock);

		res.success = true;
		res.data = {
			queueList: this.makeClientObj(),
			uid: sock.user.data.uid,
			from: from,
			to: to
		};
	}
	return res;
};

djqueue.prototype.swap = function(sock1, sock2){
	var from = this.checkQueuePos(sock1);
	var to = this.checkQueuePos(sock2);
	var res = {success: false};

	if(from != -1 && to != -1 && sock1 != this.currentdj && sock2 != this.currentdj ){
		this.users[from] = sock2;
		this.users[to] = sock1;

		res.data = {
			queueList: this.makeClientObj(),
			uid1: sock1.user.data.uid,
			uid2: sock2.user.data.uid,
			pos1: from,
			pos2: to
		};

		res.success = true;
	}
	return res;
};

djqueue.prototype.advance = function( ignoreCycle, lockSkipPosition ){
	if(this.currentdj){
		this.currentdj.user.lastdj = false;
	}
	this.lastdj = this.currentdj;
	this.lastsong = this.currentsong;
	this.currentsong = null;
	clearTimeout(this.lasttimer);

	var isSetLockSkipPos = typeof lockSkipPosition === 'number' && !isNaN(lockSkipPosition);
	var res = {success: false};

	if(this.users.length >= 1){
		if (this.currentdj && (this.cycle || isSetLockSkipPos) && !ignoreCycle) {
			this.currentdj = null;
			res.position = this._add(this.lastdj, lockSkipPosition).position;
		}
		res.success = true;
		this._advance();
	} else if (this.currentdj != null) {
		this.currentdj = null;
		if((this.cycle || isSetLockSkipPos) && !ignoreCycle) {
			res.position = this._add(this.lastdj, lockSkipPosition).position;
		}

		this._advance();
		res.success = true;
	}
	return res;
};

djqueue.prototype._advance = function(){
	var that = this;
	var lastStart = that.songstart;

	if (that.lastsong != null) {
		var historyObj = {
			votes: {
				like: that.votes.like ? that.votes.like.length : 0,
				grab: that.votes.grab ? that.votes.grab.length : 0,
				dislike: that.votes.dislike ? that.votes.dislike.length : 0
			},
			song: makeSongObj(that.lastsong),
			user: (that.lastdj ? that.lastdj.user.getClientObj() : null),
			start: lastStart
		};
		that.room.addToHistory(historyObj);
	}

	if (this.users.length > 0){
		this.currentdj = this.users.splice(0, 1)[0];
		var pl = this.currentdj.user.playlistCache[this.currentdj.user.activepl];

		pl.getFirstExpanded(function(err, data){
			if (err){
				return that._advance();
			}

			//Show the unavailable song for a limited amount of time
			if(data.unavailable){
				data.duration = 5;
			}

			that.currentsong = data;
			pl.shiftToBottom();

			that.songstart = Date.now();
			that.room.sendAll({
				type: "advance",
				data: {
					last: {
						song: makeSongObj(that.lastsong),
						uid: (that.lastdj ? that.lastdj.user.uid : null),
						start: lastStart
					},
					next: {
						song: makeSongObj(that.currentsong),
						uid: (that.currentdj ? that.currentdj.user.uid : null),
						start: that.songstart
					}
				}
			});

			//	Allow to play streaming or live videos
			if (that.currentsong && that.currentsong.duration > 0 || !Roles.checkPermission(that.currentdj.user.role, 'djqueue.playLiveVideos')){
				that.lasttimer = setTimeout(function(){
					that.advance(that.currentdj.user.lastdj);
				}, that.currentsong.duration * 1000);
			}

			that.votes = new defaultVoteObj;

			if (that.currentsong && that.currentdj){
				that.room.updateLobbyServer(that.currentsong, that.currentdj ? that.currentdj.user.getClientObj() : null);
			}
		});
	}else{
		//if (that.lastsong === null || that.lastdj === null) return;

		that.votes = new defaultVoteObj;
		that.songstart = null;

		that.room.sendAll({
			type: "advance",
			data: {
				last: {
					song: makeSongObj(that.lastsong),
					uid: (that.lastdj ? that.lastdj.user.uid : null),
					start: lastStart
				},
				next: {
					song: null,
					uid: null,
					start: that.songstart
				}
			}
		});
		that.room.updateLobbyServer(null, null);
	}

	return false;
};

djqueue.prototype.skip = function(){
	if(this.currentdj != null){
		return this.advance(this.currentdj.user.lastdj);
	}
	return {success: false};
};

djqueue.prototype.modSkip = function(lockSkipPosition){
	if(this.currentdj != null){
		return this.advance((typeof lockSkipPosition === 'number' && !isNaN(lockSkipPosition)) ? false : this.currentdj.lastdj, lockSkipPosition);
	}
	return {success: false};
};

djqueue.prototype.toggleCycle = function(){
	this.cycle = !this.cycle;

	return this.cycle;
};

djqueue.prototype.toggleLock = function(){
	this.lock = !this.lock;

	return this.lock;
};

djqueue.prototype.setLimit = function(limit){
	if(limit >= 1){
		this.limit = limit;
		return true;
	}
	return false;
};

djqueue.prototype.vote = function(voteType, sock, leaving){
	voteType = voteType.toLowerCase();
	if (['like', 'dislike', 'grab'].indexOf(voteType) == -1) return false;
	if (!this.currentdj) return false;
	if (!sock.user) return false;
	if (sock.user.uid == this.currentdj.user.uid) return false;

	var ind = this.votes[voteType].indexOf(sock);

	if (ind > -1){
		if (voteType == 'grab' && !leaving) return false;

		this.votes[voteType].splice(ind, 1);
	}else{
		if (voteType == 'like' && this.votes.dislike.indexOf(sock) > -1){
			this.vote('dislike', sock);
		}else if (voteType == 'dislike' && this.votes.like.indexOf(sock) > -1){
			this.vote('like', sock);
		}

		this.votes[voteType].push(sock);
	}

	this.room.sendAll({
		type: 'voteUpdate',
		data: {
			votes: this.makeVoteObj(),
			uid: sock.user.uid,
			action: voteType,
			voted: (ind > -1 ? -1 : 1) // -1 for removed vote, 1 for added vote
		},
	});

	return true;
};

djqueue.prototype.makeClientObj = function(){
	var arr = [];
	for(var i =0; i < this.users.length; i++){
		arr.push(this.users[i].user.uid);
	}

	return arr;
};

djqueue.prototype.makeVoteObj = function(){
	return {
		like: this.votes.like.length,
		dislike: this.votes.dislike.length,
		grab: this.votes.grab.length
	};
};

djqueue.prototype.getVotes = function(){
	var like = [];
	var dislike = [];
	var grab = [];

	for (var i in this.votes.like)	like.push(this.votes.like[i].user.uid);
	for (var i in this.votes.dislike)	dislike.push(this.votes.dislike[i].user.uid);
	for (var i in this.votes.grab)	grab.push(this.votes.grab[i].user.uid);
	return {
		like: like,
		dislike: dislike,
		grab: grab
	};
};

djqueue.prototype.getUserVote = function(socket){
	var votes = [];

	if (!socket.user) return votes;

	if (this.votes.like.filter(function(a){ return a.user.uid == socket.user.uid;}).length != 0)	votes.push('like');
	if (this.votes.dislike.filter(function(a){ return a.user.uid == socket.user.uid;}).length != 0)	votes.push('dislike');
	if (this.votes.grab.filter(function(a){ return a.user.uid == socket.user.uid;}).length != 0)	votes.push('grab');
	return votes;
};

djqueue.prototype.getCurrentTime = function(){
	if (!this.currentsong || this.currentsong.duration == 0 || !this.currentdj) return 0;

	return Math.abs((Date.now( )- this.songstart) / 1000);
};

djqueue.prototype.checkQueuePos = function(sock){
	if (!sock.user) return -1;
	for(var i =0; i < this.users.length; i++){
		if(this.users[i].user.uid == sock.user.uid) return i;
	}
	return -1;
	//return this.users.indexOf( sock );
};

djqueue.prototype.isPlaying = function(sock){
	return sock == this.currentdj || this.checkQueuePos(sock) != -1;
};

/* DJ Queue Object End */

module.exports = djqueue;<commit_msg>Fixes #69: "Once in a million error"<commit_after>var Roles = require('./role');
var config = require('../serverconfig');

var defaultVoteObj = function(){
	return {
		like: [],
		dislike: [],
		grab: []
	};
};

function makeSongObj(input){
	if (!input) return input;

	delete input.thumbnail;
	return input;
}

/* DJ Queue Object Start */
function djqueue(room){
	if (!room){
		return;
	}

	this.room = room;
	this.users = [];
	this.lastdj = null;
	this.lastsong = null;
	this.currentdj = null;
	this.currentsong = null;
	this.songstart = null;
	this.lasttimer = null;
	this.limit = config.room.queue.limit;
	this.cycle = config.room.queue.cycle;
	this.lock = config.room.queue.lock;
	this.votes = new defaultVoteObj;
}

djqueue.prototype.add = function(sock, position){
	var res = this._add(sock, position);
	if(res.success){

		if(this.users.length == 1 && this.currentdj == null)
			this.advance();
	}
	return res;
};

djqueue.prototype._add = function(sock, position){
	var res = {success: false};

	if(sock.user && this.checkQueuePos(sock) == -1 && (this.currentdj == null ? null : this.currentdj.user.uid) != sock.user.uid){
		res.success = true;
		position = parseInt(position);

		if (isNaN(position) || position < 0 || position >= this.users.length){
			this.users.push(sock);
		}else{
			res.position = position;
			this.users.splice(position, 0, sock);
		}

		this.room.sendAll({
			type: "userJoinedQueue",
			data: {
				queueList: this.makeClientObj()
			}
		});
	}
	return res;
};

djqueue.prototype.replaceSocket = function(sock_old, sock_new){
	var qpos = this.users.indexOf(sock_old);

	if (qpos >= 0) this.users[qpos] = sock_new;
	if (this.currentdj == sock_old) this.currentdj = sock_new;

	var lpos = this.votes.like.indexOf(sock_old);
	var dpos = this.votes.dislike.indexOf(sock_old);
	var gpos = this.votes.grab.indexOf(sock_old);

	if (lpos >= 0) this.votes.like[lpos] = sock_new;
	if (dpos >= 0) this.votes.dislike[dpos] = sock_new;
	if (gpos >= 0) this.votes.grab[gpos] = sock_new;
};

djqueue.prototype.remove = function(sock, type){
	var index = this.checkQueuePos(sock);

	// Remove their vote
	for (var i in this.votes){
		if (this.votes[i].indexOf(sock) > -1) this.vote(i, sock, true);
	}

	if(index != -1){
		this.users.splice(index, 1);

		this.room.sendAll({
			type: type == undefined ? "userLeftQueue" : type,
			data: {
				queueList: this.makeClientObj()
			}
		});

		return true;
	} else if (sock.user && this.currentdj && this.currentdj.user.uid == sock.user.uid){
		this.advance( true );
		return true;
	}

	return false;
};

djqueue.prototype.move = function(sock, to){
	var from = this.checkQueuePos(sock);
	var res = {success: false};

	if (from == to){
		//TODO: Return error to sender, might need to add a callback
		return res;
	}

	if(to < this.users.length && to >= 0 && from != -1 && sock != this.currentdj){
		this.users.splice(from, 1);
		var pos = (to > from+1 ? to-1 : to);
		this.users.splice( pos, 0, sock);

		res.success = true;
		res.data = {
			queueList: this.makeClientObj(),
			uid: sock.user.data.uid,
			from: from,
			to: to
		};
	}
	return res;
};

djqueue.prototype.swap = function(sock1, sock2){
	var from = this.checkQueuePos(sock1);
	var to = this.checkQueuePos(sock2);
	var res = {success: false};

	if(from != -1 && to != -1 && sock1 != this.currentdj && sock2 != this.currentdj ){
		this.users[from] = sock2;
		this.users[to] = sock1;

		res.data = {
			queueList: this.makeClientObj(),
			uid1: sock1.user.data.uid,
			uid2: sock2.user.data.uid,
			pos1: from,
			pos2: to
		};

		res.success = true;
	}
	return res;
};

djqueue.prototype.advance = function( ignoreCycle, lockSkipPosition ){
	if(this.currentdj){
		this.currentdj.user.lastdj = false;
	}
	this.lastdj = this.currentdj;
	this.lastsong = this.currentsong;
	this.currentsong = null;
	clearTimeout(this.lasttimer);

	var isSetLockSkipPos = typeof lockSkipPosition === 'number' && !isNaN(lockSkipPosition);
	var res = {success: false};

	if(this.users.length >= 1){
		if (this.currentdj && (this.cycle || isSetLockSkipPos) && !ignoreCycle) {
			this.currentdj = null;
			res.position = this._add(this.lastdj, lockSkipPosition).position;
		}
		res.success = true;
		this._advance();
	} else if (this.currentdj != null) {
		this.currentdj = null;
		if((this.cycle || isSetLockSkipPos) && !ignoreCycle) {
			res.position = this._add(this.lastdj, lockSkipPosition).position;
		}

		this._advance();
		res.success = true;
	}
	return res;
};

djqueue.prototype._advance = function(){
	var that = this;
	var lastStart = that.songstart;

	if (that.lastsong != null) {
		var historyObj = {
			votes: {
				like: that.votes.like ? that.votes.like.length : 0,
				grab: that.votes.grab ? that.votes.grab.length : 0,
				dislike: that.votes.dislike ? that.votes.dislike.length : 0
			},
			song: makeSongObj(that.lastsong),
			user: (that.lastdj ? that.lastdj.user.getClientObj() : null),
			start: lastStart
		};
		that.room.addToHistory(historyObj);
	}

	if (this.users.length > 0){
		this.currentdj = this.users.splice(0, 1)[0];
		var pl = this.currentdj.user.playlistCache[this.currentdj.user.activepl];

		pl.getFirstExpanded(function(err, data){
			if (err){
				return that._advance();
			}

			//Show the unavailable song for a limited amount of time
			if(data.unavailable){
				data.duration = 5;
			}

			that.currentsong = data;
			pl.shiftToBottom();

			that.songstart = Date.now();
			that.room.sendAll({
				type: "advance",
				data: {
					last: {
						song: makeSongObj(that.lastsong),
						uid: (that.lastdj && that.lastdj.user ? that.lastdj.user.uid : null),
						start: lastStart
					},
					next: {
						song: makeSongObj(that.currentsong),
						uid: (that.currentdj && that.currentdj.user ? that.currentdj.user.uid : null),
						start: that.songstart
					}
				}
			});

			//	Allow to play streaming or live videos
			if (that.currentsong && that.currentsong.duration > 0 || !Roles.checkPermission(that.currentdj.user.role, 'djqueue.playLiveVideos')){
				that.lasttimer = setTimeout(function(){
					that.advance(that.currentdj.user.lastdj);
				}, that.currentsong.duration * 1000);
			}

			that.votes = new defaultVoteObj;

			if (that.currentsong && that.currentdj){
				that.room.updateLobbyServer(that.currentsong, that.currentdj ? that.currentdj.user.getClientObj() : null);
			}
		});
	}else{
		//if (that.lastsong === null || that.lastdj === null) return;

		that.votes = new defaultVoteObj;
		that.songstart = null;

		that.room.sendAll({
			type: "advance",
			data: {
				last: {
					song: makeSongObj(that.lastsong),
					uid: (that.lastdj ? that.lastdj.user.uid : null),
					start: lastStart
				},
				next: {
					song: null,
					uid: null,
					start: that.songstart
				}
			}
		});
		that.room.updateLobbyServer(null, null);
	}

	return false;
};

djqueue.prototype.skip = function(){
	if(this.currentdj != null){
		return this.advance(this.currentdj.user.lastdj);
	}
	return {success: false};
};

djqueue.prototype.modSkip = function(lockSkipPosition){
	if(this.currentdj != null){
		return this.advance((typeof lockSkipPosition === 'number' && !isNaN(lockSkipPosition)) ? false : this.currentdj.lastdj, lockSkipPosition);
	}
	return {success: false};
};

djqueue.prototype.toggleCycle = function(){
	this.cycle = !this.cycle;

	return this.cycle;
};

djqueue.prototype.toggleLock = function(){
	this.lock = !this.lock;

	return this.lock;
};

djqueue.prototype.setLimit = function(limit){
	if(limit >= 1){
		this.limit = limit;
		return true;
	}
	return false;
};

djqueue.prototype.vote = function(voteType, sock, leaving){
	voteType = voteType.toLowerCase();
	if (['like', 'dislike', 'grab'].indexOf(voteType) == -1) return false;
	if (!this.currentdj) return false;
	if (!sock.user) return false;
	if (sock.user.uid == this.currentdj.user.uid) return false;

	var ind = this.votes[voteType].indexOf(sock);

	if (ind > -1){
		if (voteType == 'grab' && !leaving) return false;

		this.votes[voteType].splice(ind, 1);
	}else{
		if (voteType == 'like' && this.votes.dislike.indexOf(sock) > -1){
			this.vote('dislike', sock);
		}else if (voteType == 'dislike' && this.votes.like.indexOf(sock) > -1){
			this.vote('like', sock);
		}

		this.votes[voteType].push(sock);
	}

	this.room.sendAll({
		type: 'voteUpdate',
		data: {
			votes: this.makeVoteObj(),
			uid: sock.user.uid,
			action: voteType,
			voted: (ind > -1 ? -1 : 1) // -1 for removed vote, 1 for added vote
		},
	});

	return true;
};

djqueue.prototype.makeClientObj = function(){
	var arr = [];
	for(var i =0; i < this.users.length; i++){
		arr.push(this.users[i].user.uid);
	}

	return arr;
};

djqueue.prototype.makeVoteObj = function(){
	return {
		like: this.votes.like.length,
		dislike: this.votes.dislike.length,
		grab: this.votes.grab.length
	};
};

djqueue.prototype.getVotes = function(){
	var like = [];
	var dislike = [];
	var grab = [];

	for (var i in this.votes.like)	like.push(this.votes.like[i].user.uid);
	for (var i in this.votes.dislike)	dislike.push(this.votes.dislike[i].user.uid);
	for (var i in this.votes.grab)	grab.push(this.votes.grab[i].user.uid);
	return {
		like: like,
		dislike: dislike,
		grab: grab
	};
};

djqueue.prototype.getUserVote = function(socket){
	var votes = [];

	if (!socket.user) return votes;

	if (this.votes.like.filter(function(a){ return a.user.uid == socket.user.uid;}).length != 0)	votes.push('like');
	if (this.votes.dislike.filter(function(a){ return a.user.uid == socket.user.uid;}).length != 0)	votes.push('dislike');
	if (this.votes.grab.filter(function(a){ return a.user.uid == socket.user.uid;}).length != 0)	votes.push('grab');
	return votes;
};

djqueue.prototype.getCurrentTime = function(){
	if (!this.currentsong || this.currentsong.duration == 0 || !this.currentdj) return 0;

	return Math.abs((Date.now( )- this.songstart) / 1000);
};

djqueue.prototype.checkQueuePos = function(sock){
	if (!sock.user) return -1;
	for(var i =0; i < this.users.length; i++){
		if(this.users[i].user.uid == sock.user.uid) return i;
	}
	return -1;
	//return this.users.indexOf( sock );
};

djqueue.prototype.isPlaying = function(sock){
	return sock == this.currentdj || this.checkQueuePos(sock) != -1;
};

/* DJ Queue Object End */

module.exports = djqueue;
<|endoftext|>


<commit_before>import mongoose, { Schema } from 'mongoose';
import _ from 'lodash';
import Promise from 'bluebird';

import { MongooseHelper } from 'api/helpers';
import { Constants } from 'common';

const ObjectId = Schema.Types.ObjectId;

const Model = new Schema({
  title: { type: String, required: true, trim: true, index: true },
  category: { type: String, required: true, default: 'misc', index: true },
  description: { type: String, trim: true, default: '' },
  tags: [{ type: String, trim: true }],
  uploader: { type: ObjectId, ref: 'User', required: true, index: true },
  urls: [{ type: String }],
  zipUrl: { type: String },
  socialData: {
    views: { type: Number, default: 0 },
    favorites: [{ type: ObjectId, ref: 'User', index: true }],
    flags: [{ type: ObjectId, ref: 'User', index: true }]
  },
  metaData: {
    vertices: { type: Number, default: 0 },
    faces: { type: Number, default: 0 },
    hasExternalTexture: { type: Boolean, default: false },
    boundingRadius: { type: Number, default: 1 }
  },
  imageUrls: [{ type: String }],
  createdAt: { type: Date, index: true, default: Date.now },
  updatedAt: { type: Date, index: true, default: Date.now },
  statisticsPoints: [{
    camLongtitude: { type: Number, default: 0 },
    camLatitude: { type: Number, default: 0 },
    camRadius: { type: Number, default: 0 },
    lookAtLongtitude: { type: Number, default: 0 },
    lookAtLatitude: { type: Number, default: 0 },
    count: { type: Number, default: 0 }
  }]
});

// -----------------------------------------------------
// -----------------MODEL VALIDATION--------------------
// -----------------------------------------------------

Model.statics.validate = function (model, fields) {
  if (fields._id) {
    if (!model._id) {
      return Constants.ERROR_MODEL_ID_REQUIRED;
    }

    if (!_.isString(model._id) || model._id.trim().length === 0) {
      return Constants.ERROR_MOEDL_ID_INVALID;
    }
  }

  if (fields.title) {
    if (!model.title) {
      return Constants.ERROR_MODEL_TITLE_REQUIRED;
    }
    if (model.title.trim().length < Constants.MODEL_TITLE_MIN_LENGTH) {
      return Constants.ERROR_MODEL_TITLE_MIN_LENGTH;
    }
    if (model.title.trim().length > Constants.MODEL_TITLE_MAX_LENGTH) {
      return Constants.ERROR_MODEL_TITLE_MAX_LENGTH;
    }
  }

  return null;
};

Model.statics.validateFilePaths = function (filePaths) {
  const objFiles = filePaths.filter(filePath => filePath.endsWith('.obj'));
  const mtlFiles = filePaths.filter(filePath => filePath.endsWith('.mtl'));
  const textureFiles = filePaths.filter(filePath => {
    return !filePath.endsWith('.obj') && !filePath.endsWith('.mtl');
  });
  if (objFiles.length > 1) {
    return Constants.ERROR_MODEL_OBJ_FILE_NOT_UNIQUE;
  }
  if (mtlFiles.length > 1) {
    return Constants.ERROR_MODEL_MTL_FILE_NOT_UNIQUE;
  }
  if (mtlFiles.length === 0 && textureFiles.length > 0) {
    return Constants.ERROR_MODEL_REDUNDANT_TEXTURES;
  }

  return null;
};

// -----------------------------------------------------
// -----------------MODEL QUERY-------------------------
// -----------------------------------------------------

Model.statics.getModels = function (query = {}, options = {}) {
  return MongooseHelper.find(this, query, options);
};

Model.statics.getModelById = function (modelId, query = {}, options = {}) {
  return MongooseHelper.findOne(this, { ...query, _id: mongoose.Types.ObjectId(modelId) }, options)
    .then(result => {
      result.statisticsPoints.sort((point1, point2) => {
        return point2.count - point1.count;
      });
      result.statisticsPoints = result.statisticsPoints.slice(0, 10);
      return result;
    });
};

Model.statics.getLatestModels = function () {
  return MongooseHelper.find(
    this,
    {},
    {
      limit: 20,
      sort: '-updatedAt',
      populate: 'uploader'
    }
  );
};

Model.statics.getTopModels = function () {
  return MongooseHelper.find(
    this,
    {},
    {
      limit: 9,
      sort: '-socialData.views',
      populate: 'uploader'
    }
  );
};

Model.statics.getBrowsePageModels = function (urlQuery) {
  let query = {};
  const options = {
    limit: 21,
    populate: 'uploader',
    sort: '-updatedAt'
  };

  if (urlQuery.searchString) {
    const searchWords = urlQuery.searchString.split(/[ ,]+/);
    const regExp = new RegExp('(' + searchWords.join('|') + ')', 'i');

    let noOfFields = 3;
    if (urlQuery.searchTitle !== undefined) {
      noOfFields--;
    }
    if (urlQuery.searchTag !== undefined) {
      noOfFields--;
    }
    if (urlQuery.searchUser !== undefined) {
      noOfFields--;
    }
    if (noOfFields === 1) {
      if (!urlQuery.searchTitle) {
        query.title = regExp;
      }
      if (!urlQuery.searchTag) {
        query.tags = regExp;
      }
      if (!urlQuery.searchUser) {
        query.uploader = { $in: urlQuery.userIds };
      }
    } else if (noOfFields > 1) {
      const queryArr = [];
      if (!urlQuery.searchTitle) {
        queryArr.push({ title: regExp });
      }
      if (!urlQuery.searchTag) {
        queryArr.push({ tags: regExp });
      }
      if (!urlQuery.searchUser) {
        queryArr.push({ uploader: { $in: urlQuery.userIds } });
      }
      query = { $or: queryArr };
    } else {
      // No fields specified, return empty array
      return Promise.resolve([]);
    }
  }

  if (urlQuery.category && urlQuery.category !== 'all') {
    for (let i = 0; i < Constants.MODEL_CATEGORIES.length; i++) {
      if (Constants.MODEL_CATEGORIES[i].toLowerCase() === urlQuery.category.toLowerCase()) {
        query.category = new RegExp(urlQuery.category, 'i');
      }
    }
  }

  if (urlQuery.sort === '1') {
    options.sort = '-socialData.views';
  }

  if (urlQuery.sort === '2') {
    options.sort = 'title';
  }

  if (urlQuery.page && urlQuery.page !== '1') {
    const parsedInt = parseInt(urlQuery.page, 10);
    if (!isNaN(parsedInt) && parsedInt > 1) {
      options.skip = (parsedInt - 1) * 20;
    }
  }

  return MongooseHelper.find(this, query, options);
};

// -----------------------------------------------------
// -----------------MODEL CREATION----------------------
// -----------------------------------------------------

Model.statics.createModel = function (model) {
  const fields = [
    'title',
    'category',
    'description',
    'tags',
    'socialData',
    'metaData',
    'uploader',
    'zipUrl',
    'urls',
    'imageUrls'
  ];
  const modelInfo = _.pick(model, fields);
  // Split tags into array
  if (modelInfo.tags) {
    modelInfo.tags = modelInfo.tags.split(',').map(tag => tag.trim());
  }

  return MongooseHelper.create(this, modelInfo);
};

Model.statics.updateModelInfo = function (modelId, info) {
  const fields = ['title', 'category', 'description', 'tags'];
  const modelInfo = _.pick(info, fields);
  // Split tags into array
  if (modelInfo.tags) {
    modelInfo.tags = modelInfo.tags.split(',').map(tag => tag.trim());
  }

  return MongooseHelper.findOneAndUpdate(this, { _id: modelId }, info, { new: true }, { populate: 'uploader' });
};

// -----------------------------------------------------
// -----------------MODEL SOCIAL DATA-------------------
// -----------------------------------------------------

Model.statics.flagModel = function (modelId, userId) {
  const condition = {
    _id: modelId
  };
  const update = {
    $addToSet: {
      'socialData.flags': userId
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, update, { new: true }, { populate: 'uploader' });
};

Model.statics.unflagModel = function (modelId, userId) {
  const condition = {
    _id: modelId
  };
  const update = {
    $pull: {
      'socialData.flags': userId
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, update, { new: true }, { populate: 'uploader' });
};

Model.statics.incrementViews = function (modelId, amount) {
  const condition = {
    _id: modelId
  };
  const update = {
    $inc: {
      'socialData.views': amount
    }
  };

  return MongooseHelper.update(this, condition, update);
};

// -----------------------------------------------------
// -----------------MODEL SNAPSHOTS---------------------
// -----------------------------------------------------

Model.statics.addSnapshots = function (modelId, snapshotUrls) {
  const condition = {
    _id: modelId
  };
  const update = {
    $push: {
      imageUrls: {
        $each: snapshotUrls
      }
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, update, { new: true });
};

Model.statics.deleteSnapshot = function (modelId, index) {
  const condition = {
    _id: modelId
  };
  const unsetUpdate = {
    $unset: {
      [`imageUrls.${index}`]: 1
    }
  };
  const pullUpdate = {
    $pull: {
      imageUrls: null
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, unsetUpdate)
    .then(() => MongooseHelper.findOneAndUpdate(this, condition, pullUpdate, { new: true }));
};

// -----------------------------------------------------
// -----------------MODEL STATISTICS-------------------
// -----------------------------------------------------

Model.statics.addStatisticsPoint = function (modelId, point) {
  const condition = {
    _id: modelId,
    statisticsPoints: { $elemMatch: { ...point } }
  };

  const invCondition = {
    _id: modelId,
    statisticsPoints: { $not: { $elemMatch: { ...point } } }
  };

  const addToSetUpdate = {
    $addToSet: { statisticsPoints: { ...point } }
  };

  const incUpdate = {
    $inc: { 'statisticsPoints.$.count': 1 }
  };

  return MongooseHelper.findOneAndUpdate(this, invCondition, addToSetUpdate)
    .then(() => MongooseHelper.findOneAndUpdate(this, condition, incUpdate));
};

export default mongoose.model('Model', Model, 'Model');
<commit_msg>[update] return only top 10 popular points, converted to x,y,z forms<commit_after>import mongoose, { Schema } from 'mongoose';
import _ from 'lodash';
import Promise from 'bluebird';

import { MongooseHelper } from 'api/helpers';
import { Constants } from 'common';

const ObjectId = Schema.Types.ObjectId;

const Model = new Schema({
  title: { type: String, required: true, trim: true, index: true },
  category: { type: String, required: true, default: 'misc', index: true },
  description: { type: String, trim: true, default: '' },
  tags: [{ type: String, trim: true }],
  uploader: { type: ObjectId, ref: 'User', required: true, index: true },
  urls: [{ type: String }],
  zipUrl: { type: String },
  socialData: {
    views: { type: Number, default: 0 },
    favorites: [{ type: ObjectId, ref: 'User', index: true }],
    flags: [{ type: ObjectId, ref: 'User', index: true }]
  },
  metaData: {
    vertices: { type: Number, default: 0 },
    faces: { type: Number, default: 0 },
    hasExternalTexture: { type: Boolean, default: false },
    boundingRadius: { type: Number, default: 1 }
  },
  imageUrls: [{ type: String }],
  createdAt: { type: Date, index: true, default: Date.now },
  updatedAt: { type: Date, index: true, default: Date.now },
  statisticsPoints: [{
    camLongtitude: { type: Number, default: 0 },
    camLatitude: { type: Number, default: 0 },
    camRadius: { type: Number, default: 0 },
    lookAtLongtitude: { type: Number, default: 0 },
    lookAtLatitude: { type: Number, default: 0 },
    count: { type: Number, default: 0 }
  }]
});

// -----------------------------------------------------
// -----------------MODEL VALIDATION--------------------
// -----------------------------------------------------

Model.statics.validate = function (model, fields) {
  if (fields._id) {
    if (!model._id) {
      return Constants.ERROR_MODEL_ID_REQUIRED;
    }

    if (!_.isString(model._id) || model._id.trim().length === 0) {
      return Constants.ERROR_MOEDL_ID_INVALID;
    }
  }

  if (fields.title) {
    if (!model.title) {
      return Constants.ERROR_MODEL_TITLE_REQUIRED;
    }
    if (model.title.trim().length < Constants.MODEL_TITLE_MIN_LENGTH) {
      return Constants.ERROR_MODEL_TITLE_MIN_LENGTH;
    }
    if (model.title.trim().length > Constants.MODEL_TITLE_MAX_LENGTH) {
      return Constants.ERROR_MODEL_TITLE_MAX_LENGTH;
    }
  }

  return null;
};

Model.statics.validateFilePaths = function (filePaths) {
  const objFiles = filePaths.filter(filePath => filePath.endsWith('.obj'));
  const mtlFiles = filePaths.filter(filePath => filePath.endsWith('.mtl'));
  const textureFiles = filePaths.filter(filePath => {
    return !filePath.endsWith('.obj') && !filePath.endsWith('.mtl');
  });
  if (objFiles.length > 1) {
    return Constants.ERROR_MODEL_OBJ_FILE_NOT_UNIQUE;
  }
  if (mtlFiles.length > 1) {
    return Constants.ERROR_MODEL_MTL_FILE_NOT_UNIQUE;
  }
  if (mtlFiles.length === 0 && textureFiles.length > 0) {
    return Constants.ERROR_MODEL_REDUNDANT_TEXTURES;
  }

  return null;
};

// -----------------------------------------------------
// -----------------MODEL QUERY-------------------------
// -----------------------------------------------------

Model.statics.getModels = function (query = {}, options = {}) {
  return MongooseHelper.find(this, query, options);
};

Model.statics.getModelById = function (modelId, query = {}, options = {}) {
  return MongooseHelper.findOne(this, { ...query, _id: mongoose.Types.ObjectId(modelId) }, options)
    .then(result => {
      result.statisticsPoints.sort((point1, point2) => {
        return point2.count - point1.count;
      });
      result.statisticsPoints = result.statisticsPoints.slice(0, 10);

      const xyzCoordsPoints = [];
      for (let i = 0; i < result.statisticsPoints.length; i++) {
        const camAzimuth = result.statisticsPoints[i].camLongtitude;
        const camIncline = result.statisticsPoints[i].camLatitude;
        const camRadius = result.statisticsPoints[i].camRadius;
        let camSegX = camRadius * Math.sin(camIncline / 180.0 * Math.PI) * Math.sin(camAzimuth / 180.0 * Math.PI);
        let camSegY = camRadius * Math.cos(camIncline / 180.0 * Math.PI);
        let camSegZ = camRadius * Math.sin(camIncline / 180.0 * Math.PI) * Math.cos(camAzimuth / 180.0 * Math.PI);
        camSegX = Math.round(camSegX);
        camSegY = Math.round(camSegY);
        camSegZ = Math.round(camSegZ);

        const lookAtAzimuth = result.statisticsPoints[i].lookAtLongtitude;
        const lookAtIncline = result.statisticsPoints[i].lookAtLatitude;
        let lookAtSegX = Math.sin(lookAtIncline / 180.0 * Math.PI) * Math.sin(lookAtAzimuth / 180.0 * Math.PI);
        let lookAtSegY = Math.cos(lookAtIncline / 180.0 * Math.PI);
        let lookAtSegZ = Math.sin(lookAtIncline / 180.0 * Math.PI) * Math.cos(lookAtAzimuth / 180.0 * Math.PI);
        lookAtSegX = Math.round(lookAtSegX);
        lookAtSegY = Math.round(lookAtSegY);
        lookAtSegZ = Math.round(lookAtSegZ);

        xyzCoordsPoints.push({
          camSegX,
          camSegY,
          camSegZ,
          lookAtSegX,
          lookAtSegY,
          lookAtSegZ,
          count: result.statisticsPoints[i].count
        });
      }

      const newResult = result.toObject();
      newResult.statisticsPoints = undefined;
      newResult.popularPoints = xyzCoordsPoints;
      return newResult;
    });
};

Model.statics.getLatestModels = function () {
  return MongooseHelper.find(
    this,
    {},
    {
      limit: 20,
      sort: '-updatedAt',
      populate: 'uploader'
    }
  );
};

Model.statics.getTopModels = function () {
  return MongooseHelper.find(
    this,
    {},
    {
      limit: 9,
      sort: '-socialData.views',
      populate: 'uploader'
    }
  );
};

Model.statics.getBrowsePageModels = function (urlQuery) {
  let query = {};
  const options = {
    limit: 21,
    populate: 'uploader',
    sort: '-updatedAt'
  };

  if (urlQuery.searchString) {
    const searchWords = urlQuery.searchString.split(/[ ,]+/);
    const regExp = new RegExp('(' + searchWords.join('|') + ')', 'i');

    let noOfFields = 3;
    if (urlQuery.searchTitle !== undefined) {
      noOfFields--;
    }
    if (urlQuery.searchTag !== undefined) {
      noOfFields--;
    }
    if (urlQuery.searchUser !== undefined) {
      noOfFields--;
    }
    if (noOfFields === 1) {
      if (!urlQuery.searchTitle) {
        query.title = regExp;
      }
      if (!urlQuery.searchTag) {
        query.tags = regExp;
      }
      if (!urlQuery.searchUser) {
        query.uploader = { $in: urlQuery.userIds };
      }
    } else if (noOfFields > 1) {
      const queryArr = [];
      if (!urlQuery.searchTitle) {
        queryArr.push({ title: regExp });
      }
      if (!urlQuery.searchTag) {
        queryArr.push({ tags: regExp });
      }
      if (!urlQuery.searchUser) {
        queryArr.push({ uploader: { $in: urlQuery.userIds } });
      }
      query = { $or: queryArr };
    } else {
      // No fields specified, return empty array
      return Promise.resolve([]);
    }
  }

  if (urlQuery.category && urlQuery.category !== 'all') {
    for (let i = 0; i < Constants.MODEL_CATEGORIES.length; i++) {
      if (Constants.MODEL_CATEGORIES[i].toLowerCase() === urlQuery.category.toLowerCase()) {
        query.category = new RegExp(urlQuery.category, 'i');
      }
    }
  }

  if (urlQuery.sort === '1') {
    options.sort = '-socialData.views';
  }

  if (urlQuery.sort === '2') {
    options.sort = 'title';
  }

  if (urlQuery.page && urlQuery.page !== '1') {
    const parsedInt = parseInt(urlQuery.page, 10);
    if (!isNaN(parsedInt) && parsedInt > 1) {
      options.skip = (parsedInt - 1) * 20;
    }
  }

  return MongooseHelper.find(this, query, options);
};

// -----------------------------------------------------
// -----------------MODEL CREATION----------------------
// -----------------------------------------------------

Model.statics.createModel = function (model) {
  const fields = [
    'title',
    'category',
    'description',
    'tags',
    'socialData',
    'metaData',
    'uploader',
    'zipUrl',
    'urls',
    'imageUrls'
  ];
  const modelInfo = _.pick(model, fields);
  // Split tags into array
  if (modelInfo.tags) {
    modelInfo.tags = modelInfo.tags.split(',').map(tag => tag.trim());
  }

  return MongooseHelper.create(this, modelInfo);
};

Model.statics.updateModelInfo = function (modelId, info) {
  const fields = ['title', 'category', 'description', 'tags'];
  const modelInfo = _.pick(info, fields);
  // Split tags into array
  if (modelInfo.tags) {
    modelInfo.tags = modelInfo.tags.split(',').map(tag => tag.trim());
  }

  return MongooseHelper.findOneAndUpdate(this, { _id: modelId }, info, { new: true }, { populate: 'uploader' });
};

// -----------------------------------------------------
// -----------------MODEL SOCIAL DATA-------------------
// -----------------------------------------------------

Model.statics.flagModel = function (modelId, userId) {
  const condition = {
    _id: modelId
  };
  const update = {
    $addToSet: {
      'socialData.flags': userId
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, update, { new: true }, { populate: 'uploader' });
};

Model.statics.unflagModel = function (modelId, userId) {
  const condition = {
    _id: modelId
  };
  const update = {
    $pull: {
      'socialData.flags': userId
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, update, { new: true }, { populate: 'uploader' });
};

Model.statics.incrementViews = function (modelId, amount) {
  const condition = {
    _id: modelId
  };
  const update = {
    $inc: {
      'socialData.views': amount
    }
  };

  return MongooseHelper.update(this, condition, update);
};

// -----------------------------------------------------
// -----------------MODEL SNAPSHOTS---------------------
// -----------------------------------------------------

Model.statics.addSnapshots = function (modelId, snapshotUrls) {
  const condition = {
    _id: modelId
  };
  const update = {
    $push: {
      imageUrls: {
        $each: snapshotUrls
      }
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, update, { new: true });
};

Model.statics.deleteSnapshot = function (modelId, index) {
  const condition = {
    _id: modelId
  };
  const unsetUpdate = {
    $unset: {
      [`imageUrls.${index}`]: 1
    }
  };
  const pullUpdate = {
    $pull: {
      imageUrls: null
    }
  };

  return MongooseHelper.findOneAndUpdate(this, condition, unsetUpdate)
    .then(() => MongooseHelper.findOneAndUpdate(this, condition, pullUpdate, { new: true }));
};

// -----------------------------------------------------
// -----------------MODEL STATISTICS-------------------
// -----------------------------------------------------

Model.statics.addStatisticsPoint = function (modelId, point) {
  const condition = {
    _id: modelId,
    statisticsPoints: { $elemMatch: { ...point } }
  };

  const invCondition = {
    _id: modelId,
    statisticsPoints: { $not: { $elemMatch: { ...point } } }
  };

  const addToSetUpdate = {
    $addToSet: { statisticsPoints: { ...point } }
  };

  const incUpdate = {
    $inc: { 'statisticsPoints.$.count': 1 }
  };

  return MongooseHelper.findOneAndUpdate(this, invCondition, addToSetUpdate)
    .then(() => MongooseHelper.findOneAndUpdate(this, condition, incUpdate));
};

export default mongoose.model('Model', Model, 'Model');
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import {
  TextInput,
  StyleSheet,
  View,
  TouchableOpacity,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import dismissKeyboard from 'react-native/Libraries/Utilities/dismissKeyboard';

const styles = StyleSheet.create({
  searchBar: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: 'transparent',
    borderColor: '#b6b6b6',
    borderStyle: 'solid',
    borderWidth: 1,
  },
  searchBarInput: {
    flex: 1,
    fontWeight: 'normal',
    color: '#212121',
    backgroundColor: 'transparent',
  },
});

export default class SearchBar extends React.Component {

  static propTypes = {
    height: PropTypes.number.isRequired,
    autoCorrect: PropTypes.bool,
    returnKeyType: PropTypes.string,
    onSearchChange: PropTypes.func,
    placeholder: PropTypes.string,
    padding: PropTypes.number,
    inputStyle: PropTypes.object,
    iconCloseName: PropTypes.string,
    iconSearchName: PropTypes.string,
    iconBackName: PropTypes.string,
    placeholderColor: PropTypes.string,
    iconColor: PropTypes.string
  }

  static defaultProps = {
    onSearchChange: () => {},
    inputStyle: {},
    iconCloseName: "md-close",
    iconSearchName: "md-search",
    iconBackName: "md-arrow-back",
    placeholder: "Search...",
    returnKeyType: "search",
    padding: 5,
    placeholderColor: "#bdbdbd",
    iconColor: "#737373"
  }

  constructor(props) {
    super(props);
    this.state = {
      isOnFocus: false,
    };
    this._onFocus = this._onFocus.bind(this);
    this._onBlur = this._onBlur.bind(this);
    this._onClose = this._onClose.bind(this);
  }

  _onClose() {
    this._textInput.setNativeProps({ text: '' });
    this.props.onSearchChange({ nativeEvent: { text : ''}});
    if (this.props.onClose) {
      this.props.onClose();
    }
  }

  _onFocus() {
    this.setState({ isOnFocus: true });
    if (this.props.onFocus) {
      this.props.onFocus();
    }
  }

  _onBlur() {
    this.setState({ isOnFocus: false });
    if (this.props.onBlur) {
      this.props.onBlur();
    }
    this._dismissKeyboard();
  }

  _dismissKeyboard () {
    dismissKeyboard()
  }

  render() {
    const {
      height,
      autoCorrect,
      returnKeyType,
      onSearchChange,
      placeholder,
      padding,
      inputStyle,
      iconColor,
      iconBackName,
      iconSearchName,
      iconCloseName,
      placeholderColor
    } = this.props;

    let { iconSize } = this.props

    iconSize = typeof iconSize !== 'undefined' ? iconSize : height * 0.5

    return (
      <View
        onStartShouldSetResponder={this._dismissKeyboard}
        style={{padding: padding }}
      >
        <View
          style={
            [
              styles.searchBar,
              {
                height: height + 10,
                paddingLeft: height * 0.25,
              },
              inputStyle
            ]
          }
        >
          {this.state.isOnFocus ?
            <TouchableOpacity onPress={this._dismissKeyboard}>
              <Icon
                name={iconBackName} size={height * 0.5}
                color={iconColor}
              />
            </TouchableOpacity>
          :
            <Icon
              name={iconSearchName} size={height * 0.5}
              color={iconColor}
            />
          }
          <TextInput
            autoCorrect={autoCorrect === true}
            ref={(c) => (this._textInput = c)}
            returnKeyType={returnKeyType}
            onFocus={this._onFocus}
            onBlur={this._onBlur}
            onChangeText={onSearchChange}
            placeholder={placeholder}
            placeholderTextColor={placeholderColor}
            underlineColorAndroid="transparent"
            style={
              [styles.searchBarInput,
                {
                  paddingLeft: height * 0.5,
                  fontSize: height * 0.4,
                },
              ]
            }
          />
          {this.state.isOnFocus ?
            <TouchableOpacity onPress={this._onClose}>
              <Icon
                style={{paddingRight: height * 0.5 }}
                name={iconCloseName} size={iconSize}
                color={iconColor}
              />
            </TouchableOpacity>
          : null
          }
        </View>
      </View>
    );
  }
}
<commit_msg>Added props to override text styles<commit_after>import React, { PropTypes } from 'react';
import {
  TextInput,
  StyleSheet,
  View,
  TouchableOpacity,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import dismissKeyboard from 'react-native/Libraries/Utilities/dismissKeyboard';

const styles = StyleSheet.create({
  searchBar: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: 'transparent',
    borderColor: '#b6b6b6',
    borderStyle: 'solid',
    borderWidth: 1,
  },
  searchBarInput: {
    flex: 1,
    fontWeight: 'normal',
    color: '#212121',
    backgroundColor: 'transparent',
  },
});

export default class SearchBar extends React.Component {

  static propTypes = {
    height: PropTypes.number.isRequired,
    autoCorrect: PropTypes.bool,
    returnKeyType: PropTypes.string,
    onSearchChange: PropTypes.func,
    placeholder: PropTypes.string,
    padding: PropTypes.number,
    inputStyle: PropTypes.object,
    iconCloseName: PropTypes.string,
    iconSearchName: PropTypes.string,
    iconBackName: PropTypes.string,
    placeholderColor: PropTypes.string,
    iconColor: PropTypes.string,
    textStyle: PropTypes.object
  }

  static defaultProps = {
    onSearchChange: () => {},
    inputStyle: {},
    iconCloseName: "md-close",
    iconSearchName: "md-search",
    iconBackName: "md-arrow-back",
    placeholder: "Search...",
    returnKeyType: "search",
    padding: 5,
    placeholderColor: "#bdbdbd",
    iconColor: "#737373",
    textStyle: {}
  }

  constructor(props) {
    super(props);
    this.state = {
      isOnFocus: false,
    };
    this._onFocus = this._onFocus.bind(this);
    this._onBlur = this._onBlur.bind(this);
    this._onClose = this._onClose.bind(this);
  }

  _onClose() {
    this._textInput.setNativeProps({ text: '' });
    this.props.onSearchChange({ nativeEvent: { text : ''}});
    if (this.props.onClose) {
      this.props.onClose();
    }
  }

  _onFocus() {
    this.setState({ isOnFocus: true });
    if (this.props.onFocus) {
      this.props.onFocus();
    }
  }

  _onBlur() {
    this.setState({ isOnFocus: false });
    if (this.props.onBlur) {
      this.props.onBlur();
    }
    this._dismissKeyboard();
  }

  _dismissKeyboard () {
    dismissKeyboard()
  }

  render() {
    const {
      height,
      autoCorrect,
      returnKeyType,
      onSearchChange,
      placeholder,
      padding,
      inputStyle,
      iconColor,
      iconBackName,
      iconSearchName,
      iconCloseName,
      placeholderColor,
      textStyle
    } = this.props;

    let { iconSize } = this.props

    iconSize = typeof iconSize !== 'undefined' ? iconSize : height * 0.5

    return (
      <View
        onStartShouldSetResponder={this._dismissKeyboard}
        style={{padding: padding }}
      >
        <View
          style={
            [
              styles.searchBar,
              {
                height: height + 10,
                paddingLeft: height * 0.25,
              },
              inputStyle
            ]
          }
        >
          {this.state.isOnFocus ?
            <TouchableOpacity onPress={this._dismissKeyboard}>
              <Icon
                name={iconBackName} size={height * 0.5}
                color={iconColor}
              />
            </TouchableOpacity>
          :
            <Icon
              name={iconSearchName} size={height * 0.5}
              color={iconColor}
            />
          }
          <TextInput
            autoCorrect={autoCorrect === true}
            ref={(c) => (this._textInput = c)}
            returnKeyType={returnKeyType}
            onFocus={this._onFocus}
            onBlur={this._onBlur}
            onChangeText={onSearchChange}
            placeholder={placeholder}
            placeholderTextColor={placeholderColor}
            underlineColorAndroid="transparent"
            style={
              [styles.searchBarInput,
                {
                  paddingLeft: height * 0.5,
                  fontSize: height * 0.4,
                },
                textStyle
              ]
            }
          />
          {this.state.isOnFocus ?
            <TouchableOpacity onPress={this._onClose}>
              <Icon
                style={{paddingRight: height * 0.5 }}
                name={iconCloseName} size={iconSize}
                color={iconColor}
              />
            </TouchableOpacity>
          : null
          }
        </View>
      </View>
    );
  }
}
<|endoftext|>


<commit_before>/**
 * Created by yan on 15-12-1.
 */

var inherits = require('util').inherits;
var tar = require('tar-stream');
var Pack = tar.pack;

var stripName = function (str, stripLevel) {
  return str.split('/').slice(stripLevel).join('/')
}

inherits(StripPack, Pack);

function StripPack(options) {
  var stripLevel = 1;

  if (options) {
    stripLevel = parseInt(options.strip, 10);
  }

  Pack.call(this, options);
  var self = this;

  var extract = this.extract = tar.extract();

  extract.on('entry', function (header, stream, callback) {
    var originalName = header.name;
    header.name = stripName(originalName, stripLevel);

    //console.log('strip:%d %s => %s', stripLevel, originalName, header.name);

    stream.pipe(self.entry(header, callback));
  });

}

StripPack.prototype.write = function (buffer) {
  this.extract.write(buffer);
}

StripPack.prototype.end = function (buffer) {
  this.finalize();
}

module.exports = StripPack;<commit_msg>Refactoring using transform<commit_after>/**
 * Created by yan on 15-12-1.
 */

var inherits = require('util').inherits;
var tar = require('tar-stream');
var Stream = require('stream');

/**
 * strip leading components in filename
 *
 * @param str
 * @param stripLevel
 * @returns {string}
 */
var stripName = function (str, stripLevel) {
  return str.split('/').slice(stripLevel).join('/');
};

// inheriance
inherits(StripPack, Stream.Transform);

function StripPack(options) {

  Stream.Transform.call(this);

  var stripLevel = 1;

  if (options) {
    stripLevel = parseInt(options.strip, 10);
  }

  var extract = this.extract = tar.extract();
  var pack = this.pack = tar.pack();

  var self = this;

  extract.on('entry', function (header, stream, done) {
    var originalName = header.name;
    header.name = stripName(originalName, stripLevel);
    stream.pipe(pack.entry(header, done));
  });

  extract.on('finish', function () {
    pack.finalize();
  });

  pack.on('data', function (buf) {
    self.push(buf);
  });

  pack.on('end', function () {
    self.push(null);
  });

}

StripPack.prototype._transform = function (buffer, enc, next) {
  this.extract.write(buffer);
  // @todo speed control
  next();
};

StripPack.prototype.end = function () {
  this.extract.end();
};

module.exports = StripPack;<|endoftext|>


<commit_before>// @flow

// Similarily to the DrawerLayout component this deserves to be put in a
// separate repo. Although, keeping it here for the time being will allow us
// to move faster and fix possible issues quicker

import React, { Component } from 'react';
import { Animated, StyleSheet, View } from 'react-native';

import { PanGestureHandler, TapGestureHandler, State } from './GestureHandler';

const DRAG_TOSS = 0.05;

export type PropType = {
  children: any,
  friction: number,
  leftThreshold?: number,
  rightThreshold?: number,
  overshootLeft?: boolean,
  overshootRight?: boolean,
  overshootFriction: number,
  onSwipeableLeftOpen?: Function,
  onSwipeableRightOpen?: Function,
  onSwipeableOpen?: Function,
  onSwipeableClose?: Function,
  onSwipeableLeftWillOpen?: Function,
  onSwipeableRightWillOpen?: Function,
  onSwipeableWillOpen?: Function,
  onSwipeableWillClose?: Function,
  renderLeftActions?: (
    progressAnimatedValue: any,
    dragAnimatedValue: any
  ) => any,
  renderRightActions?: (
    progressAnimatedValue: any,
    dragAnimatedValue: any
  ) => any,
  useNativeAnimations: boolean,
  animationOptions?: Object,
  containerStyle?: Object,
  childrenContainerStyle?: Object,
};
type StateType = {
  dragX: Animated.Value,
  rowTranslation: Animated.Value,
  rowState: number,
  leftWidth: number | typeof undefined,
  rightOffset: number | typeof undefined,
  rowWidth: number | typeof undefined,
};

export default class Swipeable extends Component<PropType, StateType> {
  static defaultProps = {
    friction: 1,
    overshootFriction: 1,
    useNativeAnimations: true,
  };
  _onGestureEvent: ?Animated.Event;
  _transX: ?Animated.Interpolation;
  _showLeftAction: ?Animated.Interpolation | ?Animated.Value;
  _leftActionTranslate: ?Animated.Interpolation;
  _showRightAction: ?Animated.Interpolation | ?Animated.Value;
  _rightActionTranslate: ?Animated.Interpolation;

  constructor(props: PropType) {
    super(props);
    const dragX = new Animated.Value(0);
    this.state = {
      dragX,
      rowTranslation: new Animated.Value(0),
      rowState: 0,
      leftWidth: undefined,
      rightOffset: undefined,
      rowWidth: undefined,
    };
    this._updateAnimatedEvent(props, this.state);

    this._onGestureEvent = Animated.event(
      [{ nativeEvent: { translationX: dragX } }],
      { useNativeDriver: props.useNativeAnimations }
    );
  }

  componentWillUpdate(props: PropType, state: StateType) {
    if (
      this.props.friction !== props.friction ||
      this.props.overshootLeft !== props.overshootLeft ||
      this.props.overshootRight !== props.overshootRight ||
      this.props.overshootFriction !== props.overshootFriction ||
      this.state.leftWidth !== state.leftWidth ||
      this.state.rightOffset !== state.rightOffset ||
      this.state.rowWidth !== state.rowWidth
    ) {
      this._updateAnimatedEvent(props, state);
    }
  }

  _updateAnimatedEvent = (props: PropType, state: StateType) => {
    const { friction, overshootFriction, useNativeAnimations } = props;
    const { dragX, rowTranslation, leftWidth = 0, rowWidth = 0 } = state;
    const { rightOffset = rowWidth } = state;
    const rightWidth = Math.max(0, rowWidth - rightOffset);

    const {
      overshootLeft = leftWidth > 0,
      overshootRight = rightWidth > 0,
    } = props;

    const transX = Animated.add(
      rowTranslation,
      dragX.interpolate({
        inputRange: [0, friction],
        outputRange: [0, 1],
      })
    ).interpolate({
      inputRange: [
        -rightWidth - (overshootRight ? 1 : overshootFriction),
        -rightWidth,
        leftWidth,
        leftWidth + (overshootLeft ? 1 : overshootFriction),
      ],
      outputRange: [
        -rightWidth - (overshootRight || overshootFriction > 1 ? 1 : 0),
        -rightWidth,
        leftWidth,
        leftWidth + (overshootLeft || overshootFriction > 1 ? 1 : 0),
      ],
    });
    this._transX = transX;
    this._showLeftAction =
      leftWidth > 0
        ? transX.interpolate({
            inputRange: [-1, 0, leftWidth],
            outputRange: [0, 0, 1],
          })
        : new Animated.Value(0);
    this._leftActionTranslate = this._showLeftAction.interpolate({
      inputRange: [0, Number.MIN_VALUE],
      outputRange: [-10000, 0],
      extrapolate: 'clamp',
    });
    this._showRightAction =
      rightWidth > 0
        ? transX.interpolate({
            inputRange: [-rightWidth, 0, 1],
            outputRange: [1, 0, 0],
          })
        : new Animated.Value(0);
    this._rightActionTranslate = this._showRightAction.interpolate({
      inputRange: [0, Number.MIN_VALUE],
      outputRange: [-10000, 0],
      extrapolate: 'clamp',
    });
  };

  _onTapHandlerStateChange = ({ nativeEvent }) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      this.close();
    }
  };

  _onHandlerStateChange = ({ nativeEvent }) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      this._handleRelease(nativeEvent);
    }
  };

  _handleRelease = nativeEvent => {
    const { velocityX, translationX: dragX } = nativeEvent;
    const { leftWidth = 0, rowWidth = 0, rowState } = this.state;
    const { rightOffset = rowWidth } = this.state;
    const rightWidth = rowWidth - rightOffset;
    const {
      friction,
      leftThreshold = leftWidth / 2,
      rightThreshold = rightWidth / 2,
    } = this.props;

    const startOffsetX = this._currentOffset() + dragX / friction;
    const translationX = (dragX + DRAG_TOSS * velocityX) / friction;

    let toValue = 0;
    if (rowState === 0) {
      if (translationX > leftThreshold) {
        toValue = leftWidth;
      } else if (translationX < -rightThreshold) {
        toValue = -rightWidth;
      }
    } else if (rowState === 1) {
      // swiped to left
      if (translationX > -leftThreshold) {
        toValue = leftWidth;
      }
    } else {
      // swiped to right
      if (translationX < rightThreshold) {
        toValue = -rightWidth;
      }
    }

    this._animateRow(startOffsetX, toValue, velocityX / friction);
  };

  _animateRow = (fromValue, toValue, velocityX) => {
    const { dragX, rowTranslation } = this.state;
    dragX.setValue(0);
    rowTranslation.setValue(fromValue);

    this.setState({ rowState: Math.sign(toValue) });
    Animated.spring(rowTranslation, {
      restSpeedThreshold: 1.7,
      restDisplacementThreshold: 0.4,
      velocity: velocityX,
      bounciness: 0,
      toValue,
      useNativeDriver: this.props.useNativeAnimations,
      ...this.props.animationOptions,
    }).start(({ finished }) => {
      if (finished) {
        if (toValue > 0 && this.props.onSwipeableLeftOpen) {
          this.props.onSwipeableLeftOpen();
        } else if (toValue < 0 && this.props.onSwipeableRightOpen) {
          this.props.onSwipeableRightOpen();
        }

        if (toValue === 0) {
          this.props.onSwipeableClose && this.props.onSwipeableClose();
        } else {
          this.props.onSwipeableOpen && this.props.onSwipeableOpen();
        }
      }
    });
    if (toValue > 0 && this.props.onSwipeableLeftWillOpen) {
      this.props.onSwipeableLeftWillOpen();
    } else if (toValue < 0 && this.props.onSwipeableRightWillOpen) {
      this.props.onSwipeableRightWillOpen();
    }

    if (toValue === 0) {
      this.props.onSwipeableWillClose && this.props.onSwipeableWillClose();
    } else {
      this.props.onSwipeableWillOpen && this.props.onSwipeableWillOpen();
    }
  };

  _onRowLayout = ({ nativeEvent }) => {
    this.setState({ rowWidth: nativeEvent.layout.width });
  };

  _currentOffset = () => {
    const { leftWidth = 0, rowWidth = 0, rowState } = this.state;
    const { rightOffset = rowWidth } = this.state;
    const rightWidth = rowWidth - rightOffset;
    if (rowState === 1) {
      return leftWidth;
    } else if (rowState === -1) {
      return -rightWidth;
    }
    return 0;
  };

  close = () => {
    this._animateRow(this._currentOffset(), 0);
  };

  openLeft = () => {
    const { leftWidth = 0 } = this.state;
    this._animateRow(this._currentOffset(), leftWidth);
  };

  openRight = () => {
    const { rowWidth = 0 } = this.state;
    const { rightOffset = rowWidth } = this.state;
    const rightWidth = rowWidth - rightOffset;
    this._animateRow(this._currentOffset(), -rightWidth);
  };

  render() {
    const { rowState } = this.state;
    const { children, renderLeftActions, renderRightActions } = this.props;

    const left = renderLeftActions && (
      <Animated.View
        style={[
          styles.leftActions,
          { transform: [{ translateX: this._leftActionTranslate }] },
        ]}>
        {renderLeftActions(this._showLeftAction, this._transX)}
        <View
          onLayout={({ nativeEvent }) =>
            this.setState({ leftWidth: nativeEvent.layout.x })
          }
        />
      </Animated.View>
    );

    const right = renderRightActions && (
      <Animated.View
        style={[
          styles.rightActions,
          { transform: [{ translateX: this._rightActionTranslate }] },
        ]}>
        {renderRightActions(this._showRightAction, this._transX)}
        <View
          onLayout={({ nativeEvent }) =>
            this.setState({ rightOffset: nativeEvent.layout.x })
          }
        />
      </Animated.View>
    );

    return (
      <PanGestureHandler
        {...this.props}
        minDeltaX={10}
        onGestureEvent={this._onGestureEvent}
        onHandlerStateChange={this._onHandlerStateChange}>
        <Animated.View onLayout={this._onRowLayout} style={[styles.container, this.props.containerStyle]}>
          {left}
          {right}
          <TapGestureHandler
            enabled={rowState !== 0}
            onHandlerStateChange={this._onTapHandlerStateChange}>
            <Animated.View
              pointerEvents={rowState === 0 ? 'auto' : 'box-only'}
              style={[
                {
                  transform: [{ translateX: this._transX }],
                },
                this.props.childrenContainerStyle
              ]}>
              {children}
            </Animated.View>
          </TapGestureHandler>
        </Animated.View>
      </PanGestureHandler>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    overflow: 'hidden',
  },
  leftActions: {
    ...StyleSheet.absoluteFillObject,
    flexDirection: 'row',
  },
  rightActions: {
    ...StyleSheet.absoluteFillObject,
    flexDirection: 'row-reverse',
  },
});
<commit_msg>Use UNSAFE_componentWillUpdate in Swipable.js (#655)<commit_after>// @flow

// Similarily to the DrawerLayout component this deserves to be put in a
// separate repo. Although, keeping it here for the time being will allow us
// to move faster and fix possible issues quicker

import React, { Component } from 'react';
import { Animated, StyleSheet, View } from 'react-native';

import { PanGestureHandler, TapGestureHandler, State } from './GestureHandler';

const DRAG_TOSS = 0.05;

export type PropType = {
  children: any,
  friction: number,
  leftThreshold?: number,
  rightThreshold?: number,
  overshootLeft?: boolean,
  overshootRight?: boolean,
  overshootFriction: number,
  onSwipeableLeftOpen?: Function,
  onSwipeableRightOpen?: Function,
  onSwipeableOpen?: Function,
  onSwipeableClose?: Function,
  onSwipeableLeftWillOpen?: Function,
  onSwipeableRightWillOpen?: Function,
  onSwipeableWillOpen?: Function,
  onSwipeableWillClose?: Function,
  renderLeftActions?: (
    progressAnimatedValue: any,
    dragAnimatedValue: any
  ) => any,
  renderRightActions?: (
    progressAnimatedValue: any,
    dragAnimatedValue: any
  ) => any,
  useNativeAnimations: boolean,
  animationOptions?: Object,
  containerStyle?: Object,
  childrenContainerStyle?: Object,
};
type StateType = {
  dragX: Animated.Value,
  rowTranslation: Animated.Value,
  rowState: number,
  leftWidth: number | typeof undefined,
  rightOffset: number | typeof undefined,
  rowWidth: number | typeof undefined,
};

export default class Swipeable extends Component<PropType, StateType> {
  static defaultProps = {
    friction: 1,
    overshootFriction: 1,
    useNativeAnimations: true,
  };
  _onGestureEvent: ?Animated.Event;
  _transX: ?Animated.Interpolation;
  _showLeftAction: ?Animated.Interpolation | ?Animated.Value;
  _leftActionTranslate: ?Animated.Interpolation;
  _showRightAction: ?Animated.Interpolation | ?Animated.Value;
  _rightActionTranslate: ?Animated.Interpolation;

  constructor(props: PropType) {
    super(props);
    const dragX = new Animated.Value(0);
    this.state = {
      dragX,
      rowTranslation: new Animated.Value(0),
      rowState: 0,
      leftWidth: undefined,
      rightOffset: undefined,
      rowWidth: undefined,
    };
    this._updateAnimatedEvent(props, this.state);

    this._onGestureEvent = Animated.event(
      [{ nativeEvent: { translationX: dragX } }],
      { useNativeDriver: props.useNativeAnimations }
    );
  }

  UNSAFE_componentWillUpdate(props: PropType, state: StateType) {
    if (
      this.props.friction !== props.friction ||
      this.props.overshootLeft !== props.overshootLeft ||
      this.props.overshootRight !== props.overshootRight ||
      this.props.overshootFriction !== props.overshootFriction ||
      this.state.leftWidth !== state.leftWidth ||
      this.state.rightOffset !== state.rightOffset ||
      this.state.rowWidth !== state.rowWidth
    ) {
      this._updateAnimatedEvent(props, state);
    }
  }

  _updateAnimatedEvent = (props: PropType, state: StateType) => {
    const { friction, overshootFriction, useNativeAnimations } = props;
    const { dragX, rowTranslation, leftWidth = 0, rowWidth = 0 } = state;
    const { rightOffset = rowWidth } = state;
    const rightWidth = Math.max(0, rowWidth - rightOffset);

    const {
      overshootLeft = leftWidth > 0,
      overshootRight = rightWidth > 0,
    } = props;

    const transX = Animated.add(
      rowTranslation,
      dragX.interpolate({
        inputRange: [0, friction],
        outputRange: [0, 1],
      })
    ).interpolate({
      inputRange: [
        -rightWidth - (overshootRight ? 1 : overshootFriction),
        -rightWidth,
        leftWidth,
        leftWidth + (overshootLeft ? 1 : overshootFriction),
      ],
      outputRange: [
        -rightWidth - (overshootRight || overshootFriction > 1 ? 1 : 0),
        -rightWidth,
        leftWidth,
        leftWidth + (overshootLeft || overshootFriction > 1 ? 1 : 0),
      ],
    });
    this._transX = transX;
    this._showLeftAction =
      leftWidth > 0
        ? transX.interpolate({
            inputRange: [-1, 0, leftWidth],
            outputRange: [0, 0, 1],
          })
        : new Animated.Value(0);
    this._leftActionTranslate = this._showLeftAction.interpolate({
      inputRange: [0, Number.MIN_VALUE],
      outputRange: [-10000, 0],
      extrapolate: 'clamp',
    });
    this._showRightAction =
      rightWidth > 0
        ? transX.interpolate({
            inputRange: [-rightWidth, 0, 1],
            outputRange: [1, 0, 0],
          })
        : new Animated.Value(0);
    this._rightActionTranslate = this._showRightAction.interpolate({
      inputRange: [0, Number.MIN_VALUE],
      outputRange: [-10000, 0],
      extrapolate: 'clamp',
    });
  };

  _onTapHandlerStateChange = ({ nativeEvent }) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      this.close();
    }
  };

  _onHandlerStateChange = ({ nativeEvent }) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      this._handleRelease(nativeEvent);
    }
  };

  _handleRelease = nativeEvent => {
    const { velocityX, translationX: dragX } = nativeEvent;
    const { leftWidth = 0, rowWidth = 0, rowState } = this.state;
    const { rightOffset = rowWidth } = this.state;
    const rightWidth = rowWidth - rightOffset;
    const {
      friction,
      leftThreshold = leftWidth / 2,
      rightThreshold = rightWidth / 2,
    } = this.props;

    const startOffsetX = this._currentOffset() + dragX / friction;
    const translationX = (dragX + DRAG_TOSS * velocityX) / friction;

    let toValue = 0;
    if (rowState === 0) {
      if (translationX > leftThreshold) {
        toValue = leftWidth;
      } else if (translationX < -rightThreshold) {
        toValue = -rightWidth;
      }
    } else if (rowState === 1) {
      // swiped to left
      if (translationX > -leftThreshold) {
        toValue = leftWidth;
      }
    } else {
      // swiped to right
      if (translationX < rightThreshold) {
        toValue = -rightWidth;
      }
    }

    this._animateRow(startOffsetX, toValue, velocityX / friction);
  };

  _animateRow = (fromValue, toValue, velocityX) => {
    const { dragX, rowTranslation } = this.state;
    dragX.setValue(0);
    rowTranslation.setValue(fromValue);

    this.setState({ rowState: Math.sign(toValue) });
    Animated.spring(rowTranslation, {
      restSpeedThreshold: 1.7,
      restDisplacementThreshold: 0.4,
      velocity: velocityX,
      bounciness: 0,
      toValue,
      useNativeDriver: this.props.useNativeAnimations,
      ...this.props.animationOptions,
    }).start(({ finished }) => {
      if (finished) {
        if (toValue > 0 && this.props.onSwipeableLeftOpen) {
          this.props.onSwipeableLeftOpen();
        } else if (toValue < 0 && this.props.onSwipeableRightOpen) {
          this.props.onSwipeableRightOpen();
        }

        if (toValue === 0) {
          this.props.onSwipeableClose && this.props.onSwipeableClose();
        } else {
          this.props.onSwipeableOpen && this.props.onSwipeableOpen();
        }
      }
    });
    if (toValue > 0 && this.props.onSwipeableLeftWillOpen) {
      this.props.onSwipeableLeftWillOpen();
    } else if (toValue < 0 && this.props.onSwipeableRightWillOpen) {
      this.props.onSwipeableRightWillOpen();
    }

    if (toValue === 0) {
      this.props.onSwipeableWillClose && this.props.onSwipeableWillClose();
    } else {
      this.props.onSwipeableWillOpen && this.props.onSwipeableWillOpen();
    }
  };

  _onRowLayout = ({ nativeEvent }) => {
    this.setState({ rowWidth: nativeEvent.layout.width });
  };

  _currentOffset = () => {
    const { leftWidth = 0, rowWidth = 0, rowState } = this.state;
    const { rightOffset = rowWidth } = this.state;
    const rightWidth = rowWidth - rightOffset;
    if (rowState === 1) {
      return leftWidth;
    } else if (rowState === -1) {
      return -rightWidth;
    }
    return 0;
  };

  close = () => {
    this._animateRow(this._currentOffset(), 0);
  };

  openLeft = () => {
    const { leftWidth = 0 } = this.state;
    this._animateRow(this._currentOffset(), leftWidth);
  };

  openRight = () => {
    const { rowWidth = 0 } = this.state;
    const { rightOffset = rowWidth } = this.state;
    const rightWidth = rowWidth - rightOffset;
    this._animateRow(this._currentOffset(), -rightWidth);
  };

  render() {
    const { rowState } = this.state;
    const { children, renderLeftActions, renderRightActions } = this.props;

    const left = renderLeftActions && (
      <Animated.View
        style={[
          styles.leftActions,
          { transform: [{ translateX: this._leftActionTranslate }] },
        ]}>
        {renderLeftActions(this._showLeftAction, this._transX)}
        <View
          onLayout={({ nativeEvent }) =>
            this.setState({ leftWidth: nativeEvent.layout.x })
          }
        />
      </Animated.View>
    );

    const right = renderRightActions && (
      <Animated.View
        style={[
          styles.rightActions,
          { transform: [{ translateX: this._rightActionTranslate }] },
        ]}>
        {renderRightActions(this._showRightAction, this._transX)}
        <View
          onLayout={({ nativeEvent }) =>
            this.setState({ rightOffset: nativeEvent.layout.x })
          }
        />
      </Animated.View>
    );

    return (
      <PanGestureHandler
        {...this.props}
        minDeltaX={10}
        onGestureEvent={this._onGestureEvent}
        onHandlerStateChange={this._onHandlerStateChange}>
        <Animated.View onLayout={this._onRowLayout} style={[styles.container, this.props.containerStyle]}>
          {left}
          {right}
          <TapGestureHandler
            enabled={rowState !== 0}
            onHandlerStateChange={this._onTapHandlerStateChange}>
            <Animated.View
              pointerEvents={rowState === 0 ? 'auto' : 'box-only'}
              style={[
                {
                  transform: [{ translateX: this._transX }],
                },
                this.props.childrenContainerStyle
              ]}>
              {children}
            </Animated.View>
          </TapGestureHandler>
        </Animated.View>
      </PanGestureHandler>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    overflow: 'hidden',
  },
  leftActions: {
    ...StyleSheet.absoluteFillObject,
    flexDirection: 'row',
  },
  rightActions: {
    ...StyleSheet.absoluteFillObject,
    flexDirection: 'row-reverse',
  },
});
<|endoftext|>


<commit_before>module.exports = {
	options: {
		bump: true, //this is set to true by the rel task on the first run
		changelog: true, //default: false
		// Gets set by grunt-shared-config
		changelogText: '', //default: '### <%= version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n'
		// file: 'component.json', //default: package.json
		// add: false, //default: true
		// commit: false, //default: true
		// tag: false, //default: true
		// push: false, //default: true
		// pushTags: false, //default: true
		// npm: false, //default: true
		// npmtag: true, //default: no tag
		// indentation: '\t', //default: '  ' (two spaces)
		// folder: 'folder/to/publish/to/npm', //default project root
		tagName: 'v<%= version %>', //default: '<%= version %>'
		commitMessage: '-- v<%= version %> RELEASE --', //default: 'release <%= version %>'
		// tagMessage: 'tagging version <%= version %>', //default: 'Version <%= version %>',
		// beforeBump: [], // optional grunt tasks to run before file versions are bumped
		// afterBump: [], // optional grunt tasks to run after file versions are bumped
		// beforeRelease: [], // optional grunt tasks to run after release version is bumped up but before release is packaged
		// afterRelease: [], // optional grunt tasks to run after release is packaged
		// github: {
		// 	repo: 'geddski/grunt-release', //put your user/repo here
		// 	usernameVar: 'GITHUB_USERNAME', //ENVIRONMENT VARIABLE that contains Github username
		// 	passwordVar: 'GITHUB_PASSWORD' //ENVIRONMENT VARIABLE that contains Github password
		// }
	}
};
<commit_msg>Fix for rel bumping twice when major|minor|patch specified<commit_after>module.exports = {
	options: {
		bump: false, //this is set to true by the rel task on the first run
		changelog: true, //default: false
		// Gets set by grunt-shared-config
		changelogText: '', //default: '### <%= version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n'
		// file: 'component.json', //default: package.json
		// add: false, //default: true
		// commit: false, //default: true
		// tag: false, //default: true
		// push: false, //default: true
		// pushTags: false, //default: true
		// npm: false, //default: true
		// npmtag: true, //default: no tag
		// indentation: '\t', //default: '  ' (two spaces)
		// folder: 'folder/to/publish/to/npm', //default project root
		tagName: 'v<%= version %>', //default: '<%= version %>'
		commitMessage: '-- v<%= version %> RELEASE --', //default: 'release <%= version %>'
		// tagMessage: 'tagging version <%= version %>', //default: 'Version <%= version %>',
		// beforeBump: [], // optional grunt tasks to run before file versions are bumped
		// afterBump: [], // optional grunt tasks to run after file versions are bumped
		// beforeRelease: [], // optional grunt tasks to run after release version is bumped up but before release is packaged
		// afterRelease: [], // optional grunt tasks to run after release is packaged
		// github: {
		// 	repo: 'geddski/grunt-release', //put your user/repo here
		// 	usernameVar: 'GITHUB_USERNAME', //ENVIRONMENT VARIABLE that contains Github username
		// 	passwordVar: 'GITHUB_PASSWORD' //ENVIRONMENT VARIABLE that contains Github password
		// }
	}
};
<|endoftext|>


<commit_before>var
	SIGN_SUCCESS = String.fromCharCode(0x2714).green,
	SIGN_FAILED = String.fromCharCode(0x2613).red
;

module.exports = function(grunt) {
	grunt.config('qunit', {
		all: {
			options: {
				timeout: 10000,
				console: true,
				urls: ['http://localhost:9000/.grunt/testrunner.html']
			}
		}
	});

	var
		report = '',
		cases = 0
	;

	function addReport(message) {
		report += '\n' + message;
	}

	grunt.event.on('qunit.spawn', function (url) {
		addReport('Running test: '.green + url);
	});

	grunt.event.on('qunit.moduleStart', function (name) {
		addReport(name.green);
	});

	grunt.event.on('qunit.testDone', function (name, failed, passed, total) {
		addReport(
			'    ' +
			(failed === 0 ? SIGN_SUCCESS : SIGN_FAILED) + ' ' +
			name + ' (' + total + ')'
		);
		cases++;
	});

	grunt.event.on('qunit.done', function(failed, passed, total, runtime) {
		grunt.log.writeln(report);
		report = '';

		grunt.log.writeln((
			' Ran '.gray +
			cases.toString().bold.blue +
			' in '.gray +
			runtime.toString().bold.blue +
			'ms '.bold.blue
		).bgWhite);
	});

	grunt.loadNpmTasks('grunt-contrib-qunit');
};
<commit_msg>Update error reporting in qunit tests<commit_after>var
	SIGN_SUCCESS = String.fromCharCode(0x2714).green,
	SIGN_FAILED = String.fromCharCode(0x2613).red
;

module.exports = function(grunt) {
	grunt.config('qunit', {
		all: {
			options: {
				timeout: 10000,
				console: true,
				urls: ['http://localhost:9000/.grunt/testrunner.html']
			}
		}
	});

	var
		report = '',
		cases = 0
	;

	function addReport(message) {
		report += '\n' + message;
	}

	grunt.event.on('qunit.spawn', function (url) {
		addReport('Running test: '.green + url);
	});

	grunt.event.on('qunit.moduleStart', function (name) {
		addReport(name.green);
	});

	grunt.event.on('qunit.testDone', function (name, failed, passed, total) {
		addReport(
			'    ' +
			(failed === 0 ? SIGN_SUCCESS : SIGN_FAILED) + ' ' +
			name + ' (' + total + ')'
		);
		cases++;
	});

	grunt.event.on('qunit.done', function(failed, passed, total, runtime) {
		grunt.log.writeln(report);
		report = '';

		grunt.log.writeln((
			' Ran '.gray +
			cases.toString().bold.blue +
			' in '.gray +
			runtime.toString().bold.blue +
			'ms '.bold.blue
		).bgWhite);
	});

	grunt.event.on('qunit.fail.load', function(url) {
		grunt.log.writeln();
		grunt.log.writeln('Failed to load: ' + url.yellow);
	});

	grunt.event.on('qunit.error.onError', function(message, stackTrace) {
		grunt.log.writeln();
		grunt.log.errorlns(message);
		(stackTrace || []).forEach(function(trace) {
			grunt.log.error(
				'"' + trace.function +
				'" in ' + trace.file +
				':' + trace.line
			);
		});
	});

	grunt.loadNpmTasks('grunt-contrib-qunit');
};
<|endoftext|>


<commit_before>'use strict';

var nconf = require('nconf');

nconf.use('memory')
  .env({
  separator: '__',
  whitelist: [
    'API_TOKEN',
    'CACHE__REDIS__DISABLED',
    'CACHE__REDIS__HOST',
    'CACHE__DDB__DISABLED',
    'CACHE__DDB__TABLE',
    'DRUNK',
    'FILE__MAX_SIZE',
    'PROC_PER_CORE',
    'REDIS_HOST',
    'UTOPIA',
  ],
}).defaults({
  // Max file size allowed
  FILE: {
    MAX_SIZE: 33554432
  },
  // Concurrent scanners (sav) allowed per core
  PROC_PER_CORE: 2,
});

function bridge_deprecated_to(was, now) {
  var old_value = nconf.get(was);
  if (old_value && !nconf.get(now)) {
    nconf.set(now, old_value);
    console.log(`[DEPRECATED] ${was} has been deprecated, use ${now} instead`);
  }
}

function update_boolean(name) {
  var value = nconf.get(name);
  var as_number = Number(value);

  if (Number.isNaN(as_number)) {
    return nconf.set(name, Boolean(value));
  }

  nconf.set(name, Boolean(as_number));
}

function update_number(name) {
  var value = nconf.get(name);
  var as_number = Number(value);

  if (!Number.isNaN(as_number)) {
    nconf.set(name, as_number);
  }
}

bridge_deprecated_to('REDIS_HOST', 'CACHE:REDIS:HOST');
bridge_deprecated_to('UTOPIA', 'DRUNK');

update_number('PROC_PER_CORE');
update_boolean('DRUNK');

module.exports = nconf;
<commit_msg>feat: make 'false' false again<commit_after>'use strict';

var nconf = require('nconf');

nconf.use('memory')
  .env({
  separator: '__',
  whitelist: [
    'API_TOKEN',
    'CACHE__REDIS__DISABLED',
    'CACHE__REDIS__HOST',
    'CACHE__DDB__DISABLED',
    'CACHE__DDB__TABLE',
    'DRUNK',
    'FILE__MAX_SIZE',
    'PROC_PER_CORE',
    'REDIS_HOST',
    'UTOPIA',
  ],
}).defaults({
  // Max file size allowed
  FILE: {
    MAX_SIZE: 33554432
  },
  // Concurrent scanners (sav) allowed per core
  PROC_PER_CORE: 2,
});

function bridge_deprecated_to(was, now) {
  var old_value = nconf.get(was);
  if (old_value && !nconf.get(now)) {
    nconf.set(now, old_value);
    console.log(`[DEPRECATED] ${was} has been deprecated, use ${now} instead`);
  }
}

function update_boolean(name) {
  var value = nconf.get(name);
  var as_number = Number(value);

  if (Number.isNaN(as_number)) {
    // Great readability
    return nconf.set(name, !/^false$/i.test(value));
  }

  nconf.set(name, Boolean(as_number));
}

function update_number(name) {
  var value = nconf.get(name);
  var as_number = Number(value);

  if (!Number.isNaN(as_number)) {
    nconf.set(name, as_number);
  }
}

bridge_deprecated_to('REDIS_HOST', 'CACHE:REDIS:HOST');
bridge_deprecated_to('UTOPIA', 'DRUNK');

update_number('PROC_PER_CORE');
update_boolean('DRUNK');

module.exports = nconf;
<|endoftext|>