<!DOCTYPE html> <TMPL_IF HTML_LANG_CODE><html lang="<TMPL_VAR HTML_LANG_CODE>" dir="<TMPL_VAR HTML_LANG_DIR>" xmlns="http://www.w3.org/1999/xhtml"><TMPL_ELSE><html xmlns="http://www.w3.org/1999/xhtml"></TMPL_IF> <head> <TMPL_IF DYNAMIC> <TMPL_IF FORCEBASEURL><base href="<TMPL_VAR FORCEBASEURL>" /><TMPL_ELSE> <TMPL_IF BASEURL><base href="<TMPL_VAR BASEURL>" /></TMPL_IF> </TMPL_IF> </TMPL_IF> <TMPL_IF HTML5><meta charset="utf-8" /><TMPL_ELSE><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></TMPL_IF> <title><TMPL_LOOP PARENTLINKS><TMPL_VAR PAGE> - </TMPL_LOOP> <TMPL_VAR TITLE></title> <TMPL_IF RESPONSIVE_LAYOUT><meta name="viewport" content="width=device-width, initial-scale=1" /></TMPL_IF> <TMPL_IF FAVICON> <link rel="icon" href="<TMPL_VAR BASEURL><TMPL_VAR FAVICON>" type="image/x-icon" /> </TMPL_IF> <link rel="stylesheet" href="<TMPL_VAR BASEURL>style.css" type="text/css" /> <TMPL_IF LOCAL_CSS> <link rel="stylesheet" href="<TMPL_VAR BASEURL><TMPL_VAR LOCAL_CSS>" type="text/css" /> <TMPL_ELSE> <link rel="stylesheet" href="<TMPL_VAR BASEURL>local.css" type="text/css" /> </TMPL_IF> <TMPL_UNLESS DYNAMIC> <TMPL_IF EDITURL> <link rel="alternate" type="application/x-wiki" title="Edit this page" href="<TMPL_VAR EDITURL>" /> </TMPL_IF> <TMPL_IF FEEDLINKS><TMPL_VAR FEEDLINKS></TMPL_IF> <TMPL_IF RELVCS><TMPL_VAR RELVCS></TMPL_IF> <TMPL_IF META><TMPL_VAR META></TMPL_IF> <TMPL_LOOP TRAILLOOP> <TMPL_IF PREVPAGE> <link rel="prev" href="<TMPL_VAR PREVURL>" title="<TMPL_VAR PREVTITLE>" /> </TMPL_IF> <link rel="up" href="<TMPL_VAR TRAILURL>" title="<TMPL_VAR TRAILTITLE>" /> <TMPL_IF NEXTPAGE> <link rel="next" href="<TMPL_VAR NEXTURL>" title="<TMPL_VAR NEXTTITLE>" /> </TMPL_IF> </TMPL_LOOP> </TMPL_UNLESS> <script> // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0-or-later /* SeekToTime - simple script to add video time jump functionality to timestamp links Copyright (C) 2020 Grant Shangreaux This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. This script enables wiki editors to create anchor tags with the class "time-link" that will be parsed for seeking to specific time stamps in the main video on a page. The tag should look like this: <a href="#mainVideo" class="time-link">mm:ss</a> This could be extended to accept hours in the time stamp as well, but currently does not. */ let mainVideo; let qnaVideo; // some pages have a questions and answers video let timestamps; // expects a string like "mm:ss.mmm" function parseSeconds(timeString) { return timeString.split(":").reduce(function(prev, o) { return prev * 60 + parseFloat(o); }, 0); } function handleSubtitleClick(event) { var video = event.target.getAttribute('data-video'); var start = event.target.getAttribute('data-start'); let m = video.match(/(mainVideo|qnaVideo)-(.*)/); if (m) { video = m[2] + '-' + m[1]; } var videoElem = document.getElementById(video); if (videoElem) { videoElem.currentTime = parseSeconds(start); videoElem.scrollIntoView(); } event.preventDefault(); } window.onload = function initScript() { mainVideo = document.getElementById("mainVideo"); qnaVideo = document.getElementById("qnaVideo"); timestamps = document.getElementsByClassName("time-link"); var len = timestamps.length; for (let i = 0; i < len; i++) { timestamps[i].onclick = function () { videoType = this.href.split("/").pop(); video = (videoType == "#mainVideo") ? mainVideo : qnaVideo; video.currentTime = parseSeconds(this.innerText) }; } let subtitles = document.getElementsByClassName('subtitle'); for (let i = 0; i < subtitles.length; i++) { subtitles[i].onclick = handleSubtitleClick; } } // @license-end </script> </head> <body <TMPL_IF SIDEBAR>class="has-sidebar"</TMPL_IF>> <TMPL_IF HTML5><article class="page"><TMPL_ELSE><div class="page"></TMPL_IF> <TMPL_IF HTML5><section class="pageheader-wrapper"><TMPL_ELSE><div class="pageheader-wrapper"></TMPL_IF> <TMPL_IF HTML5><section class="pageheader"><TMPL_ELSE><div class="pageheader"></TMPL_IF> <TMPL_IF HTML5><header class="header"><TMPL_ELSE><div class="header"></TMPL_IF> <span> <span class="parentlinks"> <TMPL_LOOP PARENTLINKS> <a href="<TMPL_VAR URL>"><TMPL_VAR PAGE></a>/ </TMPL_LOOP> </span> <span class="title"> <TMPL_VAR TITLE> <TMPL_IF ISTRANSLATION> (<TMPL_VAR PERCENTTRANSLATED>%) </TMPL_IF> </span> </span> <TMPL_UNLESS DYNAMIC> <TMPL_IF SEARCHFORM> <TMPL_VAR SEARCHFORM> </TMPL_IF> </TMPL_UNLESS> <TMPL_IF HTML5></header><TMPL_ELSE></div></TMPL_IF> <TMPL_IF HAVE_ACTIONS> <TMPL_IF HTML5><nav class="actions"><TMPL_ELSE><div class="actions"></TMPL_IF> <ul> <!-- <li><a href="<TMPL_VAR BASEURL>donate/">❤️ Donate</a></li> --> <TMPL_IF EDITURL> <li><a href="<TMPL_VAR EDITURL>" rel="nofollow">Edit</a></li> <TMPL_ELSE> <li><a href="<TMPL_VAR BASEURL>edit/">Edit <span class="muted">(how to)</span></a></li> </TMPL_IF> <TMPL_IF RECENTCHANGESURL> <li><a href="<TMPL_VAR RECENTCHANGESURL>">Recent Changes</a></li> </TMPL_IF> <TMPL_IF HISTORYURL> <li><a rel="nofollow" href="<TMPL_VAR HISTORYURL>">History</a></li> </TMPL_IF> <TMPL_IF GETSOURCEURL> <li><a rel="nofollow" href="<TMPL_VAR GETSOURCEURL>">Source</a></li> </TMPL_IF> <TMPL_IF PREFSURL> <li><a rel="nofollow" href="<TMPL_VAR PREFSURL>">Preferences</a></li> </TMPL_IF> <TMPL_IF ACTIONS> <TMPL_LOOP ACTIONS> <li><TMPL_VAR ACTION></li> </TMPL_LOOP> </TMPL_IF> <TMPL_IF COMMENTSLINK> <li><TMPL_VAR COMMENTSLINK></li> <TMPL_ELSE> <TMPL_IF DISCUSSIONLINK> <li><TMPL_VAR DISCUSSIONLINK></li> </TMPL_IF> </TMPL_IF> </ul> <TMPL_IF HTML5></nav><TMPL_ELSE></div></TMPL_IF> </TMPL_IF> <TMPL_IF OTHERLANGUAGES> <TMPL_IF HTML5><nav id="otherlanguages"><TMPL_ELSE><div id="otherlanguages"></TMPL_IF> <ul> <TMPL_LOOP OTHERLANGUAGES> <li> <a href="<TMPL_VAR URL>"><TMPL_VAR LANGUAGE></a> <TMPL_IF MASTER> (master) <TMPL_ELSE> (<TMPL_VAR PERCENT>%) </TMPL_IF> </li> </TMPL_LOOP> </ul> <TMPL_IF HTML5></nav><TMPL_ELSE></div></TMPL_IF> </TMPL_IF> <TMPL_UNLESS DYNAMIC> <TMPL_VAR TRAILS> </TMPL_UNLESS> <TMPL_IF HTML5></section><TMPL_ELSE></div></TMPL_IF> <TMPL_IF HTML5></section><TMPL_ELSE></div></TMPL_IF> <div id="pagebody"> <TMPL_UNLESS DYNAMIC> <TMPL_IF SIDEBAR> <TMPL_IF HTML5><aside class="sidebar"><TMPL_ELSE><div class="sidebar"></TMPL_IF> <TMPL_VAR SIDEBAR> <TMPL_IF HTML5></aside><TMPL_ELSE></div></TMPL_IF> </TMPL_IF> </TMPL_UNLESS> <TMPL_IF HTML5><section<TMPL_ELSE><div</TMPL_IF> id="content" role="main"> <TMPL_VAR CONTENT> <TMPL_IF HTML5></section><TMPL_ELSE></div></TMPL_IF> <TMPL_IF ENCLOSURE> <TMPL_IF HTML5><section id="enclosure"><TMPL_ELSE><div id="enclosure"></TMPL_IF> <a href="<TMPL_VAR ENCLOSURE>">Download</a> <TMPL_IF HTML5></section><TMPL_ELSE></div></TMPL_IF> </TMPL_IF> <TMPL_UNLESS DYNAMIC> <TMPL_IF COMMENTS> <TMPL_IF HTML5><section<TMPL_ELSE><div</TMPL_IF> id="comments" role="complementary"> <TMPL_VAR COMMENTS> <TMPL_IF ADDCOMMENTURL> <div class="addcomment"> <a rel="nofollow" href="<TMPL_VAR ADDCOMMENTURL>">Add a comment</a> </div> <TMPL_ELSE> <div class="addcomment">Comments on this page are closed.</div> </TMPL_IF> <TMPL_IF HTML5></section><TMPL_ELSE></div></TMPL_IF> </TMPL_IF> </TMPL_UNLESS> </div> <TMPL_IF HTML5><footer<TMPL_ELSE><div</TMPL_IF> id="footer" class="pagefooter" role="contentinfo"> <TMPL_UNLESS DYNAMIC> <TMPL_IF HTML5><nav id="pageinfo"><TMPL_ELSE><div id="pageinfo"></TMPL_IF> <TMPL_VAR TRAILS> <TMPL_IF TAGS> <TMPL_IF HTML5><nav class="tags"><TMPL_ELSE><div class="tags"></TMPL_IF> Tags: <TMPL_LOOP TAGS> <TMPL_VAR LINK> </TMPL_LOOP> <TMPL_IF HTML5></nav><TMPL_ELSE></div></TMPL_IF> </TMPL_IF> <TMPL_IF BACKLINKS> <TMPL_IF HTML5><nav id="backlinks"><TMPL_ELSE><div id="backlinks"></TMPL_IF> Backlinks: <TMPL_LOOP BACKLINKS> <a href="<TMPL_VAR URL>"><TMPL_VAR PAGE></a> </TMPL_LOOP> <TMPL_IF MORE_BACKLINKS> <span class="popup">... <span class="balloon"> <TMPL_LOOP MORE_BACKLINKS> <a href="<TMPL_VAR URL>"><TMPL_VAR PAGE></a> </TMPL_LOOP> </span> </span> </TMPL_IF> <TMPL_IF HTML5></nav><TMPL_ELSE></div></TMPL_IF> </TMPL_IF> <TMPL_IF COPYRIGHT> <div class="pagecopyright"> <a name="pagecopyright"></a> <TMPL_VAR COPYRIGHT> </div> </TMPL_IF> <TMPL_IF LICENSE> <div class="pagelicense"> <a name="pagelicense"></a> <TMPL_VAR LICENSE> </div> </TMPL_IF> <div class="pagedate"> Last edited <TMPL_VAR MTIME> <!-- Created <TMPL_VAR CTIME> --> </div> <TMPL_IF HTML5></nav><TMPL_ELSE></div></TMPL_IF> <TMPL_IF EXTRAFOOTER><TMPL_VAR EXTRAFOOTER></TMPL_IF> </TMPL_UNLESS> <!-- from <TMPL_VAR WIKINAME> --> <TMPL_IF HTML5></footer><TMPL_ELSE></div></TMPL_IF> <TMPL_IF HTML5></article><TMPL_ELSE></div></TMPL_IF> <script> // @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt txt CC0-1.0 // Copyright (C) 2021, 2022 Sacha Chua if (document.querySelector('.times')) { var dateOptions = {dateStyle: 'short', timeStyle: 'short'}; var localStart = (new Date(document.querySelector('.times').getAttribute('start'))).toLocaleString([], dateOptions); var localEnd = (new Date(document.querySelector('.times').getAttribute('end'))).toLocaleString([], dateOptions); var dateElem = document.createElement('div'); dateElem.appendChild(document.createTextNode('Your local time: ~ ' + localStart + ' to ~ ' + localEnd)); document.querySelector('.times').prepend(dateElem); if (document.querySelector('.times').querySelector('.others')) { document.querySelector('.times').querySelector('.others').style.display = 'none'; } } if (document.querySelector('.time-overlay')) { document.querySelectorAll('.time-overlay').forEach(function (o) { if (o.getAttribute('title')) return; var dateOptions = {dateStyle: 'short', timeStyle: 'short'}; var localStart, localEnd; if (o.getAttribute('start') && o.getAttribute('end')) { localStart = (new Date(o.getAttribute('start'))).toLocaleString([], dateOptions); localEnd = (new Date(o.getAttribute('end'))).toLocaleString([], dateOptions); o.setAttribute('title', 'Your local time: ~ ' + localStart + ' to ~ ' + localEnd); } else if (o.getAttribute('start')) { localStart = (new Date(o.getAttribute('start'))).toLocaleString([], dateOptions); o.setAttribute('title', 'Your local time: ~ ' + localStart); } }); } if (document.querySelector('a[name=transcript]')) { var transcriptLink = document.createElement('a'); transcriptLink.setAttribute('href', '#transcript'); transcriptLink.textContent = 'View transcript'; var video = document.querySelector('.mainVideo video'); if (video) { var resources = document.querySelector('.mainVideo video').closest('.vid').querySelector('.resources'); var transcriptDiv = document.createElement('div'); transcriptDiv.appendChild(transcriptLink) if (resources) { resources.prepend(transcriptDiv); } } } var chat = document.querySelector('.chat-iframe'); if (chat) { if (chat.getAttribute('data-track')) { chat.innerHTML = '<iframe src="https://chat.emacsconf.org?join=emacsconf,emacsconf-' + chat.getAttribute('data-track').replace(/[^A-Za-z]/g, '') + '" height="600" width="100%"></iframe>'; } else { chat.innerHTML = '<iframe src="https://chat.emacsconf.org" height="600" width="100%"></iframe>'; } } // @license-end </script> <script> // @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt txt CC0-1.0 // Copyright (c) 2021 Sacha Chua - CC0 Public Domain function displayChapters(elem) { var i; var chapter; var list = document.createElement('ol'); list.setAttribute('class', 'chapters'); var link; var target = elem.getAttribute('data-target'); var video = document.getElementById(target); var track; if (video) { track = video.addTextTrack('chapters'); track.mode = 'hidden'; } var chapters = elem.textContent.split(/[ \t]*\n+[ \t]*/).forEach(function(line) { var m = (line.match(/^(((?:[0-9]+:)?[0-9]+:[0-9]+)(?:\.[0-9]+))[ \t]+(.*)/)); if (m) { var start = m[1]; var text = m[3]; chapter = document.createElement('li'); link = document.createElement('a'); link.setAttribute('href', '#'); link.setAttribute('data-video', target); link.setAttribute('data-start', start); link.setAttribute('data-start-s', parseSeconds(start)); link.appendChild(document.createTextNode(m[2] + ' ' + text)); link.onclick = handleSubtitleClick; chapter.appendChild(link); list.appendChild(chapter); if (track) { var time = parseSeconds(start); if (track.cues.length > 0) { track.cues[track.cues.length - 1].endTime = time - 1; } track.addCue(new VTTCue(time, time, text)); } } }) if (track && track.cues.length > 0) { video.addEventListener('durationchange', function() { track.cues[track.cues.length - 1].endTime = video.duration; }); track.addEventListener('cuechange', function() { if (!this.activeCues[0]) return; if (list.querySelector('.current')) { list.querySelector('.current').className = ''; } var chapter; if (chapter = list.querySelector('a[data-start-s="' + this.activeCues[0].startTime + '"]')) { chapter.parentNode.className = 'current'; } }); } elem.parentNode.replaceChild(list, elem); } document.querySelectorAll('pre.chapters').forEach(displayChapters); var video = document.querySelector('video.reload'); if (video) { var myVar = setInterval(reloadAsNeeded, 1000); var oldTime = ''; function reloadAsNeeded() { if ((video.paused != true && (video.currentTime - oldTime) == 0 && video.currentTime != 0)) { var source = video.querySelector('source'); var oldVideo = source.src; source.src = ''; source.src = oldVideo; video.load(); video.play(); } oldTime = video.currentTime; }; } // @license-end // @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt txt CC0-1.0 // Copyright (c) 2023 Sacha Chua - CC0 Public Domain function highlightTalks() { // highlight any talk mentioned in the highlight URL parameter var params = new URLSearchParams(window.location.search); if (!params.get('highlight')) return; var talks = params.get('highlight').split(',').filter(function(o) { return o.match(/^[-a-z0-9]+$/); }); var regexp = new RegExp('/talks/(' + talks.join('|') + ')/?$'); document.querySelectorAll('a[href]').forEach(function(link) { console.debug(link.getAttribute('href'), link.getAttribute('href').match(regexp)); if (link.getAttribute('href').match(regexp)) { console.debug(link); link.classList.add('highlight'); } }); } addEventListener('DOMContentLoaded', highlightTalks); // @license-end </script> </body> </html>