ajax log

library for log ajax requests and save logs as a file

สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.greasyfork.org/scripts/39403/258004/ajax%20log.js

// ==UserScript==
// @name        ajax log
// @namespace   https://greasyfork.org/users/174399
// @description library for log ajax requests and save logs as a file
// @version     0.5.1
// ==/UserScript==

(function(window){
	var DEBUG = false;
	var tracker = {},
		proto = window.XMLHttpRequest.prototype,
		eventList = ['readystatechande', 'load'],
		methodList = ['open', 'send', 'setRequestHeader', 'abort'],
		attrList = ['readyState', 'status', 'statusText', 'response'],
		totalResponse = [],
		flushAndSave = null,
		blank = function(){},
		log = function(){window.console.log.apply(this, arguments);},
		clog = blank,
		link;
	if( DEBUG )
		clog = log;
	methodList.forEach(function(m){
		tracker[m] = {
			'original': null,
			'ready': null,
		};
	});
	function setTracker(type, fun)
	{
		var t = tracker[type];
		if(!t || t.ready)
			return;
		t.original = proto[type];
		proto[type] = function()
		{
			fun.apply(this, arguments);
			t.original.apply(this, arguments);
		};
		t.ready = true;
	}
	setTracker('open', function(method, url, async){
		var req = this,
			cntx = getVal(req, 'context'),
			time = getVal(cntx, 'time');
		clog("[open] req: ", req);
		cntx.method = method.toUpperCase();
		cntx.url = getLocation(url, 'href');
		cntx.async = (async === undefined ? null: async);
		time.msec = Date.now();
		time.date = getDate();
		var aEL = req.addEventListener;
		req.addEventListener = function(ev, cb)
		{
			clog("[addEventListener] ", aEL, [].slice(arguments));
			if(eventList.indexOf(ev) == -1)
				aEL.apply(req, arguments);
		};
	});
	setTracker('setRequestHeader', function(name, val){
		var cntx = getVal(this, 'context'),
			headers = getVal(cntx, 'headers');
		clog("[header] " + name + ": " + val);
		headers[name] = val;
	});
	setTracker('abort', function(){
		var cntx = getVal(this, 'context');
		cntx.onabort = true;
	});
	setTracker('send', function(data){
		var req = this,
			cntx = getVal(req, 'context');
		cntx.data = toObj(data);
		clog("[send] context: ", JSON.stringify(cntx, null, 2));
		clog("[send] req: ", req);
		var rsc = req.onreadystatechange;
		var ld = req.onload || blank;
		clog("[onreadystatechange] ", rsc);
		req.onreadystatechange = function(e)
		{
			var t = e.target;
			if(t.readyState == 4 )
			{
				clog("[onreadystatechange] response: ", t.response);
				var cntx = getVal(t, 'context'),
					o = getVal(cntx, 'onreadystatechange'),
					time = getVal(o, 'time');
				setResponse(o, t);
				time.msec = Date.now();
				time.date = getDate();
				o.responseHeaders = getHeaders(t);
				totalResponse.push(extend({}, cntx) );
				saveLog();
			}
			rsc.apply(req, arguments);
		};
		clog("[onload] ", ld);
		req.onload = function(e)
		{
			var t = e.target,
				cntx = getVal(t, 'context'),
				o = getVal(cntx, 'onload'),
				time = getVal(o, 'time');
			clog("[onload] response: ", t.response);
			setResponse(o, t);
			o.responseHeaders = getHeaders(t);
			time.msec = Date.now();
			time.date = getDate();
			totalResponse.push(extend({}, cntx) );
			saveLog();
			ld.apply(req, arguments);
		};
	});
	function extend(t, o)
	{
		t = t || {};
		var k, v;
		for(k in o)
		{
			v = o[k];
			if( v !== undefined && o.hasOwnProperty(k) )
				t[k] = v;
		}
		return t;
	}
	function setResponse(o, t)
	{
		for(var k of attrList)
			o[k] = t[k];
		return o;
	}
	function getHeaders(t)
	{
		var headers = t.getAllResponseHeaders(), o = {}, s, h, p;
		s = headers.split(/[\r\n]+/g);
		for(h of s)
		{
			h = h.split(': ');
			p = h[0] || '';
			if( !(p = p.trim()) )
				continue;
			o[p] = h.slice(1).join(': ');
		}
		return o;
	}
	function keyboard(e)
	{
		if(!e.shiftKey)
			return;
		var code = e.keyCode || e.which,
			ch = String.fromCharCode(code).toUpperCase();
		switch(ch)
		{
			case 'S':
			flushAndSave = true;
			saveLog();
			break;
			case 'D':
			clog = (DEBUG = !DEBUG) ? log : blank;
			break;
		}
	}
	window.addEventListener('keydown', function(e){keyboard(e);});
	function saveLog()
	{
		if( totalResponse.length && flushAndSave )
		{
			saveFile('xmlHttpRequest-' + getDate() + '.txt',
				createFile(JSON.stringify(totalResponse, null, 2), 'text/plain; charset=UTF-8'));
			totalResponse.length = 0;
			flushAndSave = false;
		}
	}
	function pad(num, len){return ('000000000000' + num).slice(-len);}
	function getDate(date)
	{
		date = date || new Date();
		return '' +
		date.getFullYear() + '-' +
		pad(date.getMonth() + 1, 2) + '-' +
		pad(date.getDate(), 2) + '@' +
		pad(date.getHours(), 2) + '-' +
		pad(date.getMinutes(), 2) + '-' +
		pad(date.getSeconds(), 2) + '.' +
		pad(date.getMilliseconds(), 3);
	}
	function saveFile(name, resource)
	{
		var a = document.createElement('a');
		a.href = resource;
		a.download = name;
		document.querySelector('body').appendChild(a);
		a.click();
		a.parentNode.removeChild(a);
	}
	function createFile(data, type)
	{
		var wu = window.URL || window.webkitURL,
			b = new Blob([data], {type: type}),
			u = wu.createObjectURL(b);
		setTimeout(function(){wu.revokeObjectURL(b);}, 1e4);
		return u;
	}
	function toObj(str)
	{
		if( !str )
			return null;
		switch(typeof str)
		{
			case 'object': return str;
			case 'string':
			var o = {}, s = str.split('&'), v, p;
			for(p of s)
			{
				v = p.split('=');
				o[v[0]] = v[1] || '';
			}
			return o;
			default: return null;
		}
	}
	function getLocation(url, p)
	{
		link = link || document.createElement('a');
		link.href = url;
		return link[p||'href'];
	}
	function getVal(obj, name)
	{
		var c = obj[name] = obj[name] || {};
		return c;
	}
})(window);