debugger.js 6.2 KB

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