Stack Snippets Console

Add a console to "Stack Snippets" executable code on StackExchange

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
  1. // ==UserScript==
  2. // @name Stack Snippets Console
  3. // @namespace http://stackexchange.com/users/3846032/scimonster
  4. // @version 1.1.0
  5. // @description Add a console to "Stack Snippets" executable code on StackExchange
  6. // @include http://*.stackexchange.com/*
  7. // @include http://stackoverflow.com/*
  8. // @include http://*.stackoverflow.com/*
  9. // @include http://stacksnippets.net/*
  10. // @copyright 2014+, Scimonster
  11. // ==/UserScript==
  12.  
  13. if (!window.postMessage) {
  14. console.log('postMessage not supported; Snippets Console cannot work');
  15. return;
  16. }
  17.  
  18. if (location.hostname == 'stacksnippets.net') {
  19. (function(){
  20.  
  21. // define some functions
  22.  
  23. function isSEDomainName(dname) {
  24. return !!~["stackoverflow.com", "stackexchange.com"].indexOf(dname.split('.').slice(-2).join('.'));
  25. }
  26.  
  27. function URLparser(href) {
  28. var p = document.createElement('a');
  29. p.href = href;
  30. return p;
  31. }
  32.  
  33. // listen for the ping
  34.  
  35. this.addEventListener('message', function(ev){
  36. if (isSEDomainName(URLparser(ev.origin).hostname) && ev.data == 'console ping') {
  37. // passes the test
  38. listen(ev);
  39. }
  40. }, false);
  41.  
  42. // listen for console.log()s
  43.  
  44. function listen(ev) {
  45.  
  46. var oldLog = console.log;
  47.  
  48. function log(firstArg) {
  49. var args = [].slice.call(arguments);
  50.  
  51. oldLog.apply(console, args); // use the default console.log()
  52.  
  53. if (typeof firstArg == 'string') { // might require replacing
  54. if (~firstArg.indexOf('%s') || ~firstArg.indexOf('%d') || ~firstArg.indexOf('%i') || ~firstArg.indexOf('%f')) {
  55. // there is something to be replaced
  56. args.shift();
  57. firstArg = firstArg.replace(/%[sdif]/g, function(match){
  58. switch (match) {
  59. case '%s': return args.shift().toString();
  60. case '%d': case '%i': return parseInt(args.shift());
  61. case '%f': return parseFloat(args.shift());
  62. default: return match;
  63. }
  64. });
  65. args.unshift(firstArg);
  66. }
  67. }
  68. // replacements done, ready for concat
  69. var text = args.map(function(a){
  70. if (typeof a == 'object') {
  71. try {
  72. return JSON.stringify(a);
  73. } catch(e) {
  74. return "{bad object}";
  75. }
  76. }
  77. return a.toString();
  78. });
  79.  
  80. postMessage('log', text.join(''));
  81. }
  82.  
  83. this.console.log = log;
  84.  
  85. this.addEventListener('error', function(ev){
  86. postMessage('err', ev.message);
  87. }, false);
  88.  
  89. function postMessage(type, message) {
  90. ev.source.postMessage({
  91. type: type,
  92. message: message,
  93. time: new Date,
  94. snippet: this.name // the iframe
  95. }, ev.origin);
  96. }
  97.  
  98. }
  99.  
  100. })();
  101. } else {
  102. (function(){
  103.  
  104. function padNum(num, length) {
  105. num = num.toString();
  106. return num.length < length ? padNum('0' + num, length) : num;
  107. }
  108.  
  109. $(document).on('click', 'div.snippet-result input[type=button]', function(){
  110. var container = $(this).parent().parent();
  111. container.children('.snippet-console').remove();
  112. if (!this.className) { // the run button
  113. $('<div class="snippet-console"><h6><a href="http://stackapps.com/q/4931/28683" target="_blank">Snippet Console</a> <small>v1.1.0</small></h6><ul></ul></div>').appendTo(container).css({
  114. position: 'relative',
  115. width: '100%',
  116. maxHeight: 200,
  117. borderTopWidth: 1,
  118. borderTopStyle: 'solid',
  119. borderTopColor: 'rgb(170, 170, 170)',
  120. overflow: 'auto',
  121. display: 'none'
  122. }).children('ul').css({
  123. listStyleType: 'none',
  124. fontFamily: 'monospace'
  125. });
  126.  
  127. var snippet = container.find('iframe')[0];
  128.  
  129. snippet.onload = function(){
  130. setTimeout(function(){
  131. snippet.contentWindow.postMessage('console ping', "*");
  132. // need to accept all because the iframe has no URL
  133. }, 100);
  134. };
  135. }
  136. });
  137.  
  138. window.addEventListener('message', function(ev){
  139. if (ev.origin == 'null' && ev.data.snippet) { // seems to be a snippet
  140. var time = padNum(ev.data.time.getHours(),2)+':'+padNum(ev.data.time.getMinutes(),2)+':'+padNum(ev.data.time.getSeconds(),2)+'.'+padNum(ev.data.time.getMilliseconds(),3);
  141. var li = $('<li data-type="'+ev.data.type+'"><span style="color:gray">'+time+':</span> <span></span></li>');
  142. li.find('span').last().text(ev.data.message);
  143. if (ev.data.type == 'err') {
  144. li.find('span').last().css('color', 'red');
  145. }
  146. var snCons = $('iframe[name="'+ev.data.snippet+'"]').parent().siblings('.snippet-console').show();
  147. snCons.children('ul').append(li);
  148. snCons.scrollTop(snCons.children('ul').height()); // scroll to bottom
  149. }
  150. }, false);
  151. })();
  152. }