Skip to content

Commit

Permalink
files_panel: fix internal links and browser history.
Browse files Browse the repository at this point in the history
In the files section of a preview or published project, clicking a
link to a subdirectory or parent directory should behave like a normal
link:

- The browser's window.location should be updated, so that you can
  bookmark it.

- The former location should be saved in the browser's history stack,
  so that you can navigate with Back and Forward buttons.

Doing these things requires calling history.pushState when we navigate
to a new location, and setting window.onpopstate to a handler function
that restores the former location.

Moreover, it is cleaner and safer to avoid using inline scripts.

When a directory link is clicked, we will asynchronously load the
directory contents; once they're loaded, call pushState to update the
page URL and then parse the new directory HTML.  (pushState must be
called first so that relative links are handled correctly.)

When the back button is clicked ("onpopstate" function is called), we
likewise load the previous HTML directory contents, but in this case
call replaceState instead of pushState (again, we need to update the
page URL before parsing the HTML.)  (You might think this replaceState
is unnecessary, but because the HTML is loaded asynchronously, there
could be multiple attempts in flight to change the page location, so
it's better to keep the DOM and page URL and history state all in sync
with each other.)

To avoid overly complicated transition logic, the existing
preview_files_panel and published_files_panel views are kept as-is.
Only when somebody loads the new project_preview page or
published_project page will they use the new script, and only when
using the new script (which adds a "v=2" query parameter) will
preview_files_panel and published_files_panel return the new
inline-script-free responses.

Eventually, preview_files_panel and published_files_panel can be
changed to return an error if the v=2 parameter is missing, and the
old files_panel.html can be removed.
  • Loading branch information
Benjamin Moody committed Oct 16, 2023
1 parent 9f63ec0 commit b58038e
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 4 deletions.
38 changes: 38 additions & 0 deletions physionet-django/project/static/project/js/dynamic-files-panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
(function() {
'use strict';

var panel = $('#files-panel');
var cur_dir = panel.find('[data-dfp-cur-dir]').data('dfp-cur-dir');
var panel_url = panel.find('[data-dfp-panel-url]').data('dfp-panel-url');

function navigateDir(subdir, page_url, push_history) {
$.ajax({
type: 'GET',
url: panel_url,
data: {'subdir': subdir, 'v': '2'},
success: function(result) {
if (push_history)
history.pushState(subdir, '', page_url);
else
history.replaceState(subdir, '', page_url);
panel.html(result);
setClickHandlers();
},
});
}

function setClickHandlers() {
panel.find('a[data-dfp-dir]').click(function(event) {
navigateDir($(this).data('dfp-dir'), this.href, true);
event.preventDefault();
});
}
setClickHandlers();

window.onpopstate = function(event) {
if (event.state !== null) {
navigateDir(event.state, window.location, false);
}
};
history.replaceState(cur_dir, '');
})();
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{# Note: This template is obsolescent. Use files_panel_v2 instead. #}
<div class="card-header">
Folder Navigation:
{% spaceless %}
Expand Down
75 changes: 75 additions & 0 deletions physionet-django/project/templates/project/files_panel_v2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{# Note: This template is used together with dynamic-files-panel.js. #}
<div class="card-header"
data-dfp-panel-url="{{ files_panel_url }}"
data-dfp-cur-dir="{{ subdir }}">
Folder Navigation:
{% spaceless %}
<span class="dir-breadcrumbs">
{% for breadcrumb in dir_breadcrumbs %}
{% if forloop.counter == dir_breadcrumbs|length %}
<span class="dir-breadcrumb-self">{{ breadcrumb.name }}</span>
{% else %}
<a href="{{ breadcrumb.rel_path }}#files-panel"
data-dfp-dir="{{ breadcrumb.full_subdir }}"
class="dir-breadcrumb-up">{{ breadcrumb.name }}</a>
<span class="dir-breadcrumb-sep">/</span>
{% endif %}
{% endfor %}
</span>
{% endspaceless %}
</div>
{% if file_error %}
<div class="card-body">
<div class="alert alert-danger" role="alert">
{{ file_error|safe }}
</div>
</div>
{% else %}
{% if file_warning %}
<div class="card-body">
<div class="alert alert-warning" role="alert">
{{ file_warning|safe }}
</div>
</div>
{% endif %}
<table class="files-panel">
<col class="files-panel-name"></col>
<col class="files-panel-size"></col>
<col class="files-panel-date"></col>
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Modified</th>
</tr>
</thead>
<tbody>
{% if subdir %}
<tr class="parentdir">
<td><a href="../#files-panel" data-dfp-dir="{{ parent_dir }}">Parent Directory</a></td>
<td></td>
<td></td>
</tr>
{% endif %}
{% for dir in display_dirs %}
<tr class="subdir">
<td><a href="{{ dir.name }}/#files-panel" data-dfp-dir="{{ dir.full_subdir }}">{{ dir.name }}</a></td>
<td></td>
<td></td>
</tr>
{% endfor %}
{% for file in display_files %}
<tr>
<td><a href="{{ file.name }}">{{ file.name }}</a>
<a class="download" href="{{ file.download_url }}"
title="Download {{ file.name }}">
<span class="visually-hidden">(download)</span>
</a>
</td>
<td>{{ file.size }}</td>
<td>{{ file.last_modified }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
Original file line number Diff line number Diff line change
Expand Up @@ -236,14 +236,15 @@ <h2>Files</h2>
{% endif %}
{% endif %}
<div id="files-panel" class="card">
{% include "project/files_panel.html" %}
{% include "project/files_panel_v2.html" %}
</div>
<br>

</div>
{% endblock %}

{% block local_js_bottom %}
<script src="{% static 'project/js/dynamic-files-panel.js' %}"></script>
<script src="{% static 'custom/js/enable-popover.js' %}"></script>
<script>hljs.initHighlightingOnLoad();</script>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ <h5>Access the files</h5>
{% endif %}

<div id="files-panel" class="card">
{% include "project/files_panel.html" %}
{% include "project/files_panel_v2.html" %}
</div>
{% else %}
{% include "project/published_project_denied_downloads.html" %}
Expand Down Expand Up @@ -472,6 +472,7 @@ <h5>Access the files</h5>
{% endblock %}

{% block local_js_bottom %}
<script src="{% static 'project/js/dynamic-files-panel.js' %}"></script>
<script src="{% static 'custom/js/enable-popover.js' %}"></script>
<script>hljs.initHighlightingOnLoad();</script>
{% endblock %}
Expand Down
14 changes: 12 additions & 2 deletions physionet-django/project/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,12 @@ def preview_files_panel(request, project_slug, **kwargs):
file_warning = get_project_file_warning(display_files, display_dirs,
subdir)

return render(request, 'project/files_panel.html',
if request.GET.get('v', '1') == '1':
template = 'project/files_panel.html'
else:
template = 'project/files_panel_v2.html'

return render(request, template,
{'project':project, 'subdir':subdir, 'file_error':file_error,
'dir_breadcrumbs':dir_breadcrumbs, 'parent_dir':parent_dir,
'display_files':display_files, 'display_dirs':display_dirs,
Expand Down Expand Up @@ -1574,7 +1579,12 @@ def published_files_panel(request, project_slug, version):
files_panel_url = reverse('published_files_panel',
args=(project.slug, project.version))

return render(request, 'project/files_panel.html',
if request.GET.get('v', '1') == '1':
template = 'project/files_panel.html'
else:
template = 'project/files_panel_v2.html'

return render(request, template,
{'project':project, 'subdir':subdir,
'dir_breadcrumbs':dir_breadcrumbs, 'parent_dir':parent_dir,
'display_files':display_files, 'display_dirs':display_dirs,
Expand Down

0 comments on commit b58038e

Please sign in to comment.