<commit_before>/**
 *  Neato SDK Javascript
 *  https://github.com/NeatoRobotics/neato-sdk-js
 *
 *  Copyright (c)2016 Neato Robotics, Inc.
 *  Author: Roberto Ostinelli
 *
 *  Version: 0.8.0
 */
var Neato={user:null};Neato.utils={haveEqualProps:function(e,t){var n;for(n in e)if("undefined"==typeof t[n])return!1;for(n in e)if(e[n])switch(typeof e[n]){case"object":if(!this.haveEqualProps(e[n],t[n]))return!1;break;case"function":if("undefined"==typeof t[n]||"equals"!=n&&e[n].toString()!=t[n].toString())return!1;break;default:if(e[n]!=t[n])return!1}else if(t[n])return!1;for(n in t)if("undefined"==typeof e[n])return!1;return!0},clone:function(e,t){t=t||{};var n={},o=t.except,r=t.only;for(var a in e)!e.hasOwnProperty(a)||-1!==$.inArray(a,o)||"undefined"!=typeof r&&-1===$.inArray(a,r)||(n[a]=e[a]);return n},isNullOrEmptyJSON:function(e){return null==e?!0:jQuery.isEmptyObject(e)?!0:!1}},Neato.User=function(){this.initialize.apply(this,arguments)},Neato.User.prototype={initialize:function(){this.__parseRedirectURIResponse(),this.host="beehive-playground.neatocloud.com",this.oauthHost="beehive-playground.neatocloud.com",this.robots=[],Neato.user=this},getUserInfo:function(){return this.__call("GET","/users/me")},__getRobotMaps:function(e){return this.__call("GET","/users/me/robots/"+e+"/maps")},__getMapDetails:function(e,t){return this.__call("GET","/users/me/robots/"+e+"/maps/"+t)},getRobots:function(){var e=this,t=$.Deferred();return this.__call("GET","/users/me/robots").done(function(n){e.robots=[];for(var o=0;o<n.length;o++)e.robots.push(new Neato.Robot(n[o].serial,n[o].secret_key,{name:n[o].name,model:n[o].model}));t.resolve.call(e,e.robots)}).fail(function(n){t.reject.call(e,n)}),t},getRobotBySerial:function(e){for(var t=0;t<this.robots.length;t++)if(0==this.robots[t].serial.localeCompare(e))return this.robots[t];return null},login:function(e){e=e||{};var t="https://"+this.oauthHost+"/oauth2/authorize?client_id="+e.clientId+"&scope="+e.scopes+"&response_type=token&redirect_uri="+e.redirectUrl;this.__navigateToURL(t)},logout:function(){var e=this;return this.__call("POST","/oauth2/revoke",{token:e.token})},isConnected:function(){var e=this,t=$.Deferred();return this.__sessionsCheck().done(function(n){e.connected=!0,t.resolve.call(e,!0)}).fail(function(n){e.connected=!1,t.reject.call(e,!1)}),t},authenticationError:function(){return void 0!=this.authError},__sessionsCheck:function(){return this.__call("GET","/users/me")},__navigateToURL:function(e){location.replace(e)},__parseRedirectURIResponse:function(){var e=document.location.hash.replace("#","").split("&"),t=this;e.forEach(function(e,n){var o=e.split("=")[0]||"",r=e.split("=")[1]||"";0==o.localeCompare("access_token")?t.token=r:0==o.localeCompare("error")?t.authError=r:0==o.localeCompare("error_description")&&(t.authErrorDescription=r.replace(/\+/g," "))})},__call:function(e,t,n){n=0==e.localeCompare("GET")?n||null:n||{};var o=this,r=$.Deferred(),a="https://"+this.host+t,i="application/vnd.neato.beehive.v1+json",s=new Date,c=s.toUTCString(),l=null==n?null:JSON.stringify(n),u="Bearer "+this.token,d=$.ajax({crossDomain:!0,dataType:"json",method:e,url:a,headers:{Accept:i,Authorization:u,"X-Date":c,"Content-type":"application/json"},data:l});return d.done(function(e,t,n){r.resolve.call(o,n.responseJSON)}).fail(function(e,t,n){r.reject.call(o,e.responseJSON)}),r}},Neato.Robot=function(){this.initialize.apply(this,arguments)},Neato.Robot.prototype={initialize:function(e,t,n){this.serial=e,this.secretKey=t,n=n||{},this.host="nucleo-playground.neatocloud.com",this.port=4443,this.onConnected=n.onConnected,this.onDisconnected=n.onDisconnected,this.onStateChange=n.onStateChange,this.name=n.name,this.model=n.model},connect:function(){var e=this;this.timer=setInterval(function(){e.getState.apply(e)},3e3),this.getState()},disconnect:function(){clearInterval(this.timer),delete this.timer,this.__disconnect()},getState:function(){var e={reqId:"1",cmd:"getRobotState"};return this.__call(e)},getInfo:function(){var e={reqId:"1",cmd:"getRobotInfo"};return this.__call(e)},__call:function(e){var t=this,n=$.Deferred(),o="https://"+this.host+":"+this.port+"/vendors/neato/robots/"+this.serial+"/messages",r="application/vnd.neato.nucleo.v1",a=new Date,i=a.toUTCString(),s=JSON.stringify(e),c=this.serial.toLowerCase()+"\n"+i+"\n"+s,l="NEATOAPP "+CryptoJS.HmacSHA256(c,this.secretKey);return $.ajax({crossDomain:!0,dataType:"json",method:"POST",url:o,headers:{Accept:r,Authorization:l,"X-Date":i},data:s}).done(function(e,o,r){var a=r.responseJSON;t.__setState(a);var i=t.__extractReplyFromReponse(a);"ok"==i.result?n.resolve.call(t,i.result,i.data):n.reject.call(t,i.result,i.data)}).fail(function(e,n,o){t.__disconnect(e.status,e.responseJSON)}),n},__setState:function(e){if("undefined"!=typeof e.state){var t=Neato.utils.clone(e,{except:["version","reqId","result","data"]}),n="undefined"==typeof this.state,o=n||!Neato.utils.haveEqualProps(this.state,t);this.state=t,n&&this.onConnected&&this.onConnected(),o&&this.onStateChange&&this.onStateChange()}},__disconnect:function(e,t){delete this.state,this.onDisconnected&&this.onDisconnected(e,t)},__extractReplyFromReponse:function(e){return Neato.utils.clone(e,{only:["result","data"]})}},Neato.Robot.extend=function(e){for(var t in e)e.hasOwnProperty(t)&&(Neato.Robot.prototype[t]=e[t])},Neato.Robot.extend({findMe:function(){var e={reqId:"1",cmd:"findMe"};return this.__call(e)}}),Neato.Robot.extend({generalInfo:function(){var e={reqId:"1",cmd:"getGeneralInfo"};return this.__call(e)}}),Neato.Robot.extend({startCleaning:function(e){e=e||{};var t={reqId:"1",cmd:"startCleaning",params:{category:e.category,mode:e.mode,modifier:e.modifier,navigationMode:e.navigationMode}};return this.__call(t)},stopCleaning:function(){var e={reqId:"1",cmd:"stopCleaning"};return this.__call(e)},pauseCleaning:function(){var e={reqId:"1",cmd:"pauseCleaning"};return this.__call(e)},resumeCleaning:function(){var e={reqId:"1",cmd:"resumeCleaning"};return this.__call(e)},sendToBase:function(){var e={reqId:"1",cmd:"sendToBase"};return this.__call(e)}}),Neato.Robot.extend({maps:function(){return Neato.user.__getRobotMaps(this.serial)},mapDetails:function(e){return Neato.user.__getMapDetails(this.serial,e)}}),Neato.Robot.extend({setSchedule:function(e){e=e||{};var t=$.map(e,function(e,t){return{mode:e.mode,day:parseInt(t,10),startTime:e.startTime}}),n={reqId:"1",cmd:"setSchedule",params:{type:1,events:t}};return this.__call(n)},getSchedule:function(){var e={reqId:"1",cmd:"getSchedule"};return this.__call(e)},enableSchedule:function(){var e={reqId:"1",cmd:"enableSchedule"};return this.__call(e)},disableSchedule:function(){var e={reqId:"1",cmd:"disableSchedule"};return this.__call(e)}});
<commit_msg>Update neato-0.8.0.min.js minified library.<commit_after>/**
 *  Neato SDK Javascript
 *  https://github.com/NeatoRobotics/neato-sdk-js
 *
 *  Copyright (c)2016 Neato Robotics, Inc.
 *  Author: Roberto Ostinelli
 *
 *  Version: 0.8.0
 */
var Neato={user:null};Neato.utils={haveEqualProps:function(e,t){var n;for(n in e)if("undefined"==typeof t[n])return!1;for(n in e)if(e[n])switch(typeof e[n]){case"object":if(!this.haveEqualProps(e[n],t[n]))return!1;break;case"function":if("undefined"==typeof t[n]||"equals"!=n&&e[n].toString()!=t[n].toString())return!1;break;default:if(e[n]!=t[n])return!1}else if(t[n])return!1;for(n in t)if("undefined"==typeof e[n])return!1;return!0},clone:function(e,t){t=t||{};var n={},o=t.except,r=t.only;for(var a in e)!e.hasOwnProperty(a)||-1!==$.inArray(a,o)||"undefined"!=typeof r&&-1===$.inArray(a,r)||(n[a]=e[a]);return n},isNullOrEmptyJSON:function(e){return null==e?!0:jQuery.isEmptyObject(e)?!0:!1}},Neato.User=function(){this.initialize.apply(this,arguments)},Neato.User.prototype={initialize:function(){this.__parseRedirectURIResponse(),this.host="beehive.neatocloud.com",this.oauthHost="apps.neatorobotics.com",this.robots=[],Neato.user=this},getUserInfo:function(){return this.__call("GET","/users/me")},__getRobotMaps:function(e){return this.__call("GET","/users/me/robots/"+e+"/maps")},__getMapDetails:function(e,t){return this.__call("GET","/users/me/robots/"+e+"/maps/"+t)},getRobots:function(){var e=this,t=$.Deferred();return this.__call("GET","/users/me/robots").done(function(n){e.robots=[];for(var o=0;o<n.length;o++)e.robots.push(new Neato.Robot(n[o].serial,n[o].secret_key,{name:n[o].name,model:n[o].model}));t.resolve.call(e,e.robots)}).fail(function(n){t.reject.call(e,n)}),t},getRobotBySerial:function(e){for(var t=0;t<this.robots.length;t++)if(0==this.robots[t].serial.localeCompare(e))return this.robots[t];return null},login:function(e){e=e||{};var t="https://"+this.oauthHost+"/oauth2/authorize?client_id="+e.clientId+"&scope="+e.scopes+"&response_type=token&redirect_uri="+e.redirectUrl;this.__navigateToURL(t)},logout:function(){var e=this;return this.__call("POST","/oauth2/revoke",{token:e.token})},isConnected:function(){var e=this,t=$.Deferred();return this.__sessionsCheck().done(function(n){e.connected=!0,t.resolve.call(e,!0)}).fail(function(n){e.connected=!1,t.reject.call(e,!1)}),t},authenticationError:function(){return void 0!=this.authError},__sessionsCheck:function(){return this.__call("GET","/users/me")},__navigateToURL:function(e){location.replace(e)},__parseRedirectURIResponse:function(){var e=document.location.hash.replace("#","").split("&"),t=this;e.forEach(function(e,n){var o=e.split("=")[0]||"",r=e.split("=")[1]||"";0==o.localeCompare("access_token")?t.token=r:0==o.localeCompare("error")?t.authError=r:0==o.localeCompare("error_description")&&(t.authErrorDescription=r.replace(/\+/g," "))})},__call:function(e,t,n){n=0==e.localeCompare("GET")?n||null:n||{};var o=this,r=$.Deferred(),a="https://"+this.host+t,i="application/vnd.neato.beehive.v1+json",s=new Date,c=s.toUTCString(),l=null==n?null:JSON.stringify(n),u="Bearer "+this.token,h=$.ajax({crossDomain:!0,dataType:"json",method:e,url:a,headers:{Accept:i,Authorization:u,"X-Date":c,"Content-type":"application/json"},data:l});return h.done(function(e,t,n){r.resolve.call(o,n.responseJSON)}).fail(function(e,t,n){r.reject.call(o,e.responseJSON)}),r}},Neato.Robot=function(){this.initialize.apply(this,arguments)},Neato.Robot.prototype={initialize:function(e,t,n){this.serial=e,this.secretKey=t,n=n||{},this.host="nucleo.neatocloud.com",this.port=4443,this.onConnected=n.onConnected,this.onDisconnected=n.onDisconnected,this.onStateChange=n.onStateChange,this.name=n.name,this.model=n.model},connect:function(){var e=this;this.timer=setInterval(function(){e.getState.apply(e)},3e3),this.getState()},disconnect:function(){clearInterval(this.timer),delete this.timer,this.__disconnect()},getState:function(){var e={reqId:"1",cmd:"getRobotState"};return this.__call(e)},getInfo:function(){var e={reqId:"1",cmd:"getRobotInfo"};return this.__call(e)},__call:function(e){var t=this,n=$.Deferred(),o="https://"+this.host+":"+this.port+"/vendors/neato/robots/"+this.serial+"/messages",r="application/vnd.neato.nucleo.v1",a=new Date,i=a.toUTCString(),s=JSON.stringify(e),c=this.serial.toLowerCase()+"\n"+i+"\n"+s,l="NEATOAPP "+CryptoJS.HmacSHA256(c,this.secretKey);return $.ajax({crossDomain:!0,dataType:"json",method:"POST",url:o,headers:{Accept:r,Authorization:l,"X-Date":i},data:s}).done(function(e,o,r){var a=r.responseJSON;t.__setState(a);var i=t.__extractReplyFromReponse(a);"ok"==i.result?n.resolve.call(t,i.result,i.data):n.reject.call(t,i.result,i.data)}).fail(function(e,n,o){t.__disconnect(e.status,e.responseJSON)}),n},__setState:function(e){if("undefined"!=typeof e.state){var t=Neato.utils.clone(e,{except:["version","reqId","result","data"]}),n="undefined"==typeof this.state,o=n||!Neato.utils.haveEqualProps(this.state,t);this.state=t,n&&this.onConnected&&this.onConnected(),o&&this.onStateChange&&this.onStateChange()}},__disconnect:function(e,t){delete this.state,this.onDisconnected&&this.onDisconnected(e,t)},__extractReplyFromReponse:function(e){return Neato.utils.clone(e,{only:["result","data"]})}},Neato.Robot.extend=function(e){for(var t in e)e.hasOwnProperty(t)&&(Neato.Robot.prototype[t]=e[t])},Neato.Robot.extend({findMe:function(){var e={reqId:"1",cmd:"findMe"};return this.__call(e)}}),Neato.Robot.extend({generalInfo:function(){var e={reqId:"1",cmd:"getGeneralInfo"};return this.__call(e)}}),Neato.Robot.extend({startCleaning:function(e){e=e||{};var t={reqId:"1",cmd:"startCleaning",params:{category:e.category,mode:e.mode,modifier:e.modifier,navigationMode:e.navigationMode}};return this.__call(t)},stopCleaning:function(){var e={reqId:"1",cmd:"stopCleaning"};return this.__call(e)},pauseCleaning:function(){var e={reqId:"1",cmd:"pauseCleaning"};return this.__call(e)},resumeCleaning:function(){var e={reqId:"1",cmd:"resumeCleaning"};return this.__call(e)},sendToBase:function(){var e={reqId:"1",cmd:"sendToBase"};return this.__call(e)}}),Neato.Robot.extend({maps:function(){return Neato.user.__getRobotMaps(this.serial)},mapDetails:function(e){return Neato.user.__getMapDetails(this.serial,e)}}),Neato.Robot.extend({setSchedule:function(e){e=e||{};var t=$.map(e,function(e,t){return{mode:e.mode,day:parseInt(t,10),startTime:e.startTime}}),n={reqId:"1",cmd:"setSchedule",params:{type:1,events:t}};return this.__call(n)},getSchedule:function(){var e={reqId:"1",cmd:"getSchedule"};return this.__call(e)},enableSchedule:function(){var e={reqId:"1",cmd:"enableSchedule"};return this.__call(e)},disableSchedule:function(){var e={reqId:"1",cmd:"disableSchedule"};return this.__call(e)}});
<|endoftext|>


<commit_before>var oBuffer=Buffer;

var _ArrayBuffer_=true;

Buffer.prototype.readUInt=function() {
	switch (this.length) {
		case 1 : return this[0];
		case 2 : return this.readUInt16BE(0);
		case 4 : return this.readUInt32BE(0);
		return 0;
	};
};

//node.js buffer.js
//modified not to throw - see #3159
Buffer.prototype.slice = function(start, end) {
	if (end === undefined) end = this.length;
	if (end > this.length) {
		end = this.length;
		console.log('BUFFER : slice - bad formatted buffer ')
	};
	if (start > end) {
		start=end;
		console.log('BUFFER : slice - bad formatted buffer ')
	};

	return new oBuffer(this.parent, end - start, +start + this.offset);
};

Buffer.prototype.writeUInt=function(val) {
	switch (this.length) {
		case 1 : this.writeUInt8(val,0);break;
		case 2 : this.writeUInt16BE(val,0);break;
		case 4 : this.writeUInt32BE(val,0);break;
	};
	return this;
};

Buffer.prototype.map=function(buff) {
	var l=buff.length;
	for (var i=0;i<l;i++) {
		this[i]=buff[i];
	};
	this.fill(0,l);
};

Array.prototype.concatBuffers = function() {
	var str=[];
	this.forEach(function(val) {
		str.push(val.toString('hex'));
	});
	return new Buffer(str.join(''),'hex');
};

if (_ArrayBuffer_) {
	Buffer=function(a,e) {
		if ((!e)&&(typeof(a)==='string')) {
			e='utf8';
		};
		if ((a instanceof Array) || ((!isNaN(a))&&(!e))) {
			return new Uint8Array(a);
		};
		if (e==='utf8') {
			return (new TextEncoder('utf-8')).encode(a);
		};
		if (e==='hex') {
			var b=new Uint8Array(a.length/2);
			var l=a.length;
			for (var i=0;i<l;i+=2) {
				b[i/2]=parseInt(a[i]+a[i+1],16);
			};
		};
		if (e==='binary') {
			var b=new Uint8Array(a.length);
			var l=b.length;
			for (var i=0;i<l;i++) {
				b[i]=a.charCodeAt(i);
			};
		};
		return b;
	};

	oBuffer.isBuffer=function(b) {
		return b instanceof oBuffer;
	};

	/* See V8 note below */
	Uint8Array.prototype.slice=function(start,end) {
		return this.subarray(start,end);
	};

	Uint8Array.prototype.isBuffer=function(b) {
		return b instanceof Uint8Array;
	};

	Uint8Array.prototype.map=oBuffer.prototype.map;

	Uint8Array.prototype.readUInt=oBuffer.prototype.readUInt;

	Uint8Array.prototype.writeUInt=oBuffer.prototype.writeUInt;

	Uint8Array.prototype.fill=function(val,offset) {
		var l=this.length;
		for (var i=offset;i<l;i++) {
			this[i]=val;
		};
	};
	/*
	previous V8 versions do not implement slice for ArrayBuffers but for Typed Arrays
	replace below this.byteOffset+o by o
	and change above in the slice method : return this.buffer.slice(start,end);
	*/
	Uint8Array.prototype.readUInt16BE=function(o) {
		var a=new DataView(this.buffer);
		return a.getUint16(this.byteOffset+o);
	};

	Uint8Array.prototype.readUInt32BE=function(o) {
		var a=new DataView(this.buffer);
		return a.getUint32(this.byteOffset+o);
	};

	Uint8Array.prototype.writeUInt8=function(val,o) {
		this[o]=val;
	};

	Uint8Array.prototype.writeUInt16BE=function(val,o) {
		var a=new DataView(this.buffer);
		a.setUint16(this.byteOffset+o,val);
	};

	Uint8Array.prototype.writeUInt32BE=function(val,o) {
		var a=new DataView(this.buffer);
		a.setUint32(this.byteOffset+o,val);
	};

	Uint8Array.prototype.readUInt=oBuffer.prototype.readUInt;

	Uint8Array.prototype.writeUInt=oBuffer.prototype.writeUInt;

	Uint8Array.prototype.toString=function(enc) {
		var l=this.length;
		var r=[];
		if (enc==='utf8') {
			return (new TextDecoder('utf-8')).decode(this);
		};
		for (var i=0;i<l;i++) {
			switch (enc) {
				case 'hex' : var tmp=this[i].toString(16);r.push(tmp.length===1?('0'+tmp):tmp);break;
				case 'binary' : r.push(String.fromCharCode(this[i]));break;
			};
		};
		return r.join('');
	};

};

/*
These two functions below are not mandatory if your are in an environment that understands both Buffers and ArrayBuffers (ie node.js)
As long as node.js's Buffers have the same properties as ArrayBuffers and Typed Arrays, you can use both without converting them explicitely
(example : var a buf=Buffer_based_on_typed_array;socket.write(buf);)
They can be usefull in an environment that does not understand Buffers (browsers for example) or ArrayBuffers
*/
var ArrayBufferToBuffer= function(data) {
	if (data instanceof Uint8Array) {
		var a=new oBuffer(data.length);
		a.map(data);
		data=a;
	};
	return data;
};

var BufferToArrayBuffer= function(data) {
	if (!(data instanceof Uint8Array)) {
		var a=new Buffer(data.length);
		a.map(data);
		data=a;
	};
	return data;
};

/* Examples - note that 'new' is not required for ArrayBuffer/Typed Array but used for compatibility reasons with Buffers

	var a=new Buffer('aabbcc','hex');

	var b=a.toString('hex');

	console.log(b);

	a=new Buffer('','utf8');

	console.log(a.toString('utf8'));

	console.log(a.toString('hex'));

	console.log(a.toString('binary'));

	console.log((new Buffer('c3a9c3a8c3a0c3af','hex')).toString('binary'));

	a=new Buffer('10','hex');

	console.log(a.length);

	console.log(a.readUInt());

	a.writeUInt(0x20);

	console.log(a.readUInt());

	a=new Buffer('1020','hex');

	console.log((a.readUInt()).toString(16));

	a.writeUInt(0x2030);

	console.log((a.readUInt()).toString(16));

	a=new Buffer('10203040','hex');

	console.log((a.readUInt()).toString(16));

	a.writeUInt(0x20304050);

	console.log((a.readUInt()).toString(16));

	a=new Buffer(10);

	a[0]=0x10;
	a[1]=0x20;

	console.log(a.toString('hex'));

	a.fill(0x50,3);

	console.log(a.toString('hex'));

	b=a.slice(2);

	console.log(b.toString('hex'));

	b=a.slice(2,4);

	console.log(b.toString('hex'));

	console.log(b.readUInt);

	b=new Buffer(20);

	b.map(a);

	console.log(b.toString('hex'));

	console.log(b instanceof Uint8Array);

	var b=new Buffer('000007000200030000','hex');

	//var b=new Uint8Array([0x000007000200030000]);

	console.log(b instanceof Uint8Array);

	//b.slice=function(start,end) {return this.buffer.slice(start,end);};

	var c=b.slice(5,7);

	//console.log(c);

	console.log(c instanceof Uint8Array);

	console.log(c.readUInt());

*/<commit_msg>Modifs<commit_after>var oBuffer=Buffer;

var _ArrayBuffer_=true;

Buffer.prototype.readUInt=function() {
	switch (this.length) {
		case 1 : return this[0];
		case 2 : return this.readUInt16BE(0);
		case 4 : return this.readUInt32BE(0);
		return 0;
	};
};

//node.js buffer.js
//modified not to throw - see #3159
Buffer.prototype.slice = function(start, end) {
	if (end === undefined) end = this.length;
	if (end > this.length) {
		end = this.length;
		console.log('BUFFER : slice - bad formatted buffer ')
	};
	if (start > end) {
		start=end;
		console.log('BUFFER : slice - bad formatted buffer ')
	};

	return new oBuffer(this.parent, end - start, +start + this.offset);
};

Buffer.prototype.writeUInt=function(val) {
	switch (this.length) {
		case 1 : this.writeUInt8(val,0);break;
		case 2 : this.writeUInt16BE(val,0);break;
		case 4 : this.writeUInt32BE(val,0);break;
	};
	return this;
};

Buffer.prototype.map=function(buff) {
	var l=buff.length;
	for (var i=0;i<l;i++) {
		this[i]=buff[i];
	};
	this.fill(0,l);
};

Array.prototype.concatBuffers = function() {
	var str=[];
	this.forEach(function(val) {
		str.push(val.toString('hex'));
	});
	return new Buffer(str.join(''),'hex');
};

if (_ArrayBuffer_) {
	Buffer=function(a,e) {
		if ((!e)&&(typeof(a)==='string')) {
			e='utf8';
		};
		if ((a instanceof Array) || ((!isNaN(a))&&(!e))) {
			return new Uint8Array(a);
		};
		if (e==='utf8') {
			return (new TextEncoder('utf-8')).encode(a);
		};
		if (e==='hex') {
			var b=new Uint8Array(a.length/2);
			var l=a.length;
			for (var i=0;i<l;i+=2) {
				b[i/2]=parseInt(a[i]+a[i+1],16);
			};
		};
		if (e==='binary') {
			var b=new Uint8Array(a.length);
			var l=b.length;
			for (var i=0;i<l;i++) {
				b[i]=a.charCodeAt(i);
			};
		};
		return b;
	};

	oBuffer.isBuffer=function(b) {
		return b instanceof oBuffer;
	};

	/* See V8 note below */
	Uint8Array.prototype.slice=function(start,end) {
		return this.subarray(start,end);
	};

	Uint8Array.prototype.isBuffer=function(b) {
		return b instanceof Uint8Array;
	};

	Uint8Array.prototype.map=oBuffer.prototype.map;

	Uint8Array.prototype.readUInt=oBuffer.prototype.readUInt;

	Uint8Array.prototype.writeUInt=oBuffer.prototype.writeUInt;

	Uint8Array.prototype.fill=function(val,offset) {
		var l=this.length;
		for (var i=offset;i<l;i++) {
			this[i]=val;
		};
	};
	/*
	previous V8 versions do not implement slice for ArrayBuffers but for Typed Arrays
	replace below this.byteOffset+o by o
	and change above in the slice method : return this.buffer.slice(start,end);
	*/
	Uint8Array.prototype.readUInt16BE=function(o) {
		var a=new DataView(this.buffer);
		return a.getUint16(this.byteOffset+o);
	};

	Uint8Array.prototype.readUInt32BE=function(o) {
		var a=new DataView(this.buffer);
		return a.getUint32(this.byteOffset+o);
	};

	Uint8Array.prototype.writeUInt8=function(val,o) {
		this[o]=val;
	};

	Uint8Array.prototype.writeUInt16BE=function(val,o) {
		var a=new DataView(this.buffer);
		a.setUint16(this.byteOffset+o,val);
	};

	Uint8Array.prototype.writeUInt32BE=function(val,o) {
		var a=new DataView(this.buffer);
		a.setUint32(this.byteOffset+o,val);
	};

	Uint8Array.prototype.readUInt=oBuffer.prototype.readUInt;

	Uint8Array.prototype.writeUInt=oBuffer.prototype.writeUInt;

	Uint8Array.prototype.toString=function(enc) {
		var l=this.length;
		var r=[];
		if (enc==='utf8') {
			return (new TextDecoder('utf-8')).decode(this);
		};
		for (var i=0;i<l;i++) {
			switch (enc) {
				case 'hex' : var tmp=this[i].toString(16);r.push(tmp.length===1?('0'+tmp):tmp);break;
				case 'binary' : r.push(String.fromCharCode(this[i]));break;
			};
		};
		return r.join('');
	};

};

/*
These two functions below are not mandatory if your are in an environment that understands both Buffers and ArrayBuffers (ie node.js)
As long as node.js's Buffers have the same properties as ArrayBuffers and Typed Arrays, you can use both without any conversion
(example : var a buf=Buffer_based_on_typed_array;socket.write(buf);)
They can be usefull in an environment that does not understand Buffers (browsers for example) or ArrayBuffers
*/
var ArrayBufferToBuffer= function(data) {
	if (data instanceof Uint8Array) {
		var a=new oBuffer(data.length);
		a.map(data);
		data=a;
	};
	return data;
};

var BufferToArrayBuffer= function(data) {
	if (!(data instanceof Uint8Array)) {
		var a=new Buffer(data.length);
		a.map(data);
		data=a;
	};
	return data;
};

/* Examples - note that 'new' is not required for ArrayBuffer/Typed Array here but used for compatibility reasons with Buffers or if you want to construct Typed Arrays differently

	var a=new Buffer('aabbcc','hex');

	var b=a.toString('hex');

	console.log(b);

	a=new Buffer('','utf8');

	console.log(a.toString('utf8'));

	console.log(a.toString('hex'));

	console.log(a.toString('binary'));

	console.log((new Buffer('c3a9c3a8c3a0c3af','hex')).toString('binary'));

	a=new Buffer('10','hex');

	console.log(a.length);

	console.log(a.readUInt());

	a.writeUInt(0x20);

	console.log(a.readUInt());

	a=new Buffer('1020','hex');

	console.log((a.readUInt()).toString(16));

	a.writeUInt(0x2030);

	console.log((a.readUInt()).toString(16));

	a=new Buffer('10203040','hex');

	console.log((a.readUInt()).toString(16));

	a.writeUInt(0x20304050);

	console.log((a.readUInt()).toString(16));

	a=new Buffer(10);

	a[0]=0x10;
	a[1]=0x20;

	console.log(a.toString('hex'));

	a.fill(0x50,3);

	console.log(a.toString('hex'));

	b=a.slice(2);

	console.log(b.toString('hex'));

	b=a.slice(2,4);

	console.log(b.toString('hex'));

	console.log(b.readUInt);

	b=new Buffer(20);

	b.map(a);

	console.log(b.toString('hex'));

	console.log(b instanceof Uint8Array);

	var b=new Buffer('000007000200030000','hex');

	//var b=new Uint8Array([0x000007000200030000]);

	console.log(b instanceof Uint8Array);

	//b.slice=function(start,end) {return this.buffer.slice(start,end);};

	var c=b.slice(5,7);

	//console.log(c);

	console.log(c instanceof Uint8Array);

	console.log(c.readUInt());

*/<|endoftext|>


<commit_before>module.exports = normalize

const env = require('../env')
const headers = require('../headers')
const setIfExists = require('../set-if-exists')
const toEntityName = require('../to-entity-name')

function normalize (fixture) {
  // fixture.rawHeaders is an array in the form of ['key1', 'value1', 'key2', 'value2']
  // But the order of these can change, e.g. between local tests and CI on Travis.
  // That’s why we turn them into an object before storing the fixtures and turn
  // them back into an array before loading
  fixture.headers = headers.toObject(fixture.rawHeaders)
  delete fixture.rawHeaders

  // set all IDs in paths to 1
  fixture.path = fixture.path.replace(/\d+$/, 1)
  if (fixture.headers.location) {
    fixture.headers.location = fixture.headers.location.replace(/\d+$/, 1)
  }

  // set all dates to Universe 2017 Keynote time
  setIfExists(fixture.headers, 'date', 'Tue, 10 Oct 2017 16:00:00 GMT')
  setIfExists(fixture.headers, 'last-modified', 'Tue, 10 Oct 2017 16:00:00 GMT')
  setIfExists(fixture.headers, 'x-ratelimit-reset', '1507651200000')

  // Set remaining rate limit to 59 for unauthenticated accounts and
  // to 4999 to authenticated accounts
  const rateLimitRemaining = parseInt(fixture.headers['x-ratelimit-limit'], 10)
  setIfExists(fixture.headers, 'x-ratelimit-remaining', String(rateLimitRemaining - 1))

  // zerofy random stuff
  setIfExists(fixture.headers, 'etag', '"00000000000000000000000000000000"')
  setIfExists(fixture.headers, 'x-runtime-rack', '0.000000')
  setIfExists(fixture.headers, 'x-github-request-id', '0000:00000:0000000:0000000:00000000')

  // zerofy auth token (if present)
  if (fixture.reqheaders.authorization) {
    fixture.reqheaders.authorization = fixture.reqheaders.authorization.replace(/^token \w{40}$/, 'token 0000000000000000000000000000000000000000')
  } else {
    // otherwise add authorization to "badheaders" to help with matching the
    // right fixture to the right request
    fixture.badheaders = ['authorization']
  }

  // workaround for https://github.com/gr2m/octokit-fixtures/issues/3
  if (env.FIXTURES_PROXY) {
    fixture.scope = 'https://api.github.com:443'
    fixture.reqheaders.host = 'api.github.com'
    delete fixture.headers['x-powered-by']
  }

  // leave out headers that tend to change on different environments
  delete fixture.headers['accept-ranges']
  delete fixture.headers.region
  delete fixture.headers.server
  delete fixture.headers.vary

  const entityName = toEntityName(fixture.response)
  if (entityName) {
    require(`./${entityName}`)(fixture.response)
  }

  // update content length
  fixture.headers['content-length'] = String(JSON.stringify(fixture.response).length)

  return fixture
}
<commit_msg>fix(normalization): handle array responses<commit_after>module.exports = normalize

const env = require('../env')
const headers = require('../headers')
const setIfExists = require('../set-if-exists')
const toEntityName = require('../to-entity-name')

function normalize (fixture) {
  // fixture.rawHeaders is an array in the form of ['key1', 'value1', 'key2', 'value2']
  // But the order of these can change, e.g. between local tests and CI on Travis.
  // That’s why we turn them into an object before storing the fixtures and turn
  // them back into an array before loading
  fixture.headers = headers.toObject(fixture.rawHeaders)
  delete fixture.rawHeaders

  // set all IDs in paths to 1
  fixture.path = fixture.path.replace(/\d+$/, 1)
  if (fixture.headers.location) {
    fixture.headers.location = fixture.headers.location.replace(/\d+$/, 1)
  }

  // set all dates to Universe 2017 Keynote time
  setIfExists(fixture.headers, 'date', 'Tue, 10 Oct 2017 16:00:00 GMT')
  setIfExists(fixture.headers, 'last-modified', 'Tue, 10 Oct 2017 16:00:00 GMT')
  setIfExists(fixture.headers, 'x-ratelimit-reset', '1507651200000')

  // Set remaining rate limit to 59 for unauthenticated accounts and
  // to 4999 to authenticated accounts
  const rateLimitRemaining = parseInt(fixture.headers['x-ratelimit-limit'], 10)
  setIfExists(fixture.headers, 'x-ratelimit-remaining', String(rateLimitRemaining - 1))

  // zerofy random stuff
  setIfExists(fixture.headers, 'etag', '"00000000000000000000000000000000"')
  setIfExists(fixture.headers, 'x-runtime-rack', '0.000000')
  setIfExists(fixture.headers, 'x-github-request-id', '0000:00000:0000000:0000000:00000000')

  // zerofy auth token (if present)
  if (fixture.reqheaders.authorization) {
    fixture.reqheaders.authorization = fixture.reqheaders.authorization.replace(/^token \w{40}$/, 'token 0000000000000000000000000000000000000000')
  } else {
    // otherwise add authorization to "badheaders" to help with matching the
    // right fixture to the right request
    fixture.badheaders = ['authorization']
  }

  // workaround for https://github.com/gr2m/octokit-fixtures/issues/3
  if (env.FIXTURES_PROXY) {
    fixture.scope = 'https://api.github.com:443'
    fixture.reqheaders.host = 'api.github.com'
    delete fixture.headers['x-powered-by']
  }

  // leave out headers that tend to change on different environments
  delete fixture.headers['accept-ranges']
  delete fixture.headers.region
  delete fixture.headers.server
  delete fixture.headers.vary

  const responses = Array.isArray(fixture.response) ? fixture.response : [fixture.response]
  responses.forEach(response => {
    const entityName = toEntityName(response)
    if (entityName) {
      require(`./${entityName}`)(response)
    }
  })

  // update content length
  fixture.headers['content-length'] = String(JSON.stringify(fixture.response).length)

  return fixture
}
<|endoftext|>


<commit_before>/*

    Eval Expr

    // // // step 1

    // Write a function "evalExpr" that can solve simples arithmetic calculs;

    // We will pass only positive integer numbers.

    // Exemples:

    // evalExpr(4, 5, "*"); // 20
    // evalExpr(3, 3, "+"); // 6
    // evalExpr(2, 2, "%"); // 0
    // evalExpr(3, 3, "-"); // 0
    // evalExpr(10, 2, "/"); // 5

    // evalExpr(10, 0, "/"); // 0

    // Validation:
    // I want to see at least 5 functions.

    // Forbidden functions:
    // eval

    // // // step 2

    // Code a function "getRandNum" that return a random number between 0(included) and 10 000(included).

    // Validation:
    // really random please.

    // Code a function "getSign" that return a random number between 0(included) and 4(included).

    // Validation:
    // Reuse getRandNum

    // // // step 3

    // Write a function "generateCalculs" that can generate random calculs and display the result in the console;
    // We will pass a number, which is the desired number calculs.

    Tips:
    Reuse getSign ;)

    // // // step 4 - BONUS

    // On a new branch "bonus"

    // Migrate your script to a HTML page in a new file named "bonus.js".
    // You will reuse generateCalculs

    // Try to do a HTML/CSS interface that can:
        - control the number calcul (input);
        - display all strings like "6 + 3 = 9" in the DOM
            - you need to write this string in the DOM
            - Delay the application with "setTimeout", with 150ms between each calculs

    // // // step 5 - SUPER BONUS

    // Stay on your branch "bonus"

    // Add animations when calculs are displayed
    // Add more things as you can done !!
    // Be creative !

    // When it's done, merge it to the "master" and send me a pull request !

    // // // step 6 - ++SUPER BONUS++

    // Done it responsive :)

    Please use Objects it is very nice :)

*/

// write your code below this comment
<commit_msg>Fixed #2<commit_after>/*

    Eval Expr

    // // // step 1

    // Write a function "evalExpr" that can solve simples arithmetic calculs;

    // We will pass only positive integer numbers.

    // Exemples:

    // evalExpr(4, 5, "*"); // 20
    // evalExpr(3, 3, "+"); // 6
    // evalExpr(2, 2, "%"); // 0
    // evalExpr(3, 3, "-"); // 0
    // evalExpr(10, 2, "/"); // 5

    // evalExpr(2, 0, "%"); // 0
    // evalExpr(10, 0, "/"); // 0

    // Validation:
    // I want to see at least 5 functions.

    // Forbidden functions:
    // eval

    // // // step 2

    // Code a function "getRandNum" that return a random number between 0(included) and 10 000(included).

    // Validation:
    // really random please.

    // Code a function "getSign" that return a random number between 0(included) and 4(included).

    // Validation:
    // Reuse getRandNum

    // // // step 3

    // Write a function "generateCalculs" that can generate random calculs and display the result in the console;
    // We will pass a number, which is the desired number calculs.

    Tips:
    Reuse getSign ;)

    // // // step 4 - BONUS

    // On a new branch "bonus"

    // Migrate your script to a HTML page in a new file named "bonus.js".
    // You will reuse generateCalculs

    // Try to do a HTML/CSS interface that can:
        - control the number calcul (input);
        - display all strings like "6 + 3 = 9" in the DOM
            - you need to write this string in the DOM
            - Delay the application with "setTimeout", with 150ms between each calculs

    // // // step 5 - SUPER BONUS

    // Stay on your branch "bonus"

    // Add animations when calculs are displayed
    // Add more things as you can done !!
    // Be creative !

    // When it's done, merge it to the "master" and send me a pull request !

    // // // step 6 - ++SUPER BONUS++

    // Done it responsive :)

    Please use Objects it is very nice :)

*/

// write your code below this comment
<|endoftext|>


<commit_before>(function () {
  var scrollTimer
  var resizeTimer
  var scrollCache
  var activeElement
  var activationStartTime

  window.addEventListener('load', function () {
    createScrollCache()
    markActiveElement()
  })
  window.addEventListener('scroll', function () {
    if (scrollTimer) {
      clearTimeout(scrollTimer)
    }

    scrollTimer = setTimeout(markActiveElement, 10)
  })
  window.addEventListener('resize', function () {
    if (resizeTimer) {
      clearTimeout(resizeTimer)
    }

    resizeTimer = setTimeout(function () {
      createScrollCache()
      markActiveElement()
    }, 10)
  })

  Array.prototype.forEach.call(document.querySelectorAll('a'), function (link) {
    link.addEventListener('click', handleOutboundLinkClicks)
  })

  function createScrollCache () {
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    scrollCache = Array.prototype.map.call(document.querySelectorAll('*[id]'), function (element) {
      return { top: scrollTop + element.getBoundingClientRect().top, id: element.id }
    })

    scrollCache.unshift({
      top: 0
    })

    scrollCache.sort(function (elemA, elemB) {
      return elemA.top - elemB.top
    })

    scrollCache = scrollCache.map(function (item, index, items) {
      if (index === items.length - 1) return { bottom: document.documentElement.scrollHeight, id: item.id }
      return { bottom: items[index + 1].top - 1, id: item.id }
    })
  }

  function markActiveElement () {
    if (!scrollCache) return
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    var nextActiveElement
    var item = scrollCache.find(function (item) {
      return item.bottom > scrollTop
    })
    if (item.id) {
      nextActiveElement = document.querySelector('[href="#' + item.id + '"]')
      if (nextActiveElement === activeElement) return
    }
    activeElement && activeElement.classList.remove('active')
    if (nextActiveElement) {
      if (activationStartTime && activeElement) {
        ga('send', {
          hitType: 'event',
          eventCategory: 'Articles',
          eventAction: 'finish:read',
          eventLabel: activeElement.textContent,
          eventValue: new Date().getTime() - activationStartTime
        })
      }
      activationStartTime = new Date().getTime()
      nextActiveElement.classList.add('active')
      activeElement = nextActiveElement
    }
  }

  function handleOutboundLinkClicks (event) {
    ga('send', 'event', {
      eventCategory: 'Link',
      eventAction: 'click',
      eventLabel: event.target.href,
      transport: 'beacon'
    })
  }
}())
<commit_msg>Improve reade time analytics<commit_after>(function () {
  var scrollTimer
  var resizeTimer
  var scrollCache
  var activeElement
  var activationStartTime

  window.addEventListener('load', function () {
    createScrollCache()
    markActiveElement()
  })
  window.addEventListener('scroll', function () {
    if (scrollTimer) {
      clearTimeout(scrollTimer)
    }

    scrollTimer = setTimeout(markActiveElement, 10)
  })
  window.addEventListener('resize', function () {
    if (resizeTimer) {
      clearTimeout(resizeTimer)
    }

    resizeTimer = setTimeout(function () {
      createScrollCache()
      markActiveElement()
    }, 10)
  })

  Array.prototype.forEach.call(document.querySelectorAll('a'), function (link) {
    link.addEventListener('click', handleOutboundLinkClicks)
  })

  function createScrollCache () {
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    scrollCache = Array.prototype.map.call(document.querySelectorAll('*[id]'), function (element) {
      return { top: scrollTop + element.getBoundingClientRect().top, id: element.id }
    })

    scrollCache.unshift({
      top: 0
    })

    scrollCache.sort(function (elemA, elemB) {
      return elemA.top - elemB.top
    })

    scrollCache = scrollCache.map(function (item, index, items) {
      if (index === items.length - 1) return { bottom: document.documentElement.scrollHeight, id: item.id }
      return { bottom: items[index + 1].top - 1, id: item.id }
    })
  }

  function markActiveElement () {
    if (!scrollCache) return
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    var nextActiveElement
    var readTime
    var item = scrollCache.find(function (item) {
      return item.bottom > scrollTop
    })
    if (item.id) {
      nextActiveElement = document.querySelector('[href="#' + item.id + '"]')
      if (nextActiveElement === activeElement) return
    }
    activeElement && activeElement.classList.remove('active')
    if (nextActiveElement) {
      if (activationStartTime && activeElement) {
        readTime = new Date().getTime() - activationStartTime
        if (readTime > 3000) {
          ga('send', {
            hitType: 'event',
            eventCategory: 'Articles',
            eventAction: 'finish:read',
            eventLabel: activeElement.textContent,
            eventValue: readTime
          })
        }
      }
      activationStartTime = new Date().getTime()
      nextActiveElement.classList.add('active')
      activeElement = nextActiveElement
    }
  }

  function handleOutboundLinkClicks (event) {
    ga('send', 'event', {
      eventCategory: 'Link',
      eventAction: 'click',
      eventLabel: event.target.href,
      transport: 'beacon'
    })
  }
}())
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var moment = require('moment');

var timeago = require('./timeago')();

function init() {

  var openaps = {
    name: 'openaps'
    , label: 'OpenAPS'
    , pluginType: 'pill-status'
  };

  openaps.setProperties = function setProperties (sbx) {
    sbx.offerProperty('openaps', function setopenaps ( ) {
      var openapsStatus = _.findLast(sbx.data.devicestatus, function (status) {
        return sbx.entryMills(status) <= sbx.time && ('openaps' in status);
      });

      var loopStatus = checkLoopStatus(openapsStatus, sbx);
      openapsStatus = openapsStatus || {};
      openapsStatus.status = loopStatus;

      return openapsStatus;
    });
  };

  openaps.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.openaps;

    var info = [];

    info.push({label: 'Status', value: prop.status.label});

    var recent = moment(sbx.time).subtract(15, 'minutes');
    var suggested = prop.openaps && prop.openaps.suggested;
    var suggestedTime = suggested && moment(suggested.timestamp);
    var enacted = prop.openaps && prop.openaps.enacted;
    var enactedTime = enacted && moment(enacted.timestamp);
    var lastLoopTime = suggestedTime && enactedTime ? moment.max(suggestedTime, enactedTime) : enactedTime || suggestedTime;

    function valueString (prefix, value) {
      return value ? prefix + value : '';
    }

    function addedSuggestion() {
      info.push({
        label: timeAt(false, sbx) + timeFormat(suggestedTime, sbx)
        , value: valueString('BG: ', suggested.bg) + valueString(', ', suggested.reason)
      });
    }

    if ('enacted' === prop.status.code) {
      if (suggestedTime.isAfter(enactedTime)) {
        addedSuggestion();
      }
      var canceled = enacted.rate === 0 && enacted.duration === 0;
      info.push({
        label: timeAt(false, sbx) + timeFormat(enactedTime, sbx)
        , value: [
          valueString('BG: ', enacted.bg)
          , ', Temp Basal' + (canceled ? ' Canceled' : ' Started') + ''
          , canceled ? '' : ' ' + enacted.rate.toFixed(2) + ' for ' + enacted.duration + 'm'
          , valueString(', ', enacted.reason)
        ].join('')
      });
    } else if ('looping' === prop.status.code) {
      addedSuggestion();
    } else if (lastLoopTime) {
      info.push({
        label: timeAt(false, sbx) + timeFormat(lastLoopTime, sbx)
        , value: 'Last Loop'
      });
    }

    if (prop.pump && prop.pump.clock) {
      var pumpTime = moment(prop.pump.clock);
      if (pumpTime.isAfter(recent)) {
        info.push({label: 'Last Pump Clock', value: pumpTime.format('LT')});
      }
    }

    if (prop.openaps && prop.openaps.iob) {
      var iobTime = moment(prop.openaps.iob.timestamp);
      if (iobTime.isAfter(recent)) {
        info.push({label: 'IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.iob) + 'U' + timeAt(true, sbx) + timeFormat(iobTime, sbx)});
        info.push({label: 'Bolus IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.bolusiob) + 'U'});
        info.push({label: 'Insulin Activity', value: prop.openaps.iob.activity.toFixed(2) + ' ' + sbx.unitsLabel + '/5m'});
      }
    }

    sbx.pluginBase.updatePillText(openaps, {
      value: timeFormat(prop.status.when, sbx)
      , label: 'OpenAPS ' + prop.status.symbol
      , info: info
    });
  };

  return openaps;

}

function checkLoopStatus (openapsStatus, sbx)  {

  var status = {
    symbol: '⚠'
    , code: 'warning'
    , label: 'Warning'
  };

  if (openapsStatus) {
    var enactedLast = openapsStatus.openaps.enacted && moment(openapsStatus.openaps.enacted.timestamp);
    var suggestedLast = openapsStatus.openaps.suggested && moment(openapsStatus.openaps.suggested.timestamp);

    var last =  moment(openapsStatus.mills);
    var recent = moment(sbx.time).subtract(15, 'minutes');

    if (enactedLast && enactedLast.isAfter(recent)) {
      status.symbol = '⌁';
      status.code = 'enacted';
      status.label = 'Enacted';
    } else if (suggestedLast && suggestedLast.isAfter(recent)) {
      status.symbol = '↻';
      status.code = 'looping';
      status.label = 'Looping';
    } else if (last && last.isAfter(recent)) {
      status.symbol = '◉';
      status.code = 'waiting';
      status.label = 'Waiting';
    }

    status.when = last;
  }

  return status;
}

function timeFormat (m, sbx) {

  var when;
  if (m && sbx.data.inRetroMode) {
    when = m.format('LT');
  } else if (m) {
    var ago = timeago.calcDisplay({mills: m.valueOf()}, sbx.time);
    when = (ago.value ? ago.value : '') + ago.shortLabel + (ago.shortLabel.length === 1 ? ' ago' : '');
  } else {
    when = 'unknown';
  }

  return when;
}

function timeAt (prefix, sbx) {
  return sbx.data.inRetroMode ? (prefix ? ' ' : '') + '@ ' : (prefix ? ', ' : '');
}

module.exports = init;<commit_msg>remove Insulin Activity from the OpenAPS tooltip, add Snooze to Bolus IOB<commit_after>'use strict';

var _ = require('lodash');
var moment = require('moment');

var timeago = require('./timeago')();

function init() {

  var openaps = {
    name: 'openaps'
    , label: 'OpenAPS'
    , pluginType: 'pill-status'
  };

  openaps.setProperties = function setProperties (sbx) {
    sbx.offerProperty('openaps', function setopenaps ( ) {
      var openapsStatus = _.findLast(sbx.data.devicestatus, function (status) {
        return sbx.entryMills(status) <= sbx.time && ('openaps' in status);
      });

      var loopStatus = checkLoopStatus(openapsStatus, sbx);
      openapsStatus = openapsStatus || {};
      openapsStatus.status = loopStatus;

      return openapsStatus;
    });
  };

  openaps.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.openaps;

    var info = [];

    info.push({label: 'Status', value: prop.status.label});

    var recent = moment(sbx.time).subtract(15, 'minutes');
    var suggested = prop.openaps && prop.openaps.suggested;
    var suggestedTime = suggested && moment(suggested.timestamp);
    var enacted = prop.openaps && prop.openaps.enacted;
    var enactedTime = enacted && moment(enacted.timestamp);
    var lastLoopTime = suggestedTime && enactedTime ? moment.max(suggestedTime, enactedTime) : enactedTime || suggestedTime;

    function valueString (prefix, value) {
      return value ? prefix + value : '';
    }

    function addedSuggestion() {
      info.push({
        label: timeAt(false, sbx) + timeFormat(suggestedTime, sbx)
        , value: valueString('BG: ', suggested.bg) + valueString(', ', suggested.reason)
      });
    }

    if ('enacted' === prop.status.code) {
      if (suggestedTime.isAfter(enactedTime)) {
        addedSuggestion();
      }
      var canceled = enacted.rate === 0 && enacted.duration === 0;
      info.push({
        label: timeAt(false, sbx) + timeFormat(enactedTime, sbx)
        , value: [
          valueString('BG: ', enacted.bg)
          , ', Temp Basal' + (canceled ? ' Canceled' : ' Started') + ''
          , canceled ? '' : ' ' + enacted.rate.toFixed(2) + ' for ' + enacted.duration + 'm'
          , valueString(', ', enacted.reason)
        ].join('')
      });
    } else if ('looping' === prop.status.code) {
      addedSuggestion();
    } else if (lastLoopTime) {
      info.push({
        label: timeAt(false, sbx) + timeFormat(lastLoopTime, sbx)
        , value: 'Last Loop'
      });
    }

    if (prop.pump && prop.pump.clock) {
      var pumpTime = moment(prop.pump.clock);
      if (pumpTime.isAfter(recent)) {
        info.push({label: 'Last Pump Clock', value: pumpTime.format('LT')});
      }
    }

    if (prop.openaps && prop.openaps.iob) {
      var iobTime = moment(prop.openaps.iob.timestamp);
      if (iobTime.isAfter(recent)) {
        info.push({label: 'IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.iob) + 'U' + timeAt(true, sbx) + timeFormat(iobTime, sbx)});
        info.push({label: 'Bolus Snooze IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.bolusiob) + 'U'});
      }
    }

    sbx.pluginBase.updatePillText(openaps, {
      value: timeFormat(prop.status.when, sbx)
      , label: 'OpenAPS ' + prop.status.symbol
      , info: info
    });
  };

  return openaps;

}

function checkLoopStatus (openapsStatus, sbx)  {

  var status = {
    symbol: '⚠'
    , code: 'warning'
    , label: 'Warning'
  };

  if (openapsStatus) {
    var enactedLast = openapsStatus.openaps.enacted && moment(openapsStatus.openaps.enacted.timestamp);
    var suggestedLast = openapsStatus.openaps.suggested && moment(openapsStatus.openaps.suggested.timestamp);

    var last =  moment(openapsStatus.mills);
    var recent = moment(sbx.time).subtract(15, 'minutes');

    if (enactedLast && enactedLast.isAfter(recent)) {
      status.symbol = '⌁';
      status.code = 'enacted';
      status.label = 'Enacted';
    } else if (suggestedLast && suggestedLast.isAfter(recent)) {
      status.symbol = '↻';
      status.code = 'looping';
      status.label = 'Looping';
    } else if (last && last.isAfter(recent)) {
      status.symbol = '◉';
      status.code = 'waiting';
      status.label = 'Waiting';
    }

    status.when = last;
  }

  return status;
}

function timeFormat (m, sbx) {

  var when;
  if (m && sbx.data.inRetroMode) {
    when = m.format('LT');
  } else if (m) {
    var ago = timeago.calcDisplay({mills: m.valueOf()}, sbx.time);
    when = (ago.value ? ago.value : '') + ago.shortLabel + (ago.shortLabel.length === 1 ? ' ago' : '');
  } else {
    when = 'unknown';
  }

  return when;
}

function timeAt (prefix, sbx) {
  return sbx.data.inRetroMode ? (prefix ? ' ' : '') + '@ ' : (prefix ? ', ' : '');
}

module.exports = init;<|endoftext|>


<commit_before>// Core routines for event emitters
//

var lib = require('./lib')
  , url = require('url')
  , util = require('util')
  , request = require('request')
  , querystring = require('querystring')
  ;

var DEFAULT_HEARTBEAT = 30000;
var HEARTBEAT_TIMEOUT_COEFFICIENT = 1.25; // E.g. heartbeat 1000ms would trigger a timeout after 1250ms of no heartbeat.
var DEFAULT_MAX_RETRY_SECONDS     = 60 * 60;
var INITIAL_RETRY_DELAY           = 1000;

var FEED_PARAMETERS   = ['since', 'limit', 'feed', 'heartbeat', 'filter', 'include_docs'];

var SUPER_CLASS = require('events').EventEmitter;
//var SUPER_CLASS = require('stream').Stream;

function Feed (opts) {
  var self = this;
  SUPER_CLASS.call(self);

  self.feed = 'continuous';
  self.heartbeat         = DEFAULT_HEARTBEAT;
  self.max_retry_seconds = DEFAULT_MAX_RETRY_SECONDS;
  self.inactivity_ms = null;

  self.headers = {};
  self.request = {}; // Extra options for potentially future versions of request. The caller can supply them.

  self.since = 0;
  self.retry_delay = INITIAL_RETRY_DELAY; // ms

  opts = opts || {};
  if(typeof opts === 'string')
    opts = {'db': opts};
  Object.keys(opts).forEach(function(key) {
    self[key] = opts[key];
  })

  self.pending = { request     : null
                 , activity_at : null
                 , data        : null
                 };

  /*
  // Debugging help
  var events = ['drain', 'error', 'close', 'pipe', 'data', 'end', 'fd'];

  events.forEach(function(ev) {
    //self.on(ev, function() { return self['on_' + ev].apply(self, arguments) });
    self.on(ev, function() {
      var args = Array.prototype.slice.call(arguments);
      self.log.debug('EVENT ' + ev + ' ' + lib.JS(args));
    })
  })
  */

} // Feed
util.inherits(Feed, SUPER_CLASS);

Feed.prototype.start =
Feed.prototype.follow = function follow_feed() {
  var self = this;

  if(!self.db)
    throw new Error('Database URL required');

  if(self.feed !== 'continuous')
    throw new Error('The only valid feed option is "continuous"');

  if(typeof self.heartbeat !== 'number')
    throw new Error('Required "heartbeat" value');

  var parsed = url.parse(self.db);
  self.log = lib.log4js().getLogger(parsed.hostname + parsed.pathname);
  self.log.setLevel(process.env.changes_level || "info");

  return self.confirm();
}

Feed.prototype.confirm = function confirm_feed() {
  var self = this;

  self.db_safe = lib.scrub_creds(self.db);

  self.log.debug('Checking database: ' + self.db_safe);
  self.emit('confirm');

  var confirm_timeout = self.heartbeat * 3; // Give it time to look up the name, connect, etc.
  var timeout_id = setTimeout(function() {
    return self.die(new Error('Timeout confirming database: ' + self.db_safe));
  }, confirm_timeout);

  var headers = lib.JP(lib.JS(self.headers));
  headers.accept = 'application/json';

  request({uri:self.db, headers:headers}, function(er, resp, body) {
    clearTimeout(timeout_id);

    if(er)
      return self.die(er);

    var db;
    try {
      db = JSON.parse(body)
    } catch(json_er) {
      return self.emit('error', json_er)
    }

    if(!db.db_name || !db.instance_start_time)
      return self.emit('error', new Error('Bad DB response: ' + body));

    self.log.debug('Confirmed db: ' + self.db_safe);

    if(self.since === 'now') {
      self.since = db.update_seq;
      self.log.debug('Query since "now" will start at ' + self.since);
    }

    return self.query();
  })
}

Feed.prototype.query = function query_feed() {
  var self = this;

  var query_params = {};
  FEED_PARAMETERS.forEach(function(key) {
    if(key in self)
      query_params[key] = self[key];
  })

  if(typeof query_params.filter !== 'string')
    delete query_params.filter;

  var feed_url = self.db + '/_changes?' + querystring.stringify(query_params);

  self.headers.accept = self.headers.accept || 'application/json';
  var req = { method : 'GET'
            , uri    : feed_url
            , headers: self.headers
            , encoding: 'utf-8'
            }

  req.changes_query = query_params;
  Object.keys(self.request).forEach(function(key) {
    req[key] = self.request[key];
  })

  var now = new Date;
  self.log.debug('Query at ' + lib.JP(lib.JS(now)) + ': ' + lib.scrub_creds(feed_url));

  var timeout_id, timed_out = false;
  var in_flight, timeout_id, timed_out = false;

  var timed_out = false;
  function on_timeout() {
    self.log.debug('Request timeout: ' + in_flight.id());
    timed_out = true;
    return self.retry();
  }

  function on_response(er, resp) {
    clearTimeout(timeout_id);

    if(timed_out) {
      self.log.debug('Ignoring late response: ' + in_flight.id());
      return destroy_response(resp);
    }

    if(er) {
      self.log.debug('Request error ' + in_flight.id() + ': ' + er.stack);
      destroy_response(resp);
      return self.retry();
    }

    if(resp.statusCode !== 200) {
      self.log.debug('Bad changes response' + in_flight.id() + ': ' + resp.statusCode);
      destroy_response(resp);
      return self.retry();
    }

    self.log.debug('Good response: ' + in_flight.id());
    self.retry_delay = INITIAL_RETRY_DELAY;
    return self.prep(in_flight);
  }

  req.onResponse = on_response;
  timeout_id = setTimeout(on_timeout, self.heartbeat);
  in_flight = request(req);
  in_flight.created_at = now;
  in_flight.id = function() { return lib.JP(lib.JS(this.created_at)) };

  // Shorten the timestamp, used for debugging.
  //in_flight.id = function() { return /\.(\d\d\d)Z$/.exec(lib.JP(lib.JS(this.created_at)))[1] };

  return self.emit('query');
}

Feed.prototype.prep = function prep_request(req) {
  var self = this;

  var now = new Date;
  self.pending.request = req;
  self.pending.activity_at = now;
  self.pending.data        = "";
  self.pending.wait_timer  = null;

  function handler_for(ev) {
    var name = 'on_couch_' + ev;
    var inner_handler = self[name];

    function handle_confirmed_req_event() {
      if(self.pending.request === req)
        return inner_handler.apply(self, arguments);

      if(!req.created_at)
        return self.die(new Error("Received data from unknown request")); // Pretty sure this is impossible.

      var s_to_now = (new Date() - req.created_at) / 1000;
      var s_to_req = '[no req]';
      if(self.pending.request)
        s_to_req = (self.pending.request.created_at - req.created_at) / 1000;

      var msg = ': ' + req.id() + ' to_req=' + s_to_req + 's, to_now=' + s_to_now + 's';

      if(ev === 'end') {
        return self.log.debug('Old END' + msg);
        return destroy_req(req);
      }

      if(ev === 'data') {
        self.log.debug('Old DATA' + msg);
        return destroy_req(req);
      }

      self.log.warn('Old "'+ev+'"' + msg);
    }

    return handle_confirmed_req_event;
  }

  var handlers = ['data', 'end', 'error'];
  handlers.forEach(function(ev) {
    req.on(ev, handler_for(ev));
  })

  // The inactivity timer is for time between *changes*, or time between the
  // initial connection and the first change. Therefore it goes here.
  self.change_at = now;
  if(self.inactivity_ms)
    self.inactivity_timer = setTimeout(function() { self.on_inactivity() }, self.inactivity_ms);

  return self.wait();
}

Feed.prototype.wait = function wait_for_event() {
  var self = this;
  self.emit('wait');

  if(self.pending.wait_timer)
    return self.die(new Error('wait() called but there is already a wait_timer: ' + self.pending.wait_timer));

  var timeout_ms = self.heartbeat * HEARTBEAT_TIMEOUT_COEFFICIENT;
  var msg = 'Req ' + self.pending.request.id() + ' timeout=' + timeout_ms;
  if(self.inactivity_ms)
    msg += ', inactivity=' + self.inactivity_ms;
  msg += ': ' + self.db_safe;

  self.log.debug(msg);
  self.pending.wait_timer = setTimeout(function() { self.on_timeout() }, timeout_ms);
}

Feed.prototype.on_couch_data = function on_couch_data(data, req) {
  var self = this;

  if(! self.pending.wait_timer)
    return self.die(new Error('Cannot find timeout timer during incoming data'));

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;
  self.pending.activity_at = new Date;

  self.log.debug('Data from ' + self.pending.request.id());

  // Buf could have 0, 1, or many JSON objects in it.
  var buf = self.pending.data + data;
  var offset, json, change;

  while((offset = buf.indexOf("\n")) >= 0) {
    json = buf.substr(0, offset);
    buf = buf.substr(offset + 1);

    if(json == '') {
      self.log.debug('Heartbeat: ' + self.pending.request.id());
    } else {
      //self.log.debug('JSON: ' + json);
      try {
        change = JSON.parse(json);
      } catch(er) {
        return self.die(er);
      }

      //self.log.debug('Object:\n' + util.inspect(change));

      seq = change.seq;
      if(!seq)
        return self.die(new Error('Change has no .seq field: ' + json));

      self.on_change(change);
    }
  }

  self.pending.data = buf;
  self.wait();
}

Feed.prototype.on_timeout = function on_timeout() {
  var self = this;

  var now = new Date;
  var elapsed_ms = now - self.pending.activity_at;
  self.log.warn('Closing req ' + self.pending.request.id() + ' for timeout after ' + elapsed_ms + 'ms; heartbeat=' + self.heartbeat);

  return destroy_req(self.pending.request);
  //return self.retry();
}

Feed.prototype.retry = function retry() {
  var self = this;

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;

  self.log.info('Retrying since=' + self.since + ' after ' + self.retry_delay + 'ms: ' + self.db_safe);
  self.emit('retry');

  setTimeout(function() { self.query() }, self.retry_delay);

  var max_retry_ms = self.max_retry_seconds * 1000;
  self.retry_delay *= 2;
  if(self.retry_delay > max_retry_ms)
    self.retry_delay = max_retry_ms;
}

Feed.prototype.on_couch_end = function on_couch_end() {
  var self = this;

  self.log.debug('Changes feed ended ' + self.pending.request.id());
  self.pending.request = null;
  return self.retry();
}

Feed.prototype.on_couch_error = function on_couch_error(er) {
  var self = this;

  self.log.debug('Changes query eror: ' + lib.JS(er.stack));
  return self.retry();
}

Feed.prototype.die = function(er) {
  var self = this;

  self.log.fatal('Fatal error: ' + er.stack);

  self.emit('error', er);

  var req = self.pending.request;
  self.pending.request = null;
  if(req) {
    self.log.debug('Destroying req ' + req.id());
    destroy_req(req);
  }

  //throw er;
}

Feed.prototype.on_change = function on_change(change) {
  var self = this;

  if(!change.seq)
    return self.die(new Error('No seq value in change: ' + lib.JS(change)));

  if(change.seq <= self.since) {
    self.log.debug('Bad seq value ' + change.seq + ' since=' + self.since);
    return destroy_req(self.pending.request);
  }

  if(typeof self.filter !== 'function')
    return self.on_good_change(change);

  var req = { 'query': lib.JDUP(self.pending.request.changes_query) };
  var f_change = lib.JDUP(change); // Don't let the filter mutate the real data.
  var result = self.filter.apply(null, [f_change, req]);
  result = (result && true) || false;
  if(result) {
    self.log.debug('Builtin filter PASS for change: ' + change.seq);
    return self.on_good_change(change);
  } else
    self.log.debug('Builtin filter FAIL for change: ' + change.seq);
}

Feed.prototype.on_good_change = function on_good_change(change) {
  var self = this;

  if(self.inactivity_ms && !self.inactivity_timer)
    return self.die(new Error('Cannot find inactivity timer during change'));

  clearTimeout(self.inactivity_timer);
  self.inactivity_timer = null;
  if(self.inactivity_ms)
    self.inactivity_timer = setTimeout(function() { self.on_inactivity() }, self.inactivity_ms);

  self.change_at = new Date;
  self.since = change.seq;
  self.emit('change', change);
}

Feed.prototype.on_inactivity = function on_inactivity() {
  var self = this;
  var now = new Date;
  var elapsed_ms = now - self.change_at;
  var elapsed_s  = elapsed_ms / 1000;

  return self.die(new Error('Req ' + self.pending.request.id() + ' made no changes for ' + elapsed_s + 's'));
}

module.exports = { "Feed" : Feed
                 };

/*
 * Utilities
 */

function destroy_req(req) {
  if(req)
    return destroy_response(req.response);
}

function destroy_response(response) {
  if(!response)
    return;

  response.connection.end();
  response.connection.destroy();
}
<commit_msg>Client-side filter fix, enforce include_docs<commit_after>// Core routines for event emitters
//

var lib = require('./lib')
  , url = require('url')
  , util = require('util')
  , request = require('request')
  , querystring = require('querystring')
  ;

var DEFAULT_HEARTBEAT = 30000;
var HEARTBEAT_TIMEOUT_COEFFICIENT = 1.25; // E.g. heartbeat 1000ms would trigger a timeout after 1250ms of no heartbeat.
var DEFAULT_MAX_RETRY_SECONDS     = 60 * 60;
var INITIAL_RETRY_DELAY           = 1000;

var FEED_PARAMETERS   = ['since', 'limit', 'feed', 'heartbeat', 'filter', 'include_docs'];

var SUPER_CLASS = require('events').EventEmitter;
//var SUPER_CLASS = require('stream').Stream;

function Feed (opts) {
  var self = this;
  SUPER_CLASS.call(self);

  self.feed = 'continuous';
  self.heartbeat         = DEFAULT_HEARTBEAT;
  self.max_retry_seconds = DEFAULT_MAX_RETRY_SECONDS;
  self.inactivity_ms = null;

  self.headers = {};
  self.request = {}; // Extra options for potentially future versions of request. The caller can supply them.

  self.since = 0;
  self.retry_delay = INITIAL_RETRY_DELAY; // ms

  opts = opts || {};
  if(typeof opts === 'string')
    opts = {'db': opts};
  Object.keys(opts).forEach(function(key) {
    self[key] = opts[key];
  })

  self.pending = { request     : null
                 , activity_at : null
                 , data        : null
                 };

  /*
  // Debugging help
  var events = ['drain', 'error', 'close', 'pipe', 'data', 'end', 'fd'];

  events.forEach(function(ev) {
    //self.on(ev, function() { return self['on_' + ev].apply(self, arguments) });
    self.on(ev, function() {
      var args = Array.prototype.slice.call(arguments);
      self.log.debug('EVENT ' + ev + ' ' + lib.JS(args));
    })
  })
  */

} // Feed
util.inherits(Feed, SUPER_CLASS);

Feed.prototype.start =
Feed.prototype.follow = function follow_feed() {
  var self = this;

  if(!self.db)
    throw new Error('Database URL required');

  if(self.feed !== 'continuous')
    throw new Error('The only valid feed option is "continuous"');

  if(typeof self.heartbeat !== 'number')
    throw new Error('Required "heartbeat" value');

  var parsed = url.parse(self.db);
  self.log = lib.log4js().getLogger(parsed.hostname + parsed.pathname);
  self.log.setLevel(process.env.changes_level || "info");

  return self.confirm();
}

Feed.prototype.confirm = function confirm_feed() {
  var self = this;

  self.db_safe = lib.scrub_creds(self.db);

  self.log.debug('Checking database: ' + self.db_safe);
  self.emit('confirm');

  var confirm_timeout = self.heartbeat * 3; // Give it time to look up the name, connect, etc.
  var timeout_id = setTimeout(function() {
    return self.die(new Error('Timeout confirming database: ' + self.db_safe));
  }, confirm_timeout);

  var headers = lib.JP(lib.JS(self.headers));
  headers.accept = 'application/json';

  request({uri:self.db, headers:headers}, function(er, resp, body) {
    clearTimeout(timeout_id);

    if(er)
      return self.die(er);

    var db;
    try {
      db = JSON.parse(body)
    } catch(json_er) {
      return self.emit('error', json_er)
    }

    if(!db.db_name || !db.instance_start_time)
      return self.emit('error', new Error('Bad DB response: ' + body));

    self.log.debug('Confirmed db: ' + self.db_safe);

    if(self.since === 'now') {
      self.since = db.update_seq;
      self.log.debug('Query since "now" will start at ' + self.since);
    }

    return self.query();
  })
}

Feed.prototype.query = function query_feed() {
  var self = this;

  var query_params = {};
  FEED_PARAMETERS.forEach(function(key) {
    if(key in self)
      query_params[key] = self[key];
  })

  if(typeof query_params.filter !== 'string')
    delete query_params.filter;

  if(typeof self.filter === 'function') {
    self.log.debug('Enabling include_docs for client-side filter');
    query_params.include_docs = true;
  }

  var feed_url = self.db + '/_changes?' + querystring.stringify(query_params);

  self.headers.accept = self.headers.accept || 'application/json';
  var req = { method : 'GET'
            , uri    : feed_url
            , headers: self.headers
            , encoding: 'utf-8'
            }

  req.changes_query = query_params;
  Object.keys(self.request).forEach(function(key) {
    req[key] = self.request[key];
  })

  var now = new Date;
  self.log.debug('Query at ' + lib.JP(lib.JS(now)) + ': ' + lib.scrub_creds(feed_url));

  var timeout_id, timed_out = false;
  var in_flight, timeout_id, timed_out = false;

  var timed_out = false;
  function on_timeout() {
    self.log.debug('Request timeout: ' + in_flight.id());
    timed_out = true;
    return self.retry();
  }

  function on_response(er, resp) {
    clearTimeout(timeout_id);

    if(timed_out) {
      self.log.debug('Ignoring late response: ' + in_flight.id());
      return destroy_response(resp);
    }

    if(er) {
      self.log.debug('Request error ' + in_flight.id() + ': ' + er.stack);
      destroy_response(resp);
      return self.retry();
    }

    if(resp.statusCode !== 200) {
      self.log.debug('Bad changes response' + in_flight.id() + ': ' + resp.statusCode);
      destroy_response(resp);
      return self.retry();
    }

    self.log.debug('Good response: ' + in_flight.id());
    self.retry_delay = INITIAL_RETRY_DELAY;
    return self.prep(in_flight);
  }

  req.onResponse = on_response;
  timeout_id = setTimeout(on_timeout, self.heartbeat);
  in_flight = request(req);
  in_flight.created_at = now;
  in_flight.id = function() { return lib.JP(lib.JS(this.created_at)) };

  // Shorten the timestamp, used for debugging.
  //in_flight.id = function() { return /\.(\d\d\d)Z$/.exec(lib.JP(lib.JS(this.created_at)))[1] };

  return self.emit('query');
}

Feed.prototype.prep = function prep_request(req) {
  var self = this;

  var now = new Date;
  self.pending.request = req;
  self.pending.activity_at = now;
  self.pending.data        = "";
  self.pending.wait_timer  = null;

  function handler_for(ev) {
    var name = 'on_couch_' + ev;
    var inner_handler = self[name];

    function handle_confirmed_req_event() {
      if(self.pending.request === req)
        return inner_handler.apply(self, arguments);

      if(!req.created_at)
        return self.die(new Error("Received data from unknown request")); // Pretty sure this is impossible.

      var s_to_now = (new Date() - req.created_at) / 1000;
      var s_to_req = '[no req]';
      if(self.pending.request)
        s_to_req = (self.pending.request.created_at - req.created_at) / 1000;

      var msg = ': ' + req.id() + ' to_req=' + s_to_req + 's, to_now=' + s_to_now + 's';

      if(ev === 'end') {
        return self.log.debug('Old END' + msg);
        return destroy_req(req);
      }

      if(ev === 'data') {
        self.log.debug('Old DATA' + msg);
        return destroy_req(req);
      }

      self.log.warn('Old "'+ev+'"' + msg);
    }

    return handle_confirmed_req_event;
  }

  var handlers = ['data', 'end', 'error'];
  handlers.forEach(function(ev) {
    req.on(ev, handler_for(ev));
  })

  // The inactivity timer is for time between *changes*, or time between the
  // initial connection and the first change. Therefore it goes here.
  self.change_at = now;
  if(self.inactivity_ms)
    self.inactivity_timer = setTimeout(function() { self.on_inactivity() }, self.inactivity_ms);

  return self.wait();
}

Feed.prototype.wait = function wait_for_event() {
  var self = this;
  self.emit('wait');

  if(self.pending.wait_timer)
    return self.die(new Error('wait() called but there is already a wait_timer: ' + self.pending.wait_timer));

  var timeout_ms = self.heartbeat * HEARTBEAT_TIMEOUT_COEFFICIENT;
  var msg = 'Req ' + self.pending.request.id() + ' timeout=' + timeout_ms;
  if(self.inactivity_ms)
    msg += ', inactivity=' + self.inactivity_ms;
  msg += ': ' + self.db_safe;

  self.log.debug(msg);
  self.pending.wait_timer = setTimeout(function() { self.on_timeout() }, timeout_ms);
}

Feed.prototype.on_couch_data = function on_couch_data(data, req) {
  var self = this;

  if(! self.pending.wait_timer)
    return self.die(new Error('Cannot find timeout timer during incoming data'));

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;
  self.pending.activity_at = new Date;

  self.log.debug('Data from ' + self.pending.request.id());

  // Buf could have 0, 1, or many JSON objects in it.
  var buf = self.pending.data + data;
  var offset, json, change;

  while((offset = buf.indexOf("\n")) >= 0) {
    json = buf.substr(0, offset);
    buf = buf.substr(offset + 1);

    if(json == '') {
      self.log.debug('Heartbeat: ' + self.pending.request.id());
    } else {
      //self.log.debug('JSON: ' + json);
      try {
        change = JSON.parse(json);
      } catch(er) {
        return self.die(er);
      }

      //self.log.debug('Object:\n' + util.inspect(change));

      seq = change.seq;
      if(!seq)
        return self.die(new Error('Change has no .seq field: ' + json));

      self.on_change(change);
    }
  }

  self.pending.data = buf;
  self.wait();
}

Feed.prototype.on_timeout = function on_timeout() {
  var self = this;

  var now = new Date;
  var elapsed_ms = now - self.pending.activity_at;
  self.log.warn('Closing req ' + self.pending.request.id() + ' for timeout after ' + elapsed_ms + 'ms; heartbeat=' + self.heartbeat);

  return destroy_req(self.pending.request);
  //return self.retry();
}

Feed.prototype.retry = function retry() {
  var self = this;

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;

  self.log.info('Retrying since=' + self.since + ' after ' + self.retry_delay + 'ms: ' + self.db_safe);
  self.emit('retry');

  setTimeout(function() { self.query() }, self.retry_delay);

  var max_retry_ms = self.max_retry_seconds * 1000;
  self.retry_delay *= 2;
  if(self.retry_delay > max_retry_ms)
    self.retry_delay = max_retry_ms;
}

Feed.prototype.on_couch_end = function on_couch_end() {
  var self = this;

  self.log.debug('Changes feed ended ' + self.pending.request.id());
  self.pending.request = null;
  return self.retry();
}

Feed.prototype.on_couch_error = function on_couch_error(er) {
  var self = this;

  self.log.debug('Changes query eror: ' + lib.JS(er.stack));
  return self.retry();
}

Feed.prototype.die = function(er) {
  var self = this;

  self.log.fatal('Fatal error: ' + er.stack);

  self.emit('error', er);

  var req = self.pending.request;
  self.pending.request = null;
  if(req) {
    self.log.debug('Destroying req ' + req.id());
    destroy_req(req);
  }

  //throw er;
}

Feed.prototype.on_change = function on_change(change) {
  var self = this;

  if(!change.seq)
    return self.die(new Error('No seq value in change: ' + lib.JS(change)));

  if(change.seq <= self.since) {
    self.log.debug('Bad seq value ' + change.seq + ' since=' + self.since);
    return destroy_req(self.pending.request);
  }

  if(typeof self.filter !== 'function')
    return self.on_good_change(change);

  if(!change.doc)
    return self.die(new Error('Internal filter deeds .doc in change ' + change.seq));

  // Don't let the filter mutate the real data.
  var doc = lib.JDUP(change.doc);
  var req = lib.JDUP({'query': self.pending.request.changes_query});

  var result = false;
  try {
    result = self.filter.apply(null, [doc, req]);
  } catch (er) {
    self.log.debug('Filter error', er);
  }

  result = (result && true) || false;
  if(result) {
    self.log.debug('Builtin filter PASS for change: ' + change.seq);
    return self.on_good_change(change);
  } else
    self.log.debug('Builtin filter FAIL for change: ' + change.seq);
}

Feed.prototype.on_good_change = function on_good_change(change) {
  var self = this;

  if(self.inactivity_ms && !self.inactivity_timer)
    return self.die(new Error('Cannot find inactivity timer during change'));

  clearTimeout(self.inactivity_timer);
  self.inactivity_timer = null;
  if(self.inactivity_ms)
    self.inactivity_timer = setTimeout(function() { self.on_inactivity() }, self.inactivity_ms);

  self.change_at = new Date;
  self.since = change.seq;
  self.emit('change', change);
}

Feed.prototype.on_inactivity = function on_inactivity() {
  var self = this;
  var now = new Date;
  var elapsed_ms = now - self.change_at;
  var elapsed_s  = elapsed_ms / 1000;

  return self.die(new Error('Req ' + self.pending.request.id() + ' made no changes for ' + elapsed_s + 's'));
}

module.exports = { "Feed" : Feed
                 };

/*
 * Utilities
 */

function destroy_req(req) {
  if(req)
    return destroy_response(req.response);
}

function destroy_response(response) {
  if(!response)
    return;

  response.connection.end();
  response.connection.destroy();
}
<|endoftext|>


<commit_before>/*
 * process-monitor
 * https://github.com/onmodulus/process-monitor
 *
 * Copyright (c) 2012 Modulus
 * Licensed under the MIT license.
 */

/*
 Node module dependencies
-------------------------------------------------------------------------- */

var stringFormat = require('stringformat')
  , Emitter = require('events').EventEmitter
  , exec = require('child_process').exec
  , usage = require('pidusage')
  , s = require('string');

/*
 Helper functions
-------------------------------------------------------------------------- */

/**
 * Parses the process detail output into and object.
 * @param  {string} res Output from the PS command.
 * @return {object} CPU and memory statistics.
 */

var parsePSOutput = function(res, pid) {
  var lines = res.split('\n')
    , data = s(lines[1]).collapseWhitespace().s
    , dataParts = data.split(' ');

  return {
    pid: pid
  , cpu: dataParts[0] || '0.0'
  , mem: dataParts[1] || '0'
  };
};

/*
 ProcMon
-------------------------------------------------------------------------- */

/**
 * Initializes a new process monitor object.
 * @param {object} config Configuration options.
 * @constructor
 */

var ProcMon = function(config) {
  /**
   * Local event emitter instance to handle the PS.
   * @type {Emitter}
   */

  this.events = new Emitter();

  /**
   * Format string for formatting the output to a string.
   * @type {string}
   */

  this.format = config.format || '';

  /**
   * Update interval.
   * @type {number}
   * @default 1000
   */

  this.interval = config.interval || 1000;

  /**
   * Reference to the monitor loop interval.
   * @type {function}
   */

  this.intervalId = null;

  /**
   * Value indicating whether the monitor loop has been started.
   * @type {Boolean}
   */

  this.isRunning = false;

  /**
   * Process IDs to monitor.
   * @type {array}
   */

  this.pid = config.pid;

  /**
   * The technique used to find CPU usage.
   * @type {string}
   */

  this.technique = 'ps';
  if(config.technique === 'proc') {
    this.technique = 'proc';
  }

  /**
   * Process statistics detail.
   * @type {object}
   */

  this.stats = {};
};

ProcMon.prototype = {
  /**
   * Handle the specified event then fire the callback.
   * @param  {string}   event    The name of the event.
   * @param  {Function} callback The function callback.
   */

  on: function(event, callback) {
    this.events.on(event, callback);
  }

  /**
   * Start the process monitor loop.
   * @return {ProcMon} Chainable instance of this ProcMon.
   */

, start: function() {
    var self = this;

    // Already running.
    if (this.isRunning) {
      return this;
    }

    this.isRunning = true;

    this.intervalId = setInterval(function() {
      self.updateStats();
    }, self.interval);

    return this;
  }

  /**
   * Stop the process monitor loop.
   * @return {ProcMon} Chainable instance of this ProcMon.
   */

, stop: function() {
    clearInterval(this.intervalId);
    this.isRunning = false;

    return this;
  }

  /**
   * Updates stat information.
   */

, updateStats: function() {
    var self = this;

    this.pid.forEach(function(pid) {

      if(self.technique === 'ps') {
        self.updateStatsPS(pid);
      }
      else {
        self.updateStatsProc(pid);
      }
    });
  }

  /**
   * Updates stat information using the ps command.
   */

, updateStatsPS: function(pid) {

    var self = this;

    var command = 'ps -p ' + pid + ' -o %cpu,rss';

    exec(command, function(err, stdout, stderr) {
      if(err) {
        self.events.emit('error', err);
      }
      else {
        self.stats = parsePSOutput(stdout, pid);
        self.stats.out = self.format && stringFormat(self.format, self.stats);
        self.events.emit('stats', self.stats);
      }
    });
  }

  /**
   * Updates stat information using /proc file data.
   */

, updateStatsProc: function(pid) {

    var self = this;

    usage.lookup(pid, { keepHistory: true }, function(err, result) {
      if(err) {
        self.events.emit('error', err);
      }
      else {
        self.stats = { pid: pid, cpu: result.cpu, mem: result.memory / 1024 };
        self.stats.out = self.format && stringFormat(self.format, self.stats);
        self.events.emit('stats', self.stats);
      }
    });
  }
};

/*
 process-monitor API
-------------------------------------------------------------------------- */

/**
 * Process-monitor API definition.
 * @param  {object}   config Configuration options.
 * @return {ProcMon}  Chainable instance of ProcMon.
 */

module.exports.monitor = function(config) {
  if (typeof config.pid === 'undefined') {
    throw new TypeError('You must specify at least one PID');
  }

  if (typeof config.pid === 'number') {
    var pid = config.pid;
    config.pid = [];

    config.pid[0] = pid;
  }

  return new ProcMon(config);
};
<commit_msg>Fix using wrong usage lookup method<commit_after>/*
 * process-monitor
 * https://github.com/onmodulus/process-monitor
 *
 * Copyright (c) 2012 Modulus
 * Licensed under the MIT license.
 */

/*
 Node module dependencies
-------------------------------------------------------------------------- */

var stringFormat = require('stringformat')
  , Emitter = require('events').EventEmitter
  , exec = require('child_process').exec
  , usage = require('pidusage')
  , s = require('string');

/*
 Helper functions
-------------------------------------------------------------------------- */

/**
 * Parses the process detail output into and object.
 * @param  {string} res Output from the PS command.
 * @return {object} CPU and memory statistics.
 */

var parsePSOutput = function(res, pid) {
  var lines = res.split('\n')
    , data = s(lines[1]).collapseWhitespace().s
    , dataParts = data.split(' ');

  return {
    pid: pid
  , cpu: dataParts[0] || '0.0'
  , mem: dataParts[1] || '0'
  };
};

/*
 ProcMon
-------------------------------------------------------------------------- */

/**
 * Initializes a new process monitor object.
 * @param {object} config Configuration options.
 * @constructor
 */

var ProcMon = function(config) {
  /**
   * Local event emitter instance to handle the PS.
   * @type {Emitter}
   */

  this.events = new Emitter();

  /**
   * Format string for formatting the output to a string.
   * @type {string}
   */

  this.format = config.format || '';

  /**
   * Update interval.
   * @type {number}
   * @default 1000
   */

  this.interval = config.interval || 1000;

  /**
   * Reference to the monitor loop interval.
   * @type {function}
   */

  this.intervalId = null;

  /**
   * Value indicating whether the monitor loop has been started.
   * @type {Boolean}
   */

  this.isRunning = false;

  /**
   * Process IDs to monitor.
   * @type {array}
   */

  this.pid = config.pid;

  /**
   * The technique used to find CPU usage.
   * @type {string}
   */

  this.technique = 'ps';
  if(config.technique === 'proc') {
    this.technique = 'proc';
  }

  /**
   * Process statistics detail.
   * @type {object}
   */

  this.stats = {};
};

ProcMon.prototype = {
  /**
   * Handle the specified event then fire the callback.
   * @param  {string}   event    The name of the event.
   * @param  {Function} callback The function callback.
   */

  on: function(event, callback) {
    this.events.on(event, callback);
  }

  /**
   * Start the process monitor loop.
   * @return {ProcMon} Chainable instance of this ProcMon.
   */

, start: function() {
    var self = this;

    // Already running.
    if (this.isRunning) {
      return this;
    }

    this.isRunning = true;

    this.intervalId = setInterval(function() {
      self.updateStats();
    }, self.interval);

    return this;
  }

  /**
   * Stop the process monitor loop.
   * @return {ProcMon} Chainable instance of this ProcMon.
   */

, stop: function() {
    clearInterval(this.intervalId);
    this.isRunning = false;

    return this;
  }

  /**
   * Updates stat information.
   */

, updateStats: function() {
    var self = this;

    this.pid.forEach(function(pid) {

      if(self.technique === 'ps') {
        self.updateStatsPS(pid);
      }
      else {
        self.updateStatsProc(pid);
      }
    });
  }

  /**
   * Updates stat information using the ps command.
   */

, updateStatsPS: function(pid) {

    var self = this;

    var command = 'ps -p ' + pid + ' -o %cpu,rss';

    exec(command, function(err, stdout, stderr) {
      if(err) {
        self.events.emit('error', err);
      }
      else {
        self.stats = parsePSOutput(stdout, pid);
        self.stats.out = self.format && stringFormat(self.format, self.stats);
        self.events.emit('stats', self.stats);
      }
    });
  }

  /**
   * Updates stat information using /proc file data.
   */

, updateStatsProc: function(pid) {

    var self = this;

    usage(pid, function(err, result) {
      if(err) {
        self.events.emit('error', err);
      }
      else {
        self.stats = { pid: pid, cpu: result.cpu, mem: result.memory / 1024 };
        self.stats.out = self.format && stringFormat(self.format, self.stats);
        self.events.emit('stats', self.stats);
      }
    });
  }
};

/*
 process-monitor API
-------------------------------------------------------------------------- */

/**
 * Process-monitor API definition.
 * @param  {object}   config Configuration options.
 * @return {ProcMon}  Chainable instance of ProcMon.
 */

module.exports.monitor = function(config) {
  if (typeof config.pid === 'undefined') {
    throw new TypeError('You must specify at least one PID');
  }

  if (typeof config.pid === 'number') {
    var pid = config.pid;
    config.pid = [];

    config.pid[0] = pid;
  }

  return new ProcMon(config);
};
<|endoftext|>


<commit_before>/*global XMLHttpRequest, document, window */
(function(window) {

    // Substrat Reload Handler
    var uid = '%uid',
        reloadUrl = '%reloadUrl?r=' + Math.random(),
        initUrl = '%initUrl';

    // Save scroll position
    function save() {
        try {
            window.sessionStorage[uid + '.scroll.x'] = '' + document.body.scrollLeft;
            window.sessionStorage[uid + '.scroll.y'] = '' + document.body.scrollTop;

        } catch(e) {
        }
    }

    // Restore scroll position
    function restore() {

        var x = null,
            y = null;

        try {
            x = window.sessionStorage[uid + '.scroll.x'] || null;
            y = window.sessionStorage[uid + '.scroll.y'] || null;
            delete window.sessionStorage[uid + '.scroll.x'];
            delete window.sessionStorage[uid + '.scroll.y'];

        } catch(e) {
            return;
        }

        if (x !== null && y !== null) {

            // Since the layout might still reflow and the document size might
            // change, we need to periodically check the scroll offset
            var retries = 0,
                lx = document.body.scrollLeft,
                ly = document.body.scrollTop;

            var t = window.setInterval(function() {

                var sx = document.body.scrollLeft,
                    sy = document.body.scrollTop;

                if (sx !== lx || sy !== ly) {
                    retries = 0;
                    lx = sx;
                    ly = sy;
                }

                // No further changes, should be restored now
                if ((sx === +x && sy === +y) || retries > 30) {
                    window.clearInterval(t);

                } else {
                    retries++;
                    document.body.scrollLeft = +x;
                    document.body.scrollTop = +y;
                }

            }, 25);
        }

    }

    // Init Watcher
    function init() {

        var canceled = false,
            reloadTimeout = null,
            retryTimeout = null;

        var open = [];
        function reload(reconnect, url, initial) {

            var xhr = new XMLHttpRequest();
            xhr.open('GET', url);
            xhr.onreadystatechange = function() {

                // Keep track of running XHRs
                if (xhr.readyState === 4) {
                    open.splice(open.indexOf(xhr), 1);
                }

                // Substrat triggered a reload
                if (xhr.readyState === 4 && xhr.status === 200) {

                    // Handle partial updates
                    if (xhr.responseText.length) {
                        var data = JSON.parse(xhr.responseText);
                        if (data.error) {
                            handleError(data.error);

                        } else if (data.changes) {
                            handleUpdate(data.changes, initial);

                        } else {
                            document.location.reload();
                        }

                    // Trigger a full reload after reconnecting
                    } else {
                        document.location.reload();
                    }

                    window.clearTimeout(reloadTimeout);
                    window.clearTimeout(retryTimeout);

                    // Avoid scenarios were we have multiple reload requests
                    // running in parallel
                    if (open.length === 0) {
                        reload(false, reloadUrl);
                    }

                // Substrat exited, try to reconnect
                } else if (xhr.readyState === 4 && !canceled) {

                    retryTimeout = window.setTimeout(function() {
                        xhr.abort();
                        window.clearTimeout(reloadTimeout);
                        window.clearTimeout(retryTimeout);
                        reload(true, initUrl);

                    }, 1000);

                } else {
                    canceled = false;
                }

            };

            open.push(xhr);
            xhr.send();

            // The request will eventually fail if delayed indefinitely by the
            // server; thus, we need manually reset it every second
            reloadTimeout = window.setTimeout(function() {
                canceled = true;
                xhr.abort();
                reload(false, reloadUrl);

            }, 1000);

        }

        restore();
        reload(false, reloadUrl, true);

    }

    function handleError(message) {
        window.alert('Build Error: ' + message);
    }

    var css = /\.css$/,
        origin = window.location.protocol + "//" + window.location.host;

    function linkName(l) {
        return l.href.substring(origin.length + 1).split('?')[0];
    }

    function getLinks() {
        return Array.prototype.slice.call(document.head.getElementsByTagName('link'));
    }

    function handleUpdate(changes, initial) {

        if (initial) {
            return false;
        }

        // Handle newly added files
        var reload = false;
        changes.added.forEach(function(file) {

            // CSS
            if (css.test(file)) {
                var link = document.createElement('link');
                link.rel = 'stylesheet';
                link.href = file;
                document.head.appendChild(link);

            } else {
                reload = true;
            }

        });

        // Handle changed files
        changes.changed.forEach(function(file) {

            // CSS
            if (css.test(file)) {

                getLinks().filter(function(l) {
                    return linkName(l) === file;

                }).map(function(link) {
                    link.href = file + '?id=' + Math.random() + '-' + Date.now();
                });

            } else {
                reload = true;
            }

        });

        // Handle removed files
        changes.removed.forEach(function(file) {

            // CSS
            if (css.test(file)) {

                getLinks().filter(function(l) {
                    return linkName(l) === file;

                }).map(function(link) {
                    document.head.removeChild(link);
                });

            } else {
                reload = true;
            }

        });

        if (reload) {
            document.location.reload();
        }

    }

    // Events
    if (window.addEventListener) {
        window.addEventListener('load', init, false);
        window.addEventListener('beforeunload', save, false);

    } else {
        window.attachEvent('onload', init);
        window.attachEvent('onbeforeunload', save, false);
    }

})(window);

<commit_msg>Remove reload calls when substrat reloader is disconnected<commit_after>/*global XMLHttpRequest, document, window */
(function(window) {

    // Substrat Reload Handler
    var uid = '%uid',
        reloadUrl = '%reloadUrl?r=' + Math.random(),
        initUrl = '%initUrl';

    // Save scroll position
    function save() {
        try {
            window.sessionStorage[uid + '.scroll.x'] = '' + document.body.scrollLeft;
            window.sessionStorage[uid + '.scroll.y'] = '' + document.body.scrollTop;

        } catch(e) {
        }
    }

    // Restore scroll position
    function restore() {

        var x = null,
            y = null;

        try {
            x = window.sessionStorage[uid + '.scroll.x'] || null;
            y = window.sessionStorage[uid + '.scroll.y'] || null;
            delete window.sessionStorage[uid + '.scroll.x'];
            delete window.sessionStorage[uid + '.scroll.y'];

        } catch(e) {
            return;
        }

        if (x !== null && y !== null) {

            // Since the layout might still reflow and the document size might
            // change, we need to periodically check the scroll offset
            var retries = 0,
                lx = document.body.scrollLeft,
                ly = document.body.scrollTop;

            var t = window.setInterval(function() {

                var sx = document.body.scrollLeft,
                    sy = document.body.scrollTop;

                if (sx !== lx || sy !== ly) {
                    retries = 0;
                    lx = sx;
                    ly = sy;
                }

                // No further changes, should be restored now
                if ((sx === +x && sy === +y) || retries > 30) {
                    window.clearInterval(t);

                } else {
                    retries++;
                    document.body.scrollLeft = +x;
                    document.body.scrollTop = +y;
                }

            }, 25);
        }

    }

    // Init Watcher
    function init() {

        var canceled = false,
            reloadTimeout = null,
            retryTimeout = null;

        var open = [];
        function reload(reconnect, url, initial) {

            var xhr = new XMLHttpRequest();
            xhr.open('GET', url);
            xhr.onreadystatechange = function() {

                // Keep track of running XHRs
                if (xhr.readyState === 4) {
                    open.splice(open.indexOf(xhr), 1);
                }

                // Substrat triggered a reload
                if (xhr.readyState === 4 && xhr.status === 200) {

                    // Handle partial updates
                    if (xhr.responseText.length) {
                        var data = JSON.parse(xhr.responseText);
                        if (data.error) {
                            handleError(data.error);

                        } else if (data.changes) {
                            handleUpdate(data.changes, initial);

                        } else {
                            document.location.reload();
                        }

                    // Trigger a full reload after reconnecting
                    } else {
                        document.location.reload();
                    }

                    window.clearTimeout(reloadTimeout);
                    window.clearTimeout(retryTimeout);

                    // Avoid scenarios were we have multiple reload requests
                    // running in parallel
                    if (open.length === 0) {
                        reload(false, reloadUrl);
                    }

                // Substrat exited, try to reconnect
                } else if (xhr.readyState === 4 && !canceled) {

                    window.clearTimeout(reloadTimeout);
                    retryTimeout = window.setTimeout(function() {
                        xhr.abort();
                        window.clearTimeout(reloadTimeout);
                        window.clearTimeout(retryTimeout);
                        reload(true, initUrl);

                    }, 1000);

                } else {
                    canceled = false;
                }

            };

            open.push(xhr);
            xhr.send();

            // The request will eventually fail if delayed indefinitely by the
            // server; thus, we need manually reset it every second
            reloadTimeout = window.setTimeout(function() {
                canceled = true;
                xhr.abort();
                reload(false, reloadUrl);

            }, 1000);

        }

        restore();
        reload(false, reloadUrl, true);

    }

    function handleError(message) {
        window.alert('Build Error: ' + message);
    }

    var css = /\.css$/,
        origin = window.location.protocol + "//" + window.location.host;

    function linkName(l) {
        return l.href.substring(origin.length + 1).split('?')[0];
    }

    function getLinks() {
        return Array.prototype.slice.call(document.head.getElementsByTagName('link'));
    }

    function handleUpdate(changes, initial) {

        if (initial) {
            return false;
        }

        // Handle newly added files
        var reload = false;
        changes.added.forEach(function(file) {

            // CSS
            if (css.test(file)) {
                var link = document.createElement('link');
                link.rel = 'stylesheet';
                link.href = file;
                document.head.appendChild(link);

            } else {
                reload = true;
            }

        });

        // Handle changed files
        changes.changed.forEach(function(file) {

            // CSS
            if (css.test(file)) {

                getLinks().filter(function(l) {
                    return linkName(l) === file;

                }).map(function(link) {
                    link.href = file + '?id=' + Math.random() + '-' + Date.now();
                });

            } else {
                reload = true;
            }

        });

        // Handle removed files
        changes.removed.forEach(function(file) {

            // CSS
            if (css.test(file)) {

                getLinks().filter(function(l) {
                    return linkName(l) === file;

                }).map(function(link) {
                    document.head.removeChild(link);
                });

            } else {
                reload = true;
            }

        });

        if (reload) {
            document.location.reload();
        }

    }

    // Events
    if (window.addEventListener) {
        window.addEventListener('load', init, false);
        window.addEventListener('beforeunload', save, false);

    } else {
        window.attachEvent('onload', init);
        window.attachEvent('onbeforeunload', save, false);
    }

})(window);

<|endoftext|>