showoff 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/showoff_utils.rb CHANGED
@@ -1,40 +1,35 @@
1
1
  class ShowOffUtils
2
+ SHOWOFF_JSON_FILE = 'showoff.json'
2
3
 
3
- def self.create
4
- dirname = ARGV[1]
5
- return help('create') if !dirname
4
+ def self.create(dirname,create_samples,dir='one')
6
5
  Dir.mkdir(dirname) if !File.exists?(dirname)
7
6
  Dir.chdir(dirname) do
8
- # create section
9
- Dir.mkdir('one')
10
-
11
- # create markdown file
12
- File.open('one/slide.md', 'w+') do |f|
13
- f.puts "!SLIDE"
14
- f.puts "# My Presentation #"
15
- f.puts
16
- f.puts "!SLIDE bullets incremental"
17
- f.puts "# Bullet Points #"
18
- f.puts
19
- f.puts "* first point"
20
- f.puts "* second point"
21
- f.puts "* third point"
7
+ if create_samples
8
+ # create section
9
+ Dir.mkdir(dir)
10
+
11
+ # create markdown file
12
+ File.open("#{dir}/01_slide.md", 'w+') do |f|
13
+ f.puts make_slide("My Presentation")
14
+ f.puts make_slide("Bullet Points","bullets incremental",["first point","second point","third point"])
15
+ end
22
16
  end
23
17
 
24
18
  # create showoff.json
25
- File.open('showoff.json', 'w+') do |f|
26
- f.puts '[ {"section":"one"} ]'
19
+ File.open(SHOWOFF_JSON_FILE, 'w+') do |f|
20
+ f.puts "[ {\"section\":\"#{dir}\"} ]"
27
21
  end
28
22
 
29
- # print help
30
- puts "done. run 'showoff serve' in #{dirname}/ dir to see slideshow"""
23
+ if create_samples
24
+ puts "done. run 'showoff serve' in #{dirname}/ dir to see slideshow"
25
+ else
26
+ puts "done. add slides, modify #{SHOWOFF_JSON_FILE} and then run 'showoff serve' in #{dirname}/ dir to see slideshow"
27
+ end
31
28
  end
32
29
  end
33
30
 
34
- def self.heroku
35
- name = ARGV[1]
36
- return help('heroku') if !name
37
- if !File.exists?('showoff.json')
31
+ def self.heroku(name)
32
+ if !File.exists?(SHOWOFF_JSON_FILE)
38
33
  puts "fail. not a showoff directory"
39
34
  return false
40
35
  end
@@ -43,6 +38,7 @@ class ShowOffUtils
43
38
  f.puts "bluecloth"
44
39
  f.puts "nokogiri"
45
40
  f.puts "showoff"
41
+ f.puts "gli"
46
42
  end if !File.exists?('.gems')
47
43
 
48
44
  # create config.ru file
@@ -60,38 +56,179 @@ class ShowOffUtils
60
56
  "
61
57
  end
62
58
 
63
- def self.help(verb = nil)
64
- verb = ARGV[1] if !verb
65
- case verb
66
- when 'heroku'
67
- puts <<-HELP
68
- usage: showoff heroku (heroku-name)
59
+ # Makes a slide as a string.
60
+ # [title] title of the slide
61
+ # [classes] any "classes" to include, such as 'smaller', 'transition', etc.
62
+ # [content] slide content. Currently, if this is an array, it will make a bullet list. Otherwise
63
+ # the string value of this will be put in the slide as-is
64
+ def self.make_slide(title,classes="",content=nil)
65
+ slide = "!SLIDE #{classes}\n"
66
+ slide << "# #{title} #\n"
67
+ slide << "\n"
68
+ if content
69
+ if content.kind_of? Array
70
+ content.each { |x| slide << "* #{x.to_s}\n" }
71
+ else
72
+ slide << content.to_s
73
+ end
74
+ end
75
+ slide
76
+ end
77
+
78
+ TYPES = {
79
+ :default => lambda { |t,size,source,type| make_slide(t,"#{size} #{type}",source) },
80
+ 'title' => lambda { |t,size,dontcare| make_slide(t,size) },
81
+ 'bullets' => lambda { |t,size,dontcare| make_slide(t,"#{size} bullets incremental",["bullets","go","here"])},
82
+ 'smbullets' => lambda { |t,size,dontcare| make_slide(t,"#{size} smbullets incremental",["bullets","go","here","and","here"])},
83
+ 'code' => lambda { |t,size,src| make_slide(t,size,blank?(src) ? " @@@ Ruby\n code_here()" : src) },
84
+ 'commandline' => lambda { |t,size,dontcare| make_slide(t,"#{size} commandline"," $ command here\n output here")},
85
+ 'full-page' => lambda { |t,size,dontcare| make_slide(t,"#{size} full-page","![Image Description](image/ref.png)")},
86
+ }
69
87
 
70
- creates the .gems file and config.ru file needed to push a showoff pres to
71
- heroku. it will then run 'heroku create' for you to register the new project
72
- on heroku and add the remote for you. then all you need to do is commit the
73
- new created files and run 'git push heroku' to deploy.
74
88
 
75
- HELP
76
- when 'create'
77
- puts <<-HELP
78
- usage: showoff create (directory)
89
+ # Adds a new slide to a given dir, giving it a number such that it falls after all slides
90
+ # in that dir.
91
+ # Options are:
92
+ # [:dir] - dir where we put the slide (if omitted, slide is output to $stdout)
93
+ # [:name] - name of the file, without the number prefix. (if omitted, a default is used)
94
+ # [:title] - title in the slide. If not specified the source file name is
95
+ # used. If THAT is not specified, uses the value of +:name+. If THAT is not
96
+ # specified, a suitable default is used
97
+ # [:code] - path to a source file to use as content (force :type to be 'code')
98
+ # [:number] - true if numbering should be done, false if not
99
+ # [:type] - the type of slide to create
100
+ def self.add_slide(options)
79
101
 
80
- this command helps start a new showoff presentation by setting up the
81
- proper directory structure for you. it takes the directory name you would
82
- like showoff to create for you.
102
+ add_new_dir(options[:dir]) if options[:dir] && !File.exists?(options[:dir])
83
103
 
84
- HELP
104
+ options[:type] = 'code' if options[:code]
105
+
106
+ title = determine_title(options[:title],options[:name],options[:code])
107
+
108
+ options[:name] = 'new_slide' if !options[:name]
109
+
110
+ size,source = determine_size_and_source(options[:code])
111
+ type = options[:type] || :default
112
+ slide = TYPES[type].call(title,size,source)
113
+
114
+ if options[:dir]
115
+ filename = determine_filename(options[:dir],options[:name],options[:number])
116
+ write_file(filename,slide)
85
117
  else
86
- puts <<-HELP
87
- usage: showoff (command)
88
-
89
- commands:
90
- serve serves a showoff presentation from the current directory
91
- create generates a new showoff presentation layout
92
- heroku sets up your showoff presentation to push to heroku
93
- HELP
118
+ puts slide
119
+ puts
94
120
  end
121
+
95
122
  end
96
123
 
97
- end
124
+ # Adds the given directory to this presentation, appending it to
125
+ # the end of showoff.json as well
126
+ def self.add_new_dir(dir)
127
+ puts "Creating #{dir}..."
128
+ Dir.mkdir dir
129
+
130
+ showoff_json = JSON.parse(File.read(SHOWOFF_JSON_FILE))
131
+ showoff_json << { "section" => dir }
132
+ File.open(SHOWOFF_JSON_FILE,'w') do |file|
133
+ file.puts JSON.generate(showoff_json)
134
+ end
135
+ puts "#{SHOWOFF_JSON_FILE} updated"
136
+ end
137
+
138
+ def self.blank?(string)
139
+ string.nil? || string.strip.length == 0
140
+ end
141
+
142
+ def self.determine_size_and_source(code)
143
+ size = ""
144
+ source = ""
145
+ if code
146
+ source,lines,width = read_code(code)
147
+ size = adjust_size(lines,width)
148
+ end
149
+ [size,source]
150
+ end
151
+
152
+ def self.write_file(filename,slide)
153
+ File.open(filename,'w') do |file|
154
+ file.puts slide
155
+ end
156
+ puts "Wrote #{filename}"
157
+ end
158
+
159
+ def self.determine_filename(slide_dir,slide_name,number)
160
+ filename = "#{slide_dir}/#{slide_name}.md"
161
+ if number
162
+ max = find_next_number(slide_dir)
163
+ filename = "#{slide_dir}/#{max}_#{slide_name}.md"
164
+ end
165
+ filename
166
+ end
167
+
168
+ # Finds the next number in the given dir to
169
+ # name a slide as the last slide in the dir.
170
+ def self.find_next_number(slide_dir)
171
+ max = 0
172
+ Dir.open(slide_dir).each do |file|
173
+ if file =~ /(\d+).*\.md/
174
+ num = $1.to_i
175
+ max = num if num > max
176
+ end
177
+ end
178
+ max += 1
179
+ max = "0#{max}" if max < 10
180
+ max
181
+ end
182
+
183
+ def self.determine_title(title,slide_name,code)
184
+ if blank?(title)
185
+ title = slide_name
186
+ title = File.basename(code) if code
187
+ end
188
+ title = "Title here" if blank?(title)
189
+ title
190
+ end
191
+
192
+ # Determines a more optimal value for the size (e.g. small vs. smaller)
193
+ # based upon the size of the code being formatted.
194
+ def self.adjust_size(lines,width)
195
+ size = ""
196
+ # These values determined empircally
197
+ size = "small" if width > 50
198
+ size = "small" if lines > 15
199
+ size = "smaller" if width > 57
200
+ size = "smaller" if lines > 19
201
+ puts "warning, some lines are too long and the code may be cut off" if width > 65
202
+ puts "warning, your code is too long and the code may be cut off" if lines > 23
203
+ size
204
+ end
205
+
206
+ # Reads the code from the source file, returning
207
+ # the code, indented for markdown, as well as the number of lines
208
+ # and the width of the largest line
209
+ def self.read_code(source_file)
210
+ code = " @@@ #{lang(source_file)}\n"
211
+ lines = 0
212
+ width = 0
213
+ File.open(source_file) do |code_file|
214
+ code_file.readlines.each do |line|
215
+ code += " #{line}"
216
+ lines += 1
217
+ width = line.length if line.length > width
218
+ end
219
+ end
220
+ [code,lines,width]
221
+ end
222
+
223
+ EXTENSIONS = {
224
+ 'pl' => 'perl',
225
+ 'rb' => 'ruby',
226
+ 'erl' => 'erlang',
227
+ # so not exhaustive, but probably good enough for now
228
+ }
229
+
230
+ def self.lang(source_file)
231
+ ext = File.extname(source_file).gsub(/^\./,'')
232
+ EXTENSIONS[ext] || ext
233
+ end
234
+ end
@@ -9,3 +9,36 @@
9
9
  border: 1px solid #333;
10
10
  page-break-after: always
11
11
  }
12
+
13
+ /* iPhone */
14
+ /* Portrait */
15
+ @media screen and (max-width: 320px)
16
+ {
17
+ .preso {
18
+ margin: 10px;
19
+ padding: 0;
20
+ width: 320px;
21
+ min-height: 480px;
22
+ margin-left:auto;
23
+ margin-right:auto;
24
+ /* overflow:hidden;*/
25
+ border: 1px solid #333;
26
+ page-break-after: always
27
+ }
28
+ }
29
+ /* Landscape */
30
+ @media screen and (min-width: 321px)
31
+ {
32
+ .preso {
33
+ margin: 10px;
34
+ padding: 0;
35
+ width: 480px;
36
+ min-height: 320px;
37
+ margin-left:auto;
38
+ margin-right:auto;
39
+ /* overflow:hidden;*/
40
+ border: 1px solid #333;
41
+ page-break-after: always
42
+ }
43
+ }
44
+
@@ -20,12 +20,69 @@ body {
20
20
  margin-right:auto;
21
21
  }
22
22
 
23
+ /* iPhone */
24
+ /* Portrait */
25
+ @media screen and (max-width: 320px)
26
+ {
27
+ #preso {
28
+ margin: 0;
29
+ padding: 0;
30
+ width: 320px;
31
+ max-height: 356px;
32
+ margin-left:auto;
33
+ margin-right:auto;
34
+ /* overflow:hidden;*/
35
+ }
36
+ #footer {
37
+ background: #eee;
38
+ margin: 0;
39
+ padding: 2px;
40
+ width: 320px;
41
+ height: 20px;
42
+ margin-left:auto;
43
+ margin-right:auto;
44
+ }
45
+ }
46
+ /* Landscape */
47
+ @media screen and (max-width: 480px)
48
+ {
49
+ #preso {
50
+ margin: 0;
51
+ padding: 0;
52
+ /* min-height: 320px;*/
53
+ width: 480px;
54
+ margin-left:auto;
55
+ margin-right:auto;
56
+ }
57
+ #footer {
58
+ background: #eee;
59
+ margin: 0;
60
+ padding: 2px;
61
+ width: 480px;
62
+ height: 20px;
63
+ margin-left:auto;
64
+ margin-right:auto;
65
+ }
66
+ }
67
+
68
+ .slide {
69
+ border: 1px solid #fff;
70
+ }
71
+
23
72
  .center img {
24
73
  display:block;
25
74
  margin-left:auto;
26
75
  margin-right:auto;
27
76
  }
28
77
 
78
+ .slide .center {
79
+ height: 740px;
80
+ width: 1020px;
81
+ display: table-cell;
82
+ text-align: center;
83
+ vertical-align: middle;
84
+ }
85
+
29
86
  .bullets ul {
30
87
  font-size: 3em;
31
88
  }
Binary file
Binary file
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Cookie plugin
3
+ *
4
+ * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
5
+ * Dual licensed under the MIT and GPL licenses:
6
+ * http://www.opensource.org/licenses/mit-license.php
7
+ * http://www.gnu.org/licenses/gpl.html
8
+ *
9
+ */
10
+
11
+ /**
12
+ * Create a cookie with the given name and value and other optional parameters.
13
+ *
14
+ * @example $.cookie('the_cookie', 'the_value');
15
+ * @desc Set the value of a cookie.
16
+ * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
17
+ * @desc Create a cookie with all available options.
18
+ * @example $.cookie('the_cookie', 'the_value');
19
+ * @desc Create a session cookie.
20
+ * @example $.cookie('the_cookie', null);
21
+ * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
22
+ * used when the cookie was set.
23
+ *
24
+ * @param String name The name of the cookie.
25
+ * @param String value The value of the cookie.
26
+ * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
27
+ * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
28
+ * If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
29
+ * If set to null or omitted, the cookie will be a session cookie and will not be retained
30
+ * when the the browser exits.
31
+ * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
32
+ * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
33
+ * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
34
+ * require a secure protocol (like HTTPS).
35
+ * @type undefined
36
+ *
37
+ * @name $.cookie
38
+ * @cat Plugins/Cookie
39
+ * @author Klaus Hartl/klaus.hartl@stilbuero.de
40
+ */
41
+
42
+ /**
43
+ * Get the value of a cookie with the given name.
44
+ *
45
+ * @example $.cookie('the_cookie');
46
+ * @desc Get the value of a cookie.
47
+ *
48
+ * @param String name The name of the cookie.
49
+ * @return The value of the cookie.
50
+ * @type String
51
+ *
52
+ * @name $.cookie
53
+ * @cat Plugins/Cookie
54
+ * @author Klaus Hartl/klaus.hartl@stilbuero.de
55
+ */
56
+ jQuery.cookie = function(name, value, options) {
57
+ if (typeof value != 'undefined') { // name and value given, set cookie
58
+ options = options || {};
59
+ if (value === null) {
60
+ value = '';
61
+ options.expires = -1;
62
+ }
63
+ var expires = '';
64
+ if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
65
+ var date;
66
+ if (typeof options.expires == 'number') {
67
+ date = new Date();
68
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
69
+ } else {
70
+ date = options.expires;
71
+ }
72
+ expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
73
+ }
74
+ // CAUTION: Needed to parenthesize options.path and options.domain
75
+ // in the following expressions, otherwise they evaluate to undefined
76
+ // in the packed version for some reason...
77
+ var path = options.path ? '; path=' + (options.path) : '';
78
+ var domain = options.domain ? '; domain=' + (options.domain) : '';
79
+ var secure = options.secure ? '; secure' : '';
80
+ document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
81
+ } else { // only name given, get cookie
82
+ var cookieValue = null;
83
+ if (document.cookie && document.cookie != '') {
84
+ var cookies = document.cookie.split(';');
85
+ for (var i = 0; i < cookies.length; i++) {
86
+ var cookie = jQuery.trim(cookies[i]);
87
+ // Does this cookie string begin with the name we want?
88
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
89
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
90
+ break;
91
+ }
92
+ }
93
+ }
94
+ return cookieValue;
95
+ }
96
+ };
@@ -0,0 +1,105 @@
1
+ (function($) {
2
+ var touchStatus = function(target, touch) {
3
+ this.target = $(target);
4
+ this.touch = touch;
5
+ this.startX = this.currentX = touch.screenX;
6
+ this.startY = this.currentY = touch.screenY;
7
+ this.eventType = null;
8
+ }
9
+ touchStatus.latestTap = null;
10
+
11
+ touchStatus.prototype.move = function(touch) {
12
+ this.currentX = touch.screenX;
13
+ this.currentY = touch.screenY;
14
+ }
15
+
16
+ touchStatus.prototype.process = function() {
17
+ var offsetX = this.currentX - this.startX;
18
+ var offsetY = this.currentY - this.startY;
19
+ if(offsetX == 0 && offsetY == 0) {
20
+ this.checkForDoubleTap()
21
+ } else if(Math.abs(offsetY) > Math.abs(offsetX)) {
22
+ this.eventType = offsetY > 0 ? 'swipedown' : 'swipeup';
23
+ this.target.trigger('swipe', [this])
24
+ } else {
25
+ this.eventType = offsetX > 0 ? 'swiperight' : 'swipeleft';
26
+ this.target.trigger('swipe', [this])
27
+ }
28
+ this.target.trigger(this.eventType, [this])
29
+ this.target.trigger('touch', [this])
30
+ }
31
+
32
+ touchStatus.prototype.checkForDoubleTap = function() {
33
+ if(touchStatus.latestTap) {
34
+ if((new Date() - touchStatus.latestTap) < 400)
35
+ this.eventType = 'doubletap'
36
+ }
37
+ if(!this.eventType) this.eventType = 'tap'
38
+ touchStatus.latestTap = new Date()
39
+ }
40
+
41
+ var swipeEvents = function(elements) {
42
+ elements.bind('touchstart', this.touchStart);
43
+ elements.bind('touchmove', this.touchMove);
44
+ elements.bind('touchcancel', this.touchCancel);
45
+ elements.bind('touchend', this.touchEnd);
46
+ }
47
+
48
+ swipeEvents.prototype.touchStart = function(evt) {
49
+ var target = this;
50
+ swipeEvents.eachTouch(evt, function(touch) {
51
+ swipeEvents.touches[touch.identifier] = new touchStatus(target, touch);
52
+ })
53
+ }
54
+
55
+ swipeEvents.prototype.touchMove = function(evt) {
56
+ swipeEvents.eachTouch(evt, function(touch) {
57
+ var loc = swipeEvents.touches[touch.identifier]
58
+ if(loc) loc.move(touch)
59
+ })
60
+ }
61
+
62
+ swipeEvents.prototype.touchCancel = function(evt) {
63
+ swipeEvents.eachTouch(evt, function(touch) {
64
+ swipeEvents.purge(touch, true)
65
+ })
66
+ }
67
+
68
+ swipeEvents.prototype.touchEnd = function(evt) {
69
+ swipeEvents.eachTouch(evt, function(touch) {
70
+ swipeEvents.purge(touch)
71
+ })
72
+ }
73
+
74
+ swipeEvents.touches = {}
75
+ swipeEvents.purge = function(touch, cancelled) {
76
+ if(!cancelled) {
77
+ var loc = swipeEvents.touches[touch.identifier]
78
+ if(loc) loc.process()
79
+ }
80
+ delete swipeEvents.touches[touch.identifier]
81
+ }
82
+
83
+ swipeEvents.eachTouch = function(evt, callback) {
84
+ var evt = evt.originalEvent;
85
+ var num = evt.changedTouches.length;
86
+ for(var i = 0; i < num; i++) {
87
+ callback(evt.changedTouches[i])
88
+ }
89
+ }
90
+
91
+ // adds custom events:
92
+ // touch // all events
93
+ // swipe // only swipe* events
94
+ // swipeleft
95
+ // swiperight
96
+ // swipeup
97
+ // swipedown
98
+ // tap
99
+ // doubletap
100
+ $.fn.addSwipeEvents = function(callback) {
101
+ new swipeEvents(this);
102
+ if(callback) this.bind('touch', callback)
103
+ return this;
104
+ }
105
+ })(jQuery);
@@ -0,0 +1,24 @@
1
+ /*
2
+ Usage 1: define the default prefix by using an object with the property prefix as a parameter which contains a string value; {prefix: 'id'}
3
+ Usage 2: call the function jQuery.uuid() with a string parameter p to be used as a prefix to generate a random uuid;
4
+ Usage 3: call the function jQuery.uuid() with no parameters to generate a uuid with the default prefix; defaul prefix: '' (empty string)
5
+ */
6
+
7
+ /*
8
+ Generate fragment of random numbers
9
+ */
10
+ jQuery._uuid_default_prefix = '';
11
+ jQuery._uuidlet = function () {
12
+ return(((1+Math.random())*0x10000)|0).toString(16).substring(1);
13
+ };
14
+ /*
15
+ Generates random uuid
16
+ */
17
+ jQuery.uuid = function (p) {
18
+ if (typeof(p) == 'object' && typeof(p.prefix) == 'string') {
19
+ jQuery._uuid_default_prefix = p.prefix;
20
+ } else {
21
+ p = p || jQuery._uuid_default_prefix || '';
22
+ return(p+jQuery._uuidlet()+jQuery._uuidlet()+"-"+jQuery._uuidlet()+"-"+jQuery._uuidlet()+"-"+jQuery._uuidlet()+"-"+jQuery._uuidlet()+jQuery._uuidlet()+jQuery._uuidlet());
23
+ };
24
+ };