showoff 0.1.4 → 0.2.0

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/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
+ };