multimedia enhancements
This commit is contained in:
		
							parent
							
								
									f06df39a8e
								
							
						
					
					
						commit
						616a195301
					
				
					 6 changed files with 311 additions and 224 deletions
				
			
		|  | @ -1,214 +0,0 @@ | |||
| <!DOCTYPE html> | ||||
| <html> | ||||
| 
 | ||||
| <head> | ||||
|     {{partial "head" .}} | ||||
|     <script defer src="/alpine.js" type="text/javascript"></script> | ||||
|     <script src="/helper.js"></script> | ||||
|     <style> | ||||
|         .interverse { | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|             align-items: center; | ||||
|         } | ||||
| 
 | ||||
|         .interverse h1 { | ||||
|             text-align: center; | ||||
|         } | ||||
| 
 | ||||
|         .interverse img { | ||||
|             max-width: 15rem; | ||||
|             max-height: 15rem; | ||||
|         } | ||||
| 
 | ||||
|         .interverse section { | ||||
|             display: flex; | ||||
|             flex-direction: row; | ||||
|             align-items: center; | ||||
|             justify-content: center; | ||||
|             flex-wrap: wrap; | ||||
|             box-shadow: none; | ||||
|         } | ||||
| 
 | ||||
|         .interverse-connection-preview { | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|             padding: 1rem; | ||||
|             margin: 0.5rem; | ||||
|             align-items: center; | ||||
|         } | ||||
| 
 | ||||
|         .interverse-connection { | ||||
|             margin: 0.25rem; | ||||
|             padding: 0.25rem; | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|             align-items: center; | ||||
|             justify-items: center; | ||||
|         } | ||||
| 
 | ||||
|         .interverse-resource { | ||||
|             margin: 2rem; | ||||
|         } | ||||
| 
 | ||||
|         #interverse-resource-groups { | ||||
|             align-items: flex-start; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|     {{partial "header" .}} | ||||
|     <main class="interverse" x-data x-if="Alpine.store('data')"> | ||||
|         <section>{{.Content}}</section> | ||||
|         <section id="interverse-details"> | ||||
|             <img x-bind:src="Alpine.store('data')['image']"> | ||||
|             <div style="margin-left:3rem;"> | ||||
|                 <h1 x-text="Alpine.store('data')['name']"></h1> | ||||
|                 <a x-bind:href="Alpine.store('data')['location']" x-text="Alpine.store('data')['location']"></a> | ||||
|             </div> | ||||
|         </section> | ||||
|         <section id="interverse-resource-groups"> | ||||
|             <template x-for="(group,name) in Alpine.store('data')['resource_groups']"> | ||||
|                 <div> | ||||
|                     <h1 x-text="name"></h1> | ||||
|                     <div class="interverse-group"> | ||||
|                         <template x-for="resource in group"> | ||||
|                             <a x-bind:href="resource['location']"> | ||||
|                                 <div class="interverse-resource"> | ||||
|                                     <template x-if="resource['icon'] !=''"> | ||||
|                                         <img class="interverse-resource-icon" x-bind:src="resource['icon']"> | ||||
|                                     </template> | ||||
|                                     <h2 x-text="resource['label']"></h2> | ||||
|                                     <p x-text="resource['description']"></p> | ||||
|                                 </div> | ||||
|                             </a> | ||||
|                         </template> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </template> | ||||
|         </section> | ||||
|         <section id="interverse-resources"> | ||||
|             <template x-for="resource in Alpine.store('data')['resources']"> | ||||
|                 <a x-bind:href="resource['location']"> | ||||
|                     <div class="interverse-resource"> | ||||
|                         <template x-if="resource['icon'] !=''"> | ||||
|                             <img class="interverse-resource-icon" x-bind:src="resource['icon']"> | ||||
|                         </template> | ||||
|                         <h2 x-text="resource['label']"></h2> | ||||
|                         <p x-text="resource['description']"></p> | ||||
|                     </div> | ||||
|                 </a> | ||||
|             </template> | ||||
|         </section> | ||||
|         <div id="interverse-groups"> | ||||
|             <template x-for="(group,name) in Alpine.store('data')['connection_groups']"> | ||||
|                 <div> | ||||
|                     <h1 x-text="name"></h1> | ||||
|                     <section class="interverse-group"> | ||||
|                         <template x-for="link in group"> | ||||
|                             <div class="interverse-connection" x-data="{connected:false"> | ||||
|                                 <template x-if="Alpine.store(link)['name'] !=''"> | ||||
|                                     <div class='interverse-connection-preview' | ||||
|                                         x-on:click="initialize(link+'/.well-known/interverse')"> | ||||
|                                         <h3 x-text="Alpine.store(link)['name']"></h3> | ||||
|                                         <img x-bind:src="Alpine.store(link)['image']"> | ||||
|                                         <a x-bind:href="link" x-text="link"></a> | ||||
|                                     </div> | ||||
|                                 </template> | ||||
|                                 <template x-if="Alpine.store(link) == undefined"> | ||||
|                                     <div> | ||||
|                                         <a x-bind:href="link"> | ||||
|                                             <p x-text="link"></p> | ||||
|                                         </a> | ||||
|                                     </div> | ||||
| 
 | ||||
|                                 </template> | ||||
|                             </div> | ||||
|                         </template> | ||||
|                     </section> | ||||
| 
 | ||||
|                 </div> | ||||
|             </template> | ||||
|         </div> | ||||
|         <h3 style="text-align:center"></h3> | ||||
|         <section id="interverse-connections"> | ||||
|             <template x-for="connection in Alpine.store('data')['connections']"> | ||||
|                 <div class="interverse-connection"> | ||||
|                     <template x-if="Alpine.store(connection)['version'] >=0"> | ||||
|                         <div x-on:click="initialize(connection+'/.well-known/interverse')"> | ||||
|                             <h3 x-text="Alpine.store(connection)['name']"></h3> | ||||
|                             <template x-if="Alpine.store(connection)['image']!=''"> | ||||
|                                 <img x-bind:src="Alpine.store(connection)['image']"> | ||||
|                             </template> | ||||
|                         </div> | ||||
| 
 | ||||
|                     </template> | ||||
|                     <template x-if="name == ''"> | ||||
|                         <div> | ||||
|                             <a x-bind:href="connection" x-text="connection"></a> | ||||
|                     </template> | ||||
| 
 | ||||
|                 </div> | ||||
|             </template> | ||||
|             </div> | ||||
|             <div id="interverse-contact"> | ||||
|                 <template x-for="contact in Alpine.store('data')['contact']"> | ||||
|                     <div class="interverse-contact"> | ||||
| 
 | ||||
|                     </div> | ||||
| 
 | ||||
|                 </template> | ||||
|             </div> | ||||
| 
 | ||||
|     </main> | ||||
|     <script> | ||||
|         var t = new Date(); | ||||
|         var timestamp = t.getTime(); | ||||
|         var main_url = "/.well-known/interverse"; | ||||
|         if (getUrlParameter('s') && getUrlParameter("url")) { | ||||
|             var uri = ""; | ||||
|             main_url = getUrlParameter('s') + "://" + getUrlParameter("url") + '/.well-known/interverse?t=' + timestamp; | ||||
|         } else { | ||||
|             if (getUrlParameter('url')) { | ||||
|                 main_url = "https://" + getUrlParameter('url') + '/.well-known/interverse?t=' + timestamp; | ||||
|             } | ||||
|         } | ||||
|         document.addEventListener('alpine:init', function () { | ||||
| 
 | ||||
|             initialize(main_url); | ||||
|         }); | ||||
| 
 | ||||
|         function initialize(url) { | ||||
|             Alpine.store("data", {}); | ||||
|             fetchdata(url, function (e) { | ||||
|                 data = JSON.parse(e.target.response); | ||||
|                 raw_data = JSON.parse(e.target.response); | ||||
|                 Alpine.store("data", data); | ||||
|                 var connections = raw_data['connections']; | ||||
|                 for (group in data['connection_groups']) { | ||||
|                     for (link in data['connection_groups'][group]) { | ||||
|                         connections.push(data['connection_groups'][group][link]); | ||||
|                     } | ||||
|                 } | ||||
|                 var t = new Date(); | ||||
|                 var timestamp = t.getTime(); | ||||
|                 for (var i = 0; i < connections.length; i++) { | ||||
|                     fetchdata(connections[i] + '/.well-known/interverse?t=' + timestamp, function (e) { | ||||
|                         try { | ||||
|                             dat = JSON.parse(e.target.response); | ||||
|                             console.log(dat); | ||||
|                             Alpine.store(dat['location'], dat); | ||||
|                         } catch { | ||||
|                             console.log("Failed to parse connection JSON"); | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
| 
 | ||||
|             }); | ||||
|         } | ||||
|     </script> | ||||
| </body> | ||||
| {{partial "footer" .}} | ||||
| 
 | ||||
| </html> | ||||
|  | @ -1,3 +1,126 @@ | |||
| <audio controls preload="false"> | ||||
|     <source src='{{.Site.BaseURL}}{{.Get "src" }}'> | ||||
| </audio> | ||||
| <style> | ||||
|     .media-container { | ||||
|         width: 90%; | ||||
|     } | ||||
| 
 | ||||
|     .audio-player { | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         width: 100%; | ||||
|     } | ||||
|     .audio-player p{ | ||||
|         width: fit-content; | ||||
|     } | ||||
| 
 | ||||
|     .media-seek { | ||||
|         width: 100%; | ||||
|     } | ||||
| </style> | ||||
| <div class="media-container"> | ||||
|     <div class="audio-player"> | ||||
|         <audio onloadedmetadata="setup_audio_metadata(event)" ontimeupdate="setup_audio_metadata(event)"> | ||||
|             <source src='{{.Site.BaseURL}}{{.Get "src" }}'> | ||||
|         </audio> | ||||
|         <button onclick="toggle_play_audio(event)">Play</button> | ||||
|         <input class="media-seek" type="range" onchange="update_audio_time(event)" value="0"> | ||||
|         <select onchange="update_audio_speed(event)"> | ||||
|             <option value="1" selected>1x</option> | ||||
|             <option value="1.5">1.5x</option> | ||||
|             <option value="2">2x</option> | ||||
|             <option value="2.5">2.5x</option> | ||||
|             <option value="3">3x</option> | ||||
|         </select> | ||||
|         <div class="audio-preview"> | ||||
|         </div> | ||||
|         <p><span class="audio-currentTime"></span><span class="audio-duration"></span></p> | ||||
|         <button onclick="toggle_loop_audio(event)">Loop</button> | ||||
|         <script> | ||||
|             function setup_audio_metadata(event) { | ||||
|                 audio = event.target; | ||||
|                 var seek = audio.parentElement.children[2]; | ||||
|                 seek.min = 0; | ||||
|                 seek.max = audio.duration; | ||||
|                 seek.value = audio.currentTime; | ||||
|                 audio.parentElement.children[5].children[0].innerHTML = "" + timeToText(audio.currentTime) + "/"; | ||||
|                 audio.parentElement.children[5].children[1].innerHTML = "" + timeToText(audio.duration); | ||||
|             } | ||||
|             function toggle_loop_audio(event) { | ||||
|                 audio = event.target.parentElement.children[0]; | ||||
|                 audio.loop = !audio.loop; | ||||
|             } | ||||
|             function update_audio_time(event) { | ||||
|                 audio = event.target.parentElement.children[0]; | ||||
|                 audio.currentTime = event.target.value; | ||||
|             } | ||||
|             function toggle_play_audio(event) { | ||||
|                 el = event.target; | ||||
|                 audio = event.target.parentElement.children[0]; | ||||
|                 speed = el.parentElement.children[3].value; | ||||
|                 audio.playbackRate = speed; | ||||
| 
 | ||||
|                 if (audio.paused) { | ||||
|                     audio.play(); | ||||
|                     el.innerHTML = "Pause"; | ||||
|                 } | ||||
|                 else { | ||||
|                     audio.pause(); | ||||
|                     el.innerHTML = "Play" | ||||
|                 } | ||||
|             } | ||||
|             function update_audio_speed(event) { | ||||
|                 el = event.target; | ||||
|                 audio = event.target.parentElement.children[0]; | ||||
|                 audio.playbackRate = el.value; | ||||
| 
 | ||||
|             } | ||||
|             function timeToText(t) { | ||||
|                 hours = 0; | ||||
|                 minutes = 0; | ||||
|                 seconds = 0; | ||||
|                 if (t > 60 * 60) { | ||||
|                     hours = Math.floor(t / 60 / 60); | ||||
|                     t = t - hours * 60 * 60; | ||||
|                 } | ||||
|                 if (t > 60) { | ||||
|                     minutes = Math.floor(t / 60); | ||||
|                     t = t - minutes * 60 | ||||
| 
 | ||||
|                 } | ||||
|                 if (t > 1) { | ||||
|                     seconds = Math.floor(t); | ||||
|                 } | ||||
|                 text = ""; | ||||
|                 if (hours > 0) { | ||||
|                     text += hours + ":"; | ||||
|                 } | ||||
|                 if (minutes > 0 || hours > 0) { | ||||
|                     if (minutes == 0) { | ||||
|                         minutes = "00" | ||||
|                     } | ||||
|                     if (minutes < 10) { | ||||
|                         text += "0" | ||||
|                     } | ||||
|                     text += minutes + ":" | ||||
|                 } | ||||
|                 if (seconds == 0 && (minutes > 0 || hours > 0)) { | ||||
|                     seconds = "00"; | ||||
|                 } | ||||
|                 if (seconds < 10) { | ||||
|                     text += "0"; | ||||
|                 } | ||||
|                 text += seconds | ||||
|                 return text; | ||||
|             } | ||||
|         </script> | ||||
|     </div> | ||||
|     <noscript> | ||||
|         <style> | ||||
|             .audio-player { | ||||
|                 display: none; | ||||
|             } | ||||
|         </style> | ||||
|         <audio controls preload="false"> | ||||
|             <source src='{{.Site.BaseURL}}{{.Get "src" }}'> | ||||
|         </audio> | ||||
|     </noscript> | ||||
| </div> | ||||
|  | @ -1,4 +1,5 @@ | |||
| {{ if .Get "href"}} | ||||
| <div class="image-container"> | ||||
|     {{ if .Get "href"}} | ||||
| <a href='{{.Get "href"}}'> | ||||
| {{end}} | ||||
| <img loading="lazy" | ||||
|  | @ -12,3 +13,11 @@ style='float:{{.Get "float"}}' | |||
| {{ if .Get "href"}} | ||||
| </a> | ||||
| {{end}} | ||||
| {{if .Get "caption"}} | ||||
| <p class="caption"> | ||||
|     {{.Get "caption"}} {{if .Get "source"}}<a href='{{.Get "source"}}'>Source</a>{{end}} | ||||
| 
 | ||||
| </p> | ||||
| {{end}} | ||||
| 
 | ||||
| </div> | ||||
|  | @ -1,3 +1,137 @@ | |||
| <video {{if .Get "autoplay"}}autoplay muted loop preload="true"{{else}}controls preload="false"{{end}}> | ||||
|     <source src='{{.Site.BaseURL}}{{.Get "src" }}'> | ||||
| </video> | ||||
| <style> | ||||
|     .video-player{ | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|     } | ||||
|     .media-container{ | ||||
|         width:fit-content; | ||||
|     } | ||||
| .video-controls{ | ||||
|     display:flex; | ||||
|     flex-direction: row; | ||||
|     width:100%; | ||||
| } | ||||
| .video-controls p{ | ||||
|     width:min-content; | ||||
| } | ||||
| .media-seek{ | ||||
|     width:100%; | ||||
| } | ||||
| </style> | ||||
| <div class="media-container"> | ||||
|     <div class="video-player"> | ||||
|         <video preload="metadata" onclick="playthis(event)" onloadedmetadata="setup_video_metadata(event)" ontimeupdate="setup_video_metadata(event)"> | ||||
|             <source src='{{.Site.BaseURL}}{{.Get "src" }}'> | ||||
|         </video> | ||||
|         <div class="video-controls"> | ||||
|             <button onclick="toggle_video_play(event)">Play</button> | ||||
|             <input class="media-seek" type="range" onchange="update_video_time(event)" value="0"> | ||||
|             <select onchange="update_video_speed(event)"> | ||||
|                 <option value="1" selected>1x</option> | ||||
|                 <option value="1.5">1.5x</option> | ||||
|                 <option value="2">2x</option> | ||||
|                 <option value="2.5">2.5x</option> | ||||
|                 <option value="3">3x</option> | ||||
|             </select> | ||||
|             <p><span class="audio-currentTime"></span><span class="audio-duration"></span></p> | ||||
|         </div> | ||||
|         <script> | ||||
|             function setup_video_metadata(event){ | ||||
|                 video = event.target; | ||||
|                 var seek = video.parentElement.children[1].children[1]; | ||||
|                 seek.min = 0; | ||||
|                 seek.max = video.duration; | ||||
|                 seek.value = video.currentTime; | ||||
|                 details = video.parentElement.children[1].children[3].children; | ||||
|                 details[0].innerHTML = timeToText(video.currentTime)+"/"; | ||||
|                 details[1].innerHTML = timeToText(video.duration); | ||||
| 
 | ||||
|             } | ||||
|             function playthis(event){ | ||||
|                 if (event.target.paused){ | ||||
|                     event.target.playbackRate = video.parentElement.children[1].children[2].value; | ||||
|                     event.target.play(); | ||||
|                     video.parentElement.children[1].children[0].innerHTML="Pause"; | ||||
|                 } | ||||
|                 else{ | ||||
|                     event.target.pause(); | ||||
|                     video.parentElement.children[1].children[0].innerHTML="Play"; | ||||
|                 } | ||||
|             } | ||||
|             function toggle_video_play(event){ | ||||
|                 video = event.target.parentElement.parentElement.children[0]; | ||||
|                 if (video.paused){ | ||||
|                     video.playbackRate = video.parentElement.children[1].children[2].value; | ||||
|                     video.play(); | ||||
|                     video.parentElement.children[1].children[0].innerHTML="Pause"; | ||||
|                 } | ||||
|                 else{ | ||||
|                     video.pause(); | ||||
|                     video.parentElement.children[1].children[0].innerHTML="Play"; | ||||
|                 } | ||||
|             } | ||||
|             function update_video_speed(event){ | ||||
|                 video = event.target.parentElement.parentElement.children[0]; | ||||
|                 video.playbackRate=event.target.value; | ||||
|             } | ||||
|             function update_video_time(event){ | ||||
|                 video = event.target.parentElement.parentElement.children[0]; | ||||
|                 video.currentTime = event.target.value; | ||||
|             } | ||||
|              | ||||
|             function timeToText(t) { | ||||
|                 hours = 0; | ||||
|                 minutes = 0; | ||||
|                 seconds = 0; | ||||
|                 if (t > 60 * 60) { | ||||
|                     hours = Math.floor(t / 60 / 60); | ||||
|                     t = t - hours * 60 * 60; | ||||
|                 } | ||||
|                 if (t > 60) { | ||||
|                     minutes = Math.floor(t / 60); | ||||
|                     t = t - minutes * 60 | ||||
| 
 | ||||
|                 } | ||||
|                 if (t > 1) { | ||||
|                     seconds = Math.floor(t); | ||||
|                 } | ||||
|                 text = ""; | ||||
|                 if (hours > 0) { | ||||
|                     text += hours + ":"; | ||||
|                 } | ||||
|                 if (minutes > 0 || hours > 0) { | ||||
|                     if (minutes == 0) { | ||||
|                         minutes = "00" | ||||
|                     } | ||||
|                     if (minutes < 10) { | ||||
|                         text += "0" | ||||
|                     } | ||||
|                     text += minutes + ":" | ||||
|                 } | ||||
|                 if (seconds == 0 && (minutes > 0 || hours > 0)) { | ||||
|                     seconds = "00"; | ||||
|                 } | ||||
|                 if (seconds < 10) { | ||||
|                     text += "0"; | ||||
|                 } | ||||
|                 text += seconds | ||||
|                 return text; | ||||
|             } | ||||
|         </script> | ||||
|     </div> | ||||
|     <noscript> | ||||
|         <style> | ||||
|             .video-player { | ||||
|                 display: none; | ||||
|             } | ||||
|         </style> | ||||
|         <video preload="false"> | ||||
|             <source src='{{.Site.BaseURL}}{{.Get "src" }}'> | ||||
|         </video> | ||||
|     </noscript> | ||||
|     <p class="caption"> | ||||
|         {{.Get "caption"}} {{if .Get "source"}}<a href='{{.Get "source"}}'>Source</a>{{end}} | ||||
| 
 | ||||
|     </p> | ||||
| </div> | ||||
|  | @ -62,7 +62,6 @@ hr { | |||
|     margin-bottom: 2rem; | ||||
|     color: var(--theme-color-accent); | ||||
| } | ||||
| 
 | ||||
| main,aside { | ||||
|     padding:2.5rem; | ||||
|     display: flex; | ||||
|  | @ -105,7 +104,7 @@ main{ | |||
| .listing video{ | ||||
|     width:25rem; | ||||
|     max-width:25vw; | ||||
|     max-height: 25vh; | ||||
|     max-height: 30vh; | ||||
| } | ||||
| .listing h2, .listing p{ | ||||
|     margin:0; | ||||
|  | @ -218,10 +217,37 @@ article { | |||
|     max-width:60rem; | ||||
|     font-size: 1.5rem; | ||||
| } | ||||
| article table{ | ||||
|     width:80%; | ||||
| } | ||||
| article .media-container{ | ||||
|     display:flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     width:100%; | ||||
| } | ||||
| .media-container p, .media-container a{ | ||||
|     width:fit-content; | ||||
| } | ||||
| .media-container a{ | ||||
|     width:100%; | ||||
|     display:flex; | ||||
|     align-items: center; | ||||
|     flex-direction: column; | ||||
| } | ||||
| 
 | ||||
| article td,article th{ | ||||
|     width:fit-content; | ||||
| } | ||||
| article .footnotes{ | ||||
|     width:100%; | ||||
| } | ||||
| article .footnotes a{ | ||||
|     text-decoration: none; | ||||
| } | ||||
| article sup a{ | ||||
|     text-decoration: none; | ||||
| } | ||||
| 
 | ||||
| blockquote { | ||||
|     margin-left: 10%; | ||||
|  | @ -269,8 +295,9 @@ article img{ | |||
| article video, | ||||
| article iframe { | ||||
|     max-width: 60rem; | ||||
|     max-height: 40rem; | ||||
|     width: 80%; | ||||
|     height:30rem; | ||||
|     height:50vh; | ||||
|     background-color: transparent; | ||||
| 
 | ||||
| } | ||||
|  | @ -307,6 +334,11 @@ video{ | |||
|     text-decoration: none; | ||||
|     text-align: left; | ||||
|     width:80%; | ||||
|     box-shadow:0px 0px 0.5rem var(--theme-color-accent); | ||||
|     border-radius:2rem; | ||||
|     overflow:hidden; | ||||
|     margin-bottom:1rem; | ||||
|     margin-top:1rem; | ||||
| } | ||||
| .link-preview>div{ | ||||
|     margin:1rem; | ||||
|  | @ -445,6 +477,9 @@ video{ | |||
|         width:100vw; | ||||
|      | ||||
|     } | ||||
|     .link-preview{ | ||||
|         flex-direction: column; | ||||
|     } | ||||
|     .recent,.featured{ | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue