feat: incorporate updates to Janus

This commit is contained in:
Joshua Seigler 2016-10-28 11:23:42 -04:00
parent 36845011f8
commit f32b318593
10 changed files with 1198 additions and 113 deletions

View file

@ -2,6 +2,8 @@
This is a browser-based presentation framework with two connected views: one for the presenter, and one for the audience.
[See it in action!](https://seigler.github.io/janus)
## Features:
* Presenter view and presentation view, connected through Localstorage
@ -15,4 +17,6 @@ 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.
Currently the live editor completely blows up in Firefox. I think it's related to a flexbox bug?

675
libs/behave/behave.js Normal file
View file

@ -0,0 +1,675 @@
/*
* Behave.js
*
* Copyright 2013, Jacob Kelley - http://jakiestfu.com/
* Released under the MIT Licence
* http://opensource.org/licenses/MIT
*
* Github: http://github.com/jakiestfu/Behave.js/
* Version: 1.5
*/
(function (undefined) {
'use strict';
var BehaveHooks = BehaveHooks || (function () {
var hooks = {};
return {
add: function (hookName, fn) {
if (typeof hookName == "object") {
var i;
for (i = 0; i < hookName.length; i++) {
var theHook = hookName[i];
if (!hooks[theHook]) {
hooks[theHook] = [];
}
hooks[theHook].push(fn);
}
} else {
if (!hooks[hookName]) {
hooks[hookName] = [];
}
hooks[hookName].push(fn);
}
},
get: function (hookName) {
if (hooks[hookName]) {
return hooks[hookName];
}
}
};
})(),
Behave = Behave || function (userOpts) {
if (typeof String.prototype.repeat !== 'function') {
String.prototype.repeat = function (times) {
if (times < 1) {
return '';
}
if (times % 2) {
return this.repeat(times - 1) + this;
}
var half = this.repeat(times / 2);
return half + half;
};
}
if (typeof Array.prototype.filter !== 'function') {
Array.prototype.filter = function (func /*, thisp */ ) {
if (this === null) {
throw new TypeError();
}
var t = Object(this),
len = t.length >>> 0;
if (typeof func != "function") {
throw new TypeError();
}
var res = [],
thisp = arguments[1];
for (var i = 0; i < len; i++) {
if (i in t) {
var val = t[i];
if (func.call(thisp, val, i, t)) {
res.push(val);
}
}
}
return res;
};
}
var defaults = {
textarea: null,
replaceTab: true,
softTabs: true,
tabSize: 4,
autoOpen: true,
overwrite: true,
autoStrip: true,
autoIndent: true,
fence: false
},
tab,
newLine,
charSettings = {
keyMap: [
{
open: "\"",
close: "\"",
canBreak: false
},
{
open: "'",
close: "'",
canBreak: false
},
{
open: "(",
close: ")",
canBreak: false
},
{
open: "[",
close: "]",
canBreak: true
},
{
open: "{",
close: "}",
canBreak: true
}
]
},
utils = {
_callHook: function (hookName, passData) {
var hooks = BehaveHooks.get(hookName);
passData = typeof passData == "boolean" && passData === false ? false : true;
if (hooks) {
if (passData) {
var theEditor = defaults.textarea,
textVal = theEditor.value,
caretPos = utils.cursor.get(),
i;
for (i = 0; i < hooks.length; i++) {
hooks[i].call(undefined, {
editor: {
element: theEditor,
text: textVal,
levelsDeep: utils.levelsDeep()
},
caret: {
pos: caretPos
},
lines: {
current: utils.cursor.getLine(textVal, caretPos),
total: utils.editor.getLines(textVal)
}
});
}
} else {
for (i = 0; i < hooks.length; i++) {
hooks[i].call(undefined);
}
}
}
},
defineNewLine: function () {
var ta = document.createElement('textarea');
ta.value = "\n";
if (ta.value.length == 2) {
newLine = "\r\n";
} else {
newLine = "\n";
}
},
defineTabSize: function (tabSize) {
if (typeof defaults.textarea.style.OTabSize != "undefined") {
defaults.textarea.style.OTabSize = tabSize;
return;
}
if (typeof defaults.textarea.style.MozTabSize != "undefined") {
defaults.textarea.style.MozTabSize = tabSize;
return;
}
if (typeof defaults.textarea.style.tabSize != "undefined") {
defaults.textarea.style.tabSize = tabSize;
return;
}
},
cursor: {
getLine: function (textVal, pos) {
return ((textVal.substring(0, pos)).split("\n")).length;
},
get: function () {
if (typeof document.createElement('textarea').selectionStart === "number") {
return defaults.textarea.selectionStart;
} else if (document.selection) {
var caretPos = 0,
range = defaults.textarea.createTextRange(),
rangeDupe = document.selection.createRange().duplicate(),
rangeDupeBookmark = rangeDupe.getBookmark();
range.moveToBookmark(rangeDupeBookmark);
while (range.moveStart('character', -1) !== 0) {
caretPos++;
}
return caretPos;
}
},
set: function (start, end) {
if (!end) {
end = start;
}
if (defaults.textarea.setSelectionRange) {
defaults.textarea.focus();
defaults.textarea.setSelectionRange(start, end);
} else if (defaults.textarea.createTextRange) {
var range = defaults.textarea.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', start);
range.select();
}
},
selection: function () {
var textAreaElement = defaults.textarea,
start = 0,
end = 0,
normalizedValue,
range,
textInputRange,
len,
endRange;
if (typeof textAreaElement.selectionStart == "number" && typeof textAreaElement.selectionEnd == "number") {
start = textAreaElement.selectionStart;
end = textAreaElement.selectionEnd;
} else {
range = document.selection.createRange();
if (range && range.parentElement() == textAreaElement) {
normalizedValue = utils.editor.get();
len = normalizedValue.length;
textInputRange = textAreaElement.createTextRange();
textInputRange.moveToBookmark(range.getBookmark());
endRange = textAreaElement.createTextRange();
endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len;
} else {
start = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split(newLine).length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len;
} else {
end = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split(newLine).length - 1;
}
}
}
}
return start == end ? false : {
start: start,
end: end
};
}
},
editor: {
getLines: function (textVal) {
return (textVal).split("\n").length;
},
get: function () {
return defaults.textarea.value.replace(/\r/g, '');
},
set: function (data) {
defaults.textarea.value = data;
}
},
fenceRange: function () {
if (typeof defaults.fence == "string") {
var data = utils.editor.get(),
pos = utils.cursor.get(),
hacked = 0,
matchedFence = data.indexOf(defaults.fence),
matchCase = 0;
while (matchedFence >= 0) {
matchCase++;
if (pos < (matchedFence + hacked)) {
break;
}
hacked += matchedFence + defaults.fence.length;
data = data.substring(matchedFence + defaults.fence.length);
matchedFence = data.indexOf(defaults.fence);
}
if ((hacked) < pos && ((matchedFence + hacked) > pos) && matchCase % 2 === 0) {
return true;
}
return false;
} else {
return true;
}
},
isEven: function (_this, i) {
return i % 2;
},
levelsDeep: function () {
var pos = utils.cursor.get(),
val = utils.editor.get();
var left = val.substring(0, pos),
levels = 0,
i, j;
for (i = 0; i < left.length; i++) {
for (j = 0; j < charSettings.keyMap.length; j++) {
if (charSettings.keyMap[j].canBreak) {
if (charSettings.keyMap[j].open == left.charAt(i)) {
levels++;
}
if (charSettings.keyMap[j].close == left.charAt(i)) {
levels--;
}
}
}
}
var toDecrement = 0,
quoteMap = ["'", "\""];
for (i = 0; i < charSettings.keyMap.length; i++) {
if (charSettings.keyMap[i].canBreak) {
for (j in quoteMap) {
toDecrement += left.split(quoteMap[j]).filter(utils.isEven).join('').split(charSettings.keyMap[i].open).length - 1;
}
}
}
var finalLevels = levels - toDecrement;
return finalLevels >= 0 ? finalLevels : 0;
},
deepExtend: function (destination, source) {
for (var property in source) {
if (source[property] && source[property].constructor &&
source[property].constructor === Object) {
destination[property] = destination[property] || {};
utils.deepExtend(destination[property], source[property]);
} else {
destination[property] = source[property];
}
}
return destination;
},
addEvent: function addEvent(element, eventName, func) {
if (element.addEventListener) {
element.addEventListener(eventName, func, false);
} else if (element.attachEvent) {
element.attachEvent("on" + eventName, func);
}
},
removeEvent: function addEvent(element, eventName, func) {
if (element.addEventListener) {
element.removeEventListener(eventName, func, false);
} else if (element.attachEvent) {
element.detachEvent("on" + eventName, func);
}
},
preventDefaultEvent: function (e) {
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
},
intercept = {
tabKey: function (e) {
if (!utils.fenceRange()) {
return;
}
if (e.keyCode == 9) {
utils.preventDefaultEvent(e);
var toReturn = true;
utils._callHook('tab:before');
var selection = utils.cursor.selection(),
pos = utils.cursor.get(),
val = utils.editor.get();
if (selection) {
var tempStart = selection.start;
while (tempStart--) {
if (val.charAt(tempStart) == "\n") {
selection.start = tempStart + 1;
break;
}
}
var toIndent = val.substring(selection.start, selection.end),
lines = toIndent.split("\n"),
i;
if (e.shiftKey) {
for (i = 0; i < lines.length; i++) {
if (lines[i].substring(0, tab.length) == tab) {
lines[i] = lines[i].substring(tab.length);
}
}
toIndent = lines.join("\n");
utils.editor.set(val.substring(0, selection.start) + toIndent + val.substring(selection.end));
utils.cursor.set(selection.start, selection.start + toIndent.length);
} else {
for (i in lines) {
lines[i] = tab + lines[i];
}
toIndent = lines.join("\n");
utils.editor.set(val.substring(0, selection.start) + toIndent + val.substring(selection.end));
utils.cursor.set(selection.start, selection.start + toIndent.length);
}
} else {
var left = val.substring(0, pos),
right = val.substring(pos),
edited = left + tab + right;
if (e.shiftKey) {
if (val.substring(pos - tab.length, pos) == tab) {
edited = val.substring(0, pos - tab.length) + right;
utils.editor.set(edited);
utils.cursor.set(pos - tab.length);
}
} else {
utils.editor.set(edited);
utils.cursor.set(pos + tab.length);
toReturn = false;
}
}
utils._callHook('tab:after');
}
return toReturn;
},
enterKey: function (e) {
if (!utils.fenceRange()) {
return;
}
if (e.keyCode == 13) {
utils.preventDefaultEvent(e);
utils._callHook('enter:before');
var pos = utils.cursor.get(),
val = utils.editor.get(),
left = val.substring(0, pos),
right = val.substring(pos),
leftChar = left.charAt(left.length - 1),
rightChar = right.charAt(0),
numTabs = utils.levelsDeep(),
ourIndent = "",
closingBreak = "",
finalCursorPos,
i;
if (!numTabs) {
finalCursorPos = 1;
} else {
while (numTabs--) {
ourIndent += tab;
}
ourIndent = ourIndent;
finalCursorPos = ourIndent.length + 1;
for (i = 0; i < charSettings.keyMap.length; i++) {
if (charSettings.keyMap[i].open == leftChar && charSettings.keyMap[i].close == rightChar) {
closingBreak = newLine;
}
}
}
var edited = left + newLine + ourIndent + closingBreak + (ourIndent.substring(0, ourIndent.length - tab.length)) + right;
utils.editor.set(edited);
utils.cursor.set(pos + finalCursorPos);
utils._callHook('enter:after');
}
},
deleteKey: function (e) {
if (!utils.fenceRange()) {
return;
}
if (e.keyCode == 8) {
utils.preventDefaultEvent(e);
utils._callHook('delete:before');
var pos = utils.cursor.get(),
val = utils.editor.get(),
left = val.substring(0, pos),
right = val.substring(pos),
leftChar = left.charAt(left.length - 1),
rightChar = right.charAt(0),
i;
if (utils.cursor.selection() === false) {
for (i = 0; i < charSettings.keyMap.length; i++) {
if (charSettings.keyMap[i].open == leftChar && charSettings.keyMap[i].close == rightChar) {
var edited = val.substring(0, pos - 1) + val.substring(pos + 1);
utils.editor.set(edited);
utils.cursor.set(pos - 1);
return;
}
}
var edited = val.substring(0, pos - 1) + val.substring(pos);
utils.editor.set(edited);
utils.cursor.set(pos - 1);
} else {
var sel = utils.cursor.selection(),
edited = val.substring(0, sel.start) + val.substring(sel.end);
utils.editor.set(edited);
utils.cursor.set(pos);
}
utils._callHook('delete:after');
}
}
},
charFuncs = {
openedChar: function (_char, e) {
utils.preventDefaultEvent(e);
utils._callHook('openChar:before');
var pos = utils.cursor.get(),
val = utils.editor.get(),
left = val.substring(0, pos),
right = val.substring(pos),
edited = left + _char.open + _char.close + right;
defaults.textarea.value = edited;
utils.cursor.set(pos + 1);
utils._callHook('openChar:after');
},
closedChar: function (_char, e) {
var pos = utils.cursor.get(),
val = utils.editor.get(),
toOverwrite = val.substring(pos, pos + 1);
if (toOverwrite == _char.close) {
utils.preventDefaultEvent(e);
utils._callHook('closeChar:before');
utils.cursor.set(utils.cursor.get() + 1);
utils._callHook('closeChar:after');
return true;
}
return false;
}
},
action = {
filter: function (e) {
if (!utils.fenceRange()) {
return;
}
var theCode = e.which || e.keyCode;
if (theCode == 39 || theCode == 40 && e.which === 0) {
return;
}
var _char = String.fromCharCode(theCode),
i;
for (i = 0; i < charSettings.keyMap.length; i++) {
if (charSettings.keyMap[i].close == _char) {
var didClose = defaults.overwrite && charFuncs.closedChar(charSettings.keyMap[i], e);
if (!didClose && charSettings.keyMap[i].open == _char && defaults.autoOpen) {
charFuncs.openedChar(charSettings.keyMap[i], e);
}
} else if (charSettings.keyMap[i].open == _char && defaults.autoOpen) {
charFuncs.openedChar(charSettings.keyMap[i], e);
}
}
},
listen: function () {
if (defaults.replaceTab) {
utils.addEvent(defaults.textarea, 'keydown', intercept.tabKey);
}
if (defaults.autoIndent) {
utils.addEvent(defaults.textarea, 'keydown', intercept.enterKey);
}
if (defaults.autoStrip) {
utils.addEvent(defaults.textarea, 'keydown', intercept.deleteKey);
}
utils.addEvent(defaults.textarea, 'keypress', action.filter);
utils.addEvent(defaults.textarea, 'keydown', function () {
utils._callHook('keydown');
});
utils.addEvent(defaults.textarea, 'keyup', function () {
utils._callHook('keyup');
});
}
},
init = function (opts) {
if (opts.textarea) {
utils._callHook('init:before', false);
utils.deepExtend(defaults, opts);
utils.defineNewLine();
if (defaults.softTabs) {
tab = " ".repeat(defaults.tabSize);
} else {
tab = "\t";
utils.defineTabSize(defaults.tabSize);
}
action.listen();
utils._callHook('init:after', false);
}
};
this.destroy = function () {
utils.removeEvent(defaults.textarea, 'keydown', intercept.tabKey);
utils.removeEvent(defaults.textarea, 'keydown', intercept.enterKey);
utils.removeEvent(defaults.textarea, 'keydown', intercept.deleteKey);
utils.removeEvent(defaults.textarea, 'keypress', action.filter);
};
init(userOpts);
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = Behave;
}
if (typeof ender === 'undefined') {
this.Behave = Behave;
this.BehaveHooks = BehaveHooks;
}
if (typeof define === "function" && define.amd) {
define("behave", [], function () {
return Behave;
});
}
}).call(this);

3
libs/highlight.js/highlight.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#23241f}.hljs,.hljs-tag,.hljs-subst{color:#f8f8f2}.hljs-strong,.hljs-emphasis{color:#a8a8a2}.hljs-bullet,.hljs-quote,.hljs-number,.hljs-regexp,.hljs-literal,.hljs-link{color:#ae81ff}.hljs-code,.hljs-title,.hljs-section,.hljs-selector-class{color:#a6e22e}.hljs-strong{font-weight:bold}.hljs-emphasis{font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-name,.hljs-attr{color:#f92672}.hljs-symbol,.hljs-attribute{color:#66d9ef}.hljs-params,.hljs-class .hljs-title{color:#f8f8f2}.hljs-string,.hljs-type,.hljs-built_in,.hljs-builtin-name,.hljs-selector-id,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-addition,.hljs-variable,.hljs-template-variable{color:#e6db74}.hljs-comment,.hljs-deletion,.hljs-meta{color:#75715e}

18
libs/prefixfree/prefixfree.min.js vendored Normal file
View 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);

View file

@ -1,10 +1,27 @@
{
"name": "janus",
"version": "1.0.0",
"description": "This is a browser-based presentation framework with two connected views: one for the presenter, and one for the audience.",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "inliner index.html > presentation-bundle.html",
"start": "http-server -o -cors"
},
"repository": {
"type": "git",
"url": "git+https://github.com/seigler/janus.git"
},
"keywords": [
"presentations",
"html",
"slides"
],
"author": "Joshua Seigler",
"license": "ISC",
"bugs": {
"url": "https://github.com/seigler/janus/issues"
},
"homepage": "https://seigler.github.io/janus",
"devDependencies": {
"inliner": "^1.9.5"
},

View file

@ -1,14 +1,20 @@
/*jslint browser: true*/
/*global shortcut, Behave, hljs*/
(function () {
'use strict';
"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);
}
@ -21,111 +27,277 @@
if (newSlideNumber !== currentSlideNumber) {
currentSlideNumber = newSlideNumber;
if (storeChange) {
localStorage.setItem('janus-currentSlideNumber', currentSlideNumber);
localStorage.setItem("janus-currentSlideNumber", currentSlideNumber);
}
}
slides.forEach(function (item, index, array) {
if (index < currentSlideNumber) {
if (slides[index].contains(slides[currentSlideNumber])) {
item.setAttribute('janus-timeline', 'present');
item.setAttribute("janus-timeline", "present");
} else {
item.setAttribute('janus-timeline', 'past');
item.setAttribute("janus-timeline", "past");
}
} else if (index === currentSlideNumber) {
item.setAttribute('janus-timeline', 'present');
item.setAttribute("janus-timeline", "present");
} else {
item.setAttribute('janus-timeline', 'future');
item.setAttribute("janus-timeline", "future");
}
});
}
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(visible) {
function toggleCommandBar(visible) {
if (visible === false) {
commandBar.style.display = 'none';
commandBar.style.display = "none";
commandBarVisible = false;
} else if (visible === true) {
commandBar.style.display = 'flex';
commandField.value = '';
commandBar.style.display = "flex";
commandField.value = "";
commandField.focus();
commandBarVisible = true;
}
}
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();
if (/[0-9]/.test(typed)) {
return;
} else if (event.keyCode === 13) {
runCommand(commandField.value);
toggleCommandBar(false);
} else if (/[spc]/.test(typed)) {
} else if (/[spcr]/.test(typed)) {
runCommand(commandField.value + typed);
toggleCommandBar(false);
}
};
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;
}
}
}
var init = function() {
commandField = $('#commandField');
commandField.addEventListener('keydown', commandListener);
commandField.addEventListener('blur', function(event) {
function editorListenerGenerator(editor) {
var frameWindow, listener,
textareas = [
editor.querySelector("[name=\"html\"]"),
editor.querySelector("[name=\"css\"]"),
editor.querySelector("[name=\"js\"]")
],
highlights = [
editor.querySelector("[name=\"html\"] + .highlight"),
editor.querySelector("[name=\"css\"] + .highlight"),
editor.querySelector("[name=\"js\"] + .highlight")
],
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>" + textareas[1].value + "</style></head><body>" + textareas[0].value + "<scr" + "ipt>" + textareas[2].value + "</scr" + "ipt></body></html>";
frameWindow.document.open();
frameWindow.document.write(compiled);
frameWindow.document.close();
textareas.forEach(function (current, index, array) {
highlights[index].innerHTML = hljs.highlight(["html", "css", "js"][index], current.value + "\n", true).value;
});
}
textareas.forEach(function (current, index, array) {
var syncFormElementsIndex = syncFormElements.indexOf(current), editor;
editor = new Behave({
textarea: current,
replaceTab: true,
softTabs: true,
tabSize: 2,
autoOpen: true,
overwrite: true,
autoStrip: true,
autoIndent: true,
fence: false
});
// add a listener to build the source when the fields are changed
current.addEventListener('input', editorAction);
current.addEventListener('keydown', editorAction);
// add a listener to sync the highlight element and the textarea
current.addEventListener('scroll', function (event) {
highlights[index].scrollTop = current.scrollTop;
highlights[index].scrollLeft = current.scrollLeft;
}, { passive: true });
// add a listener to receive changes from localStorage
if (syncFormElementsIndex >= 0) {
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');
commandBar = $("body > nav");
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() {
slides = $$("main>section, [janus-timeline]");
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', function() {
shortcut.add("Escape", function () {
toggleCommandBar(!commandBarVisible);
});
var storedSlideNumber;
if (storedSlideNumber = localStorage.getItem('janus-currentSlideNumber')) {
storedSlideNumber = localStorage.getItem("janus-currentSlideNumber");
if (storedSlideNumber) {
setCurrentSlide(storedSlideNumber, false);
} else {
setCurrentSlide(0);
}
document.body.classList.remove('is-loading');
};
document.addEventListener('DOMContentLoaded', init);
window.addEventListener('storage', sessionListener);
})();
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, replacedText;
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 (event) {
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 (event) {
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);
}());

View file

@ -1,30 +1,3 @@
.credits > a {
display: block;
}
img.thumbnail {
height: 1em;
}
.row, .col {
display: flex;
flex-grow: 1;
flex-basis: 0;
}
.row {
flex-direction: row;
}
.col {
flex-direction: column;
}
.row > div, .col > div {
position: relative;
justify-content: center;
}
.title-page h1 {
font-size: 2em;
padding: 0.25em;

BIN
styles/browser-bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -7,15 +7,33 @@
}
:root {
--main-font: sans-serif;
--monospace-font: monospace;
--mouse-x: 0.5;
box-sizing: border-box;
font-family: sans-serif;
font-size: calc(3vmax + 3vmin);
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,25 +41,82 @@ 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;
}
h1 {
font-size: 1.5rem;
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 */
@ -52,6 +127,11 @@ body {
margin: 0;
background-color: black;
color: white;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
@ -81,6 +161,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;
@ -89,11 +190,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.75));
background-image: linear-gradient(to top, hsla(0, 0%, 0%, 0), hsla(0, 0%, 0%, 1));
}
nav > label {
@ -123,7 +224,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;
}
@ -144,16 +245,137 @@ section {
transform-origin: 50% 50%;
}
/*
section > * {
transform: rotate(-7deg);
/* live-coding */
.live-coding {
display: flex;
flex: 1 1 100%;
}
*/
.live-coding__code {
position: relative;
width: 100%;
}
.live-coding__code textarea, .live-coding__code .highlight {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 0.1em;
font-size: 0.4em;
resize: none;
color: white;
font-family: monospace;
white-space: pre-wrap;
word-wrap: break-word;
overflow: auto;
border: none;
outline: none;
text-align: left;
line-height: 1.1;
}
.live-coding__code textarea {
background: #333;
}
.live-coding__code .highlight {
pointer-events: none;
}
.live-coding__code textarea::selection {
background-color: black;
color: transparent;
}
.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 > .live-coding__code,
.live-coding input[type="checkbox"]:not(:checked) + .live-coding__component > .live-coding__code {
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 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 0.5s, opacity ease 0.5s, visibility step-start 0.5s;
}