<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var EventEmitter = require('events').EventEmitter;
var inherits = require('util').inherits;

var emptyBuffer = Buffer(0);

// TODO: provide streams for arg2/3

function TChannelIncomingRequest(id, options) {
    if (!(this instanceof TChannelIncomingRequest)) {
        return new TChannelIncomingRequest(id, options);
    }
    options = options || {};
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.ttl = options.ttl || 0;
    self.tracing = options.tracing || null;
    self.service = options.service || '';
    self.remoteAddr = null;
    self.headers = options.headers || {};
    self.checksumType = options.checksumType || 0;
    self.arg1 = options.arg1 || emptyBuffer;
    self.arg2 = options.arg2 || emptyBuffer;
    self.arg3 = options.arg3 || emptyBuffer;
}

inherits(TChannelIncomingRequest, EventEmitter);

function TChannelIncomingResponse(id, options) {
    if (!(this instanceof TChannelIncomingResponse)) {
        return new TChannelIncomingResponse(id, options);
    }
    options = options || {};
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.code = options.code || 0;
    self.arg1 = options.arg1 || emptyBuffer;
    self.arg2 = options.arg2 || emptyBuffer;
    self.arg3 = options.arg3 || emptyBuffer;
    self.ok = self.code === 0; // TODO: probably okay, but a bit jank
}

inherits(TChannelIncomingResponse, EventEmitter);

function TChannelOutgoingRequest(id, options) {
    if (!(this instanceof TChannelOutgoingRequest)) {
        return new TChannelOutgoingRequest(id, options);
    }
    options = options || {};
    if (!options.sendFrame) {
        throw new Error('missing sendFrame');
    }
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.ttl = options.ttl || 0;
    self.tracing = options.tracing || null;
    self.service = options.service || '';
    self.headers = options.headers || {};
    self.checksumType = options.checksumType || 0;
    self.sendFrame = options.sendFrame;
    self.sent = false;
}

inherits(TChannelOutgoingRequest, EventEmitter);

TChannelOutgoingRequest.prototype.sendCallRequestFrame = function sendCallRequestFrame(arg1, arg2, arg3) {
    var self = this;
    self.sendFrame.callRequest(arg1, arg2, arg3);
};

TChannelOutgoingRequest.prototype.send = function send(arg1, arg2, arg3, callback) {
    var self = this;
    if (callback) self.hookupCallback(callback);
    if (self.sent) {
        throw new Error('request already sent');
    }
    self.sent = true;
    self.sendCallRequestFrame(
        arg1 ? Buffer(arg1) : null,
        arg2 ? Buffer(arg2) : null,
        arg3 ? Buffer(arg3) : null);
    self.emit('end');
    return self;
};

TChannelOutgoingRequest.prototype.hookupCallback = function hookupCallback(callback) {
    var self = this;
    self.once('error', onError);
    self.once('response', onResponse);
    function onError(err) {
        self.removeListener('response', onResponse);
        callback(err, null);
    }
    function onResponse(res) {
        self.removeListener('error', onError);
        callback(null, res);
    }
    return self;
};

function TChannelOutgoingResponse(id, options) {
    if (!(this instanceof TChannelOutgoingResponse)) {
        return new TChannelOutgoingResponse(id, options);
    }
    options = options || {};
    if (!options.sendFrame) {
        throw new Error('missing sendFrame');
    }
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.code = options.code || 0;
    self.tracing = options.tracing || null;
    self.headers = options.headers || {};
    self.checksumType = options.checksumType || 0;
    self.ok = true;
    self.sendFrame = options.sendFrame;
    self.arg1 = options.arg1 || emptyBuffer;
    self.arg2 = options.arg2 || emptyBuffer;
    self.arg3 = options.arg3 || emptyBuffer;
    self.sent = false;
}

inherits(TChannelOutgoingResponse, EventEmitter);

TChannelOutgoingResponse.prototype.sendCallResponseFrame = function sendCallResponseFrame(arg1, res1, res2) {
    var self = this;
    self.sendFrame.callResponse(arg1, res1, res2);
};

TChannelOutgoingResponse.prototype.sendErrorFrame = function sendErrorFrame(codeString, message) {
    var self = this;
    self.sendFrame.error(codeString, message);
};

TChannelOutgoingResponse.prototype.setOk = function setOk(ok) {
    var self = this;
    if (self.sent) {
        throw new Error('response already sent');
    }
    self.ok = ok;
    self.code = ok ? 0 : 1; // TODO: too coupled to v2 specifics?
};

TChannelOutgoingResponse.prototype.sendOk = function sendOk(res1, res2) {
    var self = this;
    self.setOk(true);
    self.sent = true;
    self.sendCallResponseFrame(self.arg1,
        res1 ? Buffer(res1) : null,
        res2 ? Buffer(res2) : null);
    self.emit('end');
};

TChannelOutgoingResponse.prototype.sendNotOk = function sendNotOk(res1, res2) {
    var self = this;
    self.setOk(false);
    self.sent = true;
    self.sendCallResponseFrame(self.arg1,
        res1 ? Buffer(res1) : null,
        res2 ? Buffer(res2) : null);
    self.emit('end');
};

module.exports.IncomingRequest = TChannelIncomingRequest;
module.exports.IncomingResponse = TChannelIncomingResponse;
module.exports.OutgoingRequest = TChannelOutgoingRequest;
module.exports.OutgoingResponse = TChannelOutgoingResponse;
<commit_msg>reqres: add slot to carry checksum objects<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var EventEmitter = require('events').EventEmitter;
var inherits = require('util').inherits;

var emptyBuffer = Buffer(0);

// TODO: provide streams for arg2/3

function TChannelIncomingRequest(id, options) {
    if (!(this instanceof TChannelIncomingRequest)) {
        return new TChannelIncomingRequest(id, options);
    }
    options = options || {};
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.ttl = options.ttl || 0;
    self.tracing = options.tracing || null;
    self.service = options.service || '';
    self.remoteAddr = null;
    self.headers = options.headers || {};
    self.checksum = options.checksum || null;
    self.checksumType = options.checksumType || 0;
    self.arg1 = options.arg1 || emptyBuffer;
    self.arg2 = options.arg2 || emptyBuffer;
    self.arg3 = options.arg3 || emptyBuffer;
}

inherits(TChannelIncomingRequest, EventEmitter);

function TChannelIncomingResponse(id, options) {
    if (!(this instanceof TChannelIncomingResponse)) {
        return new TChannelIncomingResponse(id, options);
    }
    options = options || {};
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.code = options.code || 0;
    self.checksum = options.checksum || null;
    self.arg1 = options.arg1 || emptyBuffer;
    self.arg2 = options.arg2 || emptyBuffer;
    self.arg3 = options.arg3 || emptyBuffer;
    self.ok = self.code === 0; // TODO: probably okay, but a bit jank
}

inherits(TChannelIncomingResponse, EventEmitter);

function TChannelOutgoingRequest(id, options) {
    if (!(this instanceof TChannelOutgoingRequest)) {
        return new TChannelOutgoingRequest(id, options);
    }
    options = options || {};
    if (!options.sendFrame) {
        throw new Error('missing sendFrame');
    }
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.ttl = options.ttl || 0;
    self.tracing = options.tracing || null;
    self.service = options.service || '';
    self.headers = options.headers || {};
    self.checksumType = options.checksumType || 0;
    self.checksum = options.checksum || null;
    self.sendFrame = options.sendFrame;
    self.sent = false;
}

inherits(TChannelOutgoingRequest, EventEmitter);

TChannelOutgoingRequest.prototype.sendCallRequestFrame = function sendCallRequestFrame(arg1, arg2, arg3) {
    var self = this;
    self.sendFrame.callRequest(arg1, arg2, arg3);
};

TChannelOutgoingRequest.prototype.send = function send(arg1, arg2, arg3, callback) {
    var self = this;
    if (callback) self.hookupCallback(callback);
    if (self.sent) {
        throw new Error('request already sent');
    }
    self.sent = true;
    self.sendCallRequestFrame(
        arg1 ? Buffer(arg1) : null,
        arg2 ? Buffer(arg2) : null,
        arg3 ? Buffer(arg3) : null);
    self.emit('end');
    return self;
};

TChannelOutgoingRequest.prototype.hookupCallback = function hookupCallback(callback) {
    var self = this;
    self.once('error', onError);
    self.once('response', onResponse);
    function onError(err) {
        self.removeListener('response', onResponse);
        callback(err, null);
    }
    function onResponse(res) {
        self.removeListener('error', onError);
        callback(null, res);
    }
    return self;
};

function TChannelOutgoingResponse(id, options) {
    if (!(this instanceof TChannelOutgoingResponse)) {
        return new TChannelOutgoingResponse(id, options);
    }
    options = options || {};
    if (!options.sendFrame) {
        throw new Error('missing sendFrame');
    }
    var self = this;
    EventEmitter.call(self);
    self.id = id || 0;
    self.code = options.code || 0;
    self.tracing = options.tracing || null;
    self.headers = options.headers || {};
    self.checksumType = options.checksumType || 0;
    self.checksum = options.checksum || null;
    self.ok = true;
    self.sendFrame = options.sendFrame;
    self.arg1 = options.arg1 || emptyBuffer;
    self.arg2 = options.arg2 || emptyBuffer;
    self.arg3 = options.arg3 || emptyBuffer;
    self.sent = false;
}

inherits(TChannelOutgoingResponse, EventEmitter);

TChannelOutgoingResponse.prototype.sendCallResponseFrame = function sendCallResponseFrame(arg1, res1, res2) {
    var self = this;
    self.sendFrame.callResponse(arg1, res1, res2);
};

TChannelOutgoingResponse.prototype.sendErrorFrame = function sendErrorFrame(codeString, message) {
    var self = this;
    self.sendFrame.error(codeString, message);
};

TChannelOutgoingResponse.prototype.setOk = function setOk(ok) {
    var self = this;
    if (self.sent) {
        throw new Error('response already sent');
    }
    self.ok = ok;
    self.code = ok ? 0 : 1; // TODO: too coupled to v2 specifics?
};

TChannelOutgoingResponse.prototype.sendOk = function sendOk(res1, res2) {
    var self = this;
    self.setOk(true);
    self.sent = true;
    self.sendCallResponseFrame(self.arg1,
        res1 ? Buffer(res1) : null,
        res2 ? Buffer(res2) : null);
    self.emit('end');
};

TChannelOutgoingResponse.prototype.sendNotOk = function sendNotOk(res1, res2) {
    var self = this;
    self.setOk(false);
    self.sent = true;
    self.sendCallResponseFrame(self.arg1,
        res1 ? Buffer(res1) : null,
        res2 ? Buffer(res2) : null);
    self.emit('end');
};

module.exports.IncomingRequest = TChannelIncomingRequest;
module.exports.IncomingResponse = TChannelIncomingResponse;
module.exports.OutgoingRequest = TChannelOutgoingRequest;
module.exports.OutgoingResponse = TChannelOutgoingResponse;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         lib Legend Mod
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  try to take over the world!
// @author       You
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js
// @match        http://www.atmarkit.co.jp/d9KcsYjB
// @grant        unsafeWindow
// ==/UserScript==

loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js");

	var my = {
		"name": "lib Legend Mod",
		"log": function(msg){ console.log(this.name + ":"+ msg); }
	};
	var stat = my;
	Object.assign(stat, {
        'publicIP': "ws://37.187.176.125:3000",
		"miniMapTeammatesColor": "#01d9cc",
	});
	//var cfg = {}, cfg_org = {
	//};
	my.log("start");
	lib_ogar = my;

	// =====  Test driverー  =====

	if(/atmarkit\.co\.jp\/d9KcsYjB/.test(location.href)){
		//setTimeout(init_html, 100);
		//loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js", init_html);
		loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js");
    loadScript("https://jimboy3100.github.io/ExampleScripts/libLM.js");
}
<commit_msg>Update lib2LM.user.js<commit_after>// ==UserScript==
// @name         lib Legend Mod
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  try to take over the world!
// @author       You
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js
// @match        http://www.atmarkit.co.jp/d9KcsYjB
// @grant        unsafeWindow
// ==/UserScript==

loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js");

	var my = {
		"name": "lib Legend Mod",
		"log": function(msg){ console.log(this.name + ":"+ msg); }
	};
	var stat = my;
	Object.assign(stat, {
        'publicIP': "ws://37.187.176.125:3000",
		"miniMapTeammatesColor": "#01d9cc",
	});
	//var cfg = {}, cfg_org = {
	//};
	my.log("start");
	lib_ogar = my;

	// =====  Test driverー  =====

	if(/atmarkit\.co\.jp\/d9KcsYjB/.test(location.href)){
		//setTimeout(init_html, 100);
		//loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js", init_html);
		loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js");
    loadScript("https://jimboy3100.github.io/ExampleScripts/libLM.js");
}
	// =====  Other general-purpose processing  =====
	function loadScript(url, callback){
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.src = url;
		if(typeof callback !== 'undefined'){
			script.onload = callback;
		}
		document.head.appendChild(script);
	}
	function escapeHtml(e) {
		return e.replace(/&/g, "&")
			.replace(/</g, "<")
			.replace(/>/g, ">")
			.replace(/"/g, """)
			.replace(/'/g, "'");
	}
<|endoftext|>


<commit_before>import Vector from 'vector';
import Rect from 'rect';

function createBrick({x, y}, screen) {
	const bottom_outer_rect = Rect({x, y}, {width: 2, height: 1});
	const top_outer_rect = Rect({x, y}, {width: 1.8, height: .8});

	const inner_rect = Rect({x: x + .2, y: y + .2}, {width: 1.6, height: .6});

	const scale_factor = (screen.width/14)/2;
	const line_width = 1/scale_factor;
	return {
		get pos() {
			return Vector({x, y});
		},
		get rect() {
			return Rect(this.pos, {width: 2, height: 1});
		},
		draw(inner_color, top_outer_color, bottom_outer_color = 'black') {
			screen.save();
			screen.scale(scale_factor);
			screen.pen = line_width;

			screen.brush = bottom_outer_color;
			screen.fillRect(bottom_outer_rect);

			screen.brush = top_outer_color;
			screen.fillRect(top_outer_rect);

			screen.brush = inner_color;
			screen.fillRect(inner_rect);

			screen.restore();
		},
		get lineWidth() {
			return line_width;
		},
		get scaleFactor() {
			return scale_factor;
		}
	};
}

export function GrayBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('#777', '#ddd');
		}
	});
}

export function BlueBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(226, 95%, 62%)', 'hsl(226, 95%, 82%)');
		}
	});
}

export function RedBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(6, 76%, 47%)', 'hsl(6, 76%, 67%)');
		}
	});
}

export function GreenBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(89, 98%, 58%)', 'hsl(89, 98%, 78%)');
		}
	});
}

export function YellowBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(55, 100%, 50%)', 'hsl(55, 100%, 80%)');
		}
	});
}

export function PurpleBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(290, 97%, 57%)', 'hsl(290, 97%, 77%)');
		}
	});
}
<commit_msg>indent<commit_after>import Vector from 'vector';
import Rect from 'rect';

function createBrick({x, y}, screen) {
	const bottom_outer_rect = Rect({x, y}, {width: 2, height: 1});
	const top_outer_rect = Rect({x, y}, {width: 1.8, height: .8});
	const inner_rect = Rect({x: x + .2, y: y + .2}, {width: 1.6, height: .6});
	const scale_factor = (screen.width/14)/2;
	const line_width = 1/scale_factor;
	return {
		get pos() {
			return Vector({x, y});
		},
		get rect() {
			return Rect(this.pos, {width: 2, height: 1});
		},
		draw(inner_color, top_outer_color, bottom_outer_color = 'black') {
			screen.save();
			screen.scale(scale_factor);
			screen.pen = line_width;

			screen.brush = bottom_outer_color;
			screen.fillRect(bottom_outer_rect);

			screen.brush = top_outer_color;
			screen.fillRect(top_outer_rect);

			screen.brush = inner_color;
			screen.fillRect(inner_rect);

			screen.restore();
		},
		get lineWidth() {
			return line_width;
		},
		get scaleFactor() {
			return scale_factor;
		}
	};
}

export function GrayBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('#777', '#ddd');
		}
	});
}

export function BlueBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(226, 95%, 62%)', 'hsl(226, 95%, 82%)');
		}
	});
}

export function RedBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(6, 76%, 47%)', 'hsl(6, 76%, 67%)');
		}
	});
}

export function GreenBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(89, 98%, 58%)', 'hsl(89, 98%, 78%)');
		}
	});
}

export function YellowBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(55, 100%, 50%)', 'hsl(55, 100%, 80%)');
		}
	});
}

export function PurpleBrick({x, y}, screen) {
	const base = createBrick({x, y}, screen);
	return Object.assign({}, base, {
		draw() {
			base.draw('hsl(290, 97%, 57%)', 'hsl(290, 97%, 77%)');
		}
	});
}
<|endoftext|>


<commit_before>(function () {
    angular.module('angul3rytics', []).provider('Angul3rytics', function () {
        var eventHandlersNames = ['Google'];
        this.setEventHandlers = function (handlers) {
            if (angular.isString(handlers)) {
                handlers = [handlers];
            }
            eventHandlersNames = [];
            angular.forEach(handlers, function (handler) {
                eventHandlersNames.push(capitalizeHandler(handler));
            });
        };
        var capitalizeHandler = function (handler) {
            return handler.charAt(0).toUpperCase() + handler.substring(1);
        };
        var pageChangeEvent = '$locationChangeSuccess';
        this.setPageChangeEvent = function (newPageChangeEvent) {
            pageChangeEvent = newPageChangeEvent;
        };
        this.$get = [
            '$injector',
            '$rootScope',
            '$location',
            function ($injector, $rootScope, $location) {
                var eventHandlers = [];
                angular.forEach(eventHandlersNames, function (handler) {
                    eventHandlers.push($injector.get('Angul3rytics' + handler + 'Handler'));
                });
                var forEachHandlerDo = function (action) {
                    angular.forEach(eventHandlers, function (handler) {
                        action(handler);
                    });
                };
                var service = {};
                service.init = function () {
                };
                service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
                    forEachHandlerDo(function (handler) {
                        if (category && action) {
                            handler.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
                        }
                    });
                };
                service.trackPageView = function (url) {
                    forEachHandlerDo(function (handler) {
                        if (url) {
                            handler.trackPageView(url);
                        }
                    });
                };

                service.trackTiming = function (category, variable, value, opt_label) {
                    forEachHandlerDo(function (handler) {
                        if (category && variable && value) {
                            handler.trackTiming(category, variable, value, opt_label);
                        }
                    });
                };

                $rootScope.$on(pageChangeEvent, function () {
                    service.trackPageView($location.url());
                });

                //BE added for ecommerce order tracking
                service.trackEcommerceTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country) {
                    forEachHandlerDo(function (handler) {
                        if (transactionId) {
                            handler.trackEcommerceTrans(transactionId, affiliation, total, tax, shipping, city, state, country);
                        }
                    });
                };
                //BE added for ecommerce item tracking
                service.trackEcommerceItem = function (transactionId, sku, name, category, price, quantity) {
                    forEachHandlerDo(function (handler) {
                        if (transactionId) {
                            handler.trackEcommerceItem(transactionId, sku, name, category, price, quantity);
                        }
                    });
                };
                //BE added for ecommerce item tracking
                service.pushTransaction = function () {
                    forEachHandlerDo(function (handler) {
                        handler.pushTransaction();
                    });
                };

                return service;
            }
        ];
    });
}());
(function () {
    angular.module('angul3rytics').factory('Angul3ryticsConsoleHandler', [
        '$log',
        function ($log) {
            var service = {};
            service.trackPageView = function (url) {
                $log.log('URL visited', url);
            };
            service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
                $log.log('Event tracked', category, action, opt_label, opt_value, opt_noninteraction);
            };

            service.trackTiming = function (category, variable, value, opt_label) {
                $log.log('Timing tracked', category, variable, value, opt_label);
            };
            //BE added for ecommerce order tracking
            service.trackEcommerceTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country) {
                $log.log('TRANSACTION Tracked', transactionId, affiliation, total, tax, shipping, city, state, country);
            };
            //BE added for ecommerce item tracking
            service.trackEcommerceItem = function (transactionId, sku, name, category, price, quantity) {
                $log.log('ITEM Tracked', transactionId, sku, name, category, price, quantity);
            };
            //BE added for ecommerce transaction push
            service.pushTransaction = function () {
                $log.log('TRANSACTION Pushed', 'Success!');
            };

            return service;
        }
    ]);
}());
(function () {
    angular.module('angul3rytics').factory('Angul3ryticsGoogleHandler', [
        '$log',
        function ($log) {
            var service = {};
            service.trackPageView = function (url) {
                _gaq.push([
                    '_set',
                    'page',
                    url
                ]);
                _gaq.push([
                    '_trackPageview',
                    url
                ]);
            };
            service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
                _gaq.push([
                    '_trackEvent',
                    category,
                    action,
                    opt_label,
                    opt_value,
                    opt_noninteraction
                ]);
            };

            //BE added for ecommerce order tracking
            //(transactionId, affiliation, total, tax, shipping, city, state, country)
            service.trackEcommerceTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country) {
                _gaq.push([
                    '_addTrans',
                    transactionId,
                    affiliation,
                    total,
                    tax,
                    shipping,
                    city,
                    state,
                    country
                ]);
            };
            //BE added for ecommerce item tracking
            //(transactionId, sku, name, category, price, quantity)
            service.trackEcommerceItem = function (transactionId, sku, name, category, price, quantity) {
                _gaq.push([
                    '_addItem',
                    transactionId,
                    sku,
                    name,
                    category,
                    price,
                    quantity
                ]);
            };
            //BE added for ecommerce transaction push
            service.pushTransaction = function () {
                _gaq.push(['_trackTrans']);
            };

            return service;
        }
    ]).factory('Angul3ryticsGoogleUniversalHandler', function () {
        var service = {};
        service.trackPageView = function (url) {
            ga('set', 'page', url);
            ga('send', 'pageview', url);
        };
        service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
            ga('send', 'event', category, action, opt_label, opt_value, { 'nonInteraction': opt_noninteraction });
        };
    service.trackTiming = function (category, variable, value, opt_label) {
      ga('send', 'timing', category, variable, value, opt_label);
    };
        return service;
    });
}());
(function () {
    angular.module('angul3rytics').filter('trackEvent', [
        'Angul3rytics',
        function (Angul3rytics) {
            return function (entry, category, action, opt_label, opt_value, opt_noninteraction) {
                Angul3rytics.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
                return entry;
            };
        }
    ]);
    //BE added for ecommerce order tracking
    angular.module('angul3rytics').filter('trackEcommerceTrans', [
        'Angul3rytics',
        function (Angul3rytics) {
            return function (entry, transactionId, affiliation, total, tax, shipping, city, state, country) {
                Angul3rytics.trackEcommerceTrans(transactionId, affiliation, total, tax, shipping, city, state, country);
                return entry;
            };
        }
    ]);
    //BE added for ecommerce item tracking
    angular.module('angul3rytics').filter('trackEcommerceItem', [
        'Angul3rytics',
        function (Angul3rytics) {
            return function (entry, transactionId, sku, name, category, price, quantity) {
                Angul3rytics.trackEcommerceTrans(transactionId, sku, name, category, price, quantity);
                return entry;
            };
        }
    ]);
    //BE added for ecommerce item tracking
    angular.module('angul3rytics').filter('pushTransaction', [
        'Angul3rytics',
        function (Angul3rytics) {
            return function (entry) {
                Angul3rytics.pushTransaction();
                return entry;
            };
        }
    ]);
}());<commit_msg>Update angul3rytics.js<commit_after>(function () {
    angular.module('angul3rytics', []).provider('Angul3rytics', function () {
        var eventHandlersNames = ['Google'];
        this.setEventHandlers = function (handlers) {
            if (angular.isString(handlers)) {
                handlers = [handlers];
            }
            eventHandlersNames = [];
            angular.forEach(handlers, function (handler) {
                eventHandlersNames.push(capitalizeHandler(handler));
            });
        };
        var capitalizeHandler = function (handler) {
            return handler.charAt(0).toUpperCase() + handler.substring(1);
        };
        var pageChangeEvent = '$locationChangeSuccess';
        this.setPageChangeEvent = function (newPageChangeEvent) {
            pageChangeEvent = newPageChangeEvent;
        };
        this.$get = [
            '$injector',
            '$rootScope',
            '$location',
            function ($injector, $rootScope, $location) {
                var eventHandlers = [];
                angular.forEach(eventHandlersNames, function (handler) {
                    eventHandlers.push($injector.get('Angul3rytics' + handler + 'Handler'));
                });
                var forEachHandlerDo = function (action) {
                    angular.forEach(eventHandlers, function (handler) {
                        action(handler);
                    });
                };
                var service = {};
                service.init = function () {
                };
                service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
                    forEachHandlerDo(function (handler) {
                        if (category && action) {
                            handler.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
                        }
                    });
                };
                service.trackPageView = function (url) {
                    forEachHandlerDo(function (handler) {
                        if (url) {
                            handler.trackPageView(url);
                        }
                    });
                };
                service.trackTiming = function (category, variable, value, opt_label) {
                    forEachHandlerDo(function (handler) {
                        if (category && variable && value) {
                            handler.trackTiming(category, variable, value, opt_label);
                        }
                    });
                };
                $rootScope.$on(pageChangeEvent, function () {
                    service.trackPageView($location.url());
                });
                return service;
            }
        ];
    });
}());
(function () {
    angular.module('angul3rytics').factory('Angul3ryticsConsoleHandler', [
        '$log',
        function ($log) {
            var service = {};
            service.trackPageView = function (url) {
                $log.log('URL visited', url);
            };
            service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
                $log.log('Event tracked', category, action, opt_label, opt_value, opt_noninteraction);
            };
            service.trackTiming = function (category, variable, value, opt_label) {
                $log.log('Timing tracked', category, variable, value, opt_label);
            };
            return service;
        }
    ]);
}());
(function () {
    angular.module('angul3rytics').factory('Angul3ryticsGoogleHandler', function () {
        var service = {};
        service.trackPageView = function (url) {
            _gaq.push([
                '_set',
                'page',
                url
            ]);
            _gaq.push([
                '_trackPageview',
                url
            ]);
        };
        service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
            _gaq.push([
                '_trackEvent',
                category,
                action,
                opt_label,
                opt_value,
                opt_noninteraction
            ]);
        };
        service.trackTiming = function (category, variable, value, opt_label) {
            _gaq.push([
                '_trackTiming',
                category,
                variable,
                value,
                opt_label
            ]);
        };
        return service;
    }).factory('Angul3ryticsGoogleUniversalHandler', function () {
        var service = {};
        service.trackPageView = function (url) {
            ga('set', 'page', url);
            ga('send', 'pageview', url);
        };
        service.trackEvent = function (category, action, opt_label, opt_value, opt_noninteraction) {
            ga('send', 'event', category, action, opt_label, opt_value, { 'nonInteraction': opt_noninteraction });
        };
        service.trackTiming = function (category, variable, value, opt_label) {
            ga('send', 'timing', category, variable, value, opt_label);
        };
        return service;
    });
}());
(function () {
    angular.module('angul3rytics').filter('trackEvent', [
        'Angul3rytics',
        function (Angul3rytics) {
            return function (entry, category, action, opt_label, opt_value, opt_noninteraction) {
                Angul3rytics.trackEvent(category, action, opt_label, opt_value, opt_noninteraction);
                return entry;
            };
        }
    ]);
}());
<|endoftext|>


<commit_before>// vim: ts=4:sw=4
(function () {
    "use strict";

    var baseURL = location.protocol + "//" + location.host;
    var socket = io.connect(baseURL);

    $(document).ready(function() {
        new Dropzone(document.body, {clickable: false,url: "/upload"});
//-----------------------------------------------------------------------------
        $("body").on("click", ".delete", function(e) {
            e.preventDefault();
            $.ajax({
                type: "GET",
                url: $(this).attr("href")
            });
        });
//-----------------------------------------------------------------------------
//Attach jquery.form and handle progress updates
        $("form").change(function() {
            $("form").submit();
        });

        var bar = $("#progressBar");
        var percent = $("#percent");

        $("form").ajaxForm({
            beforeSend: function() {
                $("#progress").show();
                var perc = "0%";
                bar.width(perc);
                percent.html(perc);
            },
            uploadProgress: function(e, pos, total, completed) {
                var perc = completed + "%";
                bar.width(perc);
                percent.html(perc);
            },
            success: function() {
                var perc = "100%";
                bar.width(perc);
                percent.html(perc);
            },
            complete: function(xhr) {
                $("#progress").hide();
            }
        });
//-----------------------------------------------------------------------------
        socket.on("UPDATE_FILES", function (data) {
            var json = JSON.parse(data);
            var html = getFileList(json);
            $("#content").html(html);
        });
//-----------------------------------------------------------------------------
        $("#add-folder").click(function (){
            $("#overlay").toggle();
            $("#name").val("");
            $("#name").focus();
        });
//-----------------------------------------------------------------------------
        $("#name").keyup(function(e){
            if(e.keyCode == 27) $("#overlay").toggle(); // Escape Key
            var input = $("#name").val();
            var valid = !input.match(/[\\*{}\/<>?|]/);
            if (!valid){
                $("#info-filename").show();
                $("#name").css("background-color","#f55");
                $("#name").css("border-color","#f00");
            } else {
                $("#info-filename").hide();
                $("#name").css("background-color","#eee");
                $("#name").css("border-color","#ff9147");
            }
            if(e.keyCode == 13 && input && valid) { // Return Key
                socket.emit("CREATE_FOLDER",input);
                $("#info-filename").hide();
                $("#overlay").hide();
            }
        });

        //Initial update of files
        socket.emit("REQUEST_UPDATE");
    });
//-----------------------------------------------------------------------------
    function getFileList(fileList) {
        var htmlFiles = "",
            htmlDirs = "",
            header = '

Name

SizeDel

',
            i = 0,
            name,
            href;

        while(fileList[i]) {
            var file = fileList[i];
            if(file.type == "f") {
                var size = convertToSI(file.size);
                name = file.name;
                href = "/files/" + unescape(file.name);
                htmlFiles += '
';
                htmlFiles += '

';
                htmlFiles += '
' + name + '
';
                htmlFiles += '
' + size + '✖
';
                htmlFiles += '

';
            } else {
                name = file.name;
                href = '#'; //TODO
                htmlDirs += '
';
                htmlDirs += '

';
                htmlDirs += '
' + name + '
';
                htmlDirs += '
-✖
';
                htmlDirs += '

';
            }
            i++;
        }
        return header + htmlDirs + htmlFiles;
    }
//-----------------------------------------------------------------------------
    function convertToSI(bytes) {
        var suffix = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"], tier = 0;

        while(bytes >= 1024) {
            bytes /= 1024;
            tier++;
        }
        return Math.round(bytes * 10) / 10 + " " + suffix[tier];
    }
}());<commit_msg>Document and optimize the client js a bit<commit_after>// vim: ts=4:sw=4
(function () {
    "use strict";
//-----------------------------------------------------------------------------
// Initialize WebSocket
    var baseURL = location.protocol + "//" + location.host;
    var socket = io.connect(baseURL);
//-----------------------------------------------------------------------------
    $(document).ready(function() {
        var bar = $("#progressBar"),
            percent = $("#percent"),
            progress = $("#progress"),
            content = $("#content");
//-----------------------------------------------------------------------------
// Mark the body as destination for file drops
        new Dropzone(document.body, {clickable: false, url: "/upload"});
//-----------------------------------------------------------------------------
// Delete calls run over xhr
        $("body").on("click", ".delete", function(e) {
            e.preventDefault();
            $.ajax({
                type: "GET",
                url: $(this).attr("href")
            });
        });
//-----------------------------------------------------------------------------
// Attach jquery.form to the form and handle progress updates
//(seems to also work on the dropzone)
        $("form").change(function() {
            $("form").submit();
        });
//-----------------------------------------------------------------------------
// Upload event handling
        $("form").ajaxForm({
            beforeSend: function() {
                progress.show();
                var perc = "0%";
                bar.width(perc);
                percent.html(perc);
            },
            uploadProgress: function(e, pos, total, completed) {
                var perc = completed + "%";
                bar.width(perc);
                percent.html(perc);
            },
            success: function() {
                var perc = "100%";
                bar.width(perc);
                percent.html(perc);
            },
            complete: function(xhr) {
                progress.hide();
            }
        });
//-----------------------------------------------------------------------------
// Handle WebSocket updates from server
        socket.on("UPDATE_FILES", function (data) {
            var json = JSON.parse(data);
            var html = buildHTML(json);
            content.html(html);
        });
//-----------------------------------------------------------------------------
// Show popup for folder creation
        $("#add-folder").click(function (){
            $("#overlay").toggle();
            $("#name").val("");
            $("#name").focus();
        });
//-----------------------------------------------------------------------------
// Handler for the input of the folder name
// TODO: Sanitize on server
        $("#name").keyup(function(e){
            var name = $("#name");
            if(e.keyCode == 27)
                $("#overlay").toggle(); // Escape Key
            var input = name.val();
            var valid = !input.match(/[\\*{}\/<>?|]/);
            if (!valid){
                $("#info-filename").show();
                name.css("background-color","#f55");
                name.css("border-color","#f00");
            } else {
                $("#info-filename").hide();
                name.css("background-color","#eee");
                name.css("border-color","#ff9147");
            }
            if(e.keyCode == 13 && input && valid) { // Return Key
                socket.emit("CREATE_FOLDER",input);
                $("#info-filename").hide();
                $("#overlay").hide();
            }
        });
//-----------------------------------------------------------------------------
//Initial update of files
        socket.emit("REQUEST_UPDATE");
    });
//-----------------------------------------------------------------------------
// Convert the fileList object into HTML
    function buildHTML(fileList) {
        var htmlFiles = "",
            htmlDirs = "",
            header = '

Name

SizeDel

',
            i = 0,
            name,
            href;

        while(fileList[i]) {
            var file = fileList[i];
            if(file.type == "f") {
                var size = convertToSI(file.size);
                name = file.name;
                href = "/files/" + unescape(file.name);
                htmlFiles += '
';
                htmlFiles += '

';
                htmlFiles += '
' + name + '
';
                htmlFiles += '
' + size + '✖
';
                htmlFiles += '

';
            } else {
                name = file.name;
                href = '#'; //TODO
                htmlDirs += '
';
                htmlDirs += '

';
                htmlDirs += '
' + name + '
';
                htmlDirs += '
-✖
';
                htmlDirs += '

';
            }
            i++;
        }
        return header + htmlDirs + htmlFiles;
    }
//-----------------------------------------------------------------------------
// Helper function for size values
    function convertToSI(bytes) {
        var suffix = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"], tier = 0;

        while(bytes >= 1024) {
            bytes /= 1024;
            tier++;
        }
        return Math.round(bytes * 10) / 10 + " " + suffix[tier];
    }
}());<|endoftext|>


<commit_before>import './customBlockly';
import blocks from './blocks';
import {
    isMainBlock,
    save,
    disable,
    deleteBlocksLoadedBy,
    addLoadersFirst,
    cleanUpOnLoad,
    addDomAsBlock,
    backwardCompatibility,
    fixCollapsedBlocks,
    fixArgumentAttribute,
    removeUnavailableMarkets,
    strategyHasValidTradeTypeCategory,
} from './utils';
import Interpreter from '../../bot/Interpreter';
import createError from '../../common/error';
import { translate, xml as translateXml } from '../../../common/i18n';
import { getLanguage } from '../../../common/lang';
import { observer as globalObserver } from '../../../common/utils/observer';

const setBeforeUnload = off => {
    if (off) {
        window.onbeforeunload = null;
    } else {
        window.onbeforeunload = () => 'You have some unsaved blocks, do you want to save them before you exit?';
    }
};

const disableStrayBlocks = () => {
    const topBlocks = Blockly.mainWorkspace.getTopBlocks();
    topBlocks.forEach(block => {
        if (
            !isMainBlock(block.type) &&
            ['block_holder', 'tick_analysis', 'loader', 'procedures_defreturn', 'procedures_defnoreturn'].indexOf(
                block.type
            ) < 0 &&
            !block.disabled
        ) {
            disable(block, translate('Blocks must be inside block holders, main blocks or functions'));
        }
    });
};
const disposeBlocksWithLoaders = () => {
    Blockly.mainWorkspace.addChangeListener(ev => {
        setBeforeUnload();
        if (ev.type === 'delete' && ev.oldXml.getAttribute('type') === 'loader' && ev.group !== 'undo') {
            deleteBlocksLoadedBy(ev.blockId, ev.group);
        }
    });
};
const marketsWereRemoved = xml => {
    if (!Array.from(xml.children).every(block => !removeUnavailableMarkets(block))) {
        $('#unavailableMarkets').dialog({
            height: 'auto',
            width : 600,
            modal : true,
            open() {
                $(this)
                    .parent()
                    .find('.ui-dialog-buttonset > button')
                    .removeClass('ui-button ui-corner-all ui-widget');
            },
            buttons: [
                {
                    text : translate('OK'),
                    class: 'button-primary',
                    click() {
                        $(this).dialog('close');
                    },
                },
            ],
        });
        if (window.trackJs) {
            trackJs.track('Invalid financial market');
        }
        $('#unavailableMarkets').dialog('open');
        return true;
    }
    return false;
};
const loadWorkspace = xml => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    Blockly.Events.setGroup('load');
    Blockly.mainWorkspace.clear();

    Array.from(xml.children).forEach(block => {
        backwardCompatibility(block);
    });

    fixArgumentAttribute(xml);
    Blockly.Xml.domToWorkspace(xml, Blockly.mainWorkspace);
    addLoadersFirst(xml).then(
        () => {
            fixCollapsedBlocks();
            Blockly.Events.setGroup(false);
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            Blockly.Events.setGroup(false);
            throw e;
        }
    );
};

const loadBlocks = (xml, dropEvent = {}) => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    const variables = xml.getElementsByTagName('variables');
    if (variables.length > 0) {
        Blockly.Xml.domToVariables(variables[0], Blockly.mainWorkspace);
    }
    Blockly.Events.setGroup('load');
    addLoadersFirst(xml).then(
        loaders => {
            const addedBlocks = [
                ...loaders,
                ...Array.from(xml.children)
                    .map(block => addDomAsBlock(block))
                    .filter(b => b),
            ];
            cleanUpOnLoad(addedBlocks, dropEvent);
            fixCollapsedBlocks();
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            throw e;
        }
    );
};
const xmlToStr = xml => {
    const serializer = new XMLSerializer();
    return serializer.serializeToString(xml);
};
const addBlocklyTranslation = () => {
    $.ajaxPrefilter(options => {
        options.async = true; // eslint-disable-line no-param-reassign
    });
    let lang = getLanguage();
    if (lang === 'ach') {
        lang = 'en';
    } else if (lang === 'zh_cn') {
        lang = 'zh-hans';
    } else if (lang === 'zh_tw') {
        lang = 'zh-hant';
    }
    return new Promise(resolve => {
        $.getScript(`https://blockly-demo.appspot.com/static/msg/js/${lang}.js`, resolve);
    });
};
const onresize = () => {
    let element = document.getElementById('blocklyArea');
    const blocklyArea = element;
    const blocklyDiv = document.getElementById('blocklyDiv');
    let x = 0;
    let y = 0;
    do {
        x += element.offsetLeft;
        y += element.offsetTop;
        element = element.offsetParent;
    } while (element);
    // Position blocklyDiv over blocklyArea.
    blocklyDiv.style.left = `${x}px`;
    blocklyDiv.style.top = `${y}px`;
    blocklyDiv.style.width = `${blocklyArea.offsetWidth}px`;
    blocklyDiv.style.height = `${blocklyArea.offsetHeight}px`;
};
const render = workspace => () => {
    onresize();
    Blockly.svgResize(workspace);
};
const overrideBlocklyDefaultShape = () => {
    const addDownloadToMenu = block => {
        if (block instanceof Object) {
            // eslint-disable-next-line no-param-reassign, max-len
            block.customContextMenu = function customContextMenu(options) {
                options.push({
                    text    : translate('Download'),
                    enabled : true,
                    callback: () => {
                        const xml = Blockly.Xml.textToDom(
                            '<xml xmlns="http://www.w3.org/1999/xhtml" collection="false"></xml>'
                        );
                        xml.appendChild(Blockly.Xml.blockToDom(this));
                        save('binary-bot-block', true, xml);
                    },
                });
            };
        }
    };
    Object.keys(Blockly.Blocks).forEach(blockName => {
        const downloadDisabledBlocks = ['controls_forEach', 'controls_for', 'variables_get', 'variables_set'];
        if (!downloadDisabledBlocks.includes(blockName)) {
            addDownloadToMenu(Blockly.Blocks[blockName]);
        }
    });
};
const repaintDefaultColours = () => {
    Blockly.Msg.LOGIC_HUE = '#DEDEDE';
    Blockly.Msg.LOOPS_HUE = '#DEDEDE';
    Blockly.Msg.MATH_HUE = '#DEDEDE';
    Blockly.Msg.TEXTS_HUE = '#DEDEDE';
    Blockly.Msg.LISTS_HUE = '#DEDEDE';
    Blockly.Msg.COLOUR_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_DYNAMIC_HUE = '#DEDEDE';
    Blockly.Msg.PROCEDURES_HUE = '#DEDEDE';

    Blockly.Blocks.logic.HUE = '#DEDEDE';
    Blockly.Blocks.loops.HUE = '#DEDEDE';
    Blockly.Blocks.math.HUE = '#DEDEDE';
    Blockly.Blocks.texts.HUE = '#DEDEDE';
    Blockly.Blocks.lists.HUE = '#DEDEDE';
    Blockly.Blocks.colour.HUE = '#DEDEDE';
    Blockly.Blocks.variables.HUE = '#DEDEDE';
    Blockly.Blocks.procedures.HUE = '#DEDEDE';
};

export default class _Blockly {
    constructor() {
        this.blocksXmlStr = '';
        this.generatedJs = '';
        // eslint-disable-next-line no-underscore-dangle
        Blockly.WorkspaceSvg.prototype.preloadAudio_ = () => {}; // https://github.com/google/blockly/issues/299
        this.initPromise = new Promise(resolve => {
            $.get('xml/toolbox.xml', toolboxXml => {
                blocks();
                const workspace = Blockly.inject('blocklyDiv', {
                    toolbox: xmlToStr(translateXml(toolboxXml.getElementsByTagName('xml')[0])),
                    zoom   : {
                        wheel: false,
                    },
                    trashcan: false,
                });
                const renderInstance = render(workspace);
                window.addEventListener('resize', renderInstance, false);
                renderInstance();
                addBlocklyTranslation().then(() => {
                    $.get('xml/main.xml', main => {
                        repaintDefaultColours();
                        overrideBlocklyDefaultShape();
                        this.blocksXmlStr = Blockly.Xml.domToPrettyText(main);
                        Blockly.Xml.domToWorkspace(main.getElementsByTagName('xml')[0], workspace);
                        this.zoomOnPlusMinus();
                        disposeBlocksWithLoaders();
                        setTimeout(() => {
                            setBeforeUnload(true);
                            Blockly.mainWorkspace.cleanUp();
                            Blockly.mainWorkspace.clearUndo();
                        }, 0);
                        resolve();
                    });
                });
            });
        });
    }
    /* eslint-disable class-methods-use-this */
    zoomOnPlusMinus(zoomIn) {
        const metrics = Blockly.mainWorkspace.getMetrics();
        if (zoomIn) {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, 1);
        } else {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, -1);
        }
    }
    resetWorkspace() {
        Blockly.Events.setGroup('reset');
        Blockly.mainWorkspace.clear();
        Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(this.blocksXmlStr), Blockly.mainWorkspace);
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    cleanUp() {
        Blockly.Events.setGroup(true);
        const topBlocks = Blockly.mainWorkspace.getTopBlocks(true);
        let cursorY = 0;
        topBlocks.forEach(block => {
            if (block.getSvgRoot().style.display !== 'none') {
                const xy = block.getRelativeToSurfaceXY();
                block.moveBy(-xy.x, cursorY - xy.y);
                block.snapToGrid();
                cursorY =
                    block.getRelativeToSurfaceXY().y + block.getHeightWidth().height + Blockly.BlockSvg.MIN_BLOCK_Y;
            }
        });
        Blockly.Events.setGroup(false);
        // Fire an event to allow scrollbars to resize.
        Blockly.mainWorkspace.resizeContents();
    }
    /* eslint-disable class-methods-use-this */
    load(blockStr = '', dropEvent = {}) {
        let xml;

        try {
            xml = Blockly.Xml.textToDom(blockStr);
        } catch (e) {
            throw createError('FileLoad', translate('Unrecognized file format.'));
        }

        try {
            if (xml.hasAttribute('collection') && xml.getAttribute('collection') === 'true') {
                loadBlocks(xml, dropEvent);
            } else {
                loadWorkspace(xml);
            }
        } catch (e) {
            throw createError('FileLoad', translate('Unable to load the block file.'));
        }
    }
    /* eslint-disable class-methods-use-this */
    save(arg) {
        const { filename, collection } = arg;

        setBeforeUnload(true);
        const xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace);
        Array.from(xml.children).forEach(blockDom => {
            const blockId = blockDom.getAttribute('id');
            if (!blockId) return;
            const block = Blockly.mainWorkspace.getBlockById(blockId);
            if ('loaderId' in block) {
                blockDom.remove();
            }
        });
        save(filename, collection, xml);
    }
    run(limitations = {}) {
        disableStrayBlocks();
        let code;
        try {
            code = `
var BinaryBotPrivateInit, BinaryBotPrivateStart, BinaryBotPrivateBeforePurchase, BinaryBotPrivateDuringPurchase, BinaryBotPrivateAfterPurchase;

var BinaryBotPrivateLastTickTime
var BinaryBotPrivateTickAnalysisList = [];

function BinaryBotPrivateRun(f, arg) {
 if (f) return f(arg);
 return false;
}

function BinaryBotPrivateTickAnalysis() {
 var currentTickTime = Bot.getLastTick(true).epoch
 if (currentTickTime === BinaryBotPrivateLastTickTime) {
   return
 }
 BinaryBotPrivateLastTickTime = currentTickTime
 for (var BinaryBotPrivateI = 0; BinaryBotPrivateI < BinaryBotPrivateTickAnalysisList.length; BinaryBotPrivateI++) {
   BinaryBotPrivateRun(BinaryBotPrivateTickAnalysisList[BinaryBotPrivateI]);
 }
}

var BinaryBotPrivateLimitations = ${JSON.stringify(limitations)};

${Blockly.JavaScript.workspaceToCode(Blockly.mainWorkspace)}

BinaryBotPrivateRun(BinaryBotPrivateInit);

while(true) {
 BinaryBotPrivateTickAnalysis();
 BinaryBotPrivateRun(BinaryBotPrivateStart)
 while(watch('before')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateBeforePurchase);
 }
 while(watch('during')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateDuringPurchase);
 }
 BinaryBotPrivateTickAnalysis();
 if(!BinaryBotPrivateRun(BinaryBotPrivateAfterPurchase)) {
   break;
 }
}
       `;
            this.generatedJs = code;
            if (code) {
                this.stop(true);
                this.interpreter = new Interpreter();
                this.interpreter.run(code).catch(e => {
                    globalObserver.emit('Error', e);
                    this.stop();
                });
            }
        } catch (e) {
            globalObserver.emit('Error', e);
            this.stop();
        }
    }
    stop(stopBeforeStart) {
        if (!stopBeforeStart) {
            const $runButtons = $('#runButton, #summaryRunButton');
            const $stopButtons = $('#stopButton, #summaryStopButton');
            if ($runButtons.is(':visible') || $stopButtons.is(':visible')) {
                $runButtons.show();
                $stopButtons.hide();
            }
        }
        if (this.interpreter) {
            this.interpreter.stop();
            this.interpreter = null;
        }
    }
    /* eslint-disable class-methods-use-this */
    undo() {
        Blockly.Events.setGroup('undo');
        Blockly.mainWorkspace.undo();
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    redo() {
        Blockly.mainWorkspace.undo(true);
    }
    /* eslint-disable class-methods-use-this */
    hasStarted() {
        return this.interpreter && this.interpreter.hasStarted();
    }
    /* eslint-enable */
}

// Hooks to override default Blockly behaviour
/* eslint-disable no-unused-expressions */
const originalContextMenuFn = Blockly.ContextMenu.show;
Blockly.ContextMenu.show = (e, menuOptions, rtl) => {
    // Rename 'Clean up blocks'
    menuOptions.some(option => {
        if (option.text === Blockly.Msg.CLEAN_UP) {
            option.text = translate('Rearrange vertically'); // eslint-disable-line no-param-reassign
            return true;
        }
        return false;
    }) &&
        /* Remove delete all blocks, but only when 'Clean up blocks' is available (i.e. workspace)
         * This allows users to still delete root blocks containing blocks
         */
        menuOptions.some((option, i) => {
            if (
                option.text === Blockly.Msg.DELETE_BLOCK ||
                option.text.replace(/[0-9]+/, '%1') === Blockly.Msg.DELETE_X_BLOCKS
            ) {
                menuOptions.splice(i, 1);
                return true;
            }
            return false;
        });
    // Open the Elev.io widget when clicking 'Help'
    // eslint-disable-next-line no-underscore-dangle
    if (window._elev) {
        menuOptions.some(option => {
            if (option.text === Blockly.Msg.HELP) {
                option.callback = () => window._elev.openHome(); // eslint-disable-line no-param-reassign, no-underscore-dangle
                return true;
            }
            return false;
        });
    }
    originalContextMenuFn(e, menuOptions, rtl);
};
/* eslint-enable */
<commit_msg>Refactor to use showDialog() function<commit_after>import './customBlockly';
import blocks from './blocks';
import {
    isMainBlock,
    save,
    disable,
    deleteBlocksLoadedBy,
    addLoadersFirst,
    cleanUpOnLoad,
    addDomAsBlock,
    backwardCompatibility,
    fixCollapsedBlocks,
    fixArgumentAttribute,
    removeUnavailableMarkets,
    strategyHasValidTradeTypeCategory,
} from './utils';
import Interpreter from '../../bot/Interpreter';
import createError from '../../common/error';
import { translate, xml as translateXml } from '../../../common/i18n';
import { getLanguage } from '../../../common/lang';
import { observer as globalObserver } from '../../../common/utils/observer';
import { showDialog } from '../../bot/tools';

const setBeforeUnload = off => {
    if (off) {
        window.onbeforeunload = null;
    } else {
        window.onbeforeunload = () => 'You have some unsaved blocks, do you want to save them before you exit?';
    }
};

const disableStrayBlocks = () => {
    const topBlocks = Blockly.mainWorkspace.getTopBlocks();
    topBlocks.forEach(block => {
        if (
            !isMainBlock(block.type) &&
            ['block_holder', 'tick_analysis', 'loader', 'procedures_defreturn', 'procedures_defnoreturn'].indexOf(
                block.type
            ) < 0 &&
            !block.disabled
        ) {
            disable(block, translate('Blocks must be inside block holders, main blocks or functions'));
        }
    });
};
const disposeBlocksWithLoaders = () => {
    Blockly.mainWorkspace.addChangeListener(ev => {
        setBeforeUnload();
        if (ev.type === 'delete' && ev.oldXml.getAttribute('type') === 'loader' && ev.group !== 'undo') {
            deleteBlocksLoadedBy(ev.blockId, ev.group);
        }
    });
};
const marketsWereRemoved = xml => {
    if (!Array.from(xml.children).every(block => !removeUnavailableMarkets(block))) {
        if (window.trackJs) {
            trackJs.track('Invalid financial market');
        }
        showDialog({
            title  : translate('Warning'),
            text   : [translate('This strategy is not available in your country.')],
            buttons: [
                {
                    text : translate('OK'),
                    class: 'button-primary',
                    click() {
                        $(this).dialog('close');
                    },
                },
            ],
        })
            .then(() => {})
            .catch(() => {});
        return true;
    }
    return false;
};
const loadWorkspace = xml => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    Blockly.Events.setGroup('load');
    Blockly.mainWorkspace.clear();

    Array.from(xml.children).forEach(block => {
        backwardCompatibility(block);
    });

    fixArgumentAttribute(xml);
    Blockly.Xml.domToWorkspace(xml, Blockly.mainWorkspace);
    addLoadersFirst(xml).then(
        () => {
            fixCollapsedBlocks();
            Blockly.Events.setGroup(false);
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            Blockly.Events.setGroup(false);
            throw e;
        }
    );
};

const loadBlocks = (xml, dropEvent = {}) => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    const variables = xml.getElementsByTagName('variables');
    if (variables.length > 0) {
        Blockly.Xml.domToVariables(variables[0], Blockly.mainWorkspace);
    }
    Blockly.Events.setGroup('load');
    addLoadersFirst(xml).then(
        loaders => {
            const addedBlocks = [
                ...loaders,
                ...Array.from(xml.children)
                    .map(block => addDomAsBlock(block))
                    .filter(b => b),
            ];
            cleanUpOnLoad(addedBlocks, dropEvent);
            fixCollapsedBlocks();
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            throw e;
        }
    );
};
const xmlToStr = xml => {
    const serializer = new XMLSerializer();
    return serializer.serializeToString(xml);
};
const addBlocklyTranslation = () => {
    $.ajaxPrefilter(options => {
        options.async = true; // eslint-disable-line no-param-reassign
    });
    let lang = getLanguage();
    if (lang === 'ach') {
        lang = 'en';
    } else if (lang === 'zh_cn') {
        lang = 'zh-hans';
    } else if (lang === 'zh_tw') {
        lang = 'zh-hant';
    }
    return new Promise(resolve => {
        $.getScript(`https://blockly-demo.appspot.com/static/msg/js/${lang}.js`, resolve);
    });
};
const onresize = () => {
    let element = document.getElementById('blocklyArea');
    const blocklyArea = element;
    const blocklyDiv = document.getElementById('blocklyDiv');
    let x = 0;
    let y = 0;
    do {
        x += element.offsetLeft;
        y += element.offsetTop;
        element = element.offsetParent;
    } while (element);
    // Position blocklyDiv over blocklyArea.
    blocklyDiv.style.left = `${x}px`;
    blocklyDiv.style.top = `${y}px`;
    blocklyDiv.style.width = `${blocklyArea.offsetWidth}px`;
    blocklyDiv.style.height = `${blocklyArea.offsetHeight}px`;
};
const render = workspace => () => {
    onresize();
    Blockly.svgResize(workspace);
};
const overrideBlocklyDefaultShape = () => {
    const addDownloadToMenu = block => {
        if (block instanceof Object) {
            // eslint-disable-next-line no-param-reassign, max-len
            block.customContextMenu = function customContextMenu(options) {
                options.push({
                    text    : translate('Download'),
                    enabled : true,
                    callback: () => {
                        const xml = Blockly.Xml.textToDom(
                            '<xml xmlns="http://www.w3.org/1999/xhtml" collection="false"></xml>'
                        );
                        xml.appendChild(Blockly.Xml.blockToDom(this));
                        save('binary-bot-block', true, xml);
                    },
                });
            };
        }
    };
    Object.keys(Blockly.Blocks).forEach(blockName => {
        const downloadDisabledBlocks = ['controls_forEach', 'controls_for', 'variables_get', 'variables_set'];
        if (!downloadDisabledBlocks.includes(blockName)) {
            addDownloadToMenu(Blockly.Blocks[blockName]);
        }
    });
};
const repaintDefaultColours = () => {
    Blockly.Msg.LOGIC_HUE = '#DEDEDE';
    Blockly.Msg.LOOPS_HUE = '#DEDEDE';
    Blockly.Msg.MATH_HUE = '#DEDEDE';
    Blockly.Msg.TEXTS_HUE = '#DEDEDE';
    Blockly.Msg.LISTS_HUE = '#DEDEDE';
    Blockly.Msg.COLOUR_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_DYNAMIC_HUE = '#DEDEDE';
    Blockly.Msg.PROCEDURES_HUE = '#DEDEDE';

    Blockly.Blocks.logic.HUE = '#DEDEDE';
    Blockly.Blocks.loops.HUE = '#DEDEDE';
    Blockly.Blocks.math.HUE = '#DEDEDE';
    Blockly.Blocks.texts.HUE = '#DEDEDE';
    Blockly.Blocks.lists.HUE = '#DEDEDE';
    Blockly.Blocks.colour.HUE = '#DEDEDE';
    Blockly.Blocks.variables.HUE = '#DEDEDE';
    Blockly.Blocks.procedures.HUE = '#DEDEDE';
};

export default class _Blockly {
    constructor() {
        this.blocksXmlStr = '';
        this.generatedJs = '';
        // eslint-disable-next-line no-underscore-dangle
        Blockly.WorkspaceSvg.prototype.preloadAudio_ = () => {}; // https://github.com/google/blockly/issues/299
        this.initPromise = new Promise(resolve => {
            $.get('xml/toolbox.xml', toolboxXml => {
                blocks();
                const workspace = Blockly.inject('blocklyDiv', {
                    toolbox: xmlToStr(translateXml(toolboxXml.getElementsByTagName('xml')[0])),
                    zoom   : {
                        wheel: false,
                    },
                    trashcan: false,
                });
                const renderInstance = render(workspace);
                window.addEventListener('resize', renderInstance, false);
                renderInstance();
                addBlocklyTranslation().then(() => {
                    $.get('xml/main.xml', main => {
                        repaintDefaultColours();
                        overrideBlocklyDefaultShape();
                        this.blocksXmlStr = Blockly.Xml.domToPrettyText(main);
                        Blockly.Xml.domToWorkspace(main.getElementsByTagName('xml')[0], workspace);
                        this.zoomOnPlusMinus();
                        disposeBlocksWithLoaders();
                        setTimeout(() => {
                            setBeforeUnload(true);
                            Blockly.mainWorkspace.cleanUp();
                            Blockly.mainWorkspace.clearUndo();
                        }, 0);
                        resolve();
                    });
                });
            });
        });
    }
    /* eslint-disable class-methods-use-this */
    zoomOnPlusMinus(zoomIn) {
        const metrics = Blockly.mainWorkspace.getMetrics();
        if (zoomIn) {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, 1);
        } else {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, -1);
        }
    }
    resetWorkspace() {
        Blockly.Events.setGroup('reset');
        Blockly.mainWorkspace.clear();
        Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(this.blocksXmlStr), Blockly.mainWorkspace);
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    cleanUp() {
        Blockly.Events.setGroup(true);
        const topBlocks = Blockly.mainWorkspace.getTopBlocks(true);
        let cursorY = 0;
        topBlocks.forEach(block => {
            if (block.getSvgRoot().style.display !== 'none') {
                const xy = block.getRelativeToSurfaceXY();
                block.moveBy(-xy.x, cursorY - xy.y);
                block.snapToGrid();
                cursorY =
                    block.getRelativeToSurfaceXY().y + block.getHeightWidth().height + Blockly.BlockSvg.MIN_BLOCK_Y;
            }
        });
        Blockly.Events.setGroup(false);
        // Fire an event to allow scrollbars to resize.
        Blockly.mainWorkspace.resizeContents();
    }
    /* eslint-disable class-methods-use-this */
    load(blockStr = '', dropEvent = {}) {
        let xml;

        try {
            xml = Blockly.Xml.textToDom(blockStr);
        } catch (e) {
            throw createError('FileLoad', translate('Unrecognized file format.'));
        }

        try {
            if (xml.hasAttribute('collection') && xml.getAttribute('collection') === 'true') {
                loadBlocks(xml, dropEvent);
            } else {
                loadWorkspace(xml);
            }
        } catch (e) {
            throw createError('FileLoad', translate('Unable to load the block file.'));
        }
    }
    /* eslint-disable class-methods-use-this */
    save(arg) {
        const { filename, collection } = arg;

        setBeforeUnload(true);
        const xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace);
        Array.from(xml.children).forEach(blockDom => {
            const blockId = blockDom.getAttribute('id');
            if (!blockId) return;
            const block = Blockly.mainWorkspace.getBlockById(blockId);
            if ('loaderId' in block) {
                blockDom.remove();
            }
        });
        save(filename, collection, xml);
    }
    run(limitations = {}) {
        disableStrayBlocks();
        let code;
        try {
            code = `
var BinaryBotPrivateInit, BinaryBotPrivateStart, BinaryBotPrivateBeforePurchase, BinaryBotPrivateDuringPurchase, BinaryBotPrivateAfterPurchase;

var BinaryBotPrivateLastTickTime
var BinaryBotPrivateTickAnalysisList = [];

function BinaryBotPrivateRun(f, arg) {
 if (f) return f(arg);
 return false;
}

function BinaryBotPrivateTickAnalysis() {
 var currentTickTime = Bot.getLastTick(true).epoch
 if (currentTickTime === BinaryBotPrivateLastTickTime) {
   return
 }
 BinaryBotPrivateLastTickTime = currentTickTime
 for (var BinaryBotPrivateI = 0; BinaryBotPrivateI < BinaryBotPrivateTickAnalysisList.length; BinaryBotPrivateI++) {
   BinaryBotPrivateRun(BinaryBotPrivateTickAnalysisList[BinaryBotPrivateI]);
 }
}

var BinaryBotPrivateLimitations = ${JSON.stringify(limitations)};

${Blockly.JavaScript.workspaceToCode(Blockly.mainWorkspace)}

BinaryBotPrivateRun(BinaryBotPrivateInit);

while(true) {
 BinaryBotPrivateTickAnalysis();
 BinaryBotPrivateRun(BinaryBotPrivateStart)
 while(watch('before')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateBeforePurchase);
 }
 while(watch('during')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateDuringPurchase);
 }
 BinaryBotPrivateTickAnalysis();
 if(!BinaryBotPrivateRun(BinaryBotPrivateAfterPurchase)) {
   break;
 }
}
       `;
            this.generatedJs = code;
            if (code) {
                this.stop(true);
                this.interpreter = new Interpreter();
                this.interpreter.run(code).catch(e => {
                    globalObserver.emit('Error', e);
                    this.stop();
                });
            }
        } catch (e) {
            globalObserver.emit('Error', e);
            this.stop();
        }
    }
    stop(stopBeforeStart) {
        if (!stopBeforeStart) {
            const $runButtons = $('#runButton, #summaryRunButton');
            const $stopButtons = $('#stopButton, #summaryStopButton');
            if ($runButtons.is(':visible') || $stopButtons.is(':visible')) {
                $runButtons.show();
                $stopButtons.hide();
            }
        }
        if (this.interpreter) {
            this.interpreter.stop();
            this.interpreter = null;
        }
    }
    /* eslint-disable class-methods-use-this */
    undo() {
        Blockly.Events.setGroup('undo');
        Blockly.mainWorkspace.undo();
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    redo() {
        Blockly.mainWorkspace.undo(true);
    }
    /* eslint-disable class-methods-use-this */
    hasStarted() {
        return this.interpreter && this.interpreter.hasStarted();
    }
    /* eslint-enable */
}

// Hooks to override default Blockly behaviour
/* eslint-disable no-unused-expressions */
const originalContextMenuFn = Blockly.ContextMenu.show;
Blockly.ContextMenu.show = (e, menuOptions, rtl) => {
    // Rename 'Clean up blocks'
    menuOptions.some(option => {
        if (option.text === Blockly.Msg.CLEAN_UP) {
            option.text = translate('Rearrange vertically'); // eslint-disable-line no-param-reassign
            return true;
        }
        return false;
    }) &&
        /* Remove delete all blocks, but only when 'Clean up blocks' is available (i.e. workspace)
         * This allows users to still delete root blocks containing blocks
         */
        menuOptions.some((option, i) => {
            if (
                option.text === Blockly.Msg.DELETE_BLOCK ||
                option.text.replace(/[0-9]+/, '%1') === Blockly.Msg.DELETE_X_BLOCKS
            ) {
                menuOptions.splice(i, 1);
                return true;
            }
            return false;
        });
    // Open the Elev.io widget when clicking 'Help'
    // eslint-disable-next-line no-underscore-dangle
    if (window._elev) {
        menuOptions.some(option => {
            if (option.text === Blockly.Msg.HELP) {
                option.callback = () => window._elev.openHome(); // eslint-disable-line no-param-reassign, no-underscore-dangle
                return true;
            }
            return false;
        });
    }
    originalContextMenuFn(e, menuOptions, rtl);
};
/* eslint-enable */
<|endoftext|>


<commit_before>'use strict';

var sqlite3 = require('sqlite3');

var loadedReviews = {};

var proto = {
	db: null,
	promise: null,		// Promise resolved once the review has been fully loaded
	eventLog: null,		// Array of event objects
	clients: null,		// Set of web socket clients

	addClient: function (client, events) {
		var me = this;
		me.promise.then(function () {
			me.clients.add(client);
			me.syncClient(client, events);
		});
	},

	removeClient: function (client) {
		var me = this;
		this.promise.then(function () {
			me.clients.delete(client);
		});
	},

	syncClient: function (client, events) {
		var me = this;
		var clientEvents = events.reduce(function (set, event) {
			set.add(event.id + '/' + event.user.toLowerCase());
			return set;
		}, new Set());

		var newToClient = me.eventLog.filter(function (event) {
			return !clientEvents.has(event.id + '/' + event.user.toLowerCase());
		});

		console.log('sending', newToClient.length, 'event(s) to the client');
		client.send(JSON.stringify(newToClient));

		events.forEach(function (event) {
			me.addEvent(client, event);
		});
	},

	addEvent: function (fromClient, event) {
		var me = this;

		me.promise.then(function () {
			// SQLite doesn't store doubles with as much precision as JavaScript, so convert them to strings
			// to prevent data loss
			me.db.run('INSERT INTO log (id, type, user, data) VALUES (?,?,?,?)',
				String(event.id), event.type, event.user, JSON.stringify(event.data), broadcastEvent);
		});

		function broadcastEvent(err) {
			if (err) return;	// The record is already saved

			me.eventLog.push(event);
			console.log('event:', event.id, event.type, event.user);

			var message = JSON.stringify(event);
			for (var ws of me.clients) {
				if (ws !== fromClient) {
					ws.send(message);
				}
			}
		}
	}
};

function Review(index) {
	var obj = loadedReviews[index],
		reviewFilePath = 'reviews/' + index + '.db';
	if (obj) {
		return obj;
	}

	obj = Object.create(proto);
	obj.reviewIndex = index;
	obj.clients = new Set();
	obj.db = new sqlite3.Database(reviewFilePath);
	obj.promise = new Promise(function (resolve, reject) {
		obj.db.run('CREATE TABLE log (id TEXT, type TEXT, user TEXT COLLATE NOCASE, data TEXT,'
				+ ' UNIQUE(id, user) ON CONFLICT ABORT)', createDone);

		function createDone(err) {
			// Don't bother to check err, it'll fail whenver the table already exists
			obj.db.all('SELECT id, type, user, data FROM log ORDER BY rowid ASC', loadDone);
		}
		function loadDone(err, rows) {
			console.log('review', index, 'loaded with', rows.length, 'events');

			obj.eventLog = rows.map(function (row) {
				row.id = Number(row.id);		// Convert IDs back to Numbers
				row.data = JSON.parse(row.data);
				return row;
			});
			resolve();
		}
	})

	loadedReviews[index] = obj;
	return obj;
}

module.exports = Review;<commit_msg>reviews now close when the last client disconnects<commit_after>'use strict';

var sqlite3 = require('sqlite3');

var loadedReviews = {};

var proto = {
	reviewIndex: null,	// 1-based index of this review
	db: null,			// Connection to the review database
	promise: null,		// Promise resolved once the review has been fully loaded
	eventLog: null,		// Array of event objects
	clients: null,		// Set of web socket clients

	addClient: function (client, events) {
		var me = this;
		me.promise.then(function () {
			me.clients.add(client);
			me.syncClient(client, events);
		});
	},

	removeClient: function (client) {
		var me = this;
		this.promise.then(function () {
			me.clients.delete(client);
			me.promise.then(function () {
				if (me.clients.size === 0) {
					me.db.close();
					delete me.db;
					delete me.promise;
					delete loadedReviews[me.reviewIndex];
					console.log('review', me.reviewIndex, 'closed');
				}
			});
		});
	},

	syncClient: function (client, events) {
		var me = this;
		var clientEvents = events.reduce(function (set, event) {
			set.add(event.id + '/' + event.user.toLowerCase());
			return set;
		}, new Set());

		var newToClient = me.eventLog.filter(function (event) {
			return !clientEvents.has(event.id + '/' + event.user.toLowerCase());
		});

		console.log('sending', newToClient.length, 'event(s) to the client');
		client.send(JSON.stringify(newToClient));

		events.forEach(function (event) {
			me.addEvent(client, event);
		});
	},

	addEvent: function (fromClient, event) {
		var me = this;

		me.promise.then(function () {
			// SQLite doesn't store doubles with as much precision as JavaScript, so convert them to strings
			// to prevent data loss
			me.db.run('INSERT INTO log (id, type, user, data) VALUES (?,?,?,?)',
				String(event.id), event.type, event.user, JSON.stringify(event.data), broadcastEvent);
		});

		function broadcastEvent(err) {
			if (err) return;	// The record is already saved

			me.eventLog.push(event);
			console.log('event:', event.id, event.type, event.user);

			var message = JSON.stringify(event);
			for (var ws of me.clients) {
				if (ws !== fromClient) {
					ws.send(message);
				}
			}
		}
	}
};

function Review(index) {
	var obj = loadedReviews[index],
		reviewFilePath = 'reviews/' + index + '.db';
	if (obj) {
		return obj;
	}

	obj = Object.create(proto);
	obj.reviewIndex = index;
	obj.clients = new Set();
	obj.db = new sqlite3.Database(reviewFilePath);
	obj.promise = new Promise(function (resolve, reject) {
		obj.db.run('CREATE TABLE log (id TEXT, type TEXT, user TEXT COLLATE NOCASE, data TEXT,'
				+ ' UNIQUE(id, user) ON CONFLICT ABORT)', createDone);

		function createDone(err) {
			// Don't bother to check err, it'll fail whenver the table already exists
			obj.db.all('SELECT id, type, user, data FROM log ORDER BY rowid ASC', loadDone);
		}
		function loadDone(err, rows) {
			console.log('review', index, 'loaded with', rows.length, 'events');

			obj.eventLog = rows.map(function (row) {
				row.id = Number(row.id);		// Convert IDs back to Numbers
				row.data = JSON.parse(row.data);
				return row;
			});
			resolve();
		}
	})

	loadedReviews[index] = obj;
	return obj;
}

module.exports = Review;<|endoftext|>