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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +72 -0
- data/Gemfile +2 -0
- data/HACKING.adoc +29 -14
- data/LICENSE.adoc +1 -1
- data/README.adoc +239 -23
- data/Rakefile +22 -1
- data/asciidoctor-revealjs.gemspec +6 -4
- data/examples/background-color.adoc +45 -0
- data/examples/background-color.css +18 -0
- data/examples/history-regression-tests.adoc +12 -2
- data/examples/images.adoc +32 -0
- data/examples/revealjs-features.adoc +23 -0
- data/examples/revealjs-plugin-activation.adoc +16 -0
- data/examples/revealjs-plugins-conf.js +10 -0
- data/examples/revealjs-plugins.adoc +16 -0
- data/examples/revealjs-plugins.js +2 -0
- data/examples/revealjs-plugins/chalkboard/README.md +124 -0
- data/examples/revealjs-plugins/chalkboard/chalkboard.js +1288 -0
- data/examples/revealjs-plugins/chalkboard/img/blackboard.png +0 -0
- data/examples/revealjs-plugins/chalkboard/img/boardmarker.png +0 -0
- data/examples/revealjs-plugins/chalkboard/img/chalk.png +0 -0
- data/examples/revealjs-plugins/chalkboard/img/sponge.png +0 -0
- data/examples/revealjs-plugins/chalkboard/img/whiteboard.png +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/CONTRIBUTING.md +9 -0
- data/examples/revealjs-plugins/reveal.js-menu/LICENSE +19 -0
- data/examples/revealjs-plugins/reveal.js-menu/README.md +334 -0
- data/examples/revealjs-plugins/reveal.js-menu/bower.json +21 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/LICENSE.txt +34 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/all.css +5 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/brands.css +5 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/fontawesome.css +5 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/regular.css +5 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/solid.css +5 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/svg-with-js.css +5 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/v4-shims.css +2170 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/css/v4-shims.min.css +5 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.eot +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.svg +1127 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.ttf +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.woff +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-brands-400.woff2 +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.eot +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.svg +467 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.ttf +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.woff +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-regular-400.woff2 +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.eot +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.svg +2231 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.ttf +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.woff +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/font-awesome/webfonts/fa-solid-900.woff2 +0 -0
- data/examples/revealjs-plugins/reveal.js-menu/menu.css +345 -0
- data/examples/revealjs-plugins/reveal.js-menu/menu.js +949 -0
- data/examples/revealjs-plugins/reveal.js-menu/package.json +22 -0
- data/examples/slide-numbers-custom.adoc +18 -0
- data/examples/slide-numbers-for-speaker.adoc +23 -0
- data/examples/slide-numbers-no.adoc +18 -0
- data/examples/slide-numbers.adoc +28 -0
- data/examples/source-callouts.adoc +11 -1
- data/examples/source-highlightjs.adoc +9 -0
- data/examples/speaker-notes.adoc +25 -2
- data/lib/asciidoctor-revealjs.rb +1 -0
- data/lib/asciidoctor-revealjs/converter.rb +655 -542
- data/lib/asciidoctor-revealjs/version.rb +1 -1
- data/templates/admonition.html.slim +2 -3
- data/templates/asciidoctor_revealjs.css.slim +14 -0
- data/templates/document.html.slim +69 -26
- data/templates/helpers.rb +23 -0
- data/templates/listing.html.slim +35 -35
- data/templates/notes.html.slim +1 -0
- data/templates/open.html.slim +7 -4
- data/templates/section.html.slim +2 -8
- data/templates/sidebar.html.slim +8 -5
- data/templates/toc.html.slim +2 -2
- 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', '
|
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.
|
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.
|
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
|
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
|
-
[[
|
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 你好
|
data/examples/images.adoc
CHANGED
@@ -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,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
|
+
})();
|