goncalossilva-showoff 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +473 -0
- data/Rakefile +7 -0
- data/bin/showoff +136 -0
- data/lib/princely.rb +84 -0
- data/lib/showoff.rb +418 -0
- data/lib/showoff_utils.rb +361 -0
- data/public/css/fg.menu.css +114 -0
- data/public/css/onepage.css +60 -0
- data/public/css/pdf.css +12 -0
- data/public/css/reset.css +53 -0
- data/public/css/sh_style.css +66 -0
- data/public/css/showoff.css +350 -0
- data/public/css/spinner_bar.gif +0 -0
- data/public/css/theme/images/ui-bg_diagonals-small_100_f0efea_40x40.png +0 -0
- data/public/css/theme/images/ui-bg_flat_35_f0f0f0_40x100.png +0 -0
- data/public/css/theme/images/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
- data/public/css/theme/images/ui-bg_glow-ball_25_2e2e28_600x600.png +0 -0
- data/public/css/theme/images/ui-bg_highlight-soft_100_f0efea_1x100.png +0 -0
- data/public/css/theme/images/ui-bg_highlight-soft_25_327E04_1x100.png +0 -0
- data/public/css/theme/images/ui-bg_highlight-soft_25_5A9D1A_1x100.png +0 -0
- data/public/css/theme/images/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
- data/public/css/theme/images/ui-bg_inset-soft_22_3b3b35_1x100.png +0 -0
- data/public/css/theme/images/ui-icons_808080_256x240.png +0 -0
- data/public/css/theme/images/ui-icons_8DC262_256x240.png +0 -0
- data/public/css/theme/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/public/css/theme/images/ui-icons_e7e6e4_256x240.png +0 -0
- data/public/css/theme/images/ui-icons_eeeeee_256x240.png +0 -0
- data/public/css/theme/images/ui-icons_ffffff_256x240.png +0 -0
- data/public/css/theme/ui.accordion.css +9 -0
- data/public/css/theme/ui.all.css +2 -0
- data/public/css/theme/ui.base.css +9 -0
- data/public/css/theme/ui.core.css +37 -0
- data/public/css/theme/ui.datepicker.css +62 -0
- data/public/css/theme/ui.dialog.css +13 -0
- data/public/css/theme/ui.progressbar.css +4 -0
- data/public/css/theme/ui.resizable.css +13 -0
- data/public/css/theme/ui.slider.css +17 -0
- data/public/css/theme/ui.tabs.css +9 -0
- data/public/css/theme/ui.theme.css +245 -0
- data/public/favicon.ico +0 -0
- data/public/js/core.js +79 -0
- data/public/js/fg.menu.js +645 -0
- data/public/js/jTypeWriter.js +26 -0
- data/public/js/jquery-1.4.2.min.js +154 -0
- data/public/js/jquery-print.js +109 -0
- data/public/js/jquery.batchImageLoad.js +56 -0
- data/public/js/jquery.cookie.js +96 -0
- data/public/js/jquery.cycle.all.js +1284 -0
- data/public/js/jquery.doubletap-0.1.js +105 -0
- data/public/js/jquery.uuid.js +24 -0
- data/public/js/jquery.ws-0.3pre.js +201 -0
- data/public/js/onepage.js +5 -0
- data/public/js/sh_lang/sh_bison.min.js +1 -0
- data/public/js/sh_lang/sh_c.min.js +1 -0
- data/public/js/sh_lang/sh_caml.min.js +1 -0
- data/public/js/sh_lang/sh_changelog.min.js +1 -0
- data/public/js/sh_lang/sh_cpp.min.js +1 -0
- data/public/js/sh_lang/sh_csharp.min.js +1 -0
- data/public/js/sh_lang/sh_css.min.js +1 -0
- data/public/js/sh_lang/sh_cucumber.min.js +2 -0
- data/public/js/sh_lang/sh_desktop.min.js +1 -0
- data/public/js/sh_lang/sh_diff.min.js +1 -0
- data/public/js/sh_lang/sh_flex.min.js +1 -0
- data/public/js/sh_lang/sh_glsl.min.js +1 -0
- data/public/js/sh_lang/sh_haxe.min.js +1 -0
- data/public/js/sh_lang/sh_html.min.js +1 -0
- data/public/js/sh_lang/sh_java.min.js +1 -0
- data/public/js/sh_lang/sh_javascript.min.js +1 -0
- data/public/js/sh_lang/sh_javascript_dom.min.js +1 -0
- data/public/js/sh_lang/sh_latex.min.js +1 -0
- data/public/js/sh_lang/sh_ldap.min.js +1 -0
- data/public/js/sh_lang/sh_log.min.js +1 -0
- data/public/js/sh_lang/sh_lsm.min.js +1 -0
- data/public/js/sh_lang/sh_m4.min.js +1 -0
- data/public/js/sh_lang/sh_makefile.min.js +1 -0
- data/public/js/sh_lang/sh_oracle.min.js +1 -0
- data/public/js/sh_lang/sh_pascal.min.js +1 -0
- data/public/js/sh_lang/sh_perl.min.js +1 -0
- data/public/js/sh_lang/sh_php.min.js +1 -0
- data/public/js/sh_lang/sh_prolog.min.js +1 -0
- data/public/js/sh_lang/sh_properties.min.js +1 -0
- data/public/js/sh_lang/sh_python.min.js +1 -0
- data/public/js/sh_lang/sh_ruby.min.js +1 -0
- data/public/js/sh_lang/sh_scala.min.js +1 -0
- data/public/js/sh_lang/sh_sh.min.js +1 -0
- data/public/js/sh_lang/sh_slang.min.js +1 -0
- data/public/js/sh_lang/sh_sml.min.js +1 -0
- data/public/js/sh_lang/sh_spec.min.js +1 -0
- data/public/js/sh_lang/sh_sql.min.js +1 -0
- data/public/js/sh_lang/sh_tcl.min.js +1 -0
- data/public/js/sh_lang/sh_xml.min.js +1 -0
- data/public/js/sh_lang/sh_xorg.min.js +1 -0
- data/public/js/sh_main.min.js +4 -0
- data/public/js/showoff.js +629 -0
- data/public/js/showoffcore.js +13 -0
- data/views/index.erb +81 -0
- data/views/onepage.erb +35 -0
- metadata +217 -0
data/Rakefile
ADDED
data/bin/showoff
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
require 'showoff'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'gli'
|
7
|
+
|
8
|
+
include GLI
|
9
|
+
|
10
|
+
version ShowOff::Version
|
11
|
+
|
12
|
+
desc 'Create new showoff presentation'
|
13
|
+
arg_name 'dir_name'
|
14
|
+
long_desc 'This command helps start a new showoff presentation by setting up the proper directory structure for you. It takes the directory name you would like showoff to create for you.'
|
15
|
+
command [:create,:init] do |c|
|
16
|
+
|
17
|
+
c.desc 'Don''t create sample slides'
|
18
|
+
c.switch [:n,:nosamples]
|
19
|
+
|
20
|
+
c.desc 'sample slide directory name'
|
21
|
+
c.default_value 'one'
|
22
|
+
c.flag [:d,:slidedir]
|
23
|
+
|
24
|
+
c.action do |global_options,options,args|
|
25
|
+
raise "dir_name is required" if args.empty?
|
26
|
+
ShowOffUtils.create(args[0],!options[:n],options[:d])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Puts your showoff presentation into a gh-pages branch'
|
31
|
+
long_desc 'Generates a static version of your presentation into your gh-pages branch for publishing to GitHub Pages'
|
32
|
+
command :github do |c|
|
33
|
+
c.action do |global_options,options,args|
|
34
|
+
ShowOffUtils.github
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Serves the showoff presentation in the current directory'
|
39
|
+
desc 'Setup your presentation to serve on Heroku'
|
40
|
+
arg_name 'heroku_name'
|
41
|
+
long_desc 'Creates the Gemfile and config.ru file needed to push a showoff pres to heroku. It will then run ''heroku create'' for you to register the new project on heroku and add the remote for you. Then all you need to do is commit the new created files and run ''git push heroku'' to deploy.'
|
42
|
+
command :heroku do |c|
|
43
|
+
|
44
|
+
c.desc 'add password protection to your heroku site'
|
45
|
+
c.flag [:p,:password]
|
46
|
+
|
47
|
+
c.desc 'force overwrite of existing Gemfile/.gems and config.ru files if they exist'
|
48
|
+
c.switch [:f,:force]
|
49
|
+
|
50
|
+
c.desc 'Use older-style .gems file instead of bundler-style Gemfile'
|
51
|
+
c.switch [:g,:dotgems]
|
52
|
+
|
53
|
+
c.action do |global_options,options,args|
|
54
|
+
raise "heroku_name is required" if args.empty?
|
55
|
+
ShowOffUtils.heroku(args[0],options[:f],options[:p],options[:g])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Serves the showoff presentation in the current directory'
|
60
|
+
command :serve do |c|
|
61
|
+
|
62
|
+
c.desc 'Port on which to run'
|
63
|
+
c.default_value "9090"
|
64
|
+
c.flag [:p,:port]
|
65
|
+
|
66
|
+
c.desc 'Host or ip to run on'
|
67
|
+
c.default_value "localhost"
|
68
|
+
c.flag [:h,:host]
|
69
|
+
|
70
|
+
c.action do |global_options,options,args|
|
71
|
+
ShowOff.run! :host => options[:h], :port => options[:p].to_i
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
desc 'Add a new slide at the end in a given dir'
|
76
|
+
arg_name '[title]'
|
77
|
+
long_desc 'Outputs or creates a new slide. With -d and -n, a new slide is created in the given dir, numbered to appear as the last slide in that dir (use -u to avoid numbering). Without those, outputs the slide markdown to stdout (useful for shelling out from your editor). You may also specify a source file to use for a code slide'
|
78
|
+
command [:add,:new] do |c|
|
79
|
+
c.desc 'Don''t number the slide, use the given name verbatim'
|
80
|
+
c.switch [:u,:nonumber]
|
81
|
+
|
82
|
+
c.desc 'Include code from the given file as the slide body'
|
83
|
+
c.arg_name 'path to file'
|
84
|
+
c.flag [:s,:source]
|
85
|
+
|
86
|
+
c.desc 'Slide Type/Style'
|
87
|
+
c.arg_name 'valid showoff style/type'
|
88
|
+
c.default_value 'title'
|
89
|
+
c.flag [:t,:type,:style]
|
90
|
+
|
91
|
+
c.desc 'Slide dir (where to put a new slide file)'
|
92
|
+
c.arg_name 'dir'
|
93
|
+
c.flag [:d,:dir]
|
94
|
+
|
95
|
+
c.desc 'Slide name (name of the new slide file)'
|
96
|
+
c.arg_name 'basename'
|
97
|
+
c.flag [:n,:name]
|
98
|
+
|
99
|
+
c.action do |global_options,options,args|
|
100
|
+
title = args.join(" ")
|
101
|
+
ShowOffUtils.add_slide(:dir => options[:d],
|
102
|
+
:name => options[:n],
|
103
|
+
:title => title,
|
104
|
+
:number => !options[:u],
|
105
|
+
:code => options[:s],
|
106
|
+
:type => options[:t])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
desc 'Generate static version of presentation'
|
111
|
+
arg_name 'name'
|
112
|
+
long_desc 'Creates a static, one page version of the presentation as {name}.html'
|
113
|
+
command [:static] do |c|
|
114
|
+
c.action do |global_options,options,args|
|
115
|
+
ShowOff.do_static(args[0])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
pre do |global,command,options,args|
|
120
|
+
# Pre logic here
|
121
|
+
# Return true to proceed; false to abourt and not call the
|
122
|
+
# chosen command
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
post do |global,command,options,args|
|
127
|
+
# Post logic here
|
128
|
+
end
|
129
|
+
|
130
|
+
on_error do |exception|
|
131
|
+
# Error logic here
|
132
|
+
# return false to skip default error handling
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
136
|
+
exit GLI.run(ARGV)
|
data/lib/princely.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# PrinceXML Ruby interface.
|
2
|
+
# http://www.princexml.com
|
3
|
+
#
|
4
|
+
# Library by Subimage Interactive - http://www.subimage.com
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# USAGE
|
8
|
+
# -----------------------------------------------------------------------------
|
9
|
+
# princely = Princely.new()
|
10
|
+
# html_string = render_to_string(:template => 'some_document')
|
11
|
+
# send_data(
|
12
|
+
# princely.pdf_from_string(html_string),
|
13
|
+
# :filename => 'some_document.pdf'
|
14
|
+
# :type => 'application/pdf'
|
15
|
+
# )
|
16
|
+
#
|
17
|
+
$:.unshift(File.dirname(__FILE__))
|
18
|
+
|
19
|
+
class Princely
|
20
|
+
VERSION = "1.0.0" unless const_defined?("VERSION")
|
21
|
+
|
22
|
+
attr_accessor :exe_path, :style_sheets, :log_file
|
23
|
+
|
24
|
+
# Initialize method
|
25
|
+
#
|
26
|
+
def initialize()
|
27
|
+
# Finds where the application lives, so we can call it.
|
28
|
+
@exe_path = `which prince`.chomp
|
29
|
+
raise "Cannot find prince command-line app in $PATH" if @exe_path.length == 0
|
30
|
+
@style_sheets = ''
|
31
|
+
@log_file = "/tmp/prince.log"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sets stylesheets...
|
35
|
+
# Can pass in multiple paths for css files.
|
36
|
+
#
|
37
|
+
def add_style_sheets(*sheets)
|
38
|
+
for sheet in sheets do
|
39
|
+
@style_sheets << " -s #{sheet} "
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns fully formed executable path with any command line switches
|
44
|
+
# we've set based on our variables.
|
45
|
+
#
|
46
|
+
def exe_path
|
47
|
+
# Add any standard cmd line arguments we need to pass
|
48
|
+
@exe_path << " --input=html --server --log=#{@log_file} "
|
49
|
+
@exe_path << @style_sheets
|
50
|
+
return @exe_path
|
51
|
+
end
|
52
|
+
|
53
|
+
# Makes a pdf from a passed in string.
|
54
|
+
#
|
55
|
+
# Returns PDF as a stream, so we can use send_data to shoot
|
56
|
+
# it down the pipe using Rails.
|
57
|
+
#
|
58
|
+
def pdf_from_string(string, output_file = '-')
|
59
|
+
path = self.exe_path()
|
60
|
+
# Don't spew errors to the standard out...and set up to take IO
|
61
|
+
# as input and output
|
62
|
+
path << ' --silent - -o -'
|
63
|
+
|
64
|
+
# Actually call the prince command, and pass the entire data stream back.
|
65
|
+
pdf = IO.popen(path, "w+")
|
66
|
+
pdf.puts(string)
|
67
|
+
pdf.close_write
|
68
|
+
result = pdf.gets(nil)
|
69
|
+
pdf.close_read
|
70
|
+
return result
|
71
|
+
end
|
72
|
+
|
73
|
+
def pdf_from_string_to_file(string, output_file)
|
74
|
+
path = self.exe_path()
|
75
|
+
# Don't spew errors to the standard out...and set up to take IO
|
76
|
+
# as input and output
|
77
|
+
path << " --silent - -o '#{output_file}' >> '#{@log_file}' 2>> '#{@log_file}'"
|
78
|
+
|
79
|
+
# Actually call the prince command, and pass the entire data stream back.
|
80
|
+
pdf = IO.popen(path, "w+")
|
81
|
+
pdf.puts(string)
|
82
|
+
pdf.close
|
83
|
+
end
|
84
|
+
end
|
data/lib/showoff.rb
ADDED
@@ -0,0 +1,418 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'json'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
here = File.expand_path(File.dirname(__FILE__))
|
8
|
+
require "#{here}/showoff_utils"
|
9
|
+
require "#{here}/princely"
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'RMagick'
|
13
|
+
rescue LoadError
|
14
|
+
$stderr.puts 'image sizing disabled - install rmagick'
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'pdfkit'
|
19
|
+
rescue LoadError
|
20
|
+
$stderr.puts 'pdf generation disabled - install pdfkit'
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
require 'rdiscount'
|
25
|
+
rescue LoadError
|
26
|
+
require 'bluecloth'
|
27
|
+
Object.send(:remove_const,:Markdown)
|
28
|
+
Markdown = BlueCloth
|
29
|
+
end
|
30
|
+
require 'pp'
|
31
|
+
|
32
|
+
class ShowOff < Sinatra::Application
|
33
|
+
|
34
|
+
Version = VERSION = '0.4.2'
|
35
|
+
|
36
|
+
attr_reader :cached_image_size
|
37
|
+
|
38
|
+
set :views, File.dirname(__FILE__) + '/../views'
|
39
|
+
set :public, File.dirname(__FILE__) + '/../public'
|
40
|
+
set :pres_dir, 'example'
|
41
|
+
|
42
|
+
def initialize(app=nil)
|
43
|
+
super(app)
|
44
|
+
puts dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
45
|
+
if Dir.pwd == dir
|
46
|
+
options.pres_dir = dir + '/example'
|
47
|
+
@root_path = "."
|
48
|
+
else
|
49
|
+
options.pres_dir = Dir.pwd
|
50
|
+
@root_path = ".."
|
51
|
+
end
|
52
|
+
@cached_image_size = {}
|
53
|
+
puts options.pres_dir
|
54
|
+
@pres_name = options.pres_dir.split('/').pop
|
55
|
+
require_ruby_files
|
56
|
+
end
|
57
|
+
|
58
|
+
def require_ruby_files
|
59
|
+
Dir.glob("#{options.pres_dir}/*.rb").map { |path| require path }
|
60
|
+
end
|
61
|
+
|
62
|
+
helpers do
|
63
|
+
def load_section_files(section)
|
64
|
+
section = File.join(options.pres_dir, section)
|
65
|
+
files = Dir.glob("#{section}/**/*").sort
|
66
|
+
pp files
|
67
|
+
files
|
68
|
+
end
|
69
|
+
|
70
|
+
def css_files
|
71
|
+
Dir.glob("#{options.pres_dir}/*.css").map { |path| File.basename(path) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def js_files
|
75
|
+
Dir.glob("#{options.pres_dir}/*.js").map { |path| File.basename(path) }
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def preshow_files
|
80
|
+
Dir.glob("#{options.pres_dir}/_preshow/*").map { |path| File.basename(path) }.to_json
|
81
|
+
end
|
82
|
+
|
83
|
+
def process_markdown(name, content, static=false, pdf=false)
|
84
|
+
slides = content.split(/^<?!SLIDE/)
|
85
|
+
slides.delete('')
|
86
|
+
final = ''
|
87
|
+
if slides.size > 1
|
88
|
+
seq = 1
|
89
|
+
end
|
90
|
+
slides.each do |slide|
|
91
|
+
md = ''
|
92
|
+
# extract content classes
|
93
|
+
lines = slide.split("\n")
|
94
|
+
content_classes = lines.shift.strip.chomp('>').split rescue []
|
95
|
+
slide = lines.join("\n")
|
96
|
+
# add content class too
|
97
|
+
content_classes.unshift "content"
|
98
|
+
# extract transition, defaulting to none
|
99
|
+
transition = 'none'
|
100
|
+
content_classes.delete_if { |x| x =~ /^transition=(.+)/ && transition = $1 }
|
101
|
+
# extract id, defaulting to none
|
102
|
+
id = nil
|
103
|
+
content_classes.delete_if { |x| x =~ /^#([\w-]+)/ && id = $1 }
|
104
|
+
puts "id: #{id}" if id
|
105
|
+
puts "classes: #{content_classes.inspect}"
|
106
|
+
puts "transition: #{transition}"
|
107
|
+
# create html
|
108
|
+
md += "<div"
|
109
|
+
md += " id=\"#{id}\"" if id
|
110
|
+
md += " class=\"slide\" data-transition=\"#{transition}\">"
|
111
|
+
if seq
|
112
|
+
md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}/#{seq.to_s}\">\n"
|
113
|
+
seq += 1
|
114
|
+
else
|
115
|
+
md += "<div class=\"#{content_classes.join(' ')}\" ref=\"#{name}\">\n"
|
116
|
+
end
|
117
|
+
sl = Markdown.new(slide).to_html
|
118
|
+
sl = update_image_paths(name, sl, static, pdf)
|
119
|
+
md += sl
|
120
|
+
md += "</div>\n"
|
121
|
+
md += "</div>\n"
|
122
|
+
final += update_commandline_code(md)
|
123
|
+
final = update_p_classes(final)
|
124
|
+
end
|
125
|
+
final
|
126
|
+
end
|
127
|
+
|
128
|
+
# find any lines that start with a <p>.(something) and turn them into <p class="something">
|
129
|
+
def update_p_classes(markdown)
|
130
|
+
markdown.gsub(/<p>\.(.*?) /, '<p class="\1">')
|
131
|
+
end
|
132
|
+
|
133
|
+
def update_image_paths(path, slide, static=false, pdf=false)
|
134
|
+
paths = path.split('/')
|
135
|
+
paths.pop
|
136
|
+
path = paths.join('/')
|
137
|
+
replacement_prefix = static ?
|
138
|
+
( pdf ? %(img src="file://#{options.pres_dir}/#{path}) : %(img src="./file/#{path}) ) :
|
139
|
+
%(img src="/image/#{path})
|
140
|
+
slide.gsub(/img src=\"(.*?)\"/) do |s|
|
141
|
+
img_path = File.join(path, $1)
|
142
|
+
w, h = get_image_size(img_path)
|
143
|
+
src = %(#{replacement_prefix}/#{$1}")
|
144
|
+
if w && h
|
145
|
+
src << %( width="#{w}" height="#{h}")
|
146
|
+
end
|
147
|
+
src
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
if defined?(Magick)
|
152
|
+
def get_image_size(path)
|
153
|
+
if !cached_image_size.key?(path)
|
154
|
+
img = Magick::Image.ping(path).first
|
155
|
+
cached_image_size[path] = [img.columns, img.rows]
|
156
|
+
end
|
157
|
+
cached_image_size[path]
|
158
|
+
end
|
159
|
+
else
|
160
|
+
def get_image_size(path)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def update_commandline_code(slide)
|
165
|
+
html = Nokogiri::XML.parse(slide)
|
166
|
+
|
167
|
+
html.css('pre').each do |pre|
|
168
|
+
pre.css('code').each do |code|
|
169
|
+
out = code.text
|
170
|
+
lines = out.split("\n")
|
171
|
+
if lines.first[0, 3] == '@@@'
|
172
|
+
lang = lines.shift.gsub('@@@', '').strip
|
173
|
+
pre.set_attribute('class', 'sh_' + lang.downcase)
|
174
|
+
code.content = lines.join("\n")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
html.css('.commandline > pre > code').each do |code|
|
180
|
+
out = code.text
|
181
|
+
lines = out.split(/^\$(.*?)$/)
|
182
|
+
lines.delete('')
|
183
|
+
code.content = ''
|
184
|
+
while(lines.size > 0) do
|
185
|
+
command = lines.shift
|
186
|
+
result = lines.shift
|
187
|
+
c = Nokogiri::XML::Node.new('code', html)
|
188
|
+
c.set_attribute('class', 'command')
|
189
|
+
c.content = '$' + command
|
190
|
+
code << c
|
191
|
+
c = Nokogiri::XML::Node.new('code', html)
|
192
|
+
c.set_attribute('class', 'result')
|
193
|
+
c.content = result
|
194
|
+
code << c
|
195
|
+
end
|
196
|
+
end
|
197
|
+
html.root.to_s
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_slides_html(static=false, pdf=false)
|
201
|
+
sections = ShowOffUtils.showoff_sections(options.pres_dir)
|
202
|
+
files = []
|
203
|
+
if sections
|
204
|
+
sections.each do |section|
|
205
|
+
files << load_section_files(section)
|
206
|
+
end
|
207
|
+
files = files.flatten
|
208
|
+
files = files.select { |f| f =~ /.md$/ }
|
209
|
+
data = ''
|
210
|
+
files.each do |f|
|
211
|
+
fname = f.gsub(options.pres_dir + '/', '').gsub('.md', '')
|
212
|
+
data += process_markdown(fname, File.read(f), static, pdf)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
data
|
216
|
+
end
|
217
|
+
|
218
|
+
def inline_css(csses, pre = nil)
|
219
|
+
css_content = '<style type="text/css">'
|
220
|
+
csses.each do |css_file|
|
221
|
+
if pre
|
222
|
+
css_file = File.join(File.dirname(__FILE__), '..', pre, css_file)
|
223
|
+
else
|
224
|
+
css_file = File.join(options.pres_dir, css_file)
|
225
|
+
end
|
226
|
+
css_content += File.read(css_file)
|
227
|
+
end
|
228
|
+
css_content += '</style>'
|
229
|
+
css_content
|
230
|
+
end
|
231
|
+
|
232
|
+
def inline_js(jses, pre = nil)
|
233
|
+
js_content = '<script type="text/javascript">'
|
234
|
+
jses.each do |js_file|
|
235
|
+
if pre
|
236
|
+
js_file = File.join(File.dirname(__FILE__), '..', pre, js_file)
|
237
|
+
else
|
238
|
+
js_file = File.join(options.pres_dir, js_file)
|
239
|
+
end
|
240
|
+
js_content += File.read(js_file)
|
241
|
+
end
|
242
|
+
js_content += '</script>'
|
243
|
+
js_content
|
244
|
+
end
|
245
|
+
|
246
|
+
def inline_all_js(jses_directory)
|
247
|
+
inline_js(Dir.entries(File.join(File.dirname(__FILE__), '..', jses_directory)).find_all{|filename| filename.length > 2 }, jses_directory)
|
248
|
+
end
|
249
|
+
|
250
|
+
def index(static=false)
|
251
|
+
if static
|
252
|
+
@title = ShowOffUtils.showoff_title
|
253
|
+
@slides = get_slides_html(static)
|
254
|
+
@asset_path = "./"
|
255
|
+
end
|
256
|
+
erb :index
|
257
|
+
end
|
258
|
+
|
259
|
+
def clean_link(href)
|
260
|
+
if href && href[0, 1] == '/'
|
261
|
+
href = href[1, href.size]
|
262
|
+
end
|
263
|
+
href
|
264
|
+
end
|
265
|
+
|
266
|
+
def assets_needed
|
267
|
+
assets = ["index", "slides"]
|
268
|
+
|
269
|
+
index = erb :index
|
270
|
+
html = Nokogiri::XML.parse(index)
|
271
|
+
html.css('head link').each do |link|
|
272
|
+
href = clean_link(link['href'])
|
273
|
+
assets << href if href
|
274
|
+
end
|
275
|
+
html.css('head script').each do |link|
|
276
|
+
href = clean_link(link['src'])
|
277
|
+
assets << href if href
|
278
|
+
end
|
279
|
+
|
280
|
+
slides = get_slides_html
|
281
|
+
html = Nokogiri::XML.parse("<slides>" + slides + "</slides>")
|
282
|
+
html.css('img').each do |link|
|
283
|
+
href = clean_link(link['src'])
|
284
|
+
assets << href if href
|
285
|
+
end
|
286
|
+
|
287
|
+
css = Dir.glob("#{options.public}/**/*.css").map { |path| path.gsub(options.public + '/', '') }
|
288
|
+
assets << css
|
289
|
+
|
290
|
+
js = Dir.glob("#{options.public}/**/*.js").map { |path| path.gsub(options.public + '/', '') }
|
291
|
+
assets << js
|
292
|
+
|
293
|
+
assets.uniq.join("\n")
|
294
|
+
end
|
295
|
+
|
296
|
+
def slides(static=false)
|
297
|
+
get_slides_html(static)
|
298
|
+
end
|
299
|
+
|
300
|
+
def onepage(static=false)
|
301
|
+
@slides = get_slides_html(static)
|
302
|
+
erb :onepage
|
303
|
+
end
|
304
|
+
|
305
|
+
def pdf(static=true)
|
306
|
+
@slides = get_slides_html(static, true)
|
307
|
+
@no_js = false
|
308
|
+
html = erb :onepage
|
309
|
+
# TODO make a random filename
|
310
|
+
|
311
|
+
# PDFKit.new takes the HTML and any options for wkhtmltopdf
|
312
|
+
# run `wkhtmltopdf --extended-help` for a full list of options
|
313
|
+
kit = PDFKit.new(html, :page_size => 'Letter', :orientation => 'Landscape')
|
314
|
+
|
315
|
+
# Save the PDF to a file
|
316
|
+
file = kit.to_file('/tmp/preso.pdf')
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
def self.do_static(what)
|
323
|
+
what = "index" if !what
|
324
|
+
|
325
|
+
# Nasty hack to get the actual ShowOff module
|
326
|
+
showoff = ShowOff.new
|
327
|
+
while !showoff.is_a?(ShowOff)
|
328
|
+
showoff = showoff.instance_variable_get(:@app)
|
329
|
+
end
|
330
|
+
name = showoff.instance_variable_get(:@pres_name)
|
331
|
+
path = showoff.instance_variable_get(:@root_path)
|
332
|
+
data = showoff.send(what, true)
|
333
|
+
if data.is_a?(File)
|
334
|
+
FileUtils.cp(data.path, "#{name}.pdf")
|
335
|
+
else
|
336
|
+
out = "#{path}/#{name}/static"
|
337
|
+
# First make a directory
|
338
|
+
FileUtils.makedirs(out)
|
339
|
+
# Then write the html
|
340
|
+
file = File.new("#{out}/index.html", "w")
|
341
|
+
file.puts(data)
|
342
|
+
file.close
|
343
|
+
# Now copy all the js and css
|
344
|
+
my_path = File.join( File.dirname(__FILE__), '..', 'public')
|
345
|
+
["js", "css"].each { |dir|
|
346
|
+
FileUtils.copy_entry("#{my_path}/#{dir}", "#{out}/#{dir}")
|
347
|
+
}
|
348
|
+
# And copy the directory
|
349
|
+
Dir.glob("#{my_path}/#{name}/*").each { |subpath|
|
350
|
+
base = File.basename(subpath)
|
351
|
+
next if "static" == base
|
352
|
+
next unless File.directory?(subpath) || base.match(/\.(css|js)$/)
|
353
|
+
FileUtils.copy_entry(subpath, "#{out}/#{base}")
|
354
|
+
}
|
355
|
+
|
356
|
+
# Set up file dir
|
357
|
+
file_dir = File.join(out, 'file')
|
358
|
+
FileUtils.makedirs(file_dir)
|
359
|
+
pres_dir = showoff.options.pres_dir
|
360
|
+
|
361
|
+
# ..., copy all user-defined styles and javascript files
|
362
|
+
Dir.glob("#{pres_dir}/*.{css,js}").each { |path|
|
363
|
+
FileUtils.copy(path, File.join(file_dir, File.basename(path)))
|
364
|
+
}
|
365
|
+
|
366
|
+
# ... and copy all needed image files
|
367
|
+
data.scan(/img src=\".\/file\/(.*?)\"/).flatten.each do |path|
|
368
|
+
dir = File.dirname(path)
|
369
|
+
FileUtils.makedirs(File.join(file_dir, dir))
|
370
|
+
FileUtils.copy(File.join(pres_dir, path), File.join(file_dir, path))
|
371
|
+
end
|
372
|
+
# copy images from css too
|
373
|
+
Dir.glob("#{pres_dir}/*.css").each do |css_path|
|
374
|
+
File.open(css_path) do |file|
|
375
|
+
data = file.read
|
376
|
+
data.scan(/url\((.*)\)/).flatten.each do |path|
|
377
|
+
p path
|
378
|
+
dir = File.dirname(path)
|
379
|
+
FileUtils.makedirs(File.join(file_dir, dir))
|
380
|
+
FileUtils.copy(File.join(pres_dir, path), File.join(file_dir, path))
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def eval_ruby code
|
388
|
+
eval(code).to_s
|
389
|
+
rescue => e
|
390
|
+
e.message
|
391
|
+
end
|
392
|
+
|
393
|
+
get '/eval_ruby' do
|
394
|
+
return eval_ruby(params[:code]) if ENV['SHOWOFF_EVAL_RUBY']
|
395
|
+
|
396
|
+
return "Ruby Evaluation is off. To turn it on set ENV['SHOWOFF_EVAL_RUBY']"
|
397
|
+
end
|
398
|
+
|
399
|
+
get %r{(?:image|file)/(.*)} do
|
400
|
+
path = params[:captures].first
|
401
|
+
full_path = File.join(options.pres_dir, path)
|
402
|
+
send_file full_path
|
403
|
+
end
|
404
|
+
|
405
|
+
get %r{/(.*)} do
|
406
|
+
@title = ShowOffUtils.showoff_title
|
407
|
+
what = params[:captures].first
|
408
|
+
what = 'index' if "" == what
|
409
|
+
if (what != "favicon.ico")
|
410
|
+
data = send(what)
|
411
|
+
if data.is_a?(File)
|
412
|
+
send_file data.path
|
413
|
+
else
|
414
|
+
data
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|