mirror of
https://github.com/seigler/janus
synced 2025-07-27 01:36:11 +00:00
feat: backport lots of improvements from another project
This commit is contained in:
parent
683e645028
commit
056ce5f777
6 changed files with 570 additions and 133 deletions
|
@ -15,4 +15,4 @@ After you have edited index.html to your liking, you can run `npm run build` to
|
|||
|
||||
## Gotchas:
|
||||
|
||||
Firefox and Chrome handle `file:///` urls differently. In Firefox, local files are allowed to interact with LocalStorage, but on Chrome they are not. To circumvent this issue, you have to serve the file on `localhost` with `npm run start` or some other local server.
|
||||
Firefox and Chrome handle `file:///` urls differently. In Firefox, local files are allowed to interact with LocalStorage, but on Chrome they are not. To circumvent this issue in Chrome, you have to serve the file on `localhost` with `npm run start` or some other local server.
|
||||
|
|
153
index.html
153
index.html
|
@ -2,13 +2,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Janus Presentation Framework</title>
|
||||
<title>Tech Talk: Responsive Typography</title>
|
||||
<link rel="stylesheet" href="styles/main.css" charset="utf-8">
|
||||
<script type="text/javascript" src="libs/prefixfree/prefixfree.min.js"></script>
|
||||
</head>
|
||||
<body class="is-loading">
|
||||
<nav>
|
||||
<label for="commandField">Command:</label>
|
||||
<input list="commands" type="text" id="commandField" autocomplete="off" pattern="[0-9]+|[spc]">
|
||||
<input list="commands" type="text" id="commandField" autocomplete="off" pattern="[0-9]+|[spcr]">
|
||||
<ul class="help">
|
||||
<li>
|
||||
<strong>#</strong> - jump to a slide item
|
||||
|
@ -22,56 +23,118 @@
|
|||
<li>
|
||||
<strong>c</strong> - clone window
|
||||
</li>
|
||||
<li>
|
||||
<strong>r</strong> - reset stored state
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
|
||||
<section style="background: linear-gradient(-30deg, black, rebeccapurple)">
|
||||
<h1>Janus Presentations</h1>
|
||||
<h2>Hotkey powered, multi-window presentations</h2>
|
||||
<div><small>
|
||||
Press <kbd>Escape</kbd> for commands. <kbd>Page Up</kbd> / <kbd>Page Down</kbd> to navigate.
|
||||
</small></div>
|
||||
<div><a href="https://github.com/seigler/janus">Get the code on GitHub</a></div>
|
||||
</section>
|
||||
|
||||
<section style="background: linear-gradient(-30deg, #400, black)">
|
||||
<h1>Hotkeys</h1>
|
||||
<div janus-timeline>
|
||||
Clone Window: <kbd>F2</kbd>
|
||||
</div>
|
||||
<div janus-timeline>
|
||||
Navigation: <kbd>Page Down</kbd>, <kbd>Page Up</kbd>
|
||||
</div>
|
||||
<div janus-timeline>
|
||||
Toggle Presenter Mode: <kbd>F1</kbd>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="color: black">
|
||||
<img class="cover" src="https://placekitten.com/1024/768" alt="" />
|
||||
<h1>Self-scaling cover images with <code>object-fit</code></h1>
|
||||
</section>
|
||||
<section style="background: linear-gradient(60deg, #060, black)">
|
||||
<h1>Simple default markup</h1>
|
||||
<div>
|
||||
Any <code>section</code> tag or item with attribute <code>janus-timeline</code> is added to the timeline and revealed in sequence.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="background: linear-gradient(-60deg, black, #630)">
|
||||
<h1>Animations controlled by CSS</h1>
|
||||
<div>
|
||||
Framework sets custom attribute <code>janus-timeline</code> to <code>past</code>, <code>present</code>, or <code>future</code>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="color: black; background-color: #FA0">
|
||||
<h1>Cross-window synchronization with JavaScript <code>localStorage</code> events.</h1>
|
||||
<section class="title-page" style="background: linear-gradient(to bottom, black 30%, rebeccapurple)">
|
||||
<h1 style="font-size: 3.2em">Janus</h1>
|
||||
<h2>A live-coding web presentation framework.</h2>
|
||||
<p><kbd>Page Down</kbd> to continue</p>
|
||||
<p><kbd>Esc</kbd> for commands</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h1>The end.</h1>
|
||||
<blockquote>
|
||||
<p>Blockquotes are pretty cool, right?</p>
|
||||
<cite>Thomas Jefferson, probably</cite>
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<section id="demo1" style="--mouse-x: 0.5">
|
||||
<div class="live-coding col" style="justify-content: center;">
|
||||
<div class="browser" style="max-width: calc(100vw * var(--mouse-x)); min-width: 2em; margin-bottom: 0.2em;">
|
||||
<iframe src="about:blank" frameborder="0"></iframe>
|
||||
</div>
|
||||
<div class="row" style="min-height: 50%">
|
||||
<input type="radio" id="demo1-markup" name="demo1" janus-sync>
|
||||
<div class="live-coding__component" style="--theme-color: #E34F26;">
|
||||
<label for="demo1-markup">HTML</label>
|
||||
<textarea name="html" cols="1" id="demo1-html" spellcheck="false" janus-sync>
|
||||
<nav>
|
||||
<a href="#">Home</a>
|
||||
<a href="#">Shop</a>
|
||||
<a href="#">Blog</a>
|
||||
<a href="#">About</a>
|
||||
</nav>
|
||||
<div class="content">
|
||||
<div class="sidebar">Ads ads ads ads</div>
|
||||
<main>
|
||||
<section>Content content content</section>
|
||||
<section>Content content content</section>
|
||||
<section>Content content content</section>
|
||||
<section>Content content content</section>
|
||||
</main>
|
||||
</div>
|
||||
<footer>© by us, forever. Neener neener.</footer>
|
||||
</textarea>
|
||||
</div>
|
||||
<input type="radio" id="demo1-styles" name="demo1" checked janus-sync>
|
||||
<div class="live-coding__component" style="--theme-color: #0070BA;">
|
||||
<label for="demo1-styles">CSS</label>
|
||||
<textarea name="css" cols="1" id="demo1-css" spellcheck="false" janus-sync>* { margin: 0; padding: 0; box-size: border-box; }
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
font-size: calc(1vw + 1em);
|
||||
height: 100vh;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
margin-bottom: -0.1em;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
nav > a {
|
||||
flex: 1 1 auto;
|
||||
max-width: 7em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 0.5em;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
border: 0.1em solid black;
|
||||
}
|
||||
nav > a + a {
|
||||
margin-left: -0.1em;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.sidebar {
|
||||
width: 7em;
|
||||
order: 1;
|
||||
border: 0.1em solid;
|
||||
}
|
||||
main {
|
||||
flex-grow: 1;
|
||||
}
|
||||
section {
|
||||
min-height: 3em;
|
||||
border: solid;
|
||||
border-width: 0.1em 0;
|
||||
}
|
||||
section + section {
|
||||
margin-top: -0.1em;
|
||||
}
|
||||
</textarea>
|
||||
</div>
|
||||
<input type="radio" id="demo1-script" name="demo1" janus-sync>
|
||||
<div class="live-coding__component" style="--theme-color: #F7DF1E;">
|
||||
<label for="demo1-script">JS</label>
|
||||
<textarea name="js" cols="1" id="demo1-js" spellcheck="false" janus-sync>// no JS needed
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h1>The End</h1>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
|
18
libs/prefixfree/prefixfree.min.js
vendored
Normal file
18
libs/prefixfree/prefixfree.min.js
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* StyleFix 1.0.3 & PrefixFree 1.0.7
|
||||
* @author Lea Verou
|
||||
* MIT license
|
||||
*/
|
||||
(function(){function k(a,b){return[].slice.call((b||document).querySelectorAll(a))}if(window.addEventListener){var e=window.StyleFix={link:function(a){var c=a.href||a.getAttribute("data-href");try{if(!c||"stylesheet"!==a.rel||a.hasAttribute("data-noprefix"))return}catch(b){return}var d=c.replace(/[^\/]+$/,""),h=(/^[a-z]{3,10}:/.exec(d)||[""])[0],l=(/^[a-z]{3,10}:\/\/[^\/]+/.exec(d)||[""])[0],g=/^([^?]*)\??/.exec(c)[1],m=a.parentNode,f=new XMLHttpRequest,n;f.onreadystatechange=function(){4===f.readyState&&
|
||||
n()};n=function(){var b=f.responseText;if(b&&a.parentNode&&(!f.status||400>f.status||600<f.status)){b=e.fix(b,!0,a);if(d)var b=b.replace(/url\(\s*?((?:"|')?)(.+?)\1\s*?\)/gi,function(b,a,c){return/^([a-z]{3,10}:|#)/i.test(c)?b:/^\/\//.test(c)?'url("'+h+c+'")':/^\//.test(c)?'url("'+l+c+'")':/^\?/.test(c)?'url("'+g+c+'")':'url("'+d+c+'")'}),c=d.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1"),b=b.replace(RegExp("\\b(behavior:\\s*?url\\('?\"?)"+c,"gi"),"$1");c=document.createElement("style");c.textContent='/*# sourceURL='+a.getAttribute('href')+' */\n/*@ sourceURL='+a.getAttribute('href')+' */\n' +
|
||||
b;c.media=a.media;c.disabled=a.disabled;c.setAttribute("data-href",a.getAttribute("href"));m.insertBefore(c,a);m.removeChild(a);c.media=a.media}};try{f.open("GET",c),f.send(null)}catch(p){"undefined"!=typeof XDomainRequest&&(f=new XDomainRequest,f.onerror=f.onprogress=function(){},f.onload=n,f.open("GET",c),f.send(null))}a.setAttribute("data-inprogress","")},styleElement:function(a){if(!a.hasAttribute("data-noprefix")){var b=a.disabled;a.textContent=e.fix(a.textContent,!0,a);a.disabled=b}},styleAttribute:function(a){var b=
|
||||
a.getAttribute("style"),b=e.fix(b,!1,a);a.setAttribute("style",b)},process:function(){k('link[rel="stylesheet"]:not([data-inprogress])').forEach(StyleFix.link);k("style").forEach(StyleFix.styleElement);k("[style]").forEach(StyleFix.styleAttribute)},register:function(a,b){(e.fixers=e.fixers||[]).splice(void 0===b?e.fixers.length:b,0,a)},fix:function(a,b,c){for(var d=0;d<e.fixers.length;d++)a=e.fixers[d](a,b,c)||a;return a},camelCase:function(a){return a.replace(/-([a-z])/g,function(b,a){return a.toUpperCase()}).replace("-",
|
||||
"")},deCamelCase:function(a){return a.replace(/[A-Z]/g,function(b){return"-"+b.toLowerCase()})}};(function(){setTimeout(function(){k('link[rel="stylesheet"]').forEach(StyleFix.link)},10);document.addEventListener("DOMContentLoaded",StyleFix.process,!1)})()}})();
|
||||
(function(k){function e(b,c,d,h,l){b=a[b];b.length&&(b=RegExp(c+"("+b.join("|")+")"+d,"gi"),l=l.replace(b,h));return l}if(window.StyleFix&&window.getComputedStyle){var a=window.PrefixFree={prefixCSS:function(b,c,d){var h=a.prefix;-1<a.functions.indexOf("linear-gradient")&&(b=b.replace(/(\s|:|,)(repeating-)?linear-gradient\(\s*(-?\d*\.?\d*)deg/ig,function(b,a,c,d){return a+(c||"")+"linear-gradient("+(90-d)+"deg"}));b=e("functions","(\\s|:|,)","\\s*\\(","$1"+h+"$2(",b);b=e("keywords","(\\s|:)","(\\s|;|\\}|$)",
|
||||
"$1"+h+"$2$3",b);b=e("properties","(^|\\{|\\s|;)","\\s*:","$1"+h+"$2:",b);if(a.properties.length){var l=RegExp("\\b("+a.properties.join("|")+")(?!:)","gi");b=e("valueProperties","\\b",":(.+?);",function(a){return a.replace(l,h+"$1")},b)}c&&(b=e("selectors","","\\b",a.prefixSelector,b),b=e("atrules","@","\\b","@"+h+"$1",b));b=b.replace(RegExp("-"+h,"g"),"-");return b=b.replace(/-\*-(?=[a-z]+)/gi,a.prefix)},property:function(b){return(0<=a.properties.indexOf(b)?a.prefix:"")+b},value:function(b,c){b=
|
||||
e("functions","(^|\\s|,)","\\s*\\(","$1"+a.prefix+"$2(",b);b=e("keywords","(^|\\s)","(\\s|$)","$1"+a.prefix+"$2$3",b);0<=a.valueProperties.indexOf(c)&&(b=e("properties","(^|\\s|,)","($|\\s|,)","$1"+a.prefix+"$2$3",b));return b},prefixSelector:function(b){return b.replace(/^:{1,2}/,function(b){return b+a.prefix})},prefixProperty:function(b,c){var d=a.prefix+b;return c?StyleFix.camelCase(d):d}};(function(){var b={},c=[],d=getComputedStyle(document.documentElement,null),h=document.createElement("div").style,
|
||||
l=function(a){if("-"===a.charAt(0)){c.push(a);a=a.split("-");var d=a[1];for(b[d]=++b[d]||1;3<a.length;)a.pop(),d=a.join("-"),StyleFix.camelCase(d)in h&&-1===c.indexOf(d)&&c.push(d)}};if(0<d.length)for(var g=0;g<d.length;g++)l(d[g]);else for(var e in d)l(StyleFix.deCamelCase(e));var g=0,f,k;for(k in b)d=b[k],g<d&&(f=k,g=d);a.prefix="-"+f+"-";a.Prefix=StyleFix.camelCase(a.prefix);a.properties=[];for(g=0;g<c.length;g++)e=c[g],0===e.indexOf(a.prefix)&&(f=e.slice(a.prefix.length),StyleFix.camelCase(f)in
|
||||
h||a.properties.push(f));!("Ms"!=a.Prefix||"transform"in h||"MsTransform"in h)&&"msTransform"in h&&a.properties.push("transform","transform-origin");a.properties.sort()})();(function(){function b(a,b){h[b]="";h[b]=a;return!!h[b]}var c={"linear-gradient":{property:"backgroundImage",params:"red, teal"},calc:{property:"width",params:"1px + 5%"},element:{property:"backgroundImage",params:"#foo"},"cross-fade":{property:"backgroundImage",params:"url(a.png), url(b.png), 50%"}};c["repeating-linear-gradient"]=
|
||||
c["repeating-radial-gradient"]=c["radial-gradient"]=c["linear-gradient"];var d={initial:"color","zoom-in":"cursor","zoom-out":"cursor",box:"display",flexbox:"display","inline-flexbox":"display",flex:"display","inline-flex":"display",grid:"display","inline-grid":"display","max-content":"width","min-content":"width","fit-content":"width","fill-available":"width"};a.functions=[];a.keywords=[];var h=document.createElement("div").style,e;for(e in c){var g=c[e],k=g.property,g=e+"("+g.params+")";!b(g,k)&&
|
||||
b(a.prefix+g,k)&&a.functions.push(e)}for(var f in d)k=d[f],!b(f,k)&&b(a.prefix+f,k)&&a.keywords.push(f)})();(function(){function b(a){e.textContent=a+"{}";return!!e.sheet.cssRules.length}var c={":read-only":null,":read-write":null,":any-link":null,"::selection":null},d={keyframes:"name",viewport:null,document:'regexp(".")'};a.selectors=[];a.atrules=[];var e=k.appendChild(document.createElement("style")),l;for(l in c){var g=l+(c[l]?"("+c[l]+")":"");!b(g)&&b(a.prefixSelector(g))&&a.selectors.push(l)}for(var m in d)g=
|
||||
m+" "+(d[m]||""),!b("@"+g)&&b("@"+a.prefix+g)&&a.atrules.push(m);k.removeChild(e)})();a.valueProperties=["transition","transition-property"];k.className+=" "+a.prefix;StyleFix.register(a.prefixCSS)}})(document.documentElement);
|
266
scripts/app.js
266
scripts/app.js
|
@ -1,14 +1,20 @@
|
|||
/*jslint browser: true*/
|
||||
/*global shortcut*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var slides = [],
|
||||
currentSlideNumber = 0,
|
||||
slideNext,
|
||||
slidePrev,
|
||||
commandBar,
|
||||
commandField,
|
||||
commandBarVisible = true;
|
||||
syncFormElements = [],
|
||||
currentSlideNumber = 0,
|
||||
slideNext,
|
||||
slidePrev,
|
||||
commandBar,
|
||||
commandField,
|
||||
commandBarVisible = true,
|
||||
localStorageActions;
|
||||
|
||||
/* Dom helper functions. Who needs JQuery?? */
|
||||
function $(selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
@ -39,19 +45,17 @@
|
|||
});
|
||||
}
|
||||
|
||||
var sessionListener = function(e) {
|
||||
if (e.url === window.location.href) {
|
||||
if (e.key === 'janus-currentSlideNumber') {
|
||||
setCurrentSlide(+e.newValue, false);
|
||||
}
|
||||
function sessionListener(event) {
|
||||
if (event.url === window.location.href && localStorageActions[event.key]) {
|
||||
localStorageActions[event.key](event);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var toggleCommandBar = function() {
|
||||
if (commandBarVisible) {
|
||||
function toggleCommandBar(visible) {
|
||||
if (visible === false) {
|
||||
commandBar.style.display = 'none';
|
||||
commandBarVisible = false;
|
||||
} else {
|
||||
} else if (visible === true) {
|
||||
commandBar.style.display = 'flex';
|
||||
commandField.value = '';
|
||||
commandField.focus();
|
||||
|
@ -59,70 +63,220 @@
|
|||
}
|
||||
}
|
||||
|
||||
var commandListener = function(event) {
|
||||
function setMouseX(value, storeChange) {
|
||||
$('section[janus-timeline="present"]').style.setProperty("--mouse-x", value);
|
||||
if (storeChange) {
|
||||
localStorage.setItem('mouse-x', value);
|
||||
}
|
||||
}
|
||||
|
||||
function mouseListener(event) {
|
||||
var x = event.clientX / window.innerWidth;
|
||||
if (event.ctrlKey) {
|
||||
setMouseX(x, true);
|
||||
}
|
||||
}
|
||||
|
||||
function runCommand(command) {
|
||||
var commands, s;
|
||||
commands = {
|
||||
's': function () {
|
||||
document.body.classList.toggle('simulate-projection');
|
||||
},
|
||||
'p': function () {
|
||||
document.body.classList.toggle('show-notes');
|
||||
},
|
||||
'c': function () {
|
||||
window.open(window.location.href, '_blank');
|
||||
},
|
||||
'r': function () {
|
||||
localStorage.clear();
|
||||
setCurrentSlide(0, true);
|
||||
}
|
||||
};
|
||||
s = command.split();
|
||||
if (s.length === 1 && /^[0-9]+$/.test(s[0])) {
|
||||
setCurrentSlide(+s[0], true);
|
||||
} else if (s.length === 1 && commands[s]) {
|
||||
commands[s]();
|
||||
}
|
||||
}
|
||||
|
||||
function commandListener(event) {
|
||||
var typed = String.fromCharCode(event.keyCode).toLowerCase();
|
||||
console.log(event);
|
||||
console.log(typed);
|
||||
if (/[0-9]/.test(typed)) {
|
||||
return;
|
||||
} else if (event.keyCode === 13) {
|
||||
runCommand(commandField.value);
|
||||
toggleCommandBar();
|
||||
} else if (/[spc]/.test(typed)) {
|
||||
toggleCommandBar(false);
|
||||
} else if (/[spcr]/.test(typed)) {
|
||||
runCommand(commandField.value + typed);
|
||||
toggleCommandBar();
|
||||
}
|
||||
};
|
||||
|
||||
var runCommand = function(command) {
|
||||
var s = command.split();
|
||||
if (s.length === 1 && /^[0-9]+$/.test(s[0])) {
|
||||
setCurrentSlide(+s[0], true);
|
||||
} else if (s.length === 1) {
|
||||
switch(s[0]) {
|
||||
case 's':
|
||||
document.body.classList.toggle('simulate-projection');
|
||||
break;
|
||||
case 'p':
|
||||
document.body.classList.toggle('show-notes');
|
||||
break;
|
||||
case 'c':
|
||||
window.open(window.location.href, '_blank');
|
||||
break;
|
||||
}
|
||||
toggleCommandBar(false);
|
||||
}
|
||||
}
|
||||
|
||||
var init = function() {
|
||||
function editorListenerGenerator(editor) {
|
||||
var markupEl, styleEl, scriptEl, frameEl, frameWindow, listener;
|
||||
markupEl = editor.querySelector('[name="html"]');
|
||||
styleEl = editor.querySelector('[name="css"]');
|
||||
scriptEl = editor.querySelector('[name="js"]');
|
||||
frameEl = editor.querySelector('iframe');
|
||||
if (frameEl.contentWindow) {
|
||||
frameWindow = frameEl.contentWindow;
|
||||
} else {
|
||||
if (frameEl.contentDocument && frameEl.contentDocument.document) {
|
||||
frameWindow = frameEl.contentDocument.document;
|
||||
} else {
|
||||
frameWindow = frameEl.contentDocument;
|
||||
}
|
||||
}
|
||||
function editorAction() {
|
||||
var compiled = '<!DOCTYPE html><html><head><style>' + styleEl.value + '</style></head><body>' + markupEl.value + '<scr' + 'ipt>' + scriptEl.value + '</scr' + 'ipt></body></html>';
|
||||
frameWindow.document.open();
|
||||
frameWindow.document.write(compiled);
|
||||
frameWindow.document.close();
|
||||
// frameEl.src = 'data:text/html;charset=utf-8,' + encodeURI(compiled);
|
||||
}
|
||||
// listener = function () {
|
||||
// if (this.timeoutId) {
|
||||
// window.clearTimeout(this.timeoutId);
|
||||
// }
|
||||
// this.timeoutId = window.setTimeout(editorAction, 100);
|
||||
// };
|
||||
[markupEl, styleEl, scriptEl].forEach(function (current, index, array) {
|
||||
var syncFormElementsIndex = syncFormElements.indexOf(current);
|
||||
current.addEventListener('keyup', editorAction);
|
||||
if (syncFormElementsIndex >= 0) {
|
||||
// add a listener to receive changes from localStorage
|
||||
localStorageActions['janus-input-' + syncFormElementsIndex] = function (event) {
|
||||
var storedValue = event.newValue,
|
||||
decodedValue = storedValue.split('/', 2);
|
||||
decodedValue.push(storedValue.slice(decodedValue.join(' ').length + 1));
|
||||
current.value = decodedValue[2];
|
||||
current.setSelectionRange(+decodedValue[0], +decodedValue[1]);
|
||||
editorAction();
|
||||
current.focus();
|
||||
};
|
||||
}
|
||||
});
|
||||
editorAction();
|
||||
}
|
||||
|
||||
function init() {
|
||||
var storedSlideNumber;
|
||||
commandField = $('#commandField');
|
||||
commandField.addEventListener('keydown', commandListener);
|
||||
commandField.addEventListener('blur', function (event) {
|
||||
toggleCommandBar(false);
|
||||
});
|
||||
commandBar = $('body > nav');
|
||||
toggleCommandBar();
|
||||
toggleCommandBar(false);
|
||||
|
||||
slides = $$('main>section, [janus-timeline]');
|
||||
currentSlideNumber = 0;
|
||||
shortcut.add('F1', function() {
|
||||
document.body.classList.toggle('show-notes');
|
||||
});
|
||||
shortcut.add('F2', function() {
|
||||
window.open(window.location.href, '_blank');
|
||||
});
|
||||
shortcut.add('Page_down', function() {
|
||||
shortcut.add('Page_down', function () {
|
||||
setCurrentSlide(currentSlideNumber + 1, true);
|
||||
});
|
||||
shortcut.add('Page_up', function() {
|
||||
shortcut.add('Page_up', function () {
|
||||
setCurrentSlide(currentSlideNumber - 1, true);
|
||||
});
|
||||
shortcut.add('Escape', toggleCommandBar);
|
||||
var storedSlideNumber;
|
||||
if (storedSlideNumber = localStorage.getItem('janus-currentSlideNumber')) {
|
||||
shortcut.add('Escape', function () {
|
||||
toggleCommandBar(!commandBarVisible);
|
||||
});
|
||||
|
||||
storedSlideNumber = localStorage.getItem('janus-currentSlideNumber');
|
||||
if (storedSlideNumber) {
|
||||
setCurrentSlide(storedSlideNumber, false);
|
||||
} else {
|
||||
setCurrentSlide(0);
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', mouseListener);
|
||||
|
||||
localStorageActions = {
|
||||
'janus-currentSlideNumber': function (event) {
|
||||
setCurrentSlide(+event.newValue, false);
|
||||
},
|
||||
'mouse-x' : function (event) {
|
||||
setMouseX(event.newValue, false);
|
||||
}
|
||||
};
|
||||
|
||||
$$('[janus-sync]').forEach(function (current, index, array) {
|
||||
var currentKey, storedValue, decodedValue, group;
|
||||
syncFormElements.push(current);
|
||||
if (current.type === 'textarea' || current.type === 'text') {
|
||||
currentKey = 'janus-input-' + index;
|
||||
storedValue = localStorage.getItem(currentKey);
|
||||
if (storedValue) {
|
||||
decodedValue = storedValue.split('/', 2);
|
||||
decodedValue.push(storedValue.slice(decodedValue.join(' ').length + 1));
|
||||
current.value = decodedValue[2];
|
||||
current.setSelectionRange(+decodedValue[0], +decodedValue[1]);
|
||||
} else {
|
||||
localStorage.setItem(currentKey, '0/0/' + current.value);
|
||||
}
|
||||
// add a listener to store changes
|
||||
current.addEventListener('keyup', function () {
|
||||
localStorage.setItem(currentKey, current.selectionStart + '/' + current.selectionEnd + '/' + current.value);
|
||||
});
|
||||
// add a listener to respond to localStorage updates
|
||||
if (!localStorageActions[currentKey]) {
|
||||
localStorageActions[currentKey] = function (event) {
|
||||
var storedValue = event.newValue,
|
||||
decodedValue = storedValue.split('/', 2);
|
||||
decodedValue.push(storedValue.slice(decodedValue.join(' ').length + 1));
|
||||
current.value = decodedValue[2];
|
||||
current.focus();
|
||||
current.setSelectionRange(+decodedValue[0], +decodedValue[1]);
|
||||
};
|
||||
}
|
||||
} else if (current.type === 'checkbox') {
|
||||
currentKey = 'janus-input-' + index;
|
||||
storedValue = localStorage.getItem(currentKey);
|
||||
if (storedValue !== null) {
|
||||
current.checked = (storedValue === 'true');
|
||||
} else {
|
||||
localStorage.setItem(currentKey, current.checked);
|
||||
}
|
||||
// add a listener to store changes
|
||||
current.addEventListener('change', function () {
|
||||
localStorage.setItem(currentKey, current.checked);
|
||||
});
|
||||
// add a listener to respond to localStorage updates
|
||||
if (!localStorageActions[currentKey]) {
|
||||
localStorageActions[currentKey] = function (event) {
|
||||
current.checked = (event.newValue === 'true');
|
||||
};
|
||||
}
|
||||
} else if (current.type === 'radio') {
|
||||
group = current.getAttribute('name');
|
||||
currentKey = 'janus-input-' + group;
|
||||
storedValue = localStorage.getItem(currentKey);
|
||||
if (storedValue !== null && +storedValue === index) {
|
||||
current.checked = true;
|
||||
} else if (current.checked) {
|
||||
localStorage.setItem(currentKey, index);
|
||||
}
|
||||
// add a listener to store changes
|
||||
current.addEventListener('change', function () {
|
||||
localStorage.setItem(currentKey, index);
|
||||
});
|
||||
// add a listener to respond to localStorage updates
|
||||
if (!localStorageActions[currentKey]) {
|
||||
localStorageActions[currentKey] = function (event) {
|
||||
syncFormElements[+event.newValue].checked = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$$('.live-coding').forEach(function (current, index, array) {
|
||||
editorListenerGenerator(current);
|
||||
});
|
||||
|
||||
document.body.classList.remove('is-loading');
|
||||
};
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
window.addEventListener('storage', sessionListener);
|
||||
})();
|
||||
}());
|
||||
|
|
BIN
styles/browser-bar.png
Normal file
BIN
styles/browser-bar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
264
styles/main.css
264
styles/main.css
|
@ -7,15 +7,33 @@
|
|||
}
|
||||
|
||||
:root {
|
||||
--main-font: sans-serif;
|
||||
--monospace-font: monospace;
|
||||
--mouse-x: 100vw;
|
||||
|
||||
box-sizing: border-box;
|
||||
font-family: sans-serif;
|
||||
font-size: calc(2vw + 2em);
|
||||
font-family: var(--main-font);
|
||||
font-size: calc(5vw);
|
||||
cursor: default;
|
||||
line-height: 1.1;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
::selection {
|
||||
text-shadow: none;
|
||||
background: #FF0;
|
||||
color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
background-image: linear-gradient(to right, currentcolor, currentcolor);
|
||||
background-position: 0 1.9ex;
|
||||
background-repeat: repeat-x;
|
||||
background-size: 8px 0.08ex;
|
||||
}
|
||||
|
||||
li {
|
||||
|
@ -23,29 +41,83 @@ li {
|
|||
}
|
||||
|
||||
code {
|
||||
font-family: Consolas, monaco, monospace;
|
||||
font-family: monospace;
|
||||
font-family: var(--monospace-font, monospace);
|
||||
}
|
||||
|
||||
kbd {
|
||||
font-family: Consolas, monaco, monospace;
|
||||
background-color: hsla(0, 0%, 100%, 0.2);
|
||||
border-radius: 0.5em;
|
||||
padding: 0.1em 0.4em 0;
|
||||
margin: -0.1em 0 0;
|
||||
font-size: 0.7em;
|
||||
border-radius: 0.2em;
|
||||
padding: 0.1em;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
border: 0.1em solid;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
font-family: var(--monospace-font, monospace);
|
||||
font-size: 0.5em;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
position: relative;
|
||||
margin: 0 -10vw;
|
||||
padding: 0 10vw 0.1em;
|
||||
background-color: hsla(0, 0%, 100%, 0.2);
|
||||
font-size: 1.5em;
|
||||
font-weight: 400;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1rem;
|
||||
font-size: 1em;
|
||||
font-weight: 900;
|
||||
}
|
||||
h1, h2 {
|
||||
font-weight: bold;
|
||||
|
||||
sup {
|
||||
display: inline-block;
|
||||
margin-top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
display: inline-block;
|
||||
margin-bottom: -0.5em;
|
||||
}
|
||||
|
||||
img.thumbnail {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
iframe {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
quotes: "\201C""\201D""\2018""\2019";
|
||||
text-align: left;
|
||||
margin: 0 2em;
|
||||
}
|
||||
|
||||
blockquote > p::before, blockquote > p::after {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
margin: -0.2em;
|
||||
font-size: 4em;
|
||||
opacity: 0.25;
|
||||
pointer-events: none;
|
||||
}
|
||||
blockquote > p::before {
|
||||
content: open-quote;
|
||||
}
|
||||
blockquote > p::after {
|
||||
content: close-quote;
|
||||
}
|
||||
|
||||
blockquote > cite {
|
||||
font-style: normal;
|
||||
font-size: 0.8em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
blockquote > cite::before {
|
||||
content: '- ';
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
|
@ -56,6 +128,11 @@ body {
|
|||
margin: 0;
|
||||
background-color: black;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
@ -75,7 +152,7 @@ body.is-loading:after {
|
|||
}
|
||||
|
||||
body.simulate-projection {
|
||||
filter: brightness(1.5) contrast(0.65);
|
||||
filter: contrast(0.8);
|
||||
}
|
||||
|
||||
main {
|
||||
|
@ -85,6 +162,27 @@ main {
|
|||
height: 100vh;
|
||||
}
|
||||
|
||||
.row, .col {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.row--shrink, .col--shrink {
|
||||
flex: 0 1 auto;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* Components */
|
||||
nav {
|
||||
flex-wrap: wrap;
|
||||
|
@ -93,11 +191,11 @@ nav {
|
|||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.25em 0.25em 4em;
|
||||
padding: 0.25em 0.25em 6em;
|
||||
transition: transform ease 0.5s;
|
||||
font-size: 0.5rem;
|
||||
z-index: 2;
|
||||
background-image: linear-gradient(to top, hsla(0, 0%, 0%, 0), hsla(0, 0%, 0%, 0.5));
|
||||
background-image: linear-gradient(to top, hsla(0, 0%, 0%, 0), hsla(0, 0%, 0%, 1));
|
||||
}
|
||||
|
||||
nav > label {
|
||||
|
@ -127,7 +225,7 @@ ul.help {
|
|||
|
||||
ul.help li {
|
||||
display: inline-block;
|
||||
background-color: hsla(0, 0%, 100%, 0.2);
|
||||
background-color: hsla(0, 0%, 40%, 0.6);
|
||||
border-radius: 0.5em;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
@ -141,7 +239,6 @@ section {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
background-color: black;
|
||||
|
@ -149,34 +246,134 @@ section {
|
|||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
/*
|
||||
section > * {
|
||||
transform: rotate(-7deg);
|
||||
/* live-coding */
|
||||
|
||||
.live-coding {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
*/
|
||||
|
||||
.live-coding textarea {
|
||||
font-size: 0.5em;
|
||||
resize: none;
|
||||
flex: 1 1 auto;
|
||||
color: white;
|
||||
background: #333;
|
||||
border: none;
|
||||
padding: 0.1em;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.live-coding textarea::selection {
|
||||
background-color: black;
|
||||
color: var(--theme-color, white);
|
||||
}
|
||||
|
||||
.live-coding__component {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.col > .live-coding__component {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.live-coding__component label {
|
||||
line-height: 1;
|
||||
color: var(--theme-color, inherit);
|
||||
}
|
||||
|
||||
.hidden,
|
||||
.live-coding input[type="radio"],
|
||||
.live-coding input[type="checkbox"],
|
||||
.live-coding input[type="radio"]:not(:checked) + .live-coding__component > textarea,
|
||||
.live-coding input[type="checkbox"]:not(:checked) + .live-coding__component > textarea {
|
||||
position: fixed;
|
||||
visibility: hidden;
|
||||
right: 110vw;
|
||||
}
|
||||
|
||||
.live-coding input[type="radio"]:not(:checked) + .live-coding__component,
|
||||
.live-coding input[type="checkbox"]:not(:checked) + .live-coding__component {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.row > .live-coding__component label {
|
||||
writing-mode: vertical-rl;
|
||||
text-align: right;
|
||||
transform: rotate(180deg);
|
||||
border-left: 0.1em solid var(--theme-color, currentcolor);
|
||||
line-height: 1;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.col > .live-coding__component label {
|
||||
text-align: left;
|
||||
max-height: 1em;
|
||||
border-bottom: 0.1em solid var(--theme-color, currentcolor);
|
||||
}
|
||||
|
||||
/* adapted from Lea Verou's http://dabblet.com/gist/14cf7ecbd12505768478b36510f623c8 */
|
||||
.browser {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
margin: 0 auto;
|
||||
border-radius: 0.5vmin;
|
||||
overflow: hidden;
|
||||
--chrome-height: 4vmin;
|
||||
padding-top: var(--chrome-height);
|
||||
background-color: #DDD;
|
||||
}
|
||||
|
||||
.browser::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; right: 0; left: 0;
|
||||
border: solid;
|
||||
--right: 146; --left: 201;
|
||||
border-width: var(--chrome-height) calc(var(--right) / 38 * var(--chrome-height)) 0 calc(var(--left) / 38 * var(--chrome-height));
|
||||
border-image: url('browser-bar.png') 100% var(--right) 0 var(--left) repeat;
|
||||
}
|
||||
|
||||
.browser iframe {
|
||||
pointer-events: none;
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* states and transitions */
|
||||
|
||||
[janus-timeline='past'] {
|
||||
transition: transform ease-in 0.5s, opacity ease-in 0.5s, visibility step-end 0.5s;
|
||||
transition: transform ease 0.5s, opacity ease 0.5s, visibility step-end 0.5s;
|
||||
}
|
||||
|
||||
section[janus-timeline='present'] [janus-timeline='past'] {
|
||||
transition: transform ease 0.5s, opacity ease 0.5s;
|
||||
}
|
||||
[janus-timeline='present'] {
|
||||
transition: transform ease-out 0.5s, opacity ease-out 0.5s, visibility step-start 0.5s;
|
||||
transition: transform ease 0.5s, opacity ease 0.5s, visibility step-start 0.5s;
|
||||
}
|
||||
|
||||
[janus-timeline='future'] {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
body.show-notes section [janus-timeline='future'] {
|
||||
visibility: visible;
|
||||
opacity: 0.25;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
body.show-notes section[janus-timeline='future'] [janus-timeline='future'] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
section[janus-timeline='past'] {
|
||||
section[janus-timeline='past'], body:not(.show-notes) [janus-timeline='past'][janus-past='big'], body:not(.show-notes) [janus-timeline='future'][janus-future='big'] {
|
||||
transform: translate3d(0, 0, 0) scale(5);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
@ -196,16 +393,21 @@ section[janus-timeline='future'] {
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
img.cover {
|
||||
img.cover, img.contain {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
z-index: -1;
|
||||
transform: none;
|
||||
}
|
||||
img.cover {
|
||||
object-fit: cover;
|
||||
}
|
||||
img.contain {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
body.show-notes section {
|
||||
transition: none;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue