<commit_before>window.describe = parent.describe;
window.it = parent.it;
window.xit = parent.xit;<commit_msg>Added missing mocha functions for framework<commit_after>// BDD or QUnit
window.before = parent.before;
window.after = parent.after;
window.beforeEach = parent.beforeEach;
window.afterEach = parent.afterEach;
window.run = parent.run;

// BDD
window.describe = parent.describe;
window.it = parent.it;
window.xit = parent.xit;

// QUnit
window.suite = parent.suite;
window.test = parent.test;

// TDD
window.setup = parent.setup;
window.teardown = parent.teardown;
window.suiteSetup = parent.suiteSetup;
window.suiteTeardown = parent.suiteTeardown;<|endoftext|>


<commit_before>/*Copyright (C) 2014 Aymeric Vitte

	With modifications of https://github.com/mafintosh/torrent-stream:

	Copyright (C) 2014 Mathias Buus Madsen <mathiasbuus@gmail.com>

	Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

	And

	https://github.com/feross/bittorrent-dht

	Copyright (c) 2013 Feross Aboukhadijeh, Mathias Buus, and other contributors

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to
	use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
	the Software, and to permit persons to whom the Software is furnished to do so,
	subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
	FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
	COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
	IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
	CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var bittorrent=require('./index.js'),
	dht=require('./node_modules/bittorrent-dht/index.js'),
	blocklist=require('./lib/blocklist.js'),
	fs=require('fs'),
	oconsole=console.log.bind(console),
	torrent,
	ini_dht,
	spiesfile,
	Arrayblocklist=[],
	blocked,
	knownspies=[],
	closest,
	spies,
	magnet,
	path,
	findspies,
	findspiesonly,
	MB=1048576,
	SPEED=100000,
	START,
	RETRY=10*1000,
	MERGE=5*60*1000,
	nbspy=0,
	myPort,
	infohash,
	fakeinfohash,
	NB_FILES,
	T0;

/*
Use:
node freerider.js [infohash]
node freerider.js [magnet]
node freerider.js [magnet] [path]

node freerider.js [infohash] findspies
node freerider.js [magnet] findspies
node freerider.js [magnet] [path] findspies

node freerider.js [infohash] findspiesonly
node freerider.js [magnet] findspiesonly
node freerider.js [magnet] [path] findspiesonly
*/

process.on('uncaughtException',function (err) {
	oconsole(err.stack);
	try {
		var fd=fs.openSync('/torrent.txt','a');
		fs.writeSync(fd,(new Date().toDateString())+' '+(new Date().toTimeString()));
		fs.writeSync(fd,err.stack);
		fs.closeSync(fd);
	} catch(ee) {}
});

if (process.argv) {
	if (process.argv.length>1) {
		var args=process.argv.splice(2);
		if (args.length) {
			if (args.length>1) {
				magnet=args[0];
				if (args[1]==='findspies') {
					findspies=true;
				} else if (args[1]==='findspiesonly') {
					findspiesonly=true;
					findspies=true;
				} else {
					path=args[1];
				};
				if (args.length>2) {
					if (args[2]==='findspies') {
						findspies=true;
					} else if (args[2]==='findspiesonly') {
						findspiesonly=true;
						findspies=true;
					};
				};
			} else {
				magnet=args[0]
			};
		};
	};
};

path=path||'./store';

magnet=magnet||'magnet:?xt=urn:btih:ef330b39f4801d25b4245212e75a38634bfc856e';

if (magnet.length<60) {
	magnet='magnet:?xt=urn:btih:'+magnet;
};

if (magnet.length>60) {
	console.log('Wrong magnet format, please enter it as: ef330b39f4801d25b4245212e75a38634bfc856e or magnet:?xt=urn:btih:ef330b39f4801d25b4245212e75a38634bfc856e');
	process.exit();
};

console.log(path+' '+magnet);

infohash=magnet.split(':btih:')[1];

spiesfile='spies-'+infohash+'.txt';

fs.open('log-'+infohash+'.txt','w',function(err,fd) {
	console.log=function(txt,user) {
		if (user) {
			try {
				oconsole(txt);
			} catch (ee) {}
		};
		txt +=' '+(new Date().toDateString())+' '+(new Date().toTimeString());
		fs.write(fd,txt+'\n',function() {});
	};
});

var writefile=function(file,txt) {
	fs.open(file,'a',function(err,fd) {
		if (!err) {
			fs.write(fd,txt,function(){});
			fs.close(fd);
		} else {
			console.log('error writefile '+file,true);
			setTimeout(function() {writefile(file,txt)},1000);
		};
	});
};

var onsettorrent=function() {
	if (findspies) {
		if (ini_dht.closest_from_infohash) {
			console.log('settorrent dht ready',true);
			ini_dht.removeListener('peer',onpeer);
			ini_dht.on('peer',function(addr) {torrent.discovery.emit('peer', addr)});
			var closest_nodes=ini_dht.closest_from_infohash;
			closest_nodes.forEach(function (contact) {
				var addr=contact.addr;
				var ip=addr.split(':')[0];
				if (!blocked.contains(ip)) {
					console.log('sendgetpeer to '+addr+' for '+infohash,true);
					ini_dht._sendGetPeers(addr,infohash,function() {});
				} else {
					console.log('blocked sendgetpeer to known spy '+addr,true);
				};
			});
		} else {
			console.log('settorrent dht not ready, retry later',true);
			setTimeout(onsettorrent,RETRY);
		};
	};
};

var onready=function() {
	console.log('torrent ready',true);
	T0=Date.now();
	NB_FILES=torrent.files.length;
	torrent.files.forEach(function(file) {
		console.log('filename: '+file.name,true);
		var stream = file.createReadStream();
		var inc=0;
		var t0=Date.now();
		var l=file.length;
		var name=file.name;
		var speed,time,nbwires,onbwires;
		stream.on('data',function(chunk) {
			var nb_peer_left=0;
			inc +=chunk.length;
			speed=inc*8/(Date.now()-T0);
			time=((l-inc)*8/(speed*1000*3600)).toFixed(2).split('.');
			time[1]=parseInt(time[1]*60/100);
			time[1]=(time[1]<10)?('0'+time[1]):time[1];
			torrent.swarm._queues.forEach(function(val) {nb_peer_left +=val.length});
			console.log('got for torrent '+name+' size '+(l/MB).toFixed(2)+' MB '+chunk.length+' bytes of data - offset '+stream._piece+' - remaining '+((l-inc)/MB).toFixed(2)+' MB - speed: '+((speed<SPEED)?(parseInt(speed)+' kbps - time left: '+time[0]+'h'+time[1]):'already downloaded')+' - nb peers: '+torrent.swarm.wires.length+' - other peers '+nb_peer_left,true);
			t0=Date.now();
			if (l===inc) {
				console.log('Download finished for '+name,true);
				NB_FILES--;
				if (!NB_FILES) {
					torrent.destroy();
					process.exit();
				};
			};
		});
		var peers=function() {
			nbwires=torrent.swarm.wires.length;
			if (nbwires!==onbwires) {
				console.log('--- Peers in swarn:',true)
				torrent.swarm.wires.forEach(function(wire) {
					console.log(wire.peerAddress,true);
					if (!wire.block_timeout) {
						wire.block_timeout=true;
						wire.on('timeout',function() {
							torrent.swarm._remove(wire.peerAddress);
							console.log('request timeout, add as spy');
							onpeer(wire.peerAddress);
						});
					};
				});
			};
			onbwires=nbwires;
		};
		setInterval(peers,10000);
	});
};

var onpeer=function(addr) {
	var ip=addr.split(':')[0];
	if (!blocked.contains(ip)) {
		console.log('new spy '+addr,true);
		nbspy++;
		blocked.add(ip);
		Arrayblocklist.push(ip);
		writefile(spiesfile,'"'+ip+'",');
		if (torrent) {
			torrent.blocked=blocked;
		};
	} else {
		if (knownspies.indexOf(ip)===-1) {
			knownspies.push(ip);
		};
		console.log('already known spy - total of known spies encountered :'+knownspies.length+' of '+Arrayblocklist.length,true);
	};
};

var createDHT=function(infoHash,opts) {
	var table=dht(opts);
	var nodeId=table.nodeId.toString('hex');
	table.on('peer',onpeer);
	table.on('ready',function() {
		console.log('dht ready - starting lookup for infohash '+infoHash+' '+(new Date().toTimeString())+' - new spies found: '+nbspy,true);
		table.lookup(infoHash);
	});
	table.on('closest',function() {
		console.log('announcing',true);
		closest=table.closest_from_infohash;
		if ((closest.length)&&(!ini_dht)) {
			ini_dht=table;
		};
		closest.forEach(function (contact) {
			var addr=contact.addr;
			var addrData=table._getAddrData(addr);
			var token=table._generateToken(addrData[0]);
			table._sendAnnouncePeer(addr,fakeinfohash,myPort,token,function(err,res) {});
		});
		if (table!==ini_dht) {
			table.destroy();
		};
		createDHT(infoHash,{debug:false,freerider:false,debug:true});
	});
};

var start_torrent=function(blocklist) {
	console.log('-------------- start torrent --------------------',true);
	torrent=bittorrent(magnet,{blocklist:blocklist||null,connections:20,path:path,verify:true,debug:false,freerider:true,dht:ini_dht});
	torrent.on('setTorrent',onsettorrent);
	torrent.on('ready',onready);
	torrent.on('blocking',onpeer);
};

var start=function() {
	if (findspies) {
		var last=infohash.slice(infohash.length-1);
		myPort=parseInt(Math.random()*10000+35000);
		last=parseInt(last,16)^1;
		last=last.toString(16);
		var fake='magnet:?xt=urn:btih:'+infohash.substr(0,infohash.length-1)+last;
		fakeinfohash=fake.split(':btih:')[1];
		console.log('fake infohash '+fake,true);
		createDHT(fakeinfohash,{debug:false,freerider:false});
		if (!findspiesonly) {
			setTimeout(function() {start_torrent(Arrayblocklist)},START);
		};
	} else {
		start_torrent(Arrayblocklist);
	};
};

var merge=function(filename) {
	console.log('merging spies');
	var tmp='';
	var sp='';
	var files=fs.readdirSync('.');
	files.forEach(function(file) {
		if (file.indexOf('spies-')!==-1) {
			console.log('adding '+file);
			tmp +=fs.readFileSync(file).toString('utf8');
		};
		if (file===filename) {
			fs.unlink(file);
		};
	});
	if (tmp.length) {
		sp=fs.readFileSync('spies.txt').toString('utf8');
		console.log('Number of spies in spies.txt:'+JSON.parse('['+sp.slice(0,sp.length-1).toString('utf8')+']').length);
		sp +=tmp;
	};
	return sp;
};

//modification of http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/
var unique=function() {
	var o={};
	var l=this.length;
	var r=[];
	this.forEach(function(val,i) {o[val]=i});
	for(var i in o) {
		r.push(i);
	};
	o=null;
	return r;
};

var update_spies=function(spies) {
	var tmp=JSON.parse('['+spies.slice(0,spies.length-1).toString('utf8')+']');
	console.log('Number of known spies before cleaning: '+tmp.length);
	Arrayblocklist=unique.call(tmp);
	tmp=JSON.stringify(Arrayblocklist);
	console.log('end cleaning');
	fs.writeFileSync('./spies.txt',tmp.substr(1,tmp.length-2)+',');
	blocked=blocklist(Arrayblocklist);
	console.log('Number of known spies after cleaning: '+Arrayblocklist.length);
};

var spies=merge(spiesfile);

if (spies) {
	update_spies(spies);
} else {
	blocked=blocklist([]);
};

START=Arrayblocklist.length?(30*1000):(5*60*1000);

start();<commit_msg>modif findspies<commit_after>/*Copyright (C) 2014 Aymeric Vitte

	With modifications of https://github.com/mafintosh/torrent-stream:

	Copyright (C) 2014 Mathias Buus Madsen <mathiasbuus@gmail.com>

	Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

	And

	https://github.com/feross/bittorrent-dht

	Copyright (c) 2013 Feross Aboukhadijeh, Mathias Buus, and other contributors

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to
	use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
	the Software, and to permit persons to whom the Software is furnished to do so,
	subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
	FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
	COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
	IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
	CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var bittorrent=require('./index.js'),
	dht=require('./node_modules/bittorrent-dht/index.js'),
	blocklist=require('./lib/blocklist.js'),
	fs=require('fs'),
	oconsole=console.log.bind(console),
	torrent,
	ini_dht,
	spiesfile,
	Arrayblocklist=[],
	blocked,
	knownspies=[],
	closest,
	spies,
	magnet,
	path,
	findspies,
	findspiesonly,
	MB=1048576,
	SPEED=100000,
	START,
	RETRY=10*1000,
	MERGE=5*60*1000,
	nbspy=0,
	myPort,
	infohash,
	fakeinfohash,
	NB_FILES,
	T0;

/*
Use:
node freerider.js [infohash]
node freerider.js [magnet]
node freerider.js [magnet] [path]

node freerider.js [infohash] findspies
node freerider.js [magnet] findspies
node freerider.js [magnet] [path] findspies

node freerider.js [infohash] findspiesonly
node freerider.js [magnet] findspiesonly
node freerider.js [magnet] [path] findspiesonly
*/

process.on('uncaughtException',function (err) {
	oconsole(err.stack);
	try {
		var fd=fs.openSync('/torrent.txt','a');
		fs.writeSync(fd,(new Date().toDateString())+' '+(new Date().toTimeString()));
		fs.writeSync(fd,err.stack);
		fs.closeSync(fd);
	} catch(ee) {}
});

if (process.argv) {
	if (process.argv.length>1) {
		var args=process.argv.splice(2);
		if (args.length) {
			if (args.length>1) {
				magnet=args[0];
				if (args[1]==='findspies') {
					findspies=true;
				} else if (args[1]==='findspiesonly') {
					findspiesonly=true;
					findspies=true;
				} else {
					path=args[1];
				};
				if (args.length>2) {
					if (args[2]==='findspies') {
						findspies=true;
					} else if (args[2]==='findspiesonly') {
						findspiesonly=true;
						findspies=true;
					};
				};
			} else {
				magnet=args[0]
			};
		};
	};
};

path=path||'./store';

magnet=magnet||'magnet:?xt=urn:btih:ef330b39f4801d25b4245212e75a38634bfc856e';

if (magnet.length<60) {
	magnet='magnet:?xt=urn:btih:'+magnet;
};

if (magnet.length>60) {
	console.log('Wrong magnet format, please enter it as: ef330b39f4801d25b4245212e75a38634bfc856e or magnet:?xt=urn:btih:ef330b39f4801d25b4245212e75a38634bfc856e');
	process.exit();
};

console.log(path+' '+magnet);

infohash=magnet.split(':btih:')[1];

spiesfile='spies-'+infohash+'.txt';

fs.open('log-'+infohash+'.txt','w',function(err,fd) {
	console.log=function(txt,user) {
		if (user) {
			try {
				oconsole(txt);
			} catch (ee) {}
		};
		txt +=' '+(new Date().toDateString())+' '+(new Date().toTimeString());
		fs.write(fd,txt+'\n',function() {});
	};
});

var writefile=function(file,txt) {
	fs.open(file,'a',function(err,fd) {
		if (!err) {
			fs.write(fd,txt,function(){});
			fs.close(fd);
		} else {
			console.log('error writefile '+file,true);
			setTimeout(function() {writefile(file,txt)},1000);
		};
	});
};

var onsettorrent=function() {
	if (findspies) {
		if (ini_dht.closest_from_infohash) {
			console.log('settorrent dht ready',true);
			ini_dht.removeListener('peer',onpeer);
			ini_dht.on('peer',function(addr) {torrent.discovery.emit('peer', addr)});
			var closest_nodes=ini_dht.closest_from_infohash;
			closest_nodes.forEach(function (contact) {
				var addr=contact.addr;
				var ip=addr.split(':')[0];
				if (!blocked.contains(ip)) {
					console.log('sendgetpeer to '+addr+' for '+infohash,true);
					ini_dht._sendGetPeers(addr,infohash,function() {});
				} else {
					console.log('blocked sendgetpeer to known spy '+addr,true);
				};
			});
		} else {
			console.log('settorrent dht not ready, retry later',true);
			setTimeout(onsettorrent,RETRY);
		};
	};
};

var onready=function() {
	console.log('torrent ready',true);
	T0=Date.now();
	NB_FILES=torrent.files.length;
	torrent.files.forEach(function(file) {
		console.log('filename: '+file.name,true);
		var stream = file.createReadStream();
		var inc=0;
		var t0=Date.now();
		var l=file.length;
		var name=file.name;
		var speed,time,nbwires,onbwires;
		stream.on('data',function(chunk) {
			var nb_peer_left=0;
			inc +=chunk.length;
			speed=inc*8/(Date.now()-T0);
			time=((l-inc)*8/(speed*1000*3600)).toFixed(2).split('.');
			time[1]=parseInt(time[1]*60/100);
			time[1]=(time[1]<10)?('0'+time[1]):time[1];
			torrent.swarm._queues.forEach(function(val) {nb_peer_left +=val.length});
			console.log('got for torrent '+name+' size '+(l/MB).toFixed(2)+' MB '+chunk.length+' bytes of data - offset '+stream._piece+' - remaining '+((l-inc)/MB).toFixed(2)+' MB - speed: '+((speed<SPEED)?(parseInt(speed)+' kbps - time left: '+time[0]+'h'+time[1]):'already downloaded')+' - nb peers: '+torrent.swarm.wires.length+' - other peers '+nb_peer_left,true);
			t0=Date.now();
			if (l===inc) {
				console.log('Download finished for '+name,true);
				NB_FILES--;
				if (!NB_FILES) {
					torrent.destroy();
					process.exit();
				};
			};
		});
		var peers=function() {
			nbwires=torrent.swarm.wires.length;
			if (nbwires!==onbwires) {
				console.log('--- Peers in swarn:',true)
				torrent.swarm.wires.forEach(function(wire) {
					console.log(wire.peerAddress,true);
					if (!wire.block_timeout) {
						wire.block_timeout=true;
						wire.on('timeout',function() {
							torrent.swarm._remove(wire.peerAddress);
							console.log('request timeout, add as spy');
							onpeer(wire.peerAddress);
						});
					};
				});
			};
			onbwires=nbwires;
		};
		setInterval(peers,10000);
	});
};

var onpeer=function(addr) {
	var ip=addr.split(':')[0];
	if (!blocked.contains(ip)) {
		console.log('new spy '+addr,true);
		nbspy++;
		blocked.add(ip);
		Arrayblocklist.push(ip);
		writefile(spiesfile,'"'+ip+'",');
		if (torrent) {
			torrent.blocked=blocked;
		};
	} else {
		if (knownspies.indexOf(ip)===-1) {
			knownspies.push(ip);
		};
		console.log('already known spy - total of known spies encountered :'+knownspies.length+' of '+Arrayblocklist.length,true);
	};
};

var createDHT=function(infoHash,opts) {
	var table=dht(opts);
	var nodeId=table.nodeId.toString('hex');
	table.on('peer',onpeer);
	table.on('ready',function() {
		console.log('dht ready - starting lookup for infohash '+infoHash+' '+(new Date().toTimeString())+' - new spies found: '+nbspy,true);
		table.lookup(infoHash);
	});
	table.on('closest',function() {
		console.log('announcing',true);
		closest=table.closest_from_infohash;
		if ((closest.length)&&(!ini_dht)) {
			ini_dht=table;
		};
		closest.forEach(function (contact) {
			var addr=contact.addr;
			var addrData=table._getAddrData(addr);
			var token=table._generateToken(addrData[0]);
			table._sendAnnouncePeer(addr,fakeinfohash,myPort,token,function(err,res) {});
		});
		if (table!==ini_dht) {
			table.destroy();
		};
		createDHT(infoHash,{debug:false,freerider:false,debug:true});
	});
};

var start_torrent=function(blocklist) {
	console.log('-------------- start torrent --------------------',true);
	torrent=bittorrent(magnet,{blocklist:blocklist||null,connections:20,path:path,verify:true,debug:false,freerider:true,dht:ini_dht});
	torrent.on('setTorrent',onsettorrent);
	torrent.on('ready',onready);
	torrent.on('blocking',onpeer);
};

var start=function() {
	if (findspies) {
		var last=infohash.slice(infohash.length-1);
		myPort=parseInt(Math.random()*10000+35000);
		last=parseInt(last,16)^1;
		last=last.toString(16);
		var fake='magnet:?xt=urn:btih:'+infohash.substr(0,infohash.length-1)+last;
		fakeinfohash=fake.split(':btih:')[1];
		console.log('fake infohash '+fake,true);
		createDHT(fakeinfohash,{debug:false,freerider:false});
		if (!findspiesonly) {
			setTimeout(function() {start_torrent(Arrayblocklist)},START);
		};
	} else {
		start_torrent(Arrayblocklist);
	};
};

var merge=function(filename) {
	console.log('merging spies');
	var tmp='';
	var sp='';
	var files=fs.readdirSync('.');
	files.forEach(function(file) {
		if (file.indexOf('spies-')!==-1) {
			console.log('adding '+file);
			tmp +=fs.readFileSync(file).toString('utf8');
		};
		if (file===filename) {
			fs.unlink(file);
		};
	});
	sp=fs.readFileSync('spies.txt').toString('utf8');
	console.log('Number of spies in spies.txt:'+JSON.parse('['+sp.slice(0,sp.length-1).toString('utf8')+']').length);
	return sp;
};

//modification of http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/
var unique=function() {
	var o={};
	var l=this.length;
	var r=[];
	this.forEach(function(val,i) {o[val]=i});
	for(var i in o) {
		r.push(i);
	};
	o=null;
	return r;
};

var update_spies=function(spies) {
	var tmp=JSON.parse('['+spies.slice(0,spies.length-1).toString('utf8')+']');
	console.log('Number of known spies before cleaning: '+tmp.length);
	Arrayblocklist=unique.call(tmp);
	tmp=JSON.stringify(Arrayblocklist);
	console.log('end cleaning');
	fs.writeFileSync('./spies.txt',tmp.substr(1,tmp.length-2)+',');
	blocked=blocklist(Arrayblocklist);
	console.log('Number of known spies after cleaning: '+Arrayblocklist.length);
};

var spies=merge(spiesfile);

if (spies) {
	update_spies(spies);
} else {
	blocked=blocklist([]);
};

START=Arrayblocklist.length?(30*1000):(5*60*1000);

start();<|endoftext|>


<commit_before>define(module, function(exports, require) {

  var slice = Array.prototype.slice;
  var fs = require('fs');
  var os = require('os');
  var util = require('util');
  var path = require('path');
  var qp = require('qp-utility');

  function make_directory(directory, mode) {
    directory = path.resolve(directory);
    if (typeof mode === 'undefined') {
      mode = 0777 & (~process.umask());
    }
    try {
      if (!fs.statSync(directory).isDirectory()) {
        throw new Error(directory + ' exists and is not a directory');
      }
    } catch (e) {
      if (e.code === 'ENOENT') {
        make_directory(path.dirname(directory), mode);
        fs.mkdirSync(directory, mode);
      } else {
        throw e;
      }
    }
  }

  function delete_directory(directory, options) {
    options = options || {};
    if (fs.existsSync(directory)) {
      fs.readdirSync(directory).forEach(function(node) {
        var item = path.join(directory, node);
        if (fs.lstatSync(item).isDirectory()) {
          delete_directory(item);
        } else {
          fs.unlinkSync(item);
        }
      });
      if (!options.children) {
        fs.rmdirSync(directory);
      }
    }
  }

  function delete_files(directory) {
    if (fs.existsSync(directory)) {
      fs.readdirSync(directory).forEach(function(node) {
        var item = path.join(directory, node);
        if (fs.lstatSync(item).isFile()) {
          fs.unlinkSync(item);
        }
      });
    }
  }

  function delete_file(file) {
    if (fs.existsSync(file) && fs.lstatSync(file).isFile()) {
      fs.unlinkSync(file);
    }
  }

  qp.module(exports, 'qp-library/fss', {

    stat: function() {
      return fs.statSync(path.join.apply(null, arguments));
    },

    touch: function() {
      var file = path.join.apply(null, arguments);
      if (this.exists(file)) {
        var time = (new Date()).getTime();
        fs.utimesSync(file, time, time);
      } else {
        this.write(file, '');
      }
    },

    modify: function() {
      var file = path.join.apply(null, arguments);
      if (this.exists(file)) {
        var stat = fs.statSync(file);
        var time = (new Date()).getTime();
        fs.utimesSync(file, stat.atime, time);
      } else {
        this.write(file, '');
      }
    },

    exists: function() {
      return fs.existsSync(path.join.apply(null, arguments));
    },

    make_directory: make_directory,
    delete_directory: delete_directory,
    delete_file: delete_file,
    delete_files: delete_files,

    read: function() {
      var data = null;
      try {
        data = fs.readFileSync(path.join.apply(null, arguments), 'utf8');
      } catch(e) { throw e; }
      return data;
    },

    write: function() {
      var args = slice.call(arguments);
      var data = args.pop();
      var file = path.join.apply(null, args);
      var filepath = path.dirname(file);
      try {
        if (!fs.existsSync(filepath)) {
          make_directory(filepath);
        }
        fs.writeFileSync(file, data);
      } catch (e) { throw e; }
      return file;
    },

    clear: function() {
      var args = slice.call(arguments);
      args.push('');
      return this.write.apply(this, args);
    },

    copy: function(source, target) {
      try {
        var data = fs.readFileSync(source);
        var target_dir = path.dirname(target);
        if (!fs.existsSync(target_dir)) {
          make_directory(target_dir);
        }
        fs.writeFileSync(target, data);
        return data.length;
      } catch (e) {
        return null;
      }
    },

    rename: function(file, name) {
      try {
        fs.renameSync(file, path.join(path.dirname(file), name));
      } catch (e) {
        return null;
      }
    },

    append: function() {
      var args = slice.call(arguments);
      var data = args.pop();
      var file = path.join.apply(null, args);
      var filepath = path.dirname(file);
      try {
        if (!fs.existsSync(filepath)) {
          make_directory(filepath);
        }
        fs.appendFileSync(file, data);
      } catch (e) { throw e; }
      return file;
    },

    read_json: function() {
      var json = null;
      try {
        json = JSON.parse(fs.readFileSync(path.join.apply(null, arguments), 'utf8'));
      } catch(e) { json = {}; }
      return json;
    },

    write_json: function() {
      var args = slice.call(arguments);
      var o = args.pop();
      this.clear.apply(this, args);
      args.push(JSON.stringify(o, null, '  '));
      this.write.apply(this, args);
    },

    get_directories: function(dir, options) {
      options = options || {};
      var exclude = options.exclude;
      var directories = [];
      try {
        fs.readdirSync(dir).forEach(function(node) {
          if (node[0] !== '.' && (!exclude || exclude.indexOf(node) === -1)) {
            var name = path.join(dir, node);
            var stat = fs.statSync(name);
            if (stat.isDirectory()) {
              directories.push(name);
            }
          }
        }, this);
      } catch(e) { directories = null; }
      return directories;
    },

    get_files: function(dir, options, files) {
      options = options || {};
      files = files || [];
      var exts = options.ext;
      var search = options.file;
      try {
        var dirs = [];
        fs.readdirSync(dir).forEach(function(node) {
          if (node[0] !== '.') {
            var ext = path.extname(node);
            var name = path.join(dir, node);
            var stat = fs.statSync(name);
            if (stat.isFile()) {
              if (
                (!exts && !search) ||
                (exts && ext.length && exts.indexOf(ext) >= 0) ||
                (search && node === search)
              ) {
                files.push(name);
              }
            } else if (stat.isDirectory()) {
              if (options.deep) {
                dirs.push(name);
              }
            }
          }
        }, this);
        if (options.deep && dirs.length) {
          dirs.forEach(function(dir) {
            this.get_files(dir, options, files);
          }, this);
        }
      } catch(e) { if (options.debug) console.log(e); files = null; }
      return files;
    },

    read_files: function(files, options) {
      options = options || {};
      var data = '';
      var new_line = os.EOL + os.EOL;
      files.forEach(function(file) {
        if (fs.lstatSync(file).isFile()) {
          data += os.EOL + '/* ' + file + ' */' + new_line + fs.readFileSync(file, 'utf8');
        }
      });
      return data;
    },

    merge_files: function(files, options) {
      if (options && options.out_file) {
        this.clear(options.out_file);
        var new_line = os.EOL + os.EOL;
        files.forEach(function(file) {
          if (fs.lstatSync(file).isFile()) {
            var data = os.EOL + '/* ' + file + ' */' + new_line + fs.readFileSync(file, 'utf8');
            fs.appendFileSync(options.out_file, data);
          }
        });
      }
    },

    base64_image: function(image, mime) {
      var data = fs.readFileSync(image).toString('base64');
      return util.format('data:%s;base64,%s', mime, data);
    }

  });

});
<commit_msg>correct precision for fs.utimes<commit_after>define(module, function(exports, require) {

  var slice = Array.prototype.slice;
  var fs = require('fs');
  var os = require('os');
  var util = require('util');
  var path = require('path');
  var qp = require('qp-utility');

  function make_directory(directory, mode) {
    directory = path.resolve(directory);
    if (typeof mode === 'undefined') {
      mode = 0777 & (~process.umask());
    }
    try {
      if (!fs.statSync(directory).isDirectory()) {
        throw new Error(directory + ' exists and is not a directory');
      }
    } catch (e) {
      if (e.code === 'ENOENT') {
        make_directory(path.dirname(directory), mode);
        fs.mkdirSync(directory, mode);
      } else {
        throw e;
      }
    }
  }

  function delete_directory(directory, options) {
    options = options || {};
    if (fs.existsSync(directory)) {
      fs.readdirSync(directory).forEach(function(node) {
        var item = path.join(directory, node);
        if (fs.lstatSync(item).isDirectory()) {
          delete_directory(item);
        } else {
          fs.unlinkSync(item);
        }
      });
      if (!options.children) {
        fs.rmdirSync(directory);
      }
    }
  }

  function delete_files(directory) {
    if (fs.existsSync(directory)) {
      fs.readdirSync(directory).forEach(function(node) {
        var item = path.join(directory, node);
        if (fs.lstatSync(item).isFile()) {
          fs.unlinkSync(item);
        }
      });
    }
  }

  function delete_file(file) {
    if (fs.existsSync(file) && fs.lstatSync(file).isFile()) {
      fs.unlinkSync(file);
    }
  }

  qp.module(exports, 'qp-library/fss', {

    stat: function() {
      return fs.statSync(path.join.apply(null, arguments));
    },

    touch: function() {
      var file = path.join.apply(null, arguments);
      if (this.exists(file)) {
        var time = Date.now() / 1000;
        fs.utimesSync(file, time, time);
      } else {
        this.write(file, '');
      }
    },

    modify: function() {
      var file = path.join.apply(null, arguments);
      if (this.exists(file)) {
        var stat = fs.statSync(file);
        var time = (new Date()).getTime();
        fs.utimesSync(file, stat.atime, time);
      } else {
        this.write(file, '');
      }
    },

    exists: function() {
      return fs.existsSync(path.join.apply(null, arguments));
    },

    make_directory: make_directory,
    delete_directory: delete_directory,
    delete_file: delete_file,
    delete_files: delete_files,

    read: function() {
      var data = null;
      try {
        data = fs.readFileSync(path.join.apply(null, arguments), 'utf8');
      } catch(e) { throw e; }
      return data;
    },

    write: function() {
      var args = slice.call(arguments);
      var data = args.pop();
      var file = path.join.apply(null, args);
      var filepath = path.dirname(file);
      try {
        if (!fs.existsSync(filepath)) {
          make_directory(filepath);
        }
        fs.writeFileSync(file, data);
      } catch (e) { throw e; }
      return file;
    },

    clear: function() {
      var args = slice.call(arguments);
      args.push('');
      return this.write.apply(this, args);
    },

    copy: function(source, target) {
      try {
        var data = fs.readFileSync(source);
        var target_dir = path.dirname(target);
        if (!fs.existsSync(target_dir)) {
          make_directory(target_dir);
        }
        fs.writeFileSync(target, data);
        return data.length;
      } catch (e) {
        return null;
      }
    },

    rename: function(file, name) {
      try {
        fs.renameSync(file, path.join(path.dirname(file), name));
      } catch (e) {
        return null;
      }
    },

    append: function() {
      var args = slice.call(arguments);
      var data = args.pop();
      var file = path.join.apply(null, args);
      var filepath = path.dirname(file);
      try {
        if (!fs.existsSync(filepath)) {
          make_directory(filepath);
        }
        fs.appendFileSync(file, data);
      } catch (e) { throw e; }
      return file;
    },

    read_json: function() {
      var json = null;
      try {
        json = JSON.parse(fs.readFileSync(path.join.apply(null, arguments), 'utf8'));
      } catch(e) { json = {}; }
      return json;
    },

    write_json: function() {
      var args = slice.call(arguments);
      var o = args.pop();
      this.clear.apply(this, args);
      args.push(JSON.stringify(o, null, '  '));
      this.write.apply(this, args);
    },

    get_directories: function(dir, options) {
      options = options || {};
      var exclude = options.exclude;
      var directories = [];
      try {
        fs.readdirSync(dir).forEach(function(node) {
          if (node[0] !== '.' && (!exclude || exclude.indexOf(node) === -1)) {
            var name = path.join(dir, node);
            var stat = fs.statSync(name);
            if (stat.isDirectory()) {
              directories.push(name);
            }
          }
        }, this);
      } catch(e) { directories = null; }
      return directories;
    },

    get_files: function(dir, options, files) {
      options = options || {};
      files = files || [];
      var exts = options.ext;
      var search = options.file;
      try {
        var dirs = [];
        fs.readdirSync(dir).forEach(function(node) {
          if (node[0] !== '.') {
            var ext = path.extname(node);
            var name = path.join(dir, node);
            var stat = fs.statSync(name);
            if (stat.isFile()) {
              if (
                (!exts && !search) ||
                (exts && ext.length && exts.indexOf(ext) >= 0) ||
                (search && node === search)
              ) {
                files.push(name);
              }
            } else if (stat.isDirectory()) {
              if (options.deep) {
                dirs.push(name);
              }
            }
          }
        }, this);
        if (options.deep && dirs.length) {
          dirs.forEach(function(dir) {
            this.get_files(dir, options, files);
          }, this);
        }
      } catch(e) { if (options.debug) console.log(e); files = null; }
      return files;
    },

    read_files: function(files, options) {
      options = options || {};
      var data = '';
      var new_line = os.EOL + os.EOL;
      files.forEach(function(file) {
        if (fs.lstatSync(file).isFile()) {
          data += os.EOL + '/* ' + file + ' */' + new_line + fs.readFileSync(file, 'utf8');
        }
      });
      return data;
    },

    merge_files: function(files, options) {
      if (options && options.out_file) {
        this.clear(options.out_file);
        var new_line = os.EOL + os.EOL;
        files.forEach(function(file) {
          if (fs.lstatSync(file).isFile()) {
            var data = os.EOL + '/* ' + file + ' */' + new_line + fs.readFileSync(file, 'utf8');
            fs.appendFileSync(options.out_file, data);
          }
        });
      }
    },

    base64_image: function(image, mime) {
      var data = fs.readFileSync(image).toString('base64');
      return util.format('data:%s;base64,%s', mime, data);
    }

  });

});
<|endoftext|>


<commit_before>function wordCount(text, word) {
  if( !word )
    return -1;
  var count = 0, length = word.length, index = -length;
  while (
    ~(index = text.indexOf(word, index + length)) &&
    ++count
  );
  return count;
}

function getPrimes(n) {
  var primes = [], p = 1;
  while( ++p <= n ) {
    var sqrtP = Math.sqrt(p) ^ 0;
    for( var j = 2; j <= sqrtP; j++ )
      if( !(p % j) ) break;
    !~(sqrtP - j) && primes.push(p);
  }
  return primes;
}

<commit_msg>add primesEratosthenes function<commit_after>function wordCount(text, word) {
  if( !word )
    return -1;
  var count = 0, length = word.length, index = -length;
  while (
    ~(index = text.indexOf(word, index + length)) &&
    ++count
  );
  return count;
}

function getPrimes(n) {
  var primes = [], p = 1;
  while( ++p <= n ) {
    var sqrtP = Math.sqrt(p) ^ 0;
    for( var j = 2; j <= sqrtP; j++ )
      if( !(p % j) ) break;
    !~(sqrtP - j) && primes.push(p);
  }
  return primes;
}

function primesEratosthenes(n) {
  var arr = [], p = 2, i, ret = [];
  while( p * p <= n ) {
    for( i = 2 * p; i <= n; i += p )
      arr[i] = true;
    for( i = p + 1; i <= n; i++ )
      if( !arr[i] )
        break;
    p = i;
  }
  for( i = 2; i <= n; i++ )
   if( !arr[i] )
     ret.push(i);
  return ret;
}
<|endoftext|>


<commit_before>import React, { useState, useEffect } from 'react'
import { useMutation } from '@apollo/react-hooks'
import styled from '@emotion/styled'

import MapView from '../components/MapView'
import ColorTags from './ColorTags'
import HierarchicalTagsContainer from '../containers/HierarchicalTagsContainer'
import EditableTags from '../components/EditableTags'
import ImageHistogram from '../components/ImageHistogram'
import StarRating from './StarRating'
import { PHOTO_UPDATE } from '../graphql/photo'

import { ReactComponent as EditIcon } from '../static/images/edit.svg'
import { ReactComponent as VisibilityIcon } from '../static/images/visibility.svg'
import { ReactComponent as VisibilityOffIcon } from '../static/images/visibility_off.svg'

const Container = styled('div')`
  position: absolute;
  top: 0;
  right: -350px;
  min-height: 200px;
  width: 350px;
  height: 100%;
  background: rgba(0,0,0,0.8);
  overflow-y: auto;
  overflow-x: hidden;
  padding-top: 40px;
  transition: right 400ms;

  &.showing {
    right: 0;
    .boxes {
      .box {
        left: 0;
      }
    }
  }

  h2 {
    font-size: 18px;
    margin: 0 0 20px 0;
  }
  .boxes {
    height: 100%
    padding: 40px 0 0 40px;
    z-index: 999;
    .box {
      vertical-align: top;
      margin: 0 40px 40px 40px;
      transition-property: left;
      left: 100px;
      position: relative;

      img {
        display: inline;
      }
      .histogram {
        margin-top: 16px;
      }
      .map {
        width: 100%;
        height: 150px;
        border: 1px solid #888;
      }
      ul {
        padding: 0;
        list-style: none;
        margin: 0;
      }
      h2 svg {
        filter: invert(0.9);
        height: 24px;
        width: 24px;
        margin: 0px 0px -6px 10px;
        vertical-align: 0;
        padding: 2px;
        cursor: pointer;
      }
    }
    .box1 {
      transition-duration: 500ms;
    }
    .box2 {
      transition-duration: 600ms;
    }
    .box3 {
      transition-duration: 700ms;
    }
    .box4 {
      transition-duration: 800ms;
    }
    .box5 {
      transition-duration: 900ms;
    }
    .box6 {
      transition-duration: 1000ms;
    }
    .box7 {
      transition-duration: 1100ms;
    }
    .box8 {
      transition-duration: 1200ms;
    }
  }

  @media all and (max-width: 500px) {
    width: 80vw;
  }
`

const PhotoMetadata = ({
  photo,
  show,
  refetch,
  showBoundingBox,
  setShowBoundingBox,
}) => {
  const [starRating, updateStarRating] = useState(photo.starRating)
  const [editorMode, setEditorMode] = useState(false)
  const [updatePhoto] = useMutation(PHOTO_UPDATE)

  let date = null
  if (photo.takenAt) {
    date = new Date(photo.takenAt)
    date = new Intl.DateTimeFormat().format(date)
  }

  let location = null
  if (photo.location) {
    location = [null, null]
    location[0] = parseFloat(photo.location.split(',')[0])
    location[1] = parseFloat(photo.location.split(',')[1])
  }

  useEffect(() => {
    updateStarRating(photo.starRating)
  }, [photo.starRating])

  const onStarClick = (num, e) => {
    if (starRating === num) {
      updateStarRating(0)
      updatePhoto({
        variables: {
          photoId: photo.id,
          starRating: 0,
        },
      }).catch((e) => {})
    } else {
      updateStarRating(num)
      updatePhoto({
        variables: {
          photoId: photo.id,
          starRating: num,
        },
      }).catch((e) => {})
    }
  }

  return (
    <Container className={show && 'showing'}>

          <StarRating
            starRating={starRating}
            onStarClick={onStarClick}
            large={true}
            alwaysShow={true}
          />

            <ImageHistogram
              imageUrl={`/thumbnailer/photo/3840x3840_contain_q75/${photo.id}/`}
            />

Camera

            {photo.camera ? (

                {photo.camera.make} {photo.camera.model}

            ) : (
              ''
            )}
            {date ?
Date: {date}
 : ''}

Aperture: {photo.aperture}

Exposure: {photo.exposure}

ISO speed: {photo.isoSpeed}

Focal length: {photo.focalLength}

Flash: {photo.flash ? 'ON' : 'OFF'}

Metering mode: {photo.meteringMode}

            {photo.driveMode ?
Drive mode: {photo.driveMode}
 : ''}
            {photo.shootingMode ? (

Shooting mode: {photo.shootingMode}

            ) : (
              ''
            )}

        {photo.locationTags.length ? (

Locations

            <HierarchicalTagsContainer
              tags={photo.locationTags.map((item) => {
                let newItem = item.tag
                newItem.parent = item.parent
                return newItem
              })}
            />

        ) : (
          ''
        )}
        {photo.location ? (

Map

              {<MapView location={location} hideAttribution={true} zoom={6} />}

        ) : (
          ''
        )}
        {photo.colorTags.length ? (

Colors

            <ColorTags
              tags={photo.colorTags.map((item) => ({
                name: item.tag.name,
                significance: item.significance,
              }))}
            />

        ) : (
          ''
        )}
        {photo.objectTags.length ? (

              Objects
              {showBoundingBox ? (
                <VisibilityIcon onClick={() => setShowBoundingBox(false)} />
              ) : (
                <VisibilityOffIcon onClick={() => setShowBoundingBox(true)} />
              )}

              {photo.objectTags.map((photoTag, index) => (

{photoTag.tag.name}

              ))}

        ) : (
          ''
        )}
        {photo.styleTags.length ? (

Styles

              {photo.styleTags.map((photoTag, index) => (

{photoTag.tag.name}

              ))}

        ) : (
          ''
        )}

            Tags
            <EditIcon alt="Edit" onClick={() => setEditorMode(!editorMode)} />

          <EditableTags
            tags={photo.genericTags}
            editorMode={editorMode}
            photoId={photo.id}
            refetch={refetch}
          />

    </Container>
  )
}

export default PhotoMetadata
<commit_msg>add box div for path<commit_after>import React, { useState, useEffect } from 'react'
import { useMutation } from '@apollo/react-hooks'
import styled from '@emotion/styled'

import MapView from '../components/MapView'
import ColorTags from './ColorTags'
import HierarchicalTagsContainer from '../containers/HierarchicalTagsContainer'
import EditableTags from '../components/EditableTags'
import ImageHistogram from '../components/ImageHistogram'
import StarRating from './StarRating'
import { PHOTO_UPDATE } from '../graphql/photo'

import { ReactComponent as EditIcon } from '../static/images/edit.svg'
import { ReactComponent as VisibilityIcon } from '../static/images/visibility.svg'
import { ReactComponent as VisibilityOffIcon } from '../static/images/visibility_off.svg'

const Container = styled('div')`
  position: absolute;
  top: 0;
  right: -350px;
  min-height: 200px;
  width: 350px;
  height: 100%;
  background: rgba(0,0,0,0.8);
  overflow-y: auto;
  overflow-x: hidden;
  padding-top: 40px;
  transition: right 400ms;

  &.showing {
    right: 0;
    .boxes {
      .box {
        left: 0;
      }
    }
  }

  h2 {
    font-size: 18px;
    margin: 0 0 20px 0;
  }
  .boxes {
    height: 100%
    padding: 40px 0 0 40px;
    z-index: 999;
    .box {
      vertical-align: top;
      margin: 0 40px 40px 40px;
      transition-property: left;
      left: 100px;
      position: relative;

      img {
        display: inline;
      }
      .histogram {
        margin-top: 16px;
      }
      .map {
        width: 100%;
        height: 150px;
        border: 1px solid #888;
      }
      ul {
        padding: 0;
        list-style: none;
        margin: 0;
      }
      h2 svg {
        filter: invert(0.9);
        height: 24px;
        width: 24px;
        margin: 0px 0px -6px 10px;
        vertical-align: 0;
        padding: 2px;
        cursor: pointer;
      }
    }
    .box1 {
      transition-duration: 500ms;
    }
    .box2 {
      transition-duration: 600ms;
    }
    .box3 {
      transition-duration: 700ms;
    }
    .box4 {
      transition-duration: 800ms;
    }
    .box5 {
      transition-duration: 900ms;
    }
    .box6 {
      transition-duration: 1000ms;
    }
    .box7 {
      transition-duration: 1100ms;
    }
    .box8 {
      transition-duration: 1200ms;
    }
  }

  @media all and (max-width: 500px) {
    width: 80vw;
  }
`

const PhotoMetadata = ({
  photo,
  show,
  refetch,
  showBoundingBox,
  setShowBoundingBox,
}) => {
  const [starRating, updateStarRating] = useState(photo.starRating)
  const [editorMode, setEditorMode] = useState(false)
  const [updatePhoto] = useMutation(PHOTO_UPDATE)

  let date = null
  if (photo.takenAt) {
    date = new Date(photo.takenAt)
    date = new Intl.DateTimeFormat().format(date)
  }

  let location = null
  if (photo.location) {
    location = [null, null]
    location[0] = parseFloat(photo.location.split(',')[0])
    location[1] = parseFloat(photo.location.split(',')[1])
  }

  useEffect(() => {
    updateStarRating(photo.starRating)
  }, [photo.starRating])

  const onStarClick = (num, e) => {
    if (starRating === num) {
      updateStarRating(0)
      updatePhoto({
        variables: {
          photoId: photo.id,
          starRating: 0,
        },
      }).catch((e) => {})
    } else {
      updateStarRating(num)
      updatePhoto({
        variables: {
          photoId: photo.id,
          starRating: num,
        },
      }).catch((e) => {})
    }
  }

  return (
    <Container className={show && 'showing'}>

          <StarRating
            starRating={starRating}
            onStarClick={onStarClick}
            large={true}
            alwaysShow={true}
          />

            <ImageHistogram
              imageUrl={`/thumbnailer/photo/3840x3840_contain_q75/${photo.id}/`}
            />

Camera

            {photo.camera ? (

                {photo.camera.make} {photo.camera.model}

            ) : (
              ''
            )}
            {date ?
Date: {date}
 : ''}

Aperture: {photo.aperture}

Exposure: {photo.exposure}

ISO speed: {photo.isoSpeed}

Focal length: {photo.focalLength}

Flash: {photo.flash ? 'ON' : 'OFF'}

Metering mode: {photo.meteringMode}

            {photo.driveMode ?
Drive mode: {photo.driveMode}
 : ''}
            {photo.shootingMode ? (

Shooting mode: {photo.shootingMode}

            ) : (
              ''
            )}

        {photo.locationTags.length ? (

Locations

            <HierarchicalTagsContainer
              tags={photo.locationTags.map((item) => {
                let newItem = item.tag
                newItem.parent = item.parent
                return newItem
              })}
            />

        ) : (
          ''
        )}
        {photo.location ? (

Map

              {<MapView location={location} hideAttribution={true} zoom={6} />}

        ) : (
          ''
        )}
        {photo.colorTags.length ? (

Colors

            <ColorTags
              tags={photo.colorTags.map((item) => ({
                name: item.tag.name,
                significance: item.significance,
              }))}
            />

        ) : (
          ''
        )}
        {photo.objectTags.length ? (

              Objects
              {showBoundingBox ? (
                <VisibilityIcon onClick={() => setShowBoundingBox(false)} />
              ) : (
                <VisibilityOffIcon onClick={() => setShowBoundingBox(true)} />
              )}

              {photo.objectTags.map((photoTag, index) => (

{photoTag.tag.name}

              ))}

        ) : (
          ''
        )}
        {photo.styleTags.length ? (

Styles

              {photo.styleTags.map((photoTag, index) => (

{photoTag.tag.name}

              ))}

        ) : (
          ''
        )}

            Tags
            <EditIcon alt="Edit" onClick={() => setEditorMode(!editorMode)} />

          <EditableTags
            tags={photo.genericTags}
            editorMode={editorMode}
            photoId={photo.id}
            refetch={refetch}
          />

            Path

data/images/photo.jpeg

    </Container>
  )
}

export default PhotoMetadata
<|endoftext|>


<commit_before>import {
  getDashboards as getDashboardsAJAX,
  updateDashboard as updateDashboardAJAX,
  deleteDashboard as deleteDashboardAJAX,
  updateDashboardCell as updateDashboardCellAJAX,
  addDashboardCell as addDashboardCellAJAX,
  deleteDashboardCell as deleteDashboardCellAJAX,
} from 'src/dashboards/apis'

import {publishNotification} from 'src/shared/actions/notifications';

import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants'

export const loadDashboards = (dashboards, dashboardID) => ({
  type: 'LOAD_DASHBOARDS',
  payload: {
    dashboards,
    dashboardID,
  },
})

export const setDashboard = (dashboardID) => ({
  type: 'SET_DASHBOARD',
  payload: {
    dashboardID,
  },
})

export const setTimeRange = (timeRange) => ({
  type: 'SET_DASHBOARD_TIME_RANGE',
  payload: {
    timeRange,
  },
})

export const updateDashboard = (dashboard) => ({
  type: 'UPDATE_DASHBOARD',
  payload: {
    dashboard,
  },
})

export const deleteDashboard = (dashboard) => ({
  type: 'DELETE_DASHBOARD',
  payload: {
    dashboard,
  },
})

export const deleteDashboardFailed = (dashboard) => ({
  type: 'DELETE_DASHBOARD_FAILED',
  payload: {
    dashboard,
  },
})

export const updateDashboardCells = (cells) => ({
  type: 'UPDATE_DASHBOARD_CELLS',
  payload: {
    cells,
  },
})

export const syncDashboardCell = (cell) => ({
  type: 'SYNC_DASHBOARD_CELL',
  payload: {
    cell,
  },
})

export const addDashboardCell = (cell) => ({
  type: 'ADD_DASHBOARD_CELL',
  payload: {
    cell,
  },
})

export const editDashboardCell = (x, y, isEditing) => ({
  type: 'EDIT_DASHBOARD_CELL',
  // x and y coords are used as a alternative to cell ids, which are not
  // universally unique, and cannot be because React depends on a
  // quasi-predictable ID for keys. Since cells cannot overlap, coordinates act
  // as a suitable id
  payload: {
    x,  // x-coord of the cell to be edited
    y,  // y-coord of the cell to be edited
    isEditing,
  },
})

export const renameDashboardCell = (x, y, name) => ({
  type: 'RENAME_DASHBOARD_CELL',
  payload: {
    x,  // x-coord of the cell to be renamed
    y,  // y-coord of the cell to be renamed
    name,
  },
})

export const deleteDashboardCell = (cell) => ({
  type: 'DELETE_DASHBOARD_CELL',
  payload: {
    cell,
  },
})

// Async Action Creators

export const getDashboardsAsync = (dashboardID) => async (dispatch) => {
  const {data: {dashboards}} = await getDashboardsAJAX()
  dispatch(loadDashboards(dashboards, dashboardID))
}

export const putDashboard = () => (dispatch, getState) => {
  const {dashboardUI: {dashboard}} = getState()
  updateDashboardAJAX(dashboard).then(({data}) => {
    dispatch(updateDashboard(data))
  })
}

export const updateDashboardCell = (cell) => (dispatch) => {
  return updateDashboardCellAJAX(cell)
  .then(({data}) => {
    dispatch(syncDashboardCell(data))
  })
}

export const deleteDashboardAsync = (dashboard) => async (dispatch) => {
  dispatch(deleteDashboard(dashboard))
  try {
    await deleteDashboardAJAX(dashboard)
    dispatch(publishNotification('success', 'Dashboard deleted successfully.'))
  } catch (error) {
    dispatch(deleteDashboardFailed(dashboard))
    dispatch(publishNotification('error', `Failed to delete dashboard: ${error.data.message}.`))
  }
}

export const addDashboardCellAsync = (dashboard) => async (dispatch) => {
  try {
    const {data} = await addDashboardCellAJAX(dashboard, NEW_DEFAULT_DASHBOARD_CELL)
    dispatch(addDashboardCell(data))
  } catch (error) {
    console.error(error)
    throw error
  }
}

export const deleteDashboardCellAsync = (cell) => async (dispatch) => {
  try {
    await deleteDashboardCellAJAX(cell)
    dispatch(deleteDashboardCell(cell))
  } catch (error) {
    console.error(error)
    throw error
  }
}
<commit_msg>Use try/catch with async/await<commit_after>import {
  getDashboards as getDashboardsAJAX,
  updateDashboard as updateDashboardAJAX,
  deleteDashboard as deleteDashboardAJAX,
  updateDashboardCell as updateDashboardCellAJAX,
  addDashboardCell as addDashboardCellAJAX,
  deleteDashboardCell as deleteDashboardCellAJAX,
} from 'src/dashboards/apis'

import {publishNotification} from 'src/shared/actions/notifications';

import {NEW_DEFAULT_DASHBOARD_CELL} from 'src/dashboards/constants'

export const loadDashboards = (dashboards, dashboardID) => ({
  type: 'LOAD_DASHBOARDS',
  payload: {
    dashboards,
    dashboardID,
  },
})

export const setDashboard = (dashboardID) => ({
  type: 'SET_DASHBOARD',
  payload: {
    dashboardID,
  },
})

export const setTimeRange = (timeRange) => ({
  type: 'SET_DASHBOARD_TIME_RANGE',
  payload: {
    timeRange,
  },
})

export const updateDashboard = (dashboard) => ({
  type: 'UPDATE_DASHBOARD',
  payload: {
    dashboard,
  },
})

export const deleteDashboard = (dashboard) => ({
  type: 'DELETE_DASHBOARD',
  payload: {
    dashboard,
  },
})

export const deleteDashboardFailed = (dashboard) => ({
  type: 'DELETE_DASHBOARD_FAILED',
  payload: {
    dashboard,
  },
})

export const updateDashboardCells = (cells) => ({
  type: 'UPDATE_DASHBOARD_CELLS',
  payload: {
    cells,
  },
})

export const syncDashboardCell = (cell) => ({
  type: 'SYNC_DASHBOARD_CELL',
  payload: {
    cell,
  },
})

export const addDashboardCell = (cell) => ({
  type: 'ADD_DASHBOARD_CELL',
  payload: {
    cell,
  },
})

export const editDashboardCell = (x, y, isEditing) => ({
  type: 'EDIT_DASHBOARD_CELL',
  // x and y coords are used as a alternative to cell ids, which are not
  // universally unique, and cannot be because React depends on a
  // quasi-predictable ID for keys. Since cells cannot overlap, coordinates act
  // as a suitable id
  payload: {
    x,  // x-coord of the cell to be edited
    y,  // y-coord of the cell to be edited
    isEditing,
  },
})

export const renameDashboardCell = (x, y, name) => ({
  type: 'RENAME_DASHBOARD_CELL',
  payload: {
    x,  // x-coord of the cell to be renamed
    y,  // y-coord of the cell to be renamed
    name,
  },
})

export const deleteDashboardCell = (cell) => ({
  type: 'DELETE_DASHBOARD_CELL',
  payload: {
    cell,
  },
})

// Async Action Creators

export const getDashboardsAsync = (dashboardID) => async (dispatch) => {
  try {
    const {data: {dashboards}} = await getDashboardsAJAX()
    dispatch(loadDashboards(dashboards, dashboardID))
  } catch (error) {
    console.error(error)
    throw error
  }
}

export const putDashboard = () => (dispatch, getState) => {
  const {dashboardUI: {dashboard}} = getState()
  updateDashboardAJAX(dashboard).then(({data}) => {
    dispatch(updateDashboard(data))
  })
}

export const updateDashboardCell = (cell) => (dispatch) => {
  return updateDashboardCellAJAX(cell)
  .then(({data}) => {
    dispatch(syncDashboardCell(data))
  })
}

export const deleteDashboardAsync = (dashboard) => async (dispatch) => {
  dispatch(deleteDashboard(dashboard))
  try {
    await deleteDashboardAJAX(dashboard)
    dispatch(publishNotification('success', 'Dashboard deleted successfully.'))
  } catch (error) {
    dispatch(deleteDashboardFailed(dashboard))
    dispatch(publishNotification('error', `Failed to delete dashboard: ${error.data.message}.`))
  }
}

export const addDashboardCellAsync = (dashboard) => async (dispatch) => {
  try {
    const {data} = await addDashboardCellAJAX(dashboard, NEW_DEFAULT_DASHBOARD_CELL)
    dispatch(addDashboardCell(data))
  } catch (error) {
    console.error(error)
    throw error
  }
}

export const deleteDashboardCellAsync = (cell) => async (dispatch) => {
  try {
    await deleteDashboardCellAJAX(cell)
    dispatch(deleteDashboardCell(cell))
  } catch (error) {
    console.error(error)
    throw error
  }
}
<|endoftext|>


<commit_before>// Copyright 2015 Endless Mobile, Inc.

const Gettext = imports.gettext;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;

const Config = imports.app.config;
const Module = imports.app.interfaces.module;
const SlidingPanel = imports.app.widgets.slidingPanel;
const TabButton = imports.app.widgets.tabButton;

let _ = Gettext.dgettext.bind(null, Config.GETTEXT_PACKAGE);

/**
 * Class: HamburgerBasement
 *
 * A template with two 'pages', a upper page with three vertical slots, and a
 * basement page with one large slot.
 *
 * CSS Styles:
 *      hamburger-basement-template - on the template
 *
 * Slots:
 *   top
 *   middle
 *   bottom
 *   basement
 */
const HamburgerBasement = new Module.Class({
    Name: 'Layout.HamburgerBasement',
    Extends: Gtk.Stack,

    Properties: {
        /**
         * Property: upper-button-label
         * Label on the tab button on the upper page
         */
        'upper-button-label': GObject.ParamSpec.string('upper-button-label',
            'Upper tab button label', 'Upper tab button label',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            _("SEE ALL CATEGORIES")),
        /**
         * Property: show-upper-button
         * Whether to show the tab button on the upper page
         */
        'show-upper-button': GObject.ParamSpec.boolean('show-upper-button',
            'Show upper tab button', 'Show upper tab button',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
            false),
        /**
         * Property: basement-button-label
         * Label on the tab button on the basement page
         */
        'basement-button-label': GObject.ParamSpec.string('basement-button-label',
            'Basement tab button label', 'Basement tab button label',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            _("HOME")),
    },

    Slots: {
        'top': {},
        'middle': {},
        'bottom': {},
        'basement': {},
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/layout/hamburgerBasement.ui',
    InternalChildren: [ 'upper-grid', 'inner-grid', 'basement-grid' ],

    _BUTTON_TRANSITION_TIME: 500,

    _init: function (props={}) {
        // FIXME: these should be in the template.ui file, but aren't working
        // from there. Why?
        props.transition_type = Gtk.StackTransitionType.SLIDE_UP_DOWN;
        props.transition_duration = 500;
        this.parent(props);

        ['top', 'middle'].forEach((slot) => {
            let submodule = this.create_submodule(slot);
            this._inner_grid.add(submodule);
            this['_' + slot] = submodule;
        });

        this._bottom = this.create_submodule('bottom');
        this._upper_grid.attach(this._bottom, 0, 1, 1, 1);

        this._basement = this.create_submodule('basement');
        this._basement_grid.attach(this._basement, 0, 1, 1, 1);

        let top_panel = this._setup_panel_button(Gtk.PositionType.TOP);
        let bottom_panel = this._setup_panel_button(Gtk.PositionType.BOTTOM);

        this._basement_grid.attach(top_panel, 0, 0, 1, 1);
        this._upper_grid.attach(bottom_panel, 0, 2, 1, 1);

        this.set_visible_child(this._upper_grid);
        this.connect('notify::transition-running', () => {
            if (!this.transition_running) {
                if (this.get_visible_child() === this._basement_grid) {
                    top_panel.reveal_panel = true;
                } else {
                    bottom_panel.reveal_panel = this.show_upper_button;
                }
            }
        });

        this.connect('notify::show-upper-button', () => {
            bottom_panel.reveal_panel = this.show_upper_button;
        });
    },

    _setup_panel_button: function (position) {
        let is_bottom = (position === Gtk.PositionType.BOTTOM);
        let button = new TabButton.TabButton({
            position: position,
            visible: true,
            label: is_bottom ? this.upper_button_label : this.basement_button_label,
        });
        let panel = new SlidingPanel.SlidingPanel({
            panel_widget: button,
            hide_direction: position,
            transition_duration: this._BUTTON_TRANSITION_TIME,
        });
        button.connect('clicked', () => {
            let next_page = is_bottom ? this._basement_grid : this._upper_grid;
            panel.reveal_panel = false;
            let id = panel.connect('notify::panel-revealed', () => {
                this.set_visible_child(next_page);
                panel.disconnect(id);
            });
        });
        return panel;
    },
});
<commit_msg>Layout.HamburgerBasement: don't break when no animations<commit_after>// Copyright 2015 Endless Mobile, Inc.

const Gettext = imports.gettext;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;

const Config = imports.app.config;
const Module = imports.app.interfaces.module;
const SlidingPanel = imports.app.widgets.slidingPanel;
const TabButton = imports.app.widgets.tabButton;

let _ = Gettext.dgettext.bind(null, Config.GETTEXT_PACKAGE);

/**
 * Class: HamburgerBasement
 *
 * A template with two 'pages', a upper page with three vertical slots, and a
 * basement page with one large slot.
 *
 * CSS Styles:
 *      hamburger-basement-template - on the template
 *
 * Slots:
 *   top
 *   middle
 *   bottom
 *   basement
 */
const HamburgerBasement = new Module.Class({
    Name: 'Layout.HamburgerBasement',
    Extends: Gtk.Stack,

    Properties: {
        /**
         * Property: upper-button-label
         * Label on the tab button on the upper page
         */
        'upper-button-label': GObject.ParamSpec.string('upper-button-label',
            'Upper tab button label', 'Upper tab button label',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            _("SEE ALL CATEGORIES")),
        /**
         * Property: show-upper-button
         * Whether to show the tab button on the upper page
         */
        'show-upper-button': GObject.ParamSpec.boolean('show-upper-button',
            'Show upper tab button', 'Show upper tab button',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
            false),
        /**
         * Property: basement-button-label
         * Label on the tab button on the basement page
         */
        'basement-button-label': GObject.ParamSpec.string('basement-button-label',
            'Basement tab button label', 'Basement tab button label',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
            _("HOME")),
    },

    Slots: {
        'top': {},
        'middle': {},
        'bottom': {},
        'basement': {},
    },

    Template: 'resource:///com/endlessm/knowledge/data/widgets/layout/hamburgerBasement.ui',
    InternalChildren: [ 'upper-grid', 'inner-grid', 'basement-grid' ],

    _BUTTON_TRANSITION_TIME: 500,

    _init: function (props={}) {
        // FIXME: these should be in the template.ui file, but aren't working
        // from there. Why?
        props.transition_type = Gtk.StackTransitionType.SLIDE_UP_DOWN;
        props.transition_duration = 500;
        this.parent(props);

        ['top', 'middle'].forEach((slot) => {
            let submodule = this.create_submodule(slot);
            this._inner_grid.add(submodule);
            this['_' + slot] = submodule;
        });

        this._bottom = this.create_submodule('bottom');
        this._upper_grid.attach(this._bottom, 0, 1, 1, 1);

        this._basement = this.create_submodule('basement');
        this._basement_grid.attach(this._basement, 0, 1, 1, 1);

        this._top_panel = this._setup_panel_button(Gtk.PositionType.TOP);
        this._bottom_panel = this._setup_panel_button(Gtk.PositionType.BOTTOM);

        this._basement_grid.attach(this._top_panel, 0, 0, 1, 1);
        this._upper_grid.attach(this._bottom_panel, 0, 2, 1, 1);

        this.set_visible_child(this._upper_grid);
        this.connect('notify::transition-running', this._update_panel_button.bind(this));

        this.connect('notify::show-upper-button', () => {
            this._bottom_panel.reveal_panel = this.show_upper_button;
        });
    },

    _update_panel_button: function() {
        if (this.transition_running)
            return;
        if (this.get_visible_child() === this._basement_grid) {
            this._top_panel.reveal_panel = true;
        } else {
            this._bottom_panel.reveal_panel = this.show_upper_button;
        }
     },

    _setup_panel_button: function (position) {
        let is_bottom = (position === Gtk.PositionType.BOTTOM);
        let button = new TabButton.TabButton({
            position: position,
            visible: true,
            label: is_bottom ? this.upper_button_label : this.basement_button_label,
        });
        let panel = new SlidingPanel.SlidingPanel({
            panel_widget: button,
            hide_direction: position,
            transition_duration: this._BUTTON_TRANSITION_TIME,
        });
        button.connect('clicked', () => {
            let next_page = is_bottom ? this._basement_grid : this._upper_grid;
            let id = panel.connect('notify::panel-revealed', () => {
                this.set_visible_child(next_page);
                panel.disconnect(id);
                this._update_panel_button();
            });
            panel.reveal_panel = false;
        });
        return panel;
    },
});
<|endoftext|>


<commit_before>define(['knockout', 'text!./cohort-definition-browser.html', 'appConfig', 'webapi/AuthAPI', 'faceted-datatable'], function (ko, view, config, authApi) {
	function cohortDefinitionBrowser(params) {
		var self = this;
		self.currentService = ko.observable();
		self.reference = ko.observableArray();
		self.selected = params.cohortDefinitionSelected;
		self.loading = ko.observable(false);
		self.config = config;

		self.currentService.subscribe(function (d) {
			self.loading(true);

			$.ajax({
				url: self.currentService() + 'cohortdefinition',
				headers: {
					Authorization: authApi.getAuthorizationHeader()
				},
				method: 'GET',
				error: authApi.handleAccessDenied,
				success: function (d) {
					self.reference(d);
				},
				complete: function() {
					self.loading(false);
				}
			});
		});

		self.options = {
			Facets: [
				{
					'caption': 'Last Modified',
					'binding': function (o) {
						var daysSinceModification = (new Date().getTime() - new Date(o.modifiedDate).getTime()) / 1000 / 60 / 60 / 24;
						if (daysSinceModification < 7) {
							return 'This Week';
						} else if (daysSinceModification < 14) {
							return 'Last Week';
						} else {
							return '2+ Weeks Ago';
						}
					}
				},
				{
					'caption': 'Author',
					'binding': function (o) {
						return o.createdBy;
					}
				}
			]
		};

		self.renderCohortDefinitionLink = function (s, p, d) {
			return '' + d.name + '';
		}

		self.rowClick = function (d) {
			self.selected(d.id);
		}

		self.columns = [
			{
				title: 'Id',
				data: 'id'
			},
			{
				title: 'Name',
				render: self.renderCohortDefinitionLink
			},
			{
				title: 'Created',
				render: function (s, p, d) {
					return new Date(d.createdDate).toLocaleDateString();
				}
			},
			{
				title: 'Updated',
				render: function (s, p, d) {
					return new Date(d.modifiedDate).toLocaleDateString();
				}
			},
			{
				title: 'Author',
				data: 'createdBy'
			}
		];

	}

	var component = {
		viewModel: cohortDefinitionBrowser,
		template: view
	};

	ko.components.register('cohort-definition-browser', component);
	return component;
});
<commit_msg>Fixes #102<commit_after>define(['knockout', 'text!./cohort-definition-browser.html', 'appConfig', 'webapi/AuthAPI', 'faceted-datatable'], function (ko, view, config, authApi) {
	function cohortDefinitionBrowser(params) {
		var self = this;
		self.currentService = ko.observable();
		self.reference = ko.observableArray();
		self.selected = params.cohortDefinitionSelected;
		self.loading = ko.observable(false);
		self.config = config;

		self.currentService.subscribe(function (d) {
			self.loading(true);

			$.ajax({
				url: self.currentService() + 'cohortdefinition',
				headers: {
					Authorization: authApi.getAuthorizationHeader()
				},
				method: 'GET',
				error: authApi.handleAccessDenied,
				success: function (d) {
					self.reference(d);
				},
				complete: function() {
					self.loading(false);
				}
			});
		});

		self.options = {
			Facets: [
				{
					'caption': 'Last Modified',
					'binding': function (o) {
                        var createDate = new Date(o.createdDate);
                        var modDate = new Date(o.modifiedDate);
                        var dateForCompare = (createDate > modDate) ? createDate : modDate;
						var daysSinceModification = (new Date().getTime() - dateForCompare.getTime()) / 1000 / 60 / 60 / 24;
						if (daysSinceModification < 7) {
							return 'This Week';
						} else if (daysSinceModification < 14) {
							return 'Last Week';
						} else {
							return '2+ Weeks Ago';
						}
					}
				},
				{
					'caption': 'Author',
					'binding': function (o) {
						return o.createdBy;
					}
				}
			]
		};

		self.renderCohortDefinitionLink = function (s, p, d) {
			return '' + d.name + '';
		}

		self.rowClick = function (d) {
			self.selected(d.id);
		}

		self.columns = [
			{
				title: 'Id',
				data: 'id'
			},
			{
				title: 'Name',
				render: self.renderCohortDefinitionLink
			},
			{
				title: 'Created',
				render: function (s, p, d) {
					return new Date(d.createdDate).toLocaleDateString();
				}
			},
			{
				title: 'Updated',
				render: function (s, p, d) {
					return new Date(d.modifiedDate).toLocaleDateString();
				}
			},
			{
				title: 'Author',
				data: 'createdBy'
			}
		];

	}

	var component = {
		viewModel: cohortDefinitionBrowser,
		template: view
	};

	ko.components.register('cohort-definition-browser', component);
	return component;
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */

CLASS({
  package: 'foam.apps.builder',
  name: 'AppConfigSheetView',
  extendsModel: 'foam.ui.SimpleView',

  imports: [
    'popup',
  ],

  properties: [
    'data',
    {
      name: 'popup',
      postSet: function(old, nu) {
        if ( old === nu || ! this.$style ) return;
        this.$style.outerHTML = this.styleHTML();
      },
    },
    {
      model_: 'ViewFactoryProperty',
      name: 'innerView',
      defaultValue: 'foam.ui.md.DetailView'
    },
    {
      name: '$style',
      getter: function() {
        return this.$ ? this.$.querySelector('#' + this.id + '-style') : null;
      },
    },
  ],

  actions: [
    {
      name: 'close',
      ligature: 'close',
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAa0lEQVR4AeWSsREDIQwEtwlRhO3vP0JFPLgeHJDdnEfBh2y8F2hHnM5FJ1AayRtLshiE6F8WHUsw9kT0m8BDMFlMotZ10rzuaHtS63qo6s8HWkaLFXpo5ErXyKWukS25dRM5sXz+Pt+Ls/kBnolC6l7shJkAAAAASUVORK5CYII=',
      isAvailable: function() { return this.popup; },
      code: function() {
        this.popup && this.popup.close();
      },
    },
  ],

  templates: [
    function toHTML() {/*
      <app-config id="%%id" %%cssClassAttr()>
        <% this.styleHTML(out); %>

            <%# (this.data ? this.data.appName : 'App') + ' Configuration' %>
            $$close

        <%= this.innerView({
              data$: this.data$,
            }, this.Y) %>
      </app-config>
      <% this.setClass('expanded', function() {
           return ( ! this.popup ) ||
               this.popup.state === 'expanding' ||
               this.popup.state === 'expanded';
         }.bind(this), this.id);
         this.on('click', function() {
           this.popup && this.popup.expandOrCollapse();
         }.bind(this), this.id + '-heading'); %>
    */},
    function styleHTML() {/*

    */},
    function CSS() {/*
      app-config {
        flex-grow: 1;
        display: flex;
        flex-direction: column;
        z-index: 2;
        background-color: #fff;
        box-shadow: 2px 0px 7px rgba(0, 0, 0, 0.48);
      }
      app-config .heading-content {
        flex-grow: 1;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      app-config .md-heading {
        display: flex;
        background-color: transparent;
      }
      app-config.expanded .md-heading {
        color: #fff;
        background-color: #3e50b4;
      }

      app-config .swipeAltHeader {
        padding-left: 3px !important;
        height: 36px;
      }
      app-config .swipeAltHeader li {
        font-size: 14px;
        line-height: 46px;
        padding-bottom: 14px;
      }
      app-config .swipeAltHeader .selected {
        border-bottom: 2px solid rgba(0,0,0,.87);
        font-weight: 500;
      }
      app-config.expanded .swipeAltHeader .selected {
        border-bottom: 2px solid #ff3f80;
      }
      app-config .swipeAltHeader {
        background-color: transparent;
        box-shadow: 0 1px 1px rgba(0,0,0,.25);
        height: 47px;
        margin: 0;
        overflow: hidden;
        padding: 0 0 0 56px;
        -webkit-user-select: none;
        -khtml-user-select: none;
        -moz-user-select: -moz-none;
        -o-user-select: none;
        user-select: none;
      }
      app-config.expanded .swipeAltHeader {
        color: #fff;
        background-color: #3e50b4;
      }
      app-config .foamChoiceListView.horizontal .choice {
        text-transform: uppercase;
        padding: 14px 16px;
        margin: 0;
      }
    */},
  ],
});
<commit_msg>App Builder: Sheet view: animated transition on border-color, not all border properties.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */

CLASS({
  package: 'foam.apps.builder',
  name: 'AppConfigSheetView',
  extendsModel: 'foam.ui.SimpleView',

  imports: [
    'popup',
  ],

  properties: [
    'data',
    {
      name: 'popup',
      postSet: function(old, nu) {
        if ( old === nu || ! this.$style ) return;
        this.$style.outerHTML = this.styleHTML();
      },
    },
    {
      model_: 'ViewFactoryProperty',
      name: 'innerView',
      defaultValue: 'foam.ui.md.DetailView'
    },
    {
      name: '$style',
      getter: function() {
        return this.$ ? this.$.querySelector('#' + this.id + '-style') : null;
      },
    },
  ],

  actions: [
    {
      name: 'close',
      ligature: 'close',
      iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAAa0lEQVR4AeWSsREDIQwEtwlRhO3vP0JFPLgeHJDdnEfBh2y8F2hHnM5FJ1AayRtLshiE6F8WHUsw9kT0m8BDMFlMotZ10rzuaHtS63qo6s8HWkaLFXpo5ErXyKWukS25dRM5sXz+Pt+Ls/kBnolC6l7shJkAAAAASUVORK5CYII=',
      isAvailable: function() { return this.popup; },
      code: function() {
        this.popup && this.popup.close();
      },
    },
  ],

  templates: [
    function toHTML() {/*
      <app-config id="%%id" %%cssClassAttr()>
        <% this.styleHTML(out); %>

            <%# (this.data ? this.data.appName : 'App') + ' Configuration' %>
            $$close

        <%= this.innerView({
              data$: this.data$,
            }, this.Y) %>
      </app-config>
      <% this.setClass('expanded', function() {
           return ( ! this.popup ) ||
               this.popup.state === 'expanding' ||
               this.popup.state === 'expanded';
         }.bind(this), this.id);
         this.on('click', function() {
           this.popup && this.popup.expandOrCollapse();
         }.bind(this), this.id + '-heading'); %>
    */},
    function styleHTML() {/*

    */},
    function CSS() {/*
      app-config {
        flex-grow: 1;
        display: flex;
        flex-direction: column;
        z-index: 2;
        background-color: #fff;
        box-shadow: 2px 0px 7px rgba(0, 0, 0, 0.48);
      }
      app-config .heading-content {
        flex-grow: 1;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      app-config .md-heading {
        display: flex;
        background-color: transparent;
      }
      app-config.expanded .md-heading {
        color: #fff;
        background-color: #3e50b4;
      }

      app-config .swipeAltHeader {
        padding-left: 3px !important;
        height: 36px;
      }
      app-config .swipeAltHeader li {
        font-size: 14px;
        line-height: 46px;
        padding-bottom: 14px;
      }
      app-config .swipeAltHeader .selected {
        border-bottom: 2px solid rgba(0,0,0,.87);
        font-weight: 500;
      }
      app-config.expanded .swipeAltHeader .selected {
        border-bottom: 2px solid #ff3f80;
      }
      app-config .swipeAltHeader {
        background-color: transparent;
        box-shadow: 0 1px 1px rgba(0,0,0,.25);
        height: 47px;
        margin: 0;
        overflow: hidden;
        padding: 0 0 0 56px;
        -webkit-user-select: none;
        -khtml-user-select: none;
        -moz-user-select: -moz-none;
        -o-user-select: none;
        user-select: none;
      }
      app-config.expanded .swipeAltHeader {
        color: #fff;
        background-color: #3e50b4;
      }
      app-config .foamChoiceListView.horizontal .choice {
        text-transform: uppercase;
        padding: 14px 16px;
        margin: 0;
      }
    */},
  ],
});
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Longtail Ad Solutions Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 **/

var VERSION = '1.0.2';
var OPTIONS, CONFIG;

/** Launch the app **/
App.onLaunch = function(opts) {
  OPTIONS = opts;
  console.log("Initing with options %o", OPTIONS);

  var scripts = [
    `${OPTIONS.baseURL}/js/utils/Utils.js`,
    `${OPTIONS.baseURL}/js/utils/Promise.js`,
    `${OPTIONS.baseURL}/js/utils/Http.js`,
    `${OPTIONS.baseURL}/js/events/EventBus.js`,
    `${OPTIONS.baseURL}/js/events/Events.js`,
    `${OPTIONS.baseURL}/js/ConfigLoader.js`,
    `${OPTIONS.baseURL}/js/he.js`,
    `${OPTIONS.baseURL}/js/TemplateLoader.js`,
    `${OPTIONS.baseURL}/js/ViewManager.js`,
    `${OPTIONS.baseURL}/js/playlist/PlaylistParser.js`,
    `${OPTIONS.baseURL}/js/playlist/PlaylistLoader.js`,
    `${OPTIONS.baseURL}/js/feeds/FeedLoader.js`,
    `${OPTIONS.baseURL}/js/playlist/PlaylistManager.js`,
    `${OPTIONS.baseURL}/js/analytics/TVOSAnalytics.js`,
    `${OPTIONS.baseURL}/js/playback/PlayerStates.js`,
    `${OPTIONS.baseURL}/js/playback/Playback.js`,
    `${OPTIONS.baseURL}/js/playback/AutoAdvance.js`
  ];

  evaluateScripts(scripts, function(success) {
    if (success) {
      configLoader = new ConfigLoader();
      configLoader.loadConfig(OPTIONS.account_key, configLoaded);
    } else {
      showAlert("Error", "Unable to evaluate scripts.")
    }
  });

}

App.onResume = function() {
  reloadPlaylists();
}

function reloadPlaylists() {
  PlaylistManager.reset();
  var templateLoader = new TemplateLoader();
  templateLoader.load("templates/ListCollection.tvml", function (templateDoc) {
    // Replace all the ListCollection docs on the stack.
    navigationDocument.documents.forEach(function (doc) {
      if (doc.firstChild) {
        var view = doc.firstChild.getAttribute("data-view");
        if (view && view === 'ListCollection') {
          navigationDocument.replaceDocument(templateDoc, doc);
        }
      }
    });
  });
}

function configLoaded(config) {
  CONFIG = config;
  EventBus.publish(Events.CONFIG_LOADED, {
    config: CONFIG
  });
  var templateLoader = new TemplateLoader();
  templateLoader.load("templates/index.tvml", function(templateDoc) {
    navigationDocument.pushDocument(templateDoc);
  });
}

function resetApp() {
  while (navigationDocument.documents.length > 0) {
    navigationDocument.removeDocument(navigationDocument.documents[0]);
  }
  App.onLaunch(OPTIONS);
}

function showAlert(alertTitle, alertText) {
  var alertTVML = `<?xml version="1.0" encoding="UTF-8" ?>
<document>
  <alertTemplate>
    <title>${alertTitle}</title>
    <description>${alertText}</description>
  </alertTemplate>
</document>`;

  var domParser = new DOMParser();
  var alertDoc = domParser.parseFromString(alertTVML, "application/xml");
  navigationDocument.pushDocument(alertDoc);
}
<commit_msg>Bumped the version number to 1.0.3<commit_after>/**
 * Copyright 2015 Longtail Ad Solutions Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 **/

var VERSION = '1.0.3';
var OPTIONS, CONFIG;

/** Launch the app **/
App.onLaunch = function(opts) {
  OPTIONS = opts;
  console.log("Initing with options %o", OPTIONS);

  var scripts = [
    `${OPTIONS.baseURL}/js/utils/Utils.js`,
    `${OPTIONS.baseURL}/js/utils/Promise.js`,
    `${OPTIONS.baseURL}/js/utils/Http.js`,
    `${OPTIONS.baseURL}/js/events/EventBus.js`,
    `${OPTIONS.baseURL}/js/events/Events.js`,
    `${OPTIONS.baseURL}/js/ConfigLoader.js`,
    `${OPTIONS.baseURL}/js/he.js`,
    `${OPTIONS.baseURL}/js/TemplateLoader.js`,
    `${OPTIONS.baseURL}/js/ViewManager.js`,
    `${OPTIONS.baseURL}/js/playlist/PlaylistParser.js`,
    `${OPTIONS.baseURL}/js/playlist/PlaylistLoader.js`,
    `${OPTIONS.baseURL}/js/feeds/FeedLoader.js`,
    `${OPTIONS.baseURL}/js/playlist/PlaylistManager.js`,
    `${OPTIONS.baseURL}/js/analytics/TVOSAnalytics.js`,
    `${OPTIONS.baseURL}/js/playback/PlayerStates.js`,
    `${OPTIONS.baseURL}/js/playback/Playback.js`,
    `${OPTIONS.baseURL}/js/playback/AutoAdvance.js`
  ];

  evaluateScripts(scripts, function(success) {
    if (success) {
      configLoader = new ConfigLoader();
      configLoader.loadConfig(OPTIONS.account_key, configLoaded);
    } else {
      showAlert("Error", "Unable to evaluate scripts.")
    }
  });

}

App.onResume = function() {
  reloadPlaylists();
}

function reloadPlaylists() {
  PlaylistManager.reset();
  var templateLoader = new TemplateLoader();
  templateLoader.load("templates/ListCollection.tvml", function (templateDoc) {
    // Replace all the ListCollection docs on the stack.
    navigationDocument.documents.forEach(function (doc) {
      if (doc.firstChild) {
        var view = doc.firstChild.getAttribute("data-view");
        if (view && view === 'ListCollection') {
          navigationDocument.replaceDocument(templateDoc, doc);
        }
      }
    });
  });
}

function configLoaded(config) {
  CONFIG = config;
  EventBus.publish(Events.CONFIG_LOADED, {
    config: CONFIG
  });
  var templateLoader = new TemplateLoader();
  templateLoader.load("templates/index.tvml", function(templateDoc) {
    navigationDocument.pushDocument(templateDoc);
  });
}

function resetApp() {
  while (navigationDocument.documents.length > 0) {
    navigationDocument.removeDocument(navigationDocument.documents[0]);
  }
  App.onLaunch(OPTIONS);
}

function showAlert(alertTitle, alertText) {
  var alertTVML = `<?xml version="1.0" encoding="UTF-8" ?>
<document>
  <alertTemplate>
    <title>${alertTitle}</title>
    <description>${alertText}</description>
  </alertTemplate>
</document>`;

  var domParser = new DOMParser();
  var alertDoc = domParser.parseFromString(alertTVML, "application/xml");
  navigationDocument.pushDocument(alertDoc);
}
<|endoftext|>


<commit_before>const path = require('path');
const fs = require('fs');
const ffmpeg = require('../src/services/ffmpeg');

const sampleVideoPath = `${process.cwd()}/__tests__/videos/SampleVideo.mp4`;
const sampleVideoCopyPath = `${process.cwd()}/__tests__/videos/SampleVideoCopy.mp4`;
const sampleSongPath = `${process.cwd()}/__tests__/music/jonathan-mann-i-wont-lock-it-down.mp3`;
const tempPath = `${process.cwd()}/temp/`;

afterEach(() => {
    // delete all files in temp, except .gitkeep
    const files = fs.readdirSync(tempPath);
    files.forEach((file) => {
        if (path.extname(file)) {
            fs.unlinkSync(tempPath + file);
        }
    });
});

test('get file metadata', () => ffmpeg.getFileMetadata(sampleVideoPath).then((metadata) => {
    expect(metadata).toBeTruthy();
}));

test('resize video', () => {
    const outputPath = `${tempPath}resize-video-test.mp4`;
    return ffmpeg.resizeVideo(sampleVideoPath, outputPath).then(() => {
        expect(fs.existsSync(outputPath)).toBeTruthy();
    });
});

test('concat videos', () => {
    const outputPath = `${tempPath}concat-video-test.mp4`;
    const paths = [sampleVideoPath, sampleVideoCopyPath];

    return ffmpeg.concatVideos(paths, outputPath, tempPath).then(() => {
        expect(fs.existsSync(outputPath)).toBeTruthy();
    });
});

test('add song to video', () => {
    const outputPath = `${tempPath}song-video-test.mp4`;
    return ffmpeg.addSongToVideo(sampleSongPath, sampleVideoPath, outputPath).then(() => {
        expect(fs.existsSync(outputPath)).toBeTruthy();
    });
});
<commit_msg>print ffprobe permission error in jest test<commit_after>const path = require('path');
const fs = require('fs');
const ffmpeg = require('../src/services/ffmpeg');
const ffprobePath = require('@ffprobe-installer/ffprobe').path;

const sampleVideoPath = `${process.cwd()}/__tests__/videos/SampleVideo.mp4`;
const sampleVideoCopyPath = `${process.cwd()}/__tests__/videos/SampleVideoCopy.mp4`;
const sampleSongPath = `${process.cwd()}/__tests__/music/jonathan-mann-i-wont-lock-it-down.mp3`;
const tempPath = `${process.cwd()}/temp/`;

afterEach(() => {
    // delete all files in temp, except .gitkeep
    const files = fs.readdirSync(tempPath);
    files.forEach((file) => {
        if (path.extname(file)) {
            fs.unlinkSync(tempPath + file);
        }
    });
});

test('get file metadata', () => {
    ffmpeg.getFileMetadata(sampleVideoPath)
    .then((metadata) => {
        expect(metadata).toBeTruthy();
    })
    .catch((error) => {
        if (error.message.indexOf('EACCES') >= 0) {
            console.log('Permission denied to execute ffprobe');
            console.log(`Please check the execution permissions of ${ffprobePath}`);
        }
        throw error;
    });
});

test('resize video', () => {
    const outputPath = `${tempPath}resize-video-test.mp4`;
    return ffmpeg.resizeVideo(sampleVideoPath, outputPath).then(() => {
        expect(fs.existsSync(outputPath)).toBeTruthy();
    });
});

test('concat videos', () => {
    const outputPath = `${tempPath}concat-video-test.mp4`;
    const paths = [sampleVideoPath, sampleVideoCopyPath];

    return ffmpeg.concatVideos(paths, outputPath, tempPath).then(() => {
        expect(fs.existsSync(outputPath)).toBeTruthy();
    });
});

test('add song to video', () => {
    const outputPath = `${tempPath}song-video-test.mp4`;
    return ffmpeg.addSongToVideo(sampleSongPath, sampleVideoPath, outputPath).then(() => {
        expect(fs.existsSync(outputPath)).toBeTruthy();
    });
});
<|endoftext|>