mirror of
https://github.com/seigler/presentation-blockchains
synced 2025-07-26 01:06:09 +00:00
feat: incorporate updates to Janus
This commit is contained in:
parent
36845011f8
commit
f32b318593
10 changed files with 1198 additions and 113 deletions
|
@ -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
675
libs/behave/behave.js
Normal 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
3
libs/highlight.js/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
libs/highlight.js/monokai-sublime.css
Normal file
1
libs/highlight.js/monokai-sublime.css
Normal 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
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);
|
17
package.json
17
package.json
|
@ -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"
|
||||
},
|
||||
|
|
304
scripts/app.js
304
scripts/app.js
|
@ -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);
|
||||
}());
|
||||
|
|
|
@ -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
BIN
styles/browser-bar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
260
styles/main.css
260
styles/main.css
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue