Current section

Files

Jump to
plug lib plug templates debugger.eex
Raw

lib/plug/templates/debugger.eex

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><%= h @title %> at <%= h method(@conn) %> <%= h @conn.request_path %></title>
<meta name="viewport" content="width=device-width">
<style>/*! normalize.css v4.2.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}template,[hidden]{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,optgroup,select,textarea{font:inherit;margin:0}optgroup{font-weight:bold}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:0.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}</style>
<style>
html, body, td, input {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}
* {
box-sizing: border-box;
}
html {
font-size: 15px;
line-height: 1.6;
background: #fff;
color: <%= @style.text_color %>;
}
@media (max-width: 768px) {
html {
font-size: 14px;
}
}
@media (max-width: 480px) {
html {
font-size: 13px;
}
}
button:focus,
summary:focus {
outline: 0;
}
summary {
cursor: pointer;
}
pre {
font-family: <%= @style.monospace_font %>;
overflow: auto;
max-width: 100%;
}
.top-details {
padding: 48px;
background: #f9f9fa;
}
.top-details,
.conn-info {
padding: 48px;
}
@media (max-width: 768px) {
.top-details,
.conn-info {
padding: 32px;
}
}
@media (max-width: 480px) {
.top-details,
.conn-info {
padding: 16px;
}
}
/*
* Exception logo
*/
<%= if @style.logo do %>
.exception-logo {
position: absolute;
right: 48px;
top: 48px;
pointer-events: none;
width: 100%;
}
.exception-logo:before {
content: '';
display: block;
height: 64px;
width: 100%;
background-size: auto 100%;
background-image: url("<%= @style.logo %>");
background-position: right 0;
background-repeat: no-repeat;
margin-bottom: 16px;
}
@media (max-width: 768px) {
.exception-logo {
position: static;
}
.exception-logo:before {
height: 32px;
background-position: left 0;
}
}
@media (max-width: 480px) {
.exception-logo {
display: none;
}
}
<% end %>
/*
* Exception info
*/
/* Compensate for logo placement */
@media (min-width: 769px) {
.exception-info {
max-width: 90%;
}
}
.exception-info > .struct,
.exception-info > .title,
.exception-info > .detail {
margin: 0;
padding: 0;
}
.exception-info > .struct {
font-size: 1em;
font-weight: 700;
color: <%= @style.primary %>;
}
.exception-info > .struct > small {
font-size: 1em;
color: <%= @style.accent %>;
font-weight: 400;
}
/* Hide path until hover */
.exception-info > .struct > .path {
opacity: 0;
-webkit-transition: opacity 100ms linear;
transition: opacity 100ms linear;
}
.exception-info:hover > .struct > .path {
opacity: 1;
}
.exception-info > .title {
font-size: <%= :math.pow(1.2, 4) %>em;
line-height: 1.4;
font-weight: 300;
color: <%= @style.primary %>;
}
@media (max-width: 768px) {
.exception-info > .title {
font-size: <%= :math.pow(1.15, 4) %>em;
}
}
@media (max-width: 480px) {
.exception-info > .title {
font-size: <%= :math.pow(1.1, 4) %>em;
}
}
.exception-info > .detail {
margin-top: 1.3em;
white-space: pre;
}
/*
* Code explorer
*/
.code-explorer {
margin: 32px 0 0 0;
}
@media (max-width: 768px) {
.code-explorer {
margin-top: 16px;
}
}
.code-explorer:after {
content: '';
display: table;
clear: both;
zoom: 1;
}
.code-explorer > .code-snippets {
float: left;
width: 45%;
}
.code-explorer > .stack-trace {
float: right;
width: 55%;
padding-left: 32px;
}
/* Collapse to single-column */
@media (max-width: 960px) {
.code-explorer > .code-snippets {
float: none;
width: auto;
margin-bottom: 16px;
}
.code-explorer > .stack-trace {
float: none;
width: auto;
padding-left: 0;
}
}
/*
* Snippets
*/
.code-snippets {
}
/*
* Frame info:
* Holds the code (code-block) and more
*/
.frame-info {
background: white;
box-shadow:
0 1px 3px rgba(80, 100, 140, .1),
0 8px 15px rgba(80, 100, 140, .05);
}
.frame-info > .meta,
.frame-info > .file {
padding: 12px 16px;
white-space: no-wrap;
font-size: <%= :math.pow(1.2, -1) %>em;
}
@media (max-width: 480px) {
.frame-info > .meta,
.frame-info > .file {
padding: 6px 16px;
font-size: <%= :math.pow(1.1, -1) %>em;
}
}
.frame-info > .file > a {
text-decoration: none;
color: <%= @style.text_color %>;
font-weight: 700;
}
.frame-info > .code {
border-top: solid 1px <%= @style.line_color %>;
border-bottom: solid 1px <%= @style.line_color %>;
}
/* Hiding */
.frame-info {
display: none;
}
.frame-info.-active {
display: block;
}
.frame-info > details.meta {
padding: 0;
}
.frame-info > details.meta > summary {
padding: 12px 16px;
}
/*
* Frame details
*/
.frame-summary.-short {
color: <%= @style.accent %>;
}
.frame-summary > .app {
color: <%= @style.primary %>;
font-weight: 700;
}
.frame-summary > .app:after {
content: 'ยท';
margin: 0 .2em;
}
/*
* Code block:
* The `pre` that holds the code
*/
.code-block {
margin: 0;
padding: 12px 0;
font-size: .8em;
line-height: 1.4;
white-space: normal;
}
.code-block > .line {
white-space: pre;
display: block;
padding: 0 16px;
}
/* Line highlight */
.code-block > .line.-highlight {
background-color: <%= @style.highlight %>;
-webkit-animation: line-highlight 750ms linear;
animation: line-highlight 750ms linear;
}
@-webkit-keyframes line-highlight {
0% { background-color: <%= @style.highlight %>; }
25% { background-color: <%= @style.red_highlight %>; }
50% { background-color: <%= @style.highlight %>; }
75% { background-color: <%= @style.red_highlight %>; }
}
@keyframes line-highlight {
0% { background-color: <%= @style.highlight %>; }
25% { background-color: <%= @style.red_highlight %>; }
50% { background-color: <%= @style.highlight %>; }
75% { background-color: <%= @style.red_highlight %>; }
}
.code-block > .line > .ln {
color: <%= @style.accent %>;
margin-right: 1.5em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.code-block > .line > .code {
font-family: <%= @style.monospace_font %>;
}
/*
* Empty code
*/
.code-block-empty {
text-align: center;
color: <%= @style.accent %>;
padding-top: 48px;
padding-bottom: 48px;
}
/*
* Stack trace heading
*/
.stack-trace-heading {
padding-top: 8px;
}
.stack-trace-heading:after {
content: '';
display: block;
clear: both;
zoom: 1;
border-bottom: solid 1px <%= @style.line_color %>;
padding-top: 12px;
margin-bottom: 16px;
}
.stack-trace-heading > h3 {
display: none;
}
.stack-trace-heading > label {
display: block;
padding-left: 8px;
line-height: 1.9;
font-size: <%= :math.pow(1.2, -1) %>em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.stack-trace-heading > label > input {
margin-right: .3em;
}
@media (max-width: 480px) {
.stack-trace-heading > label {
font-size: <%= :math.pow(1.1, -1) %>em;
}
}
/*
* Stack trace
*/
.stack-trace-list,
.stack-trace-list > li {
margin: 0;
padding: 0;
list-style-type: none;
}
.stack-trace-list > li > .stack-trace-item.-all {
display: none;
}
.stack-trace-list.-show-all > li > .stack-trace-item.-all {
display: block;
}
/*
* Stack trace item:
* The clickable line to inspect a stack trace
*/
.stack-trace-item {
font-size: <%= :math.pow(1.2, -1) %>em;
display: block;
width: 100%;
border: 0;
margin: 0;
padding: 4px 8px;
background: transparent;
cursor: pointer;
text-align: left;
overflow: hidden;
white-space: nowrap;
}
.stack-trace-item:hover,
.stack-trace-item:focus {
background-color: rgba(80, 100, 140, 0.05);
}
.stack-trace-item,
.stack-trace-item:active {
color: <%= @style.text_color %>;
}
.stack-trace-item:active {
background-color: rgba(80, 100, 140, 0.1);
}
.stack-trace-item.-active {
background-color: white;
}
/* Circle */
.stack-trace-item > .left:before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
background: <%= @style.accent %>;
border-radius: 50%;
margin-right: 8px;
}
.stack-trace-item.-app > .left:before {
background: <%= @style.primary %>;
opacity: 1;
}
.stack-trace-item.-app > .left > .app {
display: none;
}
.stack-trace-item > .left {
float: left;
max-width: 55%;
}
.stack-trace-item > .info {
color: <%= @style.gray %>;
float: right;
max-width: 45%;
}
.stack-trace-item > .left,
.stack-trace-item > .info {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.stack-trace-item > .left > .filename > .line {
color: <%= @style.accent %>;
}
/* App name */
.stack-trace-item > .left > .app {
color: <%= @style.accent %>;
}
.stack-trace-item > .left > .app:after {
content: 'ยท';
margin: 0 .2em;
}
/*
* Code as a blockquote:
* Like `pre` but with wrapping
*/
.code-quote {
font-family: <%= @style.monospace_font %>;
font-size: <%= :math.pow(1.2, -1) %>em;
margin: 0;
overflow: auto;
max-width: 100%;
word-wrap: break-word;
white-space: normal;
}
.code-quote.-padded {
padding: 0 16px 16px 16px;
}
/*
* Conn info
*/
.conn-info {
border-top: solid 1px <%= @style.line_color %>;
}
/*
* Conn details
*/
.conn-details {
}
.conn-details + .conn-details {
margin-top: 16px;
}
.conn-details > summary {
}
.conn-details > dl {
display: block;
overflow: hidden;
margin: 0;
padding: 4px 0;
border-bottom: solid 1px <%= @style.line_color %>;
white-space: nowrap;
text-overflow: ellipsis;
}
.conn-details > dl:first-of-type {
margin-top: 16px;
border-top: solid 1px <%= @style.line_color %>;
}
/* Term */
.conn-details > dl > dt {
width: 20%;
float: left;
font-size: <%= :math.pow(1.2, -1) %>em;
color: <%= @style.gray %>;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
top: -1px; /* Compensate for font metrics */
}
/* Definition */
.conn-details > dl > dd {
width: 80%;
float: left;
}
@media (max-width: 480px) {
.conn-details > dl > dt {
font-size: <%= :math.pow(1.1, -1) %>em;
}
}
</style>
</head>
<body>
<div class="top-details">
<%= if @style.logo do %>
<aside class="exception-logo"></aside>
<% end %>
<header class="exception-info">
<% [headline | details] = String.split(@message, "\n\n") %>
<h5 class="struct">
<%= h @title %>
<small>at <%= h method(@conn) %></small>
<small class="path"><%= h @conn.request_path %></small>
</h5>
<h1 class="title"><%= h headline %></h1>
<%= for detail <- details do %>
<p class="detail"><%= h detail %></p>
<% end %>
</header>
<div class="code-explorer">
<div class="code-snippets">
<%= for frame <- @frames do %>
<div class="frame-info" data-index="<%= frame.index %>" role="stack-trace-details">
<div class="file">
<a href="<%= frame.link %>"><%= h frame.file %></a>
</div>
<%= if (snippet = frame.snippet) && snippet != [] do %>
<pre class="code code-block"><%= for {index, line, highlight} <- snippet do %><span class="line <%= if highlight, do: "-highlight" %>"><span class="ln"><%= h index %></span><span class="code"><%= h String.rstrip(line) %></span></span>
<% end %></pre>
<% else %>
<div class="code code-block-empty">No code available.</div>
<% end %>
<%= if frame.args != [] do %>
<details class="meta">
<summary class="frame-summary">
<%= if app = frame.app do %>
<span class="app"><%= h app %></span>
<% end %>
<%= h frame.info %>
</summary>
<blockquote class="code-quote -padded"><%= h inspect(frame.args) %></blockquote>
</details>
<% else %>
<div class="meta">
<div class="frame-summary -short">
<%= if app = frame.app do %>
<span class="app"><%= h app %></span>
<% end %>
<%= h frame.info %>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
<div class="stack-trace">
<div class="stack-trace-heading">
<label><input type="checkbox" role="show-all-toggle"> Show all frames</label>
</div>
<ul class="stack-trace-list" role="stack-trace-list">
<%= for frame <- @frames do %>
<li>
<button class="stack-trace-item -<%= frame.context %>" role="stack-trace-item" data-index="<%= frame.index %>">
<span class="left">
<%= if app = frame.app do %>
<span class="app"><%= h app %></span>
<% end %>
<span class="filename">
<%= h frame.file %><%= if frame.line do %><span class="line">:<%= h frame.line %></span><% end %>
</span>
</span>
<span class="info"><%= h frame.info %></span>
</button>
</li>
<% end %>
</ul>
</div>
</div>
</div>
<div class="conn-info">
<%= if @params && @params != %{} do %>
<details class="conn-details" open>
<summary>Params</summary>
<%= for {key, value} <- @params do %>
<dl>
<dt><%= h key %></dt>
<dd><pre><%= h inspect(value) %></pre></dd>
</dl>
<% end %>
</details>
<% end %>
<details class="conn-details">
<summary>Request info</summary>
<dl>
<dt>URI:</dt>
<dd class="code-quote"><%= h url(@conn) %></dd>
</dl>
<dl>
<dt>Query string:</dt>
<dd class="code-quote"><%= h @conn.query_string %></dd>
</dl>
<dl>
<dt>Peer:</dt>
<dd class="code-quote"><%= h peer(@conn) %></dd>
</dl>
</details>
<details class="conn-details">
<summary>Headers</summary>
<%= for {key, value} <- Enum.sort(@conn.req_headers) do %>
<dl>
<dt><%= h key %></dt>
<dd class="code-quote"><%= h value %></dd>
</dl>
<% end %>
</details>
<%= if @session && @session != %{} do %>
<details class="conn-details">
<summary>Session</summary>
<%= for {key, value} <- @session do %>
<dl>
<dt><%= h key %></dt>
<dd><pre><%= h inspect(value) %></pre></dd>
</dl>
<% end %>
</details>
<% end %>
</div>
<script>(function () {
var $items = document.querySelectorAll('[role~="stack-trace-item"]')
var $toggle = document.querySelector('[role~="show-all-toggle"]')
var $list = document.querySelector('[role~="stack-trace-list"]')
each($items, function ($item) {
on($item, 'click', itemOnclick)
})
on($toggle, 'click', toggleOnclick)
// Auto-check "show all" if there are no app frames.
if (document.querySelectorAll('[role~="stack-trace-list"] .-app').length === 0) {
$toggle.checked = true
toggleOnclick.call($toggle)
}
function toggleOnclick () {
if (this.checked) {
addClass($list, '-show-all')
} else {
removeClass($list, '-show-all')
}
}
function itemOnclick () {
var idx = this.getAttribute('data-index')
console.log(idx)
var $detail = document.querySelector('[role~="stack-trace-details"].-active')
if ($detail) removeClass($detail, '-active')
$detail = document.querySelector('[role~="stack-trace-details"][data-index="' + idx + '"]')
if ($detail) addClass($detail, '-active')
var $item = document.querySelector('[role~="stack-trace-item"].-active')
if ($item) removeClass($item, '-active')
$item = document.querySelector('[role~="stack-trace-item"][data-index="' + idx + '"]')
if ($item) addClass($item, '-active')
}
var $first =
document.querySelector('[role~="stack-trace-item"].-app:first-of-type') ||
document.querySelector('[role~="stack-trace-item"]:first-of-type')
if ($first) itemOnclick.call($first)
/*
* Helpers
*/
function each (list, fn) {
for (var i = 0, len = list.length; i < len; i++) {
var item = list[i]
fn(item)
}
}
function addClass (el, className) {
if (el.classList) {
el.classList.add(className)
} else {
el.className += ' ' + className
}
}
function removeClass (el, className) {
if (el.classList) {
el.classList.remove(className)
} else {
var expr = new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi')
el.className = el.className.replace(expr, ' ')
}
}
function on (el, event, handler) {
if (el.addEventListener) {
el.addEventListener(event, handler)
} else {
el.attachEvent('on' + event, function () {
handler.call(el)
})
}
}
}())</script>
</body>
</html>