jacktang-hacker-slides 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Jack Tang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,58 @@
1
+ h1. Story About HackerSlides
2
+
3
+ Well, I am one not-well-paid programmer undex linux and far far way from such amazing presentation software -- Apple's keynotes. And I meet some other hackers weekly and exchange the industry news and ideas, of course, by presentation. Meanwhile,I'd like my presentation can be pushed to the git repository so that I might refine some great ideas step by step. I am familiar with S5, but tired to write HTML code. To me, Textile syntax is easy to me. So the project launched...
4
+
5
+ h1. Install It!
6
+
7
+ * Install from source
8
+
9
+ <pre>
10
+ git clone git://github.com/jacktang/hacker-slides.git
11
+ rake install
12
+ </pre>
13
+
14
+ * Install through Gem directory
15
+
16
+ <pre>
17
+ gem sources -a http://gems.github.com
18
+ sudo gem install sudo gem install jacktang-hacker-slides
19
+ </pre>
20
+
21
+ h1. Sample Slides
22
+
23
+ * "HackerSlides":http://jacktang.github.com/hackerslides/sample/
24
+
25
+ h1. Amazing? Use It
26
+
27
+ * Create slides from sketch
28
+
29
+ hacker-slides --sketch sample.textile
30
+
31
+ * Convert Textile to S5 slides
32
+
33
+ hacker-slides sample.textile -o slides -b s5-simple
34
+
35
+ * List all bundles/themes
36
+
37
+ hacker-slides -l
38
+
39
+
40
+ h1. Emacs & HackerSlides
41
+
42
+ * Textile mode for Emacs (todo)
43
+ * Generate slide in Emacs (todo)
44
+
45
+
46
+
47
+ h1. Always Thanks Designers
48
+
49
+ * "S5: iBook-compatible themes":http://www.ibiblio.org/mcmahon/s5-iBook/index.html
50
+ * "Presentations S5":http://www.jesusda.com/projects/presentaciones_s5
51
+ * "Windgazer styles":http://www.windgazer.nl/projects/jsrepository/S5.wgd/
52
+ * "Glossdeck, A New Presentation Theme for S5":http://blog.jm3.net/2007/03/18/glossdeck-a-presentation-theme/
53
+ * "Microformats":http://theryanking.com/presentations/
54
+ * http://freshfoo.com/presentations/PyCon-Italia-Due/slides/
55
+ * http://www.forusers.com/static/html/sample.html
56
+ * http://alice.wu-wien.ac.at:8000/s5/presentation?slideshow=1&style=flower
57
+ * http://atuan.com/s5/
58
+ * http://python.net/~goodger/projects/pycon/2007/idiomatic/presentation.html
data/bin/hacker-slides ADDED
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'ostruct'
5
+ require 'optparse'
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
8
+
9
+ require 'highline/import'
10
+ require 'hacker_slides'
11
+
12
+ module HackerSlides
13
+ class Command
14
+
15
+ def run(args)
16
+
17
+ opts = OpenStruct.new
18
+
19
+ opt_parser = OptionParser.new do |cmd|
20
+
21
+ cmd.banner = "Usage: slideshow [options] name"
22
+
23
+ cmd.on( '-o', '--output PATH', 'Output Path' ) do |path|
24
+ opts.output = path
25
+ end
26
+
27
+ cmd.on( "-b", "--bundle NAME", "Template Bundle" ) do |name|
28
+ opts.bundle = name
29
+ end
30
+
31
+ cmd.on( "-s", "--sketch", "Create Slides From Sketch" ) do |sketch|
32
+ opts.sketch = true
33
+ end
34
+
35
+ cmd.on( '-f', '--fetch URI', 'Fetch Bundle' ) do |url|
36
+ opts.fetch_uri = url
37
+ end
38
+
39
+ cmd.on( '-l', '--list', 'List Installed Bundles' ) do
40
+ opts.list = true
41
+ end
42
+
43
+ cmd.on( '-r', '--remote URL', 'Remote Bundle Source' ) do |remote_src|
44
+ opts.remote_src = remote_src
45
+ end
46
+
47
+ cmd.on( '-i', '--install BUNDLE', 'Bundle to Install' ) do |install_bundle|
48
+ opts.install_bundle = install_bundle
49
+ end
50
+
51
+
52
+ # todo: find different letter for debug trace switch (use v for version?)
53
+ cmd.on( "-v", "--verbose", "Show debug trace" ) do
54
+ #logger.datetime_format = "%H:%H:%S"
55
+ #logger.level = Logger::DEBUG
56
+ end
57
+
58
+ cmd.on_tail( "-h", "--help", "Show this message" ) do
59
+ puts "HackerSlides: dead simple slides creator for lazy hackers"
60
+ puts
61
+ puts cmd.help
62
+ puts
63
+ puts "Examples:"
64
+ puts " hacker-slides -S sample.textile"
65
+ puts " hacker-slides -o slides sample.textile"
66
+ puts " hacker-slides -b s5-simple sample.textile"
67
+ puts " hacker-slides -l"
68
+ puts " hacker-slides -s http://example.com/bundles/ -i s5-simple"
69
+ puts
70
+ puts "Further information:"
71
+ puts " http://www.github.com/jacktang/hacker-slides"
72
+ puts
73
+ exit
74
+ end
75
+ end
76
+
77
+ opt_parser.parse!( args )
78
+
79
+ puts "HackerSlides Version: #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
80
+
81
+ if opts.list
82
+ builtin_bundles, usr_bundles = HackerSlides::Bundle.list_bundles
83
+
84
+ if(not usr_bundles.empty?)
85
+ puts
86
+ puts 'User bundles:'
87
+ usr_bundles.each do |bundle|
88
+ puts " * #{bundle}"
89
+ end
90
+ end
91
+ puts
92
+ puts 'Built-in bundles:'
93
+ builtin_bundles.each do |bundle|
94
+ puts " * #{bundle}"
95
+ end
96
+ puts
97
+ elsif opts.install_bundle
98
+ src = opts.remote_src || './'
99
+ bundle = opts.install_bundle
100
+ puts "install #{bundle.inspect} bundle from #{src}"
101
+ HackerSlides::Bundle.install(src, bundle)
102
+
103
+ elsif opts.sketch
104
+ engine = HackerSlides::SlidesEngine.new
105
+ args.each do |arg|
106
+ file = File.expand_path(arg)
107
+ if(! File.exist?(file) || (File.exist?(file) &&
108
+ agree("This task will overwrite the #{arg} file. Are you sure you want to continue? [yn] ")))
109
+ engine.sketch_slides(arg)
110
+ puts "create #{arg} from sketch"
111
+ puts
112
+ end
113
+ end
114
+ else
115
+ engine = HackerSlides::SlidesEngine.new
116
+ bundle_name = opts.bundle || 's5-simple'
117
+ output_dir = opts.output_dir || 'slides/'
118
+
119
+ args.each do |arg|
120
+ bundle_category = bundle_name.split('-').first
121
+ if(bundle_category == 's5')
122
+ HackerSlides::SlidesEngine.send(:include, HackerSlides::S5SlidesGenerator)
123
+ end
124
+ start_here = engine.create_slides(arg, bundle_name, output_dir)
125
+ if(start_here)
126
+ puts "slides are created"
127
+ puts "please check the link: #{start_here}"
128
+ puts
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+
137
+ HackerSlides::Command.new.run(ARGV)
Binary file
@@ -0,0 +1,2 @@
1
+ require 'hacker_slides/bundle'
2
+ require 'hacker_slides/engine'
@@ -0,0 +1,114 @@
1
+ require 'rubygems'
2
+ require 'zlib'
3
+ require 'open-uri'
4
+ require 'fileutils'
5
+
6
+ require 'minitar'
7
+
8
+ module HackerSlides
9
+ class Bundle
10
+ class << self
11
+
12
+ # install remote/local bundle
13
+ def install(src, bundle)
14
+
15
+ bundle = "#{bundle}.bundle" if(File.extname(bundle) != '.bundle')
16
+
17
+ if(src)
18
+ bundle_path = File.join(src, bundle)
19
+ else
20
+ bundle_path = File.expand_path(bundle)
21
+ end
22
+ dest = File.join(usr_bundle_dir, bundle)
23
+ open(bundle_path) do |stream|
24
+ File.open(dest, 'wb') do |f|
25
+ f.write(stream.read)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ def list_bundles
32
+ builtin_bundles = []
33
+ usr_bundles = []
34
+
35
+ Dir.glob(File.join(usr_bundle_dir, '*')).each do |bundle|
36
+ bundle_name = File.basename(bundle, '.*')
37
+ usr_bundles << bundle_name
38
+ end
39
+
40
+ Dir.glob(File.join(builtin_bundle_dir, '*')).each do |bundle|
41
+ bundle_name = File.basename(bundle, '.*')
42
+ builtin_bundles << bundle_name
43
+ end
44
+ return builtin_bundles, usr_bundles
45
+ end
46
+
47
+ def builtin_bundle_dir
48
+ return File.expand_path(File.dirname(__FILE__) + '/../../bundles')
49
+ end
50
+
51
+ def usr_bundle_dir
52
+ bundle_dir = File.join(cache_dir, 'bundles')
53
+ unless File.exists?(bundle_dir)
54
+ FileUtils.mkdir(bundle_dir)
55
+ end
56
+ return bundle_dir
57
+ end
58
+
59
+ def lookup_bundle_path(bundle_name)
60
+ bundle = File.join(usr_bundle_dir, "#{bundle_name}.bundle")
61
+ if(File.exists?(bundle))
62
+ return bundle
63
+ else
64
+ bundle = File.join(builtin_bundle_dir, "#{bundle_name}.bundle")
65
+ return bundle if(File.exists?(bundle))
66
+ end
67
+ end
68
+
69
+ def cache_dir
70
+ if(PLATFORM =~ /win32/)
71
+ dir = File.join(ENV['USERPROFILE'], '.hackerslides')
72
+ else
73
+ dir = File.join(File.expand_path("~"), ".hackerslides")
74
+ end
75
+
76
+ unless File.exist?(dir)
77
+ FileUtils.mkdir(dir)
78
+ end
79
+ return dir
80
+ end
81
+
82
+ def extract_files_from_bundle(bundle, files, dest)
83
+ Zlib::GzipReader.open(bundle) do |tgz|
84
+ Archive::Tar::Minitar.unpack(tgz, dest, files)
85
+ end
86
+ end
87
+
88
+ def load_manifest(bundle)
89
+ Zlib::GzipReader.open(bundle) do |tgz|
90
+ Archive::Tar::Minitar::Input.open(tgz) do |stream|
91
+ stream.each do |entry|
92
+ if(entry.name == 'MANIFEST')
93
+ manifest = []
94
+ entry.read.split(/\n/).each_with_index do |line,i|
95
+ case line
96
+ when /^\s*$/
97
+ # skip empty lines
98
+ when /^\s*#.*$/
99
+ # skip comment lines
100
+ else
101
+ manifest << line.strip
102
+ end
103
+ end
104
+ return manifest
105
+ end
106
+ end
107
+ end
108
+ end
109
+ return nil
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,5 @@
1
+ require 'hacker_slides/presentation'
2
+ require 'hacker_slides/engine/base'
3
+ require 'hacker_slides/engine/slides_engine'
4
+ require 'hacker_slides/engine/markup_engine'
5
+ require 'hacker_slides/engine/s5_slides_generator'
@@ -0,0 +1,6 @@
1
+ module HackerSlides
2
+ module Engine
3
+ class Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'redcloth'
3
+ require 'bluecloth'
4
+
5
+ module HackerSlides
6
+ class MarkupEngine < Engine::Base
7
+
8
+ def to_html(content)
9
+ raise NotImplementedError, 'implmenent the method in subclass'
10
+ end
11
+ end
12
+
13
+ class TextileMarkupEngine < MarkupEngine
14
+ def self.support_extnames
15
+ ['.textile', '.t']
16
+ end
17
+
18
+ def to_html(content)
19
+ # turn off hard line breaks
20
+ # turn off span caps (see http://rubybook.ca/2008/08/16/redcloth)
21
+ red = RedCloth.new(content, [:no_span_caps])
22
+ red.hard_breaks = false
23
+ return red.to_html
24
+ end
25
+ end
26
+
27
+ class MarkdownMarkupEngine < MarkupEngine
28
+ def self.support_extnames
29
+ ['.markdown', '.m', '.mark', '.mkdn', '.md', '.txt', '.text']
30
+ end
31
+
32
+ def to_html(content)
33
+ return BlueCloth.new( content ).to_html
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ module HackerSlides
2
+ module Engine
3
+ class RenderEngine
4
+
5
+ end
6
+
7
+ class ERbRenderEngine < RenderEngine
8
+
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,33 @@
1
+ module HackerSlides
2
+ module S5SlidesGenerator
3
+
4
+ def post_processing(content)
5
+ # create s5 slides
6
+ slide_counter = 0
7
+ result = ''
8
+
9
+ company = @presentation.meta[:company]
10
+ website = @presentation.meta[:website]
11
+
12
+ preface = "<div class=\"slide\">\n"
13
+ preface << "<h1>#{@presentation.title}</h1>\n"
14
+ preface << "<h3>#{@presentation.author}</h3>\n"
15
+ preface << "<h4><a href=\"#{website}\">#{company}</a></h4>" if(company)
16
+ preface << "</div>"
17
+
18
+ result << preface
19
+
20
+ # wrap h1's in slide divs; note use just <h1 since some processors add ids e.g. <h1 id='x'>
21
+ content.each_line do |line|
22
+ if line.include?( '<h1' ) then
23
+ result << "\n\n</div>" if slide_counter > 0
24
+ result << "<div class='slide'>\n"
25
+ slide_counter += 1
26
+ end
27
+ result << line
28
+ end
29
+ result << "\n</div>" if slide_counter > 0
30
+ return result
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,157 @@
1
+ require 'rubygems'
2
+ require 'erb'
3
+ require 'fileutils'
4
+ require 'highline/import'
5
+
6
+ require 'hacker_slides/bundle'
7
+ require 'hacker_slides/helper'
8
+ require 'hacker_slides/engine/base'
9
+ require 'hacker_slides/engine/markup_engine'
10
+
11
+ module HackerSlides
12
+ class SlidesEngine < Engine::Base
13
+ include HackerSlides::ContentHelper
14
+
15
+ attr_reader :presentation
16
+
17
+ # Create one SlidesEngine instance
18
+ #
19
+ def initialize
20
+ @presentation = HackerSlides::Presentation.new
21
+ end
22
+
23
+
24
+ #
25
+ #
26
+ def create_slides(input, bundle_name, output_dir)
27
+
28
+ dirname = File.dirname(input)
29
+ basename = File.basename(input, '.*')
30
+ extname = File.extname(input)
31
+
32
+ input_file = File.expand_path(input)
33
+ if(not File.exist?(input_file))
34
+ if(agree("Could you like to create the presentation from sketch [yn] "))
35
+ sketch_slides(input_file)
36
+ puts "generate #{input_file} from sketch"
37
+ return
38
+ end
39
+ end
40
+
41
+ output_dir = File.expand_path(File.join(output_dir, basename))
42
+ FileUtils.mkdir_p(output_dir) unless File.directory?(output_dir)
43
+
44
+ bundle_path = HackerSlides::Bundle.lookup_bundle_path(bundle_name)
45
+
46
+ manifest = HackerSlides::Bundle.load_manifest(bundle_path)
47
+
48
+ content_with_metas = File.read(input_file)
49
+
50
+ # read source document; split off optional header from source
51
+ # strip leading optional headers (key/value pairs) including optional empty lines
52
+ read_metas = true
53
+ content = ''
54
+
55
+ content_with_metas.each do |line|
56
+ if read_metas && line =~ /^\s*(\w[\w-]*)[ \t]*:[ \t]*(.*)/
57
+ name = $1.downcase
58
+ value = $2.strip
59
+ if(name == 'title')
60
+ @presentation.title = value
61
+ else
62
+ @presentation.meta[name.to_sym] = value
63
+ end
64
+ elsif line =~ /^\s*$/
65
+ content << line unless read_metas
66
+ else
67
+ read_metas = false
68
+ content << line
69
+ end
70
+ end
71
+
72
+ # ruby note: .*? is non-greedy (shortest-possible) regex match
73
+ content.gsub!(/__SKIP__.*?__END__/m, '')
74
+ content.sub!(/__END__.*/m, '')
75
+
76
+ # you can use the meta
77
+ content = ERB.new(content).result(binding)
78
+
79
+ case markup_type(extname)
80
+ when :textile
81
+ markup_engine = HackerSlides::TextileMarkupEngine.new
82
+ when :markdown
83
+ markup_engine = HackerSlides::MarkdownMarkupEngine.new
84
+ else
85
+ puts "only textile and markdown markup supported"
86
+ puts "#{extname.inspect} doesn't support util now."
87
+ end
88
+
89
+ content = markup_engine.to_html(content) if(markup_engine)
90
+
91
+ # implement in
92
+ slides = post_processing(content) if defined?(:post_processing)
93
+
94
+ start_here = nil
95
+ static_files = []
96
+
97
+ # copy manifest files
98
+ manifest.each do |file|
99
+ if(File.extname(file) == '.erb')
100
+ template_file = File.basename(file)
101
+ if(template_file == 'template.html.erb')
102
+ outname = 'index.html'
103
+ start_here = with_output_path(outname, output_dir)
104
+ else
105
+ outname = File.basename(file, '.*')
106
+ end
107
+ File.open(with_output_path(outname, output_dir), "w+") do |out|
108
+ Zlib::GzipReader.open(bundle_path) do |tgz|
109
+ Archive::Tar::Minitar::Input.open(tgz) do |stream|
110
+ stream.each { |entry| out.puts render_erb_template(entry.read, binding) if(entry.full_name == file )}
111
+ end
112
+ end
113
+ end
114
+ else
115
+ static_files << file
116
+ end
117
+
118
+ HackerSlides::Bundle.extract_files_from_bundle(bundle_path, static_files, output_dir)
119
+ end
120
+
121
+ return start_here
122
+ end
123
+
124
+ def sketch_slides(input_file)
125
+ extname = File.extname(input_file)
126
+ case markup_type(extname)
127
+ when :textile
128
+ sample_file = File.join(File.dirname(__FILE__) + '/../../../samples/sample.textile')
129
+ when :markdown
130
+ sample_file = File.join(File.dirname(__FILE__) + '/../../../samples/sample.markdown')
131
+ else
132
+ puts "only textile and markdown markup supported"
133
+ puts "#{extname.inspect} doesn't support util now."
134
+ end
135
+ FileUtils.cp(sample_file, input_file)
136
+ end
137
+
138
+ def markup_type(extname)
139
+ if(HackerSlides::TextileMarkupEngine.support_extnames.include?(extname))
140
+ return :textile
141
+ end
142
+
143
+ if(HackerSlides::MarkdownMarkupEngine.support_extnames.include?(extname))
144
+ return :markdown
145
+ end
146
+ end
147
+
148
+ def with_output_path(output_file, output_dir)
149
+ return File.expand_path(File.join(output_dir, output_file))
150
+ end
151
+
152
+ def render_erb_template(content, the_binding)
153
+ ERB.new(content).result(the_binding)
154
+ end
155
+
156
+ end
157
+ end