diff options
| author | Alex Gaynor | 2009-03-21 12:17:05 -0400 | 
|---|---|---|
| committer | Alex Gaynor | 2009-03-21 12:17:05 -0400 | 
| commit | 4ffe14db039981d06aa58f3ad3c2b3472824961b (patch) | |
| tree | b6a94348f2c20bfb6c29303227889d70f06c62c3 /debug_toolbar | |
| parent | 0fea345f0d59ed0f17f29e59072f34d7d016abde (diff) | |
| parent | 8c64bb0cc82c6129b4ab14ff4cc80880761680d4 (diff) | |
| download | django-debug-toolbar-4ffe14db039981d06aa58f3ad3c2b3472824961b.tar.bz2 | |
resoled merge conflicts
Diffstat (limited to 'debug_toolbar')
24 files changed, 389 insertions, 77 deletions
diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py index e69de29..9603d9e 100644 --- a/debug_toolbar/__init__.py +++ b/debug_toolbar/__init__.py @@ -0,0 +1,2 @@ +VERSION = (0, 0, 1) +__version__ = '.'.join(map(str, VERSION)) diff --git a/debug_toolbar/media/Makefile b/debug_toolbar/media/Makefile index 7f8f2b2..a2d6abb 100644 --- a/debug_toolbar/media/Makefile +++ b/debug_toolbar/media/Makefile @@ -3,6 +3,7 @@ all: compress_js compress_css  compress_js:  	java -jar ~/bin/yuicompressor.jar toolbar.js > toolbar.min.js +	java -jar ~/bin/yuicompressor.jar jquery.cookie.js >> toolbar.min.js  compress_css:  	java -jar ~/bin/yuicompressor.jar --type css toolbar.css > toolbar.min.css diff --git a/debug_toolbar/media/jquery.cookie.js b/debug_toolbar/media/jquery.cookie.js new file mode 100644 index 0000000..6df1fac --- /dev/null +++ b/debug_toolbar/media/jquery.cookie.js @@ -0,0 +1,96 @@ +/** + * Cookie plugin + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + *       used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + *                             If set to null or omitted, the cookie will be a session cookie and will not be retained + *                             when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + *                        require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Get the value of a cookie with the given name. + * + * @example $.cookie('the_cookie'); + * @desc Get the value of a cookie. + * + * @param String name The name of the cookie. + * @return The value of the cookie. + * @type String + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +jQuery.cookie = function(name, value, options) { +    if (typeof value != 'undefined') { // name and value given, set cookie +        options = options || {}; +        if (value === null) { +            value = ''; +            options.expires = -1; +        } +        var expires = ''; +        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { +            var date; +            if (typeof options.expires == 'number') { +                date = new Date(); +                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); +            } else { +                date = options.expires; +            } +            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE +        } +        // CAUTION: Needed to parenthesize options.path and options.domain +        // in the following expressions, otherwise they evaluate to undefined +        // in the packed version for some reason... +        var path = options.path ? '; path=' + (options.path) : ''; +        var domain = options.domain ? '; domain=' + (options.domain) : ''; +        var secure = options.secure ? '; secure' : ''; +        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); +    } else { // only name given, get cookie +        var cookieValue = null; +        if (document.cookie && document.cookie != '') { +            var cookies = document.cookie.split(';'); +            for (var i = 0; i < cookies.length; i++) { +                var cookie = jQuery.trim(cookies[i]); +                // Does this cookie string begin with the name we want? +                if (cookie.substring(0, name.length + 1) == (name + '=')) { +                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); +                    break; +                } +            } +        } +        return cookieValue; +    } +};
\ No newline at end of file diff --git a/debug_toolbar/media/toolbar.css b/debug_toolbar/media/toolbar.css index 2ce4226..c0a6e1a 100644 --- a/debug_toolbar/media/toolbar.css +++ b/debug_toolbar/media/toolbar.css @@ -4,6 +4,7 @@      margin: 0;      padding: 0;      position: static; +    text-align: left;  }  #djDebug a {      color: #f7c757; @@ -23,6 +24,27 @@      right:0;  } +#djDebugToolbarHandle { +    background: #326342; +    height: 30px; +    z-index: 100000000; +    border-bottom: 2px solid #234f32; +    position:absolute; +    top:0; +    left:0; +    right:0; +    width: 16px; +} + +#djDebugToolbarHandle ul li { +    padding: 3px 0px 0px 3px; +} + +#djDebugToolbarHandle ul li a { +    font-size: 16px; +    font-weight: bold; +} +  #djDebugToolbar ul {      margin: 0;      padding: 0; @@ -57,6 +79,22 @@      border-right: 1px solid #487858;  } +#djDebugMore ul { +    float: right; +    background: #2a5738; +    display: none; +} + +#djDebugMore:hover ul { +    display: block; +} + +#djDebugMore li { +    display: inherit; +    position: inherit; +    line-height: inherit; +} +  #djDebugToolbar #djDebugButton {      color: #92ef3f;  } @@ -65,6 +103,9 @@      background-color: #ffffff;  } +#djDebug tr.djDebugOdd pre { +    background-color: #eeeeee; +}  #djDebug .panelContent {      background: #2a5738; @@ -132,7 +173,7 @@      color: #000;      vertical-align: top;  } -#djDebug .panelContent table tr.odd td { +#djDebug .panelContent table tr.djDebugOdd td {    background: #eee;  } diff --git a/debug_toolbar/media/toolbar.js b/debug_toolbar/media/toolbar.js index 455a2fc..9cbda92 100644 --- a/debug_toolbar/media/toolbar.js +++ b/debug_toolbar/media/toolbar.js @@ -1,45 +1,59 @@  var _$ = window.$; -jQuery.noConflict(); -jQuery(function($) { -	$.djDebug = function(data, klass) { -		$.djDebug.init(); +$j = jQuery.noConflict(); +jQuery(function() { +	var COOKIE_NAME = 'dj_debug_panel'; +	$j.djDebug = function(data, klass) { +		$j.djDebug.init();  	} -	$.extend($.djDebug, { +	$j.extend($j.djDebug, {  		init: function() {  			var current = null; -			$('#djDebugPanelList li a').click(function() { -				current = $('#djDebug #' + this.className); +			$j('#djDebugPanelList li a').click(function() { +				current = $j('#djDebug #' + this.className);  				if (current.is(':visible')) { -					$(document).trigger('close.djDebug'); +					$j(document).trigger('close.djDebug');  				} else { -					$('.panelContent').hide(); +					$j('.panelContent').hide();  					current.show(); -					$.djDebug.open(); +					$j.djDebug.open();  				}  				return false;  			}); -			$('#djDebug a.close').click(function() { -				$(document).trigger('close.djDebug'); +			$j('#djDebug a.close').click(function() { +				$j(document).trigger('close.djDebug');  				return false;  			}); -			$('#djDebug a.remoteCall').click(function() { -				$('#djDebugWindow').load(this.href, {}, function() { -					$('#djDebugWindow a.back').click(function() { -						$(this).parent().hide(); +			$j('#djDebug a.remoteCall').click(function() { +				$j('#djDebugWindow').load(this.href, {}, function() { +					$j('#djDebugWindow a.back').click(function() { +						$j(this).parent().hide();  						return false;  					});  				}); -				$('#djDebugWindow').show(); +				$j('#djDebugWindow').show();  				return false;  			}); -			$('#djDebugTemplatePanel a.djTemplateShowContext').click(function() { -				$.djDebug.toggle_content($(this).parent().next()); +			$j('#djDebugTemplatePanel a.djTemplateShowContext').click(function() { +				$j.djDebug.toggle_content($j(this).parent().next()); +			}); +			$j('#djHideToolBarButton').click(function() { +				$j.djDebug.hide_toolbar(true); +			}); +			$j('#djShowToolBarButton').click(function() { +				$j.djDebug.show_toolbar();  			}); +			if ($j.cookie(COOKIE_NAME)) { +				$j.djDebug.hide_toolbar(false); +			} else { +				$j('#djDebugToolbar').show(); +			} +			$j(window).load($j.djDebug.format_panels); +			$j(window).resize($j.djDebug.format_panels);  		},  		open: function() { -			$(document).bind('keydown.djDebug', function(e) { +			$j(document).bind('keydown.djDebug', function(e) {  				if (e.keyCode == 27) { -					$.djDebug.close(); +					$j.djDebug.close();  				}  			});  		}, @@ -51,16 +65,67 @@ jQuery(function($) {  			}  		},  		close: function() { -			$(document).trigger('close.djDebug'); +			$j(document).trigger('close.djDebug');  			return false; +		}, +		hide_toolbar: function(setCookie) { +			$j('#djDebugToolbar').hide("fast"); +			$j('#djDebugToolbarHandle').show(); +			if (setCookie) { +				$j.cookie(COOKIE_NAME, 'hide', { +					path: '/', +					expires: 10 +				}); +			} +		}, +		show_toolbar: function() { +			$j('#djDebugToolbarHandle').hide(); +			$j('#djDebugToolbar').show("fast"); +			$j.cookie(COOKIE_NAME, null, { +				path: '/', +				expires: -1 +			}); +		}, +		/* Make sure that panel layout doesn't overflow the screen +		 * width. Any panel that would otherwise wrap to the next line +		 * are pushed into a "more..." vertical display in the final +		 * panel position. */ +		format_panels: function () { +			// If we've already done some overflow-avoidance, undo the +			// effect before recomputing (needed, for example, after a +			// window resize). +			$j("#djDebugMore > ul > li").appendTo("#djDebugPanelList"); +			$j("#djDebugMore").remove(); + +			// Check for wrapping by examing the position of the last +			// element. +			var row_top = $j("#djDebugPanelList > li").position().top; +			var final_pos = $j("#djDebugPanelList > li:last").position(); + +			if (final_pos.top == row_top && final_pos.left != 0) { +				return; +			} + +			function overflow_check(idx) { +				pos = $j(this).position(); +				return pos.top > row_top || (idx > 1 && pos.left == 0); +			}; + +			var more = $j("<li id='djDebugMore'>More...<ul></ul></li>"); +			more.prependTo("#djDebugPanelList"); +			overflows = $j("#djDebugPanelList > li").filter(overflow_check); +			more.appendTo("#djDebugPanelList"); +			$j("#djDebugMore > ul").append(overflows);  		}  	}); -	$(document).bind('close.djDebug', function() { -		$(document).unbind('keydown.djDebug'); -		$('.panelContent').hide(); +	$j(document).bind('close.djDebug', function() { +		$j(document).unbind('keydown.djDebug'); +		$j('.panelContent').hide();  	});  });  jQuery(function() {  	jQuery.djDebug();  }); +  $ = _$; + diff --git a/debug_toolbar/media/toolbar.min.css b/debug_toolbar/media/toolbar.min.css index bb7c034..be5ea7d 100644 --- a/debug_toolbar/media/toolbar.min.css +++ b/debug_toolbar/media/toolbar.min.css @@ -1 +1 @@ -#djDebug *{color:#000;float:none;margin:0;padding:0;position:static;}#djDebug a{color:#f7c757;}#djDebug a:hover{color:#aaa;}#djDebugToolbar{background:#326342;height:30px;z-index:100000000;border-bottom:2px solid #234f32;position:absolute;top:0;left:0;right:0;}#djDebugToolbar ul{margin:0;padding:0;list-style:none;}#djDebugToolbar li{border-left:1px solid #487858;color:#fff;display:inline;font-size:11px;font-weight:bold;float:none;height:20px;margin:0;padding:0;line-height:30px;padding:8px 9px 9px;position:relative;width:auto;}#djDebugToolbar li:hover{background:#487858;}#djDebugToolbar li:hover a{color:#fff;}#djDebugToolbar li:last-child{border-right:1px solid #487858;}#djDebugToolbar #djDebugButton{color:#92ef3f;}#djDebug pre{background-color:#fff;}#djDebug .panelContent{background:#2a5738;border-bottom:2px solid #234f32;border-top:2px solid #487858;display:none;position:absolute;margin:0;padding:10px;top:32px;width:auto;left:0;right:0;bottom:5px;color:black;z-index:1000000;overflow:auto;}#djDebug .panelContent p a,#djDebug .panelContent dl a{color:#40684c;}#djDebug .panelContent p a:hover,#djDebug .panelContent dl a:hover{color:#92EF3F;}#djDebug .panelContent h3{border-bottom:1px solid #40684c;color:#92ef3f;padding:0 0 5px;}#djDebug .panelContent p{padding:0 5px;}#djDebug .panelContent p,#djDebug .panelContent table,#djDebug .panelContent ol,#djDebug .panelContent ul,#djDebug .panelContent dl{margin:5px 0 15px;background-color:#fff;}#djDebug .panelContent table{width:100%;clear:both;}#djDebug .panelContent table a{color:#40684C;}#djDebug .panelContent table th{background-color:#9dcc49;font-weight:bold;color:#000;font-size:11px;padding:3px 7px 3px;text-align:left;cursor:pointer;border-right:1px solid #b9d977;}#djDebug .panelContent table td{padding:5px 10px;font-size:11px;background:#fff;color:#000;vertical-align:top;}#djDebug .panelContent table tr.odd td{background:#eee;}#djDebug .panelContent .close{float:right;font-weight:bold;}#djDebug .panelContent dt,#djDebug .panelContent dd{display:block;}#djDebug .panelContent dd{margin-left:10px;}#djDebug .highlight{color:#000;}#djDebug .highlight .err{color:#000;}#djDebug .highlight .g{color:#000;}#djDebug .highlight .k{color:#40684C;font-weight:bold;}#djDebug .highlight .o{color:#000;}#djDebug .highlight .n{color:#000;}#djDebug .highlight .mi{color:#40684C;font-weight:bold;}#djDebug .highlight .l{color:#000;}#djDebug .highlight .x{color:#000;}#djDebug .highlight .p{color:#000;}#djDebug .highlight .m{color:#40684C;font-weight:bold;}#djDebug .highlight .s{color:#0086d2;}#djDebug .highlight .w{color:#888;}#djDebug .highlight .il{color:#40684C;font-weight:bold;}#djDebug .highlight .na{color:#7D9029;}#djDebug .highlight .nt{color:#008000;font-weight:bold;}#djDebug .highlight .nv{color:#19177C;}#djDebug .highlight .s2{color:#BA2121;}#djDebug .highlight .cp{color:#BC7A00;}
\ No newline at end of file +#djDebug *{color:#000;float:none;margin:0;padding:0;position:static;text-align:left;}#djDebug a{color:#f7c757;}#djDebug a:hover{color:#aaa;}#djDebugToolbar{background:#326342;height:30px;z-index:100000000;border-bottom:2px solid #234f32;position:absolute;top:0;left:0;right:0;}#djDebugToolbarHandle{background:#326342;height:30px;z-index:100000000;border-bottom:2px solid #234f32;position:absolute;top:0;left:0;right:0;width:16px;}#djDebugToolbarHandle ul li{padding:3px 0 0 3px;}#djDebugToolbarHandle ul li a{font-size:16px;font-weight:bold;}#djDebugToolbar ul{margin:0;padding:0;list-style:none;}#djDebugToolbar li{border-left:1px solid #487858;color:#fff;display:inline;font-size:11px;font-weight:bold;float:none;height:20px;margin:0;padding:0;line-height:30px;padding:8px 9px 9px;position:relative;width:auto;}#djDebugToolbar li:hover{background:#487858;}#djDebugToolbar li:hover a{color:#fff;}#djDebugToolbar li:last-child{border-right:1px solid #487858;}#djDebugMore ul{float:right;background:#2a5738;display:none;}#djDebugMore:hover ul{display:block;}#djDebugMore li{display:inherit;position:inherit;line-height:inherit;}#djDebugToolbar #djDebugButton{color:#92ef3f;}#djDebug pre{background-color:#fff;}#djDebug tr.djDebugOdd pre{background-color:#eee;}#djDebug .panelContent{background:#2a5738;border-bottom:2px solid #234f32;border-top:2px solid #487858;display:none;position:absolute;margin:0;padding:10px;top:32px;width:auto;left:0;right:0;bottom:5px;color:black;z-index:1000000;overflow:auto;}#djDebug .panelContent p a,#djDebug .panelContent dl a{color:#40684c;}#djDebug .panelContent p a:hover,#djDebug .panelContent dl a:hover{color:#92EF3F;}#djDebug .panelContent h3{border-bottom:1px solid #40684c;color:#92ef3f;padding:0 0 5px;}#djDebug .panelContent p{padding:0 5px;}#djDebug .panelContent p,#djDebug .panelContent table,#djDebug .panelContent ol,#djDebug .panelContent ul,#djDebug .panelContent dl{margin:5px 0 15px;background-color:#fff;}#djDebug .panelContent table{width:100%;clear:both;}#djDebug .panelContent table a{color:#40684C;}#djDebug .panelContent table th{background-color:#9dcc49;font-weight:bold;color:#000;font-size:11px;padding:3px 7px 3px;text-align:left;cursor:pointer;border-right:1px solid #b9d977;}#djDebug .panelContent table td{padding:5px 10px;font-size:11px;background:#fff;color:#000;vertical-align:top;}#djDebug .panelContent table tr.djDebugOdd td{background:#eee;}#djDebug .panelContent .close{float:right;font-weight:bold;}#djDebug .panelContent dt,#djDebug .panelContent dd{display:block;}#djDebug .panelContent dd{margin-left:10px;}#djDebug .highlight{color:#000;}#djDebug .highlight .err{color:#000;}#djDebug .highlight .g{color:#000;}#djDebug .highlight .k{color:#40684C;font-weight:bold;}#djDebug .highlight .o{color:#000;}#djDebug .highlight .n{color:#000;}#djDebug .highlight .mi{color:#40684C;font-weight:bold;}#djDebug .highlight .l{color:#000;}#djDebug .highlight .x{color:#000;}#djDebug .highlight .p{color:#000;}#djDebug .highlight .m{color:#40684C;font-weight:bold;}#djDebug .highlight .s{color:#0086d2;}#djDebug .highlight .w{color:#888;}#djDebug .highlight .il{color:#40684C;font-weight:bold;}#djDebug .highlight .na{color:#7D9029;}#djDebug .highlight .nt{color:#008000;font-weight:bold;}#djDebug .highlight .nv{color:#19177C;}#djDebug .highlight .s2{color:#BA2121;}#djDebug .highlight .cp{color:#BC7A00;}
\ No newline at end of file diff --git a/debug_toolbar/media/toolbar.min.js b/debug_toolbar/media/toolbar.min.js index 1f8b8b5..fd41bfb 100644 --- a/debug_toolbar/media/toolbar.min.js +++ b/debug_toolbar/media/toolbar.min.js @@ -1 +1 @@ -var _$=window.$;jQuery.noConflict();jQuery(function(A){A.djDebug=function(C,B){A.djDebug.init()};A.extend(A.djDebug,{init:function(){var B=null;A("#djDebugPanelList li a").click(function(){B=A("#djDebug #"+this.className);if(B.is(":visible")){A(document).trigger("close.djDebug")}else{A(".panelContent").hide();B.show();A.djDebug.open()}return false});A("#djDebug a.close").click(function(){A(document).trigger("close.djDebug");return false});A("#djDebug a.remoteCall").click(function(){A("#djDebugWindow").load(this.href,{},function(){A("#djDebugWindow a.back").click(function(){A(this).parent().hide();return false})});A("#djDebugWindow").show();return false});A("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){A.djDebug.toggle_content(A(this).parent().next())})},open:function(){A(document).bind("keydown.djDebug",function(B){if(B.keyCode==27){A.djDebug.close()}})},toggle_content:function(B){if(B.is(":visible")){B.hide()}else{B.show()}},close:function(){A(document).trigger("close.djDebug");return false}});A(document).bind("close.djDebug",function(){A(document).unbind("keydown.djDebug");A(".panelContent").hide()})});jQuery(function(){jQuery.djDebug()});$=_$;
\ No newline at end of file +var _$=window.$;$j=jQuery.noConflict();jQuery(function(){var a="dj_debug_panel";$j.djDebug=function(c,b){$j.djDebug.init()};$j.extend($j.djDebug,{init:function(){var b=null;$j("#djDebugPanelList li a").click(function(){b=$j("#djDebug #"+this.className);if(b.is(":visible")){$j(document).trigger("close.djDebug")}else{$j(".panelContent").hide();b.show();$j.djDebug.open()}return false});$j("#djDebug a.close").click(function(){$j(document).trigger("close.djDebug");return false});$j("#djDebug a.remoteCall").click(function(){$j("#djDebugWindow").load(this.href,{},function(){$j("#djDebugWindow a.back").click(function(){$j(this).parent().hide();return false})});$j("#djDebugWindow").show();return false});$j("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){$j.djDebug.toggle_content($j(this).parent().next())});$j("#djHideToolBarButton").click(function(){$j.djDebug.hide_toolbar(true)});$j("#djShowToolBarButton").click(function(){$j.djDebug.show_toolbar()});if($j.cookie(a)){$j.djDebug.hide_toolbar(false)}else{$j("#djDebugToolbar").show()}$j(window).load($j.djDebug.format_panels);$j(window).resize($j.djDebug.format_panels)},open:function(){$j(document).bind("keydown.djDebug",function(b){if(b.keyCode==27){$j.djDebug.close()}})},toggle_content:function(b){if(b.is(":visible")){b.hide()}else{b.show()}},close:function(){$j(document).trigger("close.djDebug");return false},hide_toolbar:function(b){$j("#djDebugToolbar").hide("fast");$j("#djDebugToolbarHandle").show();if(b){$j.cookie(a,"hide",{path:"/",expires:10})}},show_toolbar:function(){$j("#djDebugToolbarHandle").hide();$j("#djDebugToolbar").show("fast");$j.cookie(a,null,{path:"/",expires:-1})},format_panels:function(){$j("#djDebugMore > ul > li").appendTo("#djDebugPanelList");$j("#djDebugMore").remove();var b=$j("#djDebugPanelList > li").position().top;var d=$j("#djDebugPanelList > li:last").position();if(d.top==b&&d.left!=0){return}function e(f){pos=$j(this).position();return pos.top>b||(f>1&&pos.left==0)}var c=$j("<li id='djDebugMore'>More...<ul></ul></li>");c.prependTo("#djDebugPanelList");overflows=$j("#djDebugPanelList > li").filter(e);c.appendTo("#djDebugPanelList");$j("#djDebugMore > ul").append(overflows)}});$j(document).bind("close.djDebug",function(){$j(document).unbind("keydown.djDebug");$j(".panelContent").hide()})});jQuery(function(){jQuery.djDebug()});$=_$;jQuery.cookie=function(b,j,m){if(typeof j!="undefined"){m=m||{};if(j===null){j="";m.expires=-1}var e="";if(m.expires&&(typeof m.expires=="number"||m.expires.toUTCString)){var f;if(typeof m.expires=="number"){f=new Date();f.setTime(f.getTime()+(m.expires*24*60*60*1000))}else{f=m.expires}e="; expires="+f.toUTCString()}var l=m.path?"; path="+(m.path):"";var g=m.domain?"; domain="+(m.domain):"";var a=m.secure?"; secure":"";document.cookie=[b,"=",encodeURIComponent(j),e,l,g,a].join("")}else{var d=null;if(document.cookie&&document.cookie!=""){var k=document.cookie.split(";");for(var h=0;h<k.length;h++){var c=jQuery.trim(k[h]);if(c.substring(0,b.length+1)==(b+"=")){d=decodeURIComponent(c.substring(b.length+1));break}}}return d}};
\ No newline at end of file diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 11ff4dd..c3cf5f9 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -8,6 +8,8 @@ from django.utils.encoding import smart_unicode  from django.conf.urls.defaults import include, patterns  import debug_toolbar.urls  from debug_toolbar.toolbar.loader import DebugToolbar +from debug_toolbar.urls import DEBUG_TB_URL_PREFIX +import os  _HTML_TYPES = ('text/html', 'application/xhtml+xml') @@ -37,24 +39,19 @@ class DebugToolbarMiddleware(object):      def show_toolbar(self, request):          if not settings.DEBUG:              return False -        if request.is_ajax(): +        if request.is_ajax() and not request.path.startswith(os.path.join('/', DEBUG_TB_URL_PREFIX)): #Allow ajax requests from the debug toolbar              return False          if not request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:              return False          return True      def process_request(self, request): -        if not hasattr(request, 'user'): -            import warnings -            warnings.warn("You should place the debug_toolbar middleware after \ -                the AuthenticationMiddleware, if you aren't using django's auth\ -                app you can safely ignore this message.") -        if self.override_url: -            debug_toolbar.urls.urlpatterns += self.original_pattern -            self.override_url = False -        request.urlconf = 'debug_toolbar.urls' -          if self.show_toolbar(request): +            if self.override_url: +                debug_toolbar.urls.urlpatterns += self.original_pattern +                self.override_url = False +            request.urlconf = 'debug_toolbar.urls' +              self.debug_toolbar = DebugToolbar(request)              for panel in self.debug_toolbar.panels:                  panel.process_request(request) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 7584bd7..b041bf8 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -19,7 +19,7 @@ class DatabaseStatTracker(util.CursorDebugWrapper):              return self.cursor.execute(sql, params)          finally:              stop = time.time() -            _params = None +            _params = ''              try:                  _params = simplejson.dumps([force_unicode(x) for x in params])              except TypeError: @@ -27,7 +27,7 @@ class DatabaseStatTracker(util.CursorDebugWrapper):              # We keep `sql` to maintain backwards compatibility              self.db.queries.append({                  'sql': self.db.ops.last_executed_query(self.cursor, sql, params), -                'time': stop - start, +                'time': (stop - start) * 1000, # convert to ms                  'raw_sql': sql,                  'params': _params,                  'hash': sha_constructor(settings.SECRET_KEY + sql + _params).hexdigest(), @@ -47,10 +47,11 @@ class SQLDebugPanel(DebugPanel):          self._sql_time = 0      def title(self): -        self._sql_time = sum(map(lambda q: float(q['time']) * 1000, connection.queries)) +        self._sql_time = sum(map(lambda q: float(q['time']), connection.queries)) +        num_queries = len(connection.queries) - self._offset          return '%d SQL %s (%.2fms)' % ( -            len(connection.queries),  -            (len(connection.queries) == 1) and 'query' or 'queries', +            num_queries, +            (num_queries == 1) and 'query' or 'queries',              self._sql_time          ) @@ -65,6 +66,7 @@ class SQLDebugPanel(DebugPanel):          context = {              'queries': sql_queries,              'sql_time': self._sql_time, +            'is_mysql': settings.DATABASE_ENGINE == 'mysql',          }          return render_to_string('debug_toolbar/panels/sql.html', context) @@ -73,9 +75,11 @@ def reformat_sql(sql):      sql = sql.replace('SELECT ', 'SELECT\n\t')      sql = sql.replace(' FROM ', '\nFROM\n\t')      sql = sql.replace(' WHERE ', '\nWHERE\n\t') -    sql = sql.replace(' INNER JOIN ', '\nINNER JOIN\n\t') -    sql = sql.replace(' OUTER JOIN ', '\nOUTER JOIN\n\t') +    sql = sql.replace(' INNER JOIN', '\n\tINNER JOIN') +    sql = sql.replace(' LEFT OUTER JOIN' , '\n\tLEFT OUTER JOIN')      sql = sql.replace(' ORDER BY ', '\nORDER BY\n\t') +    sql = sql.replace(' HAVING ', '\nHAVING\n\t') +    sql = sql.replace(' GROUP BY ', '\nGROUP BY\n\t')      # Use Pygments to highlight SQL if it's available      try:          from pygments import highlight diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py index fa85cb8..7dc7b06 100644 --- a/debug_toolbar/panels/template.py +++ b/debug_toolbar/panels/template.py @@ -48,11 +48,15 @@ class TemplateDebugPanel(DebugPanel):          return ''      def process_request(self, request): -        self.context_processors = dict( -            [("%s.%s" % (k.__module__, k.__name__), pformat(k(request))) for k in get_standard_processors()] -        ) +        self.request = request      def content(self): +        context_processors = dict( +            [ +                ("%s.%s" % (k.__module__, k.__name__), +                    pformat(k(self.request))) for k in get_standard_processors() +            ] +        )          template_context = []          for i, d in enumerate(self.templates):              info = {} @@ -73,6 +77,6 @@ class TemplateDebugPanel(DebugPanel):          context = {              'templates': template_context,              'template_dirs': [normpath(x) for x in settings.TEMPLATE_DIRS], -            'context_processors': self.context_processors, +            'context_processors': context_processors,          }          return render_to_string('debug_toolbar/panels/templates.html', context) diff --git a/debug_toolbar/panels/timer.py b/debug_toolbar/panels/timer.py index ea8ed4a..352bf55 100644 --- a/debug_toolbar/panels/timer.py +++ b/debug_toolbar/panels/timer.py @@ -1,23 +1,82 @@ +try: +    import resource +except ImportError: +    pass # Will fail on Win32 systems  import time +from django.template.loader import render_to_string  from debug_toolbar.panels import DebugPanel +  class TimerDebugPanel(DebugPanel):      """      Panel that displays the time a response took in milliseconds.      """      name = 'Timer' +    try: # if resource module not available, don't show content panel +        resource +    except NameError: +        has_content = False +        has_resource = False +    else: +        has_content = True +        has_resource = True      def process_request(self, request):          self._start_time = time.time() +        if self.has_resource: +            self._start_rusage = resource.getrusage(resource.RUSAGE_SELF)      def process_response(self, request, response):          self.total_time = (time.time() - self._start_time) * 1000 +        if self.has_resource: +            self._end_rusage = resource.getrusage(resource.RUSAGE_SELF)      def title(self): -        return 'Time: %0.2fms' % (self.total_time) +        if self.has_resource: +            utime = self._end_rusage.ru_utime - self._start_rusage.ru_utime +            stime = self._end_rusage.ru_stime - self._start_rusage.ru_stime +            return 'Time: %0.2fms, %0.2fms CPU' % (self.total_time, (utime + stime) * 1000.0) +        else: +            return 'Time: %0.2fms' % (self.total_time)      def url(self):          return '' +    def _elapsed_ru(self, name): +        return getattr(self._end_rusage, name) - getattr(self._start_rusage, name) +      def content(self): -        return '' + +        utime = 1000 * self._elapsed_ru('ru_utime') +        stime = 1000 * self._elapsed_ru('ru_stime') +        vcsw = self._elapsed_ru('ru_nvcsw') +        ivcsw = self._elapsed_ru('ru_nivcsw') +        minflt = self._elapsed_ru('ru_minflt') +        majflt = self._elapsed_ru('ru_majflt') + +# these are documented as not meaningful under Linux.  If you're running BSD +# feel free to enable them, and add any others that I hadn't gotten to before +# I noticed that I was getting nothing but zeroes and that the docs agreed. :-( +# +#        blkin = self._elapsed_ru('ru_inblock') +#        blkout = self._elapsed_ru('ru_oublock') +#        swap = self._elapsed_ru('ru_nswap') +#        rss = self._end_rusage.ru_maxrss +#        srss = self._end_rusage.ru_ixrss +#        urss = self._end_rusage.ru_idrss +#        usrss = self._end_rusage.ru_isrss + +        rows = ( +            ('User CPU time', '%0.3f msec' % utime), +            ('System CPU time', '%0.3f msec' % stime), +            ('Total CPU time', '%0.3f msec' % (utime + stime)), +            ('Elapsed time', '%0.3f msec' % self.total_time), +            ('Context switches', '%d voluntary, %d involuntary' % (vcsw, ivcsw)), +#            ('Memory use', '%d max RSS, %d shared, %d unshared' % (rss, srss, urss + usrss)), +#            ('Page faults', '%d no i/o, %d requiring i/o' % (minflt, majflt)), +#            ('Disk operations', '%d in, %d out, %d swapout' % (blkin, blkout, swap)), +        ) +        context = { +            'rows': rows, +        } +        return render_to_string('debug_toolbar/panels/timer.html', context) diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html index a6c8aef..7277620 100644 --- a/debug_toolbar/templates/debug_toolbar/base.html +++ b/debug_toolbar/templates/debug_toolbar/base.html @@ -9,9 +9,13 @@  	@import url({{ BASE_URL }}/__debug__/m/toolbar.min.css);  </style>  <div id="djDebug"> -	<div id="djDebugToolbar"> +	<div style="display: none;" id="djDebugToolbar">  		<ul id="djDebugPanelList"> +			{% if panels %} +			<li><a id="djHideToolBarButton" href="#" title="Hide Toolbar">« Hide</a></li> +			{% else %}  			<li id="djDebugButton">DEBUG</li> +			{% endif %}  			{% for panel in panels %}  				<li>  					{% if panel.has_content %} @@ -23,6 +27,11 @@  			{% endfor %}  		</ul>  	</div> +	<div style="display: none;" id="djDebugToolbarHandle"> +		<ul id="djDebugPanelList"> +			<li><a title="Show Toolbar" id="djShowToolBarButton" href="#">»</a></li> +		</ul> +	</div>  	{% for panel in panels %}  		{% if panel.has_content %}  			<div id="{{ panel.dom_id }}" class="panelContent"> diff --git a/debug_toolbar/templates/debug_toolbar/panels/headers.html b/debug_toolbar/templates/debug_toolbar/panels/headers.html index 3ac7fd3..005ffe6 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/headers.html +++ b/debug_toolbar/templates/debug_toolbar/panels/headers.html @@ -8,7 +8,7 @@  	</thead>  	<tbody>  		{% for key, value in headers.iteritems %} -			<tr class="{% cycle 'odd' 'even' %}"> +			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  				<td>{{ key|escape }}</td>  				<td>{{ value|escape }}</td>  			</tr> diff --git a/debug_toolbar/templates/debug_toolbar/panels/logger.html b/debug_toolbar/templates/debug_toolbar/panels/logger.html index c079065..2117529 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/logger.html +++ b/debug_toolbar/templates/debug_toolbar/panels/logger.html @@ -11,7 +11,7 @@  		</thead>  		<tbody>  			{% for record in records %} -				<tr class="{% cycle 'odd' 'even' %}"> +				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  					<td>{{ record.level }}</td>  					<td>{{ record.time|date:"h:i:s m/d/Y" }}</td>  					<td>{{ record.message }}</td> diff --git a/debug_toolbar/templates/debug_toolbar/panels/request_vars.html b/debug_toolbar/templates/debug_toolbar/panels/request_vars.html index fe317ae..b7d53ee 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/request_vars.html +++ b/debug_toolbar/templates/debug_toolbar/panels/request_vars.html @@ -13,7 +13,7 @@  		</thead>  		<tbody>  			{% for key, value in cookies %} -				<tr class="{% cycle 'odd' 'even' %}"> +				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  					<td>{{ key|escape }}</td>  					<td>{{ value|escape }}</td>  				</tr> @@ -38,7 +38,7 @@  		</thead>  		<tbody>  			{% for key, value in session %} -				<tr class="{% cycle 'odd' 'even' %}"> +				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  					<td>{{ key|escape }}</td>  					<td>{{ value|escape }}</td>  				</tr> @@ -59,7 +59,7 @@  		</thead>  		<tbody>  			{% for key, value in get %} -				<tr class="{% cycle 'odd' 'even' %}"> +				<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  					<td>{{ key|escape }}</td>  					<td>{{ value|join:", "|escape }}</td>  				</tr> diff --git a/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html b/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html index 1d332bf..93f0b34 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html +++ b/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html @@ -8,7 +8,7 @@  	</thead>  	<tbody>  		{% for var in settings.items|dictsort:"0" %} -			<tr class="{% cycle 'odd' 'even' %}"> +			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  				<td>{{ var.0 }}</td>  				<td><code>{{ var.1|pprint }}</code></td>  			</tr> diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql.html b/debug_toolbar/templates/debug_toolbar/panels/sql.html index d35d83e..0c8e9c3 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql.html @@ -9,13 +9,15 @@  	</thead>  	<tbody>  		{% for query in queries %} -			<tr class="{% cycle 'odd' 'even' %}"> -				<td>{{ query.time|floatformat:"4" }}</td> +			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> +				<td>{{ query.time|floatformat:"2" }}</td>  				<td>  				{% if query.params %} -					<a class="remoteCall" href="/__debug__/sql_select/?sql={{ query.raw_sql|urlencode }}¶ms={{ query.params|urlencode }}&time={{ query.time|floatformat:"4"|urlencode }}&hash={{ query.hash }}">SELECT</a> -					<a class="remoteCall" href="/__debug__/sql_explain/?sql={{ query.raw_sql|urlencode }}¶ms={{ query.params|urlencode }}&time={{ query.time|floatformat:"4"|urlencode }}&hash={{ query.hash }}">EXPLAIN</a> -					<a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}¶ms={{ query.params|urlencode }}&time={{ query.time|floatformat:"4"|urlencode }}&hash={{ query.hash }}">PROFILE</a> +					<a class="remoteCall" href="/__debug__/sql_select/?sql={{ query.raw_sql|urlencode }}¶ms={{ query.params|urlencode }}&time={{ query.time|floatformat:"2"|urlencode }}&hash={{ query.hash }}">SELECT</a> +					<a class="remoteCall" href="/__debug__/sql_explain/?sql={{ query.raw_sql|urlencode }}¶ms={{ query.params|urlencode }}&time={{ query.time|floatformat:"2"|urlencode }}&hash={{ query.hash }}">EXPLAIN</a> +					{% if is_mysql %} +						<a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}¶ms={{ query.params|urlencode }}&time={{ query.time|floatformat:"2"|urlencode }}&hash={{ query.hash }}">PROFILE</a> +					{% endif %}  				{% endif %}  				</td>  				<td class="syntax">{{ query.sql|safe }}</td> diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html b/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html index dc306f0..a163b25 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html @@ -16,7 +16,7 @@  	</thead>  	<tbody>  		{% for row in result %} -			<tr class="{% cycle 'odd' 'even' %}"> +			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  				{% for column in row %}  					<td>{{ column|escape }}</td>  				{% endfor %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html b/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html index c1d04ce..e46f41d 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html @@ -16,7 +16,7 @@  	</thead>  	<tbody>  		{% for row in result %} -			<tr class="{% cycle 'odd' 'even' %}"> +			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  				{% for column in row %}  					<td>{{ column|escape }}</td>  				{% endfor %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_select.html b/debug_toolbar/templates/debug_toolbar/panels/sql_select.html index 73109ef..17e7d48 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_select.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_select.html @@ -17,7 +17,7 @@  	</thead>  	<tbody>  		{% for row in result %} -			<tr class="{% cycle 'odd' 'even' %}"> +			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">  				{% for column in row %}  					<td>{{ column|escape }}</td>  				{% endfor %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/timer.html b/debug_toolbar/templates/debug_toolbar/panels/timer.html new file mode 100644 index 0000000..831ff86 --- /dev/null +++ b/debug_toolbar/templates/debug_toolbar/panels/timer.html @@ -0,0 +1,21 @@ +<h3>Resource Usage</h3> +<table> +	<colgroup> +		<col style="width:20%"/> +		<col/> +	</colgroup> +	<thead> +		<tr> +			<th>Key</th> +			<th>Value</th> +		</tr> +	</thead> +	<tbody> +		{% for key, value in rows %} +			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> +				<td>{{ key|escape }}</td> +				<td>{{ value|escape }}</td> +			</tr> +		{% endfor %} +	</tbody> +</table> diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index b217665..2422f3d 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -18,8 +18,8 @@ class DebugToolbar(object):              'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',              'debug_toolbar.panels.headers.HeaderDebugPanel',              'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', -            'debug_toolbar.panels.template.TemplateDebugPanel',              'debug_toolbar.panels.sql.SQLDebugPanel', +            'debug_toolbar.panels.template.TemplateDebugPanel',              'debug_toolbar.panels.cache.CacheDebugPanel',              'debug_toolbar.panels.logger.LoggingPanel',          ) diff --git a/debug_toolbar/urls.py b/debug_toolbar/urls.py index 77d1a80..05233e2 100644 --- a/debug_toolbar/urls.py +++ b/debug_toolbar/urls.py @@ -7,10 +7,12 @@ this into the urlconf for the request.  from django.conf.urls.defaults import *  from django.conf import settings +DEBUG_TB_URL_PREFIX = '__debug__' +  urlpatterns = patterns('', -    url(r'^__debug__/m/(.*)$', 'debug_toolbar.views.debug_media'), -    url(r'^__debug__/sql_select/$', 'debug_toolbar.views.sql_select', name='sql_select'), -    url(r'^__debug__/sql_explain/$', 'debug_toolbar.views.sql_explain', name='sql_explain'), -    url(r'^__debug__/sql_profile/$', 'debug_toolbar.views.sql_profile', name='sql_profile'), -    url(r'^__debug__/template_source/$', 'debug_toolbar.views.template_source', name='template_source'), +    url(r'^%s/m/(.*)$' % DEBUG_TB_URL_PREFIX, 'debug_toolbar.views.debug_media'), +    url(r'^%s/sql_select/$' % DEBUG_TB_URL_PREFIX, 'debug_toolbar.views.sql_select', name='sql_select'), +    url(r'^%s/sql_explain/$' % DEBUG_TB_URL_PREFIX, 'debug_toolbar.views.sql_explain', name='sql_explain'), +    url(r'^%s/sql_profile/$' % DEBUG_TB_URL_PREFIX, 'debug_toolbar.views.sql_profile', name='sql_profile'), +    url(r'^%s/template_source/$' % DEBUG_TB_URL_PREFIX, 'debug_toolbar.views.template_source', name='template_source'),  ) diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index e3bb5b1..0fb4168 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -8,11 +8,17 @@ import os  import django.views.static  from django.conf import settings  from django.db import connection -from django.http import HttpResponse, HttpResponseBadRequest +from django.http import HttpResponseBadRequest  from django.shortcuts import render_to_response  from django.utils import simplejson  from django.utils.hashcompat import sha_constructor +class InvalidSQLError(Exception): +    def __init__(self, value): +        self.value = value +    def __str__(self): +        return repr(self.value) +      def debug_media(request, path):      root = getattr(settings, 'DEBUG_TOOLBAR_MEDIA_ROOT', None)      if root is None: @@ -36,7 +42,7 @@ def sql_select(request):      hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest()      if hash != request.GET.get('hash', ''):          return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert -    if sql.lower().startswith('select'): +    if sql.lower().strip().startswith('select'):          params = simplejson.loads(params)          cursor = connection.cursor()          cursor.execute(sql, params) @@ -50,6 +56,7 @@ def sql_select(request):              'headers': headers,          }          return render_to_response('debug_toolbar/panels/sql_select.html', context) +    raise InvalidSQLError("Only 'select' queries are allowed.")  def sql_explain(request):      """ @@ -67,7 +74,7 @@ def sql_explain(request):      hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest()      if hash != request.GET.get('hash', ''):          return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert -    if sql.lower().startswith('select'): +    if sql.lower().strip().startswith('select'):          params = simplejson.loads(params)          cursor = connection.cursor()          cursor.execute("EXPLAIN %s" % (sql,), params) @@ -81,6 +88,7 @@ def sql_explain(request):              'headers': headers,          }          return render_to_response('debug_toolbar/panels/sql_explain.html', context) +    raise InvalidSQLError("Only 'select' queries are allowed.")  def sql_profile(request):      """ @@ -98,7 +106,7 @@ def sql_profile(request):      hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest()      if hash != request.GET.get('hash', ''):          return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert -    if sql.lower().startswith('select'): +    if sql.lower().strip().startswith('select'):          params = simplejson.loads(params)          cursor = connection.cursor()          cursor.execute("SET PROFILING=1") # Enable profiling @@ -116,6 +124,7 @@ def sql_profile(request):              'headers': headers,          }          return render_to_response('debug_toolbar/panels/sql_explain.html', context) +    raise InvalidSQLError("Only 'select' queries are allowed.")  def template_source(request):      """  | 
