Explorar el Código

Replace legacy templates for django templates

jdcaballerov hace 5 años
padre
commit
367b12ba40

+ 25 - 49
archivebox/index/html.py

@@ -1,6 +1,5 @@
 __package__ = 'archivebox.index'
 
-from string import Template
 from datetime import datetime
 from typing import List, Optional, Iterator, Mapping
 from pathlib import Path
@@ -24,14 +23,16 @@ from ..config import (
     VERSION,
     GIT_SHA,
     FOOTER_INFO,
-    ARCHIVE_DIR_NAME,
     HTML_INDEX_FILENAME,
+    STATIC_DIR_NAME,
+    ROBOTS_TXT_FILENAME,
+    FAVICON_FILENAME,
+    setup_django,
 )
 
-MAIN_INDEX_TEMPLATE = str(Path(TEMPLATES_DIR) / 'main_index.html')
-MINIMAL_INDEX_TEMPLATE = str(Path(TEMPLATES_DIR) / 'main_index_minimal.html')
-MAIN_INDEX_ROW_TEMPLATE = str(Path(TEMPLATES_DIR) / 'main_index_row.html')
-LINK_DETAILS_TEMPLATE = str(Path(TEMPLATES_DIR) / 'link_details.html')
+MAIN_INDEX_TEMPLATE = 'main_index.html'
+MINIMAL_INDEX_TEMPLATE = 'main_index_minimal.html'
+LINK_DETAILS_TEMPLATE = 'link_details.html'
 TITLE_LOADING_MSG = 'Not yet archived...'
 
 
@@ -49,54 +50,33 @@ def parse_html_main_index(out_dir: Path=OUTPUT_DIR) -> Iterator[str]:
                     yield line.split('"')[1]
     return ()
 
+@enforce_types
+def write_html_main_index(links: List[Link], out_dir: Path=OUTPUT_DIR, finished: bool=False) -> None:
+    """write the html link index to a given path"""
+
+    copy_and_overwrite(str(Path(TEMPLATES_DIR) / FAVICON_FILENAME), str(out_dir / FAVICON_FILENAME))
+    copy_and_overwrite(str(Path(TEMPLATES_DIR) / ROBOTS_TXT_FILENAME), str(out_dir / ROBOTS_TXT_FILENAME))
+    copy_and_overwrite(str(Path(TEMPLATES_DIR) / STATIC_DIR_NAME), str(out_dir / STATIC_DIR_NAME))
+
+    rendered_html = main_index_template(links, finished=finished)
+    atomic_write(str(out_dir / HTML_INDEX_FILENAME), rendered_html)
+
 
 @enforce_types
 def main_index_template(links: List[Link], template: str=MAIN_INDEX_TEMPLATE) -> str:
     """render the template for the entire main index"""
 
-    return render_legacy_template(template, {
+    return render_django_template(template, {
         'version': VERSION,
         'git_sha': GIT_SHA,
         'num_links': str(len(links)),
         'date_updated': datetime.now().strftime('%Y-%m-%d'),
         'time_updated': datetime.now().strftime('%Y-%m-%d %H:%M'),
-        'rows': '\n'.join(
-            main_index_row_template(link)
-            for link in links
-        ),
+        'links': [link._asdict(extended=True) for link in links],
         'footer_info': FOOTER_INFO,
     })
 
 
-@enforce_types
-def main_index_row_template(link: Link) -> str:
-    """render the template for an individual link row of the main index"""
-
-    from ..extractors.wget import wget_output_path
-
-    return render_legacy_template(MAIN_INDEX_ROW_TEMPLATE, {
-        **link._asdict(extended=True),
-        
-        # before pages are finished archiving, show loading msg instead of title
-        'title': htmlencode(
-            link.title
-            or (link.base_url if link.is_archived else TITLE_LOADING_MSG)
-        ),
-
-        # before pages are finished archiving, show fallback loading favicon
-        'favicon_url': (
-            str(Path(ARCHIVE_DIR_NAME) / link.timestamp / 'favicon.ico')
-            # if link['is_archived'] else 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
-        ),
-
-        # before pages are finished archiving, show the details page instead
-        'wget_url': urlencode(wget_output_path(link) or 'index.html'),
-        
-        # replace commas in tags with spaces, or file extension if it's static
-        'tags': (link.tags or '') + (' {}'.format(link.extension) if link.is_static else ''),
-    })
-
-
 ### Link Details Index
 
 @enforce_types
@@ -114,7 +94,7 @@ def link_details_template(link: Link) -> str:
 
     link_info = link._asdict(extended=True)
 
-    return render_legacy_template(LINK_DETAILS_TEMPLATE, {
+    return render_django_template(LINK_DETAILS_TEMPLATE, {
         **link_info,
         **link_info['canonical'],
         'title': htmlencode(
@@ -134,17 +114,13 @@ def link_details_template(link: Link) -> str:
         'oldest_archive_date': ts_to_date(link.oldest_archive_date),
     })
 
-
 @enforce_types
-def render_legacy_template(template_path: str, context: Mapping[str, str]) -> str:
+def render_django_template(template: str, context: Mapping[str, str]) -> str:
     """render a given html template string with the given template content"""
+    from django.template.loader import render_to_string
 
-    # will be replaced by django templates in the future
-    with open(template_path, 'r', encoding='utf-8') as template:
-        template_str = template.read()
-    return Template(template_str).substitute(**context)
-
-
+    setup_django(check_db=False)
+    return render_to_string(template, context)
 
 
 def snapshot_icons(snapshot) -> str:

+ 21 - 34
archivebox/themes/default/core/snapshot_list.html

@@ -18,40 +18,27 @@
                 <th style="width: 16vw;whitespace:nowrap;overflow-x:hidden;">Original URL</th>
             </tr>
         </thead>
-        <tbody>
-            {% for link in object_list %}
-                <tr>
-                    <td title="{{link.timestamp}}">{{link.added}}</td>
-                    <td class="title-col">
-                        {% if link.is_archived %}
-                            <a href="archive/{{link.timestamp}}/index.html"><img src="archive/{{link.timestamp}}/favicon.ico" class="link-favicon" decoding="async"></a>
-                        {% else %}
-                            <a href="archive/{{link.timestamp}}/index.html"><img src="{% static 'spinner.gif' %}" class="link-favicon" decoding="async"></a>
-                        {% endif %}
-                        <a href="archive/{{link.timestamp}}/index.html" title="{{link.title}}">
-                            <span data-title-for="{{link.url}}" data-archived="{{link.is_archived}}">{{link.title|default:'Loading...'}}</span>
-                            <small style="float:right">{{link.tags_str}}</small>
-                        </a>
-                    </td>
-                    <td>
-                        {{link.icons}}
-                    </td>
-                    <td style="text-align:left">
-                        <a href="{{link.url}}">{{link.url}}</a>
-                    </td>
-                </tr>
-            {% endfor %}
-        </tbody>
-    </table>
-    <center>
-        <span class="step-links">
-            {% if page_obj.has_previous %}
-                <a href="{% url 'public-index' %}?page=1">&laquo; first</a>
-                <a href="{% url 'public-index' %}?page={{ page_obj.previous_page_number }}">previous</a>
-            {% endif %}
-    
-            <span class="current">
-                Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
+            <tbody>
+                {% for link in object_list %}
+                    {% include 'main_index_row.html' with link=link  %}
+                {% endfor %}
+            </tbody>
+        </table>
+        <center>
+            <span class="step-links">
+                {% if page_obj.has_previous %}
+                    <a href="{% url 'public-index' %}?page=1">&laquo; first</a>
+                    <a href="{% url 'public-index' %}?page={{ page_obj.previous_page_number }}">previous</a>
+                {% endif %}
+        
+                <span class="current">
+                    Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
+                </span>
+        
+                {% if page_obj.has_next %}
+                    <a href="{% url 'public-index' %}?page={{ page_obj.next_page_number }}">next </a>
+                    <a href="{% url 'public-index' %}?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
+                {% endif %}
             </span>
     
             {% if page_obj.has_next %}

+ 488 - 0
archivebox/themes/default/link_details.html

@@ -0,0 +1,488 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <title>{{title}}</title>
+        <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
+        <style>
+            html, body {
+                width: 100%;
+                height: 100%;
+                background-color: #ddd;
+            }
+            header {
+                background-color: #aa1e55;
+                padding-bottom: 12px;
+            }
+            small {
+                font-weight: 200;
+            }
+            header a:hover {
+                text-decoration: none;
+            }
+            .header-top {
+                width: 100%;
+                height: auto;
+                min-height: 40px;
+                margin: 0px;
+                text-align: center;
+                color: white;
+                font-size: calc(11px + 0.84vw);
+                font-weight: 200;
+                padding: 4px 4px;
+                background-color: #aa1e55;
+            }
+            .nav > div {
+                min-height: 30px;
+                margin: 8px 0px;
+            }
+            .header-top a {
+                text-decoration: none;
+                color: rgba(0,0,0,0.6);
+            }
+            .header-top a:hover {
+                text-decoration: none;
+                color: rgba(0,0,0,0.9);
+            }
+            .header-top .col-lg-4 {
+                text-align: center;
+                padding-top: 4px;
+                padding-bottom: 4px;
+            }
+            .header-archivebox img {
+                display: inline-block;
+                margin-right: 3px;
+                height: 30px;
+                margin-left: 12px;
+                margin-top: -4px;
+                margin-bottom: 2px;
+            }
+            .header-archivebox img:hover {
+                opacity: 0.5;
+            }
+            .header-url small {
+                white-space: nowrap;
+                font-weight: 200;
+            }
+            .header-url img {
+                height: 20px;
+                vertical-align: -2px;
+                margin-right: 4px;
+            }
+            
+            .info-row {
+                margin-top: 2px;
+                margin-bottom: 5px;
+            }
+            .info-row .alert {
+                margin-bottom: 0px;
+            }
+            .card {
+                overflow: hidden;
+                box-shadow: 2px 3px 14px 0px rgba(0,0,0,0.02);
+                margin-top: 10px;
+            }
+            .card h4 {
+                font-size: 1.4vw;
+            }
+            .card-body {
+                font-size: 1vw;
+                padding-top: 1.2vw;
+                padding-left: 1vw;
+                padding-right: 1vw;
+                padding-bottom: 1vw;
+                line-height: 1.1;
+                word-wrap: break-word;
+                max-height: 102px;
+                overflow: hidden;
+            }
+            .card-title {
+                margin-bottom: 4px;
+            }
+            .card-img-top {
+                border: 0px;
+                padding: 0px;
+                margin: 0px;
+                overflow: hidden;
+                opacity: 0.8;
+                border-top: 1px solid rgba(0,0,0,0);
+                border-radius: 4px;
+                border-bottom: 1px solid rgba(0,0,0,0);
+                height: 430px;
+                width: 405%;
+                margin-bottom: -330px;
+                background-color: #333;
+                margin-left: -1%;
+                margin-right: -1%;
+
+                transform: scale(0.25); 
+                transform-origin: 0 0;
+            }
+            .full-page-iframe {
+                border-top: 1px solid #ddd;
+                width: 100%;
+                height: 69vh;
+                margin: 0px;
+                border: 0px;
+                border-top: 3px solid #aa1e55;
+            }
+            .card.selected-card {
+                border: 2px solid orange;
+                box-shadow: 0px -6px 13px 1px rgba(0,0,0,0.05);
+            }
+            .iframe-large {
+                height: calc(100% - 40px);
+            }
+            .pdf-frame {
+                transform: none;
+                width: 100%;
+                height: 160px;
+                margin-top: -60px;
+                margin-bottom: 0px;
+                transform: scale(1.1);
+                width: 100%;
+                margin-left: -10%;
+            }
+            img.external {
+                height: 30px;
+                margin-right: -10px;
+                padding: 3px;
+                border-radius: 4px;
+                vertical-align: middle;
+                border: 4px solid rgba(0,0,0,0);
+            }
+            img.external:hover {
+                border: 4px solid green;
+            }
+            .screenshot {
+                background-color: #333;
+                transform: none;
+                width: 100%;
+                min-height: 100px;
+                max-height: 100px;
+                margin-bottom: 0px;
+                object-fit: cover;
+                object-position: top center;
+            }
+            .header-bottom {
+                border-top: 1px solid rgba(170, 30, 85, 0.9);
+                padding-bottom: 12px;
+                border-bottom: 5px solid rgb(170, 30, 85);
+                margin-bottom: -1px;
+
+                border-radius: 4px;
+                background-color: rgba(23, 22, 22, 0.88);
+                width: 98%;
+                border: 1px solid rgba(0,0,0,0.2);
+                box-shadow: 4px 4px 4px rgba(0,0,0,0.2);
+                margin-top: 5px;
+            }
+            .header-bottom-info {
+                color: #6f6f6f;
+                padding-top: 8px;
+                padding-bottom: 13px;
+            }
+
+            .header-bottom-info > div {
+                text-align: center;
+            }
+            .header-bottom-info h5 {
+                font-size: 1.1em;
+                font-weight: 200;
+                margin-top: 3px;
+                margin-bottom: 3px;
+                color: rgba(255, 255, 255, 0.74);
+            }
+            .info-chunk {
+                width: auto;
+                display:inline-block;
+                text-align: center;
+                margin: 10px 10px;
+                vertical-align: top;
+            }
+            .info-chunk .badge {
+                margin-top: 5px;
+            }
+            .header-bottom-frames .card-title {
+                padding-bottom: 0px;
+                font-size: 1.2vw;
+                margin-bottom: 5px;
+            }
+            .header-bottom-frames .card-text {
+                font-size: 0.9em;
+            }
+
+            @media(max-width: 1092px) {
+                iframe {
+                    display: none;
+                }
+            }
+                
+
+            @media(max-width: 728px) {
+                .card h4 {
+                    font-size: 5vw;
+                }
+                .card-body {
+                    font-size: 4vw;
+                }
+                .card {
+                    margin-bottom: 5px;
+                }
+                header > h1 > a.header-url, header > h1 > a.header-archivebox {
+                    display: none;
+                }
+            }
+        </style>
+        <link rel="stylesheet" href="../../static/bootstrap.min.css">
+    </head>
+    <body>
+        <header>
+            <div class="header-top container-fluid">
+                <div class="row nav">
+                    <div class="col-lg-2" style="line-height: 64px;">
+
+                        <a href="../../index.html" class="header-archivebox" title="Go to Main Index...">
+                            <img src="../../static/archive.png" alt="Archive Icon">
+                            ArchiveBox
+                        </a>
+                    </div>
+                    <div class="col-lg-8">
+                        <img src="favicon.ico" alt="Favicon">
+                        &nbsp;&nbsp;
+                        {{title}}
+                        &nbsp;&nbsp;
+                        <a href="#" class="header-toggle">▾</a>
+                        <br/>
+                        <small>
+                            <a href="{{url}}" class="header-url" title="{{url}}">
+                                {{url_str}}
+                            </a>
+                        </small>
+                    </div>
+                </div>
+            </div>
+            <div class="header-bottom container-fluid">
+                <div class="row header-bottom-info">
+                    <div class="col-lg-4">
+                        <div title="Date bookmarked or imported" class="info-chunk">
+                            <h5>Added</h5>
+                            {{bookmarked_date}}
+                        </div>
+                        <div title="Date first archived" class="info-chunk">
+                            <h5>First Archived</h5>
+                            {{oldest_archive_date}}
+                        </div>
+                        <div title="Date last checked" class="info-chunk">
+                            <h5>Last Checked</h5>
+                            {{updated_date}}
+                        </div>
+                    </div>
+                    <div class="col-lg-4">
+                        <div class="info-chunk">
+                            <h5>Type</h5>
+                            <div class="badge badge-default">{{extension}}</div>
+                        </div>
+                        <div class="info-chunk">
+                            <h5>Tags</h5>
+                            <div class="badge badge-warning">{{tags}}</div> 
+                        </div>
+                        <div class="info-chunk">
+                            <h5>Status</h5>
+                            <div class="badge badge-{{status_color}}">{{status}}</div>
+                        </div>
+                        <div class="info-chunk">
+                            <h5>Saved</h5>
+                            ✅  {{num_outputs}}
+                        </div>
+                        <div class="info-chunk">
+                            <h5>Errors</h5>
+                            ❌  {{num_failures}}
+                        </div>
+                        <div class="info-chunk">
+                            <h5>Size</h5>
+                            {{size}}
+                        </div>
+                    </div>
+                    <div class="col-lg-4">
+                        <div class="info-chunk">
+                            <h5>🗃 Files</h5>
+                            <a href="index.json" title="JSON summary of archived link.">JSON</a> | 
+                            <a href="warc/" title="Any WARC archives for the page">WARC</a> | 
+                            <a href="media/" title="Audio, Video, and Subtitle files.">Media</a> | 
+                            <a href="git/" title="Any git repos at the url">Git</a> | 
+                            <a href="favicon.ico" title="Any git repos at the url">Favicon</a> | 
+                            <a href="." title="Webserver-provided index of files directory.">See all...</a>
+                        </div>
+                    </div>
+                </div>
+                <div class="row header-bottom-frames">
+                    <div class="col-lg-3">
+                        <div class="card selected-card">
+                          <iframe class="card-img-top" src="{{archive_url}}" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
+                          <div class="card-body">
+                                <a href="{{archive_url}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{archive_url}}" target="preview"><h4 class="card-title">Wget &gt; WARC</h4></a>
+                                <p class="card-text">archive/{{domain}}</p>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <iframe class="card-img-top" src="{{singlefile_path}}" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
+                            <div class="card-body">
+                                <a href="{{singlefile_path}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{singlefile_path}}" target="preview"><h4 class="card-title">Chrome &gt; SingleFile</h4></a>
+                                <p class="card-text">archive/singlefile.html</p>
+                          </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <iframe class="card-img-top" src="{{archive_org_path}}" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
+                            <div class="card-body">
+                                <a href="{{archive_org_path}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{archive_org_path}}" target="preview"><h4 class="card-title">Archive.Org</h4></a>
+                                <p class="card-text">web.archive.org/web/...</p>
+                          </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <iframe class="card-img-top" src="{{url}}" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
+                            <div class="card-body">
+                                <a href="{{url}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{url}}" target="preview"><h4 class="card-title">Original</h4></a>
+                                <p class="card-text">{{domain}}</p>
+                          </div>
+                        </div>
+                    </div>
+                    <br/>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <iframe class="card-img-top pdf-frame" src="{{pdf_path}}" scrolling="no"></iframe>
+                            <div class="card-body">
+                                <a href="{{pdf_path}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{pdf_path}}" target="preview" id="pdf-btn"><h4 class="card-title">Chrome &gt; PDF</h4></a>
+                                <p class="card-text">archive/output.pdf</p>
+                          </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <img class="card-img-top screenshot" src="{{screenshot_path}}"></iframe>
+                            <div class="card-body">
+                                <a href="{{screenshot_path}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{screenshot_path}}" target="preview"><h4 class="card-title">Chrome &gt; Screenshot</h4></a>
+                                <p class="card-text">archive/screenshot.png</p>
+                          </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <iframe class="card-img-top" src="{{dom_path}}" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
+                            <div class="card-body">
+                                <a href="{{dom_path}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{dom_path}}" target="preview"><h4 class="card-title">Chrome &gt; HTML</h4></a>
+                                <p class="card-text">archive/output.html</p>
+                          </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <iframe class="card-img-top" src="{{readability_path}}" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
+                            <div class="card-body">
+                                <a href="{{readability_path}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{readability_path}}" target="preview"><h4 class="card-title">Readability</h4></a>
+                                <p class="card-text">archive/readability/...</p>
+                          </div>
+                        </div>
+                    </div>
+                    <br/>
+                    <div class="col-lg-3">
+                        <div class="card">
+                            <iframe class="card-img-top" src="{{mercury_path}}" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
+                            <div class="card-body">
+                                <a href="{{mercury_path}}" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
+                                    <img src="../../static/external.png" class="external"/>
+                                </a>
+                                <a href="{{mercury_path}}" target="preview"><h4 class="card-title">mercury</h4></a>
+                                <p class="card-text">archive/mercury/...</p>
+                          </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </header>
+        <iframe sandbox="allow-same-origin allow-scripts allow-forms" class="full-page-iframe" src="{{archive_url}}" name="preview"></iframe>
+    
+        <script
+              src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
+              integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
+              crossorigin="anonymous"></script>
+        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
+
+        <script>
+            // show selected file in iframe when preview card is clicked
+            jQuery('.card').on('click', function(e) {
+                jQuery('.selected-card').removeClass('selected-card')
+                jQuery(e.target).closest('.card').addClass('selected-card')
+            })
+            jQuery('.card a[target=preview]').on('click', function(e) {
+                if (e.currentTarget.href.endsWith('.pdf')) {
+                    jQuery('.full-page-iframe')[0].removeAttribute('sandbox')
+                } else {
+                    jQuery('.full-page-iframe')[0].sandbox = "allow-same-origin allow-scripts allow-forms"
+                }
+                return true
+            })
+
+            // un-sandbox iframes showing pdfs (required to display pdf viewer)
+            jQuery('iframe').map(function() {
+                if (this.src.endsWith('.pdf')) {
+                    this.removeAttribute('sandbox')
+                    this.src = this.src
+                }
+            })
+
+            // hide header when collapse icon is clicked
+            jQuery('.header-toggle').on('click', function() {
+                if (jQuery('.header-toggle').text().includes('▾')) {
+                    jQuery('.header-toggle').text('▸')
+                    jQuery('.header-bottom').hide()
+                    jQuery('.full-page-iframe').addClass('iframe-large')
+                } else {
+                    jQuery('.header-toggle').text('▾')
+                    jQuery('.header-bottom').show()
+                    jQuery('.full-page-iframe').removeClass('iframe-large')
+                }
+                return true
+            })
+
+            // hide all preview iframes on small screens
+            if (window.innerWidth < 1091) {
+                jQuery('.card a[target=preview]').attr('target', '_self')
+            }
+
+            var pdf_frame = document.querySelector('.pdf-frame');
+            pdf_frame.onload = function () {
+                pdf_frame.contentWindow.scrollTo(0, 400);
+            }
+        </script>
+    </body>
+</html>

+ 2 - 21
archivebox/themes/default/main_index.html

@@ -233,26 +233,7 @@
             </thead>
             <tbody>
                 {% for link in links %}
-                    <tr>
-                        <td title="{{link.timestamp}}">{{link.bookmarked_date}}</td>
-                        <td class="title-col">
-                            {% if link.is_archived %}
-                                <a href="archive/{{link.timestamp}}/index.html"><img src="archive/{{link.timestamp}}/favicon.ico" class="link-favicon" decoding="async"></a>
-                            {% else %}
-                                <a href="archive/{{link.timestamp}}/index.html"><img src="{% static 'spinner.gif' %}" class="link-favicon" decoding="async"></a>
-                            {% endif %}
-                            <a href="archive/{{link.timestamp}}/{{link.canonical_outputs.wget_path}}" title="{{link.title}}">
-                                <span data-archived="{{link.is_archived}}">{{link.title|default:'Loading...'}}</span>
-                                <small style="float:right">{{link.tags|default:''}}</small>
-                            </a>
-                        </td>
-                        <td>
-                            <a href="archive/{{link.timestamp}}/index.html">📄 
-                                <span title="Number of extractor outputs present">{{link.num_outputs}}</span>
-                            </a>
-                        </td>
-                        <td style="text-align:left"><a href="{{link.url}}">{{link.url}}</a></td>
-                    </tr>
+                   {% include 'main_index_row.html' with link=link %}
                 {% endfor %}
             </tbody>
         </table>
@@ -261,7 +242,7 @@
             <center>
                 <small>
                     Archive created using <a href="https://github.com/ArchiveBox/ArchiveBox" title="Github">ArchiveBox</a>
-                    version <a href="https://github.com/ArchiveBox/ArchiveBox/tree/v{{VERSION}}" title="Git commit">v{{VERSION}}</a> &nbsp; | &nbsp; 
+                    version <a href="https://github.com/ArchiveBox/ArchiveBox/tree/v{{version}}" title="Git commit">v{{version}}</a> &nbsp; | &nbsp; 
                     Download index as <a href="index.json" title="JSON summary of archived links.">JSON</a>
                     <br/><br/>
                     {{FOOTER_INFO}}

+ 24 - 0
archivebox/themes/default/main_index_minimal.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <title>Archived Sites</title>
+        <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
+    </head>
+    <body data-status="{{status}}">
+        <table id="table-bookmarks">
+            <thead>
+                <tr class="thead-tr">
+                    <th style="width: 100px;">Bookmarked</th>
+                    <th style="width: 26vw;">Saved Link ({{num_links}})</th>
+                    <th style="width: 50px">Files</th>
+                    <th style="width: 16vw;whitespace:nowrap;overflow-x:hidden;">Original URL</th>
+                </tr>
+            </thead>
+            <tbody>
+                {% for link in links %}
+                    {% include "main_index_row.html" with link=link %} 
+                {% endfor %}
+            </tbody>
+        </table>
+    </body>
+</html>

+ 22 - 0
archivebox/themes/default/main_index_row.html

@@ -0,0 +1,22 @@
+{% load static %}
+
+<tr>
+    <td title="{{link.timestamp}}"> {% if link.bookmarked_date  %} {{ link.bookmarked_date }} {% else %} {{ link.added }} {% endif %} </td>
+    <td class="title-col">
+        {% if link.is_archived %}
+            <a href="archive/{{link.timestamp}}/index.html"><img src="archive/{{link.timestamp}}/favicon.ico" class="link-favicon" decoding="async"></a>
+        {% else %}
+            <a href="archive/{{link.timestamp}}/index.html"><img src="{% static 'spinner.gif' %}" class="link-favicon" decoding="async"></a>
+        {% endif %}
+            <a href="archive/{{link.timestamp}}/{{link.canonical_outputs.wget_path}}" title="{{link.title}}">
+                <span data-title-for="{{link.url}}" data-archived="{{link.is_archived}}">{{link.title|default:'Loading...'}}</span>
+                <small style="float:right">{% if link.tags_str != None %} {{link.tags_str|default:''}} {% else %} {{ link.tags|default:'' }} {% endif %}</small>
+            </a>
+    </td>
+    <td>
+        <a href="archive/{{link.timestamp}}/index.html">📄 
+            <span data-number-for="{{link.url}}" title="Fetching any missing files...">{% if link.icons  %} {{link.icons}} {% else %} {{ link.num_outputs}} {% endif %}<img src="{% static 'spinner.gif' %}" class="files-spinner" decoding="async"/></span>
+        </a>
+    </td>
+   <td style="text-align:left"><a href="{{link.url}}">{{link.url}}</a></td>
+</tr>