Browse Source

Keep current application state in URL hash

This commit makes it so that all application state is first written
to the URL hash and then evaluated from there. This has the
benefit that the URL hash is the ground truth for the application
state and can therefore be copied and sent to others.
2xB 9 months ago
parent
commit
808f96d8c5
1 changed files with 144 additions and 77 deletions
  1. 144 77
      viewer/archive/archiveviewer.js

+ 144 - 77
viewer/archive/archiveviewer.js

@@ -38,7 +38,6 @@ function hide_iframes(text) {
 }
 }
 
 
 function date_to_iso(value) {
 function date_to_iso(value) {
-    console.log(value)
     if ((value + "").includes("-")) {
     if ((value + "").includes("-")) {
         return value
         return value
     }
     }
@@ -58,6 +57,12 @@ class ArchiveView {
         this.url = url
         this.url = url
         this.project_id = project_id
         this.project_id = project_id
         this.branch = branch
         this.branch = branch
+
+        this.next_commit = ""
+        this.current_commit = ""
+
+        this.last_from = ""
+        this.last_to = ""
         
         
         this.maindiv = maindiv
         this.maindiv = maindiv
         
         
@@ -79,7 +84,7 @@ class ArchiveView {
         option_range.innerHTML = "Range"
         option_range.innerHTML = "Range"
         this.selecttype.appendChild(option_range)
         this.selecttype.appendChild(option_range)
         
         
-        this.selecttype.addEventListener("input",(() => this.on_selecttype_selected()))
+        this.selecttype.addEventListener("input",(() => this.update_hash()))
         
         
         // ---
         // ---
         
         
@@ -93,7 +98,7 @@ class ArchiveView {
         var maxdate = new Date().toLocaleDateString('en-ca')
         var maxdate = new Date().toLocaleDateString('en-ca')
         this.dateselector.value = ""
         this.dateselector.value = ""
         this.dateselector.max = maxdate
         this.dateselector.max = maxdate
-        this.dateselector.addEventListener("input",((event) => this.on_date_selected(event)))
+        this.dateselector.addEventListener("input",(() => this.update_hash()))
         
         
         outer_span.appendChild(this.dateselector)
         outer_span.appendChild(this.dateselector)
         
         
@@ -109,7 +114,7 @@ class ArchiveView {
         this.range_from.value = ""
         this.range_from.value = ""
         this.range_from.min = mindate + "T00:00"
         this.range_from.min = mindate + "T00:00"
         this.range_from.max = maxdate + "T23:59"
         this.range_from.max = maxdate + "T23:59"
-        this.range_from.addEventListener("input",(() => this.on_range_from_selected()))
+        this.range_from.addEventListener("input",(() => this.update_hash()))
         
         
         this.rangespan.appendChild(this.range_from)
         this.rangespan.appendChild(this.range_from)
         
         
@@ -123,7 +128,7 @@ class ArchiveView {
         
         
         this.range_to.value = ""
         this.range_to.value = ""
         this.range_to.max = maxdate + "T23:59"
         this.range_to.max = maxdate + "T23:59"
-        this.range_to.addEventListener("input",(() => this.on_range_to_selected()))
+        this.range_to.addEventListener("input",(() => this.update_hash()))
 
 
         this.rangespan.appendChild(this.range_to)
         this.rangespan.appendChild(this.range_to)
         
         
@@ -152,7 +157,7 @@ class ArchiveView {
         
         
         this.index = index
         this.index = index
         
         
-        const blobContent = new Blob([`
+        this.defaultcontent = new Blob([`
         <!doctype html><html><head>
         <!doctype html><html><head>
         <style>
         <style>
         body {
         body {
@@ -177,69 +182,149 @@ class ArchiveView {
             </body>
             </body>
         </html>
         </html>
         `], {type: "text/html"})
         `], {type: "text/html"})
-        this.content.src = URL.createObjectURL(blobContent)
+        this.content.src = URL.createObjectURL(this.defaultcontent)
         
         
         window.addEventListener('hashchange', (event) => this.on_hash_changed(event))
         window.addEventListener('hashchange', (event) => this.on_hash_changed(event))
         
         
         // Updates
         // Updates
         this.on_hash_changed(null)
         this.on_hash_changed(null)
-        this.on_selecttype_selected() // Update visibilities
+        this.update_hash() // Update visibilities
         this.load_pages() // Update version list
         this.load_pages() // Update version list
     }
     }
+
+    update_hash() {
+        var old_hash = window.location.hash
+        var new_hash = ""
+
+        if (this.selecttype.value == "date") {
+            new_hash = "#date=" + this.dateselector.value + "&commit=" + this.next_commit
+        }
+        else {
+            new_hash = "#from=" + this.range_from.value + "&to=" + this.range_to.value + "&commit=" + this.next_commit
+        }
+
+        if (new_hash == old_hash) {
+            return false
+        }
+
+        window.location.hash = new_hash
+        return true
+    }
     
     
     on_hash_changed(event) {
     on_hash_changed(event) {
         var remaining_hash = window.location.hash.substring(1).split("&")
         var remaining_hash = window.location.hash.substring(1).split("&")
         
         
         var date_from = null
         var date_from = null
         var length = null
         var length = null
-        var to = null
-        
-        var provided_full_range = false;
+        var date_to = null
+        var date = null
+
         while (remaining_hash.length > 0) {
         while (remaining_hash.length > 0) {
             var current_part = remaining_hash.pop()
             var current_part = remaining_hash.pop()
             
             
             var [key, value] = current_part.split("=")
             var [key, value] = current_part.split("=")
             
             
             if (key == "from") {
             if (key == "from") {
-                date_from = value
+                date_from = ""
+
+                if (value) {
+                    date_from = date_to_iso(value)
+                }
             }
             }
             else if (key == "length") {
             else if (key == "length") {
                 length = value
                 length = value
             }
             }
             else if (key == "to") {
             else if (key == "to") {
-                to = value
+                date_to = ""
+
+                if (value) {
+                    date_to = date_to_iso(value)
+                }
+            }
+            else if (key == "date"){
+                date = ""
+
+                if (value) {
+                    date = date_to_iso(value)
+                }
+            }
+            else if (key == "commit"){
+                this.next_commit = value
             }
             }
         }
         }
-        
-        if (date_from != null) {
-            this.selecttype.value = "range"
-            var from_iso = date_to_iso(date_from)
-            console.log(date_from, from_iso)
-            this.range_from.value = from_iso
-            this.on_range_from_selected()
+
+        if (date) {
+            this.dateselector.value = date
             
             
-            if (length != null) {
-                // Only works when given UNIX timestamps
-                console.log(from_iso)
-                console.log(from_iso,date_to_iso(date_to_unix(from_iso)))
-                this.range_to.value = date_to_iso(date_to_unix(from_iso)+parseInt(length))
-                provided_full_range = true
+            this.selecttype.value = "date"
+
+            // Convert date to range so one only needs to work with ranges
+            if (this.dateselector.value) {
+                var date_from_tmp = this.dateselector.value
+                date_from = date_from_tmp + "T00:00"
+                
+                var date_to_obj = new Date(date_from_tmp)
+                date_to_obj.setDate(date_to_obj.getDate() + 1)
+                // Works also across months, years
+                date_to = date_to_obj.toJSON().slice(0, 10) + "T00:00"
             }
             }
         }
         }
-        
-        if (to != null) {
-            if (date_from != nullptr) {
-                provided_full_range = true
-            }
+
+        if (date_from) {
+            this.range_from.value = date_from
             
             
-            this.selecttype.value = "range"
-            this.on_selecttype_selected() // Update visibilities
-            this.range_to.value = date_to_iso(to)
+            this.range_to.min = date_from
+            this.range_to.disabled = (date_from == "")
+
+            if (!date) {
+                this.selecttype.value = "range"
+            }
+        }
+
+        if (date_to) {
+            this.range_to.value = date_to
+        }
+
+        if (length) {
+            this.range_to.value = date_to_iso(date_to_unix(date_from)+parseInt(length))
+        }
+
+        if (this.update_hash()) {
+            return // Triggered another hash change
+        }
+        
+        // Visibilities
+        var is_date = this.selecttype.value == "date"
+        this.dateselector.hidden = !is_date
+        this.rangespan.hidden = is_date
+
+        if (!is_date) {
+            this.dateselector.value = ""
+        }
+
+        if (!date_from) {
+            return
+        }
+
+        if (!date_to) {
+            return
         }
         }
+
+        if (this.commitsSelect.length && date_from == this.last_from && date_to == this.last_to) {
+            this.commitsSelect.value = this.next_commit
+            this.on_commit_selected()
+            return // List didn't change
+        }
+
+        this.last_from = date_from
+        this.last_to = date_to
         
         
-        if (provided_full_range) {
-            this.load_pages(true)
+        if (this.next_commit) {
+            this.load_pages(false)
+            return
         }
         }
+        
+        this.load_pages(true)
     }
     }
     
     
     add_commits(commits, open_first) {
     add_commits(commits, open_first) {
@@ -258,47 +343,25 @@ class ArchiveView {
         })
         })
     }
     }
     
     
-    on_selecttype_selected() {
-        this.dateselector.value = ""
-        
-        var is_date = this.selecttype.value == "date"
-        
-        this.dateselector.hidden = !is_date
-        this.rangespan.hidden = is_date
-    }
-    
-    on_date_selected(event) {
-        if (!event.target.value)
-            return
-        var date_from = event.target.value
-        
-        var date_to_obj = new Date(date_from)
-        date_to_obj.setDate(date_to_obj.getDate() + 1)
-          // Works also across months, years
-        var date_to = date_to_obj.toJSON().slice(0, 10);
-        
-        this.range_from.value = date_from + "T00:00"
-        this.on_range_from_selected()
-        this.range_to.value = date_to + "T00:00"
-        this.on_range_to_selected()
-    }
-    
-    on_range_from_selected() {
-        var val = this.range_from.value
-        this.range_to.min = val
-        this.range_to.disabled = (val == "")
-        
-        this.load_pages()
-    }
-    
-    on_range_to_selected() {
-        this.load_pages()
-    }
-    
     on_commit_selected() {
     on_commit_selected() {
-        var sha = this.commitsSelect.value
+        if (!this.commitsSelect.value) {
+            this.content.contentWindow.location.replace(URL.createObjectURL(this.defaultcontent))
+            this.next_commit = ""
+            this.update_hash()
+            return
+        }
+
+        this.next_commit = this.commitsSelect.value
+        this.update_hash()
+
+        if (this.next_commit == this.current_commit) {
+            return // Nothing changed
+        }
+
+        this.current_commit = this.next_commit
+
         var url_before = 'https://' + this.url + '/api/v4/projects/' + this.project_id + '/repository/files/' + encodeURIComponent(this.page_path + "/")
         var url_before = 'https://' + this.url + '/api/v4/projects/' + this.project_id + '/repository/files/' + encodeURIComponent(this.page_path + "/")
-        var url_after = '/raw?ref=' + sha + '&private_token=' + this.token
+        var url_after = '/raw?ref=' + this.next_commit + '&private_token=' + this.token
         var url = url_before + encodeURIComponent(this.index) + url_after
         var url = url_before + encodeURIComponent(this.index) + url_after
         fetch(url)
         fetch(url)
         .then(response => response.text())
         .then(response => response.text())
@@ -306,9 +369,8 @@ class ArchiveView {
             var cleaned_text = hide_iframes(replace_urls(text, url_before, url_after))
             var cleaned_text = hide_iframes(replace_urls(text, url_before, url_after))
             
             
             const blobContent = new Blob([cleaned_text], {type: "text/html"})
             const blobContent = new Blob([cleaned_text], {type: "text/html"})
-            this.content.src = URL.createObjectURL(blobContent)
+            this.content.contentWindow.location.replace(URL.createObjectURL(blobContent)) // Don't affect browser history by content change (as opposed to this.content.src = ...)
         })
         })
-
     }
     }
     
     
     load_pages(open_first=false) {
     load_pages(open_first=false) {
@@ -356,6 +418,11 @@ class ArchiveView {
             if (length > 0) {
             if (length > 0) {
                 this.load_pages_recursive(page+1, date_from, date_to, false)
                 this.load_pages_recursive(page+1, date_from, date_to, false)
             }
             }
+            else {
+                // Loading done, update commit
+                this.commitsSelect.value = this.next_commit
+                this.on_commit_selected()
+            }
         })
         })
     }
     }
 }
 }