trickster 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.