debugger.js 6.2 KB

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