diff --git a/css/a11y.css b/css/a11y.css
index f7f6654..4749499 100755
--- a/css/a11y.css
+++ b/css/a11y.css
@@ -4,11 +4,7 @@
.htmlExercise > .example > .editorWithPreview {
border: 1px solid #DDD;
-}
-
-.htmlExercise > .example > .editorWithPreview > * {
- display: inline-block;
- vertical-align: top;
+ display: flex;
}
.editorWithPreview > .preview,
@@ -22,17 +18,19 @@
}
.editorWithPreview > .editor {
- width: 70%;
font-size: 0.8em;
+ max-width: 70%;
}
.editorWithPreview > .preview {
- width: 30%;
- padding-left: 20px;
- padding-top: 10px;
+ padding: 10px;
overflow: auto;
}
+.CodeMirror {
+ overflow: hidden;
+}
+
.htmlExercise > .example > .verify {
margin-top: 10px;
}
@@ -51,40 +49,3 @@ kbd {
line-height: 1.4;
white-space: nowrap;
}
-
-.popupMenuWidget {
- padding: 1em;
- position: relative;
-}
-
-.popupMenuWidget [role="button"] {
- border: 1px solid black;
- border-radius: 3px;
- display: inline-block;
- padding: 1px 3px;
- position: relative;
- z-index: 2;
-}
-
-.popupMenuWidget [role="button"]:hover,
-.popupMenuWidget [role="button"]:focus {
- text-decoration: none;
-}
-
-.popupMenuWidget [role="menu"] {
- border: 1px solid black;
- display: none;
- position: relative;
- top: -1px;
- z-index: 1;
-}
-
-.popupMenuWidget [role="menu"] > a {
- display: block;
- padding: 0px 3px;
-}
-
-.popupMenuWidget [role="menuitem"]:focus {
- background-color: #428bca;
- color: white;
-}
diff --git a/css/navigationButtons.css b/css/navigationButtons.css
deleted file mode 100755
index 4c71e35..0000000
--- a/css/navigationButtons.css
+++ /dev/null
@@ -1,17 +0,0 @@
-#navigation-buttons {
- text-align: right;
- padding-right: 10px;
- padding-bottom: 10px;
- width: 100%;
- max-width: 940px;
-}
-
-#navigation-buttons button {
- background-color: transparent;
- border: none;
- font-size: 1.5em;
-}
-
-#navigation-buttons button[disabled] {
- visibility: hidden;
-}
diff --git a/css/style.css b/css/style.css
index a94247b..746ca14 100644
--- a/css/style.css
+++ b/css/style.css
@@ -17,13 +17,24 @@
* so that it is easy to overwrite them.
*/
+html, body {
+ /**
+ * Disables inertia effects
+ */
+ height: 100%;
+ overflow: hidden;
+ font-family: Helvetica Neue,Helvetica,Roboto,Arial,sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
body {
color: #333;
margin: 0;
padding: 0;
}
-#loader {
+#exerslide-loader {
align-items: center;
display: flex;
font-size: 4em;
@@ -31,10 +42,11 @@ body {
justify-content: center;
}
-#page {
+#exerslide-page {
bottom: 0;
display: flex;
left: 0;
+ line-height: 1;
position: absolute;
right: 0;
top: 0;
@@ -59,100 +71,126 @@ body {
/* CODE */
-pre > code {
+#exerslide-slide code,
+#exerslide-slide p > code {
background: #f9f9f9;
- display: block;
- font-size: 0.9em;
- overflow-x: auto;
- padding: 1rem;
}
-pre {
- margin-bottom: 1.5rem;
+#exerslide-slide pre + :not(hr) {
+ margin-top: 1rem;
+}
+
+#exerslide-slide pre > code {
+ display: block;
+ font-size: 0.9rem;
+ overflow-x: auto;
+ padding: 0.5rem;
}
/* MAIN CONTENT */
-#slide {
+#exerslide-slide {
align-items: center;
+ box-sizing: border-box;
display: flex;
flex-direction: column;
flex: 2;
overflow-y: auto;
- padding: 1.5em;
+ padding: 1rem;
position: relative;
width: 100%;
}
-#slide-content {
- width: 100%;
+#exerslide-slide > * {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ line-height: 1.6;
/**
* This is the default max width of the slide content. However, since
* exerslide allows the content width to be configured via slides, this is
* not going to have any effect, unless auto-scaling is disabled completely
* (via scale: false in the meta data section of the first slide)
+ *
+ * We cannot just use `width: 45em` because we want the slide to be responsive
+ * when the viewport shrinks.
*/
max-width: 45em;
- flex: 1;
- flex-direction: column;
- display: flex;
+ /**
+ * This ensures that slides with little content (i.e. without a full line of
+ * content) are still positioned correclty.
+ */
+ width: 100%;
+}
+
+
+#exerslide-slide h1,
+#exerslide-slide h2,
+#exerslide-slide h3,
+#exerslide-slide h4 {
+ margin-top: 0.5rem;
+ margin-bottom: 0.5rem;
}
-#slide h1,
-#slide-title {
+#exerslide-slide h1,
+#exerslide-slide-title {
+ margin-top: 0;
flex-shrink: 0;
- margin-bottom: 1rem;
text-align: center;
}
-#slide-title:focus {
+#exerslide-slide h1:focus,
+#exerslide-slide-title:focus {
outline: none;
}
-/* This ensures that embedded images are scaled down to the width of the main
- * column.
- */
-#slide img {
- max-width: 100%;
+#exerslide-slide h1 {
+ font-size: 1.8rem;
}
-/* DEBUG information */
+#exerslide-slide h2 {
+ font-size: 1.4rem;
+}
-.__exerslide__file_path {
- background-color: #EEE;
- line-height: 1em;
- padding: 0.5em;
- text-align: center;
- width: 100%;
+#exerslide-slide h3 {
+ font-size: 1.2rem;
+}
+
+#exerslide-slide h4 {
+ font-size: 1.1rem;
+}
+
+#exerslide-slide h2 + p,
+#exerslide-slide h3 + p,
+#exerslide-slide h4 + p {
+ margin-top: 0.3rem;
+}
+
+/**
+ * Responsive images.
+ */
+#exerslide-slide img {
+ max-width: 100%;
}
/* MOBILE */
@media(max-width: 768px) {
+ html, body {
+ overflow: auto;
+ }
+
/*
* Makes navigation bar move to the end of the content instead of sticking to
* the bottom of the screen
*/
- #page {
+ #exerslide-page {
bottom: initial;
+ min-height: 100vh;
}
/* Makes the page look better when the menu is TOC is expanded */
- #main {
+ #exerslide-main {
overflow-x: hidden;
}
}
-
-@media screen and (min-width: 40em) {
- #slide h1 {
- font-size: 2.2em;
- }
-
- #slide h2 {
- font-size: 1.8em;
- }
-
- #slide h3 {
- font-size: 1.4em;
- }
-}
diff --git a/css/taop.css b/css/taop.css
index 0e31c35..30603b2 100644
--- a/css/taop.css
+++ b/css/taop.css
@@ -7,7 +7,8 @@
width: 1px;
}
-#toolbar {
+.exerslide-toolbar-text,
+.exerslide-toolbar-button {
color: #888;
}
@@ -16,7 +17,8 @@ html .editorWithPreview input[type=number],
html .editorWithPreview textarea {
display: inline-block;;
box-sizing: border-box;
- width: auto;;
+ width: initial;
+ max-width: 100%;
height: auto;
padding: 1px;
border: 1px inset;
@@ -39,22 +41,30 @@ html .editorWithPreview button {
line-height: inherit;
}
-#slide h2 {
+#exerslide-slide {
+ position: initial;
+}
+
+#exerslide-slide {
+ padding-top: 0;
+}
+
+#exerslide-slide h2 {
font-size: 1.3em;
margin: 1.1em 0 0.8em 0;
font-weight: bold;
}
-#slide .previewHeading {
+#exerslide-slide .previewHeading {
margin: 0 0 1.1em;
color: grey;
}
-#slide h3 {
+#exerslide-slide h3 {
font-size: 1.1em;
}
@media screen and (min-width: 40em) {
- #slide h1 {
+ #exerslide-slide h1 {
font-size: 1.8em;
font-weight: bold;
}
diff --git a/css/toc.css b/css/toc.css
deleted file mode 100755
index ffbf78d..0000000
--- a/css/toc.css
+++ /dev/null
@@ -1,51 +0,0 @@
-#toc {
- background-color: #EEE;
- border-right: 1px solid #AAA;
- font-size: 110%;
- font-weight: 100;
- max-width: 300px;
- min-width: 300px;
- overflow-y: auto;
-}
-
-#toc > ul {
- padding-left: 10px;
- padding-top: 10px;
-}
-
-#toc > h2 {
- display: none;
-}
-
-#toc li {
- list-style: none;
-}
-
-#toc .chapter {
- margin-top: 15px;
-}
-
-#toc .chapter > .title {
- font-size: 1.08em;
-}
-#toc .chapter.active > .title {
- font-weight: bold;
-}
-
-#toc .slide {
- padding-bottom: 5px;
-}
-
-#toc .slides {
- padding-left: 15px;
-}
-
-#toc .slide.active {
- font-weight: bold;
-}
-
-#toc .slide > a:active,
-#toc .slide > a:focus {
- color: #428bca;
- text-decoration: none;
-}
diff --git a/exerslide.config.js b/exerslide.config.js
index 0bb0b89..1c9c369 100644
--- a/exerslide.config.js
+++ b/exerslide.config.js
@@ -49,11 +49,11 @@ module.exports = {
* A list of module names (exerslide-plugin-* can be omitted) or paths.
*/
plugins: [
+ 'bulletlist-layout',
'center-layout',
'column-layout',
'html-converter',
'markdown-converter',
- 'shared-urls',
],
/** Advanced configuration options **/
diff --git a/js/MasterLayout.js b/js/MasterLayout.js
index 11996d9..b5bd49a 100644
--- a/js/MasterLayout.js
+++ b/js/MasterLayout.js
@@ -6,6 +6,7 @@
* the root directory of this source tree.
*/
+import ExtensionPoint from 'exerslide/components/ExtensionPoint';
import React from 'react';
import TOC from './components/TOC';
import Toolbar from './components/Toolbar';
@@ -32,48 +33,30 @@ import Toolbar from './components/Toolbar';
* +----------------------------------------+
*
*/
-export default function MasterLayout(props) {
- const slide = props.slides[props.slideIndex];
- const {className, children, ...restProps} = props;
+export default function MasterLayout({className, children}) {
return (
-
-
-
- {slide.__path__ ? // for debugging
-
- File path: {slide.__path__}
-
:
- null
- }
-
- {children}
-
-
+
+
+
+
+
+ {children}
+
+
+
);
}
MasterLayout.propTypes = {
/**
- * The index of the current slide
- */
- slideIndex: React.PropTypes.number,
-
- /**
- * All slides
+ * CSS class names to add to the page.
*/
- slides: React.PropTypes.array,
+ className: React.PropTypes.string,
/**
- * CSS class names to add to the page.
+ * The rendered slide is passed as child to the master layout.
*/
- className: React.PropTypes.string,
+ children: React.PropTypes.node,
};
diff --git a/js/SlideLayout.js b/js/SlideLayout.js
new file mode 100644
index 0000000..7632f7e
--- /dev/null
+++ b/js/SlideLayout.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the license found in the LICENSE file in
+ * the root directory of this source tree.
+ */
+
+import ExtensionPoint from 'exerslide/components/ExtensionPoint';
+import React from 'react';
+
+/**
+ * The base layout for every slide. This allows you do add additional
+ * content to all slides before or after the content.
+ */
+export default function SlideLayout({children}) {
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+SlideLayout.propTypes = {
+ /**
+ * The current slide content and header are passed in as children by exerslide
+ */
+ children: React.PropTypes.node,
+};
diff --git a/js/analytics.js b/js/analytics.js
new file mode 100644
index 0000000..78cdfe0
--- /dev/null
+++ b/js/analytics.js
@@ -0,0 +1,8 @@
+export default function(exerslide) {
+ /* global ga */
+ exerslide.subscribe('SLIDE.DID_MOUNT', ({slide}) => {
+ ga('send', 'pageview', {
+ 'page': location.pathname + slide.url,
+ });
+ });
+};
diff --git a/js/app.js b/js/app.js
deleted file mode 100755
index e76258b..0000000
--- a/js/app.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import * as config from './config';
-import presentation from 'exerslide/js/presentation';
-
-presentation.init(config);
diff --git a/js/components/TOC.js b/js/components/TOC.js
index 17199dd..d990259 100644
--- a/js/components/TOC.js
+++ b/js/components/TOC.js
@@ -7,10 +7,8 @@
*/
import React from 'react';
-import {groupByChapter} from 'exerslide/js/chapterHelper';
-import {IS_MOBILE} from 'exerslide/js/deviceHelper';
-import {getOptions} from 'exerslide/js/optionHelper';
-import {getSlideURL} from 'exerslide/js/url';
+import * as exerslide from 'exerslide/browser';
+const {groupByChapter, IS_MOBILE} = exerslide;
import './css/toc.css';
@@ -32,27 +30,28 @@ class Entry extends React.Component {
render() {
const {slideIndex, slides, active} = this.props;
const slide = slides[slideIndex];
- const slideOptions = getOptions(slides, slideIndex);
- const classes = 'slide' + (active ? ' active' : '');
- const Layout = slide.layout;
- const layoutClasses =
- Layout && Layout.getClassNames && Layout.getClassNames(slideIndex);
+ const slideOptions = slide.options;
+ let classes = ['exerslide-toc-entry'];
+ const layout = slide.layout;
+ if (layout && layout.getClassNames) {
+ classes = classes.concat(layout.getClassNames(slideIndex, exerslide));
+ }
const title = slideOptions.toc || slideOptions.title ||
`Slide ${slideIndex + 1}`;
const props = {};
if (active) {
+ classes.push('active');
props['aria-current'] = 'page';
}
return (
-
+
+ href={slide.url}>
{title}
@@ -71,34 +70,34 @@ Entry.propTypes = {
*/
export default class TOC extends React.Component {
- constructor(props) {
- super(props);
- const slideOptions = getOptions(props.slides, props.slideIndex);
+ constructor(props, context) {
+ super(props, context);
+ const slideOptions = context.slide.options;
let collapsed = false;
if (props.togglable) {
// On mobile devices we collapse the TOC by default
if (IS_MOBILE) {
collapsed = true;
- } else if (slideOptions.hasOwnProperty('hidetoc')) {
- collapsed = slideOptions.hidetoc;
+ } else if (slideOptions.hasOwnProperty('hideTOC')) {
+ collapsed = slideOptions.hideTOC;
}
}
this.state = {
- groupedSlides: groupByChapter(props.slides),
+ groupedSlides: groupByChapter(context.slides),
collapsed,
explicitlyToggled: false,
};
this._onToggle = this._onToggle.bind(this);
}
- componentWillReceiveProps(nextProps) {
- if (nextProps.slides !== this.props.slides) {
- this.setState({groupedSlides: groupByChapter(nextProps.slides)});
+ componentWillReceiveProps(nextProps, nextContext) {
+ if (nextContext.slides !== this.context.slides) {
+ this.setState({groupedSlides: groupByChapter(nextContext.slides)});
}
- if (nextProps.slideIndex !== this.props.slideIndex) {
- const slideOptions = getOptions(nextProps.slides, nextProps.slideIndex);
+ if (nextContext.slide !== this.context.slide) {
+ const slideOptions = nextContext.slide.options;
if (!this.state.explicitlyToggled) {
- let collapsed = IS_MOBILE ? true : Boolean(slideOptions.hidetoc);
+ let collapsed = IS_MOBILE ? true : Boolean(slideOptions.hideTOC);
this.setState(
{
collapsed,
@@ -121,29 +120,30 @@ export default class TOC extends React.Component {
render() {
let slideIndex = 0;
- const {slides, togglable} = this.props;
+ const {togglable} = this.props;
+ const {slides} = this.context;
const {collapsed} = this.state;
const chapters = this.state.groupedSlides.map(chapter => {
let entry;
if (Array.isArray(chapter)) {
- const isActive = this.props.slideIndex >= slideIndex &&
- this.props.slideIndex < slideIndex + chapter.length;
+ const isActive = this.context.slideIndex >= slideIndex &&
+ this.context.slideIndex < slideIndex + chapter.length;
entry = chapter.map((slide, index) =>
);
entry =
-
+ className={'exerslide-toc-chapter' + (isActive ? ' active' : '')}>
+
{chapter[0].options.chapter}
- {entry}
+ {entry}
;
slideIndex += chapter.length;
} else {
@@ -152,39 +152,42 @@ export default class TOC extends React.Component {
key={slideIndex}
slideIndex={slideIndex}
slides={slides}
- active={this.props.slideIndex === slideIndex}
+ active={this.context.slideIndex === slideIndex}
/>;
slideIndex += 1;
}
return entry;
});
+ const icon =
+
;
+
return (
-
Table of Contents
+ className={'exerslide-toc-container' + (collapsed ? ' collapsed' : '')}>
+
Table of Contents
{togglable ?
+ /* This goes against the code formatting guidelines because VoiceOver
+ * is not able to announce this button properly if there is a line
+ * break in it.
+ */
-
- :
+ onClick={this._onToggle}>{icon} :
null
}
+ aria-labelledby="exerrslide-toc-title">
{chapters}
@@ -194,24 +197,31 @@ export default class TOC extends React.Component {
TOC.propTypes = {
/**
- * Index of the currently shown slide.
+ * Whether to show a toggle button or not.
*/
- slideIndex: React.PropTypes.number,
+ togglable: React.PropTypes.bool,
/**
- * All slides.
+ * Callback called when TOC is shown or hidden.
*/
- slides: React.PropTypes.array,
+ onToggle: React.PropTypes.func,
+};
+TOC.contextTypes = {
/**
- * Whether to show a toggle button or not.
+ * Current slide.
*/
- togglable: React.PropTypes.bool,
+ slide: React.PropTypes.object,
/**
- * Callback called when TOC is shown or hidden.
+ * Index of the currently shown slide.
*/
- onToggle: React.PropTypes.func,
+ slideIndex: React.PropTypes.number,
+
+ /**
+ * All slides.
+ */
+ slides: React.PropTypes.array,
};
TOC.defaultProps = {
diff --git a/js/components/Toolbar.js b/js/components/Toolbar.js
index 15cd3dc..69fb9e6 100644
--- a/js/components/Toolbar.js
+++ b/js/components/Toolbar.js
@@ -7,8 +7,8 @@
*/
import React from 'react';
-import {nextSlide, previousSlide} from 'exerslide/js/navigation';
-import {getContentWidthStyle} from 'exerslide/js/layoutHelper';
+import ExtensionPoint from 'exerslide/components/ExtensionPoint';
+import {forward, back} from 'exerslide/browser';
import './css/toolbar.css';
@@ -16,47 +16,55 @@ import './css/toolbar.css';
* This components generates a previous and next buttons (rendered as arrows,
* using Font Awesome) to navigate the presentation.
*/
-export default function Toolbar(props) {
- const {slideIndex} = props;
+export default function Toolbar({className}, {slideIndex, slides}) {
+ const numberOfSlides = slides.length;
+
return (
-
-
-
-
-
- {' ' + (slideIndex + 1) + '/' + props.numberOfSlides + ' '}
-
-
-
-
-
+
+
+
+
+
+
+ {' ' + (slideIndex + 1) + '/' + numberOfSlides + ' '}
+
+
+
+
+
+
);
}
Toolbar.propTypes = {
+ className: React.PropTypes.string,
+};
+
+Toolbar.contextTypes = {
/**
* This index of the current slide.
*/
slideIndex: React.PropTypes.number.isRequired,
+
/**
* Number of slides.
*/
- numberOfSlides: React.PropTypes.number.isRequired,
+ slides: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
};
-
diff --git a/js/components/css/toc.css b/js/components/css/toc.css
index 65837b7..29bf1b1 100644
--- a/js/components/css/toc.css
+++ b/js/components/css/toc.css
@@ -6,7 +6,7 @@
* the root directory of this source tree.
*/
-#toc {
+.exerslide-toc-container {
background-color: #F4F4F4;
border-right: 1px solid #CCC;
display: flex;
@@ -15,7 +15,7 @@
min-width: 20em;
}
-#toc.collapsed {
+.exerslide-toc-container.collapsed {
background-color: inherit;
border-right: none;
min-width: 0;
@@ -23,83 +23,93 @@
z-index: 100;
}
-#toc ol {
+ol.exerslide-toc-entries,
+ol#exerslide-toc-list {
+ list-style: none;
+ counter-reset: item;
+ overflow-y: auto;
margin: 0;
}
-#toc > h2,
-#toc.collapsed > #toc-list {
+
+ol#exerslide-toc-list {
+ padding-left: 1em;
+ padding-right: 1em;
+}
+
+ol.exerslide-toc-entries {
+ padding-left: 0;
+}
+
+ol#exerslide-toc-list > * {
+ margin-bottom: 1em;
+}
+
+#exerslide-toc-title,
+.exerslide-toc-container.collapsed > #exerslide-toc-list {
display: none;
}
-#toc > .toggleButton {
+.exerslide-toc-toggleButton {
align-self: flex-end;
background-color: transparent;
border: none;
color: #AAA;
+ cursor: pointer;
flex-shrink: 0;
+ font-size: 1em;
outline-width: thin;
- padding: 0.5rem;
+ padding: 0.5em;
}
-#toc > .toggleButton:hover {
+.exerslide-toc-toggleButton:hover {
color: inherit;
}
-#toc #toc-list,
-#toc #toc-list .slides {
- counter-reset: item;
- overflow-y: auto;
- margin-left: 0.6em;
-}
-
-#toc > #toc-list > * {
- margin-bottom: 1em;
-}
-
-#toc > #toc-list > :first-child {
+#toc-list > :first-child {
margin-top: 0;
}
-#toc .slide,
-#toc .chapter {
+.exerslide-toc-entry,
+.exerslide-toc-chapter {
display: block;
}
-#toc .chapter::before,
-#toc .slide::before {
+.exerslide-toc-entry::before,
+.exerslide-toc-chapter::before {
content: counters(item, '.') ". ";
counter-increment: item;
}
-#toc .chapter > .title {
+.exerslide-toc-title {
display: inline-block;
margin: 0;
+ margin-bottom: 0.5em;
}
-#toc #toc-list > .slide::before,
-#toc #toc-list > .slide,
-#toc .chapter::before,
-#toc .chapter > .title {
+#exerslide-toc-list > .exerslide-toc-entry::before,
+#exerslide-toc-list > .exerslide-toc-entry,
+.exerslide-toc-chapter::before,
+.exerslide-toc-title {
font-weight: bold;
font-size: 1em;
}
-#toc .slide {
+.exerslide-toc-entry {
+ padding: 0.1em 0;
}
-#toc .slide > a {
+.exerslide-toc-entry > a {
color: inherit;
}
-
-#toc .slide,
-#toc .slide > a {
+.exerslide-toc-entry,
+.exerslide-toc-entry > a {
text-decoration: none;
outline-width: thin;
}
-#toc .slide.active,
-#toc .slide.active > a,
-#toc .slide:hover {
+.exerslide-toc-entry.active,
+.exerslide-toc-entry.active > a,
+.exerslide-toc-entry:hover {
color: #428bca;
}
diff --git a/js/components/css/toolbar.css b/js/components/css/toolbar.css
index 813567c..e4f83a4 100644
--- a/js/components/css/toolbar.css
+++ b/js/components/css/toolbar.css
@@ -6,7 +6,7 @@
* the root directory of this source tree.
*/
-#toolbar {
+.exerslide-toolbar {
box-sizing: border-box;
color: #BBB;
padding: 0 1.5em;
@@ -14,33 +14,35 @@
width: 100%;
}
-#toolbar > * {
- font-size: 1.05em;
- max-width: 45em;
+.exerslide-toolbar-button,
+.exerslide-toolbar-text {
+ font-size: 1.05rem;
}
-
-#toolbar button {
+.exerslide-toolbar-button {
background-color: transparent;
+ cursor: pointer;
+ color: #BBB;
border: none;
padding: 10px;
}
-#toolbar button:focus,
-#toolbar button:hover {
+.exerslide-toolbar-button:focus,
+.exerslide-toolbar-button:hover {
color: #555;
}
-#toolbar button[disabled] {
+.exerslide-toolbar-button[disabled] {
visibility: hidden;
}
@media(max-width: 768px) {
- #toolbar {
+ .exerslide-toolbar {
text-align: center;
margin-bottom: 1em;
}
- #toolbar > * {
- font-size: 1.5em;
+ .exerslide-toolbar-button,
+ .exerslide-toolbar-text {
+ font-size: 1.5rem;
}
}
diff --git a/js/keyMap.js b/js/keyMap.js
deleted file mode 100755
index d2720cb..0000000
--- a/js/keyMap.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * This sets up the default keybindings for a presentation. The module should
- * return an object with `keys -> function` mapping. exerslide will bind event
- * handlers for those keys (key combinations) and call the corresponding function
- * providing an API to control the presentation. Currently provided is
- *
- * - nextSlide(): Advance to the next slide
- * - previousSlide(): Go back to the previous slide
- * - gotToSlide(index): Go to slide
- *
- * We use http://dmauro.github.io/Keypress/ to bind the event handlers. Have
- * a look at the documentation to find out how to specify key combinations.
- */
-
-function next({nextSlide}) {
- nextSlide();
-}
-
-function prev({previousSlide}) {
- previousSlide();
-}
-
-export default {
- left: prev,
- right: next,
- 'alt pageup': prev,
- 'alt pagedown': next,
-};
diff --git a/js/presentation.js b/js/presentation.js
index 0565cf1..efcc192 100644
--- a/js/presentation.js
+++ b/js/presentation.js
@@ -14,7 +14,7 @@
* kind of custom initialization step.
*/
-import presentation from 'exerslide/js/presentation';
+import {present, use} from 'exerslide/browser';
/**
* The master layout for this presentation. To customize is, either edit it
@@ -23,32 +23,76 @@ import presentation from 'exerslide/js/presentation';
import MasterLayout from './MasterLayout';
/**
- * This sets up the default keybindings for a presentation. This should be an
- * object with `keys -> function` mapping. exerslide will bind event
- * handlers for those keys (key combinations) and call the corresponding function
- * providing an API to control the presentation. Currently provided is
+ * The base slide layout. To customize is, either edit it directly or copy it
+ * and point to the copy here.
+ */
+import SlideLayout from './SlideLayout';
+
+/**
+ * Many features of exerslide are actually made available via runtime plugins.
+ *
+ * The following plugin enables keyboard navigation. This sets up the default
+ * keybindings for a presentation. This should be an object with
+ * `keys -> function` mapping. exerslide will bind event handlers for those keys
+ * (key combinations) and call the corresponding function providing an API to
+ * control the presentation. Currently provided is
*
* - nextSlide(): Advance to the next slide
* - previousSlide(): Go back to the previous slide
- * - gotToSlide(index): Go to slide
*
* We use https://craig.is/killing/mice to bind the event handlers. Have
* a look at the documentation to find out how to specify key combinations.
*/
-function next({nextSlide}) {
- nextSlide();
+import keyboardNavigation from 'exerslide/browser-plugins/keyboardNavigation';
+function forward({forward}) {
+ forward();
}
-function prev({previousSlide}) {
- previousSlide();
+function back({back}) {
+ back();
}
+use(
+ keyboardNavigation,
+ {
+ left: back,
+ right: forward,
+ 'alt+pageup': back,
+ 'alt+pagedown': forward,
+ }
+);
+
+/**
+ * This plugin automatically scales the font size according to the provided
+ * settings. The default settings try to maintain a line length that is
+ * considered to be readable. To disable this plugin, just comment or remove the
+ * following two lines.
+ */
+import scaledContent from 'exerslide/browser-plugins/scaledContent';
+use(scaledContent);
+
+/**
+ * This plugin, when applied to content, injects alerts for screenreaders that
+ * let the author know if the content isn't fully visible. That allows authors
+ * with visual impairment to adjust the content or to scroll to make the content
+ * visible.
+ */
+import contentVisibility from 'exerslide/browser-plugins/contentVisibility';
+use(contentVisibility);
+
+/**
+ * This plugin is only enabled during development and shows the file path of the
+ * current slide and allows to view the source of the slide.
+ */
+import debugInformation from 'exerslide/browser-plugins/debugInformation';
+//use(debugInformation);
-const keyMap = {
- left: prev,
- right: next,
- 'alt+pageup': prev,
- 'alt+pagedown': next,
-};
+/**
+ * It is likely that you will be linking to the same external sources from
+ * multiple slides. By default exerslide provides the references.yml file as a
+ * central place to keep those references. The default markdown parser takes
+ * them into account.
+ */
+import references from '!!json!yaml!../references.yml';
/**
* __exerslide_slides__ is "magic" global variable that holds an array of slide
@@ -57,8 +101,12 @@ const keyMap = {
*/
/* global __exerslide_slides__*/
-presentation({
- MasterLayout,
- keyMap,
+import analytics from './analytics';
+use(analytics);
+
+present({
+ masterLayout: MasterLayout,
+ slideLayout: SlideLayout,
+ references,
slides: __exerslide_slides__,
});
diff --git a/js/scriptHelper.js b/js/scriptHelper.js
index 734f7b4..9d0f2a0 100755
--- a/js/scriptHelper.js
+++ b/js/scriptHelper.js
@@ -38,12 +38,12 @@ global.showHideDialog = function(ev) {
document.getElementById('inputfield').focus();
}
}
- if (el.style.display == "block") {
+ if (el.style.display == "flex") {
el.style.display = "none";
document.getElementById('sourceLink').focus();
document.body.style.overflow = 'initial';
} else {
- el.style.display = "block";
+ el.style.display = "flex";
document.getElementById('inputfield').focus();
document.body.style.overflow = 'hidden';
}
@@ -58,12 +58,12 @@ global.showHideDialog2 = function(ev) {
document.getElementById('inputfield2').focus();
}
}
- if (el.style.display == "block") {
+ if (el.style.display == "flex") {
el.style.display = "none";
document.getElementById('sourceLink2').focus();
document.body.style.overflow = 'initial';
} else {
- el.style.display = "block";
+ el.style.display = "flex";
document.getElementById('inputfield2').focus();
document.body.style.overflow = 'hidden';
}
@@ -89,9 +89,9 @@ global.closeDialog2 = function(ev) {
// Menu example code.
global.menuExample = {
onClick: function (event) {
- var isOpen = this.toggleMenu(event.currentTarget.parentNode);
+ var isOpen = this.toggleMenu(event.currentTarget);
if (isOpen) {
- this.setMenuItemFocus(event.currentTarget.parentNode);
+ this.setMenuItemFocus(event.currentTarget);
}
},
// Keep all clicks from escaping the widget.
@@ -231,14 +231,16 @@ global.menuExample = {
this.findButtonElement(menuWidgetElement).focus();
},
- getActiveMenuItem: function (menuWidgetElement) {
- var menuElement = this.findMenuElement(menuWidgetElement);
- var activeMenuItemElement = menuElement.querySelector('[data-active="true"]');
- },
findMenuElement: function (menuWidgetElement) {
- return menuWidgetElement.querySelector('[role="menu"]');
+ while (menuWidgetElement.className.indexOf('popupMenuWidget') === -1) {
+ menuWidgetElement = menuWidgetElement.parentNode;
+ }
+ return menuWidgetElement.querySelector('[role="menu"],ul');
},
findButtonElement: function (menuWidgetElement) {
- return menuWidgetElement.querySelector('[role="button"]');
+ while (menuWidgetElement.className.indexOf('popupMenuWidget') === -1) {
+ menuWidgetElement = menuWidgetElement.parentNode;
+ }
+ return menuWidgetElement.querySelector('[role="button"],a');
}
};
diff --git a/layouts/HTMLExercise.js b/layouts/HTMLExercise.js
index 3512e18..c4ec57a 100755
--- a/layouts/HTMLExercise.js
+++ b/layouts/HTMLExercise.js
@@ -1,5 +1,6 @@
import React from 'react';
import Editor from 'exerslide/components/Editor';
+import ContentRenderer from 'exerslide/components/ContentRenderer';
import '../js/scriptHelper';
import 'codemirror/mode/htmlmixed/htmlmixed';
@@ -77,11 +78,11 @@ class Example extends React.Component {
:
null
}
- {title}
+ {' '}{title}
:
null
}
- {description ? this.props.contentConverter(description) : null}
+ {description ? : null}
{this.props.code ?
@@ -129,7 +130,7 @@ class Example extends React.Component {
}
{this.props.note ?
- {this.props.contentConverter(description)}
+
:
null
}
@@ -140,20 +141,22 @@ class Example extends React.Component {
export default class HTMLExercise extends React.Component {
componentDidMount() {
- let {layoutData: {description, examples}, slideIndex} = this.props;
+ /*
if (slideIndex == 14) {
exerslide.platFormFn(document, 'script', 'facebook-jssdk');
}
+ */
}
render(element: ReactElement, container: DOMElement) {
- let {layoutData: {description, examples}, slideIndex} = this.props;
+ let {title, layoutData: {examples}, content} = this.props;
return (
- {description ? this.props.contentConverter(description) : null}
+ {title}
+ {content ? : null}
{examples && examples.map(
- (example, i) =>
+ (example, i) =>
)}
);
diff --git a/layouts/css/Center.css b/layouts/css/Center.css
deleted file mode 100755
index 010b305..0000000
--- a/layouts/css/Center.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#slide > .Center-wrapper {
- align-items: center;
- display: flex;
- flex-direction: column;
- flex: 1;
- justify-content: center;
- text-align: center;
-}
diff --git a/layouts/css/TwoColumn.css b/layouts/css/TwoColumn.css
deleted file mode 100755
index 229fe44..0000000
--- a/layouts/css/TwoColumn.css
+++ /dev/null
@@ -1,19 +0,0 @@
-.TwoColumn-wrapper {
- display: flex;
-}
-
-.TwoColumn-column {
- flex: 1;
- padding: 10px;
-}
-
-.TwoColumn-column.TwoColumn-image {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-}
-
-.TwoColumn-column.TwoColumn-image > img {
- flex-shrink: 0;
-}
diff --git a/package.json b/package.json
index e56a794..87cf2b6 100644
--- a/package.json
+++ b/package.json
@@ -5,12 +5,12 @@
"private": true,
"dependencies": {
"codemirror": "^5.7.0",
- "exerslide": "~1.0.0",
- "exerslide-plugin-center-layout": "~1.0.0",
- "exerslide-plugin-column-layout": "~1.0.0",
- "exerslide-plugin-html-converter": "~1.0.0",
- "exerslide-plugin-markdown-converter": "~1.0.0",
- "exerslide-plugin-shared-urls": "~1.0.0",
+ "exerslide": "~1.0.2",
+ "exerslide-plugin-bulletlist-layout": "^1.0.0",
+ "exerslide-plugin-center-layout": "^1.0.0",
+ "exerslide-plugin-column-layout": "^1.0.0",
+ "exerslide-plugin-html-converter": "^1.0.0",
+ "exerslide-plugin-markdown-converter": "^1.0.1",
"font-awesome": "^4.4.0",
"foundation-sites": "^6.1.2",
"react": "^15.0.0",
@@ -29,7 +29,9 @@
"extract-text-webpack-plugin": "^0.9.1",
"file-loader": "^0.8.5",
"is-text-path": "^1.0.1",
+ "json-loader": "^0.5.4",
"style-loader": "^0.13.0",
- "webpack": "^1.12.9"
+ "webpack": "^1.12.9",
+ "yaml-loader": "^0.4.0"
}
}
diff --git a/references.yml b/references.yml
new file mode 100644
index 0000000..85819e2
--- /dev/null
+++ b/references.yml
@@ -0,0 +1,7 @@
+# You can keep links to external sources here. This lets you avoid repeating the
+# same URL on different slides. The default markdown parser takes these into
+# account.The format is: "name: URL"
+#
+# Example:
+#
+# example: http://example.org
diff --git a/statics/index.html b/statics/index.html
index 9470621..7d9d754 100644
--- a/statics/index.html
+++ b/statics/index.html
@@ -33,7 +33,7 @@
-
+
diff --git a/webpack.config.js b/webpack.config.js
index 1cf935b..ee499c9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -18,6 +18,7 @@ const PROD = process.env.NODE_ENV === 'production';
const plugins = [
new webpack.DefinePlugin({
+ '__DEV__': !PROD,
'process.env.NODE_ENV':
JSON.stringify(process.env.NODE_ENV || 'development'),
}),
@@ -27,6 +28,9 @@ const plugins = [
if (PROD) {
plugins.push(new webpack.optimize.UglifyJsPlugin({
sourceMap: false,
+ compress: {
+ warnings: false,
+ },
}));
}
@@ -51,7 +55,7 @@ module.exports = {
{
test: /\.jsx?$/,
loader: 'babel',
- exclude: /node_modules\/(?!exerslide\/)/,
+ exclude: /node_modules\/(?!exerslide\b)/,
query: {
presets: [
require.resolve('babel-preset-es2015'),
@@ -63,6 +67,10 @@ module.exports = {
],
},
},
+ {
+ test: /\.json$/,
+ loader: 'json-loader',
+ },
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
@@ -93,8 +101,8 @@ module.exports = {
* slides. If you don't want to auto-copy them at all, remove this
* transform.
*/
- exerslide.transforms.extractAssetPaths({
- pattern: /(?:\.{1,2}\/)+[-_\/a-z\d.]+\.(?:png|jpe?g|gif|svg)\b/ig,
+ exerslide.transforms.requireAssets({
+ // pattern: /(?:\.{1,2}\/)+[-_\/a-z\d.]+\.(?:png|jpe?g|gif|svg)\b/ig,
}),
],
},