asciidoctor-revealjs 1.1.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +72 -0
  3. data/Gemfile +2 -0
  4. data/HACKING.adoc +29 -14
  5. data/LICENSE.adoc +1 -1
  6. data/README.adoc +239 -23
  7. data/Rakefile +22 -1
  8. data/asciidoctor-revealjs.gemspec +6 -4
  9. data/examples/background-color.adoc +45 -0
  10. data/examples/background-color.css +18 -0
  11. data/examples/history-regression-tests.adoc +12 -2
  12. data/examples/images.adoc +32 -0
  13. data/examples/revealjs-features.adoc +23 -0
  14. data/examples/revealjs-plugin-activation.adoc +16 -0
  15. data/examples/revealjs-plugins-conf.js +10 -0
  16. data/examples/revealjs-plugins.adoc +16 -0
  17. data/examples/revealjs-plugins.js +2 -0
  18. data/examples/revealjs-plugins/chalkboard/README.md +124 -0
  19. data/examples/revealjs-plugins/chalkboard/chalkboard.js +1288 -0
  20. data/examples/revealjs-plugins/chalkboard/img/blackboard.png +0 -0
  21. data/examples/revealjs-plugins/chalkboard/img/boardmarker.png +0 -0
  22. data/examples/revealjs-plugins/chalkboard/img/chalk.png +0 -0
  23. data/examples/revealjs-plugins/chalkboard/img/sponge.png +0 -0
  24. data/examples/revealjs-plugins/chalkboard/img/whiteboard.png +0 -0
  25. data/examples/revealjs-plugins/reveal.js-menu/CONTRIBUTING.md +9 -0
  26. data/examples/revealjs-plugins/reveal.js-menu/LICENSE +19 -0
  27. data/examples/revealjs-plugins/reveal.js-menu/README.md +334 -0
  28. data/examples/revealjs-plugins/reveal.js-menu/bower.json +21 -0
  29. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/LICENSE.txt +34 -0
  30. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/all.css +5 -0
  31. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/brands.css +5 -0
  32. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/fontawesome.css +5 -0
  33. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/regular.css +5 -0
  34. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/solid.css +5 -0
  35. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/svg-with-js.css +5 -0
  36. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/v4-shims.css +2170 -0
  37. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/v4-shims.min.css +5 -0
  38. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.eot +0 -0
  39. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.svg +1127 -0
  40. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.ttf +0 -0
  41. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.woff +0 -0
  42. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.woff2 +0 -0
  43. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.eot +0 -0
  44. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.svg +467 -0
  45. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.ttf +0 -0
  46. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.woff +0 -0
  47. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.woff2 +0 -0
  48. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.eot +0 -0
  49. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.svg +2231 -0
  50. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.ttf +0 -0
  51. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.woff +0 -0
  52. data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.woff2 +0 -0
  53. data/examples/revealjs-plugins/reveal.js-menu/menu.css +345 -0
  54. data/examples/revealjs-plugins/reveal.js-menu/menu.js +949 -0
  55. data/examples/revealjs-plugins/reveal.js-menu/package.json +22 -0
  56. data/examples/slide-numbers-custom.adoc +18 -0
  57. data/examples/slide-numbers-for-speaker.adoc +23 -0
  58. data/examples/slide-numbers-no.adoc +18 -0
  59. data/examples/slide-numbers.adoc +28 -0
  60. data/examples/source-callouts.adoc +11 -1
  61. data/examples/source-highlightjs.adoc +9 -0
  62. data/examples/speaker-notes.adoc +25 -2
  63. data/lib/asciidoctor-revealjs.rb +1 -0
  64. data/lib/asciidoctor-revealjs/converter.rb +655 -542
  65. data/lib/asciidoctor-revealjs/version.rb +1 -1
  66. data/templates/admonition.html.slim +2 -3
  67. data/templates/asciidoctor_revealjs.css.slim +14 -0
  68. data/templates/document.html.slim +69 -26
  69. data/templates/helpers.rb +23 -0
  70. data/templates/listing.html.slim +35 -35
  71. data/templates/notes.html.slim +1 -0
  72. data/templates/open.html.slim +7 -4
  73. data/templates/section.html.slim +2 -8
  74. data/templates/sidebar.html.slim +8 -5
  75. data/templates/toc.html.slim +2 -2
  76. metadata +81 -11
data/Rakefile CHANGED
@@ -3,10 +3,11 @@
3
3
  require 'asciidoctor'
4
4
  require 'asciidoctor/doctest'
5
5
  require 'colorize'
6
- require 'thread_safe'
7
6
  require 'tilt'
8
7
 
9
8
  CONVERTER_FILE = 'lib/asciidoctor-revealjs/converter.rb'
9
+ JS_FILE = 'build/asciidoctor-reveal.js'
10
+ DIST_FILE = 'dist/main.js'
10
11
  TEMPLATES_DIR = 'templates'
11
12
 
12
13
  file CONVERTER_FILE => FileList["#{TEMPLATES_DIR}/*"] do
@@ -25,6 +26,26 @@ namespace :build do
25
26
  task 'converter:opal' => 'clean' do
26
27
  build_converter :opal
27
28
  end
29
+
30
+ desc "Transcompile to JavaScript and generate #{JS_FILE}"
31
+ task :js => 'converter:opal' do
32
+ require 'opal'
33
+
34
+ builder = Opal::Builder.new(compiler_options: {
35
+ dynamic_require_severity: :error,
36
+ })
37
+ builder.append_paths 'lib'
38
+ builder.build 'asciidoctor-revealjs'
39
+
40
+ mkdir_p [File.dirname(JS_FILE), File.dirname(DIST_FILE)]
41
+ File.open(JS_FILE, 'w') do |file|
42
+ template = File.read('src/asciidoctor-revealjs.tmpl.js')
43
+ file << template.sub('//OPAL-GENERATED-CODE//', builder.to_s)
44
+ end
45
+ File.binwrite "#{JS_FILE}.map", builder.source_map
46
+
47
+ cp JS_FILE, DIST_FILE, :verbose => true
48
+ end
28
49
  end
29
50
 
30
51
  task :build => 'build:converter'
@@ -11,7 +11,6 @@ Gem::Specification.new do |s|
11
11
  s.summary = 'Converts AsciiDoc to HTML for a Reveal.js presentation'
12
12
  s.description = 'Reveal.JS back-end for Asciidoctor and Asciidoctor.js. Write slides in AsciiDoc!'
13
13
  s.license = 'MIT'
14
- s.required_ruby_version = '>= 1.9.3'
15
14
 
16
15
  files = begin
17
16
  if (result = Open3.popen3('git ls-files -z') {|_, out| out.read }.split %(\0)).empty?
@@ -30,17 +29,20 @@ Gem::Specification.new do |s|
30
29
  s.extra_rdoc_files = Dir['README.adoc', 'LICENSE.adoc', 'HACKING.adoc']
31
30
  s.require_paths = ['lib']
32
31
 
33
- s.add_runtime_dependency 'asciidoctor', '~> 1.5.6'
32
+ s.add_runtime_dependency 'asciidoctor', ['>= 1.5.6', '< 2.1']
34
33
  s.add_runtime_dependency 'thread_safe', '~> 0.3.5'
34
+ s.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
35
35
 
36
36
  s.add_development_dependency 'rake', '~> 10.4.2'
37
- s.add_development_dependency 'asciidoctor-doctest', '= 2.0.0.beta.4'
37
+ s.add_development_dependency 'asciidoctor-doctest', '= 2.0.0.beta.5'
38
38
  s.add_development_dependency 'pry', '~> 0.10.4'
39
39
  if RUBY_ENGINE != 'jruby'
40
40
  s.add_development_dependency 'pry-byebug'
41
41
  end
42
42
  s.add_development_dependency 'colorize'
43
- s.add_development_dependency 'asciidoctor-templates-compiler', '~> 0.3.0'
43
+ s.add_development_dependency 'asciidoctor-templates-compiler', '~> 0.4.2'
44
44
  s.add_development_dependency 'slim', '~> 3.0.6'
45
45
  s.add_development_dependency 'slim-htag', '~> 0.1.0'
46
+ # Overriden in Gemfile and Gemfile.upstream for now
47
+ #s.add_development_dependency 'opal', '~> 0.11.1'
46
48
  end
@@ -0,0 +1,45 @@
1
+ // .revealjs-background-color
2
+ // Demonstration of background color features of both reveal.js and what can be done with CSS.
3
+ // :include: //div[@class="slides"]
4
+ // :header_footer:
5
+ = Colorful Presentation
6
+ :backend: revealjs
7
+ :customcss: background-color.css
8
+ :topic: state=title
9
+ :icons: font
10
+
11
+ // classic
12
+ [background-color="yellow"]
13
+ == Hello
14
+
15
+ // New: with roles backed by CSS (see background-color.css)
16
+ [.red.background]
17
+ == Here
18
+
19
+ Red
20
+
21
+ [.green]
22
+ == We
23
+
24
+ Green
25
+
26
+ NOTE: Green background color shouldn't be applied to the whole slide
27
+
28
+ [.blue.canvas]
29
+ == Go
30
+
31
+ Blue
32
+
33
+ // role that alter style but not slide color
34
+ [.topic]
35
+ == Default color
36
+
37
+ but fancy font
38
+
39
+ // style role *and* color role (using shorthand markup)
40
+ // could also be written in long form like this:
41
+ // [role="topic red background"]
42
+ [.topic.red.background]
43
+ == fancy font
44
+
45
+ and color!
@@ -0,0 +1,18 @@
1
+ section.red.background {
2
+ background-color: red;
3
+ }
4
+
5
+ section.green {
6
+ background-color: green;
7
+ }
8
+
9
+ section.blue.canvas {
10
+ background-color: blue;
11
+ }
12
+
13
+ /* font example */
14
+ @import 'https://fonts.googleapis.com/css?family=Baloo+Bhai';
15
+ section.topic h2,
16
+ section.topic div p {
17
+ font-family: 'Baloo Bhai', cursive;
18
+ }
@@ -11,7 +11,7 @@
11
11
  == 3rd Slide
12
12
  // slide that starts with a number
13
13
 
14
- == P3rhaps this won't work
14
+ == P3rhaps this will not work
15
15
  // Second char is a number
16
16
 
17
17
  == 5th Slide
@@ -19,9 +19,15 @@
19
19
 
20
20
  == Illegal çhàrâctérß
21
21
 
22
- [[explicit]]
22
+ [[explicit_with_anchor]]
23
23
  == Explicit section id
24
24
 
25
+ [id=explicit_with_id]
26
+ == Another Explicit Section Id
27
+
28
+ [#explicit_with_short_anchor]
29
+ == 3rd Explicit is the Charm
30
+
25
31
  == 67848727
26
32
  // Everything should be stripped in the id
27
33
 
@@ -32,3 +38,7 @@
32
38
 
33
39
  == Repeated title
34
40
  // Exact same title means exact same id
41
+
42
+ == hello こんにちは
43
+
44
+ == hello 你好
@@ -17,3 +17,35 @@ image::web_surfing_time.gif[]
17
17
  == Hardcoded
18
18
 
19
19
  image::web_surfing_time.gif[width="1200"]
20
+
21
+ == Image Floating
22
+
23
+ image::web_surfing_time.gif[width=400px,float=right]
24
+
25
+ * Some
26
+ * Points
27
+ * You
28
+ * Won't
29
+ * Look At
30
+
31
+ == Image Role Right
32
+
33
+ // This is asciidoctor-revealjs specific, it allows you to put an image on the right even though it is not part of the same block
34
+ // It uses custom CSS, see #197.
35
+
36
+ image::web_surfing_time.gif[role=right,width=400px]
37
+
38
+ * Some
39
+ * Points
40
+ * You
41
+ * Won't
42
+ * Look At
43
+
44
+ == Image Role Right [alt syntax]
45
+
46
+ [.right]
47
+ image::web_surfing_time.gif[alt text,width=400px]
48
+
49
+ * Yup
50
+ * That
51
+ * Too
@@ -0,0 +1,23 @@
1
+ // .revealjs-features
2
+ // This example tests some of Reveal.js 3.7.0 features
3
+ // :include: //body/script | //div[@class="slides"]
4
+ // :header_footer:
5
+ = Reveal.JS
6
+ :revealjs_history: true
7
+ :revealjs_fragmentInURL: true
8
+
9
+ == Slide One
10
+
11
+ Some content
12
+
13
+ === And
14
+
15
+ With Reveal.JS 3.6
16
+
17
+ === Fragments
18
+
19
+ Can now be displayed in URLs
20
+
21
+ == Second slide
22
+
23
+ Done
@@ -0,0 +1,16 @@
1
+ // .revealjs-plugin-activation
2
+ // Use of the revealjs_plugin_... attributes to enable and disable some revealjs plugins
3
+ // :include: //body/script | //div[@class="slides"]
4
+ // :header_footer:
5
+ = Default Plugins Changes
6
+ Author
7
+ :revealjs_plugins_pdf: enabled
8
+ :revealjs_plugins_marked: disabled
9
+
10
+ == Slide 1
11
+
12
+ Content 1
13
+
14
+ == Slide 2
15
+
16
+ Content 2
@@ -0,0 +1,10 @@
1
+ menu: {
2
+ side: 'right'
3
+ },
4
+ keyboard: {
5
+ 67: function() { RevealChalkboard.toggleNotesCanvas() }, // toggle notes canvas when 'c' is pressed
6
+ 66: function() { RevealChalkboard.toggleChalkboard() }, // toggle chalkboard when 'b' is pressed
7
+ 46: function() { RevealChalkboard.clear() }, // clear chalkboard when 'DEL' is pressed
8
+ 8: function() { RevealChalkboard.reset() }, // reset chalkboard data on current slide when 'BACKSPACE' is pressed
9
+ 68: function() { RevealChalkboard.download() }, // downlad recorded chalkboard drawing when 'd' is pressed
10
+ },
@@ -0,0 +1,16 @@
1
+ // .revealjs-plugins
2
+ // Use of the revealjs_plugins attribute to load custom revealjs plugins
3
+ // :include: //body/script | //div[@class="slides"]
4
+ // :header_footer:
5
+ = Custom Plugins
6
+ Author
7
+ :revealjs_plugins: examples/revealjs-plugins.js
8
+ :revealjs_plugins_configuration: examples/revealjs-plugins-conf.js
9
+
10
+ == Slide 1
11
+
12
+ Content 1
13
+
14
+ == Slide 2
15
+
16
+ Content 2
@@ -0,0 +1,2 @@
1
+ { src: 'revealjs-plugins/reveal.js-menu/menu.js' },
2
+ { src: 'revealjs-plugins/chalkboard/chalkboard.js' }
@@ -0,0 +1,124 @@
1
+ # Chalkboard
2
+
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
+
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.
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.
9
+
10
+ The plugin records all drawings made so that they can be play backed using the ```autoSlide``` feature or the ```audio-slideshow``` plugin.
11
+
12
+ [Check out the live demo](https://rajgoel.github.io/reveal.js-demos/chalkboard-demo.html)
13
+
14
+ The chalkboard effect is based on [Chalkboard](https://github.com/mmoustafa/Chalkboard) by Mohamed Moustafa.
15
+
16
+ ## Installation
17
+
18
+ Copy the file ```chalkboard.js``` and the ```img``` directory into the plugin folder of your reveal.js presentation, i.e. ```plugin/chalkboard```.
19
+
20
+ Add the plugins to the dependencies in your presentation as shown below.
21
+
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
+ });
44
+ ```
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
46
+ ```
47
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
48
+ ```
49
+ to the ```head``` section of you HTML-file.
50
+
51
+ ## Usage
52
+
53
+ ### Enable & disable
54
+
55
+ With above configuration the notes canvas is opened and closed when pressing 'c' and the chalkboard is opened and closed when pressing 'b'.
56
+
57
+ ### Mouse
58
+ - Click the left mouse button and drag to write on notes canvas or chalkboard
59
+ - Click the right mouse button and drag to wipe away previous drawings
60
+
61
+ ### Touch
62
+ - Touch and move to write on notes canvas or chalkboard
63
+ - Touch and hold for half a second, then move to wipe away previous drawings
64
+
65
+ ### 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>
69
+
70
+ ## Playback
71
+
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.
73
+
74
+ ## PDF-Export
75
+
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.
77
+
78
+
79
+ ## Configuration
80
+
81
+ The plugin has several configuration options:
82
+
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"```.
89
+
90
+ 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
+
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.
95
+
96
+ All of the configurations are optional and the default values shown below are used if the options are not provided.
97
+
98
+ ```javascript
99
+ Reveal.initialize({
100
+ // ...
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
+ // ...
114
+
115
+ });
116
+ ```
117
+
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
+ ## License
121
+
122
+ MIT licensed
123
+
124
+ Copyright (C) 2016 Asvin Goel
@@ -0,0 +1,1288 @@
1
+ /*****************************************************************
2
+ ** Author: Asvin Goel, goel@telematique.eu
3
+ **
4
+ ** A plugin for reveal.js adding a chalkboard.
5
+ **
6
+ ** Version: 0.6
7
+ **
8
+ ** License: MIT license (see LICENSE.md)
9
+ **
10
+ ** Credits:
11
+ ** Chalkboard effect by Mohamed Moustafa https://github.com/mmoustafa/Chalkboard
12
+ ******************************************************************/
13
+
14
+ var RevealChalkboard = window.RevealChalkboard || (function(){
15
+ var path = scriptPath();
16
+ function scriptPath() {
17
+ // obtain plugin path from the script element
18
+ var src;
19
+ if (document.currentScript) {
20
+ src = document.currentScript.src;
21
+ } else {
22
+ var sel = document.querySelector('script[src$="/chalkboard.js"]')
23
+ if (sel) {
24
+ src = sel.src;
25
+ }
26
+ }
27
+
28
+ var path = typeof src === undefined ? src
29
+ : src.slice(0, src.lastIndexOf("/") + 1);
30
+ //console.log("Path: " + path);
31
+ return path;
32
+ }
33
+
34
+ /*****************************************************************
35
+ ** Configuration
36
+ ******************************************************************/
37
+ var config = Reveal.getConfig().chalkboard || {};
38
+
39
+ var background, pen, draw, color;
40
+ var theme = config.theme || "chalkboard";
41
+ switch ( theme ) {
42
+ case "whiteboard":
43
+ background = [ 'rgba(127,127,127,.1)' , path + 'img/whiteboard.png' ];
44
+ pen = [ 'url(' + path + 'img/boardmarker.png), auto',
45
+ 'url(' + path + 'img/boardmarker.png), auto' ];
46
+ draw = [ drawWithPen , drawWithPen ];
47
+ color = [ 'rgba(0,0,255,1)', 'rgba(0,0,255,1)' ];
48
+ break;
49
+ default:
50
+ background = [ 'rgba(127,127,127,.1)' , path + 'img/blackboard.png' ];
51
+ pen = [ 'url(' + path + 'img/boardmarker.png), auto',
52
+ 'url(' + path + 'img/chalk.png), auto' ];
53
+ draw = [ drawWithPen , drawWithChalk ];
54
+ color = [ 'rgba(0,0,255,1)', 'rgba(255,255,255,0.5)' ];
55
+ }
56
+
57
+ if ( config.background ) background = config.background;
58
+ if ( config.pen ) pen = config.pen;
59
+ if ( config.draw ) draw = config.draw;
60
+ if ( config.color ) color = config.color;
61
+
62
+ var toggleChalkboardButton = config.toggleChalkboardButton == undefined ? true : config.toggleChalkboardButton;
63
+ var toggleNotesButton = config.toggleNotesButton == undefined ? true : config.toggleNotesButton;
64
+ var transition = config.transition || 800;
65
+
66
+ var readOnly = config.readOnly;
67
+
68
+ var legacyFileSupport = config.legacyFileSupport;
69
+ if ( legacyFileSupport ) { console.warn("Legacy file support is deprecated and may be removed in future versions!") }
70
+
71
+ /*****************************************************************
72
+ ** Setup
73
+ ******************************************************************/
74
+
75
+ function whenReady( callback ) {
76
+ // wait for drawings to be loaded and markdown to be parsed
77
+ if ( loaded == null || document.querySelector('section[data-markdown]:not([data-markdown-parsed])') ) {
78
+ setTimeout( whenReady, 100, callback )
79
+ }
80
+ else {
81
+ callback();
82
+ }
83
+ }
84
+
85
+ var eraserDiameter = 20;
86
+
87
+ if ( toggleChalkboardButton ) {
88
+ //console.log("toggleChalkboardButton")
89
+ var button = document.createElement( 'div' );
90
+ button.className = "chalkboard-button";
91
+ button.id = "toggle-chalkboard";
92
+ button.style.visibility = "visible";
93
+ button.style.position = "absolute";
94
+ button.style.zIndex = 30;
95
+ button.style.fontSize = "24px";
96
+
97
+ button.style.left = toggleChalkboardButton.left || "30px";
98
+ button.style.bottom = toggleChalkboardButton.bottom || "30px";
99
+ button.style.top = toggleChalkboardButton.top || "auto";
100
+ button.style.right = toggleChalkboardButton.right || "auto";
101
+
102
+ button.innerHTML = '<a href="#" onclick="RevealChalkboard.toggleChalkboard(); return false;"><i class="fa fa-pencil-square-o"></i></a>'
103
+ document.querySelector(".reveal").appendChild( button );
104
+ }
105
+ if ( toggleNotesButton ) {
106
+ //console.log("toggleNotesButton")
107
+ var button = document.createElement( 'div' );
108
+ button.className = "chalkboard-button";
109
+ button.id = "toggle-notes";
110
+ button.style.position = "absolute";
111
+ button.style.zIndex = 30;
112
+ button.style.fontSize = "24px";
113
+
114
+ button.style.left = toggleNotesButton.left || "70px";
115
+ button.style.bottom = toggleNotesButton.bottom || "30px";
116
+ button.style.top = toggleNotesButton.top || "auto";
117
+ button.style.right = toggleNotesButton.right || "auto";
118
+
119
+ button.innerHTML = '<a href="#" onclick="RevealChalkboard.toggleNotesCanvas(); return false;"><i class="fa fa-pencil"></i></a>'
120
+ document.querySelector(".reveal").appendChild( button );
121
+ }
122
+ //alert("Buttons");
123
+
124
+ var drawingCanvas = [ {id: "notescanvas" }, {id: "chalkboard" } ];
125
+ setupDrawingCanvas(0);
126
+ setupDrawingCanvas(1);
127
+
128
+ var mode = 0; // 0: notes canvas, 1: chalkboard
129
+
130
+ var mouseX = 0;
131
+ var mouseY = 0;
132
+ var xLast = null;
133
+ var yLast = null;
134
+
135
+ var slideStart = Date.now();
136
+ var slideIndices = { h:0, v:0 };
137
+ var event = null;
138
+ var timeouts = [ [], [] ];
139
+ var touchTimeout = null;
140
+ var slidechangeTimeout = null;
141
+ var playback = false;
142
+
143
+ function setupDrawingCanvas( id ) {
144
+ var container = document.createElement( 'div' );
145
+ container.id = drawingCanvas[id].id;
146
+ container.classList.add( 'overlay' );
147
+ container.setAttribute( 'data-prevent-swipe', '' );
148
+ container.oncontextmenu = function() { return false; }
149
+ container.style.cursor = pen[ id ];
150
+
151
+ drawingCanvas[id].width = window.innerWidth;
152
+ drawingCanvas[id].height = window.innerHeight;
153
+ drawingCanvas[id].scale = 1;
154
+ drawingCanvas[id].xOffset = 0;
155
+ drawingCanvas[id].yOffset = 0;
156
+
157
+
158
+ if ( id == "0" ) {
159
+ container.style.background = 'rgba(0,0,0,0)';
160
+ container.style.zIndex = "24";
161
+ container.classList.add( 'visible' )
162
+ container.style.pointerEvents = "none";
163
+
164
+ var slides = document.querySelector(".slides");
165
+ var aspectRatio = Reveal.getConfig().width / Reveal.getConfig().height;
166
+ if ( drawingCanvas[id].width > drawingCanvas[id].height*aspectRatio ) {
167
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - drawingCanvas[id].height*aspectRatio) / 2;
168
+ }
169
+ else if ( drawingCanvas[id].height > drawingCanvas[id].width/aspectRatio ) {
170
+ drawingCanvas[id].yOffset = ( drawingCanvas[id].height - drawingCanvas[id].width/aspectRatio ) / 2;
171
+ }
172
+ }
173
+ else {
174
+ container.style.background = 'url("' + background[id] + '") repeat';
175
+ container.style.zIndex = "26";
176
+ }
177
+
178
+ var sponge = document.createElement( 'img' );
179
+ sponge.src = path + 'img/sponge.png';
180
+ sponge.id = "sponge";
181
+ sponge.style.visibility = "hidden";
182
+ sponge.style.position = "absolute";
183
+ container.appendChild( sponge );
184
+ drawingCanvas[id].sponge = sponge;
185
+
186
+ var canvas = document.createElement( 'canvas' );
187
+ canvas.width = drawingCanvas[id].width;
188
+ canvas.height = drawingCanvas[id].height;
189
+ canvas.setAttribute( 'data-chalkboard', id );
190
+ canvas.style.cursor = pen[ id ];
191
+ container.appendChild( canvas );
192
+ drawingCanvas[id].canvas = canvas;
193
+
194
+ drawingCanvas[id].context = canvas.getContext("2d");
195
+
196
+
197
+ document.querySelector( '.reveal' ).appendChild( container );
198
+ drawingCanvas[id].container = container;
199
+ }
200
+
201
+
202
+ /*****************************************************************
203
+ ** Storage
204
+ ******************************************************************/
205
+ var storage = [
206
+ { width: drawingCanvas[0].width - 2 * drawingCanvas[0].xOffset, height: drawingCanvas[0].height - 2 * drawingCanvas[0].yOffset, data: []},
207
+ { width: drawingCanvas[1].width, height: drawingCanvas[1].height, data: []}
208
+ ];
209
+ //console.log( JSON.stringify(storage));
210
+
211
+ var loaded = null;
212
+ if ( config.src != null ) {
213
+ loadData( config.src );
214
+ }
215
+
216
+
217
+ /**
218
+ * Load data.
219
+ */
220
+ function loadData( filename ) {
221
+ var xhr = new XMLHttpRequest();
222
+ xhr.onload = function() {
223
+ if (xhr.readyState === 4 && xhr.status != 404 ) {
224
+ storage = JSON.parse(xhr.responseText);
225
+ for (var id = 0; id < storage.length; id++) {
226
+ if ( drawingCanvas[id].width != storage[id].width || drawingCanvas[id].height != storage[id].height ) {
227
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height);
228
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2;
229
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2;
230
+ }
231
+ if ( config.readOnly ) {
232
+ drawingCanvas[id].container.style.cursor = 'default';
233
+ drawingCanvas[id].canvas.style.cursor = 'default';
234
+ }
235
+ }
236
+ loaded = true;
237
+ //console.log("Drawings loaded");
238
+ }
239
+ else {
240
+ config.readOnly = undefined;
241
+ readOnly = undefined;
242
+ console.warn( 'Failed to get file ' + filename +". ReadyState: " + xhr.readyState + ", Status: " + xhr.status);
243
+ loaded = false;
244
+ }
245
+ };
246
+
247
+ xhr.open( 'GET', filename, true );
248
+ try {
249
+ xhr.send();
250
+ }
251
+ catch ( error ) {
252
+ config.readOnly = undefined;
253
+ readOnly = undefined;
254
+ 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 );
255
+ loaded = false;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Download data.
261
+ */
262
+ function downloadData() {
263
+ var a = document.createElement('a');
264
+ document.body.appendChild(a);
265
+ try {
266
+ // cleanup slide data without events
267
+ for (var id = 0; id < 2; id++) {
268
+ for (var i = storage[id].data.length-1; i >= 0; i--) {
269
+ if (storage[id].data[i].events.length == 0) {
270
+ storage[id].data.splice(i, 1);
271
+ }
272
+ }
273
+ }
274
+ a.download = "chalkboard.json";
275
+ var blob = new Blob( [ JSON.stringify( storage ) ], { type: "application/json"} );
276
+ a.href = window.URL.createObjectURL( blob );
277
+ } catch( error ) {
278
+ a.innerHTML += " (" + error + ")";
279
+ }
280
+ a.click();
281
+ document.body.removeChild(a);
282
+ }
283
+
284
+ /**
285
+ * Returns data object for the slide with the given indices.
286
+ */
287
+ function getSlideData( indices, id ) {
288
+ if ( id == undefined ) id = mode;
289
+ if (!indices) indices = slideIndices;
290
+ var data;
291
+ for (var i = 0; i < storage[id].data.length; i++) {
292
+ 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 ) {
293
+ data = storage[id].data[i];
294
+ return data;
295
+ }
296
+ if ( !legacyFileSupport &&
297
+ ( storage[id].data[i].slide.h > indices.h ||
298
+ ( storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v > indices.v ) ||
299
+ ( storage[id].data[i].slide.h === indices.h && storage[id].data[i].slide.v === indices.v && storage[id].data[i].slide.f > indices.f )
300
+ )
301
+ ) {
302
+ storage[id].data.splice( i, 0, { slide: indices, events: [], duration: 0 } );
303
+ data = storage[id].data[i];
304
+ return data;
305
+ }
306
+ }
307
+ storage[id].data.push( { slide: indices, events: [], duration: 0 } );
308
+ data = storage[id].data[storage[id].data.length-1];
309
+ return data;
310
+ }
311
+
312
+ /**
313
+ * Returns maximum duration of slide playback for both modes
314
+ */
315
+ function getSlideDuration( indices ) {
316
+ if (!indices) indices = slideIndices;
317
+ var duration = 0;
318
+ for (var id = 0; id < 2; id++) {
319
+ for (var i = 0; i < storage[id].data.length; i++) {
320
+ 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 ) {
321
+ duration = Math.max( duration, storage[id].data[i].duration );
322
+ break;
323
+ }
324
+ }
325
+ }
326
+ //console.log( duration );
327
+ return duration;
328
+ }
329
+
330
+ /*****************************************************************
331
+ ** Print
332
+ ******************************************************************/
333
+ var printMode = ( /print-pdf/gi ).test( window.location.search );
334
+ //console.log("createPrintout" + printMode)
335
+
336
+ function createPrintout( ) {
337
+ //console.log( 'Create printout for ' + storage[1].data.length + " slides");
338
+ drawingCanvas[0].container.classList.remove( 'visible' ); // do not print notes canvas
339
+
340
+ var patImg = new Image();
341
+ patImg.onload = function () {
342
+ var nextSlide = [];
343
+ var width = Reveal.getConfig().width;
344
+ var height = Reveal.getConfig().height;
345
+ var scale = 1;
346
+ var xOffset = 0;
347
+ var yOffset = 0;
348
+ if ( width != storage[1].width || height != storage[1].height ) {
349
+ scale = Math.min( width/storage[1].width, height/storage[1].height);
350
+ xOffset = (width - storage[1].width * scale)/2;
351
+ yOffset = (height - storage[1].height * scale)/2;
352
+ }
353
+
354
+ for (var i = 0; i < storage[1].data.length; i++) {
355
+ var slide = Reveal.getSlide( storage[1].data[i].slide.h, storage[1].data[i].slide.v );
356
+ nextSlide.push( slide.nextSibling );
357
+ }
358
+ for (var i = 0; i < storage[1].data.length; i++) {
359
+ console.log( 'Create printout for slide ' + storage[1].data[i].slide.h + "." + storage[1].data[i].slide.v );
360
+ var parent = Reveal.getSlide( storage[1].data[i].slide.h, storage[1].data[i].slide.v ).parentElement;
361
+ var slideData = getSlideData( storage[1].data[i].slide, 1 );
362
+
363
+ var imgCanvas = document.createElement('canvas');
364
+ imgCanvas.width = width;
365
+ imgCanvas.height = height;
366
+
367
+ var imgCtx = imgCanvas.getContext("2d");
368
+ imgCtx.fillStyle = imgCtx.createPattern( patImg ,'repeat');
369
+ imgCtx.rect(0,0,imgCanvas.width,imgCanvas.height);
370
+ imgCtx.fill();
371
+
372
+ for (var j = 0; j < slideData.events.length; j++) {
373
+ switch ( slideData.events[j].type ) {
374
+ case "draw":
375
+ for (var k = 1; k < slideData.events[j].curve.length; k++) {
376
+ draw[1]( imgCtx,
377
+ xOffset + slideData.events[j].curve[k-1].x*scale,
378
+ yOffset + slideData.events[j].curve[k-1].y*scale,
379
+ xOffset + slideData.events[j].curve[k].x*scale,
380
+ yOffset + slideData.events[j].curve[k].y*scale
381
+ );
382
+ }
383
+ break;
384
+ case "erase":
385
+ for (var k = 0; k < slideData.events[j].curve.length; k++) {
386
+ eraseWithSponge( imgCtx,
387
+ xOffset + slideData.events[j].curve[k].x*scale,
388
+ yOffset + slideData.events[j].curve[k].y*scale
389
+ );
390
+ }
391
+ break;
392
+ case "clear":
393
+ addPrintout( parent, nextSlide[i], imgCanvas, patImg );
394
+ imgCtx.clearRect(0,0,imgCanvas.width,imgCanvas.height);
395
+ imgCtx.fill();
396
+ break;
397
+ default:
398
+ break;
399
+ }
400
+ }
401
+ if ( slideData.events.length ) {
402
+ addPrintout( parent, nextSlide[i], imgCanvas, patImg );
403
+ }
404
+ }
405
+ Reveal.sync();
406
+ };
407
+ patImg.src = background[1];
408
+ }
409
+
410
+ function addPrintout( parent, nextSlide, imgCanvas, patImg ) {
411
+ var slideCanvas = document.createElement('canvas');
412
+ slideCanvas.width = Reveal.getConfig().width;
413
+ slideCanvas.height = Reveal.getConfig().height;
414
+ var ctx = slideCanvas.getContext("2d");
415
+ ctx.fillStyle = ctx.createPattern( patImg ,'repeat');
416
+ ctx.rect(0,0,slideCanvas.width,slideCanvas.height);
417
+ ctx.fill();
418
+ ctx.drawImage(imgCanvas, 0, 0);
419
+
420
+ var newSlide = document.createElement( 'section' );
421
+ newSlide.classList.add( 'present' );
422
+ newSlide.innerHTML = '<h1 style="visibility:hidden">Drawing</h1>';
423
+ newSlide.setAttribute("data-background-size", '100% 100%' );
424
+ newSlide.setAttribute("data-background-repeat", 'norepeat' );
425
+ newSlide.setAttribute("data-background", 'url("' + slideCanvas.toDataURL("image/png") +'")' );
426
+ if ( nextSlide != null ) {
427
+ parent.insertBefore( newSlide, nextSlide );
428
+ }
429
+ else {
430
+ parent.append( newSlide );
431
+ }
432
+ }
433
+
434
+ /*****************************************************************
435
+ ** Drawings
436
+ ******************************************************************/
437
+
438
+ function drawWithPen(context,fromX,fromY,toX,toY){
439
+ context.lineWidth = 3;
440
+ context.lineCap = 'round';
441
+ context.strokeStyle = color[0];
442
+ context.beginPath();
443
+ context.moveTo(fromX, fromY);
444
+ context.lineTo(toX, toY);
445
+ context.stroke();
446
+ }
447
+
448
+ function drawWithChalk(context,fromX,fromY,toX,toY) {
449
+ var brushDiameter = 7;
450
+ context.lineWidth = brushDiameter;
451
+ context.lineCap = 'round';
452
+ context.fillStyle = color[1]; // 'rgba(255,255,255,0.5)';
453
+ context.strokeStyle = color[1];
454
+ var opacity = Math.min(0.8, Math.max(0,color[1].replace(/^.*,(.+)\)/,'$1') - 0.1)) + Math.random()*0.2;
455
+ context.strokeStyle = context.strokeStyle.replace(/[\d\.]+\)$/g, opacity + ')');
456
+ context.beginPath();
457
+ context.moveTo(fromX, fromY);
458
+ context.lineTo(toX, toY);
459
+ context.stroke();
460
+ // Chalk Effect
461
+ var length = Math.round(Math.sqrt(Math.pow(toX-fromX,2)+Math.pow(toY-fromY,2))/(5/brushDiameter));
462
+ var xUnit = (toX-fromX)/length;
463
+ var yUnit = (toY-fromY)/length;
464
+ for(var i=0; i<length; i++ ){
465
+ var xCurrent = fromX+(i*xUnit);
466
+ var yCurrent = fromY+(i*yUnit);
467
+ var xRandom = xCurrent+(Math.random()-0.5)*brushDiameter*1.2;
468
+ var yRandom = yCurrent+(Math.random()-0.5)*brushDiameter*1.2;
469
+ context.clearRect( xRandom, yRandom, Math.random()*2+2, Math.random()+1);
470
+ }
471
+ }
472
+
473
+ function eraseWithSponge(context,x,y) {
474
+ context.save();
475
+ context.beginPath();
476
+ context.arc(x, y, eraserDiameter, 0, 2 * Math.PI, false);
477
+ context.clip();
478
+ context.clearRect(x - eraserDiameter - 1, y - eraserDiameter - 1, eraserDiameter * 2 + 2, eraserDiameter * 2 + 2);
479
+ context.restore();
480
+ }
481
+
482
+
483
+
484
+ /**
485
+ * Opens an overlay for the chalkboard.
486
+ */
487
+ function showChalkboard() {
488
+ //console.log("showChalkboard");
489
+ clearTimeout(touchTimeout);
490
+ touchTimeout = null;
491
+ drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
492
+ drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
493
+ drawingCanvas[1].container.classList.add( 'visible' );
494
+ mode = 1;
495
+ // broadcast
496
+ var message = new CustomEvent('send');
497
+ message.content = { sender: 'chalkboard-plugin', type: 'showChalkboard' };
498
+ document.dispatchEvent( message );
499
+ }
500
+
501
+
502
+ /**
503
+ * Closes open chalkboard.
504
+ */
505
+ function closeChalkboard() {
506
+ clearTimeout(touchTimeout);
507
+ touchTimeout = null;
508
+ drawingCanvas[0].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
509
+ drawingCanvas[1].sponge.style.visibility = "hidden"; // make sure that the sponge from touch events is hidden
510
+ drawingCanvas[1].container.classList.remove( 'visible' );
511
+ xLast = null;
512
+ yLast = null;
513
+ event = null;
514
+ mode = 0;
515
+ // broadcast
516
+ var message = new CustomEvent('send');
517
+ message.content = { sender: 'chalkboard-plugin', type: 'closeChalkboard' };
518
+ document.dispatchEvent( message );
519
+ }
520
+
521
+ /**
522
+ * Clear current canvas.
523
+ */
524
+ function clearCanvas( id ) {
525
+ if ( id == 0 ) clearTimeout( slidechangeTimeout );
526
+ drawingCanvas[id].context.clearRect(0,0,drawingCanvas[id].width,drawingCanvas[id].height);
527
+ }
528
+
529
+ /*****************************************************************
530
+ ** Broadcast
531
+ ******************************************************************/
532
+ document.addEventListener( 'received', function ( message ) {
533
+ //console.log(JSON.stringify(message));
534
+ if ( message.content && message.content.sender == 'chalkboard-plugin' ) {
535
+ switch ( message.content.type ) {
536
+ case 'showChalkboard':
537
+ showChalkboard();
538
+ break;
539
+ case 'closeChalkboard':
540
+ closeChalkboard();
541
+ break;
542
+ case 'startDrawing':
543
+ startDrawing(message.content.x, message.content.y, message.content.erase);
544
+ break;
545
+ case 'startErasing':
546
+ if ( event ) {
547
+ event.type = "erase";
548
+ event.begin = Date.now() - slideStart;
549
+ eraseWithSponge(drawingCanvas[mode].context, message.content.x, message.content.y);
550
+ }
551
+ break;
552
+ case 'drawSegment':
553
+ drawSegment(message.content.x, message.content.y, message.content.erase);
554
+ break;
555
+ case 'stopDrawing':
556
+ stopDrawing();
557
+ break;
558
+ case 'clear':
559
+ clear();
560
+ break;
561
+ case 'resetSlide':
562
+ resetSlide(true);
563
+ break;
564
+ case 'init':
565
+ storage = message.content.storage;
566
+ for (var id = 0; id < 2; id++ ) {
567
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height );
568
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2;
569
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2;
570
+ }
571
+ clearCanvas( 0 );
572
+ clearCanvas( 1 );
573
+ if ( !playback ) {
574
+ slidechangeTimeout = setTimeout( startPlayback, transition, getSlideDuration(), 0 );
575
+ }
576
+ if ( mode == 1 && message.content.mode == 0) {
577
+ setTimeout( closeChalkboard, transition + 50 );
578
+ }
579
+ if ( mode == 0 && message.content.mode == 1) {
580
+ setTimeout( showChalkboard, transition + 50 );
581
+ }
582
+ mode = message.content.mode;
583
+ break;
584
+ default:
585
+ break;
586
+ }
587
+ }
588
+ });
589
+
590
+ document.addEventListener( 'newclient', function() {
591
+ // broadcast storage
592
+ var message = new CustomEvent('send');
593
+ message.content = { sender: 'chalkboard-plugin', type: 'init', storage: storage, mode: mode };
594
+ document.dispatchEvent( message );
595
+ });
596
+
597
+ /*****************************************************************
598
+ ** Playback
599
+ ******************************************************************/
600
+
601
+ document.addEventListener('seekplayback', function( event ) {
602
+ //console.log('event seekplayback ' + event.timestamp);
603
+ stopPlayback();
604
+ if ( !playback || event.timestamp == 0) {
605
+ // in other cases startplayback fires after seeked
606
+ startPlayback( event.timestamp );
607
+ }
608
+ //console.log('seeked');
609
+ });
610
+
611
+
612
+ document.addEventListener('startplayback', function( event ) {
613
+ //console.log('event startplayback ' + event.timestamp);
614
+ stopPlayback();
615
+ playback = true;
616
+ startPlayback( event.timestamp );
617
+ });
618
+
619
+ document.addEventListener('stopplayback', function( event ) {
620
+ //console.log('event stopplayback ' + (Date.now() - slideStart) );
621
+ playback = false;
622
+ stopPlayback();
623
+ });
624
+
625
+ document.addEventListener('startrecording', function( event ) {
626
+ //console.log('event startrecording ' + event.timestamp);
627
+ startRecording();
628
+ });
629
+
630
+ function recordEvent( event ) {
631
+ var slideData = getSlideData();
632
+ var i = slideData.events.length;
633
+ while ( i > 0 && event.begin < slideData.events[i-1].begin ) {
634
+ i--;
635
+ }
636
+ slideData.events.splice( i, 0, event);
637
+ slideData.duration = Math.max( slideData.duration, Date.now() - slideStart ) + 1;
638
+ }
639
+
640
+ function startRecording() {
641
+ resetSlide( true );
642
+ updateReadOnlyMode();
643
+ slideStart = Date.now();
644
+ }
645
+
646
+ function startPlayback( timestamp, finalMode, resized ) {
647
+ //console.log("playback " + timestamp );
648
+ if ( resized == undefined ) {
649
+ updateReadOnlyMode();
650
+ }
651
+ slideStart = Date.now() - timestamp;
652
+ closeChalkboard();
653
+ mode = 0;
654
+ for ( var id = 0; id < 2; id++ ) {
655
+ clearCanvas( id );
656
+ var slideData = getSlideData( slideIndices, id );
657
+ //console.log( timestamp +" / " + JSON.stringify(slideData));
658
+ var index = 0;
659
+ while ( index < slideData.events.length && slideData.events[index].begin < (Date.now() - slideStart) ) {
660
+ playEvent( id, slideData.events[index], timestamp );
661
+ index++;
662
+ }
663
+
664
+ while ( playback && index < slideData.events.length ) {
665
+ timeouts[id].push( setTimeout( playEvent, slideData.events[index].begin - (Date.now() - slideStart), id, slideData.events[index], timestamp ) );
666
+ index++;
667
+ }
668
+ }
669
+ //console.log("Mode: " + finalMode + "/" + mode );
670
+ if ( finalMode != undefined ) {
671
+ mode = finalMode;
672
+ }
673
+ if( mode == 1 ) showChalkboard();
674
+ //console.log("playback (ok)");
675
+
676
+ };
677
+
678
+ function stopPlayback() {
679
+ //console.log("stopPlayback");
680
+ //console.log("Timeouts: " + timeouts[0].length + "/"+ timeouts[1].length);
681
+ for ( var id = 0; id < 2; id++ ) {
682
+ for (var i = 0; i < timeouts[id].length; i++) {
683
+ clearTimeout(timeouts[id][i]);
684
+ }
685
+ timeouts[id] = [];
686
+ }
687
+ };
688
+
689
+ function playEvent( id, event, timestamp ) {
690
+ //console.log( timestamp +" / " + JSON.stringify(event));
691
+ //console.log( id + ": " + timestamp +" / " + event.begin +" / " + event.type +" / " + mode );
692
+ switch ( event.type ) {
693
+ case "open":
694
+ if ( timestamp <= event.begin ) {
695
+ showChalkboard();
696
+ }
697
+ else {
698
+ mode = 1;
699
+ }
700
+
701
+ break;
702
+ case "close":
703
+ if ( timestamp < event.begin ) {
704
+ closeChalkboard();
705
+ }
706
+ else {
707
+ mode = 0;
708
+ }
709
+ break;
710
+ case "clear":
711
+ clearCanvas( id );
712
+ break;
713
+ case "draw":
714
+ drawCurve( id, event, timestamp );
715
+ break;
716
+ case "erase":
717
+ eraseCurve( id, event, timestamp );
718
+ break;
719
+
720
+ }
721
+ };
722
+
723
+ function drawCurve( id, event, timestamp ) {
724
+ if ( event.curve.length > 1 ) {
725
+ var ctx = drawingCanvas[id].context;
726
+ var scale = drawingCanvas[id].scale;
727
+ var xOffset = drawingCanvas[id].xOffset;
728
+ var yOffset = drawingCanvas[id].yOffset;
729
+
730
+ var stepDuration = ( event.end - event.begin )/ ( event.curve.length - 1 );
731
+ //console.log("---");
732
+ for (var i = 1; i < event.curve.length; i++) {
733
+ if (event.begin + i * stepDuration <= (Date.now() - slideStart)) {
734
+ //console.log( "Draw " + timestamp +" / " + event.begin + " + " + i + " * " + stepDuration );
735
+ 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);
736
+ }
737
+ else if ( playback ) {
738
+ //console.log( "Cue " + timestamp +" / " + (Date.now() - slideStart) +" / " + event.begin + " + " + i + " * " + stepDuration + " = " + Math.max(0,event.begin + i * stepDuration - timestamp) );
739
+ timeouts.push( setTimeout(
740
+ draw[id], Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), ctx,
741
+ xOffset + event.curve[i-1].x*scale,
742
+ yOffset + event.curve[i-1].y*scale,
743
+ xOffset + event.curve[i].x*scale,
744
+ yOffset + event.curve[i].y*scale
745
+ )
746
+ );
747
+ }
748
+ }
749
+ }
750
+
751
+ };
752
+
753
+ function eraseCurve( id, event, timestamp ) {
754
+ if ( event.curve.length > 1 ) {
755
+ var ctx = drawingCanvas[id].context;
756
+ var scale = drawingCanvas[id].scale;
757
+ var xOffset = drawingCanvas[id].xOffset;
758
+ var yOffset = drawingCanvas[id].yOffset;
759
+
760
+ var stepDuration = ( event.end - event.begin )/ event.curve.length;
761
+ for (var i = 0; i < event.curve.length; i++) {
762
+ if (event.begin + i * stepDuration <= (Date.now() - slideStart)) {
763
+ eraseWithSponge(ctx, xOffset + event.curve[i].x*scale, yOffset + event.curve[i].y*scale);
764
+ }
765
+ else if ( playback ) {
766
+ timeouts.push( setTimeout(
767
+ eraseWithSponge, Math.max(0,event.begin + i * stepDuration - (Date.now() - slideStart)), ctx,
768
+ xOffset + event.curve[i].x * scale,
769
+ yOffset + event.curve[i].y * scale
770
+ )
771
+ );
772
+ }
773
+ }
774
+ }
775
+
776
+ };
777
+
778
+
779
+ function startDrawing( x, y, erase ) {
780
+ var ctx = drawingCanvas[mode].context;
781
+ var scale = drawingCanvas[mode].scale;
782
+ var xOffset = drawingCanvas[mode].xOffset;
783
+ var yOffset = drawingCanvas[mode].yOffset;
784
+ xLast = x * scale + xOffset;
785
+ yLast = y * scale + yOffset;
786
+ if ( erase == true) {
787
+ event = { type: "erase", begin: Date.now() - slideStart, end: null, curve: [{x: x, y: y}]};
788
+ drawingCanvas[mode].canvas.style.cursor = 'url("' + path + 'img/sponge.png") ' + eraserDiameter + ' ' + eraserDiameter + ', auto';
789
+ eraseWithSponge(ctx, x * scale + xOffset, y * scale + yOffset);
790
+ }
791
+ else {
792
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: x, y: y}] };
793
+ }
794
+ }
795
+
796
+
797
+ function showSponge(x,y) {
798
+ if ( event ) {
799
+ event.type = "erase";
800
+ event.begin = Date.now() - slideStart;
801
+ // show sponge image
802
+ drawingCanvas[mode].sponge.style.left = (x - eraserDiameter) +"px" ;
803
+ drawingCanvas[mode].sponge.style.top = (y - eraserDiameter) +"px" ;
804
+ drawingCanvas[mode].sponge.style.visibility = "visible";
805
+ eraseWithSponge(drawingCanvas[mode].context,x,y);
806
+ // broadcast
807
+ var message = new CustomEvent('send');
808
+ message.content = { sender: 'chalkboard-plugin', type: 'startErasing', x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale };
809
+ document.dispatchEvent( message );
810
+ }
811
+ }
812
+
813
+ function drawSegment( x, y, erase ) {
814
+ var ctx = drawingCanvas[mode].context;
815
+ var scale = drawingCanvas[mode].scale;
816
+ var xOffset = drawingCanvas[mode].xOffset;
817
+ var yOffset = drawingCanvas[mode].yOffset;
818
+ if ( !event ) {
819
+ // safeguard if broadcast hickup
820
+ startDrawing( x, y, erase );
821
+ }
822
+ event.curve.push({x: x, y: y});
823
+ if(y * scale + yOffset < drawingCanvas[mode].height && x * scale + xOffset < drawingCanvas[mode].width) {
824
+ if ( erase ) {
825
+ eraseWithSponge(ctx, x * scale + xOffset, y * scale + yOffset);
826
+ }
827
+ else {
828
+ draw[mode](ctx, xLast, yLast, x * scale + xOffset, y * scale + yOffset);
829
+ }
830
+ xLast = x * scale + xOffset;
831
+ yLast = y * scale + yOffset;
832
+ }
833
+ }
834
+
835
+ function stopDrawing() {
836
+ if ( event ) {
837
+ event.end = Date.now() - slideStart;
838
+ if ( event.type == "erase" || event.curve.length > 1 ) {
839
+ // do not save a line with a single point only
840
+ recordEvent( event );
841
+ }
842
+ event = null;
843
+ }
844
+ }
845
+
846
+
847
+ /*****************************************************************
848
+ ** User interface
849
+ ******************************************************************/
850
+
851
+
852
+ // TODO: check all touchevents
853
+ document.addEventListener('touchstart', function(evt) {
854
+ if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) {
855
+ // var ctx = drawingCanvas[mode].context;
856
+ var scale = drawingCanvas[mode].scale;
857
+ var xOffset = drawingCanvas[mode].xOffset;
858
+ var yOffset = drawingCanvas[mode].yOffset;
859
+
860
+ evt.preventDefault();
861
+ var touch = evt.touches[0];
862
+ mouseX = touch.pageX;
863
+ mouseY = touch.pageY;
864
+ startDrawing( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, false );
865
+ // broadcast
866
+ var message = new CustomEvent('send');
867
+ message.content = { sender: 'chalkboard-plugin', type: 'startDrawing', x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: false };
868
+ document.dispatchEvent( message );
869
+ /*
870
+ xLast = mouseX;
871
+ yLast = mouseY;
872
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] };
873
+ */
874
+ touchTimeout = setTimeout( showSponge, 500, mouseX, mouseY );
875
+ }
876
+ }, false);
877
+
878
+ document.addEventListener('touchmove', function(evt) {
879
+ clearTimeout( touchTimeout );
880
+ touchTimeout = null;
881
+ if ( event ) {
882
+ // var ctx = drawingCanvas[mode].context;
883
+ var scale = drawingCanvas[mode].scale;
884
+ var xOffset = drawingCanvas[mode].xOffset;
885
+ var yOffset = drawingCanvas[mode].yOffset;
886
+
887
+ var touch = evt.touches[0];
888
+ mouseX = touch.pageX;
889
+ mouseY = touch.pageY;
890
+ if (mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) {
891
+ evt.preventDefault();
892
+ // move sponge
893
+ if ( event.type == "erase" ) {
894
+ drawingCanvas[mode].sponge.style.left = (mouseX - eraserDiameter) +"px" ;
895
+ drawingCanvas[mode].sponge.style.top = (mouseY - eraserDiameter) +"px" ;
896
+ }
897
+ }
898
+
899
+ drawSegment( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, ( event.type == "erase" ) );
900
+ // broadcast
901
+ var message = new CustomEvent('send');
902
+ message.content = { sender: 'chalkboard-plugin', type: 'drawSegment', x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: ( event.type == "erase" ) };
903
+ document.dispatchEvent( message );
904
+ /*
905
+ if (mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) {
906
+ evt.preventDefault();
907
+ event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale});
908
+ if ( event.type == "erase" ) {
909
+ drawingCanvas[mode].sponge.style.left = (mouseX - eraserDiameter) +"px" ;
910
+ drawingCanvas[mode].sponge.style.top = (mouseY - eraserDiameter) +"px" ;
911
+ eraseWithSponge(ctx, mouseX, mouseY);
912
+ }
913
+ else {
914
+ draw[mode](ctx, xLast, yLast, mouseX, mouseY);
915
+ }
916
+ xLast = mouseX;
917
+ yLast = mouseY;
918
+ }
919
+ */
920
+ }
921
+ }, false);
922
+
923
+
924
+ document.addEventListener('touchend', function(evt) {
925
+ clearTimeout( touchTimeout );
926
+ touchTimeout = null;
927
+ // hide sponge image
928
+ drawingCanvas[mode].sponge.style.visibility = "hidden";
929
+ stopDrawing();
930
+ // broadcast
931
+ var message = new CustomEvent('send');
932
+ message.content = { sender: 'chalkboard-plugin', type: 'stopDrawing' };
933
+ document.dispatchEvent( message );
934
+ /*
935
+ if ( event ) {
936
+ event.end = Date.now() - slideStart;
937
+ if ( event.type == "erase" || event.curve.length > 1 ) {
938
+ // do not save a line with a single point only
939
+ recordEvent( event );
940
+ }
941
+ event = null;
942
+ }
943
+ */
944
+ }, false);
945
+
946
+ document.addEventListener( 'mousedown', function( evt ) {
947
+ //console.log( "Read only: " + readOnly );
948
+ if ( !readOnly && evt.target.getAttribute('data-chalkboard') == mode ) {
949
+ //console.log( "mousedown: " + evt.button );
950
+ // var ctx = drawingCanvas[mode].context;
951
+ var scale = drawingCanvas[mode].scale;
952
+ var xOffset = drawingCanvas[mode].xOffset;
953
+ var yOffset = drawingCanvas[mode].yOffset;
954
+
955
+ mouseX = evt.pageX;
956
+ mouseY = evt.pageY;
957
+ startDrawing( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, ( evt.button == 2) );
958
+ // broadcast
959
+ var message = new CustomEvent('send');
960
+ message.content = { sender: 'chalkboard-plugin', type: 'startDrawing', x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: ( evt.button == 2) };
961
+ document.dispatchEvent( message );
962
+ /*
963
+ xLast = mouseX;
964
+ yLast = mouseY;
965
+ if ( evt.button == 2) {
966
+ event = { type: "erase", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}]};
967
+ drawingCanvas[mode].canvas.style.cursor = 'url("' + path + 'img/sponge.png") ' + eraserDiameter + ' ' + eraserDiameter + ', auto';
968
+ eraseWithSponge(ctx,mouseX,mouseY);
969
+ }
970
+ else {
971
+ event = { type: "draw", begin: Date.now() - slideStart, end: null, curve: [{x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale}] };
972
+ }
973
+ */
974
+ }
975
+ } );
976
+
977
+
978
+ document.addEventListener( 'mousemove', function( evt ) {
979
+ if ( event ) {
980
+ // var ctx = drawingCanvas[mode].context;
981
+ var scale = drawingCanvas[mode].scale;
982
+ var xOffset = drawingCanvas[mode].xOffset;
983
+ var yOffset = drawingCanvas[mode].yOffset;
984
+
985
+ mouseX = evt.pageX;
986
+ mouseY = evt.pageY;
987
+ drawSegment( (mouseX - xOffset)/scale, (mouseY-yOffset)/scale, ( event.type == "erase" ) );
988
+ // broadcast
989
+ var message = new CustomEvent('send');
990
+ message.content = { sender: 'chalkboard-plugin', type: 'drawSegment', x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale, erase: ( event.type == "erase" ) };
991
+ document.dispatchEvent( message );
992
+ /*
993
+ event.curve.push({x: (mouseX - xOffset)/scale, y: (mouseY-yOffset)/scale});
994
+ if(mouseY < drawingCanvas[mode].height && mouseX < drawingCanvas[mode].width) {
995
+ if ( event.type == "erase" ) {
996
+ eraseWithSponge(ctx,mouseX,mouseY);
997
+ }
998
+ else {
999
+ draw[mode](ctx, xLast, yLast, mouseX,mouseY);
1000
+ }
1001
+ xLast = mouseX;
1002
+ yLast = mouseY;
1003
+ }
1004
+ */
1005
+ }
1006
+ } );
1007
+
1008
+
1009
+ document.addEventListener( 'mouseup', function( evt ) {
1010
+ drawingCanvas[mode].canvas.style.cursor = pen[mode];
1011
+ if ( event ) {
1012
+ stopDrawing();
1013
+ // broadcast
1014
+ var message = new CustomEvent('send');
1015
+ message.content = { sender: 'chalkboard-plugin', type: 'stopDrawing' };
1016
+ document.dispatchEvent( message );
1017
+ /* if(evt.button == 2){
1018
+ }
1019
+ event.end = Date.now() - slideStart;
1020
+ if ( event.type == "erase" || event.curve.length > 1 ) {
1021
+ // do not save a line with a single point only
1022
+ recordEvent( event );
1023
+ }
1024
+ event = null;
1025
+ */
1026
+ }
1027
+ } );
1028
+
1029
+
1030
+ window.addEventListener( "resize", function() {
1031
+ //console.log("resize");
1032
+ // Resize the canvas and draw everything again
1033
+ var timestamp = Date.now() - slideStart;
1034
+ if ( !playback ) {
1035
+ timestamp = getSlideDuration();
1036
+ }
1037
+
1038
+ //console.log( drawingCanvas[0].scale + "/" + drawingCanvas[0].xOffset + "/" +drawingCanvas[0].yOffset );
1039
+ for (var id = 0; id < 2; id++ ) {
1040
+ drawingCanvas[id].width = window.innerWidth;
1041
+ drawingCanvas[id].height = window.innerHeight;
1042
+ drawingCanvas[id].canvas.width = drawingCanvas[id].width;
1043
+ drawingCanvas[id].canvas.height = drawingCanvas[id].height;
1044
+ drawingCanvas[id].context.canvas.width = drawingCanvas[id].width;
1045
+ drawingCanvas[id].context.canvas.height = drawingCanvas[id].height;
1046
+
1047
+ drawingCanvas[id].scale = Math.min( drawingCanvas[id].width/storage[id].width, drawingCanvas[id].height/storage[id].height );
1048
+ drawingCanvas[id].xOffset = (drawingCanvas[id].width - storage[id].width * drawingCanvas[id].scale)/2;
1049
+ drawingCanvas[id].yOffset = (drawingCanvas[id].height - storage[id].height * drawingCanvas[id].scale)/2;
1050
+ //console.log( drawingCanvas[id].scale + "/" + drawingCanvas[id].xOffset + "/" +drawingCanvas[id].yOffset );
1051
+ }
1052
+ //console.log( window.innerWidth + "/" + window.innerHeight);
1053
+ startPlayback( timestamp, mode, true );
1054
+
1055
+ } );
1056
+
1057
+ function updateReadOnlyMode() {
1058
+ //console.log("updateReadOnlyMode");
1059
+ if ( config.readOnly == undefined ) {
1060
+ readOnly = ( getSlideDuration() > 0 );
1061
+ if ( readOnly ) {
1062
+ drawingCanvas[0].container.style.cursor = 'default';
1063
+ drawingCanvas[1].container.style.cursor = 'default';
1064
+ drawingCanvas[0].canvas.style.cursor = 'default';
1065
+ drawingCanvas[1].canvas.style.cursor = 'default';
1066
+ if ( notescanvas.style.pointerEvents != "none" ) {
1067
+ event = null;
1068
+ notescanvas.style.background = 'rgba(0,0,0,0)';
1069
+ notescanvas.style.pointerEvents = "none";
1070
+ }
1071
+
1072
+ }
1073
+ else {
1074
+ drawingCanvas[0].container.style.cursor = pen[0];
1075
+ drawingCanvas[1].container.style.cursor = pen[1];
1076
+ drawingCanvas[0].canvas.style.cursor = pen[0];
1077
+ drawingCanvas[1].canvas.style.cursor = pen[1];
1078
+ }
1079
+ }
1080
+ }
1081
+
1082
+ Reveal.addEventListener( 'ready', function( evt ) {
1083
+ //console.log('ready');
1084
+ if ( !printMode ) {
1085
+ slideStart = Date.now();
1086
+ slideIndices = Reveal.getIndices();
1087
+ if ( !playback ) {
1088
+ startPlayback( getSlideDuration(), 0 );
1089
+ }
1090
+ if ( Reveal.isAutoSliding() ) {
1091
+ var event = new CustomEvent('startplayback');
1092
+ event.timestamp = 0;
1093
+ document.dispatchEvent( event );
1094
+ }
1095
+ updateReadOnlyMode();
1096
+ }
1097
+ else {
1098
+ whenReady( createPrintout );
1099
+ }
1100
+ });
1101
+ Reveal.addEventListener( 'slidechanged', function( evt ) {
1102
+ // clearTimeout( slidechangeTimeout );
1103
+ //console.log('slidechanged');
1104
+ if ( !printMode ) {
1105
+ slideStart = Date.now();
1106
+ slideIndices = Reveal.getIndices();
1107
+ closeChalkboard();
1108
+ clearCanvas( 0 );
1109
+ clearCanvas( 1 );
1110
+ if ( !playback ) {
1111
+ slidechangeTimeout = setTimeout( startPlayback, transition, getSlideDuration(), 0 );
1112
+ }
1113
+ if ( Reveal.isAutoSliding() ) {
1114
+ var event = new CustomEvent('startplayback');
1115
+ event.timestamp = 0;
1116
+ document.dispatchEvent( event );
1117
+ }
1118
+
1119
+ updateReadOnlyMode();
1120
+ }
1121
+ });
1122
+ Reveal.addEventListener( 'fragmentshown', function( evt ) {
1123
+ // clearTimeout( slidechangeTimeout );
1124
+ //console.log('fragmentshown');
1125
+ if ( !printMode ) {
1126
+ slideStart = Date.now();
1127
+ slideIndices = Reveal.getIndices();
1128
+ closeChalkboard();
1129
+ clearCanvas( 0 );
1130
+ clearCanvas( 1 );
1131
+ if ( Reveal.isAutoSliding() ) {
1132
+ var event = new CustomEvent('startplayback');
1133
+ event.timestamp = 0;
1134
+ document.dispatchEvent( event );
1135
+ }
1136
+ else if ( !playback ) {
1137
+ //
1138
+ startPlayback( getSlideDuration(), 0 );
1139
+ // closeChalkboard();
1140
+ }
1141
+ updateReadOnlyMode();
1142
+ }
1143
+ });
1144
+ Reveal.addEventListener( 'fragmenthidden', function( evt ) {
1145
+ // clearTimeout( slidechangeTimeout );
1146
+ //console.log('fragmenthidden');
1147
+ if ( !printMode ) {
1148
+ slideStart = Date.now();
1149
+ slideIndices = Reveal.getIndices();
1150
+ closeChalkboard();
1151
+ clearCanvas( 0 );
1152
+ clearCanvas( 1 );
1153
+ if ( Reveal.isAutoSliding() ) {
1154
+ document.dispatchEvent( new CustomEvent('stopplayback') );
1155
+ }
1156
+ else if ( !playback ) {
1157
+ startPlayback( getSlideDuration() );
1158
+ closeChalkboard();
1159
+ }
1160
+ updateReadOnlyMode();
1161
+ }
1162
+ });
1163
+
1164
+ Reveal.addEventListener( 'autoslideresumed', function( evt ) {
1165
+ //console.log('autoslideresumed');
1166
+ var event = new CustomEvent('startplayback');
1167
+ event.timestamp = 0;
1168
+ document.dispatchEvent( event );
1169
+ });
1170
+ Reveal.addEventListener( 'autoslidepaused', function( evt ) {
1171
+ //console.log('autoslidepaused');
1172
+ document.dispatchEvent( new CustomEvent('stopplayback') );
1173
+
1174
+ // advance to end of slide
1175
+ // closeChalkboard();
1176
+ startPlayback( getSlideDuration(), 0 );
1177
+ });
1178
+
1179
+ function toggleNotesCanvas() {
1180
+ if ( !readOnly ) {
1181
+ if ( mode == 1 ) {
1182
+ toggleChalkboard();
1183
+ notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)';
1184
+ notescanvas.style.pointerEvents = "auto";
1185
+ }
1186
+ else {
1187
+ if ( notescanvas.style.pointerEvents != "none" ) {
1188
+ event = null;
1189
+ notescanvas.style.background = 'rgba(0,0,0,0)';
1190
+ notescanvas.style.pointerEvents = "none";
1191
+ }
1192
+ else {
1193
+ notescanvas.style.background = background[0]; //'rgba(255,0,0,0.5)';
1194
+ notescanvas.style.pointerEvents = "auto";
1195
+ }
1196
+ }
1197
+ }
1198
+ };
1199
+
1200
+ function toggleChalkboard() {
1201
+ //console.log("toggleChalkboard " + mode);
1202
+ if ( mode == 1 ) {
1203
+ event = null;
1204
+ if ( !readOnly ) recordEvent( { type:"close", begin: Date.now() - slideStart } );
1205
+ closeChalkboard();
1206
+ }
1207
+ else {
1208
+ showChalkboard();
1209
+ if ( !readOnly ) recordEvent( { type:"open", begin: Date.now() - slideStart } );
1210
+ }
1211
+ };
1212
+
1213
+ function clear() {
1214
+ if ( !readOnly ) {
1215
+ recordEvent( { type:"clear", begin: Date.now() - slideStart } );
1216
+ clearCanvas( mode );
1217
+ // broadcast
1218
+ var message = new CustomEvent('send');
1219
+ message.content = { sender: 'chalkboard-plugin', type: 'clear' };
1220
+ document.dispatchEvent( message );
1221
+ }
1222
+ };
1223
+
1224
+ function resetSlide( force ) {
1225
+ var ok = force || confirm("Please confirm to delete chalkboard drawings on this slide!");
1226
+ if ( ok ) {
1227
+ //console.log("resetSlide ");
1228
+ stopPlayback();
1229
+ slideStart = Date.now();
1230
+ event = null;
1231
+ closeChalkboard();
1232
+
1233
+ clearCanvas( 0 );
1234
+ clearCanvas( 1 );
1235
+
1236
+ mode = 1;
1237
+ var slideData = getSlideData();
1238
+ slideData.duration = 0;
1239
+ slideData.events = [];
1240
+ mode = 0;
1241
+ var slideData = getSlideData();
1242
+ slideData.duration = 0;
1243
+ slideData.events = [];
1244
+
1245
+ updateReadOnlyMode();
1246
+ // broadcast
1247
+ var message = new CustomEvent('send');
1248
+ message.content = { sender: 'chalkboard-plugin', type: 'resetSlide' };
1249
+ document.dispatchEvent( message );
1250
+ }
1251
+ };
1252
+
1253
+ function resetStorage( force ) {
1254
+ var ok = force || confirm("Please confirm to delete all chalkboard drawings!");
1255
+ if ( ok ) {
1256
+ stopPlayback();
1257
+ slideStart = Date.now();
1258
+ clearCanvas( 0 );
1259
+ clearCanvas( 1 );
1260
+ if ( mode == 1 ) {
1261
+ event = null;
1262
+ closeChalkboard();
1263
+ }
1264
+ storage = [
1265
+ { width: drawingCanvas[0].width - 2 * drawingCanvas[0].xOffset, height: drawingCanvas[0].height - 2 * drawingCanvas[0].yOffset, data: []},
1266
+ { width: drawingCanvas[1].width, height: drawingCanvas[1].height, data: []}
1267
+ ];
1268
+
1269
+ updateReadOnlyMode();
1270
+ // broadcast
1271
+ var message = new CustomEvent('send');
1272
+ message.content = { sender: 'chalkboard-plugin', type: 'init', storage: storage, mode: mode };
1273
+ document.dispatchEvent( message );
1274
+ }
1275
+ };
1276
+
1277
+ this.drawWithPen = drawWithPen;
1278
+ this.drawWithChalk = drawWithChalk;
1279
+ this.toggleNotesCanvas = toggleNotesCanvas;
1280
+ this.toggleChalkboard = toggleChalkboard;
1281
+ this.startRecording = startRecording;
1282
+ this.clear = clear;
1283
+ this.reset = resetSlide;
1284
+ this.resetAll = resetStorage;
1285
+ this.download = downloadData;
1286
+
1287
+ return this;
1288
+ })();