\n%s\n";
+ String autoplay = String.format("data-autoplay=%s", String.valueOf(5));
+ StringBuilder sb = new StringBuilder(impressStr);
+ String r = sb.insert(sb.indexOf(">"), " " + autoplay).toString();
+ System.out.println(r);
+ }
+*/
+}
diff --git a/src/main/java/com/fr/plugin/easyslides/slidestyles/StyleFactory.java b/src/main/java/com/fr/plugin/easyslides/slidestyles/StyleFactory.java
new file mode 100644
index 0000000..44a0536
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/slidestyles/StyleFactory.java
@@ -0,0 +1,16 @@
+package com.fr.plugin.easyslides.slidestyles;
+
+import com.fr.general.Inter;
+
+public class StyleFactory {
+ public static SlideStyle createStyle(String type) {
+ if (Inter.getLocText("Plugin-EasySlides_Style1").equalsIgnoreCase(type)) {
+ return new VerticalStyle();
+ } else if (Inter.getLocText("Plugin-EasySlides_Style2").equalsIgnoreCase(type)) {
+ return new HorizontalStyle();
+ } else if (Inter.getLocText("Plugin-EasySlides_Style3").equalsIgnoreCase(type)) {
+ return new FarAwayStyle();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/easyslides/slidestyles/VerticalStyle.java b/src/main/java/com/fr/plugin/easyslides/slidestyles/VerticalStyle.java
new file mode 100644
index 0000000..b72b555
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/slidestyles/VerticalStyle.java
@@ -0,0 +1,18 @@
+package com.fr.plugin.easyslides.slidestyles;
+
+import java.util.List;
+
+public class VerticalStyle extends SlideStyle {
+ @Override
+ protected String buildContents(List contents) {
+ StringBuilder slides = new StringBuilder();
+ String divStr = "
\n%s\n
";
+ for (int i = 0; i < contents.size(); i++) {
+ String id = "slide" + i;
+ String dataX = String.valueOf(i * 1000);
+ slides.append(String.format(divStr, id, dataX, contents.get(i)));
+ slides.append('\n');
+ }
+ return slides.toString();
+ }
+}
diff --git a/src/main/java/com/fr/plugin/easyslides/ui/AutoPlayEditor.java b/src/main/java/com/fr/plugin/easyslides/ui/AutoPlayEditor.java
new file mode 100644
index 0000000..a3cf5a1
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/ui/AutoPlayEditor.java
@@ -0,0 +1,28 @@
+package com.fr.plugin.easyslides.ui;
+
+import com.fr.design.Exception.ValidationException;
+import com.fr.design.mainframe.widget.editors.AbstractPropertyEditor;
+
+import java.awt.*;
+
+public class AutoPlayEditor extends AbstractPropertyEditor {
+ @Override
+ public void validateValue() throws ValidationException {
+
+ }
+
+ @Override
+ public void setValue(Object value) {
+
+ }
+
+ @Override
+ public Object getValue() {
+ return null;
+ }
+
+ @Override
+ public Component getCustomEditor() {
+ return null;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/easyslides/ui/EasySlidesWidgetUI.java b/src/main/java/com/fr/plugin/easyslides/ui/EasySlidesWidgetUI.java
new file mode 100644
index 0000000..1cb0e9e
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/ui/EasySlidesWidgetUI.java
@@ -0,0 +1,87 @@
+package com.fr.plugin.easyslides.ui;
+
+import com.fr.design.designer.creator.CRPropertyDescriptor;
+import com.fr.design.designer.creator.XWidgetCreator;
+import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.gui.itextfield.UITextField;
+import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.design.mainframe.widget.editors.*;
+import com.fr.form.ui.Widget;
+import com.fr.general.IOUtils;
+import com.fr.general.Inter;
+import com.fr.plugin.easyslides.ui.SlidesDataSourceEditor;
+import com.fr.stable.ArrayUtils;
+
+import javax.swing.*;
+import java.awt.*;
+import java.beans.IntrospectionException;
+
+import static com.fr.plugin.easyslides.EasySlidesWidget.*;
+
+public class EasySlidesWidgetUI extends XWidgetCreator {
+ private UITextField textField;
+
+ public EasySlidesWidgetUI(Widget widget, Dimension dimension) {
+ super(widget, dimension);
+ }
+
+ @Override
+ protected JComponent initEditor() {
+ if (this.editor == null) {
+ this.editor = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ UILabel label = new UILabel();
+ label.setIcon(IOUtils.readIcon("/com/fr/plugin/easyslides/images/display.png"));
+ label.setHorizontalAlignment(0);
+ label.setVerticalAlignment(0);
+ this.editor.add(label, "Center");
+ this.textField = new UITextField(5);
+ this.textField.setOpaque(false);
+ this.editor.add(this.textField, "South");
+ this.editor.setBackground(Color.WHITE);
+ }
+ return this.editor;
+ }
+
+ @Override
+ public String getIconPath() {
+ return "/com/fr/plugin/easyslides/images/icon.png";
+ }
+
+ public CRPropertyDescriptor[] supportedDescriptor() throws IntrospectionException {
+ String advanced = Inter.getLocText("Plugin-EasySlides_Advanced");
+ return ArrayUtils.addAll(super.supportedDescriptor(),
+ (new CRPropertyDescriptor("widgetValue", this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_DataSource"))
+ .setEditorClass(SlidesDataSourceEditor.class)
+ .putKeyValue("category", advanced),
+ (new CRPropertyDescriptor(SLIDE_STYLE, this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_SlideStyle"))
+ .setEditorClass(SlideStyleEditor.class)
+ .putKeyValue("category", advanced),
+ (new CRPropertyDescriptor(BACKGROUND_COLOR, this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_ColorBackground"))
+ .setEditorClass(ColorEditor.class)
+ .putKeyValue("category", advanced),
+ (new CRPropertyDescriptor(AUTO_PLAY, this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_AutoPlay"))
+ .setEditorClass(BooleanEditor.class)
+ .putKeyValue("category", advanced),
+ (new CRPropertyDescriptor(AUTO_PLAY_INTERVAL_TIME, this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_AutoPlayTime"))
+ .setEditorClass(IntegerPropertyEditor.class)
+ .putKeyValue("category", advanced),
+ (new CRPropertyDescriptor(TOOLBAR, this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_ToolBar"))
+ .setEditorClass(BooleanEditor.class)
+ .putKeyValue("category", advanced),
+ (new CRPropertyDescriptor(PROGRESSBAR, this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_ProgressBar"))
+ .setEditorClass(BooleanEditor.class)
+ .putKeyValue("category", advanced),
+ (new CRPropertyDescriptor(PROGRESS, this.data.getClass()))
+ .setI18NName(Inter.getLocText("Plugin-EasySlides_Progress"))
+ .setEditorClass(BooleanEditor.class)
+ .putKeyValue("category", advanced)
+ );
+ }
+}
diff --git a/src/main/java/com/fr/plugin/easyslides/ui/SlideStyleEditor.java b/src/main/java/com/fr/plugin/easyslides/ui/SlideStyleEditor.java
new file mode 100644
index 0000000..c2fe7af
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/ui/SlideStyleEditor.java
@@ -0,0 +1,23 @@
+package com.fr.plugin.easyslides.ui;
+
+import com.fr.design.mainframe.widget.editors.ComboEditor;
+import com.fr.general.Inter;
+
+import javax.swing.*;
+import java.util.Vector;
+
+public class SlideStyleEditor extends ComboEditor {
+ public static final String DEFAULT_STYLE = Inter.getLocText("Plugin-EasySlides_Style1");
+
+ public SlideStyleEditor() {
+ }
+
+ @Override
+ public ComboBoxModel model() {
+ Vector vector = new Vector();
+ for (int i = 0; i < 3; i++) {
+ vector.add(Inter.getLocText("Plugin-EasySlides_Style" + (i + 1)));
+ }
+ return new DefaultComboBoxModel(vector);
+ }
+}
diff --git a/src/main/java/com/fr/plugin/easyslides/ui/SlidesDataSourceEditor.java b/src/main/java/com/fr/plugin/easyslides/ui/SlidesDataSourceEditor.java
new file mode 100644
index 0000000..fa1572c
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/ui/SlidesDataSourceEditor.java
@@ -0,0 +1,50 @@
+package com.fr.plugin.easyslides.ui;
+
+import com.fr.design.Exception.ValidationException;
+import com.fr.design.editor.ValueEditorPane;
+import com.fr.design.editor.editor.Editor;
+import com.fr.design.mainframe.widget.editors.AbstractPropertyEditor;
+import com.fr.design.mainframe.widget.editors.DataBindingEditor;
+import com.fr.form.ui.WidgetValue;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import java.awt.Component;
+
+public class SlidesDataSourceEditor extends AbstractPropertyEditor {
+
+ private ValueEditorPane pane;
+
+ public SlidesDataSourceEditor() {
+ Editor editor = new DataBindingEditor();
+ editor.addChangeListener(new ChangeListener() {
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ SlidesDataSourceEditor.this.firePropertyChanged();
+ }
+ });
+ pane = new ValueEditorPane(new Editor[]{editor});
+ }
+
+ @Override
+ public void validateValue() throws ValidationException {
+
+ }
+
+ @Override
+ public void setValue(Object value) {
+ if (value != null) {
+ pane.populate(((WidgetValue) value).getValue());
+ }
+ }
+
+ @Override
+ public Object getValue() {
+ return new WidgetValue(pane.update());
+ }
+
+ @Override
+ public Component getCustomEditor() {
+ return pane;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/easyslides/web/impress-default.css b/src/main/java/com/fr/plugin/easyslides/web/impress-default.css
new file mode 100644
index 0000000..64369a0
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/web/impress-default.css
@@ -0,0 +1,810 @@
+/*
+ So you like the style of impress.js demo?
+ Or maybe you are just curious how it was done?
+
+ You couldn't find a better place to find out!
+
+ Welcome to the stylesheet impress.js demo presentation.
+
+ Please remember that it is not meant to be a part of impress.js and is
+ not required by impress.js.
+ I expect that anyone creating a presentation for impress.js would create
+ their own set of styles.
+
+ But feel free to read through it and learn how to get the most of what
+ impress.js provides.
+
+ And let me be your guide.
+
+ Shall we begin?
+*/
+
+
+/*
+ We start with a good ol' reset.
+ That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/
+
+ You can probably argue if it is needed here, or not, but for sure it
+ doesn't do any harm and gives us a fresh start.
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+/*
+ Now here is when interesting things start to appear.
+
+ We set up styles with default font and nice gradient in the background.
+ And yes, there is a lot of repetition there because of -prefixes but we don't
+ want to leave anybody behind.
+*/
+/*body {
+ font-family: 'PT Sans', sans-serif;
+ min-height: 740px;
+
+ background: rgb(215, 215, 215);
+ background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
+ background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+}*/
+
+.slide-body {
+ font-family: 'PT Sans', sans-serif;
+ min-height: 740px;
+
+ background: rgb(215, 215, 215);
+ background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
+ background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+ background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
+}
+
+/*
+ Now let's bring some text styles back ...
+*/
+b, strong { font-weight: bold }
+i, em { font-style: italic }
+
+/*
+ ... and give links a nice look.
+*/
+a {
+ color: inherit;
+ text-decoration: none;
+ padding: 0 0.1em;
+ background: rgba(255,255,255,0.5);
+ text-shadow: -1px -1px 2px rgba(100,100,100,0.9);
+ border-radius: 0.2em;
+
+ -webkit-transition: 0.5s;
+ -moz-transition: 0.5s;
+ -ms-transition: 0.5s;
+ -o-transition: 0.5s;
+ transition: 0.5s;
+}
+
+a:hover,
+a:focus {
+ background: rgba(255,255,255,1);
+ text-shadow: -1px -1px 2px rgba(100,100,100,0.5);
+}
+
+/*
+ Because the main point behind the impress.js demo is to demo impress.js
+ we display a fallback message for users with browsers that don't support
+ all the features required by it.
+
+ All of the content will be still fully accessible for them, but I want
+ them to know that they are missing something - that's what the demo is
+ about, isn't it?
+
+ And then we hide the message, when support is detected in the browser.
+*/
+
+.fallback-message {
+ font-family: sans-serif;
+ line-height: 1.3;
+
+ width: 780px;
+ padding: 10px 10px 0;
+ margin: 20px auto;
+
+ border: 1px solid #E4C652;
+ border-radius: 10px;
+ background: #EEDC94;
+}
+
+.fallback-message p {
+ margin-bottom: 10px;
+}
+
+.impress-supported .fallback-message {
+ display: none;
+}
+
+/*
+ Now let's style the presentation steps.
+
+ We start with basics to make sure it displays correctly in everywhere ...
+*/
+
+.step {
+ position: relative;
+ width: 900px;
+ padding: 40px;
+ margin: 20px auto;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -o-box-sizing: border-box;
+ box-sizing: border-box;
+
+ font-family: 'PT Serif', georgia, serif;
+ font-size: 48px;
+ line-height: 1.5;
+}
+
+/*
+ ... and we enhance the styles for impress.js.
+
+ Basically we remove the margin and make inactive steps a little bit transparent.
+*/
+.impress-enabled .step {
+ margin: 0;
+ opacity: 0.3;
+
+ -webkit-transition: opacity 1s;
+ -moz-transition: opacity 1s;
+ -ms-transition: opacity 1s;
+ -o-transition: opacity 1s;
+ transition: opacity 1s;
+}
+
+.impress-enabled .step.active { opacity: 1 }
+
+/*
+ These 'slide' step styles were heavily inspired by HTML5 Slides:
+ http://html5slides.googlecode.com/svn/trunk/styles.css
+
+ ;)
+
+ They cover everything what you see on first three steps of the demo.
+*/
+.slide {
+ display: block;
+
+ width: 900px;
+ height: 700px;
+ padding: 40px 60px;
+
+ background-color: white;
+ border: 1px solid rgba(0, 0, 0, .3);
+ border-radius: 10px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
+
+ color: rgb(102, 102, 102);
+ text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
+
+ font-family: 'Open Sans', Arial, sans-serif;
+ font-size: 30px;
+ line-height: 36px;
+ letter-spacing: -1px;
+}
+
+.slide q {
+ display: block;
+ font-size: 50px;
+ line-height: 72px;
+
+ margin-top: 100px;
+}
+
+.slide q strong {
+ white-space: nowrap;
+}
+
+/*
+ And now we start to style each step separately.
+
+ I agree that this may be not the most efficient, object-oriented and
+ scalable way of styling, but most of steps have quite a custom look
+ and typography tricks here and there, so they had to be styles separately.
+
+ First is the title step with a big
(no room for padding) and some
+ 3D positioning along Z axis.
+*/
+
+#title {
+ padding: 0;
+}
+
+#title .try {
+ font-size: 64px;
+ position: absolute;
+ top: -0.5em;
+ left: 1.5em;
+
+ -webkit-transform: translateZ(20px);
+ -moz-transform: translateZ(20px);
+ -ms-transform: translateZ(20px);
+ -o-transform: translateZ(20px);
+ transform: translateZ(20px);
+}
+
+#title h1 {
+ font-size: 190px;
+
+ -webkit-transform: translateZ(50px);
+ -moz-transform: translateZ(50px);
+ -ms-transform: translateZ(50px);
+ -o-transform: translateZ(50px);
+ transform: translateZ(50px);
+}
+
+#title .footnote {
+ font-size: 32px;
+}
+
+/*
+ Second step is nothing special, just a text with a link, so it doesn't need
+ any special styling.
+
+ Let's move to 'big thoughts' with centered text and custom font sizes.
+*/
+#big {
+ width: 600px;
+ text-align: center;
+ font-size: 60px;
+ line-height: 1;
+}
+
+#big b {
+ display: block;
+ font-size: 250px;
+ line-height: 250px;
+}
+
+#big .thoughts {
+ font-size: 90px;
+ line-height: 150px;
+}
+
+/*
+ 'Tiny ideas' just need some tiny styling.
+*/
+#tiny {
+ width: 500px;
+ text-align: center;
+}
+
+/*
+ This step has some animated text ...
+*/
+#ing { width: 500px }
+
+/*
+ ... so we define display to `inline-block` to enable transforms and
+ transition duration to 0.5s ...
+*/
+#ing b {
+ display: inline-block;
+ -webkit-transition: 0.5s;
+ -moz-transition: 0.5s;
+ -ms-transition: 0.5s;
+ -o-transition: 0.5s;
+ transition: 0.5s;
+}
+
+/*
+ ... and we want 'positioning` word to move up a bit when the step gets
+ `present` class ...
+*/
+#ing.present .positioning {
+ -webkit-transform: translateY(-10px);
+ -moz-transform: translateY(-10px);
+ -ms-transform: translateY(-10px);
+ -o-transform: translateY(-10px);
+ transform: translateY(-10px);
+}
+
+/*
+ ... 'rotating' to rotate quater of a second later ...
+*/
+#ing.present .rotating {
+ -webkit-transform: rotate(-10deg);
+ -moz-transform: rotate(-10deg);
+ -ms-transform: rotate(-10deg);
+ -o-transform: rotate(-10deg);
+ transform: rotate(-10deg);
+
+ -webkit-transition-delay: 0.25s;
+ -moz-transition-delay: 0.25s;
+ -ms-transition-delay: 0.25s;
+ -o-transition-delay: 0.25s;
+ transition-delay: 0.25s;
+}
+
+/*
+ ... and 'scaling' to scale down after another quater of a second.
+*/
+#ing.present .scaling {
+ -webkit-transform: scale(0.7);
+ -moz-transform: scale(0.7);
+ -ms-transform: scale(0.7);
+ -o-transform: scale(0.7);
+ transform: scale(0.7);
+
+ -webkit-transition-delay: 0.5s;
+ -moz-transition-delay: 0.5s;
+ -ms-transition-delay: 0.5s;
+ -o-transition-delay: 0.5s;
+ transition-delay: 0.5s;
+}
+
+/*
+ The 'imagination' step is again some boring font-sizing.
+*/
+
+#imagination {
+ width: 600px;
+}
+
+#imagination .imagination {
+ font-size: 78px;
+}
+
+/*
+ There is nothing really special about 'use the source, Luke' step, too,
+ except maybe of the Yoda background.
+
+ As you can see below I've 'hard-coded' it in data URL.
+ That's not the best way to serve images, but because that's just this one
+ I decided it will be OK to have it this way.
+
+ Just make sure you don't blindly copy this approach.
+*/
+#source {
+ width: 700px;
+ padding-bottom: 300px;
+
+ /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==);
+ background-position: bottom right;
+ background-repeat: no-repeat;
+}
+
+#source q {
+ font-size: 60px;
+}
+
+/*
+ And the "it's in 3D" step again brings some 3D typography - just for fun.
+
+ Because we want to position elements in 3D we set transform-style to
+ `preserve-3d` on the paragraph.
+ It is not needed by webkit browsers, but it is in Firefox. It's hard to say
+ which behaviour is correct as 3D transforms spec is not very clear about it.
+*/
+#its-in-3d p {
+ -webkit-transform-style: preserve-3d;
+ -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */
+ -ms-transform-style: preserve-3d;
+ -o-transform-style: preserve-3d;
+ transform-style: preserve-3d;
+}
+
+/*
+ Below we position each word separately along Z axis and we want it to transition
+ to default position in 0.5s when the step gets `present` class.
+
+ Quite a simple idea, but lot's of styles and prefixes.
+*/
+#its-in-3d span,
+#its-in-3d b {
+ display: inline-block;
+ -webkit-transform: translateZ(40px);
+ -moz-transform: translateZ(40px);
+ -ms-transform: translateZ(40px);
+ -o-transform: translateZ(40px);
+ transform: translateZ(40px);
+
+ -webkit-transition: 0.5s;
+ -moz-transition: 0.5s;
+ -ms-transition: 0.5s;
+ -o-transition: 0.5s;
+ transition: 0.5s;
+}
+
+#its-in-3d .have {
+ -webkit-transform: translateZ(-40px);
+ -moz-transform: translateZ(-40px);
+ -ms-transform: translateZ(-40px);
+ -o-transform: translateZ(-40px);
+ transform: translateZ(-40px);
+}
+
+#its-in-3d .you {
+ -webkit-transform: translateZ(20px);
+ -moz-transform: translateZ(20px);
+ -ms-transform: translateZ(20px);
+ -o-transform: translateZ(20px);
+ transform: translateZ(20px);
+}
+
+#its-in-3d .noticed {
+ -webkit-transform: translateZ(-40px);
+ -moz-transform: translateZ(-40px);
+ -ms-transform: translateZ(-40px);
+ -o-transform: translateZ(-40px);
+ transform: translateZ(-40px);
+}
+
+#its-in-3d .its {
+ -webkit-transform: translateZ(60px);
+ -moz-transform: translateZ(60px);
+ -ms-transform: translateZ(60px);
+ -o-transform: translateZ(60px);
+ transform: translateZ(60px);
+}
+
+#its-in-3d .in {
+ -webkit-transform: translateZ(-10px);
+ -moz-transform: translateZ(-10px);
+ -ms-transform: translateZ(-10px);
+ -o-transform: translateZ(-10px);
+ transform: translateZ(-10px);
+}
+
+#its-in-3d .footnote {
+ font-size: 32px;
+
+ -webkit-transform: translateZ(-10px);
+ -moz-transform: translateZ(-10px);
+ -ms-transform: translateZ(-10px);
+ -o-transform: translateZ(-10px);
+ transform: translateZ(-10px);
+}
+
+#its-in-3d.present span,
+#its-in-3d.present b {
+ -webkit-transform: translateZ(0px);
+ -moz-transform: translateZ(0px);
+ -ms-transform: translateZ(0px);
+ -o-transform: translateZ(0px);
+ transform: translateZ(0px);
+}
+
+/*
+ The last step is an overview.
+ There is no content in it, so we make sure it's not visible because we want
+ to be able to click on other steps.
+
+*/
+#overview { display: none }
+
+/*
+ We also make other steps visible and give them a pointer cursor using the
+ `impress-on-` class.
+*/
+.impress-on-overview .step {
+ opacity: 1;
+ cursor: pointer;
+}
+
+
+/*
+ Now, when we have all the steps styled let's give users a hint how to navigate
+ around the presentation.
+
+ The best way to do this would be to use JavaScript, show a delayed hint for a
+ first time users, then hide it and store a status in cookie or localStorage...
+
+ But I wanted to have some CSS fun and avoid additional scripting...
+
+ Let me explain it first, so maybe the transition magic will be more readable
+ when you read the code.
+
+ First of all I wanted the hint to appear only when user is idle for a while.
+ You can't detect the 'idle' state in CSS, but I delayed a appearing of the
+ hint by 5s using transition-delay.
+
+ You also can't detect in CSS if the user is a first-time visitor, so I had to
+ make an assumption that I'll only show the hint on the first step. And when
+ the step is changed hide the hint, because I can assume that user already
+ knows how to navigate.
+
+ To summarize it - hint is shown when the user is on the first step for longer
+ than 5 seconds.
+
+ The other problem I had was caused by the fact that I wanted the hint to fade
+ in and out. It can be easily achieved by transitioning the opacity property.
+ But that also meant that the hint was always on the screen, even if totally
+ transparent. It covered part of the screen and you couldn't correctly clicked
+ through it.
+ Unfortunately you cannot transition between display `block` and `none` in pure
+ CSS, so I needed a way to not only fade out the hint but also move it out of
+ the screen.
+
+ I solved this problem by positioning the hint below the bottom of the screen
+ with CSS transform and moving it up to show it. But I also didn't want this move
+ to be visible. I wanted the hint only to fade in and out visually, so I delayed
+ the fade in transition, so it starts when the hint is already in its correct
+ position on the screen.
+
+ I know, it sounds complicated ... maybe it would be easier with the code?
+*/
+
+.hint {
+ /*
+ We hide the hint until presentation is started and from browsers not supporting
+ impress.js, as they will have a linear scrollable view ...
+ */
+ display: none;
+
+ /*
+ ... and give it some fixed position and nice styles.
+ */
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 200px;
+
+ background: rgba(0,0,0,0.5);
+ color: #EEE;
+ text-align: center;
+
+ font-size: 50px;
+ padding: 20px;
+
+ z-index: 100;
+
+ /*
+ By default we don't want the hint to be visible, so we make it transparent ...
+ */
+ opacity: 0;
+
+ /*
+ ... and position it below the bottom of the screen (relative to it's fixed position)
+ */
+ -webkit-transform: translateY(400px);
+ -moz-transform: translateY(400px);
+ -ms-transform: translateY(400px);
+ -o-transform: translateY(400px);
+ transform: translateY(400px);
+
+ /*
+ Now let's imagine that the hint is visible and we want to fade it out and move out
+ of the screen.
+
+ So we define the transition on the opacity property with 1s duration and another
+ transition on transform property delayed by 1s so it will happen after the fade out
+ on opacity finished.
+
+ This way user will not see the hint moving down.
+ */
+ -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s;
+ -moz-transition: opacity 1s, -moz-transform 0.5s 1s;
+ -ms-transition: opacity 1s, -ms-transform 0.5s 1s;
+ -o-transition: opacity 1s, -o-transform 0.5s 1s;
+ transition: opacity 1s, transform 0.5s 1s;
+}
+
+/*
+ Now we 'enable' the hint when presentation is initialized ...
+*/
+.impress-enabled .hint { display: block }
+
+/*
+ ... and we will show it when the first step (with id 'bored') is active.
+*/
+.impress-on-bored .hint {
+ /*
+ We remove the transparency and position the hint in its default fixed
+ position.
+ */
+ opacity: 1;
+
+ -webkit-transform: translateY(0px);
+ -moz-transform: translateY(0px);
+ -ms-transform: translateY(0px);
+ -o-transform: translateY(0px);
+ transform: translateY(0px);
+
+ /*
+ Now for fade in transition we have the oposite situation from the one
+ above.
+
+ First after 4.5s delay we animate the transform property to move the hint
+ into its correct position and after that we fade it in with opacity
+ transition.
+ */
+ -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s;
+ -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s;
+ -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s;
+ -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s;
+ transition: opacity 1s 5s, transform 0.5s 4.5s;
+}
+
+/*
+ And as the last thing there is a workaround for quite strange bug.
+ It happens a lot in Chrome. I don't remember if I've seen it in Firefox.
+
+ Sometimes the element positioned in 3D (especially when it's moved back
+ along Z axis) is not clickable, because it falls 'behind' the
+ element.
+
+ To prevent this, I decided to make non clickable by setting
+ pointer-events property to `none` value.
+ Value if this property is inherited, so to make everything else clickable
+ I bring it back on the #impress element.
+
+ If you want to know more about `pointer-events` here are some docs:
+ https://developer.mozilla.org/en/CSS/pointer-events
+
+ There is one very important thing to notice about this workaround - it makes
+ everything 'unclickable' except what's in #impress element.
+
+ So use it wisely ... or don't use at all.
+*/
+.impress-enabled { pointer-events: none }
+.impress-enabled #impress { pointer-events: auto }
+
+/*
+ There is one funny thing I just realized.
+
+ Thanks to this workaround above everything except #impress element is invisible
+ for click events. That means that the hint element is also not clickable.
+ So basically all of this transforms and delayed transitions trickery was probably
+ not needed at all...
+
+ But it was fun to learn about it, wasn't it?
+*/
+
+/*
+ That's all I have for you in this file.
+ Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it
+ for you.
+*/
+
+/******************* PLUGINS *************************************************************/
+/*
+ This version of impress.js supports plugins, and in particular, a UI toolbar
+ plugin that allows easy navigation between steps and autoplay.
+*/
+.impress-enabled div#impress-toolbar {
+ position: fixed;
+ right: 1px;
+ bottom: 1px;
+ opacity: 0.6;
+ z-index: 10;
+}
+.impress-enabled div#impress-toolbar > span {
+ margin-right: 10px;
+}
+
+/*
+ With help from the mouse-timeout plugin, we can hide the toolbar and
+ have it show only when you move/click/touch the mouse.
+*/
+body.impress-mouse-timeout div#impress-toolbar {
+ display: none;
+}
+
+/*
+ In fact, we can hide the mouse cursor itself too, when mouse isn't used.
+*/
+body.impress-mouse-timeout {
+ cursor: none;
+}
+
+
+
+/* Progress bar */
+.impress-progressbar {
+ position: absolute;
+ right: 118px;
+ bottom: 1px;
+ left: 118px;
+ border-radius: 7px;
+ border: 2px solid rgba(100, 100, 100, 0.2);
+}
+.impress-progressbar DIV {
+ width: 0;
+ height: 2px;
+ border-radius: 5px;
+ background: rgba(75, 75, 75, 0.4);
+ transition: width 1s linear;
+}
+.impress-progress {
+ position: absolute;
+ left: 59px;
+ bottom: 1px;
+ text-align: left;
+ font-size: 10pt;
+ opacity: 0.6;
+}
+
+/* Help popup plugin */
+.impress-enabled #impress-help {
+ background: none repeat scroll 0 0 rgba(0, 0, 0, 0.5);
+ color: #EEEEEE;
+ font-size: 80%;
+ position: fixed;
+ left: 2em;
+ bottom: 2em;
+ width: 24em;
+ border-radius: 1em;
+ padding: 1em;
+ text-align: center;
+ z-index: 100;
+ font-family: Verdana, Arial, Sans;
+}
+.impress-enabled #impress-help td {
+ padding-left: 1em;
+ padding-right: 1em;
+}
+
+/* Substep plugin */
+
+#impress .step .substep {
+ opacity: 0;
+}
+
+#impress .step .substep.substep-visible {
+ opacity: 1;
+ transition: opacity 1s;
+}
+
+.impress-enabled { pointer-events: none }
+.impress-enabled #impress { pointer-events: auto }
+.impress-enabled #impress-toolbar { pointer-events: auto }
+.impress-enabled #impress-console-button { pointer-events: auto }
diff --git a/src/main/java/com/fr/plugin/easyslides/web/impress-main.js b/src/main/java/com/fr/plugin/easyslides/web/impress-main.js
new file mode 100644
index 0000000..a0c2b7f
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/web/impress-main.js
@@ -0,0 +1,918 @@
+// This file was automatically generated from files in src/ directory.
+
+/*! Licensed under MIT License - http://github.com/impress/impress.js */
+/**
+ * impress.js
+ *
+ * impress.js is a presentation tool based on the power of CSS3 transforms and transitions
+ * in modern browsers and inspired by the idea behind prezi.com.
+ *
+ *
+ * Copyright 2011-2012 Bartek Szopka (@bartaz), 2016-2018 Henrik Ingo (@henrikingo)
+ *
+ * Released under the MIT License.
+ *
+ * ------------------------------------------------
+ * author: Bartek Szopka, Henrik Ingo
+ * version: 1.0.0
+ * url: http://impress.js.org
+ * source: http://github.com/impress/impress.js/
+ */
+
+// You are one of those who like to know how things work inside?
+// Let me show you the cogs that make impress.js run...
+( function( document, window ) {
+ "use strict";
+ var lib;
+
+ // HELPER FUNCTIONS
+
+ // `pfx` is a function that takes a standard CSS property name as a parameter
+ // and returns it's prefixed version valid for current browser it runs in.
+ // The code is heavily inspired by Modernizr http://www.modernizr.com/
+ var pfx = ( function() {
+
+ var style = document.createElement( "dummy" ).style,
+ prefixes = "Webkit Moz O ms Khtml".split( " " ),
+ memory = {};
+
+ return function( prop ) {
+ if ( typeof memory[ prop ] === "undefined" ) {
+
+ var ucProp = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
+ props = ( prop + " " + prefixes.join( ucProp + " " ) + ucProp ).split( " " );
+
+ memory[ prop ] = null;
+ for ( var i in props ) {
+ if ( style[ props[ i ] ] !== undefined ) {
+ memory[ prop ] = props[ i ];
+ break;
+ }
+ }
+
+ }
+
+ return memory[ prop ];
+ };
+
+ } )();
+
+ var validateOrder = function( order, fallback ) {
+ var validChars = "xyz";
+ var returnStr = "";
+ if ( typeof order === "string" ) {
+ for ( var i in order.split( "" ) ) {
+ if ( validChars.indexOf( order[ i ] ) >= 0 ) {
+ returnStr += order[ i ];
+
+ // Each of x,y,z can be used only once.
+ validChars = validChars.split( order[ i ] ).join( "" );
+ }
+ }
+ }
+ if ( returnStr ) {
+ return returnStr;
+ } else if ( fallback !== undefined ) {
+ return fallback;
+ } else {
+ return "xyz";
+ }
+ };
+
+ // `css` function applies the styles given in `props` object to the element
+ // given as `el`. It runs all property names through `pfx` function to make
+ // sure proper prefixed version of the property is used.
+ var css = function( el, props ) {
+ var key, pkey;
+ for ( key in props ) {
+ if ( props.hasOwnProperty( key ) ) {
+ pkey = pfx( key );
+ if ( pkey !== null ) {
+ el.style[ pkey ] = props[ key ];
+ }
+ }
+ }
+ return el;
+ };
+
+ // `translate` builds a translate transform string for given data.
+ var translate = function( t ) {
+ return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) ";
+ };
+
+ // `rotate` builds a rotate transform string for given data.
+ // By default the rotations are in X Y Z order that can be reverted by passing `true`
+ // as second parameter.
+ var rotate = function( r, revert ) {
+ var order = r.order ? r.order : "xyz";
+ var css = "";
+ var axes = order.split( "" );
+ if ( revert ) {
+ axes = axes.reverse();
+ }
+
+ for ( var i = 0; i < axes.length; i++ ) {
+ css += " rotate" + axes[ i ].toUpperCase() + "(" + r[ axes[ i ] ] + "deg)";
+ }
+ return css;
+ };
+
+ // `scale` builds a scale transform string for given data.
+ var scale = function( s ) {
+ return " scale(" + s + ") ";
+ };
+
+ // `computeWindowScale` counts the scale factor between window size and size
+ // defined for the presentation in the config.
+ var computeWindowScale = function( config ) {
+ var hScale = window.innerHeight / config.height,
+ wScale = window.innerWidth / config.width,
+ scale = hScale > wScale ? wScale : hScale;
+
+ if ( config.maxScale && scale > config.maxScale ) {
+ scale = config.maxScale;
+ }
+
+ if ( config.minScale && scale < config.minScale ) {
+ scale = config.minScale;
+ }
+
+ return scale;
+ };
+
+ // CHECK SUPPORT
+ var body = document.body;
+ var impressSupported =
+
+ // Browser should support CSS 3D transtorms
+ ( pfx( "perspective" ) !== null ) &&
+
+ // And `classList` and `dataset` APIs
+ ( body.classList ) &&
+ ( body.dataset );
+
+ if ( !impressSupported ) {
+
+ // We can't be sure that `classList` is supported
+ body.className += " impress-not-supported ";
+ }
+
+ // GLOBALS AND DEFAULTS
+
+ // This is where the root elements of all impress.js instances will be kept.
+ // Yes, this means you can have more than one instance on a page, but I'm not
+ // sure if it makes any sense in practice ;)
+ var roots = {};
+
+ var preInitPlugins = [];
+ var preStepLeavePlugins = [];
+
+ // Some default config values.
+ var defaults = {
+ width: 1024,
+ height: 768,
+ maxScale: 1,
+ minScale: 0,
+
+ perspective: 1000,
+
+ transitionDuration: 1000
+ };
+
+ // It's just an empty function ... and a useless comment.
+ var empty = function() { return false; };
+
+ // IMPRESS.JS API
+
+ // And that's where interesting things will start to happen.
+ // It's the core `impress` function that returns the impress.js API
+ // for a presentation based on the element with given id ("impress"
+ // by default).
+ var impress = window.impress = function( rootId ) {
+
+ // If impress.js is not supported by the browser return a dummy API
+ // it may not be a perfect solution but we return early and avoid
+ // running code that may use features not implemented in the browser.
+ if ( !impressSupported ) {
+ return {
+ init: empty,
+ goto: empty,
+ prev: empty,
+ next: empty,
+ swipe: empty,
+ tear: empty,
+ lib: {}
+ };
+ }
+
+ rootId = rootId || "impress";
+
+ // If given root is already initialized just return the API
+ if ( roots[ "impress-root-" + rootId ] ) {
+ return roots[ "impress-root-" + rootId ];
+ }
+
+ // The gc library depends on being initialized before we do any changes to DOM.
+ lib = initLibraries( rootId );
+
+ body.classList.remove( "impress-not-supported" );
+ body.classList.add( "impress-supported" );
+
+ // Data of all presentation steps
+ var stepsData = {};
+
+ // Element of currently active step
+ var activeStep = null;
+
+ // Current state (position, rotation and scale) of the presentation
+ var currentState = null;
+
+ // Array of step elements
+ var steps = null;
+
+ // Configuration options
+ var config = null;
+
+ // Scale factor of the browser window
+ var windowScale = null;
+
+ // Root presentation elements
+ var root = lib.util.byId( rootId );
+ var canvas = document.createElement( "div" );
+
+ var initialized = false;
+
+ // STEP EVENTS
+ //
+ // There are currently two step events triggered by impress.js
+ // `impress:stepenter` is triggered when the step is shown on the
+ // screen (the transition from the previous one is finished) and
+ // `impress:stepleave` is triggered when the step is left (the
+ // transition to next step just starts).
+
+ // Reference to last entered step
+ var lastEntered = null;
+
+ // `onStepEnter` is called whenever the step element is entered
+ // but the event is triggered only if the step is different than
+ // last entered step.
+ // We sometimes call `goto`, and therefore `onStepEnter`, just to redraw a step, such as
+ // after screen resize. In this case - more precisely, in any case - we trigger a
+ // `impress:steprefresh` event.
+ var onStepEnter = function( step ) {
+ if ( lastEntered !== step ) {
+ lib.util.triggerEvent( step, "impress:stepenter" );
+ lastEntered = step;
+ }
+ lib.util.triggerEvent( step, "impress:steprefresh" );
+ };
+
+ // `onStepLeave` is called whenever the currentStep element is left
+ // but the event is triggered only if the currentStep is the same as
+ // lastEntered step.
+ var onStepLeave = function( currentStep, nextStep ) {
+ if ( lastEntered === currentStep ) {
+ lib.util.triggerEvent( currentStep, "impress:stepleave", { next: nextStep } );
+ lastEntered = null;
+ }
+ };
+
+ // `initStep` initializes given step element by reading data from its
+ // data attributes and setting correct styles.
+ var initStep = function( el, idx ) {
+ var data = el.dataset,
+ step = {
+ translate: {
+ x: lib.util.toNumber( data.x ),
+ y: lib.util.toNumber( data.y ),
+ z: lib.util.toNumber( data.z )
+ },
+ rotate: {
+ x: lib.util.toNumber( data.rotateX ),
+ y: lib.util.toNumber( data.rotateY ),
+ z: lib.util.toNumber( data.rotateZ || data.rotate ),
+ order: validateOrder( data.rotateOrder )
+ },
+ scale: lib.util.toNumber( data.scale, 1 ),
+ transitionDuration: lib.util.toNumber(
+ data.transitionDuration, config.transitionDuration
+ ),
+ el: el
+ };
+
+ if ( !el.id ) {
+ el.id = "step-" + ( idx + 1 );
+ }
+
+ stepsData[ "impress-" + el.id ] = step;
+
+ css( el, {
+ position: "absolute",
+ transform: "translate(-50%,-50%)" +
+ translate( step.translate ) +
+ rotate( step.rotate ) +
+ scale( step.scale ),
+ transformStyle: "preserve-3d"
+ } );
+ };
+
+ // Initialize all steps.
+ // Read the data-* attributes, store in internal stepsData, and render with CSS.
+ var initAllSteps = function() {
+ steps = lib.util.$$( ".step", root );
+ steps.forEach( initStep );
+ };
+
+ // `init` API function that initializes (and runs) the presentation.
+ var init = function() {
+ if ( initialized ) { return; }
+ execPreInitPlugins( root );
+
+ // First we set up the viewport for mobile devices.
+ // For some reason iPad goes nuts when it is not done properly.
+ var meta = lib.util.$( "meta[name='viewport']" ) || document.createElement( "meta" );
+ meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";
+ if ( meta.parentNode !== document.head ) {
+ meta.name = "viewport";
+ document.head.appendChild( meta );
+ }
+
+ // Initialize configuration object
+ var rootData = root.dataset;
+ config = {
+ width: lib.util.toNumber( rootData.width, defaults.width ),
+ height: lib.util.toNumber( rootData.height, defaults.height ),
+ maxScale: lib.util.toNumber( rootData.maxScale, defaults.maxScale ),
+ minScale: lib.util.toNumber( rootData.minScale, defaults.minScale ),
+ perspective: lib.util.toNumber( rootData.perspective, defaults.perspective ),
+ transitionDuration: lib.util.toNumber(
+ rootData.transitionDuration, defaults.transitionDuration
+ )
+ };
+
+ windowScale = computeWindowScale( config );
+
+ // Wrap steps with "canvas" element
+ lib.util.arrayify( root.childNodes ).forEach( function( el ) {
+ canvas.appendChild( el );
+ } );
+ root.appendChild( canvas );
+
+ // Set initial styles
+ document.documentElement.style.height = "100%";
+
+ css( body, {
+ height: "100%",
+ overflow: "hidden"
+ } );
+
+ var rootStyles = {
+ position: "absolute",
+ transformOrigin: "top left",
+ transition: "all 0s ease-in-out",
+ transformStyle: "preserve-3d"
+ };
+
+ css( root, rootStyles );
+ css( root, {
+ top: "50%",
+ left: "50%",
+ perspective: ( config.perspective / windowScale ) + "px",
+ transform: scale( windowScale )
+ } );
+ css( canvas, rootStyles );
+
+ body.classList.remove( "impress-disabled" );
+ body.classList.add( "impress-enabled" );
+
+ // Get and init steps
+ initAllSteps();
+
+ // Set a default initial state of the canvas
+ currentState = {
+ translate: { x: 0, y: 0, z: 0 },
+ rotate: { x: 0, y: 0, z: 0, order: "xyz" },
+ scale: 1
+ };
+
+ initialized = true;
+
+ lib.util.triggerEvent( root, "impress:init",
+ { api: roots[ "impress-root-" + rootId ] } );
+ };
+
+ // `getStep` is a helper function that returns a step element defined by parameter.
+ // If a number is given, step with index given by the number is returned, if a string
+ // is given step element with such id is returned, if DOM element is given it is returned
+ // if it is a correct step element.
+ var getStep = function( step ) {
+ if ( typeof step === "number" ) {
+ step = step < 0 ? steps[ steps.length + step ] : steps[ step ];
+ } else if ( typeof step === "string" ) {
+ step = lib.util.byId( step );
+ }
+ return ( step && step.id && stepsData[ "impress-" + step.id ] ) ? step : null;
+ };
+
+ // Used to reset timeout for `impress:stepenter` event
+ var stepEnterTimeout = null;
+
+ // `goto` API function that moves to step given as `el` parameter (by index, id or element).
+ // `duration` optionally given as second parameter, is the transition duration in css.
+ // `reason` is the string "next", "prev" or "goto" (default) and will be made available to
+ // preStepLeave plugins.
+ // `origEvent` may contain event that caused the call to goto, such as a key press event
+ var goto = function( el, duration, reason, origEvent ) {
+ reason = reason || "goto";
+ origEvent = origEvent || null;
+
+ if ( !initialized ) {
+ return false;
+ }
+
+ // Re-execute initAllSteps for each transition. This allows to edit step attributes
+ // dynamically, such as change their coordinates, or even remove or add steps, and have
+ // that change apply when goto() is called.
+ initAllSteps();
+
+ if ( !( el = getStep( el ) ) ) {
+ return false;
+ }
+
+ // Sometimes it's possible to trigger focus on first link with some keyboard action.
+ // Browser in such a case tries to scroll the page to make this element visible
+ // (even that body overflow is set to hidden) and it breaks our careful positioning.
+ //
+ // So, as a lousy (and lazy) workaround we will make the page scroll back to the top
+ // whenever slide is selected
+ //
+ // If you are reading this and know any better way to handle it, I'll be glad to hear
+ // about it!
+ window.scrollTo( 0, 0 );
+
+ var step = stepsData[ "impress-" + el.id ];
+ duration = ( duration !== undefined ? duration : step.transitionDuration );
+
+ // If we are in fact moving to another step, start with executing the registered
+ // preStepLeave plugins.
+ if ( activeStep && activeStep !== el ) {
+ var event = { target: activeStep, detail: {} };
+ event.detail.next = el;
+ event.detail.transitionDuration = duration;
+ event.detail.reason = reason;
+ if ( origEvent ) {
+ event.origEvent = origEvent;
+ }
+
+ if ( execPreStepLeavePlugins( event ) === false ) {
+
+ // PreStepLeave plugins are allowed to abort the transition altogether, by
+ // returning false.
+ // see stop and substep plugins for an example of doing just that
+ return false;
+ }
+
+ // Plugins are allowed to change the detail values
+ el = event.detail.next;
+ step = stepsData[ "impress-" + el.id ];
+ duration = event.detail.transitionDuration;
+ }
+
+ if ( activeStep ) {
+ activeStep.classList.remove( "active" );
+ body.classList.remove( "impress-on-" + activeStep.id );
+ }
+ el.classList.add( "active" );
+
+ body.classList.add( "impress-on-" + el.id );
+
+ // Compute target state of the canvas based on given step
+ var target = {
+ rotate: {
+ x: -step.rotate.x,
+ y: -step.rotate.y,
+ z: -step.rotate.z,
+ order: step.rotate.order
+ },
+ translate: {
+ x: -step.translate.x,
+ y: -step.translate.y,
+ z: -step.translate.z
+ },
+ scale: 1 / step.scale
+ };
+
+ // Check if the transition is zooming in or not.
+ //
+ // This information is used to alter the transition style:
+ // when we are zooming in - we start with move and rotate transition
+ // and the scaling is delayed, but when we are zooming out we start
+ // with scaling down and move and rotation are delayed.
+ var zoomin = target.scale >= currentState.scale;
+
+ duration = lib.util.toNumber( duration, config.transitionDuration );
+ var delay = ( duration / 2 );
+
+ // If the same step is re-selected, force computing window scaling,
+ // because it is likely to be caused by window resize
+ if ( el === activeStep ) {
+ windowScale = computeWindowScale( config );
+ }
+
+ var targetScale = target.scale * windowScale;
+
+ // Trigger leave of currently active element (if it's not the same step again)
+ if ( activeStep && activeStep !== el ) {
+ onStepLeave( activeStep, el );
+ }
+
+ // Now we alter transforms of `root` and `canvas` to trigger transitions.
+ //
+ // And here is why there are two elements: `root` and `canvas` - they are
+ // being animated separately:
+ // `root` is used for scaling and `canvas` for translate and rotations.
+ // Transitions on them are triggered with different delays (to make
+ // visually nice and "natural" looking transitions), so we need to know
+ // that both of them are finished.
+ css( root, {
+
+ // To keep the perspective look similar for different scales
+ // we need to "scale" the perspective, too
+ // For IE 11 support we must specify perspective independent
+ // of transform.
+ perspective: ( config.perspective / targetScale ) + "px",
+ transform: scale( targetScale ),
+ transitionDuration: duration + "ms",
+ transitionDelay: ( zoomin ? delay : 0 ) + "ms"
+ } );
+
+ css( canvas, {
+ transform: rotate( target.rotate, true ) + translate( target.translate ),
+ transitionDuration: duration + "ms",
+ transitionDelay: ( zoomin ? 0 : delay ) + "ms"
+ } );
+
+ // Here is a tricky part...
+ //
+ // If there is no change in scale or no change in rotation and translation, it means
+ // there was actually no delay - because there was no transition on `root` or `canvas`
+ // elements. We want to trigger `impress:stepenter` event in the correct moment, so
+ // here we compare the current and target values to check if delay should be taken into
+ // account.
+ //
+ // I know that this `if` statement looks scary, but it's pretty simple when you know
+ // what is going on - it's simply comparing all the values.
+ if ( currentState.scale === target.scale ||
+ ( currentState.rotate.x === target.rotate.x &&
+ currentState.rotate.y === target.rotate.y &&
+ currentState.rotate.z === target.rotate.z &&
+ currentState.translate.x === target.translate.x &&
+ currentState.translate.y === target.translate.y &&
+ currentState.translate.z === target.translate.z ) ) {
+ delay = 0;
+ }
+
+ // Store current state
+ currentState = target;
+ activeStep = el;
+
+ // And here is where we trigger `impress:stepenter` event.
+ // We simply set up a timeout to fire it taking transition duration (and possible delay)
+ // into account.
+ //
+ // I really wanted to make it in more elegant way. The `transitionend` event seemed to
+ // be the best way to do it, but the fact that I'm using transitions on two separate
+ // elements and that the `transitionend` event is only triggered when there was a
+ // transition (change in the values) caused some bugs and made the code really
+ // complicated, cause I had to handle all the conditions separately. And it still
+ // needed a `setTimeout` fallback for the situations when there is no transition at all.
+ // So I decided that I'd rather make the code simpler than use shiny new
+ // `transitionend`.
+ //
+ // If you want learn something interesting and see how it was done with `transitionend`
+ // go back to version 0.5.2 of impress.js:
+ // http://github.com/bartaz/impress.js/blob/0.5.2/js/impress.js
+ window.clearTimeout( stepEnterTimeout );
+ stepEnterTimeout = window.setTimeout( function() {
+ onStepEnter( activeStep );
+ }, duration + delay );
+
+ return el;
+ };
+
+ // `prev` API function goes to previous step (in document order)
+ // `event` is optional, may contain the event that caused the need to call prev()
+ var prev = function( origEvent ) {
+ var prev = steps.indexOf( activeStep ) - 1;
+ prev = prev >= 0 ? steps[ prev ] : steps[ steps.length - 1 ];
+
+ return goto( prev, undefined, "prev", origEvent );
+ };
+
+ // `next` API function goes to next step (in document order)
+ // `event` is optional, may contain the event that caused the need to call next()
+ var next = function( origEvent ) {
+ var next = steps.indexOf( activeStep ) + 1;
+ next = next < steps.length ? steps[ next ] : steps[ 0 ];
+
+ return goto( next, undefined, "next", origEvent );
+ };
+
+ // Swipe for touch devices by @and3rson.
+ // Below we extend the api to control the animation between the currently
+ // active step and a presumed next/prev step. See touch plugin for
+ // an example of using this api.
+
+ // Helper function
+ var interpolate = function( a, b, k ) {
+ return a + ( b - a ) * k;
+ };
+
+ // Animate a swipe.
+ //
+ // Pct is a value between -1.0 and +1.0, designating the current length
+ // of the swipe.
+ //
+ // If pct is negative, swipe towards the next() step, if positive,
+ // towards the prev() step.
+ //
+ // Note that pre-stepleave plugins such as goto can mess with what is a
+ // next() and prev() step, so we need to trigger the pre-stepleave event
+ // here, even if a swipe doesn't guarantee that the transition will
+ // actually happen.
+ //
+ // Calling swipe(), with any value of pct, won't in itself cause a
+ // transition to happen, this is just to animate the swipe. Once the
+ // transition is committed - such as at a touchend event - caller is
+ // responsible for also calling prev()/next() as appropriate.
+ //
+ // Note: For now, this function is made available to be used by the swipe plugin (which
+ // is the UI counterpart to this). It is a semi-internal API and intentionally not
+ // documented in DOCUMENTATION.md.
+ var swipe = function( pct ) {
+ if ( Math.abs( pct ) > 1 ) {
+ return;
+ }
+
+ // Prepare & execute the preStepLeave event
+ var event = { target: activeStep, detail: {} };
+ event.detail.swipe = pct;
+
+ // Will be ignored within swipe animation, but just in case a plugin wants to read this,
+ // humor them
+ event.detail.transitionDuration = config.transitionDuration;
+ var idx; // Needed by jshint
+ if ( pct < 0 ) {
+ idx = steps.indexOf( activeStep ) + 1;
+ event.detail.next = idx < steps.length ? steps[ idx ] : steps[ 0 ];
+ event.detail.reason = "next";
+ } else if ( pct > 0 ) {
+ idx = steps.indexOf( activeStep ) - 1;
+ event.detail.next = idx >= 0 ? steps[ idx ] : steps[ steps.length - 1 ];
+ event.detail.reason = "prev";
+ } else {
+
+ // No move
+ return;
+ }
+ if ( execPreStepLeavePlugins( event ) === false ) {
+
+ // If a preStepLeave plugin wants to abort the transition, don't animate a swipe
+ // For stop, this is probably ok. For substep, the plugin it self might want to do
+ // some animation, but that's not the current implementation.
+ return false;
+ }
+ var nextElement = event.detail.next;
+
+ var nextStep = stepsData[ "impress-" + nextElement.id ];
+
+ // If the same step is re-selected, force computing window scaling,
+ var nextScale = nextStep.scale * windowScale;
+ var k = Math.abs( pct );
+
+ var interpolatedStep = {
+ translate: {
+ x: interpolate( currentState.translate.x, -nextStep.translate.x, k ),
+ y: interpolate( currentState.translate.y, -nextStep.translate.y, k ),
+ z: interpolate( currentState.translate.z, -nextStep.translate.z, k )
+ },
+ rotate: {
+ x: interpolate( currentState.rotate.x, -nextStep.rotate.x, k ),
+ y: interpolate( currentState.rotate.y, -nextStep.rotate.y, k ),
+ z: interpolate( currentState.rotate.z, -nextStep.rotate.z, k ),
+
+ // Unfortunately there's a discontinuity if rotation order changes. Nothing I
+ // can do about it?
+ order: k < 0.7 ? currentState.rotate.order : nextStep.rotate.order
+ },
+ scale: interpolate( currentState.scale * windowScale, nextScale, k )
+ };
+
+ css( root, {
+
+ // To keep the perspective look similar for different scales
+ // we need to 'scale' the perspective, too
+ perspective: config.perspective / interpolatedStep.scale + "px",
+ transform: scale( interpolatedStep.scale ),
+ transitionDuration: "0ms",
+ transitionDelay: "0ms"
+ } );
+
+ css( canvas, {
+ transform: rotate( interpolatedStep.rotate, true ) +
+ translate( interpolatedStep.translate ),
+ transitionDuration: "0ms",
+ transitionDelay: "0ms"
+ } );
+ };
+
+ // Teardown impress
+ // Resets the DOM to the state it was before impress().init() was called.
+ // (If you called impress(rootId).init() for multiple different rootId's, then you must
+ // also call tear() once for each of them.)
+ var tear = function() {
+ lib.gc.teardown();
+ delete roots[ "impress-root-" + rootId ];
+ };
+
+ // Adding some useful classes to step elements.
+ //
+ // All the steps that have not been shown yet are given `future` class.
+ // When the step is entered the `future` class is removed and the `present`
+ // class is given. When the step is left `present` class is replaced with
+ // `past` class.
+ //
+ // So every step element is always in one of three possible states:
+ // `future`, `present` and `past`.
+ //
+ // There classes can be used in CSS to style different types of steps.
+ // For example the `present` class can be used to trigger some custom
+ // animations when step is shown.
+ lib.gc.addEventListener( root, "impress:init", function() {
+
+ // STEP CLASSES
+ steps.forEach( function( step ) {
+ step.classList.add( "future" );
+ } );
+
+ lib.gc.addEventListener( root, "impress:stepenter", function( event ) {
+ event.target.classList.remove( "past" );
+ event.target.classList.remove( "future" );
+ event.target.classList.add( "present" );
+ }, false );
+
+ lib.gc.addEventListener( root, "impress:stepleave", function( event ) {
+ event.target.classList.remove( "present" );
+ event.target.classList.add( "past" );
+ }, false );
+
+ }, false );
+
+ // Adding hash change support.
+ lib.gc.addEventListener( root, "impress:init", function() {
+
+ // Last hash detected
+ var lastHash = "";
+
+ // `#/step-id` is used instead of `#step-id` to prevent default browser
+ // scrolling to element in hash.
+ //
+ // And it has to be set after animation finishes, because in Chrome it
+ // makes transtion laggy.
+ // BUG: http://code.google.com/p/chromium/issues/detail?id=62820
+ lib.gc.addEventListener( root, "impress:stepenter", function( event ) {
+ window.location.hash = lastHash = "#/" + event.target.id;
+ }, false );
+
+ lib.gc.addEventListener( window, "hashchange", function() {
+
+ // When the step is entered hash in the location is updated
+ // (just few lines above from here), so the hash change is
+ // triggered and we would call `goto` again on the same element.
+ //
+ // To avoid this we store last entered hash and compare.
+ if ( window.location.hash !== lastHash ) {
+ goto( lib.util.getElementFromHash() );
+ }
+ }, false );
+
+ // START
+ // by selecting step defined in url or first step of the presentation
+ goto( lib.util.getElementFromHash() || steps[ 0 ], 0 );
+ }, false );
+
+ body.classList.add( "impress-disabled" );
+
+ // Store and return API for given impress.js root element
+ return ( roots[ "impress-root-" + rootId ] = {
+ init: init,
+ goto: goto,
+ next: next,
+ prev: prev,
+ swipe: swipe,
+ tear: tear,
+ lib: lib
+ } );
+
+ };
+
+ // Flag that can be used in JS to check if browser have passed the support test
+ impress.supported = impressSupported;
+
+ // ADD and INIT LIBRARIES
+ // Library factories are defined in src/lib/*.js, and register themselves by calling
+ // impress.addLibraryFactory(libraryFactoryObject). They're stored here, and used to augment
+ // the API with library functions when client calls impress(rootId).
+ // See src/lib/README.md for clearer example.
+ // (Advanced usage: For different values of rootId, a different instance of the libaries are
+ // generated, in case they need to hold different state for different root elements.)
+ var libraryFactories = {};
+ impress.addLibraryFactory = function( obj ) {
+ for ( var libname in obj ) {
+ if ( obj.hasOwnProperty( libname ) ) {
+ libraryFactories[ libname ] = obj[ libname ];
+ }
+ }
+ };
+
+ // Call each library factory, and return the lib object that is added to the api.
+ var initLibraries = function( rootId ) { //jshint ignore:line
+ var lib = {};
+ for ( var libname in libraryFactories ) {
+ if ( libraryFactories.hasOwnProperty( libname ) ) {
+ if ( lib[ libname ] !== undefined ) {
+ throw "impress.js ERROR: Two libraries both tried to use libname: " + libname;
+ }
+ lib[ libname ] = libraryFactories[ libname ]( rootId );
+ }
+ }
+ return lib;
+ };
+
+ // `addPreInitPlugin` allows plugins to register a function that should
+ // be run (synchronously) at the beginning of init, before
+ // impress().init() itself executes.
+ impress.addPreInitPlugin = function( plugin, weight ) {
+ weight = parseInt( weight ) || 10;
+ if ( weight <= 0 ) {
+ throw "addPreInitPlugin: weight must be a positive integer";
+ }
+
+ if ( preInitPlugins[ weight ] === undefined ) {
+ preInitPlugins[ weight ] = [];
+ }
+ preInitPlugins[ weight ].push( plugin );
+ };
+
+ // Called at beginning of init, to execute all pre-init plugins.
+ var execPreInitPlugins = function( root ) { //jshint ignore:line
+ for ( var i = 0; i < preInitPlugins.length; i++ ) {
+ var thisLevel = preInitPlugins[ i ];
+ if ( thisLevel !== undefined ) {
+ for ( var j = 0; j < thisLevel.length; j++ ) {
+ thisLevel[ j ]( root );
+ }
+ }
+ }
+ };
+
+ // `addPreStepLeavePlugin` allows plugins to register a function that should
+ // be run (synchronously) at the beginning of goto()
+ impress.addPreStepLeavePlugin = function( plugin, weight ) { //jshint ignore:line
+ weight = parseInt( weight ) || 10;
+ if ( weight <= 0 ) {
+ throw "addPreStepLeavePlugin: weight must be a positive integer";
+ }
+
+ if ( preStepLeavePlugins[ weight ] === undefined ) {
+ preStepLeavePlugins[ weight ] = [];
+ }
+ preStepLeavePlugins[ weight ].push( plugin );
+ };
+
+ // Called at beginning of goto(), to execute all preStepLeave plugins.
+ var execPreStepLeavePlugins = function( event ) { //jshint ignore:line
+ for ( var i = 0; i < preStepLeavePlugins.length; i++ ) {
+ var thisLevel = preStepLeavePlugins[ i ];
+ if ( thisLevel !== undefined ) {
+ for ( var j = 0; j < thisLevel.length; j++ ) {
+ if ( thisLevel[ j ]( event ) === false ) {
+
+ // If a plugin returns false, the stepleave event (and related transition)
+ // is aborted
+ return false;
+ }
+ }
+ }
+ }
+ };
+
+} )( document, window );
+
+// THAT'S ALL FOLKS!
+//
+// Thanks for reading it all.
+// Or thanks for scrolling down and reading the last part.
+//
+// I've learnt a lot when building impress.js and I hope this code and comments
+// will help somebody learn at least some part of it.
diff --git a/src/main/java/com/fr/plugin/easyslides/web/impress-plugins.js b/src/main/java/com/fr/plugin/easyslides/web/impress-plugins.js
new file mode 100644
index 0000000..70bffe3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/easyslides/web/impress-plugins.js
@@ -0,0 +1,3384 @@
+// This file was automatically generated from files in src/ directory.
+
+/*! Licensed under MIT License - http://github.com/impress/impress.js */
+
+/**
+ * Garbage collection utility
+ *
+ * This library allows plugins to add elements and event listeners they add to the DOM. The user
+ * can call `impress().lib.gc.teardown()` to cause all of them to be removed from DOM, so that
+ * the document is in the state it was before calling `impress().init()`.
+ *
+ * In addition to just adding elements and event listeners to the garbage collector, plugins
+ * can also register callback functions to do arbitrary cleanup upon teardown.
+ *
+ * Henrik Ingo (c) 2016
+ * MIT License
+ */
+
+( function( document, window ) {
+ "use strict";
+ var roots = [];
+ var rootsCount = 0;
+ var startingState = { roots: [] };
+
+ var libraryFactory = function( rootId ) {
+ if ( roots[ rootId ] ) {
+ return roots[ rootId ];
+ }
+
+ // Per root global variables (instance variables?)
+ var elementList = [];
+ var eventListenerList = [];
+ var callbackList = [];
+
+ recordStartingState( rootId );
+
+ // LIBRARY FUNCTIONS
+ // Definitions of the library functions we return as an object at the end
+
+ // `pushElement` adds a DOM element to the gc stack
+ var pushElement = function( element ) {
+ elementList.push( element );
+ };
+
+ // `appendChild` is a convenience wrapper that combines DOM appendChild with gc.pushElement
+ var appendChild = function( parent, element ) {
+ parent.appendChild( element );
+ pushElement( element );
+ };
+
+ // `pushEventListener` adds an event listener to the gc stack
+ var pushEventListener = function( target, type, listenerFunction ) {
+ eventListenerList.push( { target:target, type:type, listener:listenerFunction } );
+ };
+
+ // `addEventListener` combines DOM addEventListener with gc.pushEventListener
+ var addEventListener = function( target, type, listenerFunction ) {
+ target.addEventListener( type, listenerFunction );
+ pushEventListener( target, type, listenerFunction );
+ };
+
+ // `pushCallback` If the above utilities are not enough, plugins can add their own callback
+ // function to do arbitrary things.
+ var pushCallback = function( callback ) {
+ callbackList.push( callback );
+ };
+ pushCallback( function( rootId ) { resetStartingState( rootId ); } );
+
+ // `teardown` will
+ // - execute all callbacks in LIFO order
+ // - call `removeChild` on all DOM elements in LIFO order
+ // - call `removeEventListener` on all event listeners in LIFO order
+ // The goal of a teardown is to return to the same state that the DOM was before
+ // `impress().init()` was called.
+ var teardown = function() {
+
+ // Execute the callbacks in LIFO order
+ var i; // Needed by jshint
+ for ( i = callbackList.length - 1; i >= 0; i-- ) {
+ callbackList[ i ]( rootId );
+ }
+ callbackList = [];
+ for ( i = 0; i < elementList.length; i++ ) {
+ elementList[ i ].parentElement.removeChild( elementList[ i ] );
+ }
+ elementList = [];
+ for ( i = 0; i < eventListenerList.length; i++ ) {
+ var target = eventListenerList[ i ].target;
+ var type = eventListenerList[ i ].type;
+ var listener = eventListenerList[ i ].listener;
+ target.removeEventListener( type, listener );
+ }
+ };
+
+ var lib = {
+ pushElement: pushElement,
+ appendChild: appendChild,
+ pushEventListener: pushEventListener,
+ addEventListener: addEventListener,
+ pushCallback: pushCallback,
+ teardown: teardown
+ };
+ roots[ rootId ] = lib;
+ rootsCount++;
+ return lib;
+ };
+
+ // Let impress core know about the existence of this library
+ window.impress.addLibraryFactory( { gc: libraryFactory } );
+
+ // CORE INIT
+ // The library factory (gc(rootId)) is called at the beginning of impress(rootId).init()
+ // For the purposes of teardown(), we can use this as an opportunity to save the state
+ // of a few things in the DOM in their virgin state, before impress().init() did anything.
+ // Note: These could also be recorded by the code in impress.js core as these values
+ // are changed, but in an effort to not deviate too much from upstream, I'm adding
+ // them here rather than the core itself.
+ var recordStartingState = function( rootId ) {
+ startingState.roots[ rootId ] = {};
+ startingState.roots[ rootId ].steps = [];
+
+ // Record whether the steps have an id or not
+ var steps = document.getElementById( rootId ).querySelectorAll( ".step" );
+ for ( var i = 0; i < steps.length; i++ ) {
+ var el = steps[ i ];
+ startingState.roots[ rootId ].steps.push( {
+ el: el,
+ id: el.getAttribute( "id" )
+ } );
+ }
+
+ // In the rare case of multiple roots, the following is changed on first init() and
+ // reset at last tear().
+ if ( rootsCount === 0 ) {
+ startingState.body = {};
+
+ // It is customary for authors to set body.class="impress-not-supported" as a starting
+ // value, which can then be removed by impress().init(). But it is not required.
+ // Remember whether it was there or not.
+ if ( document.body.classList.contains( "impress-not-supported" ) ) {
+ startingState.body.impressNotSupported = true;
+ } else {
+ startingState.body.impressNotSupported = false;
+ }
+
+ // If there's a element, its contents will be overwritten by init
+ var metas = document.head.querySelectorAll( "meta" );
+ for ( i = 0; i < metas.length; i++ ) {
+ var m = metas[ i ];
+ if ( m.name === "viewport" ) {
+ startingState.meta = m.content;
+ }
+ }
+ }
+ };
+
+ // CORE TEARDOWN
+ var resetStartingState = function( rootId ) {
+
+ // Reset body element
+ document.body.classList.remove( "impress-enabled" );
+ document.body.classList.remove( "impress-disabled" );
+
+ var root = document.getElementById( rootId );
+ var activeId = root.querySelector( ".active" ).id;
+ document.body.classList.remove( "impress-on-" + activeId );
+
+ document.documentElement.style.height = "";
+ document.body.style.height = "";
+ document.body.style.overflow = "";
+
+ // Remove style values from the root and step elements
+ // Note: We remove the ones set by impress.js core. Otoh, we didn't preserve any original
+ // values. A more sophisticated implementation could keep track of original values and then
+ // reset those.
+ var steps = root.querySelectorAll( ".step" );
+ for ( var i = 0; i < steps.length; i++ ) {
+ steps[ i ].classList.remove( "future" );
+ steps[ i ].classList.remove( "past" );
+ steps[ i ].classList.remove( "present" );
+ steps[ i ].classList.remove( "active" );
+ steps[ i ].style.position = "";
+ steps[ i ].style.transform = "";
+ steps[ i ].style[ "transform-style" ] = "";
+ }
+ root.style.position = "";
+ root.style[ "transform-origin" ] = "";
+ root.style.transition = "";
+ root.style[ "transform-style" ] = "";
+ root.style.top = "";
+ root.style.left = "";
+ root.style.transform = "";
+
+ // Reset id of steps ("step-1" id's are auto generated)
+ steps = startingState.roots[ rootId ].steps;
+ var step;
+ while ( step = steps.pop() ) {
+ if ( step.id === null ) {
+ step.el.removeAttribute( "id" );
+ } else {
+ step.el.setAttribute( "id", step.id );
+ }
+ }
+ delete startingState.roots[ rootId ];
+
+ // Move step div elements away from canvas, then delete canvas
+ // Note: There's an implicit assumption here that the canvas div is the only child element
+ // of the root div. If there would be something else, it's gonna be lost.
+ var canvas = root.firstChild;
+ var canvasHTML = canvas.innerHTML;
+ root.innerHTML = canvasHTML;
+
+ if ( roots[ rootId ] !== undefined ) {
+ delete roots[ rootId ];
+ rootsCount--;
+ }
+ if ( rootsCount === 0 ) {
+
+ // In the rare case that more than one impress root elements were initialized, these
+ // are only reset when all are uninitialized.
+ document.body.classList.remove( "impress-supported" );
+ if ( startingState.body.impressNotSupported ) {
+ document.body.classList.add( "impress-not-supported" );
+ }
+
+ // We need to remove or reset the meta element inserted by impress.js
+ var metas = document.head.querySelectorAll( "meta" );
+ for ( i = 0; i < metas.length; i++ ) {
+ var m = metas[ i ];
+ if ( m.name === "viewport" ) {
+ if ( startingState.meta !== undefined ) {
+ m.content = startingState.meta;
+ } else {
+ m.parentElement.removeChild( m );
+ }
+ }
+ }
+ }
+
+ };
+
+} )( document, window );
+
+/**
+ * Common utility functions
+ *
+ * Copyright 2011-2012 Bartek Szopka (@bartaz)
+ * Henrik Ingo (c) 2016
+ * MIT License
+ */
+
+( function( document, window ) {
+ "use strict";
+ var roots = [];
+
+ var libraryFactory = function( rootId ) {
+ if ( roots[ rootId ] ) {
+ return roots[ rootId ];
+ }
+
+ // `$` returns first element for given CSS `selector` in the `context` of
+ // the given element or whole document.
+ var $ = function( selector, context ) {
+ context = context || document;
+ return context.querySelector( selector );
+ };
+
+ // `$$` return an array of elements for given CSS `selector` in the `context` of
+ // the given element or whole document.
+ var $$ = function( selector, context ) {
+ context = context || document;
+ return arrayify( context.querySelectorAll( selector ) );
+ };
+
+ // `arrayify` takes an array-like object and turns it into real Array
+ // to make all the Array.prototype goodness available.
+ var arrayify = function( a ) {
+ return [].slice.call( a );
+ };
+
+ // `byId` returns element with given `id` - you probably have guessed that ;)
+ var byId = function( id ) {
+ return document.getElementById( id );
+ };
+
+ // `getElementFromHash` returns an element located by id from hash part of
+ // window location.
+ var getElementFromHash = function() {
+
+ // Get id from url # by removing `#` or `#/` from the beginning,
+ // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
+ return byId( window.location.hash.replace( /^#\/?/, "" ) );
+ };
+
+ // Throttling function calls, by Remy Sharp
+ // http://remysharp.com/2010/07/21/throttling-function-calls/
+ var throttle = function( fn, delay ) {
+ var timer = null;
+ return function() {
+ var context = this, args = arguments;
+ window.clearTimeout( timer );
+ timer = window.setTimeout( function() {
+ fn.apply( context, args );
+ }, delay );
+ };
+ };
+
+ // `toNumber` takes a value given as `numeric` parameter and tries to turn
+ // it into a number. If it is not possible it returns 0 (or other value
+ // given as `fallback`).
+ var toNumber = function( numeric, fallback ) {
+ return isNaN( numeric ) ? ( fallback || 0 ) : Number( numeric );
+ };
+
+ // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data
+ // and triggers it on element given as `el`.
+ var triggerEvent = function( el, eventName, detail ) {
+ var event = document.createEvent( "CustomEvent" );
+ event.initCustomEvent( eventName, true, true, detail );
+ el.dispatchEvent( event );
+ };
+
+ var lib = {
+ $: $,
+ $$: $$,
+ arrayify: arrayify,
+ byId: byId,
+ getElementFromHash: getElementFromHash,
+ throttle: throttle,
+ toNumber: toNumber,
+ triggerEvent: triggerEvent
+ };
+ roots[ rootId ] = lib;
+ return lib;
+ };
+
+ // Let impress core know about the existence of this library
+ window.impress.addLibraryFactory( { util: libraryFactory } );
+
+} )( document, window );
+
+/**
+ * Autoplay plugin - Automatically advance slideshow after N seconds
+ *
+ * Copyright 2016 Henrik Ingo, henrik.ingo@avoinelama.fi
+ * Released under the MIT license.
+ */
+/* global clearTimeout, setTimeout, document */
+
+( function( document ) {
+ "use strict";
+
+ var autoplayDefault = 0;
+ var currentStepTimeout = 0;
+ var api = null;
+ var timeoutHandle = null;
+ var root = null;
+ var util;
+
+ // On impress:init, check whether there is a default setting, as well as
+ // handle step-1.
+ document.addEventListener( "impress:init", function( event ) {
+ util = event.detail.api.lib.util;
+
+ // Getting API from event data instead of global impress().init().
+ // You don't even need to know what is the id of the root element
+ // or anything. `impress:init` event data gives you everything you
+ // need to control the presentation that was just initialized.
+ api = event.detail.api;
+ root = event.target;
+
+ // Element attributes starting with "data-", become available under
+ // element.dataset. In addition hyphenized words become camelCased.
+ var data = root.dataset;
+
+ if ( data.autoplay ) {
+ autoplayDefault = util.toNumber( data.autoplay, 0 );
+ }
+
+ var toolbar = document.querySelector( "#impress-toolbar" );
+ if ( toolbar ) {
+ addToolbarButton( toolbar );
+ }
+
+ api.lib.gc.pushCallback( function() {
+ clearTimeout( timeoutHandle );
+ } );
+
+ // Note that right after impress:init event, also impress:stepenter is
+ // triggered for the first slide, so that's where code flow continues.
+ }, false );
+
+ document.addEventListener( "impress:autoplay:pause", function( event ) {
+ status = "paused";
+ reloadTimeout( event );
+ }, false );
+
+ document.addEventListener( "impress:autoplay:play", function( event ) {
+ status = "playing";
+ reloadTimeout( event );
+ }, false );
+
+ // If default autoplay time was defined in the presentation root, or
+ // in this step, set timeout.
+ var reloadTimeout = function( event ) {
+ var step = event.target;
+ currentStepTimeout = util.toNumber( step.dataset.autoplay, autoplayDefault );
+ if ( status === "paused" ) {
+ setAutoplayTimeout( 0 );
+ } else {
+ setAutoplayTimeout( currentStepTimeout );
+ }
+ };
+
+ document.addEventListener( "impress:stepenter", function( event ) {
+ reloadTimeout( event );
+ }, false );
+
+ document.addEventListener( "impress:substep:enter", function( event ) {
+ reloadTimeout( event );
+ }, false );
+
+ /**
+ * Set timeout after which we move to next() step.
+ */
+ var setAutoplayTimeout = function( timeout ) {
+ if ( timeoutHandle ) {
+ clearTimeout( timeoutHandle );
+ }
+
+ if ( timeout > 0 ) {
+ timeoutHandle = setTimeout( function() { api.next(); }, timeout * 1000 );
+ }
+ setButtonText();
+ };
+
+ /*** Toolbar plugin integration *******************************************/
+ var status = "not clicked";
+ var toolbarButton = null;
+
+ var makeDomElement = function( html ) {
+ var tempDiv = document.createElement( "div" );
+ tempDiv.innerHTML = html;
+ return tempDiv.firstChild;
+ };
+
+ var toggleStatus = function() {
+ if ( currentStepTimeout > 0 && status !== "paused" ) {
+ status = "paused";
+ } else {
+ status = "playing";
+ }
+ };
+
+ var getButtonText = function() {
+ if ( currentStepTimeout > 0 && status !== "paused" ) {
+ return "||"; // Pause
+ } else {
+ return "▶"; // Play
+ }
+ };
+
+ var setButtonText = function() {
+ if ( toolbarButton ) {
+
+ // Keep button size the same even if label content is changing
+ var buttonWidth = toolbarButton.offsetWidth;
+ var buttonHeight = toolbarButton.offsetHeight;
+ toolbarButton.innerHTML = getButtonText();
+ if ( !toolbarButton.style.width ) {
+ toolbarButton.style.width = buttonWidth + "px";
+ }
+ if ( !toolbarButton.style.height ) {
+ toolbarButton.style.height = buttonHeight + "px";
+ }
+ }
+ };
+
+ var addToolbarButton = function( toolbar ) {
+ var html = '"; // jshint ignore:line
+ toolbarButton = makeDomElement( html );
+ toolbarButton.addEventListener( "click", function() {
+ toggleStatus();
+ if ( status === "playing" ) {
+ if ( autoplayDefault === 0 ) {
+ autoplayDefault = 7;
+ }
+ if ( currentStepTimeout === 0 ) {
+ currentStepTimeout = autoplayDefault;
+ }
+ setAutoplayTimeout( currentStepTimeout );
+ } else if ( status === "paused" ) {
+ setAutoplayTimeout( 0 );
+ }
+ } );
+
+ util.triggerEvent( toolbar, "impress:toolbar:appendChild",
+ { group: 10, element: toolbarButton } );
+ };
+
+} )( document );
+
+/**
+ * Blackout plugin
+ *
+ * Press b or . to hide all slides, and b or . again to show them.
+ * Also navigating to a different slide will show them again (impress:stepleave).
+ *
+ * Copyright 2014 @Strikeskids
+ * Released under the MIT license.
+ */
+/* global document */
+
+( function( document ) {
+ "use strict";
+
+ var canvas = null;
+ var blackedOut = false;
+ var util = null;
+ var root = null;
+ var api = null;
+
+ // While waiting for a shared library of utilities, copying these 2 from main impress.js
+ var css = function( el, props ) {
+ var key, pkey;
+ for ( key in props ) {
+ if ( props.hasOwnProperty( key ) ) {
+ pkey = pfx( key );
+ if ( pkey !== null ) {
+ el.style[ pkey ] = props[ key ];
+ }
+ }
+ }
+ return el;
+ };
+
+ var pfx = ( function() {
+
+ var style = document.createElement( "dummy" ).style,
+ prefixes = "Webkit Moz O ms Khtml".split( " " ),
+ memory = {};
+
+ return function( prop ) {
+ if ( typeof memory[ prop ] === "undefined" ) {
+
+ var ucProp = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
+ props = ( prop + " " + prefixes.join( ucProp + " " ) + ucProp ).split( " " );
+
+ memory[ prop ] = null;
+ for ( var i in props ) {
+ if ( style[ props[ i ] ] !== undefined ) {
+ memory[ prop ] = props[ i ];
+ break;
+ }
+ }
+
+ }
+
+ return memory[ prop ];
+ };
+
+ } )();
+
+ var removeBlackout = function() {
+ if ( blackedOut ) {
+ css( canvas, {
+ display: "block"
+ } );
+ blackedOut = false;
+ util.triggerEvent( root, "impress:autoplay:play", {} );
+ }
+ };
+
+ var blackout = function() {
+ if ( blackedOut ) {
+ removeBlackout();
+ } else {
+ css( canvas, {
+ display: ( blackedOut = !blackedOut ) ? "none" : "block"
+ } );
+ blackedOut = true;
+ util.triggerEvent( root, "impress:autoplay:pause", {} );
+ }
+ };
+
+ // Wait for impress.js to be initialized
+ document.addEventListener( "impress:init", function( event ) {
+ api = event.detail.api;
+ util = api.lib.util;
+ root = event.target;
+ canvas = root.firstElementChild;
+ var gc = api.lib.gc;
+ var util = api.lib.util;
+
+ gc.addEventListener( document, "keydown", function( event ) {
+
+ // Accept b or . -> . is sent by presentation remote controllers
+ if ( event.keyCode === 66 || event.keyCode === 190 ) {
+ event.preventDefault();
+ if ( !blackedOut ) {
+ blackout();
+ } else {
+ removeBlackout();
+ }
+ }
+ }, false );
+
+ gc.addEventListener( document, "keyup", function( event ) {
+
+ // Accept b or . -> . is sent by presentation remote controllers
+ if ( event.keyCode === 66 || event.keyCode === 190 ) {
+ event.preventDefault();
+ }
+ }, false );
+
+ }, false );
+
+ document.addEventListener( "impress:stepleave", function() {
+ removeBlackout();
+ }, false );
+
+} )( document );
+
+
+/**
+ * Extras Plugin
+ *
+ * This plugin performs initialization (like calling mermaid.initialize())
+ * for the extras/ plugins if they are loaded into a presentation.
+ *
+ * See README.md for details.
+ *
+ * Copyright 2016 Henrik Ingo (@henrikingo)
+ * Released under the MIT license.
+ */
+/* global markdown, hljs, mermaid, impress, document, window */
+
+( function( document, window ) {
+ "use strict";
+
+ var preInit = function() {
+ if ( window.markdown ) {
+
+ // Unlike the other extras, Markdown.js doesn't by default do anything in
+ // particular. We do it ourselves here.
+ // In addition, we use "-----" as a delimiter for new slide.
+
+ // Query all .markdown elements and translate to HTML
+ var markdownDivs = document.querySelectorAll( ".markdown" );
+ for ( var idx = 0; idx < markdownDivs.length; idx++ ) {
+ var element = markdownDivs[ idx ];
+
+ var slides = element.textContent.split( /^-----$/m );
+ var i = slides.length - 1;
+ element.innerHTML = markdown.toHTML( slides[ i ] );
+
+ // If there's an id, unset it for last, and all other, elements,
+ // and then set it for the first.
+ var id = null;
+ if ( element.id ) {
+ id = element.id;
+ element.id = "";
+ }
+ i--;
+ while ( i >= 0 ) {
+ var newElement = element.cloneNode( false );
+ newElement.innerHTML = markdown.toHTML( slides[ i ] );
+ element.parentNode.insertBefore( newElement, element );
+ element = newElement;
+ i--;
+ }
+ if ( id !== null ) {
+ element.id = id;
+ }
+ }
+ } // Markdown
+
+ if ( window.hljs ) {
+ hljs.initHighlightingOnLoad();
+ }
+
+ if ( window.mermaid ) {
+ mermaid.initialize( { startOnLoad:true } );
+ }
+ };
+
+ // Register the plugin to be called in pre-init phase
+ // Note: Markdown.js should run early/first, because it creates new div elements.
+ // So add this with a lower-than-default weight.
+ impress.addPreInitPlugin( preInit, 1 );
+
+} )( document, window );
+
+
+/**
+ * Form support
+ *
+ * Functionality to better support use of input, textarea, button... elements in a presentation.
+ *
+ * This plugin does two things:
+ *
+ * Set stopPropagation on any element that might take text input. This allows users to type, for
+ * example, the letter 'P' into a form field, without causing the presenter console to spring up.
+ *
+ * On impress:stepleave, de-focus any potentially active
+ * element. This is to prevent the focus from being left in a form element that is no longer visible
+ * in the window, and user therefore typing garbage into the form.
+ *
+ * TODO: Currently it is not possible to use TAB to navigate between form elements. Impress.js, and
+ * in particular the navigation plugin, unfortunately must fully take control of the tab key,
+ * otherwise a user could cause the browser to scroll to a link or button that's not on the current
+ * step. However, it could be possible to allow tab navigation between form elements, as long as
+ * they are on the active step. This is a topic for further study.
+ *
+ * Copyright 2016 Henrik Ingo
+ * MIT License
+ */
+/* global document */
+( function( document ) {
+ "use strict";
+ var root;
+ var api;
+
+ document.addEventListener( "impress:init", function( event ) {
+ root = event.target;
+ api = event.detail.api;
+ var gc = api.lib.gc;
+
+ var selectors = [ "input", "textarea", "select", "[contenteditable=true]" ];
+ for ( var selector of selectors ) {
+ var elements = document.querySelectorAll( selector );
+ if ( !elements ) {
+ continue;
+ }
+
+ for ( var i = 0; i < elements.length; i++ ) {
+ var e = elements[ i ];
+ gc.addEventListener( e, "keydown", function( event ) {
+ event.stopPropagation();
+ } );
+ gc.addEventListener( e, "keyup", function( event ) {
+ event.stopPropagation();
+ } );
+ }
+ }
+ }, false );
+
+ document.addEventListener( "impress:stepleave", function() {
+ document.activeElement.blur();
+ }, false );
+
+} )( document );
+
+
+/**
+ * Fullscreen plugin
+ *
+ * Press F5 to enter fullscreen and ESC to exit fullscreen mode.
+ *
+ * Copyright 2019 @giflw
+ * Released under the MIT license.
+ */
+/* global document */
+
+( function( document ) {
+ "use strict";
+
+ function enterFullscreen() {
+ var elem = document.documentElement;
+ if ( !document.fullscreenElement ) {
+ elem.requestFullscreen();
+ }
+ }
+
+ function exitFullscreen() {
+ if ( document.fullscreenElement ) {
+ document.exitFullscreen();
+ }
+ }
+
+ // Wait for impress.js to be initialized
+ document.addEventListener( "impress:init", function( event ) {
+ var api = event.detail.api;
+ var root = event.target;
+ var gc = api.lib.gc;
+ var util = api.lib.util;
+
+ gc.addEventListener( document, "keydown", function( event ) {
+
+ // 116 (F5) is sent by presentation remote controllers
+ if ( event.code === "F5" ) {
+ event.preventDefault();
+ enterFullscreen();
+ util.triggerEvent( root.querySelector( ".active" ), "impress:steprefresh" );
+ }
+
+ // 27 (Escape) is sent by presentation remote controllers
+ if ( event.key === "Escape" || event.key === "F5" ) {
+ event.preventDefault();
+ exitFullscreen();
+ util.triggerEvent( root.querySelector( ".active" ), "impress:steprefresh" );
+ }
+ }, false );
+
+ util.triggerEvent( document, "impress:help:add",
+ { command: "F5 / ESC", text: "Fullscreen: Enter / Exit", row: 200 } );
+
+ }, false );
+
+} )( document );
+
+
+/**
+ * Goto Plugin
+ *
+ * The goto plugin is a pre-stepleave plugin. It is executed before impress:stepleave,
+ * and will alter the destination where to transition next.
+ *
+ * Example:
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values for a table
+ * of what strings to use for each key.
+ *
+ * Copyright 2016-2017 Henrik Ingo (@henrikingo)
+ * Released under the MIT license.
+ */
+/* global window, document, impress */
+
+( function( document, window ) {
+ "use strict";
+ var lib;
+
+ document.addEventListener( "impress:init", function( event ) {
+ lib = event.detail.api.lib;
+ }, false );
+
+ var isNumber = function( numeric ) {
+ return !isNaN( numeric );
+ };
+
+ var goto = function( event ) {
+ if ( ( !event ) || ( !event.target ) ) {
+ return;
+ }
+
+ var data = event.target.dataset;
+ var steps = document.querySelectorAll( ".step" );
+
+ // Data-goto-key-list="" & data-goto-next-list="" //////////////////////////////////////////
+ if ( data.gotoKeyList !== undefined &&
+ data.gotoNextList !== undefined &&
+ event.origEvent !== undefined &&
+ event.origEvent.key !== undefined ) {
+ var keylist = data.gotoKeyList.split( " " );
+ var nextlist = data.gotoNextList.split( " " );
+
+ if ( keylist.length !== nextlist.length ) {
+ window.console.log(
+ "impress goto plugin: data-goto-key-list and data-goto-next-list don't match:"
+ );
+ window.console.log( keylist );
+ window.console.log( nextlist );
+
+ // Don't return, allow the other categories to work despite this error
+ } else {
+ var index = keylist.indexOf( event.origEvent.key );
+ if ( index >= 0 ) {
+ var next = nextlist[ index ];
+ if ( isNumber( next ) ) {
+ event.detail.next = steps[ next ];
+
+ // If the new next element has its own transitionDuration, we're responsible
+ // for setting that on the event as well
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration,
+ event.detail.transitionDuration
+ );
+ return;
+ } else {
+ var newTarget = document.getElementById( next );
+ if ( newTarget && newTarget.classList.contains( "step" ) ) {
+ event.detail.next = newTarget;
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration,
+ event.detail.transitionDuration
+ );
+ return;
+ } else {
+ window.console.log( "impress goto plugin: " + next +
+ " is not a step in this impress presentation." );
+ }
+ }
+ }
+ }
+ }
+
+ // Data-goto-next="" & data-goto-prev="" ///////////////////////////////////////////////////
+
+ // Handle event.target data-goto-next attribute
+ if ( isNumber( data.gotoNext ) && event.detail.reason === "next" ) {
+ event.detail.next = steps[ data.gotoNext ];
+
+ // If the new next element has its own transitionDuration, we're responsible for setting
+ // that on the event as well
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration, event.detail.transitionDuration
+ );
+ return;
+ }
+ if ( data.gotoNext && event.detail.reason === "next" ) {
+ var newTarget = document.getElementById( data.gotoNext ); // jshint ignore:line
+ if ( newTarget && newTarget.classList.contains( "step" ) ) {
+ event.detail.next = newTarget;
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration,
+ event.detail.transitionDuration
+ );
+ return;
+ } else {
+ window.console.log( "impress goto plugin: " + data.gotoNext +
+ " is not a step in this impress presentation." );
+ }
+ }
+
+ // Handle event.target data-goto-prev attribute
+ if ( isNumber( data.gotoPrev ) && event.detail.reason === "prev" ) {
+ event.detail.next = steps[ data.gotoPrev ];
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration, event.detail.transitionDuration
+ );
+ return;
+ }
+ if ( data.gotoPrev && event.detail.reason === "prev" ) {
+ var newTarget = document.getElementById( data.gotoPrev ); // jshint ignore:line
+ if ( newTarget && newTarget.classList.contains( "step" ) ) {
+ event.detail.next = newTarget;
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration, event.detail.transitionDuration
+ );
+ return;
+ } else {
+ window.console.log( "impress goto plugin: " + data.gotoPrev +
+ " is not a step in this impress presentation." );
+ }
+ }
+
+ // Data-goto="" ///////////////////////////////////////////////////////////////////////////
+
+ // Handle event.target data-goto attribute
+ if ( isNumber( data.goto ) ) {
+ event.detail.next = steps[ data.goto ];
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration, event.detail.transitionDuration
+ );
+ return;
+ }
+ if ( data.goto ) {
+ var newTarget = document.getElementById( data.goto ); // jshint ignore:line
+ if ( newTarget && newTarget.classList.contains( "step" ) ) {
+ event.detail.next = newTarget;
+ event.detail.transitionDuration = lib.util.toNumber(
+ event.detail.next.dataset.transitionDuration, event.detail.transitionDuration
+ );
+ return;
+ } else {
+ window.console.log( "impress goto plugin: " + data.goto +
+ " is not a step in this impress presentation." );
+ }
+ }
+ };
+
+ // Register the plugin to be called in pre-stepleave phase
+ impress.addPreStepLeavePlugin( goto );
+
+} )( document, window );
+
+
+/**
+ * Help popup plugin
+ *
+ * Example:
+ *
+ *
+ *
+ *
+ * For developers:
+ *
+ * Typical use for this plugin, is for plugins that support some keypress, to add a line
+ * to the help popup produced by this plugin. For example "P: Presenter console".
+ *
+ * Copyright 2016 Henrik Ingo (@henrikingo)
+ * Released under the MIT license.
+ */
+/* global window, document */
+
+( function( document, window ) {
+ "use strict";
+ var rows = [];
+ var timeoutHandle;
+
+ var triggerEvent = function( el, eventName, detail ) {
+ var event = document.createEvent( "CustomEvent" );
+ event.initCustomEvent( eventName, true, true, detail );
+ el.dispatchEvent( event );
+ };
+
+ var renderHelpDiv = function() {
+ var helpDiv = document.getElementById( "impress-help" );
+ if ( helpDiv ) {
+ var html = [];
+ for ( var row in rows ) {
+ for ( var arrayItem in row ) {
+ html.push( rows[ row ][ arrayItem ] );
+ }
+ }
+ if ( html ) {
+ helpDiv.innerHTML = "
\n" + html.join( "\n" ) + "
\n";
+ }
+ }
+ };
+
+ var toggleHelp = function() {
+ var helpDiv = document.getElementById( "impress-help" );
+ if ( !helpDiv ) {
+ return;
+ }
+
+ if ( helpDiv.style.display === "block" ) {
+ helpDiv.style.display = "none";
+ } else {
+ helpDiv.style.display = "block";
+ window.clearTimeout( timeoutHandle );
+ }
+ };
+
+ document.addEventListener( "keyup", function( event ) {
+
+ if ( event.keyCode === 72 || event.keyCode === 191 ) { // "h" || "?"
+ event.preventDefault();
+ toggleHelp();
+ }
+ }, false );
+
+ // API
+ // Other plugins can add help texts, typically if they support an action on a keypress.
+ /**
+ * Add a help text to the help popup.
+ *
+ * :param: e.detail.command Example: "H"
+ * :param: e.detail.text Example: "Show this help."
+ * :param: e.detail.row Row index from 0 to 9 where to place this help text. Example: 0
+ */
+ document.addEventListener( "impress:help:add", function( e ) {
+
+ // The idea is for the sender of the event to supply a unique row index, used for sorting.
+ // But just in case two plugins would ever use the same row index, we wrap each row into
+ // its own array. If there are more than one entry for the same index, they are shown in
+ // first come, first serve ordering.
+ var rowIndex = e.detail.row;
+ if ( typeof rows[ rowIndex ] !== "object" || !rows[ rowIndex ].isArray ) {
+ rows[ rowIndex ] = [];
+ }
+ rows[ e.detail.row ].push( "
" + e.detail.command + "
" +
+ e.detail.text + "
" );
+ renderHelpDiv();
+ } );
+
+ document.addEventListener( "impress:init", function( e ) {
+ renderHelpDiv();
+
+ // At start, show the help for 7 seconds.
+ var helpDiv = document.getElementById( "impress-help" );
+ if ( helpDiv ) {
+ helpDiv.style.display = "block";
+ timeoutHandle = window.setTimeout( function() {
+ var helpDiv = document.getElementById( "impress-help" );
+ helpDiv.style.display = "none";
+ }, 7000 );
+
+ // Regster callback to empty the help div on teardown
+ var api = e.detail.api;
+ api.lib.gc.pushCallback( function() {
+ window.clearTimeout( timeoutHandle );
+ helpDiv.style.display = "";
+ helpDiv.innerHTML = "";
+ rows = [];
+ } );
+ }
+
+ // Use our own API to register the help text for "h"
+ triggerEvent( document, "impress:help:add",
+ { command: "H", text: "Show this help", row: 0 } );
+ } );
+
+} )( document, window );
+
+
+/**
+ * Adds a presenter console to impress.js
+ *
+ * MIT Licensed, see license.txt.
+ *
+ * Copyright 2012, 2013, 2015 impress-console contributors (see README.txt)
+ *
+ * version: 1.3-dev
+ *
+ */
+
+// This file contains so much HTML, that we will just respectfully disagree about js
+/* jshint quotmark:single */
+/* global navigator, top, setInterval, clearInterval, document, window */
+
+( function( document, window ) {
+ 'use strict';
+
+ // TODO: Move this to src/lib/util.js
+ var triggerEvent = function( el, eventName, detail ) {
+ var event = document.createEvent( 'CustomEvent' );
+ event.initCustomEvent( eventName, true, true, detail );
+ el.dispatchEvent( event );
+ };
+
+ // Create Language object depending on browsers language setting
+ var lang;
+ switch ( navigator.language ) {
+ case 'de':
+ lang = {
+ 'noNotes': '
+ if ( root.dataset.consoleAutolaunch === 'true' ) {
+ open();
+ }
+ };
+
+ var init = function( cssConsole, cssIframe ) {
+ if ( ( cssConsole === undefined || cssConsole === cssFileOldDefault ) &&
+ ( cssIframe === undefined || cssIframe === cssFileIframeOldDefault ) ) {
+ window.console.log( 'impressConsole().init() is deprecated. ' +
+ 'impressConsole is now initialized automatically when you ' +
+ 'call impress().init().' );
+ }
+ _init( cssConsole, cssIframe );
+ };
+
+ // New API for impress.js plugins is based on using events
+ root.addEventListener( 'impress:console:open', function() {
+ open();
+ } );
+
+ /**
+ * Register a key code to an event handler
+ *
+ * :param: event.detail.keyCodes List of key codes
+ * :param: event.detail.handler A function registered as the event handler
+ * :param: event.detail.window The console window to register the keycode in
+ */
+ root.addEventListener( 'impress:console:registerKeyEvent', function( event ) {
+ registerKeyEvent( event.detail.keyCodes, event.detail.handler, event.detail.window );
+ } );
+
+ // Return the object
+ allConsoles[ rootId ] = { init: init, open: open, clockTick: clockTick,
+ registerKeyEvent: registerKeyEvent, _init: _init };
+ return allConsoles[ rootId ];
+
+ };
+
+ // This initializes impressConsole automatically when initializing impress itself
+ document.addEventListener( 'impress:init', function( event ) {
+
+ // Note: impressConsole wants the id string, not the DOM element directly
+ impressConsole( event.target.id )._init();
+
+ // Add 'P' to the help popup
+ triggerEvent( document, 'impress:help:add',
+ { command: 'P', text: 'Presenter console', row: 10 } );
+ } );
+
+ // Returns a string to be used inline as a css `;
+ };
+
+} )( document, window );
+
+/**
+ * Media Plugin
+ *
+ * This plugin will do the following things:
+ *
+ * - Add a special class when playing (body.impress-media-video-playing
+ * and body.impress-media-video-playing) and pausing media (body.impress-media-video-paused
+ * and body.impress-media-audio-paused) (removing them when ending).
+ * This can be useful for example for darkening the background or fading out other elements
+ * while a video is playing.
+ * Only media at the current step are taken into account. All classes are removed when leaving
+ * a step.
+ *
+ * - Introduce the following new data attributes:
+ *
+ * - data-media-autoplay="true": Autostart media when entering its step.
+ * - data-media-autostop="true": Stop media (= pause and reset to start), when leaving its
+ * step.
+ * - data-media-autopause="true": Pause media but keep current time when leaving its step.
+ *
+ * When these attributes are added to a step they are inherited by all media on this step.
+ * Of course this setting can be overwritten by adding different attributes to inidvidual
+ * media.
+ *
+ * The same rule applies when this attributes is added to the root element. Settings can be
+ * overwritten for individual steps and media.
+ *
+ * Examples:
+ * - data-media-autostart="true" data-media-autostop="true": start media on enter, stop on
+ * leave, restart from beginning when re-entering the step.
+ *
+ * - data-media-autostart="true" data-media-autopause="true": start media on enter, pause on
+ * leave, resume on re-enter
+ *
+ * - data-media-autostart="true" data-media-autostop="true" data-media-autopause="true": start
+ * media on enter, stop on leave (stop overwrites pause).
+ *
+ * - data-media-autostart="true" data-media-autopause="false": let media start automatically
+ * when entering a step and let it play when leaving the step.
+ *
+ * -
...
+ * All media is startet automatically on all steps except the one that has the
+ * data-media-autostart="false" attribute.
+ *
+ * - Pro tip: Use