debugger.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. $(function() {
  2. if (!EVALEX_TRUSTED) {
  3. initPinBox();
  4. }
  5. /**
  6. * if we are in console mode, show the console.
  7. */
  8. if (CONSOLE_MODE && EVALEX) {
  9. openShell(null, $('div.console div.inner').empty(), 0);
  10. }
  11. $("div.detail").click(function() {
  12. $("div.traceback").get(0).scrollIntoView(false);
  13. });
  14. $('div.traceback div.frame').each(function() {
  15. var
  16. target = $('pre', this),
  17. consoleNode = null,
  18. frameID = this.id.substring(6);
  19. target.click(function() {
  20. $(this).parent().toggleClass('expanded');
  21. });
  22. /**
  23. * Add an interactive console to the frames
  24. */
  25. if (EVALEX && target.is('.current')) {
  26. $('<img src="?__debugger__=yes&cmd=resource&f=console.png">')
  27. .attr('title', 'Open an interactive python shell in this frame')
  28. .click(function() {
  29. consoleNode = openShell(consoleNode, target, frameID);
  30. return false;
  31. })
  32. .prependTo(target);
  33. }
  34. });
  35. /**
  36. * toggle traceback types on click.
  37. */
  38. $('h2.traceback').click(function() {
  39. $(this).next().slideToggle('fast');
  40. $('div.plain').slideToggle('fast');
  41. }).css('cursor', 'pointer');
  42. $('div.plain').hide();
  43. /**
  44. * Add extra info (this is here so that only users with JavaScript
  45. * enabled see it.)
  46. */
  47. $('span.nojavascript')
  48. .removeClass('nojavascript')
  49. .html('<p>To switch between the interactive traceback and the plaintext ' +
  50. 'one, you can click on the "Traceback" headline. From the text ' +
  51. 'traceback you can also create a paste of it. ' + (!EVALEX ? '' :
  52. 'For code execution mouse-over the frame you want to debug and ' +
  53. 'click on the console icon on the right side.' +
  54. '<p>You can execute arbitrary Python code in the stack frames and ' +
  55. 'there are some extra helpers available for introspection:' +
  56. '<ul><li><code>dump()</code> shows all variables in the frame' +
  57. '<li><code>dump(obj)</code> dumps all that\'s known about the object</ul>'));
  58. /**
  59. * Add the pastebin feature
  60. */
  61. $('div.plain form')
  62. .submit(function() {
  63. var label = $('input[type="submit"]', this);
  64. var old_val = label.val();
  65. label.val('submitting...');
  66. $.ajax({
  67. dataType: 'json',
  68. url: document.location.pathname,
  69. data: {__debugger__: 'yes', tb: TRACEBACK, cmd: 'paste',
  70. s: SECRET},
  71. success: function(data) {
  72. $('div.plain span.pastemessage')
  73. .removeClass('pastemessage')
  74. .text('Paste created: ')
  75. .append($('<a>#' + data.id + '</a>').attr('href', data.url));
  76. },
  77. error: function() {
  78. alert('Error: Could not submit paste. No network connection?');
  79. label.val(old_val);
  80. }
  81. });
  82. return false;
  83. });
  84. // if we have javascript we submit by ajax anyways, so no need for the
  85. // not scaling textarea.
  86. var plainTraceback = $('div.plain textarea');
  87. plainTraceback.replaceWith($('<pre>').text(plainTraceback.text()));
  88. });
  89. function initPinBox() {
  90. $('.pin-prompt form').submit(function(evt) {
  91. evt.preventDefault();
  92. var pin = this.pin.value;
  93. var btn = this.btn;
  94. btn.disabled = true;
  95. $.ajax({
  96. dataType: 'json',
  97. url: document.location.pathname,
  98. data: {__debugger__: 'yes', cmd: 'pinauth', pin: pin,
  99. s: SECRET},
  100. success: function(data) {
  101. btn.disabled = false;
  102. if (data.auth) {
  103. EVALEX_TRUSTED = true;
  104. $('.pin-prompt').fadeOut();
  105. } else {
  106. if (data.exhausted) {
  107. alert('Error: too many attempts. Restart server to retry.');
  108. } else {
  109. alert('Error: incorrect pin');
  110. }
  111. }
  112. console.log(data);
  113. },
  114. error: function() {
  115. btn.disabled = false;
  116. alert('Error: Could not verify PIN. Network error?');
  117. }
  118. });
  119. });
  120. }
  121. function promptForPin() {
  122. if (!EVALEX_TRUSTED) {
  123. $.ajax({
  124. url: document.location.pathname,
  125. data: {__debugger__: 'yes', cmd: 'printpin', s: SECRET}
  126. });
  127. $('.pin-prompt').fadeIn(function() {
  128. $('.pin-prompt input[name="pin"]').focus();
  129. });
  130. }
  131. }
  132. /**
  133. * Helper function for shell initialization
  134. */
  135. function openShell(consoleNode, target, frameID) {
  136. promptForPin();
  137. if (consoleNode)
  138. return consoleNode.slideToggle('fast');
  139. consoleNode = $('<pre class="console">')
  140. .appendTo(target.parent())
  141. .hide()
  142. var historyPos = 0, history = [''];
  143. var output = $('<div class="output">[console ready]</div>')
  144. .appendTo(consoleNode);
  145. var form = $('<form>&gt;&gt;&gt; </form>')
  146. .submit(function() {
  147. var cmd = command.val();
  148. $.get('', {
  149. __debugger__: 'yes', cmd: cmd, frm: frameID, s: SECRET}, function(data) {
  150. var tmp = $('<div>').html(data);
  151. $('span.extended', tmp).each(function() {
  152. var hidden = $(this).wrap('<span>').hide();
  153. hidden
  154. .parent()
  155. .append($('<a href="#" class="toggle">&nbsp;&nbsp;</a>')
  156. .click(function() {
  157. hidden.toggle();
  158. $(this).toggleClass('open')
  159. return false;
  160. }));
  161. });
  162. output.append(tmp);
  163. command.focus();
  164. consoleNode.scrollTop(consoleNode.get(0).scrollHeight);
  165. var old = history.pop();
  166. history.push(cmd);
  167. if (typeof old != 'undefined')
  168. history.push(old);
  169. historyPos = history.length - 1;
  170. });
  171. command.val('');
  172. return false;
  173. }).
  174. appendTo(consoleNode);
  175. var command = $('<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">')
  176. .appendTo(form)
  177. .keydown(function(e) {
  178. if (e.key == 'l' && e.ctrlKey) {
  179. output.text('--- screen cleared ---');
  180. return false;
  181. }
  182. else if (e.charCode == 0 && (e.keyCode == 38 || e.keyCode == 40)) {
  183. // handle up arrow and down arrow
  184. if (e.keyCode == 38 && historyPos > 0)
  185. historyPos--;
  186. else if (e.keyCode == 40 && historyPos < history.length)
  187. historyPos++;
  188. command.val(history[historyPos]);
  189. return false;
  190. }
  191. });
  192. return consoleNode.slideDown('fast', function() {
  193. command.focus();
  194. });
  195. }