您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Intercepts console.log calls to display log messages in a div floating on the page. Tries to be a replacement for normal browser Error Consoles (which can be a little slow to open).
- // ==UserScript==
- // @name FastJSLogger
- // @namespace FastJSLogger
- // @description Intercepts console.log calls to display log messages in a div floating on the page. Tries to be a replacement for normal browser Error Consoles (which can be a little slow to open).
- // @include *
- // But this script appears to break Facebook somewhat
- // @exclude *facebook.com/*
- // @version 1.2.8
- // @grant none
- // ==/UserScript==
- (function(){
- // You may configure FastJSLogger before loading it, by putting options in
- // window.FJSL = { startHidden: false, displaySearchFilter: true };
- // var FJSL = {};
- if (!window.FJSL) {
- window.FJSL = {};
- }
- var FJSL = window.FJSL;
- // @concern Don't double-load
- // I haven't actually had any problems from double loading, but it seems silly to have two loggers.
- if (FJSL.loaded) {
- console.log("FJSL refusing to load a second time.");
- return;
- }
- FJSL.loaded = true;
- FJSL.defaults = {
- autoHide: true, // hides some time after displaying, even if you are focused on it!
- startHidden: true,
- logTimeouts: false,
- logEvents: false, // Log any activity over the wrapped listeners.
- logCommonMouseEvents: false, // These can be triggered a lot!
- logChangesToGlobal: true, // Logs any new properties which are added to window
- watchWindowForErrors: true, // Catches and reports DOM error events, including syntax errors from scripts loaded later, and missing images. BUG: It can hide the line number from Chrome's console - sometimes worth disabling to get that back.
- interceptTimeouts: true, // Wraps calls to setTimeout, so any errors thrown may be reported.
- interceptEvents: true, // Wraps any listeners registered later, so errors can be caught and reported.
- displaySearchFilter: false, // This messes up the size of the textbox on Firefox and Konqueror but not Chrome.
- // Hack to avoid infinite loops. Disabled since they have stopped! :)
- // Mute console.log messages repeated to the browser console.
- // (Display them only in the fastlogger, keep the browser log clear for
- // warnings, errors and info messages.)
- // TODO: Similarly, we may want to mute log messages in the FJSL, and only
- // show info/warn/errors.
- muteLogRepeater: 0,
- logNodeInsertions: false, // Watch for and report DOMNodeInserted events.
- bringBackTheStatusBar: true // TODO: add fake statusbar div, watch window.status for change?
- };
- for (var k in FJSL.defaults) {
- if (FJSL[k] === undefined) {
- FJSL[k] = FJSL.defaults[k];
- }
- }
- if (this.localStorage) {
- // TODO: Recall and store FJSL preferences in localStorage.
- // UNWANTED: During testing, I am toggling this setting.
- // FJSL.interceptTimeouts = !Boolean(localStorage['fastjslogger_interceptTimeouts']);
- localStorage['fastjslogger_interceptTimeouts'] = FJSL.interceptTimeouts;
- }
- // This fails to intercept GM_log calls from other userscripts.
- // However it intercepts everything when we run bookmarklets.
- // Partly DONE: We may be able to capture errors by overriding setTimeout,
- // setInterval, XMLHttpRequest and any other functions which use callbacks,
- // with our own versions which attempt the callback within a try-catch which
- // logs and throws any intercepted exceptions.
- // Unfortunately wrapping a try-catch around the main scripts in the document
- // (which have alread run) is a bit harder from here. ;)
- // Aha! Turns out we can catch those errors with window.onerror!
- // TODO: No thanks to Wikipedia's pre styling(?) I can't set a good default, so we need font zoom buttons!
- // TODO: Add ability to minimize but popup again on new log message.
- // TODO: Report file/line-numbers where possible. (Split output into table? :E)
- // TODO: Option to watch for new globals.
- // TODO: Options to toggle logging of any intercepted setTimeouts, XMLHttpRequest's etc, in case the reader is interested.
- // TESTING: all DOM events! TODO: XHR.
- // TESTING: We could even put intercepts on generic functions, in case an error occurs inside them, in a context which we had otherwise failed to intercept.
- // TODO: If the FJSL is wanted for custom logging, not normal console.log
- // logging, we will need to expose a function (FJSL.log()?), and *not*
- // intercept console.log.
- // TODO: Perhaps instead of creating a new console, we should just replace
- // functions in the existing one (and leave anything we haven't altered
- // intact).
- // TODO: See https://github.com/h5bp/html5-boilerplate/blob/master/js/plugins.js
- // for more log actions.
- var loadFJSL = function(){
- // I did have two running happily in parallel (Chrome userscript and page
- // script) but it is rarely useful. Perhaps we should close the older one?
- if (document.getElementById("fastJSLogger") != null) {
- return;
- }
- // GUI creation library functions
- function addStyle(css) {
- // Konqueror 3.5 does not act on textValue, it requires textContent.
- // innerHTML doesn't work for selectors containing '>'
- var st = newNode("style", { type: "text/css", innerHTML: css } );
- document.getElementsByTagName('head')[0].appendChild(st);
- }
- function newNode(tag,data) {
- var elem = document.createElement(tag);
- if (data) {
- for (var prop in data) {
- elem[prop] = data[prop];
- }
- }
- return elem;
- }
- function newSpan(text) {
- return newNode("span",{textContent:text});
- }
- function addCloseButtonTo(elem) {
- var b = document.createElement("button");
- b.textContent = "X";
- b.style.zIndex = 2000;
- b.onclick = function() {
- //GM_log("[",b,"] Destroying:",elem);
- elem.style.display = 'none';
- elem.parentNode.removeChild(elem);
- };
- b.style.float = 'right'; // BUG: not working in FF4!
- elem.appendChild(b);
- }
- // == Main Feature - Present floating popup for log messages ==
- // Cleaner to expose the div via id? It may have been removed from DOM!
- // We could fetch logDiv from FJSL when needed?
- var logDiv = null;
- var logContainer = null;
- var autoHideTimer = null;
- var oldSetTimeout = window.setTimeout;
- function createGUI() {
- var css = "";
- css += " .fastJSLogger { position: fixed; right: 8px; top: 8px; width: 40%; /*max-height: 90%; height: 320px;*/ background-color: #333; color: white; border: 1px solid #666; border-radius: 5px; padding: 2px 4px; z-index: 10000; } ";
- css += " .fastJSLogger > span { max-height: 10%; padding: 0 0.3em; }";
- css += " .fastJSLogger > .fjsl-title { font-weight: bold; }";
- // css += " .fastJSLogger > pre { max-height: 90%; overflow: auto; }";
- //// On the pre, max-height: 90% is not working, but specifying px does.
- var maxHeight = window.innerHeight * 0.8 | 0;
- css += " .fastJSLogger > pre { max-height: "+maxHeight+"px; overflow: auto; word-wrap: break-word; }";
- css += " .fastJSLogger > pre { padding: 0px; margin: 0.4em 0.2em; }";
- // Must set the colors again, in case the page defined its own bg or fg color for pres that conflicts ours.
- css += " .fastJSLogger > pre { /* background-color: #ffffcc; */ background-color: #888; color: black; }";
- css += " .fastJSLogger > pre { font-family: Sans; }";
- // css += " .fastJSLogger > pre > input { width: 100%, background-color: #888888; }";
- css += " .fastJSLogger > pre > div { border-top: 1px solid #888; padding: 0px 2px; ";
- css += " white-space: normal; word-break: break-all; }";
- css += " .fastJSLogger > pre > .log { background-color: #eee; color: #555; }";
- css += " .fastJSLogger > pre > .info { background-color: #aaf; }";
- css += " .fastJSLogger > pre > .warn { background-color: #ff4; }";
- css += " .fastJSLogger > pre > .error { background-color: #f99; }";
- css += " .fastJSLogger { opacity: 0.1; transition: opacity 1s ease-out; } ";
- css += " .fastJSLogger:hover { opacity: 1.0; transition: opacity 400ms ease-out; } ";
- css += " .fastJSLogger.notifying { opacity: 1.0; transition: opacity 200ms ease-out; } ";
- if (document.location.host.match(/wikipedia/))
- css += " .fastJSLogger > pre { font-size: 60%; }";
- else
- css += " .fastJSLogger > pre { font-size: 70%; } ";
- addStyle(css);
- // Add var before logDiv to break hideLogger()!
- logDiv = newNode("div",{ id: 'fastJSLogger', className: 'fastJSLogger' });
- if (FJSL.startHidden) {
- hideLogger();
- }
- document.body.appendChild(logDiv);
- // logDiv.style.position = 'fixed';
- // logDiv.style.top = '20px';
- // logDiv.style.right = '20px';
- // @todo refactor
- // I/O: logDiv, logContainer
- var heading = newSpan("FastJSLogger");
- heading.className = "fjsl-title";
- logDiv.appendChild(heading);
- // addCloseButtonTo(logDiv);
- var closeButton = newSpan("[X]");
- closeButton.style.float = 'right';
- closeButton.style.cursor = 'pointer';
- closeButton.style.paddingLeft = '5px';
- closeButton.onclick = function() { logDiv.parentNode.removeChild(logDiv); };
- logDiv.appendChild(closeButton);
- var logContainer = newNode("pre");
- var rollupButton = newSpan("[--]");
- rollupButton.style.float = 'right';
- rollupButton.style.cursor = 'pointer';
- rollupButton.style.paddingLeft = '10px';
- rollupButton.onclick = function() {
- if (logContainer.style.display == 'none') {
- logContainer.style.display = '';
- rollupButton.textContent = "[--]";
- } else {
- logContainer.style.display = 'none';
- rollupButton.textContent = "[+]";
- }
- };
- logDiv.appendChild(rollupButton);
- if (FJSL.displaySearchFilter) {
- function createSearchFilter(logDiv,logContainer) {
- var searchFilter = document.createElement("input");
- searchFilter.type = 'text';
- searchFilter.style.float = 'right';
- searchFilter.style.paddingLeft = '5px';
- searchFilter.onchange = function(evt) {
- var searchText = this.value;
- // console.log("Searching for "+searchText);
- var logLines = logContainer.childNodes;
- for (var i=0;i<logLines.length;i++) {
- if (logLines[i].textContent.indexOf(searchText) >= 0) {
- logLines[i].style.display = '';
- } else {
- logLines[i].style.display = 'none';
- }
- }
- };
- return searchFilter;
- }
- var searchFilter = createSearchFilter(logDiv,logContainer);
- // logDiv.appendChild(document.createElement("br"));
- logDiv.appendChild(searchFilter);
- }
- logDiv.appendChild(logContainer);
- return [logDiv,logContainer];
- }
- // @parameter channel String
- // @parameter args Array<*>
- // Do not pass more than two parameters; addToFastJSLog will ignore any extra.
- function addToFastJSLog(channel, args) {
- // Make FJSL visible if hidden
- showLogger();
- if (logContainer) {
- var out = "";
- for (var i=0; i<args.length; i++) {
- var obj = args[i];
- var str = "" + obj;
- if (obj && obj.constructor === "Array") {
- str = "[" + obj.map(showObject).join(", ") + "]";
- }
- // Non-standard: inform type if toString() is dull.
- if (str === "[object Object]") {
- str = "";
- if (typeof obj !== 'object') {
- // Surely this is incredibly unlikely! Apart from the obvious case of a String.
- str += "(" + (typeof obj) + ")";
- }
- if (obj.constructor && obj.constructor.name) {
- str = "[" + obj.constructor.name + "]";
- if (obj.constructor.name === "Object") {
- str = "";
- // Because showObject will give it {}s, this is implied.
- // Although Chrome's console does actually print: "Object { ... }"
- }
- }
- str += showObject(obj);
- } else {
- }
- str = shortenString(str);
- var gap = (i>0?' ':'');
- out += gap + str;
- }
- var d = document.createElement("div");
- d.className = channel;
- d.textContent = out;
- if (logContainer.childNodes.length >= 1000) {
- logContainer.removeChild(logContainer.firstChild);
- }
- logContainer.appendChild(d);
- // Scroll to bottom
- // TODO: This is undesirable if the scrollbar was not already at the bottom, i.e. the user has scrolled up manually and is trying to read earlier log entries!
- logContainer.scrollTop = logContainer.scrollHeight;
- }
- if (autoHideTimer !== null) {
- clearTimeout(autoHideTimer);
- autoHideTimer = null;
- }
- if (FJSL.autoHide) {
- // Never log this setTimeout! That produces an infinite loop!
- autoHideTimer = oldSetTimeout(hideLogger,15 * 1000);
- }
- return d;
- }
- function debounce(duration, fn) {
- var timer = null;
- return function() {
- clearTimeout(timer);
- timer = setTimeout(fn, duration);
- };
- }
- var clearOpacity = debounce(3000, function(){
- //logDiv.style.opacity = '';
- logDiv.className = logDiv.className.replace(/(^|\s)notifying(\s|$)/, '');
- });
- function showLogger() {
- if (logDiv) {
- // logDiv.style.display = '';
- //// BUG: Transition is not working! Perhaps it only works when switching between CSS classes.
- // logDiv.style._webkit_transition_property = 'opacity';
- // logDiv.style._webkit_transition_duration = '2s';
- //logDiv.style.opacity = 1.0;
- if (!logDiv.className.match(/(^|\s)notifying(\s|$)/)) {
- logDiv.className += " notifying";
- }
- clearOpacity();
- }
- }
- function hideLogger() {
- if (logDiv) {
- // logDiv.style.display = 'none';
- // logDiv.style._webkit_transition_property = 'opacity';
- // logDiv.style._webkit_transition_duration = '2s';
- // logDiv.style.opacity = 0.0;
- clearOpacity();
- }
- }
- var k = createGUI();
- logDiv = k[0];
- logContainer = k[1];
- // target.console.log("FastJSLogger loaded this="+this+" GM_log="+typeof this.GM_log);
- // console.log("interceptTimeouts is "+(FJSL.interceptTimeouts?"ENABLED":"DISABLED"));
- // Intercept messages for console.log if it exists.
- // Create console.log if it does not exist.
- var oldConsole = this.console; // When running as a userscript in Chrome, cannot see this.console!
- // TODO: We should probably remove all this GM_ stuff.
- // We aren't using it even if we do find it.
- var oldGM_log = this.GM_log;
- var target = ( this.unsafeWindow ? this.unsafeWindow : window );
- // Replace the old console
- target.console = {};
- var lastArgs = [];
- target.console.log = function(a,b,c) {
- // I tried disabling this and regretted it!
- // My Console bookmarklet can cause an infloop with FJSL if you want to test it.
- var args = Array.prototype.slice.call(arguments, 0);
- if (arraysEqual(args, lastArgs)) {
- return;
- }
- lastArgs = args;
- // Replicate to the old loggers we intercepted (overrode)
- if (oldConsole && oldConsole.log && !FJSL.muteLogRepeater) {
- // Some browsers dislike use of .call and .apply here, e.g. GM in FF4.
- // So to avoid "oldConsole.log is not a function":
- try {
- oldConsole.log.apply(oldConsole,arguments);
- } catch (e) {
- // Ugly chars to signify that sucky fallback is being used
- oldConsole.log("[noapply]",a,b,c);
- }
- }
- /*
- //// WARNING: *This* is the cause of infinite console.logging, if it has been implemented by FallbackGMAPI.
- //// Solution: FBGMAPI should take a reference to console when creating it's GM_log, not waiting until later to look for the console.
- if (oldGM_log) {
- oldGM_log(a,b,c);
- }
- */
- addToFastJSLog("log", arguments);
- };
- //// NOT TESTED. Intercept Greasemonkey log messages. (Worth noting we have disabled calls *to* GM_log above.)
- // this.GM_log = target.console.log;
- // == MAIN SCRIPT ENDS ==
- // The rest is all optional so may be stripped for minification.
- /* Provide/intercept console.info/warn/error(). */
- target.console.error = function() {
- // We can get away with this in Chrome!
- //var args = Array.prototype.slice.call(arguments,0);
- //args.unshift("[ERROR]");
- oldConsole.error.apply(oldConsole, arguments);
- addToFastJSLog("error", arguments); // ,'\n'+getStack(2,20).join("\n"));
- // Report stacktrace if we were passed an error.
- if (arguments[0] instanceof Error) {
- //target.console.error("" + arguments[0].stack);
- addToFastJSLog("error", "" + arguments[0].stack);
- }
- };
- // Could generalise the two functions below:
- //interceptLogLevel("warn");
- //interceptLogLevel("info");
- target.console.warn = function() {
- //var args = Array.prototype.slice.call(arguments,0);
- //args.push(""+getCallerFromStack());
- oldConsole.warn.apply(oldConsole, arguments);
- addToFastJSLog("warn", arguments);
- //// This was quite useful on one occasion. But not in the presence of lots of warnings!
- // logStack(getStackFromCaller());
- };
- target.console.info = function() {
- oldConsole.info.apply(oldConsole, arguments);
- addToFastJSLog("info", arguments);
- };
- /*
- target.console.error = function(e) {
- // target.console.log("[ERROR]",e,e.stack);
- var newArgs = [];
- newArgs.push("[ERROR]");
- for (var i=0;i<arguments.length;i++) {
- newArgs.push(arguments[i]);
- }
- try {
- newArgs.push("(reported from function "+arguments.callee.caller.name+")");
- // console.error("caller = "+arguments.callee.caller);
- // console.error("stack = "+e.stack);
- } catch (e) {
- }
- target.console.log.apply(target.console,newArgs);
- target.console.log.apply(target.console,["Stacktrace:\n",e.stack]);
- };
- */
- // Some more library functions:
- function arraysEqual(a, b) {
- for (var i=0; i<a.length; i++) {
- if (a[i] !== b[i]) {
- return false;
- }
- }
- return true;
- }
- function tryToDo(fn, target, args) {
- try {
- return fn.apply(target, args);
- } catch (e) {
- // Actually hard to read!
- // var prettyFn = fn; // ("" + fn) .replace(/\n/g,'\\n');
- var fnName = fn && fn.name;
- if (!fnName) {
- fnName = "<anonymous>";
- }
- // console.log("[ERR]", e, prettyFn);
- // console.log("[Exception]", e, "from " + fnName + "()");
- if (e.stack) {
- console.error("!! " + e.stack);
- } else if (e.lineNumber) {
- console.error("!! " + e + " on " + e.lineNumber + ")");
- } else {
- console.error("!!", e);
- }
- // var prettyFn = ("" + fn).replace(/\n/,/ /, 'g');
- var prettyFn = shortenString(fn);
- console.error("occurred when calling: " + prettyFn);
- // If we rethrow the error, browser devtools will show the throw as coming from here!
- // But we should throw it anyway. That is what the function's caller expects.
- // At one point we tried to reproduce the error by calling fn again, but not catching it.
- // However this is a dangerous tactic. What if fn() creates a setTimeout before throwing its exception? Then calling it again would produce a second setTimeout!
- //return fn.apply(target, args);
- throw e;
- }
- }
- // TODO: Unify showObject and shortenString with replay.js's toDetailedString and toSimpleString.
- function showObject(obj) {
- return "{ " + Object.keys(obj).map(function(prop) {
- return prop + ": " + shortenString(obj[prop]);
- }).join(", ") + " }";
- }
- function getXPath(node) {
- var parent = node.parentNode;
- if (!parent) {
- return '';
- }
- var siblings = parent.childNodes;
- var totalCount = 0;
- var thisCount = -1;
- for (var i=0;i<siblings.length;i++) {
- var sibling = siblings[i];
- if (sibling.nodeType == node.nodeType) {
- totalCount++;
- }
- if (sibling == node) {
- thisCount = totalCount;
- break;
- }
- }
- return getXPath(parent) + '/' + node.nodeName.toLowerCase() + (totalCount>1 ? '[' + thisCount + ']' : '' );
- }
- function getStack(drop,max) {
- var stack;
- try {
- throw new Error("Dummy for getStack");
- } catch (e) {
- stack = e.stack.split('\n').slice(drop).slice(0,max);
- }
- return stack;
- }
- // What frame/function called the function which called us?
- function getCallerFromStack() {
- return getStack(4,1);
- }
- // The frame/function which called our caller, and all above it.
- function getStackFromCaller() {
- return getStack(4,-1);
- }
- function logStack(stack) {
- for (var i=0;i<stack.length;i++) {
- console.log(""+stack[i]);
- }
- }
- // == Error Interceptors ==
- // Whenever a callback is placed, we should wrap it!
- // DONE:
- // event listeners (added after we run)
- // setTimeout
- // TODO:
- // setInterval
- // XMLHttpRequest
- // We could even wrap any standard functions which are common sources of
- // Errors, in case we fail to catch them any other way.
- if (FJSL.watchWindowForErrors) {
- // Registers a window.onerror event handler, which catches DOM Errors like
- // img elements which failed to load their src resource.
- function handleError(evt) {
- if (!FJSL.firstWindowErrorEvent) {
- FJSL.firstWindowErrorEvent = evt;
- }
- //// Expose this event for inspection
- FJSL.lastWindowErrorEvent = evt;
- // console.log("Error caught by FJSL:");
- // console.log(evt);
- // console.log(Object.keys(evt));
- // target.console.error(evt.filename+":"+evt.lineno+" "+evt.message+" ["+evt.srcElement+"]",evt);
- // In fact repeating these to the browser log is redundant, since the
- // browser is likely to report these errors anyway.
- // Therefore, we could use addToFastJSLog instead of console.log below.
- // Chrome sometimes gives us a message, sometimes doesn't.
- if (evt.message) {
- // This is what Chrome provides for an error thrown by a page script.
- // In Chrome this event object contains neither an Error nor a stack-trace.
- // Also the current stack-trace is uninformative (see nothing about the call to handleError.)
- // So we don't use the following: , evt, getStack(0,99).join('\n')
- console.error("[Caught Error] "+evt.message+" "+evt.filename+":"+evt.lineno);
- } else {
- var report = '[Caught Unknown Error] ';
- report += "type="+evt.type+" ";
- // For some errors neither Firefox nor Chrome give us a message.
- // But sometimes we can peek into the element that fired the error.
- // If it was an image, then its src attribute may be useful.
- var elem = evt.srcElement || evt.target;
- if (elem) {
- report += "From element "+elem+" ";
- try {
- report += "with src="+elem.src+" ";
- } catch (e) { }
- }
- /*
- if (elem == window) {
- FJSL.lastEE = evt;
- }
- */
- /*
- if (!elem) {
- // Firefox 14 was providing only a constructor function in the event,
- // so I wonder if calling it will reproduce the error for us. Either
- // that, or it will try to construct an event object. :P
- // I will probably remove this if I see it again and confirm that it's useless.
- // I did get a HUGE string from Firefox which was pretty interesting.
- // But it's not what we were looking for.
- if (typeof evt.constructor == "function") {
- try {
- var result = evt.constructor();
- if (result) {
- console.error(report + "Constructor result: "+result);
- }
- } catch (e) {
- // I think in Firefox this gives us useful information.
- // TODO: But I have seen us reach here in Chrome, with an error "cannot call DOM Object constructor" presumably meaning out evt.constructor() call was forbidden, and we should look elsewhere for information.
- report += "(Constructor error: \""+e+"\") ";
- // Dirty cheat expose it for dev/debugger:
- evt.FJSLconstructorError = e;
- // I have come to the conclusion that although they are different, the errors I get in both Firefox and Chrome are errors about the way I have called the constructor, and not any useful error I was after :P
- // FF: Timestamp: 29/07/12 18:10:14
- // Error: NS_ERROR_XPC_NOT_ENOUGH_ARGS: Not enough arguments
- console.log("[Constructor Error]",e);
- throw e;
- }
- }
- }
- */
- console.error(report,evt);
- }
- }
- window.addEventListener("error",handleError,true);
- // document.body.addEventListener("error",handleError,true);
- }
- // Split into shortenString, escapeString, compressString, and toSimpleString.
- function shortenString(s) {
- s = ""+s;
- s = s.replace(/\n[ \t]+/,'\\n ','g');
- s = s.replace(/\n/,'\\n','g');
- s = s.replace(/\t/,' ','g');
- if (s.length > 202) {
- s = s.substring(0,202) + "...";
- }
- return s;
- }
- if (FJSL.interceptTimeouts) {
- window.setTimeout = function(fn, ms) {
- if (FJSL.logTimeouts) {
- // TODO: We used to pass ,fn to log here. That was nice in Chrome, because we can fold it open or closed, but too spammy for plain FJSL. Recommendation: Refactor out prettyShow() fn which will determine browser and provide raw object for chrome or shortened string for noob browsers?
- console.info("[EVENT] setTimeout() called: "+ms+", "+shortenString(fn));
- }
- var wrappedFn = function(){
- // setTimeout can be passed a string. But we want a function so we can call it later.
- if (typeof fn === 'string') {
- var str = fn;
- fn = function(){ eval(str); };
- }
- // CONSIDER: Of dubious value
- if (typeof fn !== 'function') {
- console.error("[FJSL] setTimeout was not given a function!",fn);
- return;
- }
- // We don't really need to return here. setTimeout won't do anything with the returned value.
- // Similarly it probably hasn't passed us any arguments. The context object 'this' is probably 'window'.
- return tryToDo(fn, this, arguments);
- };
- return oldSetTimeout(wrappedFn, ms);
- };
- }
- // == General Info Logging Utilities ==
- if (FJSL.logNodeInsertions) {
- document.body.addEventListener('DOMNodeInserted', function(evt) {
- if (evt.target === logContainer || evt.target.parentNode === logContainer) {
- return;
- }
- // console.log("[DOMNodeInserted]: target=",evt.target," event=",evt);
- // console.log("[DOMNodeInserted]:",evt,evt.target,"path="+getXPath(evt.target),"html="+evt.target.outerHTML);
- console.log("[DOMNodeInserted]: path="+getXPath(evt.target)+" html="+evt.target.outerHTML,evt);
- }, true);
- }
- if (FJSL.interceptEvents) {
- // This store helps us to remove the wrapped event listeners which we add
- var wrapped_and_unwrapped = [];
- var realAddEventListener = HTMLElement.prototype.addEventListener;
- // HTMLElement.prototype.oldAddEventListener = realAddEventListener;
- HTMLElement.prototype.addEventListener = function(type,handler,capture,other){
- if (FJSL.logEvents) {
- // Note niceFunc is re-used later, so don't remove this!
- var niceFunc = handler.name || (""+handler).substring(0,80).replace(/\n/," ",'g').replace(/[ \t]*/," ",'g')+"...";
- /*
- console.info("[EVENTS] Listening for "+type+" events on "+getXPath(this)+" with handler "+niceFunc);
- console.info(getStack(3,3).join("\n"));
- */
- }
- var newHandler = function(evt) {
- if (FJSL.logEvents) {
- var isSpammyEvent = ["mousemove","mouseover","mouseout","mousedown","mouseup","keydown","keyup"].indexOf(evt.type) >= 0;
- if (!isSpammyEvent || FJSL.logCommonMouseEvents) {
- if (evt.target.parentNode == logContainer) {
- // Do not log events in the console's log area, such as DOMNodeInserted!
- } else {
- // console.log("("+type+") on "+evt.target);
- // console.info("[EVENT] "+type+" on "+getXPath(evt.target));
- // console.log("[EVENT] "+type+" on "+getXPath(evt.target)+" evt="+showObject(evt));
- console.info("[EVENT] "+type+" on "+getXPath(evt.target)+" being handled by "+niceFunc);
- }
- }
- }
- return tryToDo(handler, this, arguments);
- // return handler.apply(this, arguments);
- };
- wrapped_and_unwrapped.push({
- unwrapped: handler,
- wrapped: newHandler,
- });
- return realAddEventListener.call(this,type,newHandler,capture,other);
- };
- // We cannot remove the original handler, because it was never added. Instead we need to remove the wrapper that we did add.
- var realRemoveEventListener = HTMLElement.prototype.removeEventListener;
- // HTMLElement.prototype.oldRemoveEventListener = realAddEventListener;
- HTMLElement.prototype.removeEventListener = function(type,handler,capture,other){
- //console.log("removeEventListener was called with:",type,handler);
- //try { throw new Error("dummy for stacktrace"); } catch (e) { console.log("At:",e); }
- for (var i = wrapped_and_unwrapped.length; i--;) {
- var both = wrapped_and_unwrapped[i];
- if (handler === both.unwrapped) {
- handler = both.wrapped;
- wrapped_and_unwrapped.splice(i,1);
- break;
- }
- }
- return realRemoveEventListener.call(this,type,handler,capture,other);
- };
- }
- if (FJSL.logChangesToGlobal) {
- function newChangeWatcher() {
- var checkSpeed = 4000;
- var watcher = {};
- var global = window;
- var knownKeys = {};
- var firstCheck = true;
- function checkGlobal() {
- if (FJSL.logChangesToGlobal) {
- for (var key in global) {
- if (knownKeys[key] === undefined) {
- if (!firstCheck) {
- var obj = global[key];
- console.log("[New global detected] "+key+" =",obj);
- }
- knownKeys[key] = 1;
- }
- }
- firstCheck = false;
- }
- oldSetTimeout(checkGlobal,checkSpeed);
- }
- oldSetTimeout(checkGlobal,checkSpeed);
- return watcher;
- };
- var changeWatcher = newChangeWatcher();
- }
- };
- if (document.body) {
- loadFJSL();
- } else {
- // doc bod may not exist if we are loaded in the HEAD! Better wait till later...
- setTimeout(loadFJSL,1000);
- // But we would quite like to start doing stuff now anyway!
- // TODO: Load what we can immediately, delay only things we must.
- // E.g. start capturing text, even if we can't display it yet.
- }
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址