<commit_before>Core.Object.extend('Core.Socket',
/**@lends Core.Socket#*/
{
	host: undefined,
	service: '/',
	port: 80,
	mode: undefined,
	websocket: undefined,
	emuopenrequest: undefined,
	emupollrequest: undefined,
	emusendrequest: undefined,
	emuid: undefined,
	emumessages: undefined,
	lastPosition: 0,
	readSize: true,
	size: 0,
	data: '',
	isClosed: false,
	closeSent: false,

	/**
	*	@constructs
	*	@class
	*	@extends Core.Object
	*/
	constructor: function(config) {
		if(config.host != undefined) {
			this.host = config.host;
			delete(config.host);
		}
		else
			this.host = document.location.hostname;
		if(config.port != undefined) {
			this.port = config.port;
			delete(config.port);
		}
		else if((document.location.port != undefined) && (document.location.port != ''))
			this.port = document.location.port;
		else
			this.port = 80;
		if(config.service != undefined) {
			this.service = config.service;
			delete(config.service);
		}
		if(config.mode != undefined) {
			this.mode = config.mode;
			delete(config.mode);
		}
		else {
			if(Core.Socket.supportWebSocket)
				this.mode = 'websocket';
			else
				this.mode = 'poll';
		}

		if(this.mode == 'websocket') {
			this.websocket = new WebSocket('ws://'+this.host+':'+this.port+this.service);
			this.connect(this.websocket, 'open', this.onWebSocketOpen);
			this.connect(this.websocket, 'error', this.onWebSocketError);
			this.connect(this.websocket, 'message', this.onWebSocketMessage);
			this.connect(this.websocket, 'close', this.onWebSocketClose);
		}
		else {
			this.emumessages = [];
			this.emuopenrequest = new Core.HttpRequest({ url: 'http://'+this.host+':'+this.port+this.service+'?socket=poll&command=open' });
			this.connect(this.emuopenrequest, 'done', this.onEmuSocketOpenDone);
			this.connect(this.emuopenrequest, 'error', this.onEmuSocketOpenError);
			this.emuopenrequest.send();
		}
		this.addEvents('error', 'message', 'close', 'open');
	},

	send: function(msg) {
		if(this.websocket != undefined) {
			this.websocket.send(JSON.stringify(msg));
		}
		else {
			if(this.emusendrequest == undefined) {
				this.emusendrequest = new Core.HttpRequest({ url: 'http://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=send&id='+this.emuid+'&messages='+encodeURIComponent((JSON.stringify(msg)).toBase64()) });
				this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
				this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
				this.emusendrequest.send();
			}
			else
				this.emumessages.push((JSON.stringify(msg)).toBase64());
		}
	},

	close: function() {
		if(this.websocket != undefined) {
			this.isClosed = true;
			this.websocket.close();
		}
		else {
			if(!this.isClosed) {
				this.isClosed = true;
				if(this.emuopenrequest != undefined) {
					this.emuopenrequest.abort();
					this.emuopenrequest = undefined;
				}
				else if(this.emuid != undefined) {
					if(this.emusendrequest == undefined) {
						this.closeSent = true;
						this.emusendrequest = new Core.HttpRequest({ url: 'http://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=close&id='+this.emuid });
						this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
						this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
						this.emusendrequest.send();
					}
				}
			}
		}
	},

	/**#@+
	* @private
	*/
	onWebSocketOpen: function() {
		this.fireEvent('open', this);
	},

	onWebSocketError: function() {
		this.fireEvent('error', this);
	},

	onWebSocketMessage: function(msg) {
		if(msg.data == 'PING')
			this.websocket.send('PONG');
		else
			this.fireEvent('message', this, JSON.parse(msg.data));
	},

	onWebSocketClose: function(msg) {
		this.fireEvent('close', this);
	},

	emuSocketDataAvailable: function(data) {
		if(this.emuid == undefined) {
			this.emuid = data;
			this.fireEvent('open', this);
		}
		else {
			if(data != 'keepalive')
				this.fireEvent('message', this, JSON.parse(data.fromBase64()));
		}
	},

	emuSocketUpdate: function(delta) {
		for(var i = 0; i < delta.length; i++) {
			var character = delta[i];
			if(this.readSize) {
				if(character == ':') {
					this.readSize = false;
					this.size = parseInt('0x'+this.data);
					this.data = '';
				}
				else
					this.data += character;
			}
			else {
				this.data += character;
				if(this.data.length >= this.size + 2) {
					this.emuSocketDataAvailable(this.data.substring(0, this.data.length - 2));
					this.readSize = true;
					this.data = '';
				}
			}
		}
	},

	onEmuSocketSendDone: function() {
		var response = this.emusendrequest.getResponseJSON();
		if(this.emumessages.length > 0) {
			var messages = '';
			for(var i = 0; i < this.emumessages.length; i++) {
				if(messages != '')
					messages += ';';
				messages += this.emumessages[i];
			}
			this.emusendrequest = new Core.HttpRequest({ url: 'http://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=send&id='+this.emuid+'&messages='+encodeURIComponent(messages) });
			this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
			this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
			this.emusendrequest.send();
			this.emumessages = [];
		}
		else if(this.isClosed && !this.closeSent) {
			this.emusendrequest = new Core.HttpRequest({ url: 'http://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=close&id='+this.emuid });
			this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
			this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
			this.emusendrequest.send();
		}
		else
			this.emusendrequest = undefined;
	},

	onEmuSocketSendError: function() {
		this.emusendrequest = undefined;
	},

	onEmuSocketOpenDone: function() {
		var response = this.emuopenrequest.getResponseJSON();
		this.emuopenrequest = undefined;
		if(response == undefined) {
			this.fireEvent('error', this);
			this.fireEvent('close', this);
		}
		else {
			this.emuid = response.id;
			if(response.status != 'open') {
				this.fireEvent('error', this);
				this.fireEvent('close', this);
			}
			else {
				this.fireEvent('open', this);
				this.emupollrequest = new Core.HttpRequest({ url: 'http://'+this.host+':'+this.port+this.service+'?socket=poll&command=poll&id='+this.emuid });
				this.connect(this.emupollrequest, 'done', this.onEmuSocketPollDone);
				this.connect(this.emupollrequest, 'error', this.onEmuSocketPollError);
				this.emupollrequest.send();
			}
		}
	},

	onEmuSocketOpenError: function(request, status) {
		this.emuopenrequest = undefined;
		this.fireEvent('error', this);
		this.fireEvent('close', this);
	},

	onEmuSocketPollDone: function() {
		var response = this.emupollrequest.getResponseJSON();
		if(response == undefined) {
			this.close();
			this.fireEvent('close', this);
		}
		else {
			if(response.messages != undefined) {
				for(var i = 0; i < response.messages.length; i++) {
					var msg = JSON.parse(response.messages[i].fromBase64());
					this.fireEvent('message', this, msg);
				}
			}
			if(response.status != 'open') {
				this.close();
				this.fireEvent('close', this);
			}
			else {
				this.emupollrequest = new Core.HttpRequest({ url: 'http://'+this.host+':'+this.port+this.service+'?socket=poll&command=poll&id='+this.emuid });
				this.connect(this.emupollrequest, 'done', this.onEmuSocketPollDone);
				this.connect(this.emupollrequest, 'error', this.onEmuSocketPollError);
				this.emupollrequest.send();
			}
		}
	},

	onEmuSocketPollError: function() {
		this.emupollrequest = undefined;
		this.fireEvent('error', this);
		this.close();
	}
	/**#@-*/
});

Core.Socket.supportWebSocket = "WebSocket" in window;

<commit_msg>add secure support (HTTPS, WSS) for Core.Socket<commit_after>Core.Object.extend('Core.Socket',
/**@lends Core.Socket#*/
{
	host: undefined,
	service: '/',
	port: 80,
	mode: undefined,
	secure: false,
	websocket: undefined,
	emuopenrequest: undefined,
	emupollrequest: undefined,
	emusendrequest: undefined,
	emuid: undefined,
	emumessages: undefined,
	lastPosition: 0,
	readSize: true,
	size: 0,
	data: '',
	isClosed: false,
	closeSent: false,

	/**
	*	@constructs
	*	@class
	*	@extends Core.Object
	*/
	constructor: function(config) {
		if('host' in config) {
			this.host = config.host;
			delete(config.host);
		}
		else
			this.host = document.location.hostname;
		if('secure' in config) {
			this.secure = config.secure;
			delete(config.secure);
		}
		if('port' in config) {
			this.port = config.port;
			delete(config.port);
		}
		else if((document.location.port != undefined) && (document.location.port != ''))
			this.port = document.location.port;
		else {
			if(this.secure)
				this.port = 443;
			else
				this.port = 80;
		}
		if('service' in config) {
			this.service = config.service;
			delete(config.service);
		}
		if('mode' in config) {
			this.mode = config.mode;
			delete(config.mode);
		}
		else {
			if(Core.Socket.supportWebSocket)
				this.mode = 'websocket';
			else
				this.mode = 'poll';
		}

		if(this.mode == 'websocket') {
			this.websocket = new WebSocket((this.secure?'wss':'ws')+'://'+this.host+':'+this.port+this.service);
			this.connect(this.websocket, 'open', this.onWebSocketOpen);
			this.connect(this.websocket, 'error', this.onWebSocketError);
			this.connect(this.websocket, 'message', this.onWebSocketMessage);
			this.connect(this.websocket, 'close', this.onWebSocketClose);
		}
		else {
			this.emumessages = [];
			this.emuopenrequest = new Core.HttpRequest({ url: (this.secure?'https':'http')+'://'+this.host+':'+this.port+this.service+'?socket=poll&command=open' });
			this.connect(this.emuopenrequest, 'done', this.onEmuSocketOpenDone);
			this.connect(this.emuopenrequest, 'error', this.onEmuSocketOpenError);
			this.emuopenrequest.send();
		}
		this.addEvents('error', 'message', 'close', 'open');
	},

	send: function(msg) {
		if(this.websocket != undefined) {
			this.websocket.send(JSON.stringify(msg));
		}
		else {
			if(this.emusendrequest == undefined) {
				this.emusendrequest = new Core.HttpRequest({ url:  (this.secure?'https':'http')+'://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=send&id='+this.emuid+'&messages='+encodeURIComponent((JSON.stringify(msg)).toBase64()) });
				this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
				this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
				this.emusendrequest.send();
			}
			else
				this.emumessages.push((JSON.stringify(msg)).toBase64());
		}
	},

	close: function() {
		if(this.websocket != undefined) {
			this.isClosed = true;
			this.websocket.close();
		}
		else {
			if(!this.isClosed) {
				this.isClosed = true;
				if(this.emuopenrequest != undefined) {
					this.emuopenrequest.abort();
					this.emuopenrequest = undefined;
				}
				else if(this.emuid != undefined) {
					if(this.emusendrequest == undefined) {
						this.closeSent = true;
						this.emusendrequest = new Core.HttpRequest({ url:  (this.secure?'https':'http')+'://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=close&id='+this.emuid });
						this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
						this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
						this.emusendrequest.send();
					}
				}
			}
		}
	},

	/**#@+
	* @private
	*/
	onWebSocketOpen: function() {
		this.fireEvent('open', this);
	},

	onWebSocketError: function() {
		this.fireEvent('error', this);
	},

	onWebSocketMessage: function(msg) {
		if(msg.data == 'PING')
			this.websocket.send('PONG');
		else
			this.fireEvent('message', this, JSON.parse(msg.data));
	},

	onWebSocketClose: function(msg) {
		this.fireEvent('close', this);
	},

	emuSocketDataAvailable: function(data) {
		if(this.emuid == undefined) {
			this.emuid = data;
			this.fireEvent('open', this);
		}
		else {
			if(data != 'keepalive')
				this.fireEvent('message', this, JSON.parse(data.fromBase64()));
		}
	},

	emuSocketUpdate: function(delta) {
		for(var i = 0; i < delta.length; i++) {
			var character = delta[i];
			if(this.readSize) {
				if(character == ':') {
					this.readSize = false;
					this.size = parseInt('0x'+this.data);
					this.data = '';
				}
				else
					this.data += character;
			}
			else {
				this.data += character;
				if(this.data.length >= this.size + 2) {
					this.emuSocketDataAvailable(this.data.substring(0, this.data.length - 2));
					this.readSize = true;
					this.data = '';
				}
			}
		}
	},

	onEmuSocketSendDone: function() {
		var response = this.emusendrequest.getResponseJSON();
		if(this.emumessages.length > 0) {
			var messages = '';
			for(var i = 0; i < this.emumessages.length; i++) {
				if(messages != '')
					messages += ';';
				messages += this.emumessages[i];
			}
			this.emusendrequest = new Core.HttpRequest({ url:  (this.secure?'https':'http')+'://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=send&id='+this.emuid+'&messages='+encodeURIComponent(messages) });
			this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
			this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
			this.emusendrequest.send();
			this.emumessages = [];
		}
		else if(this.isClosed && !this.closeSent) {
			this.emusendrequest = new Core.HttpRequest({ url:  (this.secure?'https':'http')+'://'+this.host+':'+this.port+this.service+'?socket='+this.mode+'&command=close&id='+this.emuid });
			this.connect(this.emusendrequest, 'done', this.onEmuSocketSendDone);
			this.connect(this.emusendrequest, 'error', this.onEmuSocketSendError);
			this.emusendrequest.send();
		}
		else
			this.emusendrequest = undefined;
	},

	onEmuSocketSendError: function() {
		this.emusendrequest = undefined;
	},

	onEmuSocketOpenDone: function() {
		var response = this.emuopenrequest.getResponseJSON();
		this.emuopenrequest = undefined;
		if(response == undefined) {
			this.fireEvent('error', this);
			this.fireEvent('close', this);
		}
		else {
			this.emuid = response.id;
			if(response.status != 'open') {
				this.fireEvent('error', this);
				this.fireEvent('close', this);
			}
			else {
				this.fireEvent('open', this);
				this.emupollrequest = new Core.HttpRequest({ url:  (this.secure?'https':'http')+'://'+this.host+':'+this.port+this.service+'?socket=poll&command=poll&id='+this.emuid });
				this.connect(this.emupollrequest, 'done', this.onEmuSocketPollDone);
				this.connect(this.emupollrequest, 'error', this.onEmuSocketPollError);
				this.emupollrequest.send();
			}
		}
	},

	onEmuSocketOpenError: function(request, status) {
		this.emuopenrequest = undefined;
		this.fireEvent('error', this);
		this.fireEvent('close', this);
	},

	onEmuSocketPollDone: function() {
		var response = this.emupollrequest.getResponseJSON();
		if(response == undefined) {
			this.close();
			this.fireEvent('close', this);
		}
		else {
			if(response.messages != undefined) {
				for(var i = 0; i < response.messages.length; i++) {
					var msg = JSON.parse(response.messages[i].fromBase64());
					this.fireEvent('message', this, msg);
				}
			}
			if(response.status != 'open') {
				this.close();
				this.fireEvent('close', this);
			}
			else {
				this.emupollrequest = new Core.HttpRequest({ url:  (this.secure?'https':'http')+'://'+this.host+':'+this.port+this.service+'?socket=poll&command=poll&id='+this.emuid });
				this.connect(this.emupollrequest, 'done', this.onEmuSocketPollDone);
				this.connect(this.emupollrequest, 'error', this.onEmuSocketPollError);
				this.emupollrequest.send();
			}
		}
	},

	onEmuSocketPollError: function() {
		this.emupollrequest = undefined;
		this.fireEvent('error', this);
		this.close();
	}
	/**#@-*/
});

Core.Socket.supportWebSocket = "WebSocket" in window;

<|endoftext|>


<commit_before>const { BagItFile } = require('./bagit_file');
const { Constants } = require('../core/constants');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { Util } = require('../core/util');

test('Constructor sets initial properties', () => {
    let stats = fs.statSync(__filename);
    let f = new BagItFile(__filename, 'data/bagit_file.test.js', stats);
    expect(f.absSourcePath).toEqual(__filename);
    expect(f.relDestPath).toEqual('data/bagit_file.test.js');
    expect(f.size).toEqual(stats.size);
    expect(f.uid).toEqual(stats.uid);
    expect(f.gid).toEqual(stats.gid);
    expect(f.mtime).toEqual(stats.mtime);
    expect(f.isFile).toEqual(true);
    expect(f.isDirectory).toEqual(false);
    expect(f.fileType).toEqual(Constants.PAYLOAD_FILE);
    expect(f.checksums).toEqual({});
    expect(f.keyValueCollection).toBeNull();
});

test('getManifestEntry()', () => {
    let stats = fs.statSync(__filename);
    let f = new BagItFile(__filename, 'data/bagit_file.test.js', stats);
    f.checksums['md5'] = '1234';
    f.checksums['sha256'] = '5678';
    expect(f.getManifestEntry('md5')).toEqual("1234 data/bagit_file.test.js")
    expect(f.getManifestEntry('sha256')).toEqual("5678 data/bagit_file.test.js")
    expect(() => { f.getManifestEntry('md4') }).toThrow(Error);
});

test('getFileType', () => {
    expect(BagItFile.getFileType('data/file.txt')).toEqual(Constants.PAYLOAD_FILE);
    expect(BagItFile.getFileType('manifest-md5.txt')).toEqual(Constants.PAYLOAD_MANIFEST);
    expect(BagItFile.getFileType('tagmanifest-sha256.txt')).toEqual(Constants.TAG_MANIFEST);
    expect(BagItFile.getFileType('dpn-tags/file.txt')).toEqual(Constants.TAG_FILE);
});

test('isPayloadFile', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isPayloadFile()).toEqual(true);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isPayloadFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isPayloadFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isPayloadFile()).toEqual(false);
});

test('isPayloadManifest', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isPayloadManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isPayloadManifest()).toEqual(true);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isPayloadManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isPayloadManifest()).toEqual(false);
});

test('isTagFile', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isTagFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isTagFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isTagFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isTagFile()).toEqual(true);
});

test('isTagManifest', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isTagManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isTagManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isTagManifest()).toEqual(true);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isTagManifest()).toEqual(false);
});

test('getCryptoHash()', done => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    var md5 = f.getCryptoHash('md5', function(data) {
        expect(data.absSourcePath).toEqual('/path/to/file.txt');
        expect(data.relDestPath).toEqual('data/file.txt');
        expect(data.algorithm).toEqual('md5');
        expect(data.digest).toEqual('d4ff2da092d09cbc0ef62428b78d13b1');
        expect(f.checksums.md5).toEqual(data.digest);
        done();
    });
    expect(typeof md5).toEqual('object');

    // Pipe in the contents of a known file, so we get a predictable digest.
    var testFile = path.join(__dirname, '..', 'test', 'fixtures', 'bag-info.txt')
    var reader = fs.createReadStream(testFile);
    reader.pipe(md5);
});
<commit_msg>Attempt to fix Windows test problem<commit_after>const { BagItFile } = require('./bagit_file');
const { Constants } = require('../core/constants');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { Util } = require('../core/util');

test('Constructor sets initial properties', () => {
    let stats = fs.statSync(__filename);
    let f = new BagItFile(__filename, 'data/bagit_file.test.js', stats);
    expect(f.absSourcePath).toEqual(__filename);
    expect(f.relDestPath).toEqual('data/bagit_file.test.js');
    expect(f.size).toEqual(stats.size);
    expect(f.uid).toEqual(stats.uid);
    expect(f.gid).toEqual(stats.gid);
    expect(f.mtime).toEqual(stats.mtime);
    expect(f.isFile).toEqual(true);
    expect(f.isDirectory).toEqual(false);
    expect(f.fileType).toEqual(Constants.PAYLOAD_FILE);
    expect(f.checksums).toEqual({});
    expect(f.keyValueCollection).toBeNull();
});

test('getManifestEntry()', () => {
    let stats = fs.statSync(__filename);
    let f = new BagItFile(__filename, 'data/bagit_file.test.js', stats);
    f.checksums['md5'] = '1234';
    f.checksums['sha256'] = '5678';
    expect(f.getManifestEntry('md5')).toEqual("1234 data/bagit_file.test.js")
    expect(f.getManifestEntry('sha256')).toEqual("5678 data/bagit_file.test.js")
    expect(() => { f.getManifestEntry('md4') }).toThrow(Error);
});

test('getFileType', () => {
    expect(BagItFile.getFileType('data/file.txt')).toEqual(Constants.PAYLOAD_FILE);
    expect(BagItFile.getFileType('manifest-md5.txt')).toEqual(Constants.PAYLOAD_MANIFEST);
    expect(BagItFile.getFileType('tagmanifest-sha256.txt')).toEqual(Constants.TAG_MANIFEST);
    expect(BagItFile.getFileType('dpn-tags/file.txt')).toEqual(Constants.TAG_FILE);
});

test('isPayloadFile', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isPayloadFile()).toEqual(true);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isPayloadFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isPayloadFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isPayloadFile()).toEqual(false);
});

test('isPayloadManifest', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isPayloadManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isPayloadManifest()).toEqual(true);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isPayloadManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isPayloadManifest()).toEqual(false);
});

test('isTagFile', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isTagFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isTagFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isTagFile()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isTagFile()).toEqual(true);
});

test('isTagManifest', () => {
    let stats = fs.statSync(__filename);
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    expect(f.isTagManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'manifest-sha256.txt', stats);
    expect(f.isTagManifest()).toEqual(false);

    f = new BagItFile('/path/to/file.txt', 'tagmanifest-sha256.txt', stats);
    expect(f.isTagManifest()).toEqual(true);

    f = new BagItFile('/path/to/file.txt', 'custom-tags/random-tag-file.txt', stats);
    expect(f.isTagManifest()).toEqual(false);
});

test('getCryptoHash()', done => {
    let stats = fs.statSync(path.join(__dirname, '..', 'test', 'fixtures', 'tagmanifest-sha256.txt'));
    var f = new BagItFile('/path/to/file.txt', 'data/file.txt', stats);
    var md5 = f.getCryptoHash('md5', function(data) {
        expect(data.absSourcePath).toEqual('/path/to/file.txt');
        expect(data.relDestPath).toEqual('data/file.txt');
        expect(data.algorithm).toEqual('md5');
        expect(data.digest).toEqual('d4ff2da092d09cbc0ef62428b78d13b1');
        expect(f.checksums.md5).toEqual(data.digest);
        done();
    });
    expect(typeof md5).toEqual('object');

    // Pipe in the contents of a known file, so we get a predictable digest.
    var testFile = path.join(__dirname, '..', 'test', 'fixtures', 'bag-info.txt')
    var reader = fs.createReadStream(testFile);
    reader.pipe(md5);
});
<|endoftext|>


<commit_before>(function($, Vis) {
"use strict";

var busyDelay = 2500;

Vis.blocks['i-chart'] = {
    init: function(params) {
        var _this = this;

        _this.dimensions = {
            width: 0,
            height: 0
        };

        params = params || {};

        _this.title = params.title || "";

        var colorScheme = params.colorScheme || {};
        _this.colorScheme = Vis.create(
            colorScheme,
            colorScheme.name || 'i-tango-color-scheme'
        );

        _this.content = {
            tAxes: params.tAxes || [],
            xAxes: params.xAxes || [],
            yAxes: params.yAxes || [],
            items: params.items || [],
            overlays: params.overlays || [],
            layers: []
        };
        _this._initContent();

        // _this.applySize();
        // _this.initResize();
    },

    initTAxis: function(tAxisNo) { /* override me */ },
    _initTAxis: function(tAxisNo) {
        var _this = this,
            tAxis = _this.content.tAxes[tAxisNo];

        tAxis.rangeProvider = Vis.create(
            tAxis.rangeProvider,
            tAxis.rangeProvider.name || 'undefined-t-range-provider'
        );

        _this.initTAxis(tAxisNo);

        tAxis.rangeProvider.on('update', function() {
            _this._updateTAxisRange(tAxisNo);
        });
        _this._updateTAxisRange(tAxisNo);
    },

    updateTAxisRange: function(tAxisNo) { /* override me */ },
    _updateTAxisRange: function(tAxisNo) {
        var _this = this,
            tAxis = _this.content.tAxes[tAxisNo],
            range = tAxis.rangeProvider.get(),
            scale = tAxis.scale || {},
            items = _this.content.items;

        _this.updateTAxisRange(tAxisNo);

        if (range.min == scale.inputMin && range.max == scale.inputMax) {
            return;
        }

        tAxis.scale = {
            inputMin: range.min,
            inputMax: range.max
        };

        _this.updateTAxisRange(tAxisNo);
    },

    initTAxes: function() { /* override me */ },
    _initTAxes: function() {
        var _this = this,
            tAxes = _this.content.tAxes;

        for (var tAxisNo = 0, l = tAxes.length; tAxisNo < l; ++tAxisNo) {
            _this._initTAxis(tAxisNo);
        }

        _this.initTAxes();
    },

    _initItem: function(itemNo) {
        var _this = this,
            tAxes = _this.content.tAxes,
            xAxes = _this.content.xAxes,
            yAxes = _this.content.yAxes,
            item = _this.content.items[itemNo];

        if (typeof item.tAxisNo === 'undefined') {
            item.tAxisNo = 0;
        }
        item.tAxis = tAxes[item.tAxisNo] || Vis.error("No t-axis for item #" + itemNo);

        if (typeof item.xAxisNo === 'undefined') {
            item.xAxisNo = 0;
        }
        item.xAxis = xAxes[item.xAxisNo] || Vis.error("No x-axis for item #" + itemNo);

        if (typeof item.yAxisNo === 'undefined') {
            item.yAxisNo = 0;
        }
        item.yAxis = yAxes[item.yAxisNo] || Vis.error("No y-axis for item #" + itemNo);

        if (typeof item.color === 'undefined') {
            item.color = _this.colorScheme.get(itemNo);
        }

        if (typeof item.units === 'undefined') {
            item.units = "";
        }

        if (typeof item.dataProvider.timeRangeProvider === 'undefined') {
            item.dataProvider.timeRangeProvider = item.tAxis.rangeProvider;
        }
        item.dataProvider = Vis.create(
            item.dataProvider,
            item.dataProvider.name || 'undefined-item-data-provider'
        );
        item.dataProvider.on('update', function(e) {
            _this._updateItemData(itemNo);
        });

        if (!item.filters) {
            item.filters = [];
        }
        for (var i = 0, l = item.filters.length; i < l; ++i) {
            item.filters[i] = Vis.create(
                item.filters[i],
                item.filters[i].name
            );
        }

        item.on = function(action, callback) {
            $(item).on(action + '.vis', callback);
        };
        item.trigger = function(action) {
            $(item).trigger(action + '.vis');
        };

        item.renderData = {};

        _this._updateItemData(itemNo);
        // _requestItemData?
    },

    _updateItemData: function(itemNo) {
        var _this = this,
            item = _this.content.items[itemNo],
            filters = item.filters;

        item.rawData = item.dataProvider.get();

        var data = {};
        $.each(item.rawData, function(name, arr) {
            data[name] = arr.slice();
        });
        for (var i = 0, l = filters.length; i < l; ++i) {
            data = filters[i].run(item, data);
        }
        item.data = data;

        item.trigger('data');

        // override me
        this.render();
    },

    _initItems: function() {
        var _this = this,
            items = _this.content.items;

        for (var itemNo = 0, l = items.length; itemNo < l; ++itemNo) {
            _this._initItem(itemNo);
        }
    },

    _initXYAxis: function(xy, XY, pos, no) {
        var _this = this,
            tAxes = _this.content.tAxes,
            items = _this.content.items,
            axis = _this.content[xy + 'Axes'][no],
            i, l;

        var scale = axis.scale || {};
        axis.scale = Vis.create(
            scale,
            scale.name || 'i-scale-linear'
        );

        if (typeof axis.units === 'undefined') {
            axis.units = "";
        }

        var validPos = false;
        for (i = 0, l = pos.length; i < l; ++i) {
            if (axis.pos === pos[i]) {
                validPos = true;
                break;
            }
        }
        if (!validPos) {
            axis.pos = pos[0];
        }

        var axisItems = [];
        for (i = 0, l = items.length; i < l; ++i) {
            var item = items[i];
            if (item[xy + 'AxisNo'] != no) continue;

            axisItems.push(item);
        }
        axis.items = axisItems;

        axis.tAxis = tAxes[axis.tAxisNo || 0] || Vis.error("No t-axis for " + xy + "-axis #" + no);

        if (typeof axis.rangeProvider.timeRangeProvider === 'undefined') {
            axis.rangeProvider.timeRangeProvider = axis.tAxis.rangeProvider;
        }

        if (typeof axis.rangeProvider.xy === 'undefined') {
            axis.rangeProvider.xy = xy;
        }

        if (typeof axis.rangeProvider.items === 'undefined') {
            axis.rangeProvider.items = axisItems;
        }

        axis.rangeProvider = Vis.create(
            axis.rangeProvider,
            axis.rangeProvider.name || 'undefined-y-range-provider'
        );

        if (typeof axis.processors === 'undefined') {
            axis.processors = [];
        }
        for (i = 0, l = axis.processors.length; i < l; ++i) {
            var processor = axis.processors[i];
            processor.dimensions = _this.dimensions;
            processor.content = _this.content;
            axis.processors[i] = Vis.create(processor, processor.name);
        }

        _this['init' + XY + 'Axis'](no);

        axis.rangeProvider.on('update', function() {
            _this['_update' + XY + 'AxisRange'](no);
        });
        _this['_update' + XY + 'AxisRange'](no);
    },

    _updateXYAxisRange: function(xy, XY, no) {
        var _this = this,
            axis = _this.content[xy + 'Axes'][no],
            range = axis.rangeProvider.get(),
            scale = axis.scale;

        if (range.min == scale.inputMin && range.max == scale.inputMax) {
            return;
        }

        scale.input(range.min, range.max);

        _this['update' + XY + 'AxisRange'](no);

        _this['_render' + XY + 'Axis'](no);
    },

    _renderXYAxis: function(xy, XY, no, n, a, b) {
        var _this = this,
            axis = this.content[xy + 'Axes'][no];

        axis.ticks = axis.scale.ticks(n, axis.units);

        var ticks = Units.formatTicks(axis.ticks, axis.units, axis.scale);
        for (var i = 0, l = ticks.length; i < l; ++i) {
            var tick = ticks[i];
            tick.offset = b + a*Math.round(axis.scale.f(tick.tickValue));
        }

        _this['render' + XY + 'Axis'](no, ticks);
    },

    initXAxis: function(xAxisNo) { /* override me */ },
    _initXAxis: function(xAxisNo) {
        this._initXYAxis('x', 'X', ['bottom', 'top'], xAxisNo);
    },

    updateXAxisRange: function(xAxisNo) { /* override me */ },
    _updateXAxisRange: function(xAxisNo) {
        this._updateXYAxisRange('x', 'X', xAxisNo);
    },

    renderXAxis: function(xAxisNo, ticks) { /* override me */ },
    _renderXAxis: function(xAxisNo) {
        this._renderXYAxis('x', 'X', xAxisNo, Math.floor(this.dimensions.width / 65), 1, 0);
    },

    initXAxes: function() { /* override me */ },
    _initXAxes: function() {
        var _this = this,
            xAxes = _this.content.xAxes;

        for (var xAxisNo = 0, l = xAxes.length; xAxisNo < l; ++xAxisNo) {
            _this._initXAxis(xAxisNo);
        }

        _this.initXAxes();
    },

    initYAxis: function(yAxisNo) { /* override me */ },
    _initYAxis: function(yAxisNo) {
        this._initXYAxis('y', 'Y', ['right', 'left'], yAxisNo);
    },

    updateYAxisRange: function(yAxisNo) { /* override me */ },
    _updateYAxisRange: function(yAxisNo) {
        this._updateXYAxisRange('y', 'Y', yAxisNo);
    },

    renderYAxis: function(yAxisNo, ticks) { /* override me */ },
    _renderYAxis: function(yAxisNo) {
        this._renderXYAxis('y', 'Y', yAxisNo, Math.floor(this.dimensions.height / 40), -1, this.dimensions.height);
    },

    initYAxes: function() { /* override me */ },
    _initYAxes: function() {
        var _this = this,
            yAxes = _this.content.yAxes;

        for (var yAxisNo = 0, l = yAxes.length; yAxisNo < l; ++yAxisNo) {
            _this._initYAxis(yAxisNo);
        }

        _this.initYAxes();
    },

    initOverlay: function(overlayNo) { /* override me */ },
    _initOverlay: function(overlayNo) {
        var _this = this,
            overlays = _this.content.overlays,
            overlay = overlays[overlayNo];

        overlay.dimensions = _this.dimensions;
        overlay.content = _this.content;
        overlays[overlayNo] = Vis.create(
            overlay,
            overlay.name || 'undefined-overlay'
        );

        _this.initOverlay(overlayNo);
    },

    _initOverlays: function() {
        var _this = this,
            overlays = _this.content.overlays;

        for (var i = 0, l = overlays.length; i < l; ++i) {
            _this._initOverlay(i);
        }
    },

    initContent: function () { /* override me */ },
    _initContent: function () {
        this._initTAxes();
        this._initItems();
        this._initXAxes();
        this._initYAxes();
        this._initOverlays();

        this.initContent();
    },

    applySize: function () { /* override me */ },
    _applySize: function() {
        var _this = this,
            dim = _this.dimensions,
            xAxes = _this.content.xAxes,
            yAxes = _this.content.yAxes,
            i, l;

        for (i = 0, l = xAxes.length; i < l; ++i) {
            xAxes[i].scale.output(0, dim.width - 1);
        }

        for (i = 0, l = yAxes.length; i < l; ++i) {
            yAxes[i].scale.output(0, dim.height - 1);
        }

        _this.applySize();
    },

    _updateItem: function(itemNo) {
        var _this = this,
            item = _this.content.items[itemNo];

        _this._updateItemData(item);
        item.ready = true;
        _this.renderItem(itemNo);
    },

    _requestItemData: function(item) {
        var _this = this,
            xAxis = _this.content.xAxes[item.xAxis || 0] || _this.content.xAxes[0];

        item.ready = false;
        item.dataProvider.range(xAxis.scale.inputMin, xAxis.scale.inputMax);
    },

    _runProcessors: function() {
        var _this = this,
            yAxes = _this.content.yAxes,
            items = _this.content.items;

        for (var yAxisNo = 0, l = yAxes.length; yAxisNo < l; ++yAxisNo) {
            var yAxis = yAxes[yAxisNo];

            for (var i = 0; i < yAxis.items.length; ++i) {
                yAxis.items[i].renderData = yAxis.items[i].data;
            }

            for (var j = 0, m = yAxis.processors.length; j < m; ++j) {
                yAxis.processors[j].run(yAxis.items);
            }

            for (var k = 0; k < yAxis.items.length; ++k) {
                yAxis.items[k].trigger('renderData');
            }
        }
    },

    _beforeRender: function() {
        this._runProcessors();
    },

    _afterRender: function() {
    },

    renderItem: function(itemNo) {
        if (this._init != -1) {
            return;
        }

        this.render();
    }
};

})(jQuery, Vis);
<commit_msg>Fixed Y axis ticks position<commit_after>(function($, Vis) {
"use strict";

var busyDelay = 2500;

Vis.blocks['i-chart'] = {
    init: function(params) {
        var _this = this;

        _this.dimensions = {
            width: 0,
            height: 0
        };

        params = params || {};

        _this.title = params.title || "";

        var colorScheme = params.colorScheme || {};
        _this.colorScheme = Vis.create(
            colorScheme,
            colorScheme.name || 'i-tango-color-scheme'
        );

        _this.content = {
            tAxes: params.tAxes || [],
            xAxes: params.xAxes || [],
            yAxes: params.yAxes || [],
            items: params.items || [],
            overlays: params.overlays || [],
            layers: []
        };
        _this._initContent();

        // _this.applySize();
        // _this.initResize();
    },

    initTAxis: function(tAxisNo) { /* override me */ },
    _initTAxis: function(tAxisNo) {
        var _this = this,
            tAxis = _this.content.tAxes[tAxisNo];

        tAxis.rangeProvider = Vis.create(
            tAxis.rangeProvider,
            tAxis.rangeProvider.name || 'undefined-t-range-provider'
        );

        _this.initTAxis(tAxisNo);

        tAxis.rangeProvider.on('update', function() {
            _this._updateTAxisRange(tAxisNo);
        });
        _this._updateTAxisRange(tAxisNo);
    },

    updateTAxisRange: function(tAxisNo) { /* override me */ },
    _updateTAxisRange: function(tAxisNo) {
        var _this = this,
            tAxis = _this.content.tAxes[tAxisNo],
            range = tAxis.rangeProvider.get(),
            scale = tAxis.scale || {},
            items = _this.content.items;

        _this.updateTAxisRange(tAxisNo);

        if (range.min == scale.inputMin && range.max == scale.inputMax) {
            return;
        }

        tAxis.scale = {
            inputMin: range.min,
            inputMax: range.max
        };

        _this.updateTAxisRange(tAxisNo);
    },

    initTAxes: function() { /* override me */ },
    _initTAxes: function() {
        var _this = this,
            tAxes = _this.content.tAxes;

        for (var tAxisNo = 0, l = tAxes.length; tAxisNo < l; ++tAxisNo) {
            _this._initTAxis(tAxisNo);
        }

        _this.initTAxes();
    },

    _initItem: function(itemNo) {
        var _this = this,
            tAxes = _this.content.tAxes,
            xAxes = _this.content.xAxes,
            yAxes = _this.content.yAxes,
            item = _this.content.items[itemNo];

        if (typeof item.tAxisNo === 'undefined') {
            item.tAxisNo = 0;
        }
        item.tAxis = tAxes[item.tAxisNo] || Vis.error("No t-axis for item #" + itemNo);

        if (typeof item.xAxisNo === 'undefined') {
            item.xAxisNo = 0;
        }
        item.xAxis = xAxes[item.xAxisNo] || Vis.error("No x-axis for item #" + itemNo);

        if (typeof item.yAxisNo === 'undefined') {
            item.yAxisNo = 0;
        }
        item.yAxis = yAxes[item.yAxisNo] || Vis.error("No y-axis for item #" + itemNo);

        if (typeof item.color === 'undefined') {
            item.color = _this.colorScheme.get(itemNo);
        }

        if (typeof item.units === 'undefined') {
            item.units = "";
        }

        if (typeof item.dataProvider.timeRangeProvider === 'undefined') {
            item.dataProvider.timeRangeProvider = item.tAxis.rangeProvider;
        }
        item.dataProvider = Vis.create(
            item.dataProvider,
            item.dataProvider.name || 'undefined-item-data-provider'
        );
        item.dataProvider.on('update', function(e) {
            _this._updateItemData(itemNo);
        });

        if (!item.filters) {
            item.filters = [];
        }
        for (var i = 0, l = item.filters.length; i < l; ++i) {
            item.filters[i] = Vis.create(
                item.filters[i],
                item.filters[i].name
            );
        }

        item.on = function(action, callback) {
            $(item).on(action + '.vis', callback);
        };
        item.trigger = function(action) {
            $(item).trigger(action + '.vis');
        };

        item.renderData = {};

        _this._updateItemData(itemNo);
        // _requestItemData?
    },

    _updateItemData: function(itemNo) {
        var _this = this,
            item = _this.content.items[itemNo],
            filters = item.filters;

        item.rawData = item.dataProvider.get();

        var data = {};
        $.each(item.rawData, function(name, arr) {
            data[name] = arr.slice();
        });
        for (var i = 0, l = filters.length; i < l; ++i) {
            data = filters[i].run(item, data);
        }
        item.data = data;

        item.trigger('data');

        // override me
        this.render();
    },

    _initItems: function() {
        var _this = this,
            items = _this.content.items;

        for (var itemNo = 0, l = items.length; itemNo < l; ++itemNo) {
            _this._initItem(itemNo);
        }
    },

    _initXYAxis: function(xy, XY, pos, no) {
        var _this = this,
            tAxes = _this.content.tAxes,
            items = _this.content.items,
            axis = _this.content[xy + 'Axes'][no],
            i, l;

        var scale = axis.scale || {};
        axis.scale = Vis.create(
            scale,
            scale.name || 'i-scale-linear'
        );

        if (typeof axis.units === 'undefined') {
            axis.units = "";
        }

        var validPos = false;
        for (i = 0, l = pos.length; i < l; ++i) {
            if (axis.pos === pos[i]) {
                validPos = true;
                break;
            }
        }
        if (!validPos) {
            axis.pos = pos[0];
        }

        var axisItems = [];
        for (i = 0, l = items.length; i < l; ++i) {
            var item = items[i];
            if (item[xy + 'AxisNo'] != no) continue;

            axisItems.push(item);
        }
        axis.items = axisItems;

        axis.tAxis = tAxes[axis.tAxisNo || 0] || Vis.error("No t-axis for " + xy + "-axis #" + no);

        if (typeof axis.rangeProvider.timeRangeProvider === 'undefined') {
            axis.rangeProvider.timeRangeProvider = axis.tAxis.rangeProvider;
        }

        if (typeof axis.rangeProvider.xy === 'undefined') {
            axis.rangeProvider.xy = xy;
        }

        if (typeof axis.rangeProvider.items === 'undefined') {
            axis.rangeProvider.items = axisItems;
        }

        axis.rangeProvider = Vis.create(
            axis.rangeProvider,
            axis.rangeProvider.name || 'undefined-y-range-provider'
        );

        if (typeof axis.processors === 'undefined') {
            axis.processors = [];
        }
        for (i = 0, l = axis.processors.length; i < l; ++i) {
            var processor = axis.processors[i];
            processor.dimensions = _this.dimensions;
            processor.content = _this.content;
            axis.processors[i] = Vis.create(processor, processor.name);
        }

        _this['init' + XY + 'Axis'](no);

        axis.rangeProvider.on('update', function() {
            _this['_update' + XY + 'AxisRange'](no);
        });
        _this['_update' + XY + 'AxisRange'](no);
    },

    _updateXYAxisRange: function(xy, XY, no) {
        var _this = this,
            axis = _this.content[xy + 'Axes'][no],
            range = axis.rangeProvider.get(),
            scale = axis.scale;

        if (range.min == scale.inputMin && range.max == scale.inputMax) {
            return;
        }

        scale.input(range.min, range.max);

        _this['update' + XY + 'AxisRange'](no);

        _this['_render' + XY + 'Axis'](no);
    },

    _renderXYAxis: function(xy, XY, no, n, a, b) {
        var _this = this,
            axis = this.content[xy + 'Axes'][no];

        axis.ticks = axis.scale.ticks(n, axis.units);

        var ticks = Units.formatTicks(axis.ticks, axis.units, axis.scale);
        for (var i = 0, l = ticks.length; i < l; ++i) {
            var tick = ticks[i];
            tick.offset = b + a*Math.round(axis.scale.f(tick.tickValue));
        }

        _this['render' + XY + 'Axis'](no, ticks);
    },

    initXAxis: function(xAxisNo) { /* override me */ },
    _initXAxis: function(xAxisNo) {
        this._initXYAxis('x', 'X', ['bottom', 'top'], xAxisNo);
    },

    updateXAxisRange: function(xAxisNo) { /* override me */ },
    _updateXAxisRange: function(xAxisNo) {
        this._updateXYAxisRange('x', 'X', xAxisNo);
    },

    renderXAxis: function(xAxisNo, ticks) { /* override me */ },
    _renderXAxis: function(xAxisNo) {
        this._renderXYAxis('x', 'X', xAxisNo, Math.floor(this.dimensions.width / 65), 1, 0);
    },

    initXAxes: function() { /* override me */ },
    _initXAxes: function() {
        var _this = this,
            xAxes = _this.content.xAxes;

        for (var xAxisNo = 0, l = xAxes.length; xAxisNo < l; ++xAxisNo) {
            _this._initXAxis(xAxisNo);
        }

        _this.initXAxes();
    },

    initYAxis: function(yAxisNo) { /* override me */ },
    _initYAxis: function(yAxisNo) {
        this._initXYAxis('y', 'Y', ['right', 'left'], yAxisNo);
    },

    updateYAxisRange: function(yAxisNo) { /* override me */ },
    _updateYAxisRange: function(yAxisNo) {
        this._updateXYAxisRange('y', 'Y', yAxisNo);
    },

    renderYAxis: function(yAxisNo, ticks) { /* override me */ },
    _renderYAxis: function(yAxisNo) {
        this._renderXYAxis('y', 'Y', yAxisNo, Math.floor(this.dimensions.height / 40), -1, this.dimensions.height - 1);
    },

    initYAxes: function() { /* override me */ },
    _initYAxes: function() {
        var _this = this,
            yAxes = _this.content.yAxes;

        for (var yAxisNo = 0, l = yAxes.length; yAxisNo < l; ++yAxisNo) {
            _this._initYAxis(yAxisNo);
        }

        _this.initYAxes();
    },

    initOverlay: function(overlayNo) { /* override me */ },
    _initOverlay: function(overlayNo) {
        var _this = this,
            overlays = _this.content.overlays,
            overlay = overlays[overlayNo];

        overlay.dimensions = _this.dimensions;
        overlay.content = _this.content;
        overlays[overlayNo] = Vis.create(
            overlay,
            overlay.name || 'undefined-overlay'
        );

        _this.initOverlay(overlayNo);
    },

    _initOverlays: function() {
        var _this = this,
            overlays = _this.content.overlays;

        for (var i = 0, l = overlays.length; i < l; ++i) {
            _this._initOverlay(i);
        }
    },

    initContent: function () { /* override me */ },
    _initContent: function () {
        this._initTAxes();
        this._initItems();
        this._initXAxes();
        this._initYAxes();
        this._initOverlays();

        this.initContent();
    },

    applySize: function () { /* override me */ },
    _applySize: function() {
        var _this = this,
            dim = _this.dimensions,
            xAxes = _this.content.xAxes,
            yAxes = _this.content.yAxes,
            i, l;

        for (i = 0, l = xAxes.length; i < l; ++i) {
            xAxes[i].scale.output(0, dim.width - 1);
        }

        for (i = 0, l = yAxes.length; i < l; ++i) {
            yAxes[i].scale.output(0, dim.height - 1);
        }

        _this.applySize();
    },

    _updateItem: function(itemNo) {
        var _this = this,
            item = _this.content.items[itemNo];

        _this._updateItemData(item);
        item.ready = true;
        _this.renderItem(itemNo);
    },

    _requestItemData: function(item) {
        var _this = this,
            xAxis = _this.content.xAxes[item.xAxis || 0] || _this.content.xAxes[0];

        item.ready = false;
        item.dataProvider.range(xAxis.scale.inputMin, xAxis.scale.inputMax);
    },

    _runProcessors: function() {
        var _this = this,
            yAxes = _this.content.yAxes,
            items = _this.content.items;

        for (var yAxisNo = 0, l = yAxes.length; yAxisNo < l; ++yAxisNo) {
            var yAxis = yAxes[yAxisNo];

            for (var i = 0; i < yAxis.items.length; ++i) {
                yAxis.items[i].renderData = yAxis.items[i].data;
            }

            for (var j = 0, m = yAxis.processors.length; j < m; ++j) {
                yAxis.processors[j].run(yAxis.items);
            }

            for (var k = 0; k < yAxis.items.length; ++k) {
                yAxis.items[k].trigger('renderData');
            }
        }
    },

    _beforeRender: function() {
        this._runProcessors();
    },

    _afterRender: function() {
    },

    renderItem: function(itemNo) {
        if (this._init != -1) {
            return;
        }

        this.render();
    }
};

})(jQuery, Vis);
<|endoftext|>


<commit_before>var KVO = function() {
	this.bound = {};
	this.observer = {};
};

KVO.prototype.setKey = "_kvo";

KVO.prototype.didSet = function(id, prop, value) {

};

KVO.prototype.didGet = function(id, prop, value) {

};

KVO.prototype.wrapSetter = function(id, prop, setter) {
	var self = this;

	return function(value) {
		setter.call(this, value);
		self.didSet(this, prop, value);
	};
};

KVO.prototype.generateSetter = function(obj, prop) {
	var self = this;
	var _kvo = this.setKey;

	return function(value) {
		this[_kvo][prop] = value;
		self.didSet(this, prop, value);
	}.bind(obj);
};

KVO.prototype.wrapGetter = function(obj, prop, getter) {
	var self = this;

	return function() {
		var value = getter.call(this);
		self.didGet(this, prop, value);
		return value;
	}.bind(obj);
};

KVO.prototype.generateGetter = function(obj, prop) {
	var self = this;
	var _kvo = this.setKey;

	return function() {
		var value = this[_kvo][prop];
		self.didGet(this, prop, value);
		return value;
	}.bind(obj);
};

KVO.prototype.convert = function(obj, prop) {
	if(this.isBound(obj) === false) {
		this.bind(obj);
	}

	var hasSet = hasGet = false;

	var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
	if(typeof descriptor !== "undefined") {
		var hasSet = typeof descriptor.set !== "undefined";
		var hasGet = typeof descriptor.get !== "undefined";

		if((hasSet && !hasGet) || (hasGet && !hasSet)) {
			console.dir(obj);
			throw new Error("Must have both a setter and a getter for prop ("+prop+")");
			return false;
		}
	}

	var _kvo = this.setKey;
	if(typeof obj[_kvo][prop] === "undefined") {
		obj[_kvo][prop] = obj[prop];
	}

	var setter, getter;
	if(hasSet && hasGet) {
		setter = this.wrapSetter(obj, prop, descriptor.set);
		getter = this.wrapGetter(obj, prop, descriptor.get);
	} else {
		setter = this.generateSetter(obj, prop);
		getter = this.generateGetter(obj, prop);
	}

	Object.defineProperty(obj, prop, {
		set: setter,
		get: getter
	});
	return true;
};

KVO.prototype.isBound = function(obj) {
	var _kvo = this.setKey;
	if(typeof obj[_kvo] === "undefined") {
		return false;
	}

	var id = obj[_kvo]["_kvoid"];
	return typeof this.bound[id] !== "undefined";
};

KVO.prototype.bind = function(obj) {
	var id;
	do {
		id = Math.random().toString(36).substr(2, 5);
	}while(typeof this.bound[id] !== "undefined")

	var _kvo = this.setKey;
	obj[_kvo] = { "_kvoid":id };
	this.bound[id] = obj;
}

<commit_msg>Adds didGet and didSet hooks<commit_after>var KVO = function() {
	this.bound = {};
	this.observers = {};
};

KVO.prototype.setKey = "_kvo";

KVO.prototype.do_didSet = function(id, prop, value) {
	this.observers[id].didSet[prop].forEach(function(obs) {
		obs(value);
	});
};

KVO.prototype.do_didGet = function(id, prop, value) {
	this.observers[id].didGet[prop].forEach(function(obs){
		obs(value);
	});
};;

//KVO.prototype.didSet = function()

KVO.prototype.wrapSetter = function(id, prop, setter) {
	var self = this;

	return function(value) {
		setter.call(this, value);
		self.do_didSet(id, prop, value);
	};
};

KVO.prototype.generateSetter = function(id, prop) {
	var self = this;
	var _kvo = this.setKey;

	return function(value) {
		this[_kvo][prop] = value;
		self.do_didSet(id, prop, value);
	};
};

KVO.prototype.wrapGetter = function(id, prop, getter) {
	var self = this;

	return function() {
		var value = getter.call(this);
		self.do_didGet(id, prop, value);
		return value;
	};
};

KVO.prototype.generateGetter = function(id, prop) {
	var self = this;
	var _kvo = this.setKey;

	return function() {
		var value = this[_kvo][prop];
		self.do_didGet(id, prop, value);
		return value;
	};
};

KVO.prototype.convert = function(obj, prop) {
	if(this.isBound(obj) === false) {
		this.bind(obj);
	}

	var hasSet = hasGet = false;

	var descriptor = Object.getOwnPropertyDescriptor(obj, prop);
	if(typeof descriptor !== "undefined") {
		var hasSet = typeof descriptor.set !== "undefined";
		var hasGet = typeof descriptor.get !== "undefined";

		if((hasSet && !hasGet) || (hasGet && !hasSet)) {
			console.dir(obj);
			throw new Error("Must have both a setter and a getter for prop ("+prop+")");
			return false;
		}
	}

	var _kvo = this.setKey;
	var id = obj[_kvo]["_kvoid"];

	obj[_kvo][prop] = obj[prop];
	this.observers[id].didSet[prop] = [];
	this.observers[id].didGet[prop] = [];

	var setter, getter;
	if(hasSet && hasGet) {
		setter = this.wrapSetter(id, prop, descriptor.set);
		getter = this.wrapGetter(id, prop, descriptor.get);
	} else {
		setter = this.generateSetter(id, prop);
		getter = this.generateGetter(id, prop);
	}

	Object.defineProperty(obj, prop, {
		set: setter,
		get: getter
	});
	return true;
};

KVO.prototype.isBound = function(obj) {
	var _kvo = this.setKey;
	if(typeof obj[_kvo] === "undefined") {
		return false;
	}

	var id = obj[_kvo]["_kvoid"];
	return typeof this.bound[id] !== "undefined";
};

KVO.prototype.bind = function(obj) {
	var id;
	do {
		id = Math.random().toString(36).substr(2, 5);
	}while(typeof this.bound[id] !== "undefined");

	var _kvo = this.setKey;
	obj[_kvo] = { "_kvoid":id };
	this.bound[id] = obj;

	this.observers[id] = {
		didSet:{},
		didGet:{}
	};
}

<|endoftext|>


<commit_before>/** @jsxImportSource theme-ui */

// gallery

import Image from 'next/image'
import { useQuery, gql } from '@apollo/client'
import Snuggle from 'react-snuggle'
import { Box, AspectRatio } from 'theme-ui'
import { useRouter } from 'next/router'
import Loading from './loading'
import Squiggle from './squiggle'

// lightbox
import SimpleReactLightbox, { SRLWrapper } from 'simple-react-lightbox'

const QUERY = gql`
  query ($title: String) {
    galleryCollection(where: { title: $title }) {
      items {
        title
        imagesCollection {
          items {
            url(transform: { width: 900, quality: 60 })
            loader: url(transform: { width: 5, quality: 1 })
            title
            width
            height
          }
        }
      }
    }
  }
`

export default function Gallery(props) {
  const router = useRouter()
  const pathname = router.asPath

  const { data, loading, error } = useQuery(QUERY, {
    variables: {
      title: props.imageset,
    },
  })

  if (loading) {
    return (
      <Box sx={{ width: 'fit-content', margin: '0 auto' }}>
        <Loading />
        <Squiggle />
      </Box>
    )
  }

  if (error) {
    console.error(error)
    return null
  }

  const imageSetTitle = data.galleryCollection.items[0].title
  const imageSetImages = data.galleryCollection.items[0].imagesCollection.items

  const options = {
    settings: {
      overlayColor: 'rgba(0, 0, 0, 0.9)',
      autoplaySpeed: 0,
      hideControlsAfter: false,
      disablePanzoom: true,
    },
    buttons: {
      backgroundColor: 'white',
      iconColor: 'black',
      showDownloadButton: false,
    },
    caption: {
      showCaption: false,
    },
    thumbnails: {
      showThumbnails: false,
    },
  }

  const callbacks = {
    onSlideChange: object => handleSlideChange(object),
    onLightboxOpened: object => handleLightboxOpen(object),
    onLightboxClosed: object => handleLightboxClose(object),
  }

  function handleSlideChange(object) {
    if (typeof window !== 'undefined') {
      global.analytics.track('Lightbox Slide Changed', {
        src: object.slides.current.source,
        location: pathname,
        direction: object.action,
      })
    }

    return object
  }

  function handleLightboxOpen(object) {
    if (typeof window !== 'undefined') {
      global.analytics.track('Lightbox Opened', {
        src: object.currentSlide.source,
        location: pathname,
      })
    }

    return object
  }

  function handleLightboxClose(object) {
    if (typeof window !== 'undefined') {
      global.analytics.track('Lightbox Closed', {
        src: object.currentSlide.source,
        location: pathname,
      })
    }

    return object
  }

  const columnWidth =
    imageSetImages.length == 1
      ? ''
      : imageSetImages.length == 2
      ? '450'
      : imageSetImages.length == 3
      ? '350'
      : '225'

  return (
    <SimpleReactLightbox>
      <SRLWrapper options={options} callbacks={callbacks}>
        <Box sx={{ mx: 'auto' }} id="gallery">
          <Snuggle columnWidth={columnWidth}>
            {imageSetImages.map(image => (
              <AspectRatio key={image} ratio={eval(props.ratio)}>
                <Image
                  src={image.url}
                  alt={image.title}
                  layout="fill"
                  placeholder="blur"
                  blurDataURL={image.loader}
                  objectFit="cover"
                  sx={{
                    borderRadius: columnWidth == 1 ? '4px' : '',
                    boxShadow: 'card',
                  }}
                />
              </AspectRatio>
            ))}
          </Snuggle>
        </Box>
      </SRLWrapper>
    </SimpleReactLightbox>
  )
}
<commit_msg>fix: better masonry for galleries<commit_after>/** @jsxImportSource theme-ui */

// gallery

import Image from 'next/image'
import { useQuery, gql } from '@apollo/client'
import { Box } from 'theme-ui'
import { useRouter } from 'next/router'
import { Grid } from 'mauerwerk'
import useMeasure from 'react-use-measure'
import Loading from './loading'
import Squiggle from './squiggle'

// lightbox
import SimpleReactLightbox, {
  SRLWrapper,
  useLightbox,
} from 'simple-react-lightbox'

const QUERY = gql`
  query ($title: String) {
    galleryCollection(where: { title: $title }) {
      items {
        title
        imagesCollection {
          items {
            url(transform: { width: 900, quality: 60 })
            loader: url(transform: { width: 5, quality: 1 })
            title
            width
            height
          }
        }
      }
    }
  }
`

export default function Gallery(props) {
  const router = useRouter()
  const pathname = router.asPath
  const [ref, bounds] = useMeasure()
  const { openLightbox } = useLightbox()

  const { data, loading, error } = useQuery(QUERY, {
    variables: {
      title: props.imageset,
    },
  })

  if (loading) {
    return (
      <Box sx={{ width: 'fit-content', margin: '0 auto' }}>
        <Loading />
        <Squiggle />
      </Box>
    )
  }

  if (error) {
    console.error(error)
    return null
  }

  const imageSetImages = data.galleryCollection.items[0].imagesCollection.items

  const options = {
    settings: {
      overlayColor: 'rgba(0, 0, 0, 0.9)',
      autoplaySpeed: 0,
      hideControlsAfter: false,
      disablePanzoom: true,
    },
    buttons: {
      backgroundColor: 'white',
      iconColor: 'black',
      showDownloadButton: false,
    },
    caption: {
      showCaption: false,
    },
    thumbnails: {
      showThumbnails: false,
    },
  }

  const callbacks = {
    onSlideChange: object => handleSlideChange(object),
    onLightboxOpened: object => handleLightboxOpen(object),
    onLightboxClosed: object => handleLightboxClose(object),
  }

  function handleSlideChange(object) {
    if (typeof window !== 'undefined') {
      global.analytics.track('Lightbox Slide Changed', {
        src: object.slides.current.source,
        location: pathname,
        direction: object.action,
      })
    }

    return object
  }

  function handleLightboxOpen(object) {
    if (typeof window !== 'undefined') {
      global.analytics.track('Lightbox Opened', {
        src: object.currentSlide.source,
        location: pathname,
      })
    }

    return object
  }

  function handleLightboxClose(object) {
    if (typeof window !== 'undefined') {
      global.analytics.track('Lightbox Closed', {
        src: object.currentSlide.source,
        location: pathname,
      })
    }

    return object
  }

  const length = imageSetImages.length <= 4 ? imageSetImages.length : 4

  return (
    <Box ref={ref}>
      <SimpleReactLightbox>
        <SRLWrapper options={options} callbacks={callbacks}>
          <Grid
            className="grid"
            // Arbitrary data, should contain keys, possibly heights, etc.
            data={imageSetImages}
            // Key accessor, instructs grid on how to fet individual keys from the data set
            keys={d => d.title}
            // Can be a fixed value or an individual data accessor
            heights={d => {
              const aspect = props.ratio
                ? eval(props.ratio) * bounds.width
                : ((d.height / d.width) * bounds.width) / length
              console.log(aspect)
              return aspect
            }}
            columns={length}
          >
            {data => (
              <Image
                src={data.url}
                alt={data.title}
                layout="fill"
                placeholder="blur"
                blurDataURL={data.loader}
                objectFit="cover"
                width={data.width}
                height={data.height}
                onClick={() => openLightbox(data.index)}
              />
            )}
          </Grid>
        </SRLWrapper>
      </SimpleReactLightbox>
    </Box>
  )
}
<|endoftext|>


<commit_before>var app = require('http').createServer(function(){})
  , redis = require('redis')
  , _db = redis.createClient()
  , _md = require("node-markdown").Markdown
  , IO = require('socket.io').listen(app)
  , QS = require('querystring')
  , Chat = require('./chat')
  , Channel = require('./channel')
  , HTTP = require('http');

_db.select(1);
app.listen(1985);
Chat.setDB(_db);
Channel.setDB(_db);

function uidgen() {
  return (Math.random() * Math.pow(2,63)).toString(36);
}

var search = (function(){

  function process(data, cb) {
    var results = [], title, artist, split;
    if(data.feed.entry) {
      data.feed.entry.forEach(function(result) {
        split = result.title.$t.split('-');
        if(split.length == 1) {
          artist = "";
          title = split[0];
        } else {
          artist = split.shift();
          title = split.join('-');
        }
        results.push({
          vid: 'yt:' + result.media$group.yt$videoid.$t,
          title: title,
          artist: artist,
          len: result.media$group.yt$duration.seconds
        });
      });
    }
    cb(false, results);
  }

  function remote(query, cb) {
    var
      results = [],
      req = HTTP.request({
        host: 'gdata.youtube.com',
        path: '/feeds/api/videos?' + QS.stringify({
          alt: 'json',
          q: query,
          orderby: 'relevance',
          'max-results': 10,
          v: 2,
          format: 5
        })
      }, function(res) {
        res.setEncoding('utf8');
        res.on('data', function(data) { results.push(data); });
        res.on('end', function(){ process(JSON.parse(results.join('')), cb); });
      });

    req.on('error', function(){ cb(arguments, fale); });
    req.end();
  }

  return function(query, cb) {
    var local = [];
    remote(query, function(err, res) {
      var idList = res.map(function(row) { return row.vid });
      _db.hmget("vid", idList, function(err, data) {
        if(data) {
          for(var ix = data.length - 1; ix >= 0; ix--) {
            if(data[ix]) {
              local.push(res[ix]);
              res.splice(ix, 1);
            }
          }
        }
        cb(false, {
          local: local,
          remote: res,
          total: local.length + res.length
        });
      });
    });
  }

})();

IO.sockets.on('connection', function (socket) {
  var
    _user = {},
    _song = {},
    _online = -1,
    _ival = {};

  var _channel = {

    join: function(which) {
      Channel.get(which, function(){
        _channel.leave();
        _user.channel = which;
        socket.emit("channel-name", which);
        if(_user.uid) {
          Channel.join(which, _user.uid);
        }
      });
    },

    leave: function() {
      Channel.leave(_user.channel, _user.uid);
    },

    play: function(params) {
      _db.set("request", JSON.stringify(params));
    }
  };

  function song() {
    if(!_user.channel) {
      return;
    }
    _db.set("user:" + _user.channel + ":" + _user.uid, 1);
    _db.expire("user:" + _user.channel + ":" + _user.uid, 10);

    _db.hget("play", _user.channel, function(err, last) {
      if(!last) {
        return;
      }
      var song = JSON.parse(last);
      if(song.vid == _song.vid) {
        return;
      } else {
        _song = song;
        socket.emit("song", [
          song.vid,
          song.len,
          song.offset.toFixed(3),
          song.start,
          song.volume,
          song.artist,
          song.title,
          song.notes
        ]);
      }
    });
  }

  function poll() {
    if(!_user.channel) {
      return;
    }
    _db.get("ix", function(err, last) {

      _db.lrange(
        "log:" + _user.channel,
        0, -1,
        function(err, data) {
          var
            row,
            chat = [];

          data.forEach(function(rowRaw) {
            row = JSON.parse(rowRaw);
            if(row._id > _user.lastid) {
              _user.lastid = row._id;
              chat.push(row);
            }
          })
          if(chat.length) {
            socket.emit("chat", chat);
          }
        });
    });

    _db.keys("user:" + _user.channel + ":*", function(err, all) {
      var online = all.length;
      if(online != _online) {
        _online = online;
        socket.emit("stats", {online: online});
      }
    });
  }

  function announce(message) {
    Chat.add(
      _user.channel,
      'announce',
      message
    );
  }

  socket.on("disconnect", function(){
    if(_user.name && _user.name != "anonymous") {
      announce(_user.name + " logged out");
    }
    for(var which in _ival) {
      clearInterval(_ival[which]);
    };
    _channel.leave();
  });

  socket.on("channel-join", function(obj){
    console.log(obj);
    _channel.join(obj.channel);
  });

  socket.on("channel-create", function(obj) {
    Channel.create(obj.channel);
  });

  socket.on("get-channels", function(obj) {
    _db.hvals("channel", function(err, channelList) {

      for(var ix = 0; ix < channelList.length; ix++) {
        channelList[ix] = JSON.parse(channelList[ix]);
      }

      socket.emit("channel-results",
        channelList.sort(function(a, b) {
          return (b.count || 0) - (a.count || 0);
        })
      );
    });
  });

  socket.on("greet-response", function(p) {
    _user = p;
    _user.name = "anonymous";

    if(!_user.uid) {
      _user.uid = uidgen();
      socket.emit("uid", _user.uid);
    } else {
      _db.hget("user", _user.uid, function(err, last) {
        if(last) {
          _user.name = last;
          socket.emit("username", _user.name);
          announce(_user.name + " logged in");
        } else {
          socket.emit("username", false);
        }
      });
    }

    if(!_ival.poll) {
      _ival.poll = setInterval(poll, 50);
      _ival.song = setInterval(song, 1000);
      song();
    }
  });

  socket.on("search", function(q) {
    search(q, function(err, res) {
      socket.emit("song-results", {
        query: q,
        results: res
      });
    });
  });

  socket.on("video-play", function(p) {
    p.channel = _user.channel;
    p.name = _user.name;
    p.now = false;
    _db.lpush("request", JSON.stringify(p));
  });

  socket.on("video-play-now", function(p) {
    p.channel = _user.channel;
    p.name = _user.name;
    p.now = true;
    _db.lpush("request", JSON.stringify(p));
  });

  socket.on("delist", function(p) {
    _db.lpush("request", JSON.stringify({
      action: "delist",
      track: p,
      name: _user.name,
      channel: _user.channel
    }));
  });

  socket.on("skip", function(p) {
    _db.lpush("request", JSON.stringify({
      track: p,
      action: "skip",
      name: _user.name,
      channel: _user.channel
    }));
  });

  socket.on("get-history", function(p) {
    var chan = _user.channel;
    _db.lrange("lastplayed:" + chan, 0, -1, function(err, last){
      for(var ix = 0; ix < last.length; ix++) {
        last[ix] = JSON.parse(last[ix]);
      }
      socket.emit("history", {
        channel: chan,
        results: {
          total: last.length,
          history: last.reverse()
        }
      });
    });
  });

  socket.on("set-user", function(p) {
    if(p.user) {
      if(_user.name != "anonymous") {
        announce(_user.name + " is now known as " + p.user);
      } else {
        announce(p.user + " logged in");
      }
      _user.name = p.user;
      _db.hset("user", _user.uid, p.user);
      socket.emit("username", _user.name);
    } else {
      announce(_user.name + " logged out");
      _db.hdel("user", _user.uid);
      _user.name = "anonymous";
    }
  });

  socket.on("get-all-videos", function() {
    _db.lrange("pl:" + _user.channel, 0, -1, function(err, list) {
      // This means an empty channel, it's probably an error
      // and we should probably handle it better than this.
      // But at least we aren't crashing.
      if(list.length == 0) {
        socket.emit("all-videos", {
          channel: _user.channel,
          data: [[]]
        });
        return;
      }
      _db.hmget("vid", list, function(err, allvideos) {
        for(var ix = 0; ix < allvideos.length; ix++) {
          allvideos[ix] = JSON.parse(allvideos[ix]);
          allvideos[ix].unshift(list[ix]);
        }
        socket.emit("all-videos", {
          channel: _user.channel,
          data: allvideos
        });
      });
    });
  });

  socket.on("chat", function(data) {
    Chat.add(_user.channel, {
      type: "chat",
      text: _md(data.d
        .replace(/</g, '<')
        .replace(/>/g, '>')),
      vid: data.vid,
      who: _user.name,
      offset: data.offset
    });
  });

  setTimeout(function(){
    if(!_user.color) {
      socket.emit("greet-request");
    }
  }, 1000);
});
<commit_msg>might be a title guess fix<commit_after>var app = require('http').createServer(function(){})
  , redis = require('redis')
  , _db = redis.createClient()
  , _md = require("node-markdown").Markdown
  , IO = require('socket.io').listen(app)
  , QS = require('querystring')
  , Chat = require('./chat')
  , Channel = require('./channel')
  , HTTP = require('http');

_db.select(1);
app.listen(1985);
Chat.setDB(_db);
Channel.setDB(_db);

function uidgen() {
  return (Math.random() * Math.pow(2,63)).toString(36);
}

var search = (function(){

  function process(data, cb) {
    var results = [], title, artist, split;
    if(data.feed.entry) {
      data.feed.entry.forEach(function(result) {
        split = result.title.$t.split(' - ');
        if(split.length == 1) {
          artist = "";
          title = split[0];
        } else {
          artist = split.shift();
          title = split.join(' - ');
        }
        results.push({
          vid: 'yt:' + result.media$group.yt$videoid.$t,
          title: title,
          artist: artist,
          len: result.media$group.yt$duration.seconds
        });
      });
    }
    cb(false, results);
  }

  function remote(query, cb) {
    var
      results = [],
      req = HTTP.request({
        host: 'gdata.youtube.com',
        path: '/feeds/api/videos?' + QS.stringify({
          alt: 'json',
          q: query,
          orderby: 'relevance',
          'max-results': 10,
          v: 2,
          format: 5
        })
      }, function(res) {
        res.setEncoding('utf8');
        res.on('data', function(data) { results.push(data); });
        res.on('end', function(){ process(JSON.parse(results.join('')), cb); });
      });

    req.on('error', function(){ cb(arguments, fale); });
    req.end();
  }

  return function(query, cb) {
    var local = [];
    remote(query, function(err, res) {
      var idList = res.map(function(row) { return row.vid });
      _db.hmget("vid", idList, function(err, data) {
        if(data) {
          for(var ix = data.length - 1; ix >= 0; ix--) {
            if(data[ix]) {
              local.push(res[ix]);
              res.splice(ix, 1);
            }
          }
        }
        cb(false, {
          local: local,
          remote: res,
          total: local.length + res.length
        });
      });
    });
  }

})();

IO.sockets.on('connection', function (socket) {
  var
    _user = {},
    _song = {},
    _online = -1,
    _ival = {};

  var _channel = {

    join: function(which) {
      Channel.get(which, function(){
        _channel.leave();
        _user.channel = which;
        socket.emit("channel-name", which);
        if(_user.uid) {
          Channel.join(which, _user.uid);
        }
      });
    },

    leave: function() {
      Channel.leave(_user.channel, _user.uid);
    },

    play: function(params) {
      _db.set("request", JSON.stringify(params));
    }
  };

  function song() {
    if(!_user.channel) {
      return;
    }
    _db.set("user:" + _user.channel + ":" + _user.uid, 1);
    _db.expire("user:" + _user.channel + ":" + _user.uid, 10);

    _db.hget("play", _user.channel, function(err, last) {
      if(!last) {
        return;
      }
      var song = JSON.parse(last);
      if(song.vid == _song.vid) {
        return;
      } else {
        _song = song;
        socket.emit("song", [
          song.vid,
          song.len,
          song.offset.toFixed(3),
          song.start,
          song.volume,
          song.artist,
          song.title,
          song.notes
        ]);
      }
    });
  }

  function poll() {
    if(!_user.channel) {
      return;
    }
    _db.get("ix", function(err, last) {

      _db.lrange(
        "log:" + _user.channel,
        0, -1,
        function(err, data) {
          var
            row,
            chat = [];

          data.forEach(function(rowRaw) {
            row = JSON.parse(rowRaw);
            if(row._id > _user.lastid) {
              _user.lastid = row._id;
              chat.push(row);
            }
          })
          if(chat.length) {
            socket.emit("chat", chat);
          }
        });
    });

    _db.keys("user:" + _user.channel + ":*", function(err, all) {
      var online = all.length;
      if(online != _online) {
        _online = online;
        socket.emit("stats", {online: online});
      }
    });
  }

  function announce(message) {
    Chat.add(
      _user.channel,
      'announce',
      message
    );
  }

  socket.on("disconnect", function(){
    if(_user.name && _user.name != "anonymous") {
      announce(_user.name + " logged out");
    }
    for(var which in _ival) {
      clearInterval(_ival[which]);
    };
    _channel.leave();
  });

  socket.on("channel-join", function(obj){
    console.log(obj);
    _channel.join(obj.channel);
  });

  socket.on("channel-create", function(obj) {
    Channel.create(obj.channel);
  });

  socket.on("get-channels", function(obj) {
    _db.hvals("channel", function(err, channelList) {

      for(var ix = 0; ix < channelList.length; ix++) {
        channelList[ix] = JSON.parse(channelList[ix]);
      }

      socket.emit("channel-results",
        channelList.sort(function(a, b) {
          return (b.count || 0) - (a.count || 0);
        })
      );
    });
  });

  socket.on("greet-response", function(p) {
    _user = p;
    _user.name = "anonymous";

    if(!_user.uid) {
      _user.uid = uidgen();
      socket.emit("uid", _user.uid);
    } else {
      _db.hget("user", _user.uid, function(err, last) {
        if(last) {
          _user.name = last;
          socket.emit("username", _user.name);
          announce(_user.name + " logged in");
        } else {
          socket.emit("username", false);
        }
      });
    }

    if(!_ival.poll) {
      _ival.poll = setInterval(poll, 50);
      _ival.song = setInterval(song, 1000);
      song();
    }
  });

  socket.on("search", function(q) {
    search(q, function(err, res) {
      socket.emit("song-results", {
        query: q,
        results: res
      });
    });
  });

  socket.on("video-play", function(p) {
    p.channel = _user.channel;
    p.name = _user.name;
    p.now = false;
    _db.lpush("request", JSON.stringify(p));
  });

  socket.on("video-play-now", function(p) {
    p.channel = _user.channel;
    p.name = _user.name;
    p.now = true;
    _db.lpush("request", JSON.stringify(p));
  });

  socket.on("delist", function(p) {
    _db.lpush("request", JSON.stringify({
      action: "delist",
      track: p,
      name: _user.name,
      channel: _user.channel
    }));
  });

  socket.on("skip", function(p) {
    _db.lpush("request", JSON.stringify({
      track: p,
      action: "skip",
      name: _user.name,
      channel: _user.channel
    }));
  });

  socket.on("get-history", function(p) {
    var chan = _user.channel;
    _db.lrange("lastplayed:" + chan, 0, -1, function(err, last){
      for(var ix = 0; ix < last.length; ix++) {
        last[ix] = JSON.parse(last[ix]);
      }
      socket.emit("history", {
        channel: chan,
        results: {
          total: last.length,
          history: last.reverse()
        }
      });
    });
  });

  socket.on("set-user", function(p) {
    if(p.user) {
      if(_user.name != "anonymous") {
        announce(_user.name + " is now known as " + p.user);
      } else {
        announce(p.user + " logged in");
      }
      _user.name = p.user;
      _db.hset("user", _user.uid, p.user);
      socket.emit("username", _user.name);
    } else {
      announce(_user.name + " logged out");
      _db.hdel("user", _user.uid);
      _user.name = "anonymous";
    }
  });

  socket.on("get-all-videos", function() {
    _db.lrange("pl:" + _user.channel, 0, -1, function(err, list) {
      // This means an empty channel, it's probably an error
      // and we should probably handle it better than this.
      // But at least we aren't crashing.
      if(list.length == 0) {
        socket.emit("all-videos", {
          channel: _user.channel,
          data: [[]]
        });
        return;
      }
      _db.hmget("vid", list, function(err, allvideos) {
        for(var ix = 0; ix < allvideos.length; ix++) {
          allvideos[ix] = JSON.parse(allvideos[ix]);
          allvideos[ix].unshift(list[ix]);
        }
        socket.emit("all-videos", {
          channel: _user.channel,
          data: allvideos
        });
      });
    });
  });

  socket.on("chat", function(data) {
    Chat.add(_user.channel, {
      type: "chat",
      text: _md(data.d
        .replace(/</g, '<')
        .replace(/>/g, '>')),
      vid: data.vid,
      who: _user.name,
      offset: data.offset
    });
  });

  setTimeout(function(){
    if(!_user.color) {
      socket.emit("greet-request");
    }
  }, 1000);
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * After prepare, files are copied to the platforms/ios and platforms/android folders.
 * Lets clean up some of those files that arent needed with this hook.
 */
var fs = require('fs');
var path = require('path');

var deleteFolderRecursive = function(removePath) {
  if( fs.existsSync(removePath) ) {
    fs.readdirSync(removePath).forEach(function(file,index){
      var curPath = path.join(removePath, file);
      console.log('Curr path', curPath)
      if(fs.lstatSync(curPath).isDirectory()) { // recurse
        deleteFolderRecursive(curPath);
      } else { // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(removePath);
  }
};

var iosPlatformsDir = path.resolve(__dirname, '../../platforms/ios/www/lib/ionic/scss');
var androidPlatformsDir = path.resolve(__dirname, '../../platforms/android/assets/www/lib/ionic/scss');

deleteFolderRecursive(iosPlatformsDir);
deleteFolderRecursive(androidPlatformsDir);
<commit_msg>Removing console.log command from removing sass files<commit_after>#!/usr/bin/env node

/**
 * After prepare, files are copied to the platforms/ios and platforms/android folders.
 * Lets clean up some of those files that arent needed with this hook.
 */
var fs = require('fs');
var path = require('path');

var deleteFolderRecursive = function(removePath) {
  if( fs.existsSync(removePath) ) {
    fs.readdirSync(removePath).forEach(function(file,index){
      var curPath = path.join(removePath, file);
      if(fs.lstatSync(curPath).isDirectory()) { // recurse
        deleteFolderRecursive(curPath);
      } else { // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(removePath);
  }
};

var iosPlatformsDir = path.resolve(__dirname, '../../platforms/ios/www/lib/ionic/scss');
var androidPlatformsDir = path.resolve(__dirname, '../../platforms/android/assets/www/lib/ionic/scss');

deleteFolderRecursive(iosPlatformsDir);
deleteFolderRecursive(androidPlatformsDir);
<|endoftext|>


<commit_before>// Common utilities.
var coalesce = require('nascent.coalesce')

// Control-flow utilities.
var cadence = require('cadence')

// Sencha Connect middleware builder.
var Dispatcher = require('inlet/dispatcher')

// Event wrapper around our consensus algorithm.
var Kibitzer = require('kibitz')

// Construct an http responder for the given `Kibitzer` that forwards out of
// band messages to the given Conduit `Requester`.

//
function Middleware (startedAt, island, kibitzer, colleague) {
    var dispatcher = new Dispatcher(this)
    dispatcher.dispatch('GET /', 'index')
    dispatcher.dispatch('POST /oob', 'outOfBand')
    dispatcher.dispatch('POST /kibitz', 'kibitz')
    dispatcher.dispatch('GET /health', 'health')
    this.dispatcher = dispatcher
    this._startedAt = startedAt
    this._island = island
    this._kibitzer = kibitzer
    this._colleague = colleague
}

// Return an index message.

//
Middleware.prototype.index = cadence(function (async) {
    return 'Compassion Colleague API\n'
})

// Forward out of band requests to our Conduit `Requester`.

//
Middleware.prototype.outOfBand = cadence(function (async, request) {
    console.log('MIDDLEWARE OOB', request.body)
    this._colleague.outOfBand(request.body, async())
})

// Pass Kibitz envelopes to our `Kibitzer`.

//
Middleware.prototype.kibitz = cadence(function (async, request) {
        this._kibitzer.request(request.body, async())
})

// Report on the health and provide general info for bootstrap discovery.

//
Middleware.prototype.health = cadence(function (async) {
    return {
        dispatcher: this.dispatcher.turnstile.health,
        startedAt: this._startedAt,
        island: this._island,
        id: this._kibitzer.paxos.id,
        republic: coalesce(this._kibitzer.paxos.republic),
        government: this._kibitzer.paxos.government
    }
})

// Export as constructor.
module.exports = Middleware
<commit_msg>Add promise to colleague health.<commit_after>// Common utilities.
var coalesce = require('nascent.coalesce')

// Control-flow utilities.
var cadence = require('cadence')

// Sencha Connect middleware builder.
var Dispatcher = require('inlet/dispatcher')

// Event wrapper around our consensus algorithm.
var Kibitzer = require('kibitz')

// Construct an http responder for the given `Kibitzer` that forwards out of
// band messages to the given Conduit `Requester`.

//
function Middleware (startedAt, island, kibitzer, colleague) {
    var dispatcher = new Dispatcher(this)
    dispatcher.dispatch('GET /', 'index')
    dispatcher.dispatch('POST /oob', 'outOfBand')
    dispatcher.dispatch('POST /kibitz', 'kibitz')
    dispatcher.dispatch('GET /health', 'health')
    this.dispatcher = dispatcher
    this._startedAt = startedAt
    this._island = island
    this._kibitzer = kibitzer
    this._colleague = colleague
}

// Return an index message.

//
Middleware.prototype.index = cadence(function (async) {
    return 'Compassion Colleague API\n'
})

// Forward out of band requests to our Conduit `Requester`.

//
Middleware.prototype.outOfBand = cadence(function (async, request) {
    console.log('MIDDLEWARE OOB', request.body)
    this._colleague.outOfBand(request.body, async())
})

// Pass Kibitz envelopes to our `Kibitzer`.

//
Middleware.prototype.kibitz = cadence(function (async, request) {
        this._kibitzer.request(request.body, async())
})

// Report on the health and provide general info for bootstrap discovery.

//
Middleware.prototype.health = cadence(function (async) {
    return {
        dispatcher: this.dispatcher.turnstile.health,
        startedAt: this._startedAt,
        island: this._island,
        id: this._kibitzer.paxos.id,
        promise: this._kibitzer.paxos.government.promise,
        republic: coalesce(this._kibitzer.paxos.republic),
        government: this._kibitzer.paxos.government
    }
})

// Export as constructor.
module.exports = Middleware
<|endoftext|>


<commit_before>
Ui.MouseOverable.extend('Ui.Carousel',
/**@lends Ui.Carousel#*/
{
	carouselable: undefined,
	buttonNext: undefined,
	buttonNextIcon: undefined,
	buttonPrevious: undefined,
	buttonPreviousIcon: undefined,
	showClock: undefined,
//	hideClock: undefined,
	hideTimeout: undefined,

	showNext: false,
	showPrevious: false,

	/**
	 * @constructs
     * @class
	 * @extends Ui.MouseOverable
     */
	constructor: function(config) {
		this.addEvents('change', 'press');

		this.connect(this, 'enter', this.onMouseEnter);
		this.connect(this, 'leave', this.onMouseLeave);
		this.connect(this, 'move', this.onMouseOverMove);

		this.carouselable = new Ui.Carouselable();
		Ui.Carousel.base.append.call(this, this.carouselable);
		this.connect(this.carouselable, 'focus', this.onCarouselableFocus);
		this.connect(this.carouselable, 'blur', this.onCarouselableBlur);
		this.connect(this.carouselable, 'change', this.onCarouselableChange);
		this.connect(this.carouselable, 'press', this.onCarouselablePress);

		this.buttonPrevious = new Ui.Pressable({ horizontalAlign: 'left', verticalAlign: 'center', opacity: 0 });
		this.buttonPrevious.setFocusable(false);
		this.buttonPreviousIcon = new Ui.Icon({ icon: 'arrowleft', width: 48, height: 48 });
		this.buttonPrevious.append(this.buttonPreviousIcon);
		Ui.Carousel.base.append.call(this, this.buttonPrevious);
//		this.buttonPrevious.hide();
		this.connect(this.buttonPrevious, 'press', this.onPreviousPress);

		this.buttonNext = new Ui.Pressable({ horizontalAlign: 'right', verticalAlign: 'center', opacity: 0 });
		this.buttonNext.setFocusable(false);
		this.buttonNextIcon = new Ui.Icon({ icon: 'arrowright', width: 48, height: 48 });
		this.buttonNext.append(this.buttonNextIcon);
		Ui.Carousel.base.append.call(this, this.buttonNext);
//		this.buttonNext.hide();
		this.connect(this.buttonNext, 'press', this.onNextPress);
	},

	setAutoPlay: function(delay) {
		this.carouselable.setAutoPlay(delay);
	},

	next: function() {
		this.carouselable.next();
	},

	previous: function() {
		this.carouselable.previous();
	},

	getLogicalChildren: function() {
		return this.carouselable.getLogicalChildren();
	},

	getCurrentPosition: function() {
		return this.carouselable.getCurrentPosition();
	},

	getCurrent: function() {
		return this.carouselable.getCurrent();
	},

	/**#@+
	* @private
	*/

	onCarouselablePress: function(carouselable) {
		this.fireEvent('press', this);
	},

	onCarouselableChange: function(carouselable, position) {
		this.showArrows();
		this.fireEvent('change', this, position);
	},

	onCarouselableFocus: function() {
		this.showArrows();
	},

	onCarouselableBlur: function() {
		this.hideArrows();
	},

	onPreviousPress: function() {
		this.carouselable.focus();
		this.previous();
	},

	onNextPress: function() {
		this.carouselable.focus();
		this.next();
	},

	onMouseEnter: function() {
		this.showArrows();
	},

	onMouseOverMove: function() {
		this.showArrows();
	},

	onMouseLeave: function() {
		this.hideArrows();
	},

	showArrows: function() {
		if(this.hideTimeout != undefined) {
			this.hideTimeout.abort();
			this.hideTimeout = new Core.DelayedTask({ delay: 2, scope: this, callback: this.hideArrows });
		}
		else
			this.hideTimeout = new Core.DelayedTask({ delay: 2, scope: this, callback: this.hideArrows });

		var pos = this.carouselable.getCurrentPosition();
		var children = this.carouselable.getLogicalChildren();

		if(children.length > 0) {
			this.showPrevious = (pos > 0);
			this.showNext = (pos < children.length - 1);
		}
		else {
			this.showPrevious = false;
			this.showNext = false;
		}

		if(this.showClock === undefined) {
			this.showClock = new Anim.Clock({ duration: 'forever', scope: this, target: this, onTimeupdate: this.onShowTick });
			this.showClock.begin();
		}
	},

	hideArrows: function() {
		if(this.hideTimeout != undefined) {
			this.hideTimeout.abort();
			this.hideTimeout = undefined;
		}
		this.showPrevious = false;
		this.showNext = false;
		if(this.showClock === undefined) {
			this.showClock = new Anim.Clock({ duration: 'forever', scope: this, target: this, onTimeupdate: this.onShowTick });
			this.showClock.begin();
		}
	},

	onShowTick: function(clock, progress, delta) {
		if(delta == 0)
			return;

		var previousDone = false;
		if(this.showPrevious) {
			var opacity = this.buttonPrevious.getOpacity();
			opacity = Math.min(opacity + delta, 1);
			this.buttonPrevious.setOpacity(opacity);
			if(opacity == 1)
				previousDone = true;
		}
		else {
			var opacity = this.buttonPrevious.getOpacity();
			opacity = Math.max(opacity - (delta * 2), 0);
			this.buttonPrevious.setOpacity(opacity);
			if(opacity == 0)
				previousDone = true;
		}

		var nextDone = false;
		if(this.showNext) {
			var opacity = this.buttonNext.getOpacity();
			opacity = Math.min(opacity + delta, 1);
			this.buttonNext.setOpacity(opacity);
			if(opacity == 1)
				nextDone = true;
		}
		else {
			var opacity = this.buttonNext.getOpacity();
			opacity = Math.max(opacity - (delta * 2), 0);
			this.buttonNext.setOpacity(opacity);
			if(opacity == 0)
				nextDone = true;
		}

		if(previousDone && nextDone) {
			this.showClock.stop();
			this.showClock = undefined;
		}
	}

	/**#@-*/
},
/**@lends Ui.Carousel#*/
{
	append: function(child) {
		this.carouselable.append(child);
	},

	remove: function(child) {
		this.carouselable.remove(child);
	},

	onStyleChange: function() {
		var color = this.getStyleProperty('focusColor');
		this.buttonPreviousIcon.setFill(color);
		this.buttonNextIcon.setFill(color);
	}
}, {
	style: {
		focusColor: '#5d3109'
	}
});

<commit_msg>add setCurrent and setCurrentAt in Ui.Carousel<commit_after>
Ui.MouseOverable.extend('Ui.Carousel',
/**@lends Ui.Carousel#*/
{
	carouselable: undefined,
	buttonNext: undefined,
	buttonNextIcon: undefined,
	buttonPrevious: undefined,
	buttonPreviousIcon: undefined,
	showClock: undefined,
//	hideClock: undefined,
	hideTimeout: undefined,

	showNext: false,
	showPrevious: false,

	/**
	 * @constructs
     * @class
	 * @extends Ui.MouseOverable
     */
	constructor: function(config) {
		this.addEvents('change', 'press');

		this.connect(this, 'enter', this.onMouseEnter);
		this.connect(this, 'leave', this.onMouseLeave);
		this.connect(this, 'move', this.onMouseOverMove);

		this.carouselable = new Ui.Carouselable();
		Ui.Carousel.base.append.call(this, this.carouselable);
		this.connect(this.carouselable, 'focus', this.onCarouselableFocus);
		this.connect(this.carouselable, 'blur', this.onCarouselableBlur);
		this.connect(this.carouselable, 'change', this.onCarouselableChange);
		this.connect(this.carouselable, 'press', this.onCarouselablePress);

		this.buttonPrevious = new Ui.Pressable({ horizontalAlign: 'left', verticalAlign: 'center', opacity: 0 });
		this.buttonPrevious.setFocusable(false);
		this.buttonPreviousIcon = new Ui.Icon({ icon: 'arrowleft', width: 48, height: 48 });
		this.buttonPrevious.append(this.buttonPreviousIcon);
		Ui.Carousel.base.append.call(this, this.buttonPrevious);
//		this.buttonPrevious.hide();
		this.connect(this.buttonPrevious, 'press', this.onPreviousPress);

		this.buttonNext = new Ui.Pressable({ horizontalAlign: 'right', verticalAlign: 'center', opacity: 0 });
		this.buttonNext.setFocusable(false);
		this.buttonNextIcon = new Ui.Icon({ icon: 'arrowright', width: 48, height: 48 });
		this.buttonNext.append(this.buttonNextIcon);
		Ui.Carousel.base.append.call(this, this.buttonNext);
//		this.buttonNext.hide();
		this.connect(this.buttonNext, 'press', this.onNextPress);
	},

	setAutoPlay: function(delay) {
		this.carouselable.setAutoPlay(delay);
	},

	next: function() {
		this.carouselable.next();
	},

	previous: function() {
		this.carouselable.previous();
	},

	getLogicalChildren: function() {
		return this.carouselable.getLogicalChildren();
	},

	getCurrentPosition: function() {
		return this.carouselable.getCurrentPosition();
	},

	getCurrent: function() {
		return this.carouselable.getCurrent();
	},

	setCurrentAt: function(position) {
		this.carouselable.setCurrentAt(position);
	},

	setCurrent: function(current) {
		this.carouselable.setCurrent(current);
	},

	/**#@+
	* @private
	*/

	onCarouselablePress: function(carouselable) {
		this.fireEvent('press', this);
	},

	onCarouselableChange: function(carouselable, position) {
		this.showArrows();
		this.fireEvent('change', this, position);
	},

	onCarouselableFocus: function() {
		this.showArrows();
	},

	onCarouselableBlur: function() {
		this.hideArrows();
	},

	onPreviousPress: function() {
		this.carouselable.focus();
		this.previous();
	},

	onNextPress: function() {
		this.carouselable.focus();
		this.next();
	},

	onMouseEnter: function() {
		this.showArrows();
	},

	onMouseOverMove: function() {
		this.showArrows();
	},

	onMouseLeave: function() {
		this.hideArrows();
	},

	showArrows: function() {
		if(this.hideTimeout != undefined) {
			this.hideTimeout.abort();
			this.hideTimeout = new Core.DelayedTask({ delay: 2, scope: this, callback: this.hideArrows });
		}
		else
			this.hideTimeout = new Core.DelayedTask({ delay: 2, scope: this, callback: this.hideArrows });

		var pos = this.carouselable.getCurrentPosition();
		var children = this.carouselable.getLogicalChildren();

		if(children.length > 0) {
			this.showPrevious = (pos > 0);
			this.showNext = (pos < children.length - 1);
		}
		else {
			this.showPrevious = false;
			this.showNext = false;
		}

		if(this.showClock === undefined) {
			this.showClock = new Anim.Clock({ duration: 'forever', scope: this, target: this, onTimeupdate: this.onShowTick });
			this.showClock.begin();
		}
	},

	hideArrows: function() {
		if(this.hideTimeout != undefined) {
			this.hideTimeout.abort();
			this.hideTimeout = undefined;
		}
		this.showPrevious = false;
		this.showNext = false;
		if(this.showClock === undefined) {
			this.showClock = new Anim.Clock({ duration: 'forever', scope: this, target: this, onTimeupdate: this.onShowTick });
			this.showClock.begin();
		}
	},

	onShowTick: function(clock, progress, delta) {
		if(delta == 0)
			return;

		var previousDone = false;
		if(this.showPrevious) {
			var opacity = this.buttonPrevious.getOpacity();
			opacity = Math.min(opacity + delta, 1);
			this.buttonPrevious.setOpacity(opacity);
			if(opacity == 1)
				previousDone = true;
		}
		else {
			var opacity = this.buttonPrevious.getOpacity();
			opacity = Math.max(opacity - (delta * 2), 0);
			this.buttonPrevious.setOpacity(opacity);
			if(opacity == 0)
				previousDone = true;
		}

		var nextDone = false;
		if(this.showNext) {
			var opacity = this.buttonNext.getOpacity();
			opacity = Math.min(opacity + delta, 1);
			this.buttonNext.setOpacity(opacity);
			if(opacity == 1)
				nextDone = true;
		}
		else {
			var opacity = this.buttonNext.getOpacity();
			opacity = Math.max(opacity - (delta * 2), 0);
			this.buttonNext.setOpacity(opacity);
			if(opacity == 0)
				nextDone = true;
		}

		if(previousDone && nextDone) {
			this.showClock.stop();
			this.showClock = undefined;
		}
	}

	/**#@-*/
},
/**@lends Ui.Carousel#*/
{
	append: function(child) {
		this.carouselable.append(child);
	},

	remove: function(child) {
		this.carouselable.remove(child);
	},

	onStyleChange: function() {
		var color = this.getStyleProperty('focusColor');
		this.buttonPreviousIcon.setFill(color);
		this.buttonNextIcon.setFill(color);
	}
}, {
	style: {
		focusColor: '#5d3109'
	}
});

<|endoftext|>