Native implementation of a jsFiddle.net in-pane log console

To be used among external libraries when testing other jsFiddle.net scripts

目前为 2018-12-24 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/375910/656390/Native%20implementation%20of%20a%20jsFiddlenet%20in-pane%20log%20console.js

  1. // ==UserScript==
  2. // @name Native implementation of a jsFiddle.net in-pane log console
  3. // @version 0.1.0
  4. // @description To be used among external libraries when testing other jsFiddle.net scripts
  5. // @namespace https://greasyfork.org/en/users/15562
  6. // @author Jonathan Brochu (https://greasyfork.org/en/users/15562)
  7. // @license GPLv3 or later (http://www.gnu.org/licenses/gpl-3.0.en.html)
  8. // @include https://jsfiddle.net/*
  9. // @grant GM_addStyle
  10. // ==/UserScript==
  11.  
  12. /***
  13. * History:
  14. *
  15. * 0.1.0 First implementation, based on own's previous work. (2018-12-24)
  16. *
  17. */
  18.  
  19. // Helpers, implemented on Element/Document/DocumentFragment.prototype
  20. (function (arrTargets, arrSpecs) {
  21. var __fn__ = 'function',
  22. __und__ = 'undefined';
  23. arrTargets.forEach(function (target) {
  24. arrSpecs.forEach(function (spec) {
  25. if (typeof spec.prop === __und__) {
  26. return;
  27. }
  28. if (typeof spec.impl === __und__) {
  29. return;
  30. }
  31. var propName = spec.prop,
  32. propImpl = spec.impl;
  33. accessors = spec.accessors.split('|'),
  34. getArgCount = spec.getArgCount || spec.setArgCount-1 || 0,
  35. readOnly = (typeof spec.readOnly !== __und__ ? spec.readOnly : false);
  36. if (target.hasOwnProperty(propName)) {
  37. return;
  38. }
  39. if (typeof propImpl !== __fn__) {
  40. return;
  41. }
  42. var desc = {
  43. configurable: true,
  44. enumerable: true,
  45. writable: !readOnly
  46. };
  47. ['get', 'set', 'value'].forEach(function(item) {
  48. if (accessors.indexOf(item) == -1) return;
  49. switch(item) {
  50. case 'get': desc[item] = function() {
  51. return (typeof propImpl === __fn__ ? propImpl.apply(this, arguments) : propImpl);
  52. }; break;
  53. case 'set': desc[item] = function() {
  54. return (typeof propImpl === __fn__ ? propImpl.apply(this, arguments) : propImpl);
  55. }; break;
  56. default: desc[item] = propImpl;
  57. }
  58. });
  59. if (typeof desc.get !== __und__) {
  60. delete desc.value;
  61. delete desc.writable;
  62. }
  63. if (typeof desc.set !== __und__) {
  64. delete desc.writable;
  65. }
  66. Object.defineProperty(target, propName, desc);
  67. });
  68. });
  69. })(
  70. [Element.prototype, Document.prototype, DocumentFragment.prototype],
  71. [
  72. {
  73. /*
  74. * Source: https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append
  75. * Original Source: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md
  76. */
  77. prop: 'append',
  78. accessors: 'value',
  79. impl: function append() {
  80. console.$nativeImpl.log('.append() { console.log(this); } => ' + this);
  81. var argArr = Array.prototype.slice.call(arguments),
  82. docFrag = document.createDocumentFragment();
  83. argArr.forEach(function (argItem) {
  84. var isNode = argItem instanceof Node;
  85. docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
  86. });
  87. this.appendChild(docFrag);
  88. return this;
  89. }
  90. },
  91. {
  92. prop: 'empty',
  93. accessors: 'value',
  94. impl: function empty() {
  95. while (this.childNodes.length) this.removeChild(this.firstChild);
  96. return this;
  97. }
  98. },
  99. {
  100. prop: 'html',
  101. accessors: 'value',
  102. impl: function html() {
  103. if (arguments.length) {
  104. switch(typeof arguments[0]) {
  105. case 'string':
  106. this.innerHTML = arguments[0];
  107. break;
  108. default:
  109. this.empty();
  110. this.append(arguments[0]);
  111. }
  112. return this;
  113. } else {
  114. return this.innerHTML;
  115. }
  116. }
  117. }
  118. ]
  119. );
  120.  
  121. // doc.gEBI() & createElementFromHTML() Wrapper
  122. $ = function() {
  123. if (!arguments.length) return;
  124. if (typeof arguments[0] === 'string') {
  125. var arg = arguments[0];
  126. if (/\s/.test(arg)) {
  127. /*
  128. * function createElementFromHTML()
  129. * Source: https://stackoverflow.com/a/494348/3865919
  130. * Author: Crescent Fresh <https://stackoverflow.com/users/45433/crescent-fresh>
  131. */
  132. try {
  133. var tmpDiv = document.createElement('div');
  134. tmpDiv.innerHTML = arg.trim();
  135. return tmpDiv.firstChild;
  136. } catch(e) {
  137. return null;
  138. }
  139. } else {
  140. return document.getElementById(arg);
  141. }
  142. } else if (arguments[0] instanceof Element) {
  143. return arguments[0];
  144. } else return null;
  145. };
  146. $.addCSS = function(css, media) {
  147. /*
  148. * Rewrite of own's addStyle() using code from...
  149. * Source: https://stackoverflow.com/a/524721
  150. * Author: Christoph <https://stackoverflow.com/users/48015/christoph>
  151. */
  152. if (typeof(GM_addStyle) !== 'undefined' && !media) {
  153. GM_addStyle(css);
  154. } else {
  155. if (!media) { media = 'all'; }
  156. var head = document.head || document.getElementsByTagName('head')[0],
  157. styleNode = document.createElement('style');
  158. styleNode.type = 'text/css';
  159. if (media) styleNode.media = media;
  160. if (styleNode.styleSheet){
  161. // This is required for IE8 and below.
  162. styleNode.styleSheet.cssText = css;
  163. } else {
  164. styleNode.appendChild(document.createTextNode(css));
  165. }
  166. head.appendChild(styleNode);
  167. }
  168. };
  169.  
  170. // console.{} implementation
  171. (function(_) {
  172. // make sure we don't override native implementation more than once
  173. if (_.console.clear.toString() !== 'function clear() { [native code] }') { return; }
  174. var __fn__ = 'function',
  175. consoleId = 'console-log',
  176. consoleLine = '<p id="%" class="*-line"></p>',
  177. _console = _.console || {},
  178. methods = ['log', 'info', 'warn', 'error', 'trace'];
  179. _.console = {
  180. $init: function() {
  181. if ($(consoleId)) { return; }
  182. var container = '<div id="console-log" class="scrollbar"></div>',
  183. css =
  184. 'body {\n' +
  185. ' /*background: #1F252D;\n*/' +
  186. '}\n' +
  187. '#console-log {\n' +
  188. ' font-family: "SF Mono", Monaco, "Andale Mono", "Lucida Console", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;\n' +
  189. ' font-size: 12px;\n' +
  190. ' font-variant-ligatures: contextual;\n' +
  191. ' color: rgb(207, 208, 210);\n' +
  192. ' background: #313B47;\n' +
  193. ' border: 3px solid #1B2027;\n' +
  194. ' border-radius: 5px;\n' +
  195. ' padding: 10px; \n' +
  196. ' margin: 5px;\n' +
  197. ' width: 93%;\n' +
  198. ' min-height: 50px;\n' +
  199. ' max-height: 150px;\n' +
  200. ' position: absolute;\n' +
  201. ' bottom: 5px;\n' +
  202. ' overflow: scroll;\n' +
  203. '}\n' +
  204. '.log-line,\n' +
  205. '.info-line,\n' +
  206. '.warn-line,\n' +
  207. '.error-line,\n' +
  208. '.trace-line {\n' +
  209. ' font-family: monospace;\n' +
  210. ' margin: 2px;\n' +
  211. ' white-space: nowrap;\n' +
  212. '}\n' +
  213. '.log-line {\n' +
  214. ' color: rgb(207, 208, 210);\n' +
  215. '}\n' +
  216. '.info-line {\n' +
  217. ' color: #6ce890;\n' +
  218. '}\n' +
  219. '.warn-line {\n' +
  220. ' color: #f8b068;\n' +
  221. '}\n' +
  222. '.error-line {\n' +
  223. ' color: #ff4f68;\n' +
  224. ' font-weight: bold;\n' +
  225. '}\n' +
  226. '.trace-line {\n' +
  227. ' color: #b896ed;\n' +
  228. '}\n' +
  229. '#console-log.scrollbar::-webkit-scrollbar-track {\n' +
  230. ' -webkit-box-shadow: inset 0 0 4px rgba(0,0,0,0.3);\n' +
  231. ' border-radius: 5px;\n' +
  232. ' background-color: #313B47;\n' +
  233. '}\n' +
  234. '#console-log.scrollbar::-webkit-scrollbar {\n' +
  235. ' width: 10px;\n' +
  236. ' height: 10px;\n' +
  237. ' background-color: #313B47;\n' +
  238. '}\n' +
  239. '#console-log.scrollbar::-webkit-scrollbar-thumb {\n' +
  240. ' border-radius: 5px;\n' +
  241. ' -webkit-box-shadow: inset 0 0 4px rgba(0,0,0,.3);\n' +
  242. ' background-color: #262E38;\n' +
  243. '}\n' +
  244. '#console-log.scrollbar::-webkit-scrollbar-corner {\n' +
  245. ' background: #262E38;\n' +
  246. '}\n',
  247. body = document.body || document.getElementsByTagName('body')[0];
  248. $.addCSS(css);
  249. body.append($(container));
  250. },
  251. $typedLog: function(logMethod, text) {
  252. if (methods.indexOf(logMethod) == -1) { return; }
  253. if (!this.$enabled) { return; }
  254. this.$init();
  255. var consoleEl = $(consoleId),
  256. lineHTML = consoleLine
  257. .replace(/\*.{0}/, logMethod)
  258. .replace(/\%/, 'console-line-'+this.$lineCounter),
  259. newLineEl = $(lineHTML);
  260. newLineEl.html(text);
  261. consoleEl.append(newLineEl);
  262. consoleEl.scrollTop = consoleEl.scrollHeight;
  263. this.$lineCounter++;
  264. },
  265. $enabled: true,
  266. $echoNative: false,
  267. $lineCounter: 0,
  268. $nativeImpl: _console,
  269. clear: function() {
  270. this.$init();
  271. $(consoleId).empty();
  272. }
  273. };
  274. for (var prop in _.console) {
  275. if (!Object.prototype.hasOwnProperty.call(_.console, prop)) continue;
  276. if (typeof _.console[prop] !== __fn__) continue;
  277. _.console[prop] = _.console[prop].bind(_.console);
  278. }
  279. methods.forEach(function(method) {
  280. _.console[method] = function() { this.$typedLog(method, Array.prototype.slice.call(arguments).join(' ')); }.bind(_.console);
  281. });
  282. var doEcho = function(method, args) {
  283. if (!this.$echoNative && this.$enabled) { return; }
  284. if (typeof _console.clear !== __fn__ ) { return; }
  285. if (typeof _console[method] !== __fn__ ) { return; }
  286. _console[method].apply(this, args);
  287. }.bind(_.console);
  288. // wrap methods to include echo calls
  289. (function(target) {
  290. for (var prop in target) {
  291. if (!Object.prototype.hasOwnProperty.call(target, prop)) continue;
  292. if (typeof target[prop] !== __fn__) continue;
  293. if (target[prop].$extended) continue;
  294. if (prop.charAt(0) === '$') continue;
  295. target[prop] = (function(){
  296. var _prop = prop,
  297. _fn = target[prop];
  298. return function() {
  299. doEcho(_prop, Array.prototype.slice.call(arguments));
  300. _fn.apply(_fn, arguments);
  301. };
  302. })();
  303. target[prop].$extended = true;
  304. }
  305. })(_.console);
  306. })(window);
  307.  
  308. /*
  309. * Examples:
  310. *
  311. console.$echoNative = true;
  312. console.clear();
  313. console.log("Hello console");
  314. console.info("Need some info?");
  315. console.warn("I might not have what you need though");
  316. console.error("ERROR: Data not found!!");
  317. console.trace("This is somevery long line of unnecessary text his is somevery long line of unnecessary text his is somevery long line of unnecessary text");
  318. */