asciidoctor-revealjs 4.1.0 → 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +11 -1350
  3. data/asciidoctor-revealjs.gemspec +0 -2
  4. data/examples/auto-animate-code.adoc +47 -0
  5. data/examples/auto-animate.adoc +351 -0
  6. data/examples/favicon.adoc +13 -0
  7. data/examples/mathjax-cdn.adoc +1 -1
  8. data/examples/mathjax.adoc +1 -1
  9. data/examples/revealjs-custom-theme.adoc +1 -1
  10. data/examples/revealjs-plugin-activation.adoc +3 -2
  11. data/examples/revealjs-plugins/chalkboard/README.md +94 -61
  12. data/examples/revealjs-plugins/chalkboard/img/boardmarker-black.png +0 -0
  13. data/examples/revealjs-plugins/chalkboard/img/boardmarker-blue.png +0 -0
  14. data/examples/revealjs-plugins/chalkboard/img/boardmarker-green.png +0 -0
  15. data/examples/revealjs-plugins/chalkboard/img/boardmarker-orange.png +0 -0
  16. data/examples/revealjs-plugins/chalkboard/img/boardmarker-purple.png +0 -0
  17. data/examples/revealjs-plugins/chalkboard/img/boardmarker-red.png +0 -0
  18. data/examples/revealjs-plugins/chalkboard/img/boardmarker-yellow.png +0 -0
  19. data/examples/revealjs-plugins/chalkboard/img/chalk-blue.png +0 -0
  20. data/examples/revealjs-plugins/chalkboard/img/chalk-green.png +0 -0
  21. data/examples/revealjs-plugins/chalkboard/img/chalk-orange.png +0 -0
  22. data/examples/revealjs-plugins/chalkboard/img/chalk-purple.png +0 -0
  23. data/examples/revealjs-plugins/chalkboard/img/chalk-red.png +0 -0
  24. data/examples/revealjs-plugins/chalkboard/img/{chalk.png → chalk-white.png} +0 -0
  25. data/examples/revealjs-plugins/chalkboard/img/chalk-yellow.png +0 -0
  26. data/examples/revealjs-plugins/chalkboard/plugin.js +1836 -0
  27. data/examples/revealjs-plugins/chalkboard/style.css +38 -0
  28. data/examples/revealjs-plugins/{reveal.js-menu → menu}/LICENSE +1 -1
  29. data/examples/revealjs-plugins/menu/README.md +368 -0
  30. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/LICENSE.txt +0 -0
  31. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/all.css +0 -0
  32. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/brands.css +0 -0
  33. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/fontawesome.css +0 -0
  34. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/regular.css +0 -0
  35. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/solid.css +0 -0
  36. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/svg-with-js.css +0 -0
  37. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/v4-shims.css +0 -0
  38. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/css/v4-shims.min.css +0 -0
  39. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-brands-400.eot +0 -0
  40. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-brands-400.svg +0 -0
  41. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-brands-400.ttf +0 -0
  42. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-brands-400.woff +0 -0
  43. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-brands-400.woff2 +0 -0
  44. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-regular-400.eot +0 -0
  45. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-regular-400.svg +0 -0
  46. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-regular-400.ttf +0 -0
  47. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-regular-400.woff +0 -0
  48. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-regular-400.woff2 +0 -0
  49. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-solid-900.eot +0 -0
  50. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-solid-900.svg +0 -0
  51. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-solid-900.ttf +0 -0
  52. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-solid-900.woff +0 -0
  53. data/examples/revealjs-plugins/{reveal.js-menu → menu}/font-awesome/webfonts/fa-solid-900.woff2 +0 -0
  54. data/examples/revealjs-plugins/{reveal.js-menu → menu}/menu.css +116 -115
  55. data/examples/revealjs-plugins/menu/menu.esm.js +1 -0
  56. data/examples/revealjs-plugins/menu/menu.js +1 -0
  57. data/examples/revealjs-plugins/menu/plugin.js +1252 -0
  58. data/examples/revealjs-plugins-docinfo-footer.html +20 -0
  59. data/examples/revealjs-plugins.adoc +2 -3
  60. data/examples/search-plugin.adoc +26 -0
  61. data/examples/source-rouge.adoc +1 -1
  62. data/examples/text-formatting.adoc +34 -0
  63. data/lib/asciidoctor-revealjs/converter.rb +1029 -966
  64. data/lib/asciidoctor-revealjs/highlightjs.rb +155 -14
  65. data/lib/asciidoctor-revealjs/version.rb +1 -1
  66. data/templates/asciidoctor-compatibility.css +26 -1
  67. data/templates/document.html.slim +28 -44
  68. data/templates/helpers.rb +83 -9
  69. data/templates/inline_quoted.html.slim +2 -2
  70. data/templates/listing.html.slim +2 -1
  71. data/templates/section.html.slim +7 -1
  72. data/templates/title_slide.html.slim +1 -2
  73. metadata +57 -69
  74. data/examples/revealjs-plugins/chalkboard/chalkboard.js +0 -1288
  75. data/examples/revealjs-plugins/chalkboard/img/boardmarker.png +0 -0
  76. data/examples/revealjs-plugins/reveal.js-menu/CONTRIBUTING.md +0 -9
  77. data/examples/revealjs-plugins/reveal.js-menu/README.md +0 -334
  78. data/examples/revealjs-plugins/reveal.js-menu/bower.json +0 -21
  79. data/examples/revealjs-plugins/reveal.js-menu/menu.js +0 -949
  80. data/examples/revealjs-plugins/reveal.js-menu/package.json +0 -22
  81. data/examples/revealjs-plugins-conf.js +0 -10
  82. data/examples/revealjs-plugins.js +0 -2
@@ -4,8 +4,9 @@
4
4
  // :header_footer:
5
5
  = Default Plugins Changes
6
6
  Author
7
- :revealjs_plugins_pdf: enabled
8
- :revealjs_plugins_marked: disabled
7
+ :revealjs_plugins_zoom: disabled
8
+ // notes is enabled by default!
9
+ :revealjs_plugin_notes: enabled
9
10
 
10
11
  == Slide 1
11
12
 
@@ -3,54 +3,49 @@
3
3
  With this plugin you can add a chalkboard to reveal.js. The plugin provides two possibilities to include handwritten notes to your presentation:
4
4
 
5
5
  - you can make notes directly on the slides, e.g. to comment on certain aspects,
6
- - you can open a chalkboard on which you can make notes.
6
+ - you can open a chalkboard or whiteboard on which you can make notes.
7
7
 
8
- The main use case in mind when implementing the plugin is classroom usage in which you may want to explain some course content and quickly need to make some notes.
8
+ The main use case in mind when implementing the plugin is classroom usage in which you may want to explain some course content and quickly need to make some notes.
9
9
 
10
- The plugin records all drawings made so that they can be play backed using the ```autoSlide``` feature or the ```audio-slideshow``` plugin.
10
+ The plugin records all drawings made so that they can be play backed using the `autoSlide` feature or the `audio-slideshow` plugin.
11
11
 
12
12
  [Check out the live demo](https://rajgoel.github.io/reveal.js-demos/chalkboard-demo.html)
13
13
 
14
14
  The chalkboard effect is based on [Chalkboard](https://github.com/mmoustafa/Chalkboard) by Mohamed Moustafa.
15
15
 
16
+ Multi color support added by Kurt Rinnert [GitHub](https://github.com/rinnert).
17
+
16
18
  ## Installation
17
19
 
18
- Copy the file ```chalkboard.js``` and the ```img``` directory into the plugin folder of your reveal.js presentation, i.e. ```plugin/chalkboard```.
20
+ Copy the file `plugin.js` and the `img` directory into the plugin folder of your reveal.js presentation, i.e. `plugin/chalkboard` and load the plugin as shown below.
19
21
 
20
- Add the plugins to the dependencies in your presentation as shown below.
22
+ ```html
23
+ <link rel="stylesheet" href="plugin/chalkboard/style.css">
24
+ <script src="plugin/chalkboard/plugin.js"></script>
21
25
 
22
- ```javascript
23
- Reveal.initialize({
24
- // ...
25
- chalkboard: {
26
- // optionally load pre-recorded chalkboard drawing from file
27
- src: "chalkboard.json",
28
- },
29
- dependencies: [
30
- // ...
31
- { src: 'plugin/chalkboard/chalkboard.js' },
32
- // ...
33
- ],
34
- keyboard: {
35
- 67: function() { RevealChalkboard.toggleNotesCanvas() }, // toggle notes canvas when 'c' is pressed
36
- 66: function() { RevealChalkboard.toggleChalkboard() }, // toggle chalkboard when 'b' is pressed
37
- 46: function() { RevealChalkboard.clear() }, // clear chalkboard when 'DEL' is pressed
38
- 8: function() { RevealChalkboard.reset() }, // reset chalkboard data on current slide when 'BACKSPACE' is pressed
39
- 68: function() { RevealChalkboard.download() }, // downlad recorded chalkboard drawing when 'd' is pressed
40
- },
41
- // ...
42
-
43
- });
26
+ <script>
27
+ Reveal.initialize({
28
+ // ...
29
+ plugins: [ RevealChalkboard ],
30
+ // ...
31
+ });
32
+ </script>
44
33
  ```
45
- In order to include buttons for opening and closing the notes canvas or the chalkboard you should make sure that ```font-awesome``` is available. The easiest way is to include
34
+
35
+ In order to include buttons for opening and closing the notes canvas or the chalkboard you should make sure that `font-awesome` is available. The easiest way is to include
36
+ ```html
37
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.3/css/all.css">
46
38
  ```
47
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
39
+ to the `head` section of you HTML-file.
40
+
41
+ Alternatively, if you're using this package via NPM, you can use the built-in font-awesome distribution like so:
42
+ ```html
43
+ <link rel="stylesheet" href="node_modules/reveal.js-plugins/menu/font-awesome/css/all.css">
48
44
  ```
49
- to the ```head``` section of you HTML-file.
50
45
 
51
46
  ## Usage
52
47
 
53
- ### Enable & disable
48
+ ### Enable & disable
54
49
 
55
50
  With above configuration the notes canvas is opened and closed when pressing 'c' and the chalkboard is opened and closed when pressing 'b'.
56
51
 
@@ -63,62 +58,100 @@ With above configuration the notes canvas is opened and closed when pressing 'c'
63
58
  - Touch and hold for half a second, then move to wipe away previous drawings
64
59
 
65
60
  ### Keyboard
66
- - Click the 'DEL' key to clear the chalkboard </li>
67
- - Click the 'd' key to download chalkboard drawings</li>
68
- - Click the 'BACKSPACE' key to delete all chalkboard drawings on the current slide</li>
61
+ - Press the 'DEL' key to clear the chalkboard
62
+ - Press the 'd' key to download chalkboard drawings
63
+ - Press the 'BACKSPACE' key to delete all chalkboard drawings on the current slide
64
+ - Press the 'x' key to cycle colors forward
65
+ - Press the 'y' key to cycle colors backward
69
66
 
70
67
  ## Playback
71
68
 
72
- If the ```autoSlide``` feature is set or if the ```audio-slideshow``` plugin is used, pre-recorded chalkboard drawings can be played. The slideshow plays back the user interaction with the chalkboard in the same way as it was conducted when recording the data.
69
+ If the `autoSlide` feature is set or if the `audio-slideshow` plugin is used, pre-recorded chalkboard drawings can be played. The slideshow plays back the user interaction with the chalkboard in the same way as it was conducted when recording the data.
70
+
71
+ ## Broadcasting
72
+
73
+ The plugin supports broadcasting of drawings via the [`seminar`](https://github.com/rajgoel/reveal.js-plugins/tree/master/seminar) plugin. Alternatively, the [`multiplex`](https://github.com/reveal/multiplex) plugin can be used to share drawings (in this case, the `messageType` parameter (see Configuration) must be set to `'send'`).
74
+
73
75
 
74
76
  ## PDF-Export
75
77
 
76
- If the slideshow is opened in [print mode](https://github.com/hakimel/reveal.js/#pdf-export) the pre-recorded chalkboard drawings (which must be provided in a file, see ```src``` option) are included in the PDF-file. Each drawing on the chalkboard is added after the slide that was shown when opening the chalkboard. Drawings are also included if they had been cleared (using the 'DEL' key). Drawings on the notes canvas are not included in the PDF-file.
78
+ If the slideshow is opened in [print mode](https://revealjs.com/pdf-export/), the chalkboard drawings in the session storage (see `storage` option - print version must be opened in the same tab or window as the original slideshow) or provided in a file (see `src` option) are included in the PDF-file. Each drawing on the chalkboard is added after the slide that was shown when opening the chalkboard. Drawings on the notes canvas are not included in the PDF-file.
77
79
 
78
80
 
79
81
  ## Configuration
80
82
 
81
83
  The plugin has several configuration options:
82
84
 
83
- - ```src```: Optional filename for pre-recorded drawings.
84
- - ```readOnly```: Configuation option allowing to prevent changes to existing drawings. If set to ```true``` no changes can be made, if set to false ```false``` changes can be made, if unset or set to ```undefined``` no changes to the drawings can be made after returning to a slide or fragment for which drawings had been recorded before. In any case the recorded drawings for a slide or fragment can be cleared by pressing the 'DEL' key (i.e. by using the ```RevealChalkboard.clear()``` function).
85
- - ```toggleNotesButton```: If set to ```true``` a button for opening and closing the notes canvas is shown. Alternatively, the css position attributes can be provided if the default position is not appropriate.
86
- - ```toggleChalkboardButton```: If set to ```true``` a button for opening and closing the chalkboard is shown. Alternatively, the css position attributes can be provided if the default position is not appropriate.
87
- - ```transition```: Gives the duration (in milliseconds) of the transition for a slide change, so that the notes canvas is drawn after the transition is completed.
88
- - ```theme```: Can be set to either ```"chalkboard"``` or ```"whiteboard"```.
85
+ - `boardmarkerWidth`: an integer, the drawing width of the boardmarker; larger values draw thicker lines.
86
+ - `chalkWidth`: an integer, the drawing width of the chalk; larger values draw thicker lines.
87
+ - `chalkEffect`: a float in the range `[0.0, 1.0]`, the intesity of the chalk effect on the chalk board. Full effect (default) `1.0`, no effect `0.0`.
88
+ - `storage`: Optional variable name for session storage of drawings.
89
+ - `src`: Optional filename for pre-recorded drawings.
90
+ - `readOnly`: Configuation option allowing to prevent changes to existing drawings. If set to `true`, no changes can be made. However, recorded drawings for a slide or fragment can be cleared by pressing the 'DEL' key (i.e. by using the `RevealChalkboard.clear()` function).
91
+ - `toggleNotesButton`: If set to `true`, a button for opening and closing the notes canvas is shown. Alternatively, the css position attributes can be provided if the default position is not appropriate.
92
+ - `toggleChalkboardButton`: If set to `true`, a button for opening and closing the chalkboard is shown. Alternatively, the css position attributes can be provided if the default position is not appropriate.
93
+ - `colorButtons`: If set to `true`, the notes canvas and chalkboard will show a palette with buttons allowing to change the color. Alternatively, a numeric value can be assigned to the parameter limiting the number of colors buttons shown.
94
+ - `boardHandle`: If set to `true`, navigation buttons are shown, allowing to use multiple chalkboards on the same slide.
95
+ - `transition`: Gives the duration (in milliseconds) of the transition for a slide change, so that the notes canvas is drawn after the transition is completed.
96
+ - `theme`: Can be set to either `"chalkboard"` or `"whiteboard"`.
89
97
 
90
98
  The following configuration options allow to change the appearance of the notes canvas and the chalkboard. All of these options require two values, the first gives the value for the notes canvas, the second for the chalkboard.
91
99
 
92
- - ```color```: The first value gives the pen color, the second value gives the color of the chalk.
93
- - ```background```: The first value expects a (semi-)transparent color which is used to provide visual feedback that the notes canvas is enabled, the second value expects a filename to a background image for the chalkboard.
94
- - ```pen```: The first value expects a filename for an image of the pen used for the notes canvas, the second value expects a filename for an image of the pen used for the chalkboard.
100
+ - `background`: The first value expects a (semi-)transparent color which is used to provide visual feedback that the notes canvas is enabled, the second value expects a filename to a background image for the chalkboard.
101
+ - `grid`: By default whiteboard and chalkboard themes include a grid pattern on the background. This pattern can be modified by setting the color, the distance between lines, and the line width, e.g. `{ color: 'rgb(127,127,255,0.1)', distance: 40, width: 2}`. Alternatively, the grid can be removed by setting the value to `false`.
102
+ - `eraser`: An image path and radius for the eraser.
103
+ - `boardmarkers`: A list of boardmarkers with given color and cursor.
104
+ - `chalks`: A list of chalks with given color and cursor.
105
+ - `rememberColor`: Whether to remember the last selected color for the slide canvas or the board.
95
106
 
96
107
  All of the configurations are optional and the default values shown below are used if the options are not provided.
97
108
 
98
109
  ```javascript
99
110
  Reveal.initialize({
100
111
  // ...
101
- chalkboard: {
102
- src: null,
103
- readOnly: undefined,
104
- toggleChalkboardButton: { left: "30px", bottom: "30px", top: "auto", right: "auto" },
105
- toggleNotesButton: { left: "30px", bottom: "30px", top: "auto", right: "auto" },
106
- transition: 800,
107
- theme: "chalkboard",
108
- // configuration options for notes canvas and chalkboard
109
- color: [ 'rgba(0,0,255,1)', 'rgba(255,255,255,0.5)' ],
110
- background: [ 'rgba(127,127,127,.1)' , 'reveal.js-plugins/chalkboard/img/blackboard.png' ],
111
- pen: [ 'url(reveal.js-plugins/chalkboard/img/boardmarker.png), auto', 'url(reveal.js-plugins/chalkboard/img/chalk.png), auto' ],
112
- },
113
- // ...
112
+ chalkboard: {
113
+ boardmarkerWidth: 3,
114
+ chalkWidth: 7,
115
+ chalkEffect: 1.0,
116
+ storage: null,
117
+ src: null,
118
+ readOnly: false,
119
+ messageType: 'broadcast',
120
+ toggleChalkboardButton: { left: "30px", bottom: "30px", top: "auto", right: "auto" },
121
+ toggleNotesButton: { left: "30px", bottom: "30px", top: "auto", right: "auto" },
122
+ colorButtons: true,
123
+ boardHandle: true,
124
+ transition: 800,
125
+ theme: "chalkboard",
126
+ background: [ 'rgba(127,127,127,.1)' , path + 'img/blackboard.png' ],
127
+ grid: { color: 'rgb(50,50,10,0.5)', distance: 80, width: 2},
128
+ eraser: { src: path + 'img/sponge.png', radius: 20},
129
+ boardmarkers : [
130
+ { color: 'rgba(100,100,100,1)', cursor: 'url(' + path + 'img/boardmarker-black.png), auto'},
131
+ { color: 'rgba(30,144,255, 1)', cursor: 'url(' + path + 'img/boardmarker-blue.png), auto'},
132
+ { color: 'rgba(220,20,60,1)', cursor: 'url(' + path + 'img/boardmarker-red.png), auto'},
133
+ { color: 'rgba(50,205,50,1)', cursor: 'url(' + path + 'img/boardmarker-green.png), auto'},
134
+ { color: 'rgba(255,140,0,1)', cursor: 'url(' + path + 'img/boardmarker-orange.png), auto'},
135
+ { color: 'rgba(150,0,20150,1)', cursor: 'url(' + path + 'img/boardmarker-purple.png), auto'},
136
+ { color: 'rgba(255,220,0,1)', cursor: 'url(' + path + 'img/boardmarker-yellow.png), auto'}
137
+ ],
138
+ chalks: [
139
+ { color: 'rgba(255,255,255,0.5)', cursor: 'url(' + path + 'img/chalk-white.png), auto'},
140
+ { color: 'rgba(96, 154, 244, 0.5)', cursor: 'url(' + path + 'img/chalk-blue.png), auto'},
141
+ { color: 'rgba(237, 20, 28, 0.5)', cursor: 'url(' + path + 'img/chalk-red.png), auto'},
142
+ { color: 'rgba(20, 237, 28, 0.5)', cursor: 'url(' + path + 'img/chalk-green.png), auto'},
143
+ { color: 'rgba(220, 133, 41, 0.5)', cursor: 'url(' + path + 'img/chalk-orange.png), auto'},
144
+ { color: 'rgba(220,0,220,0.5)', cursor: 'url(' + path + 'img/chalk-purple.png), auto'},
145
+ { color: 'rgba(255,220,0,0.5)', cursor: 'url(' + path + 'img/chalk-yellow.png), auto'}
146
+ ]
147
+ },
148
+ // ...
114
149
 
115
150
  });
116
151
  ```
117
152
 
118
- **Note:** Customisation of pens has changed since version 0.5 of the plugin, it is now possible to use standard cursors, e.g. by setting ```pen: [ 'crosshair', 'pointer' ]```. Please update your parameters if migrating from an older version.
119
-
120
153
  ## License
121
154
 
122
155
  MIT licensed
123
156
 
124
- Copyright (C) 2016 Asvin Goel
157
+ Copyright (C) 2021 Asvin Goel
@@ -0,0 +1,1836 @@
1
+ /*****************************************************************
2
+ ** Author: Asvin Goel, goel@telematique.eu
3
+ **
4
+ ** A plugin for reveal.js adding a chalkboard.
5
+ **
6
+ ** Version: 1.5.0
7
+ **
8
+ ** License: MIT license (see LICENSE.md)
9
+ **
10
+ ** Credits:
11
+ ** Chalkboard effect by Mohamed Moustafa https://github.com/mmoustafa/Chalkboard
12
+ ** Multi color support by Kurt Rinnert https://github.com/rinnert
13
+ ** Compatibility with reveal.js v4 by Hakim El Hattab https://github.com/hakimel
14
+ ******************************************************************/
15
+
16
+ window.RevealChalkboard = window.RevealChalkboard || {
17
+ id: 'RevealChalkboard',
18
+ init: function(deck) {
19
+ initChalkboard(deck);
20
+ },
21
+ configure: function(config) { configure(config); },
22
+ toggleNotesCanvas: function() { toggleNotesCanvas(); },
23
+ toggleChalkboard: function() { toggleChalkboard(); },
24
+ colorIndex: function() { colorIndex(); },
25
+ colorNext: function() { colorNext(); },
26
+ colorPrev: function() {colorPrev(); },
27
+ clear: function() { clear(); },
28
+ reset: function() { reset(); },
29
+ resetAll: function() { resetAll(); },
30
+ updateStorage: function() { updateStorage(); },
31
+ getData: function() { return getData(); },
32
+ download: function() { download(); },
33
+ };
34
+
35
+ function scriptPath() {
36
+ // obtain plugin path from the script element
37
+ var src;
38
+ if (document.currentScript) {
39
+ src = document.currentScript.src;
40
+ } else {
41
+ var sel = document.querySelector('script[src$="/chalkboard/plugin.js"]')
42
+ if (sel) {
43
+ src = sel.src;
44
+ }
45
+ }
46
+ var path = (src === undefined) ? "" : src.slice(0, src.lastIndexOf("/") + 1);
47
+ //console.log("Path: " + path);
48
+ return path;
49
+ }
50
+ var path = scriptPath();
51
+
52
+ const initChalkboard = function(Reveal){
53
+ //console.warn(path);
54
+ /* Feature detection for passive event handling*/
55
+ var passiveSupported = false;
56
+
57
+ try {
58
+ window.addEventListener("test", null, Object.defineProperty({}, "passive", { get: function() { passiveSupported = true; } }));
59
+ } catch(err) {}
60
+
61
+
62
+ /*****************************************************************
63
+ ** Configuration
64
+ ******************************************************************/
65
+ var background, pen, draw, color;
66
+ var grid = false;
67
+ var boardmarkerWidth = 3;
68
+ var chalkWidth = 7;
69
+ var chalkEffect = 1.0;
70
+ var rememberColor = [true, false];
71
+ var eraser = { src: path + 'img/sponge.png', radius: 20};
72
+ var boardmarkers = [
73
+ { color: 'rgba(100,100,100,1)', cursor: 'url(' + path + 'img/boardmarker-black.png), auto'},
74
+ { color: 'rgba(30,144,255, 1)', cursor: 'url(' + path + 'img/boardmarker-blue.png), auto'},
75
+ { color: 'rgba(220,20,60,1)', cursor: 'url(' + path + 'img/boardmarker-red.png), auto'},
76
+ { color: 'rgba(50,205,50,1)', cursor: 'url(' + path + 'img/boardmarker-green.png), auto'},
77
+ { color: 'rgba(255,140,0,1)', cursor: 'url(' + path + 'img/boardmarker-orange.png), auto'},
78
+ { color: 'rgba(150,0,20150,1)', cursor: 'url(' + path + 'img/boardmarker-purple.png), auto'},
79
+ { color: 'rgba(255,220,0,1)', cursor: 'url(' + path + 'img/boardmarker-yellow.png), auto'}
80
+ ];
81
+ var chalks = [
82
+ { color: 'rgba(255,255,255,0.5)', cursor: 'url(' + path + 'img/chalk-white.png), auto'},
83
+ { color: 'rgba(96, 154, 244, 0.5)', cursor: 'url(' + path + 'img/chalk-blue.png), auto'},
84
+ { color: 'rgba(237, 20, 28, 0.5)', cursor: 'url(' + path + 'img/chalk-red.png), auto'},
85
+ { color: 'rgba(20, 237, 28, 0.5)', cursor: 'url(' + path + 'img/chalk-green.png), auto'},
86
+ { color: 'rgba(220, 133, 41, 0.5)', cursor: 'url(' + path + 'img/chalk-orange.png), auto'},
87
+ { color: 'rgba(220,0,220,0.5)', cursor: 'url(' + path + 'img/chalk-purple.png), auto'},
88
+ { color: 'rgba(255,220,0,0.5)', cursor: 'url(' + path + 'img/chalk-yellow.png), auto'}
89
+ ];
90
+ var keyBindings = {
91
+ toggleNotesCanvas: { keyCode: 67, key: 'C', description: 'Toggle notes canvas' },
92
+ toggleChalkboard: { keyCode: 66, key: 'B', description: 'Toggle chalkboard' },
93
+ clear: { keyCode: 171, key: '+', description: 'Clear drawings on slide' },
94
+ reset: { keyCode: 46, key: 'DEL', description: 'Reset drawings on slide' },
95
+ resetAll: { keyCode: 8, key: 'BACKSPACE', description: 'Reset all drawings' },
96
+ colorNext: { keyCode: 88, key: 'X', description: 'Next color' },
97
+ colorPrev: { keyCode: 89, key: 'Y', description: 'Previous color' },
98
+ download: { keyCode: 68, key: 'D', description: 'Download drawings' }
99
+ };
100
+
101
+
102
+ var theme = "chalkboard";
103
+ var color = [0, 0];
104
+ var toggleChalkboardButton = true;
105
+ var toggleNotesButton = true;
106
+ var colorButtons = true;
107
+ var boardHandle = true;
108
+ var transition = 800;
109
+
110
+ var readOnly = false;
111
+ var messageType = 'broadcast';
112
+
113
+ var config = configure( Reveal.getConfig().chalkboard || {} );
114
+ if ( config.keyBindings ) {
115
+ for (var key in config.keyBindings) {
116
+ keyBindings[key] = config.keyBindings[key];
117
+ };
118
+ }
119
+
120
+ function configure( config ) {
121
+
122
+ if ( config.boardmarkerWidth || config.penWidth ) boardmarkerWidth = config.boardmarkerWidth || config.penWidth;
123
+ if ( config.chalkWidth ) chalkWidth = config.chalkWidth;
124
+ if ( config.chalkEffect ) chalkEffect = config.chalkEffect;
125
+ if ( config.rememberColor ) rememberColor = config.rememberColor;
126
+ if ( config.eraser ) eraser = config.eraser;
127
+ if ( config.boardmarkers ) boardmarkers = config.boardmarkers;
128
+ if ( config.chalks) chalks = config.chalks;
129
+
130
+ if ( config.theme ) theme = config.theme;
131
+ switch ( theme ) {
132
+ case "whiteboard":
133
+ background = [ 'rgba(127,127,127,.1)' , path + 'img/whiteboard.png' ];
134
+ draw = [ drawWithBoardmarker , drawWithBoardmarker ];
135
+ pens = [ boardmarkers, boardmarkers ];
136
+ grid = { color: 'rgb(127,127,255,0.1)', distance: 40, width: 2};
137
+ break;
138
+ case "chalkboard":
139
+ default:
140
+ background = [ 'rgba(127,127,127,.1)' , path + 'img/blackboard.png' ];
141
+ draw = [ drawWithBoardmarker , drawWithChalk ];
142
+ pens = [ boardmarkers, chalks ];
143
+ grid = { color: 'rgb(50,50,10,0.5)', distance: 80, width: 2};
144
+ }
145
+
146
+ if ( config.background ) background = config.background;
147
+ if ( config.grid != undefined ) grid = config.grid;
148
+
149
+ if (config.toggleChalkboardButton != undefined) toggleChalkboardButton = config.toggleChalkboardButton;
150
+ if (config.toggleNotesButton != undefined) toggleNotesButton = config.toggleNotesButton;
151
+ if (config.colorButtons != undefined) colorButtons = config.colorButtons;
152
+ if (config.boardHandle != undefined) boardHandle = config.boardHandle;
153
+ if (config.transition) transition = config.transition;
154
+
155
+ if (config.readOnly != undefined) readOnly = config.readOnly;
156
+ if (config.messageType) messageType = config.messageType;
157
+
158
+ if ( drawingCanvas && ( config.theme || config.background || config.grid ) ) {
159
+ var canvas = document.getElementById( drawingCanvas[1].id );
160
+ canvas.style.background = 'url("' + background[1] + '") repeat';
161
+ clearCanvas( 1 );
162
+ drawGrid();
163
+ }
164
+
165
+ return config;
166
+ }
167
+ /*****************************************************************
168
+ ** Setup
169
+ ******************************************************************/
170
+
171
+ function whenReady( callback ) {
172
+ // wait for drawings to be loaded and markdown to be parsed
173
+ if ( document.querySelectorAll(".pdf-page").length && loaded !== null ) {
174
+ callback();
175
+ }
176
+ else {
177
+ console.log("Wait for pdf pages to be created and drawings to be loaded");
178
+ setTimeout( whenReady, 500, callback )
179
+ }
180
+ }
181
+
182
+
183
+ if ( toggleChalkboardButton ) {
184
+ //console.log("toggleChalkboardButton")
185
+ var button = document.createElement( 'div' );
186
+ button.className = "chalkboard-button";
187
+ button.id = "toggle-chalkboard";
188
+ button.style.visibility = "visible";
189
+ button.style.position = "absolute";
190
+ button.style.zIndex = 30;
191
+ button.style.fontSize = "24px";
192
+
193
+ button.style.left = toggleChalkboardButton.left || "30px";
194
+ button.style.bottom = toggleChalkboardButton.bottom || "30px";
195
+ button.style.top = toggleChalkboardButton.top || "auto";
196
+ button.style.right = toggleChalkboardButton.right || "auto";
197
+
198
+ button.innerHTML = '<a href="#" title="Toggle chalkboard ('+keyBindings.toggleChalkboard.key+')" onclick="RevealChalkboard.toggleChalkboard(); return false;"><i class="fa fa-pen-square"></i></a>'
199
+ document.querySelector(".reveal").appendChild( button );
200
+ }
201
+ if ( toggleNotesButton ) {
202
+ //console.log("toggleNotesButton")
203
+ var button = document.createElement( 'div' );
204
+ button.className = "chalkboard-button";
205
+ button.id = "toggle-notes";
206
+ button.style.position = "absolute";
207
+ button.style.zIndex = 30;
208
+ button.style.fontSize = "24px";
209
+
210
+ button.style.left = toggleNotesButton.left || "70px";
211
+ button.style.bottom = toggleNotesButton.bottom || "30px";
212
+ button.style.top = toggleNotesButton.top || "auto";
213
+ button.style.right = toggleNotesButton.right || "auto";
214
+
215
+ button.innerHTML = '<a href="#" title="Toggle slide annotation ('+keyBindings.toggleNotesCanvas.key+')" onclick="RevealChalkboard.toggleNotesCanvas(); return false;"><i class="fa fa-pen"></i></a>'
216
+ document.querySelector(".reveal").appendChild( button );
217
+ }
218
+ //alert("Buttons");
219
+
220
+ var drawingCanvas = [ {id: "notescanvas" }, {id: "chalkboard" } ];
221
+ setupDrawingCanvas(0);
222
+ setupDrawingCanvas(1);
223
+
224
+ var mode = 0; // 0: notes canvas, 1: chalkboard
225
+ var board = 0; // board index (only for chalkboard)
226
+
227
+ var mouseX = 0;
228
+ var mouseY = 0;
229
+ var xLast = null;
230
+ var yLast = null;
231
+
232
+ var slideStart = Date.now();
233
+ var slideIndices = { h:0, v:0 };
234
+ var event = null;
235
+ var timeouts = [ [], [] ];
236
+ var touchTimeout = null;
237
+ var slidechangeTimeout = null;
238
+ var playback = false;
239
+
240
+ function createPalette( colors, length ) {
241
+ if ( length === true || length > colors.length ) {
242
+ length = colors.length;
243
+ }
244
+ var palette = document.createElement( 'div' );
245
+ palette.classList.add('palette');
246
+ var list = document.createElement( 'ul' );
247
+ // color pickers
248
+ for (var i = 0; i < length; i++ ) {
249
+ var colorButton = document.createElement( 'li' );
250
+ colorButton.setAttribute("data-color",i);
251
+ colorButton.innerHTML = '<i class="fa fa-square"></i>';
252
+ colorButton.style.color = colors[i].color;
253
+ colorButton.addEventListener("click", function(e) {
254
+ colorIndex(e.target.parentElement.getAttribute("data-color"));
255
+ });
256
+ list.appendChild( colorButton );
257
+ }
258
+ palette.appendChild( list );
259
+ return palette;
260
+ };
261
+
262
+ function setupDrawingCanvas( id ) {
263
+ var container = document.createElement( 'div' );
264
+ container.id = drawingCanvas[id].id;
265
+ container.classList.add( 'overlay' );
266
+ container.setAttribute( 'data-prevent-swipe', '' );
267
+ container.oncontextmenu = function() { return false; }
268
+ container.style.cursor = pens[ id ][ color[id] ].cursor;
269
+
270
+ drawingCanvas[id].width = window.innerWidth;
271
+ drawingCanvas[id].height = window.innerHeight;
272
+ drawingCanvas[id].scale = 1;
273
+ drawingCanvas[id].xOffset = 0;
274
+ drawingCanvas[id].yOffset = 0;
275
+
276
+ if ( id == "0" ) {
277
+ container.style.background = 'rgba(0,0,0,0)';
278
+ container.style.zIndex = 24;
279
+ container.style.opacity = 1;
280
+ container.style.visibility = 'visible';
281
+ container.style.pointerEvents = "none";
282
+
283
+ var slides = document.querySelector(".slides");
284
+ var aspectRatio = Reveal.getConfig().width / Reveal.getConfig().height;
285
+ if ( drawingCanvas[id].width > drawingCanvas[id].height*aspectRatio ) {
286
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - drawingCanvas[id].height*aspectRatio) / 2;
287
+ }
288
+ else if ( drawingCanvas[id].height > drawingCanvas[id].width/aspectRatio ) {
289
+ drawingCanvas[id].yOffset = ( drawingCanvas[id].height - drawingCanvas[id].width/aspectRatio ) / 2;
290
+ }
291
+
292
+ if ( colorButtons ) {
293
+ var palette = createPalette( boardmarkers, colorButtons );
294
+ palette.style.visibility = 'hidden'; // only show palette in drawing mode
295
+ container.appendChild(palette);
296
+ }
297
+ }
298
+ else {
299
+ container.style.background = 'url("' + background[id] + '") repeat';
300
+ container.style.zIndex = 26;
301
+ container.style.opacity = 0;
302
+ container.style.visibility = 'hidden';
303
+
304
+ if ( colorButtons ) {
305
+ var palette = createPalette( chalks, colorButtons );
306
+ container.appendChild(palette);
307
+ }
308
+ if ( boardHandle ) {
309
+ var handle = document.createElement( 'div' );
310
+ handle.classList.add('boardhandle');
311
+ handle.innerHTML='<ul><li><a id="previousboard" href="#" title="Previous board"><i class="fas fa-chevron-up"></i></a></li><li><a id="nextboard" href="#" title="Next board"><i class="fas fa-chevron-down"></i></a></li></ul>';
312
+ handle.querySelector("#previousboard").addEventListener("click", function(e) {
313
+ e.preventDefault();
314
+ setBoard(board-1,true);
315
+ // broadcast
316
+ var message = new CustomEvent(messageType);
317
+ message.content = { sender: 'chalkboard-plugin', type: 'setboard', timestamp: Date.now() - slideStart, index: board, status: { mode, board, color } };
318
+ document.dispatchEvent( message );
319
+ });
320
+ handle.querySelector("#nextboard").addEventListener("click", function(e) {
321
+ e.preventDefault();
322
+ setBoard(board+1,true);
323
+ // broadcast
324
+ var message = new CustomEvent(messageType);
325
+ message.content = { sender: 'chalkboard-plugin', type: 'setboard', timestamp: Date.now() - slideStart, index: board, status: { mode, board, color } };
326
+ document.dispatchEvent( message );
327
+ });
328
+
329
+ container.appendChild(handle);
330
+ }
331
+ }
332
+
333
+
334
+ var sponge = document.createElement( 'img' );
335
+ sponge.src = eraser.src;
336
+ sponge.id = "sponge";
337
+ sponge.style.visibility = "hidden";
338
+ sponge.style.position = "absolute";
339
+ container.appendChild( sponge );
340
+ drawingCanvas[id].sponge = sponge;
341
+
342
+ var canvas = document.createElement( 'canvas' );
343
+ canvas.width = drawingCanvas[id].width;
344
+ canvas.height = drawingCanvas[id].height;
345
+ canvas.setAttribute( 'data-chalkboard', id );
346
+ canvas.style.cursor = pens[ id ][ color[id] ].cursor;
347
+ container.appendChild( canvas );
348
+ drawingCanvas[id].canvas = canvas;
349
+
350
+ drawingCanvas[id].context = canvas.getContext("2d");
351
+
352
+
353
+ document.querySelector( '.reveal' ).appendChild( container );
354
+ drawingCanvas[id].container = container;
355
+ }
356
+
357
+
358
+ /*****************************************************************
359
+ ** Storage
360
+ ******************************************************************/
361
+
362
+ var storage = [
363
+ { width: Reveal.getConfig().width, height: Reveal.getConfig().height, data: []},
364
+ { width: Reveal.getConfig().width, height: Reveal.getConfig().height, data: []}
365
+ ];
366
+
367
+ var loaded = null;
368
+
369
+ if ( config.storage ) {
370
+ // Get chalkboard drawings from session storage
371
+ loaded = initStorage( sessionStorage.getItem( config.storage ) );
372
+ }
373
+
374
+ if ( !loaded && config.src != null ) {
375
+ // Get chalkboard drawings from the given file
376
+ loadData( config.src );
377
+ }
378
+
379
+ /**
380
+ * Initialize storage.
381
+ */
382
+ function initStorage( json ) {
383
+ var success = false;
384
+ try {
385
+ var data = JSON.parse( json );
386
+ for (var id = 0; id < data.length; id++) {
387
+ if ( drawingCanvas[id].width != data[id].width || drawingCanvas[id].height != data[id].height ) {
388
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/data[id].width, drawingCanvas[id].height/data[id].height);
389
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - data[id].width * drawingCanvas[id].scale)/2;
390
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - data[id].height * drawingCanvas[id].scale)/2;
391
+ }
392
+ if ( config.readOnly ) {
393
+ drawingCanvas[id].container.style.cursor = 'default';
394
+ drawingCanvas[id].canvas.style.cursor = 'default';
395
+ }
396
+ }
397
+ success = true;
398
+ storage = data;
399
+ }
400
+ catch ( err ) {
401
+ console.warn( "Cannot initialise storage!" );
402
+ }
403
+ return success;
404
+ }
405
+
406
+
407
+ /**
408
+ * Load data.
409
+ */
410
+ function loadData( filename ) {
411
+ var xhr = new XMLHttpRequest();
412
+ xhr.onload = function() {
413
+ if (xhr.readyState === 4 && xhr.status != 404 ) {
414
+ loaded = initStorage(xhr.responseText);
415
+ console.log("Drawings loaded from file");
416
+ }
417
+ else {
418
+ config.readOnly = undefined;
419
+ readOnly = undefined;
420
+ console.warn( 'Failed to get file ' + filename +". ReadyState: " + xhr.readyState + ", Status: " + xhr.status);
421
+ loaded = false;
422
+ }
423
+ };
424
+
425
+ xhr.open( 'GET', filename, true );
426
+ try {
427
+ xhr.send();
428
+ }
429
+ catch ( error ) {
430
+ config.readOnly = undefined;
431
+ readOnly = undefined;
432
+ console.warn( 'Failed to get file ' + filename + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + error );
433
+ loaded = false;
434
+ }
435
+ }
436
+
437
+
438
+ function updateStorage() {
439
+ var json = JSON.stringify( storage )
440
+ if ( config.storage ) {
441
+ sessionStorage.setItem( config.storage, json )
442
+ }
443
+ return json;
444
+ }
445
+
446
+ /**
447
+ * Get data as json string.
448
+ */
449
+ function getData() {
450
+ // cleanup slide data without events
451
+ for (var id = 0; id < 2; id++) {
452
+ for (var i = storage[id].data.length-1; i >= 0; i--) {
453
+ if (storage[id].data[i].events.length == 0) {
454
+ storage[id].data.splice(i, 1);
455
+ }
456
+ }
457
+ }
458
+
459
+ return updateStorage();
460
+ }
461
+
462
+ /**
463
+ * Download data.
464
+ */
465
+ function downloadData() {
466
+ var a = document.createElement('a');
467
+ document.body.appendChild(a);
468
+ try {
469
+ a.download = "chalkboard.json";
470
+ var blob = new Blob( [ getData() ], { type: "application/json"} );
471
+ a.href = window.URL.createObjectURL( blob );
472
+ } catch( error ) {
473
+ a.innerHTML += " (" + error + ")";
474
+ }
475
+ a.click();
476
+ document.body.removeChild(a);
477
+ }
478
+
479
+ /**
480
+ * Returns data object for the slide with the given indices.
481
+ */
482
+ function getSlideData( indices, id ) {
483
+ if ( id == undefined ) id = mode;
484
+ if (!indices) indices = slideIndices;
485
+ var data;
486
+ for (var i = 0; i < storage[id].data.length; i++) {
487
+ if (storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v === indices.v && storage[id].data[i].slide.f === indices.f ) {
488
+ data = storage[id].data[i];
489
+ return data;
490
+ }
491
+ }
492
+ storage[id].data.push( { slide: indices, events: [], duration: 0 } );
493
+ data = storage[id].data[storage[id].data.length-1];
494
+ return data;
495
+ }
496
+
497
+ /**
498
+ * Returns maximum duration of slide playback for both modes
499
+ */
500
+ function getSlideDuration( indices ) {
501
+ if (!indices) indices = slideIndices;
502
+ var duration = 0;
503
+ for (var id = 0; id < 2; id++) {
504
+ for (var i = 0; i < storage[id].data.length; i++) {
505
+ if (storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v === indices.v && storage[id].data[i].slide.f === indices.f ) {
506
+ duration = Math.max( duration, storage[id].data[i].duration );
507
+ break;
508
+ }
509
+ }
510
+ }
511
+ //console.log( duration );
512
+ return duration;
513
+ }
514
+
515
+ /*****************************************************************
516
+ ** Print
517
+ ******************************************************************/
518
+ var printMode = ( /print-pdf/gi ).test( window.location.search );
519
+ //console.log("createPrintout" + printMode)
520
+
521
+ function createPrintout( ) {
522
+ //console.warn(Reveal.getTotalSlides(),Reveal.getSlidesElement());
523
+ if ( storage[1].data.length == 0 ) return;
524
+ console.log( 'Create printout(s) for ' + storage[1].data.length + " slides");
525
+ drawingCanvas[0].container.style.opacity = 0; // do not print notes canvas
526
+ drawingCanvas[0].container.style.visibility = 'hidden';
527
+
528
+ var patImg = new Image();
529
+ patImg.onload = function () {
530
+ var slides = getSlidesArray();
531
+ //console.log(slides);
532
+ for (var i = storage[1].data.length-1; i>=0; i--) {
533
+ console.log( 'Create printout for slide ' + storage[1].data[i].slide.h + "." + storage[1].data[i].slide.v );
534
+ var slideData = getSlideData( storage[1].data[i].slide, 1 );
535
+ var drawings = createDrawings( slideData, patImg );
536
+ var slide = slides[ storage[1].data[i].slide.h][ storage[1].data[i].slide.v ];
537
+ //console.log("Slide:", slide);
538
+ addDrawings( slide, drawings );
539
+
540
+ }
541
+ // Reveal.sync();
542
+ };
543
+ patImg.src = background[1];
544
+ }
545
+
546
+ function getSlidesArray() {
547
+ var horizontal = document.querySelectorAll('.slides > div.pdf-page > section, .slides > section');
548
+ var slides = [];
549
+ var slidenumber = undefined;
550
+ for ( var i=0; i < horizontal.length; i++) {
551
+ if ( horizontal[i].parentElement.classList.contains("pdf-page") ) {
552
+ // Horizontal slide
553
+ if ( horizontal[i].getAttribute("data-slide-number") != slidenumber ) {
554
+ // new slide
555
+ slides.push([]);
556
+ slides[slides.length-1].push(horizontal[i]);
557
+ slidenumber = horizontal[i].getAttribute("data-slide-number");
558
+ }
559
+ else {
560
+ // fragment of same slide
561
+ slides[slides.length-1][slides[slides.length-1].length-1] = horizontal[i];
562
+ }
563
+ }
564
+ else {
565
+ // Vertical slides
566
+ var vertical = horizontal[i].querySelectorAll('section');
567
+ slides.push([]);
568
+ var slidenumber = undefined;
569
+ for ( var j=0; j < vertical.length; j++) {
570
+ if ( vertical[j].getAttribute("data-slide-number") != slidenumber ) {
571
+ // new slide
572
+ slides[slides.length-1].push(vertical[j]);
573
+ slidenumber = vertical[j].getAttribute("data-slide-number");
574
+ }
575
+ else {
576
+ // fragment of same slide
577
+ slides[slides.length-1][slides[slides.length-1].length-1] = vertical[j];
578
+ }
579
+ }
580
+ }
581
+ }
582
+ //console.log("Slides:", slides);
583
+ return slides;
584
+ }
585
+
586
+ function cloneCanvas(oldCanvas) {
587
+ //create a new canvas
588
+ var newCanvas = document.createElement('canvas');
589
+ var context = newCanvas.getContext('2d');
590
+ //set dimensions
591
+ newCanvas.width = oldCanvas.width;
592
+ newCanvas.height = oldCanvas.height;
593
+ //apply the old canvas to the new one
594
+ context.drawImage(oldCanvas, 0, 0);
595
+ //return the new canvas
596
+ return newCanvas;
597
+ }
598
+
599
+ function getCanvas( template, container, board ) {
600
+ var idx = container.findIndex(element => element.board === board);
601
+ if ( idx === -1 ) {
602
+ var canvas = cloneCanvas(template);
603
+ if ( !container.length ) {
604
+ idx = 0;
605
+ container.push({ board, canvas });
606
+ }
607
+ else if ( board < container[0].board ) {
608
+ idx = 0;
609
+ container.unshift({ board, canvas });
610
+ }
611
+ else if ( board > container[container.length-1].board ) {
612
+ idx = container.length;
613
+ container.push({ board, canvas });
614
+ }
615
+ }
616
+
617
+ return container[idx].canvas;
618
+ }
619
+
620
+ function createDrawings( slideData, patImg ) {
621
+ var width = Reveal.getConfig().width;
622
+ var height = Reveal.getConfig().height;
623
+ var scale = 1;
624
+ var xOffset = 0;
625
+ var yOffset = 0;
626
+ if ( width != storage[1].width || height != storage[1].height ) {
627
+ scale = Math.min( width/storage[1].width, height/storage[1].height);
628
+ xOffset = (width - storage[1].width * scale)/2;
629
+ yOffset = (height - storage[1].height * scale)/2;
630
+ }
631
+ mode = 1;
632
+ board = 0;
633
+ console.log( 'Create printout(s) for slide ', slideData);
634
+
635
+ var drawings = [];
636
+ var template = document.createElement('canvas');
637
+ template.width = width;
638
+ template.height = height;
639
+
640
+ var imgCtx = template.getContext("2d");
641
+ imgCtx.fillStyle = imgCtx.createPattern( patImg ,'repeat');
642
+ imgCtx.rect(0,0,width,height);
643
+ imgCtx.fill();
644
+
645
+ for (var j = 0; j < slideData.events.length; j++) {
646
+ switch ( slideData.events[j].type ) {
647
+ case "draw":
648
+ for (var k = 1; k < slideData.events[j].curve.length; k++) {
649
+ draw[1]( getCanvas(template,drawings,board).getContext("2d"),
650
+ xOffset + slideData.events[j].curve[k-1].x*scale,
651
+ yOffset + slideData.events[j].curve[k-1].y*scale,
652
+ xOffset + slideData.events[j].curve[k].x*scale,
653
+ yOffset + slideData.events[j].curve[k].y*scale
654
+ );
655
+ }
656
+ break;
657
+ case "erase":
658
+ for (var k = 0; k < slideData.events[j].curve.length; k++) {
659
+ eraseWithSponge( getCanvas(template,drawings,board).getContext("2d"),
660
+ xOffset + slideData.events[j].curve[k].x*scale,
661
+ yOffset + slideData.events[j].curve[k].y*scale
662
+ );
663
+ }
664
+ break;
665
+ case "setcolor":
666
+ setColor(slideData.events[j].index);
667
+ break;
668
+ case "setboard":
669
+ // Todo: create new canvas for each new index
670
+ setBoard(slideData.events[j].index);
671
+ //board = 0;
672
+ break;
673
+ case "clear":
674
+ getCanvas(template,drawings,board).getContext("2d").clearRect(0,0,width,height);
675
+ getCanvas(template,drawings,board).getContext("2d").fill();
676
+ break;
677
+ default:
678
+ break;
679
+ }
680
+ }
681
+
682
+ drawings = drawings.sort((a, b) => a.board > b.board && 1 || -1);
683
+
684
+ mode = 0;
685
+
686
+ return drawings;
687
+ }
688
+
689
+ function addDrawings( slide, drawings ) {
690
+ var parent = slide.parentElement.parentElement;
691
+ var nextSlide = slide.parentElement.nextElementSibling;
692
+
693
+ for (var i = 0; i < drawings.length; i++) {
694
+ var newPDFPage = document.createElement( 'div' );
695
+ newPDFPage.classList.add('pdf-page');
696
+ newPDFPage.style.height = Reveal.getConfig().height;
697
+ // newPDFPage.innerHTML = '<h1>Drawing should be here!</h1>';
698
+ newPDFPage.append(drawings[i].canvas);
699
+ //console.log("Add drawing", newPDFPage);
700
+ if ( nextSlide != null ) {
701
+ parent.insertBefore( newPDFPage, nextSlide );
702
+ }
703
+ else {
704
+ parent.append( newPDFPage );
705
+ }
706
+ }
707
+ }
708
+
709
+ /*****************************************************************
710
+ ** Drawings
711
+ ******************************************************************/
712
+
713
+ function drawWithBoardmarker(context,fromX,fromY,toX,toY){
714
+ context.lineWidth = boardmarkerWidth;
715
+ context.lineCap = 'round';
716
+ context.strokeStyle = boardmarkers[color[mode]].color;
717
+ context.beginPath();
718
+ context.moveTo(fromX, fromY);
719
+ context.lineTo(toX, toY);
720
+ context.stroke();
721
+ }
722
+
723
+ function drawWithChalk(context,fromX,fromY,toX,toY) {
724
+ var brushDiameter = chalkWidth;
725
+ context.lineWidth = brushDiameter;
726
+ context.lineCap = 'round';
727
+ context.fillStyle = chalks[color[mode]].color; // 'rgba(255,255,255,0.5)';
728
+ context.strokeStyle = chalks[color[mode]].color;
729
+ /*var opacity = Math.min(0.8, Math.max(0,color[1].replace(/^.*,(.+)\)/,'$1') - 0.1)) + Math.random()*0.2;*/
730
+ var opacity = 1.0;
731
+ context.strokeStyle = context.strokeStyle.replace(/[\d\.]+\)$/g, opacity + ')');
732
+ context.beginPath();
733
+ context.moveTo(fromX, fromY);
734
+ context.lineTo(toX, toY);
735
+ context.stroke();
736
+ // Chalk Effect
737
+ var length = Math.round(Math.sqrt(Math.pow(toX-fromX,2)+Math.pow(toY-fromY,2))/(5/brushDiameter));
738
+ var xUnit = (toX-fromX)/length;
739
+ var yUnit = (toY-fromY)/length;
740
+ for(var i=0; i<length; i++ ){
741
+ if (chalkEffect > (Math.random() * 0.9)) {
742
+ var xCurrent = fromX+(i*xUnit);
743
+ var yCurrent = fromY+(i*yUnit);
744
+ var xRandom = xCurrent+(Math.random()-0.5)*brushDiameter*1.2;
745
+ var yRandom = yCurrent+(Math.random()-0.5)*brushDiameter*1.2;
746
+ context.clearRect( xRandom, yRandom, Math.random()*2+2, Math.random()+1);
747
+ }
748
+ }
749
+ }
750
+
751
+ function eraseWithSponge(context,x,y) {
752
+ context.save();
753
+ context.beginPath();
754
+ context.arc(x, y, eraser.radius, 0, 2 * Math.PI, false);
755
+ context.clip();
756
+ context.clearRect(x - eraser.radius - 1, y - eraser.radius - 1, eraser.radius * 2 + 2, eraser.radius * 2 + 2);
757
+ context.restore();
758
+ if ( mode == 1 && grid) {
759
+ redrawGrid(x,y,eraser.radius);
760
+ }
761
+ }
762
+
763
+
764
+
765
+ /**
766
+ * Show an overlay for the chalkboard.
767
+ */
768
+ function showChalkboard() {
769
+ //console.log("showChalkboard");
770
+ clearTimeout(touchTimeout);
771
+ touchTimeout = null;
772
+ drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
773
+ drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
774
+ drawingCanvas[1].container.style.opacity = 1;
775
+ drawingCanvas[1].container.style.visibility = 'visible';
776
+ mode = 1;
777
+ }
778
+
779
+
780
+ /**
781
+ * Closes open chalkboard.
782
+ */
783
+ function closeChalkboard() {
784
+ clearTimeout(touchTimeout);
785
+ touchTimeout = null;
786
+ drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
787
+ drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
788
+ drawingCanvas[1].container.style.opacity = 0;
789
+ drawingCanvas[1].container.style.visibility = 'hidden';
790
+ xLast = null;
791
+ yLast = null;
792
+ event = null;
793
+ mode = 0;
794
+ }
795
+
796
+ /**
797
+ * Clear current canvas.
798
+ */
799
+ function clearCanvas( id ) {
800
+ if ( id == 0 ) clearTimeout( slidechangeTimeout );
801
+ drawingCanvas[id].context.clearRect(0,0,drawingCanvas[id].width,drawingCanvas[id].height);
802
+ if ( id == 1 && grid ) drawGrid();
803
+ }
804
+
805
+ /**
806
+ * Draw grid on background
807
+ */
808
+ function drawGrid() {
809
+ var context = drawingCanvas[1].context;
810
+
811
+ drawingCanvas[1].scale = Math.min( drawingCanvas[1].width/storage[1].width, drawingCanvas[1].height/storage[1].height );
812
+ drawingCanvas[1].xOffset = (drawingCanvas[1].width - storage[1].width * drawingCanvas[1].scale)/2;
813
+ drawingCanvas[1].yOffset = (drawingCanvas[1].height - storage[1].height * drawingCanvas[1].scale)/2;
814
+
815
+ var scale = drawingCanvas[1].scale;
816
+ var xOffset = drawingCanvas[1].xOffset;
817
+ var yOffset = drawingCanvas[1].yOffset;
818
+
819
+ var distance = grid.distance*scale;
820
+
821
+ var fromX = drawingCanvas[1].width/2 - distance/2 - Math.floor( (drawingCanvas[1].width - distance)/2 / distance ) * distance;
822
+ for( var x=fromX; x < drawingCanvas[1].width; x+=distance ) {
823
+ context.beginPath();
824
+ context.lineWidth = grid.width*scale;
825
+ context.lineCap = 'round';
826
+ context.fillStyle = grid.color;
827
+ context.strokeStyle = grid.color;
828
+ context.moveTo(x, 0);
829
+ context.lineTo(x, drawingCanvas[1].height);
830
+ context.stroke();
831
+ }
832
+ var fromY = drawingCanvas[1].height/2 - distance/2 - Math.floor( (drawingCanvas[1].height - distance)/2 / distance ) * distance ;
833
+
834
+ for( var y=fromY; y < drawingCanvas[1].height; y+=distance ) {
835
+ context.beginPath();
836
+ context.lineWidth = grid.width*scale;
837
+ context.lineCap = 'round';
838
+ context.fillStyle = grid.color;
839
+ context.strokeStyle = grid.color;
840
+ context.moveTo(0, y);
841
+ context.lineTo(drawingCanvas[1].width, y);
842
+ context.stroke();
843
+ }
844
+ }
845
+
846
+ function redrawGrid(centerX,centerY,diameter) {
847
+ var context = drawingCanvas[1].context;
848
+
849
+ drawingCanvas[1].scale = Math.min( drawingCanvas[1].width/storage[1].width, drawingCanvas[1].height/storage[1].height );
850
+ drawingCanvas[1].xOffset = (drawingCanvas[1].width - storage[1].width * drawingCanvas[1].scale)/2;
851
+ drawingCanvas[1].yOffset = (drawingCanvas[1].height - storage[1].height * drawingCanvas[1].scale)/2;
852
+
853
+ var scale = drawingCanvas[1].scale;
854
+ var xOffset = drawingCanvas[1].xOffset;
855
+ var yOffset = drawingCanvas[1].yOffset;
856
+
857
+ var distance = grid.distance*scale;
858
+
859
+ var fromX = drawingCanvas[1].width/2 - distance/2 - Math.floor( (drawingCanvas[1].width - distance)/2 / distance ) * distance;
860
+
861
+ for( var x=fromX + distance* Math.ceil( (centerX-diameter-fromX) / distance); x <= fromX + distance* Math.floor( (centerX+diameter-fromX) / distance); x+=distance ) {
862
+ context.beginPath();
863
+ context.lineWidth = grid.width*scale;
864
+ context.lineCap = 'round';
865
+ context.fillStyle = grid.color;
866
+ context.strokeStyle = grid.color;
867
+ context.moveTo(x, centerY - Math.sqrt( diameter*diameter - (centerX-x)*(centerX-x) ));
868
+ context.lineTo(x, centerY + Math.sqrt( diameter*diameter - (centerX-x)*(centerX-x) ) );
869
+ context.stroke();
870
+ }
871
+ var fromY = drawingCanvas[1].height/2 - distance/2 - Math.floor( (drawingCanvas[1].height - distance)/2 / distance ) * distance ;
872
+ for( var y=fromY + distance* Math.ceil( (centerY-diameter-fromY) / distance); y <= fromY + distance* Math.floor( (centerY+diameter-fromY) / distance); y+=distance ) {
873
+ context.beginPath();
874
+ context.lineWidth = grid.width*scale;
875
+ context.lineCap = 'round';
876
+ context.fillStyle = grid.color;
877
+ context.strokeStyle = grid.color;
878
+ context.moveTo(centerX - Math.sqrt( diameter*diameter - (centerY-y)*(centerY-y) ), y );
879
+ context.lineTo(centerX + Math.sqrt( diameter*diameter - (centerY-y)*(centerY-y) ), y );
880
+ context.stroke();
881
+ }
882
+ }
883
+
884
+ /**
885
+ * Set the color
886
+ */
887
+ function setColor( index, record ) {
888
+ // protect against out of bounds (this could happen when
889
+ // replaying events recorded with different color settings).
890
+ if ( index >= boardmarkers[mode].length ) index = 0;
891
+ color[mode] = index;
892
+ drawingCanvas[mode].canvas.style.cursor = pens[mode][color[mode]].cursor;
893
+ if ( record ) {
894
+ recordEvent( { type: "setcolor", index: index, begin: Date.now() - slideStart } );
895
+ updateStorage();
896
+ }
897
+ }
898
+
899
+ /**
900
+ * Set the board
901
+ */
902
+ function setBoard( index, record ) {
903
+ //console.log("Set board",index);
904
+ board = index;
905
+ redrawChalkboard( board );
906
+
907
+ if ( record ) {
908
+ recordEvent( { type: "setboard", index: board, begin: Date.now() - slideStart } );
909
+ updateStorage();
910
+ }
911
+ }
912
+
913
+ function redrawChalkboard( board ) {
914
+ clearCanvas( 1 );
915
+ var slideData = getSlideData( slideIndices, 1 );
916
+ var index = 0;
917
+ var play = ( board == 0 );
918
+ while ( index < slideData.events.length && slideData.events[index].begin < Date.now() - slideStart) {
919
+ if ( slideData.events[index].type == "setboard" ) {
920
+ play = ( board == slideData.events[index].index );
921
+ }
922
+ else if ( play || slideData.events[index].type == "setcolor" ) {
923
+ playEvent( 1, slideData.events[index], Date.now() - slideStart );
924
+ }
925
+ index++;
926
+ }
927
+ }
928
+
929
+
930
+ /**
931
+ * Forward cycle color
932
+ */
933
+ function cycleColorNext() {
934
+ color[mode] = (color[mode] + 1) % pens[mode].length;
935
+ return color[mode];
936
+ }
937
+
938
+ /**
939
+ * Backward cycle color
940
+ */
941
+ function cycleColorPrev() {
942
+ color[mode] = (color[mode] + (pens[mode].length - 1)) % pens[mode].length;
943
+ return color[mode];
944
+ }
945
+
946
+ /*****************************************************************
947
+ ** Broadcast
948
+ ******************************************************************/
949
+
950
+ var eventQueue = [];
951
+
952
+ document.addEventListener( 'received', function ( message ) {
953
+ if ( message.content && message.content.sender == 'chalkboard-plugin' ) {
954
+ // add message to queue
955
+ eventQueue.push(message);
956
+ }
957
+ if ( eventQueue.length == 1 ) processQueue();
958
+ });
959
+
960
+ //console.log(JSON.stringify(message));
961
+ function processQueue() {
962
+ // take first message from queue
963
+ var message = eventQueue.shift();
964
+
965
+ // synchronize time with seminar host
966
+ slideStart = Date.now() - message.content.timestamp;
967
+ // set status
968
+ if ( mode < message.content.status.mode ) {
969
+ // open chalkboard
970
+ showChalkboard();
971
+ }
972
+ else if ( mode > message.content.status.mode ) {
973
+ // close chalkboard
974
+ closeChalkboard();
975
+ }
976
+ if ( board != message.content.status.board ) {
977
+ board = message.content.status.board;
978
+ redrawChalkboard( board );
979
+ };
980
+ color = message.content.status.color;
981
+
982
+ switch ( message.content.type ) {
983
+ case 'showChalkboard':
984
+ showChalkboard();
985
+ break;
986
+ case 'closeChalkboard':
987
+ closeChalkboard();
988
+ break;
989
+ case 'startDrawing':
990
+ startDrawing(message.content.x, message.content.y, message.content.erase);
991
+ break;
992
+ case 'startErasing':
993
+ if ( message.content ) {
994
+ message.content.type = "erase";
995
+ message.content.begin = Date.now() - slideStart;
996
+ eraseWithSponge(drawingCanvas[mode].context, message.content.x, message.content.y);
997
+ }
998
+ break;
999
+ case 'drawSegment':
1000
+ drawSegment(message.content.x, message.content.y, message.content.erase);
1001
+ break;
1002
+ case 'stopDrawing':
1003
+ stopDrawing();
1004
+ break;
1005
+ case 'clear':
1006
+ clear();
1007
+ break;
1008
+ case 'setcolor':
1009
+ setColor(message.content.index, true);
1010
+ break;
1011
+ case 'setboard':
1012
+ setBoard(message.content.index, true);
1013
+ break;
1014
+ case 'resetSlide':
1015
+ resetSlide(true);
1016
+ break;
1017
+ case 'init':
1018
+ storage = message.content.storage;
1019
+ for (var id = 0; id < 2; id++ ) {
1020
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height );
1021
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2;
1022
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2;
1023
+ }
1024
+ clearCanvas( 0 );
1025
+ clearCanvas( 1 );
1026
+ if ( !playback ) {
1027
+ slidechangeTimeout = setTimeout( startPlayback, transition, getSlideDuration(), 0 );
1028
+ }
1029
+ if ( mode == 1 && message.content.mode == 0) {
1030
+ setTimeout( closeChalkboard, transition + 50 );
1031
+ }
1032
+ if ( mode == 0 && message.content.mode == 1) {
1033
+ setTimeout( showChalkboard, transition + 50 );
1034
+ }
1035
+ mode = message.content.mode;
1036
+ break;
1037
+ default:
1038
+ break;
1039
+ }
1040
+
1041
+ // continue with next message if queued
1042
+ if ( eventQueue.length > 0 ) {
1043
+ processQueue();
1044
+ }
1045
+ else {
1046
+ updateStorage();
1047
+ }
1048
+ }
1049
+
1050
+ document.addEventListener( 'welcome', function( user ) {
1051
+ // broadcast storage
1052
+ var message = new CustomEvent(messageType);
1053
+ message.content = { sender: 'chalkboard-plugin', recipient: user.id, type: 'init', timestamp: Date.now() - slideStart, storage: storage, status: { mode, board, color } };
1054
+ document.dispatchEvent( message );
1055
+ });
1056
+
1057
+ /*****************************************************************
1058
+ ** Playback
1059
+ ******************************************************************/
1060
+
1061
+ document.addEventListener('seekplayback', function( event ) {
1062
+ //console.log('event seekplayback ' + event.timestamp);
1063
+ stopPlayback();
1064
+ if ( !playback || event.timestamp == 0) {
1065
+ // in other cases startplayback fires after seeked
1066
+ startPlayback( event.timestamp );
1067
+ }
1068
+ //console.log('seeked');
1069
+ });
1070
+
1071
+
1072
+ document.addEventListener('startplayback', function( event ) {
1073
+ //console.log('event startplayback ' + event.timestamp);
1074
+ stopPlayback();
1075
+ playback = true;
1076
+ startPlayback( event.timestamp );
1077
+ });
1078
+
1079
+ document.addEventListener('stopplayback', function( event ) {
1080
+ //console.log('event stopplayback ' + (Date.now() - slideStart) );
1081
+ playback = false;
1082
+ stopPlayback();
1083
+ });
1084
+
1085
+ document.addEventListener('startrecording', function( event ) {
1086
+ //console.log('event startrecording ' + event.timestamp);
1087
+ startRecording();
1088
+ });
1089
+
1090
+ function recordEvent( event ) {
1091
+ var slideData = getSlideData();
1092
+ var i = slideData.events.length;
1093
+ while ( i > 0 && event.begin < slideData.events[i-1].begin ) {
1094
+ i--;
1095
+ }
1096
+ slideData.events.splice( i, 0, event);
1097
+ slideData.duration = Math.max( slideData.duration, Date.now() - slideStart ) + 1;
1098
+ }
1099
+
1100
+ function startRecording() {
1101
+ resetSlide( true );
1102
+ slideStart = Date.now();
1103
+ }
1104
+
1105
+ function startPlayback( timestamp, finalMode ) {
1106
+ //console.log("playback " + timestamp );
1107
+ slideStart = Date.now() - timestamp;
1108
+ closeChalkboard();
1109
+ mode = 0;
1110
+ board = 0;
1111
+ for ( var id = 0; id < 2; id++ ) {
1112
+ clearCanvas( id );
1113
+ var slideData = getSlideData( slideIndices, id );
1114
+ //console.log( timestamp +" / " + JSON.stringify(slideData));
1115
+ var index = 0;
1116
+ while ( index < slideData.events.length && slideData.events[index].begin < (Date.now() - slideStart) ) {
1117
+ playEvent( id, slideData.events[index], timestamp );
1118
+ index++;
1119
+ }
1120
+
1121
+ while ( playback && index < slideData.events.length ) {
1122
+ timeouts[id].push( setTimeout( playEvent, slideData.events[index].begin - (Date.now() - slideStart), id, slideData.events[index], timestamp ) );
1123
+ index++;
1124
+ }
1125
+ }
1126
+ //console.log("Mode: " + finalMode + "/" + mode );
1127
+ if ( finalMode != undefined ) {
1128
+ mode = finalMode;
1129
+ }
1130
+ if( mode == 1 ) showChalkboard();
1131
+ //console.log("playback (ok)");
1132
+
1133
+ };
1134
+
1135
+ function stopPlayback() {
1136
+ //console.log("stopPlayback");
1137
+ //console.log("Timeouts: " + timeouts[0].length + "/"+ timeouts[1].length);
1138
+ for ( var id = 0; id < 2; id++ ) {
1139
+ for (var i = 0; i < timeouts[id].length; i++) {
1140
+ clearTimeout(timeouts[id][i]);
1141
+ }
1142
+ timeouts[id] = [];
1143
+ }
1144
+ };
1145
+
1146
+ function playEvent( id, event, timestamp ) {
1147
+ //console.log( timestamp +" / " + JSON.stringify(event));
1148
+ //console.log( id + ": " + timestamp +" / " + event.begin +" / " + event.type +" / " + mode );
1149
+ switch ( event.type ) {
1150
+ case "open":
1151
+ if ( timestamp <= event.begin ) {
1152
+ showChalkboard();
1153
+ }
1154
+ else {
1155
+ mode = 1;
1156
+ }
1157
+
1158
+ break;
1159
+ case "close":
1160
+ if ( timestamp < event.begin ) {
1161
+ closeChalkboard();
1162
+ }
1163
+ else {
1164
+ mode = 0;
1165
+ }
1166
+ break;
1167
+ case "clear":
1168
+ clearCanvas( id );
1169
+ break;
1170
+ case "setcolor":
1171
+ setColor(event.index);
1172
+ break;
1173
+ case "setboard":
1174
+ setBoard(event.index);
1175
+ break;
1176
+ case "draw":
1177
+ drawCurve( id, event, timestamp );
1178
+ break;
1179
+ case "erase":
1180
+ eraseCurve( id, event, timestamp );
1181
+ break;
1182
+
1183
+ }
1184
+ };
1185
+
1186
+ function drawCurve( id, event, timestamp ) {
1187
+ if ( event.curve.length > 1 ) {
1188
+ var ctx = drawingCanvas[id].context;
1189
+ var scale = drawingCanvas[id].scale;
1190
+ var xOffset = drawingCanvas[id].xOffset;
1191
+ var yOffset = drawingCanvas[id].yOffset;
1192
+
1193
+ var stepDuration = ( event.end - event.begin )/ ( event.curve.length - 1 );
1194
+ //console.log("---");
1195
+ for (var i = 1; i < event.curve.length; i++) {
1196
+ if (event.begin + i * stepDuration <= (Date.now() - slideStart)) {
1197
+ //console.log( "Draw " + timestamp +" / " + event.begin + " + " + i + " * " + stepDuration );
1198
+ draw[id](ctx, xOffset + event.curve[i-1].x*scale, yOffset + event.curve[i-1].y*scale, xOffset + event.curve[i].x*scale, yOffset + event.curve[i].y*scale);
1199
+ }
1200
+ else if ( playback ) {
1201
+ //console.log( "Cue " + timestamp +" / " + (Date.now() - slideStart) +" / " + event.begin + " + " + i + " * " + stepDuration + " = " + Math.max(0,event.begin + i * stepDuration - timestamp) );
1202
+ timeouts.push( setTimeout(
1203
+ draw[id], Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), ctx,
1204
+ xOffset + event.curve[i-1].x*scale,
1205
+ yOffset + event.curve[i-1].y*scale,
1206
+ xOffset + event.curve[i].x*scale,
1207
+ yOffset + event.curve[i].y*scale
1208
+ )
1209
+ );
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ };
1215
+
1216
+ function eraseCurve( id, event, timestamp ) {
1217
+ if ( event.curve.length > 1 ) {
1218
+ var ctx = drawingCanvas[id].context;
1219
+ var scale = drawingCanvas[id].scale;
1220
+ var xOffset = drawingCanvas[id].xOffset;
1221
+ var yOffset = drawingCanvas[id].yOffset;
1222
+
1223
+ var stepDuration = ( event.end - event.begin )/ event.curve.length;
1224
+ for (var i = 0; i < event.curve.length; i++) {
1225
+ if (event.begin + i * stepDuration <= (Date.now() - slideStart)) {
1226
+ eraseWithSponge(ctx, xOffset + event.curve[i].x*scale, yOffset + event.curve[i].y*scale);
1227
+ }
1228
+ else if ( playback ) {
1229
+ timeouts.push( setTimeout(
1230
+ eraseWithSponge, Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), ctx,
1231
+ xOffset + event.curve[i].x * scale,
1232
+ yOffset + event.curve[i].y * scale
1233
+ )
1234
+ );
1235
+ }
1236
+ }
1237
+ }
1238
+
1239
+ };
1240
+
1241
+
1242
+ function startDrawing( x, y, erase ) {
1243
+ var ctx = drawingCanvas[mode].context;
1244
+ var scale = drawingCanvas[mode].scale;
1245
+ var xOffset = drawingCanvas[mode].xOffset;
1246
+ var yOffset = drawingCanvas[mode].yOffset;
1247
+ xLast = x * scale + xOffset;
1248
+ yLast = y * scale + yOffset;
1249
+ if ( erase == true) {
1250
+ event = { type: "erase", begin: Date.now() - slideStart, end: null, curve: [{x: x, y: y}]};
1251
+ drawingCanvas[mode].canvas.style.cursor = 'url("' + eraser.src + '") ' + eraser.radius + ' ' + eraser.radius + ', auto';
1252
+ eraseWithSponge(ctx, x * scale + xOffset, y * scale + yOffset);
1253
+ }
1254
+ else {
1255
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: x, y: y}] };
1256
+ }
1257
+ }
1258
+
1259
+
1260
+ function showSponge(x,y) {
1261
+ if ( event ) {
1262
+ event.type = "erase";
1263
+ event.begin = Date.now() - slideStart;
1264
+ // show sponge image
1265
+ drawingCanvas[mode].sponge.style.left = (x - eraser.radius) +"px" ;
1266
+ drawingCanvas[mode].sponge.style.top = (y - eraser.radius) +"px" ;
1267
+ drawingCanvas[mode].sponge.style.visibility = "visible";
1268
+ eraseWithSponge(drawingCanvas[mode].context,x,y);
1269
+ // broadcast
1270
+ var message = new CustomEvent(messageType);
1271
+ message.content = { sender: 'chalkboard-plugin', type: 'startErasing', timestamp: Date.now() - slideStart, status: { mode, board, color }, x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale };
1272
+ document.dispatchEvent( message );
1273
+ }
1274
+ }
1275
+
1276
+ function drawSegment( x, y, erase ) {
1277
+ var ctx = drawingCanvas[mode].context;
1278
+ var scale = drawingCanvas[mode].scale;
1279
+ var xOffset = drawingCanvas[mode].xOffset;
1280
+ var yOffset = drawingCanvas[mode].yOffset;
1281
+ if ( !event ) {
1282
+ // safeguard if broadcast hickup
1283
+ startDrawing( x, y, erase );
1284
+ }
1285
+ event.curve.push({x: x, y: y});
1286
+ if(y * scale + yOffset < drawingCanvas[mode].height && x * scale + xOffset < drawingCanvas[mode].width) {
1287
+ if ( erase ) {
1288
+ eraseWithSponge(ctx, x * scale + xOffset, y * scale + yOffset);
1289
+ }
1290
+ else {
1291
+ draw[mode](ctx, xLast, yLast, x * scale + xOffset, y * scale + yOffset);
1292
+ }
1293
+ xLast = x * scale + xOffset;
1294
+ yLast = y * scale + yOffset;
1295
+ }
1296
+ }
1297
+
1298
+ function stopDrawing() {
1299
+ if ( event ) {
1300
+ event.end = Date.now() - slideStart;
1301
+ if ( event.type == "erase" || event.curve.length > 1 ) {
1302
+ // do not save a line with a single point only
1303
+ recordEvent( event );
1304
+ updateStorage();
1305
+ }
1306
+ event = null;
1307
+ }
1308
+ }
1309
+
1310
+
1311
+ /*****************************************************************
1312
+ ** User interface
1313
+ ******************************************************************/
1314
+
1315
+
1316
+ // TODO: check all touchevents
1317
+ document.addEventListener('touchstart', function(evt) {
1318
+ //console.log("Touch start");
1319
+ if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) {
1320
+ // var ctx = drawingCanvas[mode].context;
1321
+ var scale = drawingCanvas[mode].scale;
1322
+ var xOffset = drawingCanvas[mode].xOffset;
1323
+ var yOffset = drawingCanvas[mode].yOffset;
1324
+
1325
+ evt.preventDefault();
1326
+ var touch = evt.touches[0];
1327
+ mouseX = touch.pageX;
1328
+ mouseY = touch.pageY;
1329
+ startDrawing( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, false );
1330
+ // broadcast
1331
+ var message = new CustomEvent(messageType);
1332
+ message.content = { sender: 'chalkboard-plugin', type: 'startDrawing', timestamp: Date.now() - slideStart, status: { mode, board, color }, x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: false };
1333
+ document.dispatchEvent( message );
1334
+ /*
1335
+ xLast = mouseX;
1336
+ yLast = mouseY;
1337
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] };
1338
+ */
1339
+ touchTimeout = setTimeout( showSponge, 500, mouseX, mouseY );
1340
+ }
1341
+ }, passiveSupported ? {passive: false} : false);
1342
+
1343
+ document.addEventListener('touchmove', function(evt) {
1344
+ //console.log("Touch move");
1345
+ clearTimeout( touchTimeout );
1346
+ touchTimeout = null;
1347
+ if ( event ) {
1348
+ // var ctx = drawingCanvas[mode].context;
1349
+ var scale = drawingCanvas[mode].scale;
1350
+ var xOffset = drawingCanvas[mode].xOffset;
1351
+ var yOffset = drawingCanvas[mode].yOffset;
1352
+
1353
+ var touch = evt.touches[0];
1354
+ mouseX = touch.pageX;
1355
+ mouseY = touch.pageY;
1356
+ if (mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) {
1357
+ evt.preventDefault();
1358
+ // move sponge
1359
+ if ( event.type == "erase" ) {
1360
+ drawingCanvas[mode].sponge.style.left = (mouseX - eraser.radius) +"px" ;
1361
+ drawingCanvas[mode].sponge.style.top = (mouseY - eraser.radius) +"px" ;
1362
+ }
1363
+ }
1364
+
1365
+ drawSegment( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, ( event.type == "erase" ) );
1366
+ // broadcast
1367
+ var message = new CustomEvent(messageType);
1368
+ message.content = { sender: 'chalkboard-plugin', type: 'drawSegment', timestamp: Date.now() - slideStart, status: { mode, board, color }, x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: ( event.type == "erase" ) };
1369
+ document.dispatchEvent( message );
1370
+ /*
1371
+ if (mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) {
1372
+ evt.preventDefault();
1373
+ event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale});
1374
+ if ( event.type == "erase" ) {
1375
+ drawingCanvas[mode].sponge.style.left = (mouseX - eraser.radius) +"px" ;
1376
+ drawingCanvas[mode].sponge.style.top = (mouseY - eraser.radius) +"px" ;
1377
+ eraseWithSponge(ctx, mouseX, mouseY);
1378
+ }
1379
+ else {
1380
+ draw[mode](ctx, xLast, yLast, mouseX, mouseY);
1381
+ }
1382
+ xLast = mouseX;
1383
+ yLast = mouseY;
1384
+ }
1385
+ */
1386
+ }
1387
+ }, false);
1388
+
1389
+
1390
+ document.addEventListener('touchend', function(evt) {
1391
+ clearTimeout( touchTimeout );
1392
+ touchTimeout = null;
1393
+ // hide sponge image
1394
+ drawingCanvas[mode].sponge.style.visibility = "hidden";
1395
+ stopDrawing();
1396
+ // broadcast
1397
+ var message = new CustomEvent(messageType);
1398
+ message.content = { sender: 'chalkboard-plugin', timestamp: Date.now() - slideStart, type: 'stopDrawing', status: { mode, board, color } };
1399
+ document.dispatchEvent( message );
1400
+ /*
1401
+ if ( event ) {
1402
+ event.end = Date.now() - slideStart;
1403
+ if ( event.type == "erase" || event.curve.length > 1 ) {
1404
+ // do not save a line with a single point only
1405
+ recordEvent( event );
1406
+ }
1407
+ event = null;
1408
+ }
1409
+ */
1410
+ }, false);
1411
+
1412
+ document.addEventListener( 'mousedown', function( evt ) {
1413
+ //console.log("Mouse down");
1414
+ //console.log( "Read only: " + readOnly );
1415
+ if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) {
1416
+ //console.log( "mousedown: " + evt.button );
1417
+ // var ctx = drawingCanvas[mode].context;
1418
+ var scale = drawingCanvas[mode].scale;
1419
+ var xOffset = drawingCanvas[mode].xOffset;
1420
+ var yOffset = drawingCanvas[mode].yOffset;
1421
+
1422
+ mouseX = evt.pageX;
1423
+ mouseY = evt.pageY;
1424
+ startDrawing( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, ( evt.button == 2 || evt.button == 1) );
1425
+ // broadcast
1426
+ var message = new CustomEvent(messageType);
1427
+ message.content = { sender: 'chalkboard-plugin', type: 'startDrawing', timestamp: Date.now() - slideStart, status: { mode, board, color }, x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: ( evt.button == 2 || evt.button == 1) };
1428
+ document.dispatchEvent( message );
1429
+ /*
1430
+ xLast = mouseX;
1431
+ yLast = mouseY;
1432
+ if ( evt.button == 2) {
1433
+ event = { type: "erase", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}]};
1434
+ drawingCanvas[mode].canvas.style.cursor = 'url("' + path + 'img/sponge.png") ' + eraser.radius + ' ' + eraser.radius + ', auto';
1435
+ eraseWithSponge(ctx,mouseX,mouseY);
1436
+ }
1437
+ else {
1438
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] };
1439
+ }
1440
+ */
1441
+ }
1442
+ } );
1443
+
1444
+ document.addEventListener( 'mousemove', function( evt ) {
1445
+ //console.log("Mouse move");
1446
+ if ( event ) {
1447
+ // var ctx = drawingCanvas[mode].context;
1448
+ var scale = drawingCanvas[mode].scale;
1449
+ var xOffset = drawingCanvas[mode].xOffset;
1450
+ var yOffset = drawingCanvas[mode].yOffset;
1451
+
1452
+ mouseX = evt.pageX;
1453
+ mouseY = evt.pageY;
1454
+ drawSegment( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, ( event.type == "erase" ) );
1455
+ // broadcast
1456
+ var message = new CustomEvent(messageType);
1457
+ message.content = { sender: 'chalkboard-plugin', type: 'drawSegment', timestamp: Date.now() - slideStart, status: { mode, board, color }, x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: ( event.type == "erase" ) };
1458
+ document.dispatchEvent( message );
1459
+ /*
1460
+ event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale});
1461
+ if(mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) {
1462
+ if ( event.type == "erase" ) {
1463
+ eraseWithSponge(ctx,mouseX,mouseY);
1464
+ }
1465
+ else {
1466
+ draw[mode](ctx, xLast, yLast, mouseX,mouseY);
1467
+ }
1468
+ xLast = mouseX;
1469
+ yLast = mouseY;
1470
+ }
1471
+ */
1472
+ }
1473
+ } );
1474
+
1475
+
1476
+ document.addEventListener( 'mouseup', function( evt ) {
1477
+ drawingCanvas[mode].canvas.style.cursor = pens[mode][color[mode]].cursor;
1478
+ if ( event ) {
1479
+ stopDrawing();
1480
+ // broadcast
1481
+ var message = new CustomEvent(messageType);
1482
+ message.content = { sender: 'chalkboard-plugin', type: 'stopDrawing', timestamp: Date.now() - slideStart, status: { mode, board, color } };
1483
+ document.dispatchEvent( message );
1484
+ /* if(evt.button == 2){
1485
+ }
1486
+ event.end = Date.now() - slideStart;
1487
+ if ( event.type == "erase" || event.curve.length > 1 ) {
1488
+ // do not save a line with a single point only
1489
+ recordEvent( event );
1490
+ }
1491
+ event = null;
1492
+ */
1493
+ }
1494
+ } );
1495
+
1496
+
1497
+ window.addEventListener( "resize", function() {
1498
+ //console.log("resize");
1499
+ // Resize the canvas and draw everything again
1500
+ var timestamp = Date.now() - slideStart;
1501
+ if ( !playback ) {
1502
+ timestamp = getSlideDuration();
1503
+ }
1504
+
1505
+ //console.log( drawingCanvas[0].scale + "/" + drawingCanvas[0].xOffset + "/" +drawingCanvas[0].yOffset );
1506
+ for (var id = 0; id < 2; id++ ) {
1507
+ drawingCanvas[id].width = window.innerWidth;
1508
+ drawingCanvas[id].height = window.innerHeight;
1509
+ drawingCanvas[id].canvas.width = drawingCanvas[id].width;
1510
+ drawingCanvas[id].canvas.height = drawingCanvas[id].height;
1511
+ drawingCanvas[id].context.canvas.width = drawingCanvas[id].width;
1512
+ drawingCanvas[id].context.canvas.height = drawingCanvas[id].height;
1513
+
1514
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height );
1515
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2;
1516
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2;
1517
+ //console.log( drawingCanvas[id].scale + "/" + drawingCanvas[id].xOffset + "/" +drawingCanvas[id].yOffset );
1518
+ }
1519
+ //console.log( window.innerWidth + "/" + window.innerHeight);
1520
+ startPlayback( timestamp, mode, true );
1521
+
1522
+ } );
1523
+
1524
+ Reveal.addEventListener( 'ready', function( evt ) {
1525
+ //console.log('ready');
1526
+ if ( !printMode ) {
1527
+ slideStart = Date.now() - getSlideDuration();
1528
+ slideIndices = Reveal.getIndices();
1529
+ if ( !playback ) {
1530
+ startPlayback( getSlideDuration(), 0 );
1531
+ }
1532
+ if ( Reveal.isAutoSliding() ) {
1533
+ var event = new CustomEvent('startplayback');
1534
+ event.timestamp = 0;
1535
+ document.dispatchEvent( event );
1536
+ }
1537
+ updateStorage();
1538
+ }
1539
+ else {
1540
+ console.log("Create printouts when ready");
1541
+ whenReady( createPrintout );
1542
+ }
1543
+ });
1544
+ Reveal.addEventListener( 'slidechanged', function( evt ) {
1545
+ // clearTimeout( slidechangeTimeout );
1546
+ //console.log('slidechanged');
1547
+ if ( !printMode ) {
1548
+ slideStart = Date.now() - getSlideDuration();
1549
+ slideIndices = Reveal.getIndices();
1550
+ closeChalkboard();
1551
+ clearCanvas( 0 );
1552
+ clearCanvas( 1 );
1553
+ if ( !playback ) {
1554
+ slidechangeTimeout = setTimeout( startPlayback, transition, getSlideDuration(), 0 );
1555
+ }
1556
+ if ( Reveal.isAutoSliding() ) {
1557
+ var event = new CustomEvent('startplayback');
1558
+ event.timestamp = 0;
1559
+ document.dispatchEvent( event );
1560
+ }
1561
+
1562
+ updateStorage();
1563
+ }
1564
+ });
1565
+ Reveal.addEventListener( 'fragmentshown', function( evt ) {
1566
+ // clearTimeout( slidechangeTimeout );
1567
+ //console.log('fragmentshown');
1568
+ if ( !printMode ) {
1569
+ slideStart = Date.now() - getSlideDuration();
1570
+ slideIndices = Reveal.getIndices();
1571
+ closeChalkboard();
1572
+ clearCanvas( 0 );
1573
+ clearCanvas( 1 );
1574
+ if ( Reveal.isAutoSliding() ) {
1575
+ var event = new CustomEvent('startplayback');
1576
+ event.timestamp = 0;
1577
+ document.dispatchEvent( event );
1578
+ }
1579
+ else if ( !playback ) {
1580
+ //
1581
+ startPlayback( getSlideDuration(), 0 );
1582
+ // closeChalkboard();
1583
+ }
1584
+ }
1585
+ });
1586
+ Reveal.addEventListener( 'fragmenthidden', function( evt ) {
1587
+ // clearTimeout( slidechangeTimeout );
1588
+ //console.log('fragmenthidden');
1589
+ if ( !printMode ) {
1590
+ slideStart = Date.now() - getSlideDuration();
1591
+ slideIndices = Reveal.getIndices();
1592
+ closeChalkboard();
1593
+ clearCanvas( 0 );
1594
+ clearCanvas( 1 );
1595
+ if ( Reveal.isAutoSliding() ) {
1596
+ document.dispatchEvent( new CustomEvent('stopplayback') );
1597
+ }
1598
+ else if ( !playback ) {
1599
+ startPlayback( getSlideDuration() );
1600
+ closeChalkboard();
1601
+ }
1602
+ }
1603
+ });
1604
+
1605
+ Reveal.addEventListener( 'autoslideresumed', function( evt ) {
1606
+ //console.log('autoslideresumed');
1607
+ var event = new CustomEvent('startplayback');
1608
+ event.timestamp = 0;
1609
+ document.dispatchEvent( event );
1610
+ });
1611
+ Reveal.addEventListener( 'autoslidepaused', function( evt ) {
1612
+ //console.log('autoslidepaused');
1613
+ document.dispatchEvent( new CustomEvent('stopplayback') );
1614
+
1615
+ // advance to end of slide
1616
+ // closeChalkboard();
1617
+ startPlayback( getSlideDuration(), 0 );
1618
+ });
1619
+
1620
+ function toggleNotesCanvas() {
1621
+ if ( !readOnly ) {
1622
+ if ( mode == 1 ) {
1623
+ toggleChalkboard();
1624
+ notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)';
1625
+ notescanvas.style.pointerEvents = "auto";
1626
+ }
1627
+ else {
1628
+ if ( notescanvas.style.pointerEvents != "none" ) {
1629
+ // hide notes canvas
1630
+ if ( colorButtons) {
1631
+ notescanvas.querySelector(".palette").style.visibility = "hidden";
1632
+ }
1633
+ event = null;
1634
+ notescanvas.style.background = 'rgba(0,0,0,0)';
1635
+ notescanvas.style.pointerEvents = "none";
1636
+ }
1637
+ else {
1638
+ // show notes canvas
1639
+ if ( colorButtons) {
1640
+ notescanvas.querySelector(".palette").style.visibility = "visible";
1641
+ }
1642
+ notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)';
1643
+ notescanvas.style.pointerEvents = "auto";
1644
+
1645
+ var idx = 0;
1646
+ if (color[mode]) {
1647
+ idx = color[mode];
1648
+ }
1649
+
1650
+ setColor(idx, true);
1651
+
1652
+ // broadcast
1653
+ var message = new CustomEvent(messageType);
1654
+ message.content = { sender: 'chalkboard-plugin', type: 'setcolor', timestamp: Date.now() - slideStart, status: { mode, board, color }, index: idx };
1655
+ document.dispatchEvent( message );
1656
+ }
1657
+ }
1658
+ }
1659
+ };
1660
+
1661
+ function toggleChalkboard() {
1662
+ //console.log("toggleChalkboard " + mode);
1663
+ if ( mode == 1 ) {
1664
+ event = null;
1665
+ if ( !readOnly ) {
1666
+ recordEvent( { type:"close", begin: Date.now() - slideStart } );
1667
+ updateStorage();
1668
+ }
1669
+ closeChalkboard();
1670
+
1671
+ // broadcast
1672
+ var message = new CustomEvent(messageType);
1673
+ message.content = { sender: 'chalkboard-plugin', type: 'closeChalkboard', timestamp: Date.now() - slideStart, status: { mode, board, color } };
1674
+ document.dispatchEvent( message );
1675
+ }
1676
+ else {
1677
+ showChalkboard();
1678
+ if ( !readOnly ) {
1679
+ recordEvent( { type:"open", begin: Date.now() - slideStart } );
1680
+ // broadcast
1681
+ var message = new CustomEvent(messageType);
1682
+ message.content = { sender: 'chalkboard-plugin', type: 'showChalkboard', timestamp: Date.now() - slideStart, status: { mode, board, color } };
1683
+ document.dispatchEvent( message );
1684
+
1685
+ var idx = 0;
1686
+
1687
+ if (rememberColor[mode]) {
1688
+ idx = color[mode];
1689
+ }
1690
+
1691
+ setColor(idx, true);
1692
+
1693
+ // broadcast
1694
+ message = new CustomEvent(messageType);
1695
+ message.content = { sender: 'chalkboard-plugin', type: 'setcolor', timestamp: Date.now() - slideStart, index: idx, status: { mode, board, color } };
1696
+ document.dispatchEvent( message );
1697
+
1698
+ }
1699
+ }
1700
+ };
1701
+
1702
+ function clear() {
1703
+ if ( !readOnly ) {
1704
+ recordEvent( { type:"clear", begin: Date.now() - slideStart } );
1705
+ clearCanvas( mode );
1706
+ // broadcast
1707
+ var message = new CustomEvent(messageType);
1708
+ message.content = { sender: 'chalkboard-plugin', type: 'clear', timestamp: Date.now() - slideStart, status: { mode, board, color } };
1709
+ document.dispatchEvent( message );
1710
+ }
1711
+ };
1712
+
1713
+ function colorIndex( idx ) {
1714
+ if ( !readOnly ) {
1715
+ setColor(idx, true);
1716
+ // recordEvent( { type: "setcolor", index: idx, begin: Date.now() - slideStart } );
1717
+ // broadcast
1718
+ var message = new CustomEvent(messageType);
1719
+ message.content = { sender: 'chalkboard-plugin', type: 'setcolor', timestamp: Date.now() - slideStart, index: idx, status: { mode, board, color } };
1720
+ document.dispatchEvent( message );
1721
+ }
1722
+ }
1723
+
1724
+ function colorNext() {
1725
+ if ( !readOnly ) {
1726
+ let idx = cycleColorNext();
1727
+ setColor(idx, true);
1728
+ // recordEvent( { type: "setcolor", index: idx, begin: Date.now() - slideStart } );
1729
+ // broadcast
1730
+ var message = new CustomEvent(messageType);
1731
+ message.content = { sender: 'chalkboard-plugin', type: 'setcolor', timestamp: Date.now() - slideStart, index: idx, status: { mode, board, color } };
1732
+ document.dispatchEvent( message );
1733
+ }
1734
+ }
1735
+
1736
+ function colorPrev() {
1737
+ if ( !readOnly ) {
1738
+ let idx = cycleColorPrev();
1739
+ setColor(idx, true);
1740
+ // recordEvent( { type: "setcolor", index: idx, begin: Date.now() - slideStart } );
1741
+ // broadcast
1742
+ var message = new CustomEvent(messageType);
1743
+ message.content = { sender: 'chalkboard-plugin', type: 'setcolor', timestamp: Date.now() - slideStart, index: idx, status: { mode, board, color } };
1744
+ document.dispatchEvent( message );
1745
+ }
1746
+ }
1747
+
1748
+ function resetSlide( force ) {
1749
+ var ok = force || confirm("Please confirm to delete chalkboard drawings on this slide!");
1750
+ if ( ok ) {
1751
+ //console.log("resetSlide ");
1752
+ stopPlayback();
1753
+ slideStart = Date.now();
1754
+ event = null;
1755
+ closeChalkboard();
1756
+
1757
+ clearCanvas( 0 );
1758
+ clearCanvas( 1 );
1759
+
1760
+ mode = 1;
1761
+ var slideData = getSlideData();
1762
+ slideData.duration = 0;
1763
+ slideData.events = [];
1764
+ mode = 0;
1765
+ var slideData = getSlideData();
1766
+ slideData.duration = 0;
1767
+ slideData.events = [];
1768
+
1769
+ updateStorage();
1770
+ // broadcast
1771
+ var message = new CustomEvent(messageType);
1772
+ message.content = { sender: 'chalkboard-plugin', type: 'resetSlide', timestamp: Date.now() - slideStart, status: { mode, board, color } };
1773
+ document.dispatchEvent( message );
1774
+ }
1775
+ };
1776
+
1777
+ function resetStorage( force ) {
1778
+ var ok = force || confirm("Please confirm to delete all chalkboard drawings!");
1779
+ if ( ok ) {
1780
+ stopPlayback();
1781
+ slideStart = Date.now();
1782
+ clearCanvas( 0 );
1783
+ clearCanvas( 1 );
1784
+ if ( mode == 1 ) {
1785
+ event = null;
1786
+ closeChalkboard();
1787
+ }
1788
+
1789
+ storage = [
1790
+ { width: Reveal.getConfig().width, height: Reveal.getConfig().height, data: []},
1791
+ { width: Reveal.getConfig().width, height: Reveal.getConfig().height, data: []}
1792
+ ];
1793
+ /*
1794
+ storage = [
1795
+ { width: drawingCanvas[0].width - 2 * drawingCanvas[0].xOffset, height: drawingCanvas[0].height - 2 * drawingCanvas[0].yOffset, data: []},
1796
+ { width: drawingCanvas[1].width, height: drawingCanvas[1].height, data: []}
1797
+ ];
1798
+ */
1799
+ if ( config.storage ) {
1800
+ sessionStorage.setItem( config.storage, null )
1801
+ }
1802
+ // broadcast
1803
+ var message = new CustomEvent(messageType);
1804
+ message.content = { sender: 'chalkboard-plugin', type: 'init', timestamp: Date.now() - slideStart, storage: storage, status: { mode, board, color } };
1805
+ document.dispatchEvent( message );
1806
+ }
1807
+ };
1808
+
1809
+
1810
+ /*
1811
+ this.drawWithBoardmarker = drawWithBoardmarker;
1812
+ this.drawWithChalk = drawWithChalk;
1813
+ this.startRecording = startRecording;
1814
+ */
1815
+ this.toggleNotesCanvas = toggleNotesCanvas;
1816
+ this.toggleChalkboard = toggleChalkboard;
1817
+ this.colorIndex = colorIndex;
1818
+ this.colorNext = colorNext;
1819
+ this.colorPrev = colorPrev;
1820
+ this.clear = clear;
1821
+ this.reset = resetSlide;
1822
+ this.resetAll = resetStorage;
1823
+ this.download = downloadData;
1824
+ this.updateStorage = updateStorage;
1825
+ this.getData = getData;
1826
+ this.configure = configure;
1827
+
1828
+
1829
+ for (var key in keyBindings) {
1830
+ if ( keyBindings[key] ) {
1831
+ Reveal.addKeyBinding( keyBindings[key], RevealChalkboard[key] );
1832
+ }
1833
+ };
1834
+
1835
+ return this;
1836
+ };