snapshot_live.html 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. {% load static tz core_tags %}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <title>{{title}}</title>
  6. <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
  7. <link href="{% static 'bootstrap.min.css' %}" rel="stylesheet">
  8. <style>
  9. /* Keep this inline, don't move to external css file because this template is used to generate static exports that need to be usable as-is without an accompanying staticfiles dir */
  10. html, body {
  11. width: 100%;
  12. height: 100%;
  13. background-color: #ddd;
  14. }
  15. header {
  16. background-color: #aa1e55;
  17. }
  18. small {
  19. font-weight: 200;
  20. }
  21. header a:hover {
  22. text-decoration: none;
  23. }
  24. .header-top {
  25. width: 100%;
  26. height: auto;
  27. min-height: 40px;
  28. margin: 0px;
  29. text-align: center;
  30. color: #f6f6f6;
  31. font-size: calc(10px + 0.84vw);
  32. font-weight: 200;
  33. padding: 3px 4px;
  34. background-color: #aa1e55;
  35. }
  36. .header-top .nav {
  37. width: 100%;
  38. }
  39. .nav > div {
  40. min-height: 30px;
  41. line-height: 1.2;
  42. }
  43. .header-top .header-url {
  44. display: inline-block;
  45. width: 100%;
  46. background-color: rgb(216, 216, 235, 0.05);
  47. text-align: center;
  48. line-height: 1.3;
  49. font-family: monospace;
  50. white-space: nowrap;
  51. font-weight: 200;
  52. display: block;
  53. margin-top: -1px;
  54. font-size: 23px;
  55. opacity: 0.8;
  56. border-radius: 0px 0px 8px 8px;
  57. }
  58. .header-top .header-url a.header-url-text {
  59. color: #f6f6f6;
  60. user-select: all;
  61. text-overflow: ellipsis;
  62. }
  63. .header-top .header-url a.header-url-text:hover {
  64. color: rgb(144, 161, 255);
  65. }
  66. .header-top a {
  67. text-decoration: none;
  68. color: rgba(0,0,0,0.6);
  69. }
  70. .header-top a:hover {
  71. text-decoration: none;
  72. color: rgba(0,0,0,0.9);
  73. }
  74. .header-top .header-title {
  75. color: rgba(0,0,0,0.6);
  76. }
  77. .header-top .favicon {
  78. height: 24px;
  79. vertical-align: -5px;
  80. margin-right: 4px;
  81. }
  82. .header-top .col-lg-4 {
  83. text-align: center;
  84. padding-top: 4px;
  85. padding-bottom: 4px;
  86. }
  87. .header-archivebox img {
  88. display: inline-block;
  89. margin-right: 3px;
  90. height: 30px;
  91. margin-left: 12px;
  92. margin-top: -4px;
  93. margin-bottom: 2px;
  94. }
  95. .header-archivebox img:hover {
  96. opacity: 0.5;
  97. }
  98. header small code {
  99. white-space: nowrap;
  100. font-weight: 200;
  101. display: block;
  102. margin-top: -1px;
  103. font-size: 13px;
  104. opacity: 0.8;
  105. user-select: all;
  106. }
  107. .header-toggle {
  108. line-height: 12px;
  109. font-size: 70px;
  110. vertical-align: -12px;
  111. margin-left: 4px;
  112. }
  113. .info-row {
  114. margin-top: 2px;
  115. margin-bottom: 5px;
  116. }
  117. .info-row .alert {
  118. margin-bottom: 0px;
  119. }
  120. .row.header-bottom {
  121. margin-left: -10px;
  122. margin-right: -10px;
  123. }
  124. .header-bottom .col-lg-2 {
  125. padding-left: 4px;
  126. padding-right: 4px;
  127. }
  128. .header-bottom-frames .card {
  129. box-shadow: 2px 2px 7px 0px rgba(0, 0, 0, 0.1);
  130. margin-bottom: 5px;
  131. border: 1px solid rgba(0, 0, 0, 0.06);
  132. border-radius: 10px;
  133. background-color: #efefef;
  134. overflow: hidden;
  135. height: 130px;
  136. }
  137. .card h4 {
  138. font-size: 0.8em;
  139. display: inline-block;
  140. width: auto;
  141. text-transform: uppercase;
  142. margin-top: 0px;
  143. margin-bottom: 5px;
  144. color: rgb(93, 105, 110);
  145. }
  146. .card-body {
  147. font-size: 14px;
  148. padding: 4px 10px;
  149. padding-bottom: 0px;
  150. /* padding-left: 3px; */
  151. /* padding-right: 3px; */
  152. /* padding-bottom: 3px; */
  153. line-height: 1;
  154. word-wrap: break-word;
  155. max-height: 102px;
  156. overflow: hidden;
  157. text-overflow: ellipsis;
  158. color: #d3d3d3;
  159. }
  160. .card-title {
  161. margin-bottom: 4px;
  162. text-transform: uppercase;
  163. }
  164. .card-img-top {
  165. border: 0px;
  166. padding: 0px;
  167. margin: 0px;
  168. overflow: hidden;
  169. opacity: 0.8;
  170. border-top: 1px solid rgba(0,0,0,0);
  171. border-radius: 4px;
  172. border-bottom: 1px solid rgba(0,0,0,0);
  173. height: 430px;
  174. width: 405%;
  175. margin-bottom: -330px;
  176. background-color: #333;
  177. margin-left: -1%;
  178. margin-right: -1%;
  179. pointer-events: none;
  180. transform: scale(0.25);
  181. transform-origin: 0 0;
  182. }
  183. #main-frame {
  184. border-top: 1px solid #ddd;
  185. width: 100%;
  186. height: calc(100vh - 210px);
  187. margin: 0px;
  188. border: 0px;
  189. border-top: 3px solid #aa1e55;
  190. }
  191. .card.selected-card {
  192. border: 2px solid orange;
  193. box-shadow: 0px -6px 13px 1px rgba(0,0,0,0.05);
  194. }
  195. .iframe-large {
  196. height: calc(100vh - 70px);
  197. }
  198. img.external {
  199. height: 30px;
  200. margin-right: -10px;
  201. padding: 3px;
  202. border-radius: 4px;
  203. vertical-align: middle;
  204. border: 4px solid rgba(0,0,0,0);
  205. }
  206. img.external:hover {
  207. border: 4px solid green;
  208. }
  209. .screenshot {
  210. background-color: #333;
  211. transform: none;
  212. width: 100%;
  213. min-height: 100px;
  214. max-height: 100px;
  215. margin-bottom: 0px;
  216. object-fit: cover;
  217. object-position: top center;
  218. }
  219. .header-bottom {
  220. border-top: 1px solid rgba(170, 30, 85, 0.9);
  221. padding-bottom: 1px;
  222. border-bottom: 5px solid rgb(170, 30, 85);
  223. margin-bottom: -1px;
  224. border-radius: 0px;
  225. background-color: #f4eeee;
  226. border: 1px solid rgba(0,0,0,0.2);
  227. box-shadow: 4px 4px 4px rgba(0,0,0,0.2);
  228. margin-top: 0px;
  229. }
  230. .header-bottom-info {
  231. color: #6f6f6f;
  232. padding-top: 0px;
  233. padding-bottom: 0px;
  234. margin: 0px -15px;
  235. }
  236. .header-bottom-info > div {
  237. text-align: center;
  238. }
  239. .header-bottom-info h5 {
  240. font-size: 12px;
  241. font-weight: 400;
  242. margin-top: 3px;
  243. margin-bottom: 3px;
  244. }
  245. .info-chunk {
  246. width: auto;
  247. display: inline-block;
  248. text-align: center;
  249. margin: 8px 4px;
  250. vertical-align: top;
  251. font-size: 14px;
  252. }
  253. header .badge {
  254. margin-top: 3px;
  255. font-size: 0.9rem;
  256. font-weight: 200;
  257. font-family: monospace;
  258. }
  259. header .internal-links {
  260. text-align: left;
  261. opacity: 1;
  262. background-color: rgba(0,0,0,0.03);
  263. padding: 1px 3px;
  264. }
  265. header .external-links {
  266. text-align: center;
  267. opacity: 0.9;
  268. /*background-color: rgba(0,0,0,0.03);*/
  269. margin-top: 0px;
  270. padding: 1px 3px;
  271. font-size: 14px;
  272. color: #ddd;
  273. width: 100%;
  274. overflow: hidden;
  275. }
  276. .header-bottom-frames {
  277. padding-top: 5px;
  278. justify-content: center;
  279. }
  280. .header-bottom-frames .card-title {
  281. width: 100%;
  282. text-align: center;
  283. font-size: 17px;
  284. margin-bottom: 0px;
  285. display: inline-block;
  286. color: #d3d3d3;
  287. font-weight: 200;
  288. vertical-align: 3px;
  289. }
  290. .header-bottom-frames .card-text {
  291. /* width: 100%;
  292. text-align: center;*/
  293. font-size: 0.9em;
  294. display: inline-block;
  295. position: relative;
  296. /* top: -11px;*/
  297. }
  298. .card-text code {
  299. padding: .1rem .2rem;
  300. font-size: 90%;
  301. color: #bd4147;
  302. background-color: rgb(204, 204, 204, 0.28);
  303. border-radius: .25rem;
  304. }
  305. /*@media(max-width: 1092px) {
  306. iframe {
  307. display: none;
  308. }
  309. }*/
  310. @media(max-width: 728px) {
  311. .card h4 {
  312. font-size: 5vw;
  313. }
  314. .card-body {
  315. font-size: 4vw;
  316. }
  317. .card {
  318. margin-bottom: 5px;
  319. }
  320. header > h1 > a.header-url, header > h1 > a.header-archivebox {
  321. display: none;
  322. }
  323. }
  324. </style>
  325. </head>
  326. <body>
  327. <header>
  328. <div class="header-top container-fluid">
  329. <div class="row nav">
  330. <div class="col-lg-2" style="line-height: 58px; vertical-align: middle">
  331. <a href="../../index.html" class="header-archivebox" title="Go to Main Index...">
  332. <img src="../../static/archive.png" alt="Archive Icon">
  333. ArchiveBox
  334. </a>
  335. </div>
  336. <div class="col-lg-8">
  337. <div class="header-url">
  338. <a class="header-url-text" href="{{url}}" title="Open original URL in new window..." target="_blank" rel="noreferrer">
  339. {{url}}
  340. </a>
  341. </div>
  342. <div class="badge badge-{{status_color}}" style="float: left">
  343. <a href="/admin/core/snapshot/?q={{snapshot_id}}" title="Click to see options to pull, re-snapshot, or delete this Snapshot">
  344. {{status|upper}}
  345. </a>
  346. </div>
  347. <div class="badge badge-default" style="float: left; font-weight: 200">
  348. {{num_outputs}}
  349. {% if num_failures %}
  350. + {{num_failures}} <small>errors</small>
  351. {% endif %}
  352. </div>
  353. <div class="badge badge-info" style="float: right">
  354. <a href="/admin/core/snapshot/{{snapshot_id}}/change/" title="Click to edit this Snapshot in the Admin UI">
  355. {{size}}
  356. </a>
  357. </div>
  358. <div class="badge badge-default" style="float: right">
  359. <a href="/admin/core/snapshot/{{snapshot_id}}/change/" title="Click to edit this Snapshot in the Admin UI">
  360. {{extension}}
  361. </a>
  362. </div>
  363. <small class="header-title header-toggle-trigger">
  364. <img src="favicon.ico" onerror="this.style.opacity=0" alt="Favicon" class="favicon"/>
  365. {{title|truncatechars:120|safe}} <a href="#" class="header-toggle header-toggle-trigger">▾</a>
  366. <br/>
  367. {% for tag in tags_str|split:',' %}
  368. <div class="badge badge-default tag" style="word-break: break-all;">{{tag}}</div>
  369. {% endfor %}
  370. </small>
  371. </div>
  372. <div class="col-lg-2" style="padding-top: 4px">
  373. <a href="/archive/{{url}}" title="Date Added: {{bookmarked_date}} | First Archived: {{oldest_archive_date|default:updated_date}} | Last Checked: {{updated_date}} (UTC)">
  374. {{oldest_archive_date|default:updated_date|default:bookmarked_date}}
  375. </a>
  376. <br/>
  377. <div class="external-links">
  378. ↗️ &nbsp;
  379. <a href="./index.json" title="Get the Snapshot details as a JSON file" target="_blank">JSON</a> &nbsp;|&nbsp; 🗃️
  380. <a href="{{warc_path}}" title="Download the ArchiveBox-generated WARC file" target="_blank">WARC</a> &nbsp;|&nbsp;
  381. <a href="https://web.archive.org/web/{{url}}" title="Search for a copy of the URL saved in Archive.org" target="_blank" rel="noreferrer">🏛️ Archive.org</a>
  382. <!--<a href="https://archive.md/{{url}}" title="Search for a copy of the URL saved in Archive.today" target="_blank" rel="noreferrer">Archive.today</a> &nbsp;|&nbsp; -->
  383. <!--<a href="https://ghostarchive.org/search?term={{url|urlencode}}" title="Search for a copy of the URL saved in GhostArchive.org" target="_blank" rel="noreferrer">More...</a>-->
  384. </div>
  385. </div>
  386. </div>
  387. </div>
  388. <div class="header-bottom container-fluid">
  389. <div class="row header-bottom-frames">
  390. {% for result in archiveresults %}
  391. <div class="col-lg-2">
  392. <div class="card {% if forloop.first %}selected-card{% endif %}">
  393. <div class="card-body">
  394. <a href="{{result.path|urlencode}}" target="preview" title="./{{result.path}} (downloaded {{result.ts}})">
  395. <h4>{{result.name|truncatechars:24}} <small>({{result.size|filesizeformat}})</small></h4>
  396. <!-- <p class="card-text" ><code>./{{result.path|truncatechars:30}}</code></p> -->
  397. </a>
  398. <!--<a href="{{result.path}}" target="preview"><h4 class="card-title">{{result.name}}</h4></a>-->
  399. </div>
  400. <iframe class="card-img-top" src="{{result.path|urlencode}}?autoplay=0" allow="autoplay 'none'; fullscreen 'none'; navigation-override 'none'; " sandbox="allow-same-origin allow-top-navigation-by-user-activation allow-scripts allow-forms" scrolling="no" loading="lazy"></iframe>
  401. </div>
  402. </div>
  403. {% endfor %}
  404. <div class="col-lg-2">
  405. <div class="card">
  406. <div class="card-body">
  407. <a href="./" target="preview">
  408. <h4>Headers, JSON, etc.</h4>
  409. </a>
  410. <!--<a href="{{result.path|urlencode}}" target="preview"><h4 class="card-title">{{result.name}}</h4></a>-->
  411. </div>
  412. <iframe class="card-img-top" src="./" sandbox="" scrolling="no" loading="lazy"></iframe>
  413. </div>
  414. </div>
  415. </div>
  416. </div>
  417. </header>
  418. <iframe id="main-frame" sandbox="allow-same-origin allow-top-navigation-by-user-activation allow-scripts allow-forms" class="full-page-iframe" src="{{best_result.path|urlencode}}" name="preview"></iframe>
  419. <script src="{% static 'jquery.min.js' %}" type="text/javascript"></script>
  420. <script>
  421. // un-sandbox iframes showing pdfs (required to display pdf viewer)
  422. jQuery('iframe').map(function() {
  423. if (this.src.endsWith('.pdf')) {
  424. this.removeAttribute('sandbox')
  425. this.src = this.src + '#toolbar=0'
  426. }
  427. this.onload = function() {
  428. if (this.src.includes('.pdf')) {
  429. this.removeAttribute('sandbox')
  430. this.src = this.src.split('?autoplay=')[0] + '#toolbar=0'
  431. }
  432. try {
  433. // doesnt work if frame origin rules prevent accessing its DOM via JS
  434. this.contentWindow.scrollTo(0, 0);
  435. } catch(err) {}
  436. }
  437. })
  438. function getPreviewTypeFromPath(link) {
  439. if (link.getAttribute('href') == './') {
  440. return 'all'
  441. }
  442. return link.getAttribute('href')
  443. }
  444. const iframe_elem = document.getElementById('main-frame')
  445. for (const card of [...document.querySelectorAll('.card')]) {
  446. card.addEventListener('click', function(event) {
  447. const target = event.currentTarget.querySelector('a').href
  448. jQuery('.selected-card').removeClass('selected-card')
  449. jQuery(event.currentTarget).closest('.card').addClass('selected-card')
  450. if (target.endsWith('.pdf')) {
  451. jQuery('#main-frame')[0].removeAttribute('sandbox')
  452. } else {
  453. jQuery('#main-frame')[0].sandbox = "allow-same-origin allow-top-navigation-by-user-activation allow-scripts allow-forms"
  454. }
  455. window.location.hash = getPreviewTypeFromPath(event.currentTarget.querySelector('a'))
  456. iframe_elem.src = target
  457. })
  458. }
  459. function hideSnapshotHeader() {
  460. console.log('Collapsing Snapshot header...')
  461. jQuery('.header-toggle').text('▸')
  462. jQuery('.header-bottom').hide()
  463. jQuery('#main-frame').addClass('iframe-large')
  464. try {
  465. localStorage.setItem("archivebox-snapshot-header-visible", "false")
  466. } catch (e) {
  467. console.log('Could not use localStorage to persist header collapse state', e)
  468. }
  469. }
  470. function showSnapshotHeader() {
  471. console.log('Expanding Snapshot header...')
  472. jQuery('.header-toggle').text('▾')
  473. jQuery('.header-bottom').show()
  474. jQuery('#main-frame').removeClass('iframe-large')
  475. try {
  476. localStorage.setItem("archivebox-snapshot-header-visible", "true")
  477. } catch (e) {
  478. console.log('Could not use localStorage to persist header collapse state', e)
  479. }
  480. }
  481. function loadSnapshotHeaderState() {
  482. // collapse snapshot header if user has previously hidden it
  483. let snapshotHeaderIsExpanded = 'false'
  484. try {
  485. snapshotHeaderIsExpanded = localStorage.getItem("archivebox-snapshot-header-visible") || 'false'
  486. } catch (e) {
  487. console.log('Could not use localStorage to get header collapse state', e)
  488. }
  489. if (snapshotHeaderIsExpanded === 'false') {
  490. hideSnapshotHeader()
  491. }
  492. }
  493. function handleSnapshotHeaderToggle() {
  494. if (jQuery('.header-toggle').text().includes('▾')) {
  495. hideSnapshotHeader()
  496. } else {
  497. showSnapshotHeader()
  498. }
  499. return true
  500. }
  501. // hide header when collapse icon is clicked
  502. jQuery('.header-toggle').on('click', handleSnapshotHeaderToggle)
  503. jQuery('.header-toggle-trigger').on('click', handleSnapshotHeaderToggle)
  504. // check URL for hash e.g. #git and load relevant preview
  505. jQuery(document).ready(function() {
  506. if (window.location.hash) {
  507. for (const link of jQuery('a[target=preview]')) {
  508. console.log(link.pathname)
  509. if (getPreviewTypeFromPath(link) == window.location.hash.slice(1).toLowerCase()) {
  510. jQuery(link).closest('.card').click()
  511. jQuery(link).click()
  512. link.click()
  513. }
  514. }
  515. }
  516. loadSnapshotHeaderState()
  517. })
  518. // hide all preview iframes on small screens
  519. // if (window.innerWidth < 1091) {
  520. // jQuery('.card a[target=preview]').attr('target', '_self')
  521. // }
  522. </script>
  523. </body>
  524. </html>