trickster 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/js/trickster.js ADDED
@@ -0,0 +1,216 @@
1
+ var TricksterDefaultConfig = {
2
+ /** Padding between max width and max font size */
3
+ padding: 128,
4
+ /** Time, in ms, to transition between slides */
5
+ transitionTime: 200,
6
+ /** Time, in ms, that it should take to type each command */
7
+ typingTime: 1000,
8
+ /** Keycodes that advance to the next slide */
9
+ advanceKeycodes: [74, // j
10
+ 39, // right arrow
11
+ 34, // Kensington presenter right arrow
12
+ 32], // space bar
13
+ /** Keycodes that go back to the previous slide */
14
+ backKeycodes: [75, // k
15
+ 37, // left arrow
16
+ 33, // Kensington presenter left arrow
17
+ 8], // delete
18
+ startOverKeycodes: [66], // Kensington presenter down/stop
19
+ /** These keycodes, if encountered, will not be sent along
20
+ to the browser. Useful if there might be some vertical
21
+ scrolling and 32/33/34 would otherwise scroll */
22
+ keyCodesPreventingDefault: [ 34, 32, 33 ]
23
+ };
24
+ /** Loads Trickster.
25
+ * config: configuration, or TricksterDefaultConfig to get the defaults
26
+ * functions: override helper functions
27
+ */
28
+ var TricksterLoader = function(config,functions) {
29
+ var slides = Utils.fOr(functions.slides,function() {
30
+ return $("section");
31
+ });
32
+ var browserWindow = Utils.fOr(functions.browserWindow,function() {
33
+ return {
34
+ width: $(window).width(),
35
+ height: $(window).height()
36
+ };
37
+ });
38
+ var keyboardBindings = Utils.fOr(functions.keyboardBindings,function() {
39
+ return $(window);
40
+ });
41
+
42
+ var strikeThroughCode = Utils.fOr(functions.strikeThroughCode,function() {
43
+ $("code").each(function() {
44
+ if ($(this).attr("data-strikeouts")) {
45
+ var strikes = $(this).attr("data-strikeouts").split(",");
46
+ for(var index in strikes) {
47
+ var line = parseInt(strikes[index]) - 1;
48
+ $(this).find(".line-" + line).css("text-decoration","line-through");
49
+ }
50
+ }
51
+ });
52
+ });
53
+
54
+ var syntaxHighlighter = Utils.fOr(functions.syntaxHighlighter,function() {
55
+ return {
56
+ highlight: function() {
57
+ hljs.lineNodes = true;
58
+ hljs.initHighlighting();
59
+ strikeThroughCode();
60
+ }
61
+ }
62
+ });
63
+ var bindKeys = Utils.fOr(functions.bindKeys,function(keyCodes,f) {
64
+ $(window).keyup(function(event) {
65
+ if (keyCodes.indexOf(event.which) != -1) {
66
+ f();
67
+ }
68
+ });
69
+ });
70
+ var preventDefaultKeyCodeAction = Utils.fOr(functions.preventDefaultKeyCodeAction,function(keyCodes) {
71
+ $(window).keydown(function(event) {
72
+ if (keyCodes.indexOf(event.which) != -1) {
73
+ event.preventDefault();
74
+ }
75
+ });
76
+ });
77
+
78
+ function currentSlide(whichSlide) {
79
+ if (typeof whichSlide === "undefined") {
80
+ whichSlide = Trickster.currentSlide;
81
+ }
82
+ return slides().eq(whichSlide);
83
+ };
84
+
85
+ function initCurrentSlide() {
86
+ var slideNumber = 0;
87
+ if (document.location.hash !== "") {
88
+ slideNumber = parseInt(document.location.hash.replace("#",""));
89
+ Trickster.currentSlide = slideNumber;
90
+ }
91
+ applySlideClassToBody(currentSlide(slideNumber));
92
+ };
93
+
94
+ function applySlideClassToBody(slide) {
95
+ $("body").attr("class",slide.attr("class"));
96
+ if (slide.attr("data-background")) {
97
+ $("body").css("background","#" + slide.attr("data-background"));
98
+ }
99
+ else {
100
+ $("body").css("background","");
101
+ }
102
+ }
103
+
104
+ function changeSlides(nextSlide,afterChanges) {
105
+ if ((nextSlide != 0) && (nextSlide != Trickster.previousSlide)){
106
+ Trickster.previousSlide = Trickster.currentSlide;
107
+ }
108
+ afterChanges = Utils.f(afterChanges);
109
+ var transitionTime = config.transitionTime / 2;
110
+ if (currentSlide().attr("data-transition")) {
111
+ transitionTime = parseInt(currentSlide().attr("data-transitionTime"));
112
+ }
113
+ currentSlide().fadeOut(transitionTime, function() {
114
+ applySlideClassToBody(currentSlide(nextSlide));
115
+ currentSlide(nextSlide).fadeIn(transitionTime, function() {
116
+ Trickster.currentSlide = nextSlide;
117
+ window.history.replaceState({},"",document.URL.replace(/#.*$/,"") + "#" + Trickster.currentSlide);
118
+ afterChanges();
119
+ });
120
+ });
121
+ };
122
+
123
+ function sizeAllToFit() {
124
+ slides().each(function(index,element) {
125
+ // Order matters here
126
+ var sizeToFit = Sizer.sizeFunction(browserWindow().width,browserWindow().height);
127
+ sizeToFit($(element));
128
+ });
129
+ }
130
+
131
+ function setupKeyBindings() {
132
+ bindKeys(config.advanceKeycodes,Trickster.advance);
133
+ bindKeys(config.backKeycodes,Trickster.back);
134
+ bindKeys(config.startOverKeycodes,Trickster.startOver);
135
+ preventDefaultKeyCodeAction(config.keyCodesPreventingDefault);
136
+ bindKeys([189],Trickster.shrink); // -
137
+ bindKeys([187],Trickster.embiggen); // +
138
+ }
139
+
140
+ function hideAllSlides() {
141
+ $("section").css("display","none");
142
+ $("li").css("visibility","hidden");
143
+ $("section.COMMANDLINE .cli-element").css("display","none");
144
+ }
145
+
146
+ var Bullets = TricksterBullets(currentSlide,config);
147
+ return {
148
+ /** State */
149
+ currentSlide: 0,
150
+ totalSlides: 1,
151
+ previousSlide: 0,
152
+
153
+ /** Set everything up for the slideshow */
154
+ load: function() {
155
+ // Order matters here
156
+ Trickster.totalSlides = slides().length;
157
+ initCurrentSlide();
158
+ setupKeyBindings();
159
+ syntaxHighlighter().highlight();
160
+ sizeAllToFit();
161
+ hideAllSlides();
162
+ currentSlide().fadeIn(config.transitionTime / 2);
163
+ },
164
+
165
+ /** Reduce the font-size of the current slide slightly */
166
+ shrink: function() {
167
+ var currentSize = parseInt(currentSlide().css("font-size"));
168
+ currentSlide().css("font-size",currentSize - 4);
169
+ if (currentSlide().hasClass("CODE") || currentSlide().hasClass("COMMANDLINE")) {
170
+ currentSlide().css("margin-top",-1 * (currentSize - 4));
171
+ }
172
+ },
173
+ /** Increase the font-size of the current slide slightly */
174
+ embiggen: function() {
175
+ var currentSize = parseInt(currentSlide().css("font-size"));
176
+ currentSlide().css("font-size",currentSize + 4);
177
+ if (currentSlide().hasClass("CODE") || currentSlide().hasClass("COMMANDLINE")) {
178
+ currentSlide().css("margin-top",-1 * (currentSize - 4));
179
+ }
180
+ },
181
+
182
+ startOver: function() {
183
+ if (Trickster.currentSlide == 0) {
184
+ changeSlides(Trickster.previousSlide, Bullets.rehideBullets());
185
+ }
186
+ else {
187
+ changeSlides(0,Bullets.rehideBullets());
188
+ }
189
+ },
190
+
191
+ /** Move forward one slide */
192
+ advance: function() {
193
+ if (Bullets.hasBulletsToAdvanceFirst()) {
194
+ Bullets.advanceToNextBullet();
195
+ }
196
+ else {
197
+ var nextSlide = Trickster.currentSlide + 1;
198
+ if (nextSlide >= Trickster.totalSlides) {
199
+ nextSlide = 0;
200
+ }
201
+ changeSlides(nextSlide, Bullets.rehideBullets());
202
+ }
203
+ },
204
+
205
+ /** Move back one slide */
206
+ back: function() {
207
+ var nextSlide = Trickster.currentSlide - 1;
208
+ if (nextSlide < 0) {
209
+ nextSlide = Trickster.totalSlides - 1;
210
+ }
211
+ changeSlides(nextSlide, Bullets.rehideBullets());
212
+ }
213
+ };
214
+ };
215
+ // 66 - Kensington down/stop
216
+ // 116 - Kensington up/laser
data/js/utils.js ADDED
@@ -0,0 +1,19 @@
1
+ var Utils = function() {
2
+ /** If possibleFunction is undefined, use otherFunction */
3
+ function fOr(possibleFunction,otherFunction) {
4
+ if (typeof possibleFunction != "undefined") {
5
+ return possibleFunction;
6
+ }
7
+ else {
8
+ return otherFunction;
9
+ }
10
+ };
11
+ /** Turn possibly-undefined into a function */
12
+ function f(possibleFunction) {
13
+ return fOr(possibleFunction,function() {});
14
+ };
15
+ return {
16
+ f: f,
17
+ fOr: fOr
18
+ };
19
+ }();
@@ -0,0 +1,25 @@
1
+ module Trickster
2
+ class FileCopier
3
+ # install_root:: where the root of trickster's install is, to find files to copy
4
+ # destination_root:: root of where we are copying to
5
+ def initialize(install_root,destination_root)
6
+ @install_root = install_root
7
+ @destination_root = destination_root
8
+ end
9
+
10
+ # Shallow copies all files from +from+ with the given extension
11
+ #
12
+ # from:: location, relative to @install_root where files are copied from
13
+ # extension:: extension of files to copy
14
+ # options:: options to control the copy. Currently recognized keys:
15
+ # :except:: array of filenames to skip
16
+ def copy_files(from,extension,options={})
17
+ dest_dir = File.join(@destination_root,from)
18
+ FileUtils.mkdir(dest_dir) unless File.exists?(dest_dir)
19
+ Dir["#{@install_root}/#{from}/*.#{extension}"].each do |file|
20
+ next if Array(options[:except]).include?(File.basename(file))
21
+ FileUtils.cp file,dest_dir
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,12 @@
1
+ module Trickster
2
+ module Renderer
3
+ class BulletsRenderer
4
+ def render(io,content,options)
5
+ io.puts "<h1>#{content[0]}</h1>"
6
+ io.puts "<ul>"
7
+ io.puts((content[1..-1].map { |_| "<li>" + _.gsub(/^[*+-o] /,'') + "</li>\n" }).join(""))
8
+ io.puts "</ul>"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,47 @@
1
+ module Trickster
2
+ module Renderer
3
+ class CodeRenderer
4
+ def render(io,content,options)
5
+ content = read_file_if_content_is_filename(content)
6
+ io.puts "<pre><code #{language(options)}data-strikeouts='#{strikes(options)}' data-callout-lines='#{callouts(options)}'>#{content[0]}"
7
+ content[1..-2].each { |line| io.puts line }
8
+ io.puts "#{content[-1]}</code></pre>"
9
+ end
10
+
11
+ private
12
+
13
+ def read_file_if_content_is_filename(content)
14
+ if content[0] =~ /file:\/\/(.*$)/
15
+ File.open($1).readlines.map(&:chomp)
16
+ else
17
+ content
18
+ end
19
+ end
20
+
21
+ def language(options)
22
+ language = ''
23
+ if options =~/language=([^\s]+)/
24
+ language = "class='#{$1}' "
25
+ end
26
+ language
27
+ end
28
+
29
+ def callout_option(options)
30
+ if options =~ /callout=([^\s]+)/
31
+ callouts = $1
32
+ callouts.split(/,/).map(&:to_i)
33
+ else
34
+ []
35
+ end
36
+ end
37
+
38
+ def callouts(options)
39
+ callout_option(options).map(&:abs).join(',')
40
+ end
41
+
42
+ def strikes(options)
43
+ callout_option(options).select { |_| _ < 0 }.map(&:abs).join(',')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ module Trickster
2
+ module Renderer
3
+ class CommandlineRenderer
4
+ def render(io,content,options)
5
+ io.print "<pre><code class='no-highlight'>"
6
+ content = split_by_commands_and_results(content)
7
+ content.each do |(prompt,command,result)|
8
+ io.puts "<span class='cli-prompt'>#{prompt}</span> <span class='cli-element cli-command cli-line'>#{command}</span>"
9
+ unless result.empty?
10
+ io.puts "<span class='cli-element cli-result'>#{result.join("\n")}</span>"
11
+ end
12
+ end
13
+ io.puts "</code></pre>"
14
+ end
15
+
16
+ private
17
+
18
+ def split_by_commands_and_results(content)
19
+ results = []
20
+ current_command_response = []
21
+ content.each do |line|
22
+ if line =~ /^([>%#])(.*)$/
23
+ prompt = $1
24
+ command = $2
25
+ results << current_command_response unless current_command_response.empty?
26
+ current_command_response = [prompt.gsub(">","&gt;"),command,[]]
27
+ else
28
+ current_command_response[2] << "<span class='cli-line'>#{line}</span>"
29
+ end
30
+ end
31
+ results << current_command_response unless current_command_response.empty?
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ module Trickster
2
+ module Renderer
3
+ class ImageRenderer
4
+ def render(io,content,options)
5
+ io.puts "<img src='#{content[0]}'>"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Trickster
2
+ module Renderer
3
+ class NormalRenderer
4
+ def render(io,content,options)
5
+ io.puts "<h1>#{content[0]}</h1>"
6
+ io.puts "<h2>#{content[1]}</h2>" unless content[1].nil? || content[1] == ''
7
+ io.puts "<h3>#{content[2]}</h3>" unless content[2].nil? || content[2] == ''
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ module Trickster
2
+ class SlideParser
3
+ attr_reader :title
4
+ def initialize(lines,renderer)
5
+ @lines = lines
6
+ @renderer = renderer
7
+ @title = @lines.shift
8
+ end
9
+
10
+ def body
11
+ each_slide do |slide_type,content|
12
+ @renderer.render_slide(slide_type,content)
13
+ end
14
+ @renderer.content
15
+ end
16
+
17
+ private
18
+
19
+ def each_slide(&block)
20
+ content = []
21
+ slide_type = nil
22
+ @lines.each do |line|
23
+ if line =~/^!(.*)$/
24
+ block.call(slide_type,content) unless slide_type.nil?
25
+ slide_type = $1
26
+ content = []
27
+ else
28
+ content << line
29
+ end
30
+ end
31
+ block.call(slide_type,content)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,51 @@
1
+ require 'stringio'
2
+ module Trickster
3
+ class SlideRenderer
4
+ def initialize
5
+ @body_io = StringIO.new
6
+ end
7
+
8
+ def render_slide(slide_type,content)
9
+ write_slide(slide_type,content,@body_io)
10
+ end
11
+
12
+ def content
13
+ @body_io.string
14
+ end
15
+
16
+ private
17
+ RENDERERS = Hash.new {
18
+ Renderer::NormalRenderer.new
19
+ }.tap { |hash|
20
+ hash["IMAGE"] = Renderer::ImageRenderer.new
21
+ hash["COMMANDLINE"] = Renderer::CommandlineRenderer.new
22
+ hash["BULLETS"] = Renderer::BulletsRenderer.new
23
+ hash["CODE"] = Renderer::CodeRenderer.new
24
+ }.freeze
25
+
26
+ def write_slide(slide_type,content,slide)
27
+ slide_type,options = slide_type.split(/:/)
28
+
29
+ content = content[0..-2] if content[-1] =~ /^\s*$/
30
+ slide.puts "<section class='#{slide_type}'#{background_data(options)}#{transition_data(options)}>"
31
+ RENDERERS[slide_type].render(slide,content,options)
32
+ slide.puts "</section>"
33
+ end
34
+
35
+ def transition_data(options)
36
+ data_attribute('transition',options)
37
+ end
38
+
39
+ def background_data(options)
40
+ data_attribute('background',options)
41
+ end
42
+
43
+ def data_attribute(attribute,options)
44
+ attribute_element = ''
45
+ if options =~ /#{attribute}=([^\s]+)/
46
+ attribute_element = " data-#{attribute}='#{$1}'"
47
+ end
48
+ attribute_element
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module Trickster
2
+ VERSION = '0.0.4'
3
+ end
data/lib/trickster.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'trickster/version'
2
+ require 'trickster/slide_parser'
3
+ require 'trickster/renderer/code_renderer'
4
+ require 'trickster/renderer/bullets_renderer'
5
+ require 'trickster/renderer/commandline_renderer'
6
+ require 'trickster/renderer/normal_renderer'
7
+ require 'trickster/renderer/image_renderer'
8
+ require 'trickster/slide_renderer'
9
+ require 'trickster/file_copier'
data/trickster.rdoc ADDED
@@ -0,0 +1,69 @@
1
+ == trickster - Bootstrap, generate, and manage your trickster presentation
2
+
3
+ v0.0.4
4
+
5
+ === Global Options
6
+ === --help
7
+ Show this message
8
+
9
+
10
+
11
+
12
+ === --version
13
+
14
+
15
+
16
+
17
+
18
+ === Commands
19
+ ==== build [slides_file]
20
+ Generate the presentation from your slides
21
+
22
+
23
+ Given your slides_file (defaults to 'slides' in current directory), generates an HTML
24
+ file (based on --output-file) that, when loaded, can be used to play your presentation.
25
+
26
+ Any .scss files found in the css/ directory will be run through SASS to be converted.
27
+
28
+ Note that currently, the CSS and JS files included are fixed, so keep all your customizations
29
+ to styles.css/styles.scss and custom.js (these files will not be overwritten when
30
+ you do a trickster update).
31
+ ===== Options
32
+ ===== -o file
33
+
34
+ Name of the generated HTML file
35
+
36
+ [Aliases] --output-file
37
+ [Default Value] index.html
38
+
39
+
40
+ ==== help command
41
+ Shows a list of commands or help for one command
42
+
43
+
44
+ Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
45
+ ==== init dir_name
46
+ Create a new trickster presentation
47
+
48
+
49
+ This will create a completely self-contained presentation that can be run just from the files
50
+ in the directory you name. It will have a static copy of the JavaScript files, JavaScript third-party
51
+ libraries and CSS files you need to run your trickster application.
52
+
53
+ You can examine the file `slides` to see some examples of how to format your
54
+ presentation. You can run `trickster build` in the directory to create the HTML
55
+ file from your slides.
56
+
57
+ Note that if you upgrade trickster, you should run `tricksterd update` in your slide directory
58
+ to get the latest JavaScript and CSS files.
59
+ ==== update [dir_name]
60
+ Update an existing slideshow with the latest Trickster JS/CSS files
61
+
62
+
63
+ Run this after you've upgraded trickster and want to get the latest features. Since your
64
+ slideshow is entirely self-contained, when you upgrade tricksterd, your static JavaScript and
65
+ CSS files will be out of date.
66
+
67
+ Note that `styles.css`, which was provided by trickster as a basis for styling, will not
68
+ be touched, and your customizations will remain. All other files that trickster gave you
69
+ when you ran `trickster init` will be overwritten.