archiveviewer.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * Git HTML Archive Viewer
  3. *
  4. * Benedikt Bieringer <benedikt.b@wwu.de>
  5. * https://nuserv.uni-muenster.de:8443/bbieringer/statusarchiveviewer
  6. */
  7. class ArchiveCommit {
  8. constructor(message, time, sha) {
  9. this.message = message
  10. this.time = time
  11. this.sha = sha
  12. }
  13. }
  14. function replace_urls(text, url_before, url_after) {
  15. for(const key of ["src=\""]) {
  16. segments = text.split(key)
  17. result_text = segments[0]
  18. for(const segment of segments.slice(1)) {
  19. var index = segment.indexOf('"')
  20. var first = segment.substring(0, index)
  21. var second = segment.substring(index)
  22. var new_url = url_before + encodeURIComponent(first) + url_after
  23. result_text += key + new_url + second
  24. }
  25. text = result_text
  26. }
  27. return text
  28. }
  29. function date_to_iso(value) {
  30. console.log(value)
  31. if ((value + "").includes("-")) {
  32. return value
  33. }
  34. var date = new Date(parseInt(value)*1000)
  35. return date.toISOString().substring(0,16)
  36. }
  37. function date_to_unix(value) {
  38. return parseInt(new Date(value + ":00+00:00").getTime() / 1000)
  39. }
  40. class ArchiveView {
  41. constructor(mindate, selecttype, dateselector, range_from, range_to, rangespan, commitsSelect, content, token, url, project_id, branch, page_path, index) {
  42. this.token = token
  43. this.url = url
  44. this.project_id = project_id
  45. this.branch = branch
  46. // Alternative, doesn't allow selecting today in first two hours of day:
  47. // var maxdate = new Date().toISOString().split("T")[0]
  48. var maxdate = new Date().toLocaleDateString('en-ca')
  49. this.dateselector = dateselector
  50. this.dateselector.value = ""
  51. this.dateselector.max = maxdate
  52. this.dateselector.addEventListener("input",((event) => this.on_date_selected(event)))
  53. this.range_from = range_from
  54. this.range_from.value = ""
  55. this.range_from.min = mindate + "T00:00"
  56. this.range_from.max = maxdate + "T23:59"
  57. this.range_from.addEventListener("input",(() => this.on_range_from_selected()))
  58. this.range_to = range_to
  59. this.range_to.value = ""
  60. this.range_to.max = maxdate + "T23:59"
  61. this.range_to.addEventListener("input",(() => this.on_range_to_selected()))
  62. this.rangespan = rangespan
  63. this.selecttype = selecttype
  64. this.selecttype.addEventListener("input",(() => this.on_selecttype_selected()))
  65. this.commitsSelect = commitsSelect
  66. this.commitsSelect.addEventListener("input",(() => this.on_commit_selected()))
  67. this.content = content
  68. this.page_path = page_path
  69. this.index = index
  70. const blobContent = new Blob([`
  71. <!doctype html><html><head>
  72. <style>
  73. body {
  74. min-height: 100vh;
  75. display: flex;
  76. align-items: center;
  77. justify-content: space-around;
  78. }
  79. h1,h2 {
  80. display: flex;
  81. align-items: center;
  82. justify-content: center;
  83. }
  84. </style>
  85. </head>
  86. <body style="background:#aaa; color:#fff;">
  87. <div>
  88. <h1>Welcome to the archive viewer!</h1>
  89. <h2>Select a date and version to start.</h2>
  90. </div>
  91. </body>
  92. </html>
  93. `], {type: "text/html"})
  94. this.content.src = URL.createObjectURL(blobContent)
  95. window.addEventListener('hashchange', (event) => this.on_hash_changed(event))
  96. // Updates
  97. this.on_hash_changed(null)
  98. this.on_selecttype_selected() // Update visibilities
  99. this.load_pages() // Update version list
  100. }
  101. on_hash_changed(event) {
  102. var remaining_hash = window.location.hash.substring(1).split("&")
  103. var date_from = null
  104. var length = null
  105. var to = null
  106. var provided_full_range = false;
  107. while (remaining_hash.length > 0) {
  108. var current_part = remaining_hash.pop()
  109. var [key, value] = current_part.split("=")
  110. if (key == "from") {
  111. date_from = value
  112. }
  113. else if (key == "length") {
  114. length = value
  115. }
  116. else if (key == "to") {
  117. to = value
  118. }
  119. }
  120. if (date_from != null) {
  121. this.selecttype.value = "range"
  122. var from_iso = date_to_iso(date_from)
  123. console.log(date_from, from_iso)
  124. this.range_from.value = from_iso
  125. this.on_range_from_selected()
  126. if (length != null) {
  127. // Only works when given UNIX timestamps
  128. console.log(from_iso)
  129. console.log(from_iso,date_to_iso(date_to_unix(from_iso)))
  130. this.range_to.value = date_to_iso(date_to_unix(from_iso)+parseInt(length))
  131. provided_full_range = true
  132. }
  133. }
  134. if (to != null) {
  135. if (date_from != nullptr) {
  136. provided_full_range = true
  137. }
  138. this.selecttype.value = "range"
  139. this.on_selecttype_selected() // Update visibilities
  140. this.range_to.value = date_to_iso(to)
  141. }
  142. if (provided_full_range) {
  143. this.load_pages(true)
  144. }
  145. }
  146. add_commits(commits, open_first) {
  147. var to_open = open_first
  148. commits.map(commit => {
  149. var elem = document.createElement("option")
  150. elem.innerHTML = commit.message
  151. elem.value = commit.sha
  152. this.commitsSelect.appendChild(elem)
  153. if (to_open) {
  154. this.commitsSelect.value = elem.value
  155. this.on_commit_selected()
  156. to_open = false
  157. }
  158. })
  159. }
  160. on_selecttype_selected() {
  161. this.dateselector.value = ""
  162. var is_date = this.selecttype.value == "date"
  163. this.dateselector.hidden = !is_date
  164. this.rangespan.hidden = is_date
  165. }
  166. on_date_selected(event) {
  167. if (!event.target.value)
  168. return
  169. var date_from = event.target.value
  170. var date_to_obj = new Date(date_from)
  171. date_to_obj.setDate(date_to_obj.getDate() + 1)
  172. // Works also across months, years
  173. var date_to = date_to_obj.toLocaleDateString('en-ca')
  174. this.range_from.value = date_from + "T00:00"
  175. this.range_to.value = date_to + "T00:00"
  176. this.load_pages()
  177. }
  178. on_range_from_selected() {
  179. var val = this.range_from.value
  180. this.range_to.min = val
  181. this.range_to.disabled = (val == "")
  182. this.load_pages()
  183. }
  184. on_range_to_selected() {
  185. this.load_pages()
  186. }
  187. on_commit_selected() {
  188. var sha = this.commitsSelect.value
  189. var url_before = 'https://' + this.url + '/api/v4/projects/' + this.project_id + '/repository/files/' + encodeURIComponent(this.page_path + "/")
  190. var url_after = '/raw?ref=' + sha + '&private_token=' + this.token
  191. var url = url_before + encodeURIComponent(this.index) + url_after
  192. fetch(url)
  193. .then(response => response.text())
  194. .then(text => {
  195. var cleaned_text = replace_urls(text, url_before, url_after)
  196. const blobContent = new Blob([cleaned_text], {type: "text/html"})
  197. this.content.src = URL.createObjectURL(blobContent)
  198. })
  199. }
  200. load_pages(open_first=false) {
  201. var date_from = this.range_from.value
  202. var date_to = this.range_to.value
  203. this.load_pages_recursive(1, date_from, date_to, open_first)
  204. }
  205. load_pages_recursive(page, date_from, date_to, open_first) {
  206. if (page == 1) {
  207. // Clean commits list
  208. this.commitsSelect.innerHTML = ""
  209. this.commitsSelect.appendChild(document.createElement("option"))
  210. }
  211. if (date_from == "" || date_to == "") {
  212. return
  213. }
  214. var commits_url = 'https://' + this.url + '/api/v4/projects/' + this.project_id + '/repository/commits?ref_name=' + this.branch + '&private_token=' + this.token + "&since="+date_from+"&until="+date_to+"&per_page=100&page=" + page;
  215. var length_promise = fetch(commits_url)
  216. .then(response => response.json())
  217. .then(
  218. // Translate response to commits
  219. json => json.map(
  220. gitlab_commit => new ArchiveCommit(
  221. gitlab_commit.message,
  222. gitlab_commit.created_at,
  223. gitlab_commit.id
  224. )
  225. )
  226. ).then((commits) => {
  227. // Skip if new date is selected
  228. if (date_from != this.range_from.value
  229. || date_to != this.range_to.value) {
  230. return 0;
  231. }
  232. this.add_commits(commits, open_first)
  233. return commits.length
  234. }
  235. ).then((length) => {
  236. if (length > 0) {
  237. this.load_pages_recursive(page+1, date_from, date_to, false)
  238. }
  239. })
  240. }
  241. }