Greasy Fork

ajax log

library for log ajax requests and save logs as a file

目前为 2018-03-11 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.icu/scripts/39403/257981/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.0-beta.2.0
// ==/UserScript==

(function(window){
	try{
	var tracker = {},
		proto = window.XMLHttpRequest.prototype;
	['open', 'send', 'setRequestHeader', 'abort'].forEach(function(m){
		tracker[m] = {
			'original': null,
			'hook': null,
			'ready': null,
		};
	});
	/*function getVal(obj, name){ var c = obj[name] = obj[name] || {}; return c;};*/
	var getVal = function(obj, name){ var c = obj[name] = obj[name] || {}; return c;};
	console.log("tracker: ", tracker);
	/*function setTracker(type, hook)*/
	var setTracker = function(type, hook)
	{
		var t = tracker[type];
		if(!t || t.ready)
			return;
		t.original = proto[type];
		/*t.hook = proto[type] = function()*/
		proto[type] = function()
		{
			hook.apply(this, arguments);
			t.original.apply(this, arguments);
		};
		t.ready = true;
	};
	/*function toObj(str)*/
	var toObj = function(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;
		}
	};
	Object.defineProperty(proto, 'xhrTracker', {
		value: null,
		writable: true,
	});
	proto.xhrTracker = {};
	['addEventListener', 'onreadystatechange', 'onload'].forEach(function(m){
		proto.xhrTracker[m] = {
			'original': null,
			'hook': null,
			'ready': null,
		};
	});
	console.log("prototype: ", proto);
	var responseL = ['response', 'readyState', 'status', 'statusText'];
	/*function setResponse(obj, r)*/
	var setResponse = function(obj, r)
	{
		responseL.forEach(function(n){
			obj[n] = r[n];
		});
	};
	var xhrHook = {
		'onreadystatechange': null,
		'onload': null,
	};
	var flushAndSave = null, totalResponse = '';
	var saveLog = function()
	{
		if( !flushAndSave )
			return;
		console.log("save.totalResponse:\r\n" + totalResponse);
		saveFile('xml-http-request-' + getDate() + '.txt', createFile(totalResponse, 'text/plain'));
		totalResponse = '';
		flushAndSave = false;
	};
	var getHeaders = function(x)
	{
		var headers = x.getAllResponseHeaders(), o = {}, h;
		var hs = headers.split(/\r\n/);
		for(var h of hs)
		{
			h = h.split(': ');
			o[h[0].trim()] = h.slice(1).join(': ').trim();
		}
		return o;
	};
	xhrHook.onreadystatechange = function(e)
	{
		var t = e.target;
		if( t.readyState == 4 )
		{
			var cntx = getVal(t, 'context'),
				o = cntx.onreadystatechange = {};
			setResponse(o, t);
			o.responseHeaders = getHeaders(t);
			o.respHeads = t.getAllResponseHeaders();
			totalResponse += '\r\n\r\n' + JSON.stringify(cntx, null, 2);
			console.log("onreadystatechange.totalResponse:\r\n" + totalResponse);
			saveLog();
		}
	};
	/*function getDate(date)*/
	var getDate = function(date)
	{
		date = date || new Date();
		return '' +
			date.getFullYear() + '-' +
			(date.getMonth() + 1) + '-' +
			date.getDate() + '-@' +
			date.getHours() + '-' +
			date.getMinutes() + '-' +
			(date.getSeconds() * 1e3 + date.getMilliseconds())/1e3;
	};
	console.log("date: ", getDate());
	xhrHook.onload = function(e)
	{
		/*
		var t = e.target,
			cntx = getVal(t, 'context'),
			o = cntx.onload = {};
		setResponse(o, t);
		o.responseHeaders = getHeaders(t);
		totalResponse += '\r\n\r\n' + JSON.stringify(cntx, null, 2);
		console.log("onload.totalResponse:\r\n" + totalResponse);
		saveLog();
		*/
	};
	/*function setXhrTracker(inst, type, hook, original)*/
	var setXhrTracker = function(inst, type, hook, original)
	{
		var t = inst.xhrTracker[type];
		if( t.ready )
			return;
		t.inst = inst;
		console.log("setXhrTracker:" + type + ": ", inst);
		if( typeof original != 'function' )
		{
			t.original = inst[type] || function(){console.log("dummy: " + type);};
			t.hook = inst[type] = function(){
				hook.apply(t.inst, arguments);
				t.original.apply(t.inst, arguments);
			};
		}else{
			t.original = original;
			t.hook = function(){
				hook.apply(t.inst, arguments);
				t.original.apply(t.inst, arguments);
			};
		}
		t.ready = true;
	};
	/*function setEventTracker(inst)*/
	var setEventTracker = function(inst)
	{
		var n = 'addEventListener',
			o = inst.xhrTracker[n],
			i = inst;
		o.inst = inst;
		o.original = inst[n];
		o.hook = inst[n] = function(evt, fun)
		{
			var t = i.xhrTracker['on' + evt];
			if( !!t )
				setXhrTracker(i, 'on' + ev, xhrHook['on' + ev], fun);
			else
				o.original.apply(o.inst, arguments);
		};
	};
	var link;
	var getLocation = function(url, p)
	{
		link = link || document.createElement('a');
		link.href = url;
		return link[p||'href'];
	};
	setTracker('open', function(method, url, async){
		try{
		var cntx = getVal(this, 'context');
		cntx.method = method.toUpperCase();
		cntx.url = getLocation(url, 'href');
		cntx.async = (async === undefined ? null: async);
		console.log("open:context: ", JSON.stringify(cntx, null, 2));
		setEventTracker(this);
		}catch(er){console.error(er);}
	});
	setTracker('setRequestHeader', function(name, val){
		try{
		var cntx = getVal(this, 'context'),
			headers = getVal(cntx, 'headers');
		headers[name] = val;
		console.log("headers:context: ", JSON.stringify(cntx, null, 2));
		}catch(er){console.error(er);}
	});
	setTracker('abort', function(){
		var cntx = getVal(this, 'context');
		cntx.onabort = true;
	});
	setTracker('send', function(data){
		try{
		var cntx = getVal(this, 'context');
		cntx.data = toObj(data);
		console.log("send:context: ", JSON.stringify(cntx, null, 2));
		setXhrTracker(this, 'onreadystatechange', xhrHook.onreadystatechange);
		setXhrTracker(this, 'onload', xhrHook.onreadystatechange);
		}catch(er){console.error(er);}
	});
	var keyboard = function(e)
	{
		if(!e.shiftKey)
			return;
		var code = e.keyCode || e.which,
			ch = String.fromCharCode(code).toUpperCase();
		switch(ch)
		{
			case 'S':
			flushAndSave = true;
			saveLog();
			break;
		}
	};
	window.addEventListener('keydown', function(e){
		keyboard(e);
	});
	function saveFile(name, resource)
	{
		var link = document.createElement('a');
		link.href = resource;
		link.download = name;
		document.querySelector('body').appendChild(link);
		link.click();
		link.parentNode.removeChild(link);
	}
	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;
	}
	}catch(err){console.error(err);}
})(window);