better_errors 2.6.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/release-drafter.yml +46 -0
  3. data/.github/workflows/ci.yml +123 -0
  4. data/.github/workflows/draft_release_update.yml +22 -0
  5. data/.github/workflows/pull_request.yml +20 -0
  6. data/.github/workflows/release.yml +68 -0
  7. data/.gitignore +4 -0
  8. data/.ruby-version +1 -0
  9. data/Gemfile +6 -1
  10. data/{LICENSE.txt → LICENSE} +1 -1
  11. data/README.md +34 -2
  12. data/better_errors.gemspec +8 -6
  13. data/gemfiles/pry010.gemfile +2 -1
  14. data/gemfiles/pry011.gemfile +2 -1
  15. data/gemfiles/pry09.gemfile +2 -1
  16. data/gemfiles/rack.gemfile +2 -1
  17. data/gemfiles/rack_boc.gemfile +2 -1
  18. data/gemfiles/rails42.gemfile +2 -1
  19. data/gemfiles/rails42_boc.gemfile +2 -1
  20. data/gemfiles/rails42_haml.gemfile +2 -1
  21. data/gemfiles/rails50.gemfile +2 -1
  22. data/gemfiles/rails50_boc.gemfile +2 -1
  23. data/gemfiles/rails50_haml.gemfile +2 -1
  24. data/gemfiles/rails51.gemfile +2 -1
  25. data/gemfiles/rails51_boc.gemfile +2 -1
  26. data/gemfiles/rails51_haml.gemfile +2 -1
  27. data/gemfiles/rails52.gemfile +2 -1
  28. data/gemfiles/rails52_boc.gemfile +2 -1
  29. data/gemfiles/rails52_haml.gemfile +2 -1
  30. data/gemfiles/rails60.gemfile +8 -0
  31. data/gemfiles/rails60_boc.gemfile +9 -0
  32. data/gemfiles/rails60_haml.gemfile +9 -0
  33. data/gemfiles/rails61.gemfile +8 -0
  34. data/gemfiles/rails61_boc.gemfile +9 -0
  35. data/gemfiles/rails61_haml.gemfile +9 -0
  36. data/lib/better_errors/code_formatter/html.rb +15 -1
  37. data/lib/better_errors/code_formatter.rb +16 -27
  38. data/lib/better_errors/editor.rb +103 -0
  39. data/lib/better_errors/error_page.rb +53 -12
  40. data/lib/better_errors/error_page_style.rb +43 -0
  41. data/lib/better_errors/exception_hint.rb +29 -0
  42. data/lib/better_errors/middleware.rb +75 -12
  43. data/lib/better_errors/raised_exception.rb +25 -4
  44. data/lib/better_errors/templates/main.css +1 -0
  45. data/lib/better_errors/templates/main.erb +94 -709
  46. data/lib/better_errors/templates/text.erb +6 -3
  47. data/lib/better_errors/templates/variable_info.erb +24 -14
  48. data/lib/better_errors/version.rb +2 -1
  49. data/lib/better_errors.rb +20 -34
  50. metadata +51 -8
  51. data/.travis.yml +0 -58
@@ -2,706 +2,14 @@
2
2
  <html>
3
3
  <head>
4
4
  <title><%= exception_type %> at <%= request_path %></title>
5
+ <link rel="icon" href="data:;base64,=" />
5
6
  </head>
6
- <body>
7
+ <body class="better-errors-javascript-not-loaded">
7
8
  <%# Stylesheets are placed in the <body> for Turbolinks compatibility. %>
8
- <style>
9
- /* Basic reset */
10
- * {
11
- margin: 0;
12
- padding: 0;
13
- }
14
-
15
- table {
16
- width: 100%;
17
- border-collapse: collapse;
18
- }
19
-
20
- th, td {
21
- vertical-align: top;
22
- text-align: left;
23
- }
24
-
25
- textarea {
26
- resize: none;
27
- }
28
-
29
- body {
30
- font-size: 10pt;
31
- }
32
-
33
- body, td, input, textarea {
34
- font-family: helvetica neue, lucida grande, sans-serif;
35
- line-height: 1.5;
36
- color: #333;
37
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
38
- }
39
-
40
- html {
41
- background: #f0f0f5;
42
- }
43
-
44
- .clearfix::after{
45
- clear: both;
46
- content: ".";
47
- display: block;
48
- height: 0;
49
- visibility: hidden;
50
- }
51
-
52
- /* ---------------------------------------------------------------------
53
- * Basic layout
54
- * --------------------------------------------------------------------- */
55
-
56
- /* Small */
57
- @media screen and (max-width: 1100px) {
58
- html {
59
- overflow-y: scroll;
60
- }
61
-
62
- body {
63
- margin: 0 20px;
64
- }
65
-
66
- header.exception {
67
- margin: 0 -20px;
68
- }
69
-
70
- nav.sidebar {
71
- padding: 0;
72
- margin: 20px 0;
73
- }
74
-
75
- ul.frames {
76
- max-height: 200px;
77
- overflow: auto;
78
- }
79
- }
80
-
81
- /* Wide */
82
- @media screen and (min-width: 1100px) {
83
- header.exception {
84
- position: fixed;
85
- top: 0;
86
- left: 0;
87
- right: 0;
88
- }
89
-
90
- nav.sidebar,
91
- .frame_info {
92
- position: fixed;
93
- top: 95px;
94
- bottom: 0;
95
-
96
- box-sizing: border-box;
97
-
98
- overflow-y: auto;
99
- overflow-x: hidden;
100
- }
101
-
102
- nav.sidebar {
103
- width: 40%;
104
- left: 20px;
105
- top: 115px;
106
- bottom: 20px;
107
- }
108
-
109
- .frame_info {
110
- right: 0;
111
- left: 40%;
112
-
113
- padding: 20px;
114
- padding-left: 10px;
115
- margin-left: 30px;
116
- }
117
- }
118
-
119
- nav.sidebar {
120
- background: #d3d3da;
121
- border-top: solid 3px #a33;
122
- border-bottom: solid 3px #a33;
123
- border-radius: 4px;
124
- box-shadow: 0 0 6px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
125
- }
126
-
127
- /* ---------------------------------------------------------------------
128
- * Header
129
- * --------------------------------------------------------------------- */
130
-
131
- header.exception {
132
- padding: 18px 20px;
133
-
134
- height: 59px;
135
- min-height: 59px;
136
-
137
- overflow: hidden;
138
-
139
- background-color: #20202a;
140
- color: #aaa;
141
- text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
142
- font-weight: 200;
143
- box-shadow: inset 0 -5px 3px -3px rgba(0, 0, 0, 0.05), inset 0 -1px 0 rgba(0, 0, 0, 0.05);
144
-
145
- -webkit-text-smoothing: antialiased;
146
- }
147
-
148
- /* Heading */
149
- header.exception h2 {
150
- font-weight: 200;
151
- font-size: 11pt;
152
- }
153
-
154
- header.exception h2,
155
- header.exception p {
156
- line-height: 1.4em;
157
- overflow: hidden;
158
- white-space: pre;
159
- text-overflow: ellipsis;
160
- }
161
-
162
- header.exception h2 strong {
163
- font-weight: 700;
164
- color: #d55;
165
- }
166
-
167
- header.exception p {
168
- font-weight: 200;
169
- font-size: 20pt;
170
- color: white;
171
- }
172
-
173
- header.exception:hover {
174
- height: auto;
175
- z-index: 2;
176
- }
177
-
178
- header.exception:hover h2,
179
- header.exception:hover p {
180
- padding-right: 20px;
181
- overflow-y: auto;
182
- word-wrap: break-word;
183
- white-space: pre-wrap;
184
- height: auto;
185
- max-height: 7.5em;
186
- }
187
-
188
- @media screen and (max-width: 1100px) {
189
- header.exception {
190
- height: auto;
191
- }
192
-
193
- header.exception h2,
194
- header.exception p {
195
- padding-right: 20px;
196
- overflow-y: auto;
197
- word-wrap: break-word;
198
- height: auto;
199
- max-height: 7em;
200
- }
201
- }
202
-
203
- <%#
204
- /* Light theme */
205
- header.exception {
206
- text-shadow: 0 1px 0 rgba(250, 250, 250, 0.6);
207
- background: rgba(200,100,50,0.10);
208
- color: #977;
209
- }
210
- header.exception h2 strong {
211
- color: #533;
212
- }
213
- header.exception p {
214
- color: #744;
215
- }
216
- %>
217
-
218
- /* ---------------------------------------------------------------------
219
- * Navigation
220
- * --------------------------------------------------------------------- */
221
-
222
- nav.tabs {
223
- border-bottom: solid 1px #ddd;
224
-
225
- background-color: #eee;
226
- text-align: center;
227
-
228
- padding: 6px;
229
-
230
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
231
- }
232
-
233
- nav.tabs a {
234
- display: inline-block;
235
-
236
- height: 22px;
237
- line-height: 22px;
238
- padding: 0 10px;
239
-
240
- text-decoration: none;
241
- font-size: 8pt;
242
- font-weight: bold;
243
-
244
- color: #999;
245
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
246
- }
247
-
248
- nav.tabs a.selected {
249
- color: white;
250
- background: rgba(0, 0, 0, 0.5);
251
- border-radius: 16px;
252
- box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.1);
253
- text-shadow: 0 0 4px rgba(0, 0, 0, 0.4), 0 1px 0 rgba(0, 0, 0, 0.4);
254
- }
255
-
256
- nav.tabs a.disabled {
257
- text-decoration: line-through;
258
- text-shadow: none;
259
- cursor: default;
260
- }
261
-
262
- /* ---------------------------------------------------------------------
263
- * Sidebar
264
- * --------------------------------------------------------------------- */
265
-
266
- ul.frames {
267
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
268
- }
269
-
270
- /* Each item */
271
- ul.frames li {
272
- background-color: #f8f8f8;
273
- background: -webkit-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
274
- background: -moz-linear-gradient(top, #f8f8f8 80%, #f0f0f0);
275
- background: linear-gradient(top, #f8f8f8 80%, #f0f0f0);
276
- box-shadow: inset 0 -1px 0 #e2e2e2;
277
- padding: 7px 20px;
278
-
279
- cursor: pointer;
280
- overflow: hidden;
281
- }
282
-
283
- ul.frames .name,
284
- ul.frames .location {
285
- overflow: hidden;
286
- height: 1.5em;
287
-
288
- white-space: nowrap;
289
- word-wrap: none;
290
- text-overflow: ellipsis;
291
- }
292
-
293
- ul.frames .method {
294
- color: #966;
295
- }
296
-
297
- ul.frames .location {
298
- font-size: 0.85em;
299
- font-weight: 400;
300
- color: #999;
301
- }
302
-
303
- ul.frames .line {
304
- font-weight: bold;
305
- }
306
-
307
- /* Selected frame */
308
- ul.frames li.selected {
309
- background: #38a;
310
- box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), inset 0 2px 0 rgba(255, 255, 255, 0.01), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
311
- }
312
-
313
- ul.frames li.selected .name,
314
- ul.frames li.selected .method,
315
- ul.frames li.selected .location {
316
- color: white;
317
- text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
318
- }
319
-
320
- ul.frames li.selected .location {
321
- opacity: 0.6;
322
- }
323
-
324
- /* Iconography */
325
- ul.frames li {
326
- padding-left: 60px;
327
- position: relative;
328
- }
329
-
330
- ul.frames li .icon {
331
- display: block;
332
- width: 20px;
333
- height: 20px;
334
- line-height: 20px;
335
- border-radius: 15px;
336
-
337
- text-align: center;
338
-
339
- background: white;
340
- border: solid 2px #ccc;
341
-
342
- font-size: 9pt;
343
- font-weight: 200;
344
- font-style: normal;
345
-
346
- position: absolute;
347
- top: 14px;
348
- left: 20px;
349
- }
350
-
351
- ul.frames .icon.application {
352
- background: #808090;
353
- border-color: #555;
354
- }
355
-
356
- ul.frames .icon.application:before {
357
- content: 'A';
358
- color: white;
359
- text-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
360
- }
361
-
362
- /* Responsiveness -- flow to single-line mode */
363
- @media screen and (max-width: 1100px) {
364
- ul.frames li {
365
- padding-top: 6px;
366
- padding-bottom: 6px;
367
- padding-left: 36px;
368
- line-height: 1.3;
369
- }
370
-
371
- ul.frames li .icon {
372
- width: 11px;
373
- height: 11px;
374
- line-height: 11px;
375
-
376
- top: 7px;
377
- left: 10px;
378
- font-size: 5pt;
379
- }
380
-
381
- ul.frames .name,
382
- ul.frames .location {
383
- display: inline-block;
384
- line-height: 1.3;
385
- height: 1.3em;
386
- }
387
-
388
- ul.frames .name {
389
- margin-right: 10px;
390
- }
391
- }
392
-
393
- /* ---------------------------------------------------------------------
394
- * Monospace
395
- * --------------------------------------------------------------------- */
396
-
397
- pre, code, .be-repl input, .be-repl .command-line span, textarea, .code_linenums {
398
- font-family: menlo, lucida console, monospace;
399
- font-size: 8pt;
400
- }
401
-
402
- /* ---------------------------------------------------------------------
403
- * Display area
404
- * --------------------------------------------------------------------- */
405
-
406
- .trace_info {
407
- background: #fff;
408
- padding: 6px;
409
- border-radius: 3px;
410
- margin-bottom: 2px;
411
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.03), 1px 1px 0 rgba(0, 0, 0, 0.05), -1px 1px 0 rgba(0, 0, 0, 0.05), 0 0 0 4px rgba(0, 0, 0, 0.04);
412
- }
413
-
414
- .code_block{
415
- background: #f1f1f1;
416
- border-left: 1px solid #ccc;
417
- }
418
-
419
- /* Titlebar */
420
- .trace_info .title {
421
- background: #f1f1f1;
422
-
423
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
424
- overflow: hidden;
425
- padding: 6px 10px;
426
-
427
- border: solid 1px #ccc;
428
- border-bottom: 0;
429
-
430
- border-top-left-radius: 2px;
431
- border-top-right-radius: 2px;
432
- }
433
-
434
- .trace_info .title .name,
435
- .trace_info .title .location {
436
- font-size: 9pt;
437
- line-height: 26px;
438
- height: 26px;
439
- overflow: hidden;
440
- }
441
-
442
- .trace_info .title .location {
443
- float: left;
444
- font-weight: bold;
445
- font-size: 10pt;
446
- }
447
-
448
- .trace_info .title .location a {
449
- color:inherit;
450
- text-decoration:none;
451
- border-bottom:1px solid #aaaaaa;
452
- }
453
-
454
- .trace_info .title .location a:hover {
455
- border-color:#666666;
456
- }
457
-
458
- .trace_info .title .name {
459
- float: right;
460
- font-weight: 200;
461
- }
462
-
463
- .code, .be-console, .unavailable {
464
- background: #fff;
465
- padding: 5px;
466
-
467
- box-shadow: inset 3px 3px 3px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1);
468
- }
469
-
470
- .code_linenums{
471
- background:#f1f1f1;
472
- padding-top:10px;
473
- padding-bottom:9px;
474
- float:left;
475
- }
476
-
477
- .code_linenums span{
478
- display:block;
479
- padding:0 12px;
480
- }
481
-
482
- .code {
483
- margin-bottom: -1px;
484
- border-top-left-radius:2px;
485
- padding: 10px 0;
486
- overflow: auto;
487
- }
488
-
489
- .code pre{
490
- padding-left:12px;
491
- min-height:16px;
492
- }
493
-
494
- /* Source unavailable */
495
- p.unavailable {
496
- padding: 20px 0 40px 0;
497
- text-align: center;
498
- color: #b99;
499
- font-weight: bold;
500
- }
501
-
502
- p.unavailable:before {
503
- content: '\00d7';
504
- display: block;
505
-
506
- color: #daa;
507
-
508
- text-align: center;
509
- font-size: 40pt;
510
- font-weight: normal;
511
- margin-bottom: -10px;
512
- }
513
-
514
- @-webkit-keyframes highlight {
515
- 0% { background: rgba(220, 30, 30, 0.3); }
516
- 100% { background: rgba(220, 30, 30, 0.1); }
517
- }
518
- @-moz-keyframes highlight {
519
- 0% { background: rgba(220, 30, 30, 0.3); }
520
- 100% { background: rgba(220, 30, 30, 0.1); }
521
- }
522
- @keyframes highlight {
523
- 0% { background: rgba(220, 30, 30, 0.3); }
524
- 100% { background: rgba(220, 30, 30, 0.1); }
525
- }
526
-
527
- .code .highlight, .code_linenums .highlight {
528
- background: rgba(220, 30, 30, 0.1);
529
- -webkit-animation: highlight 400ms linear 1;
530
- -moz-animation: highlight 400ms linear 1;
531
- animation: highlight 400ms linear 1;
532
- }
533
-
534
- /* REPL shell */
535
- .be-console {
536
- padding: 0 1px 10px 1px;
537
- border-bottom-left-radius: 2px;
538
- border-bottom-right-radius: 2px;
539
- }
540
-
541
- .be-console pre {
542
- padding: 10px 10px 0 10px;
543
- max-height: 400px;
544
- overflow-x: none;
545
- overflow-y: auto;
546
- margin-bottom: -3px;
547
- word-wrap: break-word;
548
- white-space: pre-wrap;
549
- }
550
-
551
- /* .command-line > span + input */
552
- .be-console .command-line {
553
- display: table;
554
- width: 100%;
555
- }
556
-
557
- .be-console .command-line span,
558
- .be-console .command-line input {
559
- display: table-cell;
560
- }
561
-
562
- .be-console .command-line span {
563
- width: 1%;
564
- padding-right: 5px;
565
- padding-left: 10px;
566
- white-space: pre;
567
- }
568
-
569
- .be-console .command-line input {
570
- width: 99%;
571
- }
572
-
573
- /* Input box */
574
- .be-console input,
575
- .be-console input:focus {
576
- outline: 0;
577
- border: 0;
578
- padding: 0;
579
- background: transparent;
580
- margin: 0;
581
- }
582
-
583
- /* Hint text */
584
- .hint {
585
- margin: 15px 0 20px 0;
586
- font-size: 8pt;
587
- color: #8080a0;
588
- padding-left: 20px;
589
- }
590
-
591
- .hint:before {
592
- content: '\25b2';
593
- margin-right: 5px;
594
- opacity: 0.5;
595
- }
596
-
597
- /* ---------------------------------------------------------------------
598
- * Variable infos
599
- * --------------------------------------------------------------------- */
600
-
601
- .sub {
602
- padding: 10px 0;
603
- margin: 10px 0;
604
- }
605
-
606
- .sub:before {
607
- content: '';
608
- display: block;
609
- width: 100%;
610
- height: 4px;
611
-
612
- border-radius: 2px;
613
- background: rgba(0, 150, 200, 0.05);
614
- box-shadow: 1px 1px 0 rgba(255, 255, 255, 0.7), inset 0 0 0 1px rgba(0, 0, 0, 0.04), inset 2px 2px 2px rgba(0, 0, 0, 0.07);
615
- }
616
-
617
- .sub h3 {
618
- color: #39a;
619
- font-size: 1.1em;
620
- margin: 10px 0;
621
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
622
-
623
- -webkit-font-smoothing: antialiased;
624
- }
625
-
626
- .sub .inset {
627
- overflow-y: auto;
628
- }
629
-
630
- .sub table {
631
- table-layout: fixed;
632
- }
633
-
634
- .sub table td {
635
- border-top: dotted 1px #ddd;
636
- padding: 7px 1px;
637
- }
638
-
639
- .sub table td.name {
640
- width: 150px;
641
-
642
- font-weight: bold;
643
- font-size: 0.8em;
644
- padding-right: 20px;
645
-
646
- word-wrap: break-word;
647
- }
648
-
649
- .sub table td pre {
650
- max-height: 15em;
651
- overflow-y: auto;
652
- }
653
-
654
- .sub table td pre {
655
- width: 100%;
656
-
657
- word-wrap: break-word;
658
- white-space: normal;
659
- }
660
-
661
- /* "(object doesn't support inspect)" */
662
- .sub .unsupported {
663
- font-family: sans-serif;
664
- color: #777;
665
- }
666
-
667
- /* ---------------------------------------------------------------------
668
- * Scrollbar
669
- * --------------------------------------------------------------------- */
670
-
671
- nav.sidebar::-webkit-scrollbar,
672
- .inset pre::-webkit-scrollbar,
673
- .be-console pre::-webkit-scrollbar,
674
- .code::-webkit-scrollbar {
675
- width: 10px;
676
- height: 10px;
677
- }
678
-
679
- .inset pre::-webkit-scrollbar-thumb,
680
- .be-console pre::-webkit-scrollbar-thumb,
681
- .code::-webkit-scrollbar-thumb {
682
- background: #ccc;
683
- border-radius: 5px;
684
- }
685
-
686
- nav.sidebar::-webkit-scrollbar-thumb {
687
- background: rgba(0, 0, 0, 0.0);
688
- border-radius: 5px;
689
- }
690
-
691
- nav.sidebar:hover::-webkit-scrollbar-thumb {
692
- background-color: #999;
693
- background: -webkit-linear-gradient(left, #aaa, #999);
694
- }
695
-
696
- .be-console pre:hover::-webkit-scrollbar-thumb,
697
- .inset pre:hover::-webkit-scrollbar-thumb,
698
- .code:hover::-webkit-scrollbar-thumb {
699
- background: #888;
700
- }
701
- </style>
9
+ <%== ErrorPageStyle.style_tag(csp_nonce) %>
702
10
 
703
11
  <%# IE8 compatibility crap %>
704
- <script>
12
+ <script nonce="<%= csp_nonce %>">
705
13
  (function() {
706
14
  var elements = ["section", "nav", "header", "footer", "audio"];
707
15
  for (var i = 0; i < elements.length; i++) {
@@ -715,7 +23,7 @@
715
23
  rendered in the host app's layout. Let's empty out the styles of the
716
24
  host app.
717
25
  %>
718
- <script>
26
+ <script nonce="<%= csp_nonce %>">
719
27
  if (window.Turbolinks) {
720
28
  for(var i=0; i < document.styleSheets.length; i++) {
721
29
  if(document.styleSheets[i].href)
@@ -740,10 +48,34 @@
740
48
  }
741
49
  </script>
742
50
 
51
+ <p class='no-inline-style-notice'>
52
+ <strong>
53
+ Better Errors can't apply inline style<span class='no-javascript-notice'> (or run Javascript)</span>,
54
+ possibly because you have a Content Security Policy along with Turbolinks.
55
+ But you can
56
+ <a href='/__better_errors' target="_blank">open the interactive console in a new tab/window</a>.
57
+ </strong>
58
+ </p>
59
+
743
60
  <div class='top'>
744
61
  <header class="exception">
745
62
  <h2><strong><%= exception_type %></strong> <span>at <%= request_path %></span></h2>
746
63
  <p><%= exception_message %></p>
64
+ <% unless active_support_actions.empty? %>
65
+ <div class='fix-actions'>
66
+ <% active_support_actions.each do |action, _| %>
67
+ <form class="button_to" method="post" action="<%= action_dispatch_action_endpoint %>">
68
+ <input type="submit" value="<%= action %>">
69
+ <input type="hidden" name="action" value="<%= action %>">
70
+ <input type="hidden" name="error" value="<%= exception_type %>">
71
+ <input type="hidden" name="location" value="<%= request_path %>">
72
+ </form>
73
+ <% end %>
74
+ </div>
75
+ <% end %>
76
+ <% if exception_hint %>
77
+ <h2>Hint: <%= exception_hint %></h2>
78
+ <% end %>
747
79
  </header>
748
80
  </div>
749
81
 
@@ -771,25 +103,43 @@
771
103
  </ul>
772
104
  </nav>
773
105
 
774
- <% backtrace_frames.each_with_index do |frame, index| %>
775
- <div class="frame_info" id="frame_info_<%= index %>" style="display:none;"></div>
776
- <% end %>
106
+ <div class="frameInfos">
107
+ <div class="frame_info current" data-frame-idx="0">
108
+ <p class='no-javascript-notice'>
109
+ Better Errors can't run Javascript here<span class='no-inline-style-notice'> (or apply inline style)</span>,
110
+ possibly because you have a Content Security Policy along with Turbolinks.
111
+ But you can
112
+ <a href='/__better_errors' target="_blank">open the interactive console in a new tab/window</a>.
113
+ </p>
114
+ <!-- this is enough information to show something in case JS doesn't get to load -->
115
+ <%== ErrorPage.render_template('variable_info', first_frame_variable_info) %>
116
+ </div>
117
+ </div>
777
118
  </section>
778
119
  </body>
779
- <script>
120
+ <script nonce="<%= csp_nonce %>">
780
121
  (function() {
781
122
 
782
123
  var OID = "<%= id %>";
124
+ var csrfToken = "<%= csrf_token %>";
783
125
 
784
126
  var previousFrame = null;
785
- var previousFrameInfo = null;
786
127
  var allFrames = document.querySelectorAll("ul.frames li");
787
- var allFrameInfos = document.querySelectorAll(".frame_info");
128
+ var frameInfos = document.querySelector(".frameInfos");
129
+
130
+ document.querySelector('body').classList.remove("better-errors-javascript-not-loaded");
131
+ document.querySelector('body').classList.add("better-errors-javascript-loaded");
132
+
133
+ var noJSNotices = document.querySelectorAll('.no-javascript-notice');
134
+ for(var i = 0; i < noJSNotices.length; i++) {
135
+ noJSNotices[i].remove();
136
+ }
788
137
 
789
138
  function apiCall(method, opts, cb) {
790
139
  var req = new XMLHttpRequest();
791
140
  req.open("POST", "//" + window.location.host + <%== uri_prefix.gsub("<", "&lt;").inspect %> + "/__better_errors/" + OID + "/" + method, true);
792
141
  req.setRequestHeader("Content-Type", "application/json");
142
+ opts.csrfToken = csrfToken;
793
143
  req.send(JSON.stringify(opts));
794
144
  req.onreadystatechange = function() {
795
145
  if(req.readyState == 4) {
@@ -803,6 +153,28 @@
803
153
  return html.replace(/&/, "&amp;").replace(/</g, "&lt;");
804
154
  }
805
155
 
156
+ function hasConsoleBeenUsedPreviously() {
157
+ return !!document.cookie.split('; ').find(function(cookie) {
158
+ return cookie.startsWith('BetterErrors-has-used-console=');
159
+ });
160
+ }
161
+
162
+ var consoleHasBeenUsed = hasConsoleBeenUsedPreviously();
163
+
164
+ function consoleWasJustUsed() {
165
+ if (consoleHasBeenUsed) {
166
+ return;
167
+ }
168
+
169
+ hideConsoleHint();
170
+ consoleHasBeenUsed = true;
171
+ document.cookie = "BetterErrors-has-used-console=true;path=/;max-age=31536000;samesite"
172
+ }
173
+
174
+ function hideConsoleHint() {
175
+ document.querySelector('body').className += " console-has-been-used";
176
+ }
177
+
806
178
  function REPL(index) {
807
179
  this.index = index;
808
180
 
@@ -824,15 +196,20 @@
824
196
  this.inputElement = this.container.querySelector("input");
825
197
  this.outputElement = this.container.querySelector("pre");
826
198
 
199
+ if (consoleHasBeenUsed) {
200
+ hideConsoleHint();
201
+ }
202
+
827
203
  var self = this;
828
204
  this.inputElement.onkeydown = function(ev) {
829
205
  self.onKeyDown(ev);
206
+ consoleWasJustUsed();
830
207
  };
831
208
 
832
209
  this.setPrompt(">>");
833
210
 
834
211
  REPL.all[this.index] = this;
835
- }
212
+ };
836
213
 
837
214
  REPL.prototype.focus = function() {
838
215
  this.inputElement.focus();
@@ -872,7 +249,7 @@
872
249
  self.writeOutput(response.error + "\n");
873
250
  }
874
251
  self.writeOutput(self._prompt + " ");
875
- self.writeRawOutput(response.highlighted_input + "\n");
252
+ self.writeRawOutput("<span class='syntax-highlighted'>" + response.highlighted_input + "</span>\n");
876
253
  self.writeOutput(response.result);
877
254
  self.setPrompt(response.prompt);
878
255
  self.setInput(response.prefilled_input);
@@ -930,17 +307,25 @@
930
307
  };
931
308
 
932
309
  function switchTo(el) {
933
- if(previousFrameInfo) previousFrameInfo.style.display = "none";
934
- previousFrameInfo = el;
310
+ var currentFrameInfo = document.querySelectorAll('.frame_info.current');
311
+ for(var i = 0; i < currentFrameInfo.length; i++) {
312
+ currentFrameInfo[i].className = "frame_info";
313
+ }
935
314
 
936
- el.style.display = "block";
315
+ el.className = "frame_info current";
937
316
 
938
317
  var replInput = el.querySelector('.be-console input');
939
318
  if (replInput) replInput.focus();
940
319
  }
941
320
 
942
321
  function selectFrameInfo(index) {
943
- var el = allFrameInfos[index];
322
+ var el = document.querySelector(".frame_info[data-frame-idx='" + index + "']")
323
+ if (!el) {
324
+ el = document.createElement("div");
325
+ el.className = "frame_info";
326
+ el.setAttribute('data-frame-idx', index);
327
+ frameInfos.appendChild(el);
328
+ }
944
329
  if(el) {
945
330
  if (el.loaded) {
946
331
  return switchTo(el);