| $(function() { |
| if (!EVALEX_TRUSTED) { |
| initPinBox(); |
| } |
| |
| /** |
| * if we are in console mode, show the console. |
| */ |
| if (CONSOLE_MODE && EVALEX) { |
| openShell(null, $('div.console div.inner').empty(), 0); |
| } |
| |
| $('div.detail').click(function() { |
| $('div.traceback').get(0).scrollIntoView(false); |
| }); |
| |
| $('div.traceback div.frame').each(function() { |
| var target = $('pre', this), consoleNode = null, |
| frameID = this.id.substring(6); |
| |
| target.click(function() { |
| $(this).parent().toggleClass('expanded'); |
| }); |
| |
| /** |
| * Add an interactive console to the frames |
| */ |
| if (EVALEX && target.is('.current')) { |
| $('<img src="?__debugger__=yes&cmd=resource&f=console.png">') |
| .attr('title', 'Open an interactive python shell in this frame') |
| .click(function() { |
| consoleNode = openShell(consoleNode, target, frameID); |
| return false; |
| }) |
| .prependTo(target); |
| } |
| }); |
| |
| /** |
| * toggle traceback types on click. |
| */ |
| $('h2.traceback') |
| .click(function() { |
| $(this).next().slideToggle('fast'); |
| $('div.plain').slideToggle('fast'); |
| }) |
| .css('cursor', 'pointer'); |
| $('div.plain').hide(); |
| |
| /** |
| * Add extra info (this is here so that only users with JavaScript |
| * enabled see it.) |
| */ |
| $('span.nojavascript') |
| .removeClass('nojavascript') |
| .html( |
| '<p>To switch between the interactive traceback and the plaintext ' + |
| 'one, you can click on the "Traceback" headline. From the text ' + |
| 'traceback you can also create a paste of it. ' + |
| (!EVALEX ? |
| '' : |
| 'For code execution mouse-over the frame you want to debug and ' + |
| 'click on the console icon on the right side.' + |
| '<p>You can execute arbitrary Python code in the stack frames and ' + |
| 'there are some extra helpers available for introspection:' + |
| '<ul><li><code>dump()</code> shows all variables in the frame' + |
| '<li><code>dump(obj)</code> dumps all that\'s known about the object</ul>')); |
| |
| /** |
| * Add the pastebin feature |
| */ |
| $('div.plain form').submit(function() { |
| var label = $('input[type="submit"]', this); |
| var old_val = label.val(); |
| label.val('submitting...'); |
| $.ajax({ |
| dataType: 'json', |
| url: document.location.pathname, |
| data: {__debugger__: 'yes', tb: TRACEBACK, cmd: 'paste', s: SECRET}, |
| success: function(data) { |
| $('div.plain span.pastemessage') |
| .removeClass('pastemessage') |
| .text('Paste created: ') |
| .append($('<a>#' + data.id + '</a>').attr('href', data.url)); |
| }, |
| error: function() { |
| alert('Error: Could not submit paste. No network connection?'); |
| label.val(old_val); |
| } |
| }); |
| return false; |
| }); |
| |
| // if we have javascript we submit by ajax anyways, so no need for the |
| // not scaling textarea. |
| var plainTraceback = $('div.plain textarea'); |
| plainTraceback.replaceWith($('<pre>').text(plainTraceback.text())); |
| }); |
| |
| function initPinBox() { |
| $('.pin-prompt form').submit(function(evt) { |
| evt.preventDefault(); |
| var pin = this.pin.value; |
| var btn = this.btn; |
| btn.disabled = true; |
| $.ajax({ |
| dataType: 'json', |
| url: document.location.pathname, |
| data: {__debugger__: 'yes', cmd: 'pinauth', pin: pin, s: SECRET}, |
| success: function(data) { |
| btn.disabled = false; |
| if (data.auth) { |
| EVALEX_TRUSTED = true; |
| $('.pin-prompt').fadeOut(); |
| } else { |
| if (data.exhausted) { |
| alert('Error: too many attempts. Restart server to retry.'); |
| } else { |
| alert('Error: incorrect pin'); |
| } |
| } |
| console.log(data); |
| }, |
| error: function() { |
| btn.disabled = false; |
| alert('Error: Could not verify PIN. Network error?'); |
| } |
| }); |
| }); |
| } |
| |
| function promptForPin() { |
| if (!EVALEX_TRUSTED) { |
| $.ajax({ |
| url: document.location.pathname, |
| data: {__debugger__: 'yes', cmd: 'printpin', s: SECRET} |
| }); |
| $('.pin-prompt').fadeIn(function() { |
| $('.pin-prompt input[name="pin"]').focus(); |
| }); |
| } |
| } |
| |
| |
| /** |
| * Helper function for shell initialization |
| */ |
| function openShell(consoleNode, target, frameID) { |
| promptForPin(); |
| if (consoleNode) |
| return consoleNode.slideToggle('fast'); |
| consoleNode = $('<pre class="console">').appendTo(target.parent()).hide() |
| var historyPos = 0, history = ['']; |
| var output = |
| $('<div class="output">[console ready]</div>').appendTo(consoleNode); |
| var form = |
| $('<form>>>> </form>') |
| .submit(function() { |
| var cmd = command.val(); |
| $.get( |
| '', {__debugger__: 'yes', cmd: cmd, frm: frameID, s: SECRET}, |
| function(data) { |
| var tmp = $('<div>').html(data); |
| $('span.extended', tmp).each(function() { |
| var hidden = $(this).wrap('<span>').hide(); |
| hidden.parent().append( |
| $('<a href="#" class="toggle"> </a>') |
| .click(function() { |
| hidden.toggle(); |
| $(this).toggleClass('open') |
| return false; |
| })); |
| }); |
| output.append(tmp); |
| command.focus(); |
| consoleNode.scrollTop(consoleNode.get(0).scrollHeight); |
| var old = history.pop(); |
| history.push(cmd); |
| if (typeof old != 'undefined') |
| history.push(old); |
| historyPos = history.length - 1; |
| }); |
| command.val(''); |
| return false; |
| }) |
| .appendTo(consoleNode); |
| |
| var command = |
| $('<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">') |
| .appendTo(form) |
| .keydown(function(e) { |
| if (e.key == 'l' && e.ctrlKey) { |
| output.text('--- screen cleared ---'); |
| return false; |
| } else if ( |
| e.charCode == 0 && (e.keyCode == 38 || e.keyCode == 40)) { |
| // handle up arrow and down arrow |
| if (e.keyCode == 38 && historyPos > 0) |
| historyPos--; |
| else if (e.keyCode == 40 && historyPos < history.length) |
| historyPos++; |
| command.val(history[historyPos]); |
| return false; |
| } |
| }); |
| |
| return consoleNode.slideDown('fast', function() { |
| command.focus(); |
| }); |
| } |