Browse code

improved starting point

B Mathis authored on 19/10/2009 at 00:07:36
Showing 13 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+site
0 1
\ No newline at end of file
1 2
new file mode 100644
... ...
@@ -0,0 +1,63 @@
0
+require 'active_support'
1
+
2
+def ok_failed(condition)
3
+  if (condition)
4
+    puts "OK"
5
+  else
6
+    puts "FAILED"
7
+  end
8
+end
9
+
10
+port = "4000"
11
+site = "site"
12
+
13
+desc "list tasks"
14
+task :default do
15
+  puts "Tasks: #{(Rake::Task.tasks - [Rake::Task[:default]]).to_sentence}"
16
+  puts "(type rake -T for more detail)\n\n"
17
+end
18
+
19
+desc "remove files in output directory"
20
+task :clean do
21
+  puts "Removing output..."
22
+  Dir["#{site}/*"].each { |f| rm_rf(f) }
23
+end
24
+
25
+desc "generate website in output directory"
26
+task :generate => :clean do
27
+  puts "Generating website..."
28
+  system "compass"
29
+  system "jekyll"
30
+  Dir["#{site}/stylesheets/*.sass"].each { |f| rm_rf(f) }
31
+  system "mv #{site}/atom.html #{site}/blog/atom.xml"
32
+end
33
+
34
+desc "generate and deploy website"
35
+task :deploy => :generate do
36
+  print "Deploying website..."
37
+  ok_failed system("rsync -avz --delete #{site}/ user@host.com:~/document_root/")
38
+end
39
+
40
+desc "start up an instance of serve on the output files"
41
+task :start_serve => :stop_serve do
42
+  cd "#{site}" do
43
+    print "Starting serve..."
44
+    ok_failed system("serve #{port} > /dev/null 2>&1 &")
45
+  end
46
+end
47
+
48
+desc "stop all instances of serve"
49
+task :stop_serve do
50
+  pid = `ps auxw | awk '/bin\\/serve\\ #{port}/ { print $2 }'`.strip
51
+  if pid.empty?
52
+    puts "Serve is not running"
53
+  else
54
+    print "Stoping serve..."
55
+    ok_failed system("kill -9 #{pid}")
56
+  end
57
+end
58
+
59
+desc "preview the site in a web browser"
60
+multitask :preview => [:generate, :start_serve] do
61
+  system "open http://localhost:#{port}"
62
+end
0 63
\ No newline at end of file
1 64
new file mode 100644
... ...
@@ -0,0 +1,11 @@
0
+source: source
1
+destination: site
2
+markdown: rdiscount
3
+pygments: true
4
+permalink: /blog/:year/:month/:day/:title
5
+url: http://yoursite.com
6
+multiviews: true
7
+sass: false
8
+haml: true
9
+post_defaults:
10
+  layout: post
0 11
\ No newline at end of file
1 12
new file mode 100644
... ...
@@ -0,0 +1,10 @@
0
+# Require any additional compass plugins here.
1
+project_type = :stand_alone
2
+# Set this to the root of your project when deployed:
3
+http_path = "/"
4
+css_dir = "_site/stylesheets"
5
+sass_dir = "_source/stylesheets"
6
+images_dir = "images"
7
+
8
+# To enable relative paths to assets via compass helper functions. Uncomment:
9
+# relative_assets = true
0 10
new file mode 100644
... ...
@@ -0,0 +1,483 @@
0
+#
1
+# = RubyPants - SmartyPants ported to Ruby
2
+#
3
+# Ported by Christian Neukirchen <mailto:chneukirchen@gmail.com>
4
+#   Copyright (C) 2004 Christian Neukirchen
5
+#
6
+# Incooporates ideas, comments and documentation by Chad Miller
7
+#   Copyright (C) 2004 Chad Miller
8
+#
9
+# Original SmartyPants by John Gruber
10
+#   Copyright (C) 2003 John Gruber
11
+#
12
+
13
+#
14
+# = RubyPants - SmartyPants ported to Ruby
15
+#
16
+# == Synopsis
17
+#
18
+# RubyPants is a Ruby port of the smart-quotes library SmartyPants.
19
+#
20
+# The original "SmartyPants" is a free web publishing plug-in for
21
+# Movable Type, Blosxom, and BBEdit that easily translates plain ASCII
22
+# punctuation characters into "smart" typographic punctuation HTML
23
+# entities.
24
+#
25
+#
26
+# == Description
27
+# 
28
+# RubyPants can perform the following transformations:
29
+# 
30
+# * Straight quotes (<tt>"</tt> and <tt>'</tt>) into "curly" quote
31
+#   HTML entities
32
+# * Backticks-style quotes (<tt>``like this''</tt>) into "curly" quote
33
+#   HTML entities
34
+# * Dashes (<tt>--</tt> and <tt>---</tt>) into en- and em-dash
35
+#   entities
36
+# * Three consecutive dots (<tt>...</tt> or <tt>. . .</tt>) into an
37
+#   ellipsis entity
38
+# 
39
+# This means you can write, edit, and save your posts using plain old
40
+# ASCII straight quotes, plain dashes, and plain dots, but your
41
+# published posts (and final HTML output) will appear with smart
42
+# quotes, em-dashes, and proper ellipses.
43
+# 
44
+# RubyPants does not modify characters within <tt><pre></tt>,
45
+# <tt><code></tt>, <tt><kbd></tt>, <tt><math></tt> or
46
+# <tt><script></tt> tag blocks. Typically, these tags are used to
47
+# display text where smart quotes and other "smart punctuation" would
48
+# not be appropriate, such as source code or example markup.
49
+#
50
+#
51
+# == Backslash Escapes
52
+# 
53
+# If you need to use literal straight quotes (or plain hyphens and
54
+# periods), RubyPants accepts the following backslash escape sequences
55
+# to force non-smart punctuation. It does so by transforming the
56
+# escape sequence into a decimal-encoded HTML entity:
57
+# 
58
+#   \\    \"    \'    \.    \-    \`
59
+#
60
+# This is useful, for example, when you want to use straight quotes as
61
+# foot and inch marks: 6'2" tall; a 17" iMac.  (Use <tt>6\'2\"</tt>
62
+# resp. <tt>17\"</tt>.)
63
+#
64
+# 
65
+# == Algorithmic Shortcomings
66
+# 
67
+# One situation in which quotes will get curled the wrong way is when
68
+# apostrophes are used at the start of leading contractions. For
69
+# example:
70
+# 
71
+#   'Twas the night before Christmas.
72
+# 
73
+# In the case above, RubyPants will turn the apostrophe into an
74
+# opening single-quote, when in fact it should be a closing one. I
75
+# don't think this problem can be solved in the general case--every
76
+# word processor I've tried gets this wrong as well. In such cases,
77
+# it's best to use the proper HTML entity for closing single-quotes
78
+# (``&#8217;``) by hand.
79
+# 
80
+# 
81
+# == Bugs
82
+#
83
+# To file bug reports or feature requests (except see above) please
84
+# send email to: mailto:chneukirchen@gmail.com
85
+#
86
+# If the bug involves quotes being curled the wrong way, please send
87
+# example text to illustrate.
88
+#
89
+#
90
+# == Authors
91
+# 
92
+# John Gruber did all of the hard work of writing this software in
93
+# Perl for Movable Type and almost all of this useful documentation.
94
+# Chad Miller ported it to Python to use with Pyblosxom.
95
+#
96
+# Christian Neukirchen provided the Ruby port, as a general-purpose
97
+# library that follows the *Cloth api.
98
+# 
99
+#
100
+# == Copyright and License
101
+# 
102
+# === SmartyPants license:
103
+# 
104
+# Copyright (c) 2003 John Gruber
105
+# (http://daringfireball.net)
106
+# All rights reserved.
107
+# 
108
+# Redistribution and use in source and binary forms, with or without
109
+# modification, are permitted provided that the following conditions
110
+# are met:
111
+# 
112
+# * Redistributions of source code must retain the above copyright
113
+#   notice, this list of conditions and the following disclaimer.
114
+# 
115
+# * Redistributions in binary form must reproduce the above copyright
116
+#   notice, this list of conditions and the following disclaimer in
117
+#   the documentation and/or other materials provided with the
118
+#   distribution.
119
+# 
120
+# * Neither the name "SmartyPants" nor the names of its contributors
121
+#   may be used to endorse or promote products derived from this
122
+#   software without specific prior written permission.
123
+# 
124
+# This software is provided by the copyright holders and contributors
125
+# "as is" and any express or implied warranties, including, but not
126
+# limited to, the implied warranties of merchantability and fitness
127
+# for a particular purpose are disclaimed. In no event shall the
128
+# copyright owner or contributors be liable for any direct, indirect,
129
+# incidental, special, exemplary, or consequential damages (including,
130
+# but not limited to, procurement of substitute goods or services;
131
+# loss of use, data, or profits; or business interruption) however
132
+# caused and on any theory of liability, whether in contract, strict
133
+# liability, or tort (including negligence or otherwise) arising in
134
+# any way out of the use of this software, even if advised of the
135
+# possibility of such damage.
136
+# 
137
+# === RubyPants license
138
+# 
139
+# RubyPants is a derivative work of SmartyPants and smartypants.py.
140
+# 
141
+# Redistribution and use in source and binary forms, with or without
142
+# modification, are permitted provided that the following conditions
143
+# are met:
144
+# 
145
+# * Redistributions of source code must retain the above copyright
146
+#   notice, this list of conditions and the following disclaimer.
147
+# 
148
+# * Redistributions in binary form must reproduce the above copyright
149
+#   notice, this list of conditions and the following disclaimer in
150
+#   the documentation and/or other materials provided with the
151
+#   distribution.
152
+# 
153
+# This software is provided by the copyright holders and contributors
154
+# "as is" and any express or implied warranties, including, but not
155
+# limited to, the implied warranties of merchantability and fitness
156
+# for a particular purpose are disclaimed. In no event shall the
157
+# copyright owner or contributors be liable for any direct, indirect,
158
+# incidental, special, exemplary, or consequential damages (including,
159
+# but not limited to, procurement of substitute goods or services;
160
+# loss of use, data, or profits; or business interruption) however
161
+# caused and on any theory of liability, whether in contract, strict
162
+# liability, or tort (including negligence or otherwise) arising in
163
+# any way out of the use of this software, even if advised of the
164
+# possibility of such damage.
165
+# 
166
+#
167
+# == Links
168
+#
169
+# John Gruber:: http://daringfireball.net
170
+# SmartyPants:: http://daringfireball.net/projects/smartypants
171
+#
172
+# Chad Miller:: http://web.chad.org
173
+#
174
+# Christian Neukirchen:: http://kronavita.de/chris
175
+#
176
+
177
+
178
+class RubyPants < String
179
+  VERSION = "0.1"
180
+
181
+  # Allowed elements in the options array:
182
+  # 
183
+  # 0 :: do nothing
184
+  # 1 :: set all
185
+  # 2 :: set all, using old school en- and em- dash shortcuts
186
+  # 3 :: set all, using inverted old school en and em- dash shortcuts
187
+  # -1 :: stupefy (translate HTML entities to their ASCII-counterparts)
188
+  #
189
+  # <tt>:quotes</tt>        :: quotes
190
+  # <tt>:backticks</tt>     :: backtick quotes (``double'' only)
191
+  # <tt>:allbackticks</tt>  :: backtick quotes (``double'' and `single')
192
+  # <tt>:dashes</tt>        :: dashes
193
+  # <tt>:oldschool</tt>     :: old school dashes
194
+  # <tt>:inverted</tt>      :: inverted old school dashes
195
+  # <tt>:ellipses</tt>      :: ellipses
196
+  # <tt>:convertquotes</tt> :: convert <tt>&quot;</tt> entities to
197
+  #                            <tt>"</tt> for Dreamweaver users
198
+  # <tt>:stupefy</tt>       :: translate SmartyPants HTML entities
199
+  #                            to their ASCII counterparts.
200
+  #
201
+  def initialize(string, options=[2])
202
+    super string
203
+    @options = [*options]
204
+  end
205
+
206
+  # Apply SmartyPants transformations.
207
+  def to_html
208
+    do_quotes = do_backticks = do_dashes = do_ellipses = do_stupify = nil
209
+    convert_quotes = false
210
+
211
+    if @options.include? 0
212
+      # Do nothing.
213
+      return self
214
+    elsif @options.include? 1
215
+      # Do everything, turn all options on.
216
+      do_quotes = do_backticks = do_ellipses = true
217
+      do_dashes = :normal
218
+    elsif @options.include? 2
219
+      # Do everything, turn all options on, use old school dash shorthand.
220
+      do_quotes = do_backticks = do_ellipses = true
221
+      do_dashes = :oldschool
222
+    elsif @options.include? 3
223
+      # Do everything, turn all options on, use inverted old school
224
+      # dash shorthand.
225
+      do_quotes = do_backticks = do_ellipses = true
226
+      do_dashes = :inverted
227
+    elsif @options.include?(-1)
228
+      do_stupefy = true
229
+    else
230
+      do_quotes =                @options.include? :quotes
231
+      do_backticks =             @options.include? :backticks
232
+      do_backticks = :both    if @options.include? :allbackticks
233
+      do_dashes = :normal     if @options.include? :dashes
234
+      do_dashes = :oldschool  if @options.include? :oldschool
235
+      do_dashes = :inverted   if @options.include? :inverted
236
+      do_ellipses =              @options.include? :ellipses
237
+      convert_quotes =           @options.include? :convertquotes
238
+      do_stupefy =               @options.include? :stupefy
239
+    end
240
+
241
+    # Parse the HTML
242
+    tokens = tokenize
243
+    
244
+    # Keep track of when we're inside <pre> or <code> tags.
245
+    in_pre = false
246
+
247
+    # Here is the result stored in.
248
+    result = ""
249
+
250
+    # This is a cheat, used to get some context for one-character
251
+    # tokens that consist of just a quote char. What we do is remember
252
+    # the last character of the previous text token, to use as context
253
+    # to curl single- character quote tokens correctly.
254
+    prev_token_last_char = ""
255
+
256
+    tokens.each { |token|
257
+      if token.first == :tag
258
+        result << token[1]
259
+        if token[1] =~ %r!<(/?)(?:pre|code|kbd|script|math)[\s>]!
260
+          in_pre = ($1 != "/")  # Opening or closing tag?
261
+        end
262
+      else
263
+        t = token[1]
264
+
265
+        # Remember last char of this token before processing.
266
+        last_char = t[-1]
267
+
268
+        unless in_pre
269
+          t = process_escapes t
270
+          
271
+          t.gsub!(/&quot;/, '"')  if convert_quotes
272
+
273
+          if do_dashes
274
+            t = educate_dashes t            if do_dashes == :normal
275
+            t = educate_dashes_oldschool t  if do_dashes == :oldschool
276
+            t = educate_dashes_inverted t   if do_dashes == :inverted
277
+          end
278
+
279
+          t = educate_ellipses t  if do_ellipses
280
+
281
+          # Note: backticks need to be processed before quotes.
282
+          if do_backticks
283
+            t = educate_backticks t
284
+            t = educate_single_backticks t  if do_backticks == :both
285
+          end
286
+
287
+          if do_quotes
288
+            if t == "'"
289
+              # Special case: single-character ' token
290
+              if prev_token_last_char =~ /\S/
291
+                t = "&#8217;"
292
+              else
293
+                t = "&#8216;"
294
+              end
295
+            elsif t == '"'
296
+              # Special case: single-character " token
297
+              if prev_token_last_char =~ /\S/
298
+                t = "&#8221;"
299
+              else
300
+                t = "&#8220;"
301
+              end
302
+            else
303
+              # Normal case:                  
304
+              t = educate_quotes t
305
+            end
306
+          end
307
+
308
+          t = stupefy_entities t  if do_stupefy
309
+        end
310
+
311
+        prev_token_last_char = last_char
312
+        result << t
313
+      end
314
+    }
315
+
316
+    # Done
317
+    result
318
+  end
319
+
320
+  protected
321
+
322
+  # Return the string, with after processing the following backslash
323
+  # escape sequences. This is useful if you want to force a "dumb" quote
324
+  # or other character to appear.
325
+  #
326
+  # Escaped are:
327
+  #      \\    \"    \'    \.    \-    \`
328
+  #
329
+  def process_escapes(str)
330
+    str.gsub(/\\\\/, '&#92;').
331
+      gsub(/\\"/, '&#34;').
332
+      gsub(/\\'/, '&#39;').
333
+      gsub(/\\\./, '&#46;').
334
+      gsub(/\\-/, '&#45;').
335
+      gsub(/\\`/, '&#96;')
336
+  end
337
+
338
+  # The string, with each instance of "<tt>--</tt>" translated to an
339
+  # em-dash HTML entity.
340
+  #
341
+  def educate_dashes(str)
342
+    str.gsub(/--/, '&#8212;')
343
+  end
344
+
345
+  # The string, with each instance of "<tt>--</tt>" translated to an
346
+  # en-dash HTML entity, and each "<tt>---</tt>" translated to an
347
+  # em-dash HTML entity.
348
+  #
349
+  def educate_dashes_oldschool(str)
350
+    str.gsub(/---/, '&#8212;').gsub(/--/, '&#8211;')
351
+  end
352
+
353
+  # Return the string, with each instance of "<tt>--</tt>" translated
354
+  # to an em-dash HTML entity, and each "<tt>---</tt>" translated to
355
+  # an en-dash HTML entity. Two reasons why: First, unlike the en- and
356
+  # em-dash syntax supported by +educate_dashes_oldschool+, it's
357
+  # compatible with existing entries written before SmartyPants 1.1,
358
+  # back when "<tt>--</tt>" was only used for em-dashes.  Second,
359
+  # em-dashes are more common than en-dashes, and so it sort of makes
360
+  # sense that the shortcut should be shorter to type. (Thanks to
361
+  # Aaron Swartz for the idea.)
362
+  #
363
+  def educate_dashes_inverted(str)
364
+    str.gsub(/---/, '&#8211;').gsub(/--/, '&#8212;')
365
+  end
366
+
367
+  # Return the string, with each instance of "<tt>...</tt>" translated
368
+  # to an ellipsis HTML entity. Also converts the case where there are
369
+  # spaces between the dots.
370
+  #
371
+  def educate_ellipses(str)
372
+    str.gsub('...', '&#8230;').gsub('. . .', '&#8230;')
373
+  end
374
+
375
+  # Return the string, with <tt>``backticks''</tt>-style single quotes
376
+  # translated into HTML curly quote entities.
377
+  #
378
+  def educate_backticks(str)
379
+    str.gsub("``", '&#8220;').gsub("''", '&#8221;')
380
+  end
381
+
382
+  # Return the string, with <tt>`backticks'</tt>-style single quotes
383
+  # translated into HTML curly quote entities.
384
+  #
385
+  def educate_single_backticks(str)
386
+    str.gsub("`", '&#8216;').gsub("'", '&#8217;')
387
+  end
388
+
389
+  # Return the string, with "educated" curly quote HTML entities.
390
+  #
391
+  def educate_quotes(str)
392
+    punct_class = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'
393
+
394
+    str = str.dup
395
+      
396
+    # Special case if the very first character is a quote followed by
397
+    # punctuation at a non-word-break. Close the quotes by brute
398
+    # force:
399
+    str.gsub!(/^'(?=#{punct_class}\B)/, '&#8217;')
400
+    str.gsub!(/^"(?=#{punct_class}\B)/, '&#8221;')
401
+
402
+    # Special case for double sets of quotes, e.g.:
403
+    #   <p>He said, "'Quoted' words in a larger quote."</p>
404
+    str.gsub!(/"'(?=\w)/, '&#8220;&#8216;')
405
+    str.gsub!(/'"(?=\w)/, '&#8216;&#8220;')
406
+
407
+    # Special case for decade abbreviations (the '80s):
408
+    str.gsub!(/'(?=\d\ds)/, '&#8217;')
409
+
410
+    close_class = %![^\ \t\r\n\\[\{\(\-]!
411
+    dec_dashes = '&#8211;|&#8212;'
412
+    
413
+    # Get most opening single quotes:
414
+    str.gsub!(/(\s|&nbsp;|--|&[mn]dash;|#{dec_dashes}|&#x201[34];)'(?=\w)/,
415
+             '\1&#8216;')
416
+    # Single closing quotes:
417
+    str.gsub!(/(#{close_class})'/, '\1&#8217;')
418
+    str.gsub!(/'(\s|s\b|$)/, '&#8217;\1')
419
+    # Any remaining single quotes should be opening ones:
420
+    str.gsub!(/'/, '&#8216;')
421
+
422
+    # Get most opening double quotes:
423
+    str.gsub!(/(\s|&nbsp;|--|&[mn]dash;|#{dec_dashes}|&#x201[34];)"(?=\w)/,
424
+             '\1&#8220;')
425
+    # Double closing quotes:
426
+    str.gsub!(/(#{close_class})"/, '\1&#8221;')
427
+    str.gsub!(/"(\s|s\b|$)/, '&#8221;\1')
428
+    # Any remaining quotes should be opening ones:
429
+    str.gsub!(/"/, '&#8220;')
430
+
431
+    str
432
+  end
433
+
434
+  # Return the string, with each SmartyPants HTML entity translated to
435
+  # its ASCII counterpart.
436
+  #
437
+  def stupefy_entities(str)
438
+    str.
439
+      gsub(/&#8211;/, '-').      # en-dash
440
+      gsub(/&#8212;/, '--').     # em-dash
441
+      
442
+      gsub(/&#8216;/, "'").      # open single quote
443
+      gsub(/&#8217;/, "'").      # close single quote
444
+      
445
+      gsub(/&#8220;/, '"').      # open double quote
446
+      gsub(/&#8221;/, '"').      # close double quote
447
+      
448
+      gsub(/&#8230;/, '...')     # ellipsis
449
+  end
450
+
451
+  # Return an array of the tokens comprising the string. Each token is
452
+  # either a tag (possibly with nested, tags contained therein, such
453
+  # as <tt><a href="<MTFoo>"></tt>, or a run of text between
454
+  # tags. Each element of the array is a two-element array; the first
455
+  # is either :tag or :text; the second is the actual value.
456
+  #
457
+  # Based on the <tt>_tokenize()</tt> subroutine from Brad Choate's
458
+  # MTRegex plugin.  <http://www.bradchoate.com/past/mtregex.php>
459
+  #
460
+  # This is actually the easier variant using tag_soup, as used by
461
+  # Chad Miller in the Python port of SmartyPants.
462
+  #
463
+  def tokenize
464
+    tag_soup = /([^<]*)(<[^>]*>)/
465
+
466
+    tokens = []
467
+
468
+    prev_end = 0
469
+    scan(tag_soup) {
470
+      tokens << [:text, $1]  if $1 != ""
471
+      tokens << [:tag, $2]
472
+      
473
+      prev_end = $~.end(0)
474
+    }
475
+
476
+    if prev_end < size
477
+      tokens << [:text, self[prev_end..-1]]
478
+    end
479
+
480
+    tokens
481
+  end
482
+end
0 483
\ No newline at end of file
1 484
deleted file mode 100644
... ...
@@ -1,11 +0,0 @@
1
-source: .
2
-destination: ../site
3
-markdown: rdiscount
4
-pygments: true
5
-permalink: /blog/:year/:month/:day/:title
6
-url: 
7
-multiviews: true
8
-sass: false
9
-haml: true
10
-post_defaults:
11
-  layout: post
12 1
\ No newline at end of file
13 2
new file mode 100644
... ...
@@ -0,0 +1,191 @@
0
+gem 'activesupport', ">= 2.3.2"
1
+require 'active_support'
2
+require 'rubypants'
3
+
4
+module Helpers
5
+  module EscapeHelper
6
+    HTML_ESCAPE = { '&' => '&amp; ',  '>' => '&gt;',   '<' => '&lt;', '"' => '&quot;' }
7
+    JSON_ESCAPE = { '&' => '\u0026 ', '>' => '\u003E', '<' => '\u003C' }
8
+    
9
+    # A utility method for escaping HTML tag characters.
10
+    # This method is also aliased as <tt>h</tt>.
11
+    #
12
+    # In your ERb templates, use this method to escape any unsafe content. For example:
13
+    #   <%=h @person.name %>
14
+    #
15
+    # ==== Example:
16
+    #   puts html_escape("is a > 0 & a < 10?")
17
+    #   # => is a &gt; 0 &amp; a &lt; 10?
18
+    def html_escape(html)
19
+      html.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
20
+    end
21
+    def escape_once(html)
22
+      html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| HTML_ESCAPE[special] }
23
+    end
24
+    alias h escape_once
25
+    
26
+    # A utility method for escaping HTML entities in JSON strings.
27
+    # This method is also aliased as <tt>j</tt>.
28
+    #
29
+    # In your ERb templates, use this method to escape any HTML entities:
30
+    #   <%=j @person.to_json %>
31
+    #
32
+    # ==== Example:
33
+    #   puts json_escape("is a > 0 & a < 10?")
34
+    #   # => is a \u003E 0 \u0026 a \u003C 10?
35
+    def json_escape(s)
36
+      s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
37
+    end
38
+    
39
+    alias j json_escape
40
+  end
41
+  include EscapeHelper
42
+  
43
+  module ParamsHelper
44
+    def params
45
+      @params ||= begin
46
+        q = request.query.dup
47
+        q.each { |(k,v)| q[k.to_s.intern] = v }
48
+        q
49
+      end
50
+    end
51
+  end
52
+  include ParamsHelper
53
+  
54
+  module TagHelper
55
+    def content_tag(name, content, html_options={})
56
+      %{<#{name}#{html_attributes(html_options)}>#{content}</#{name}>}
57
+    end
58
+    
59
+    def tag(name, html_options={})
60
+      %{<#{name}#{html_attributes(html_options)} />}
61
+    end
62
+    
63
+    def image_tag(src, html_options = {})
64
+      tag(:img, html_options.merge({:src=>src}))
65
+    end
66
+    
67
+    def javascript_tag(content = nil, html_options = {})
68
+      content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => "text/javascript"))
69
+    end
70
+    
71
+    def link_to(name, href, html_options = {})
72
+      html_options = html_options.stringify_keys
73
+      confirm = html_options.delete("confirm")
74
+      onclick = "if (!confirm('#{html_escape(confirm)}')) return false;" if confirm
75
+      content_tag(:a, name, html_options.merge(:href => href, :onclick=>onclick))
76
+    end
77
+    
78
+    def link_to_function(name, *args, &block)
79
+      html_options = {}
80
+      html_options = args.pop if args.last.is_a? Hash
81
+      function = args[0] || ''
82
+      onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
83
+      href = html_options[:href] || '#'
84
+      content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
85
+    end
86
+    
87
+    def mail_to(email_address, name = nil, html_options = {})
88
+      html_options = html_options.stringify_keys
89
+      encode = html_options.delete("encode").to_s
90
+      cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
91
+      
92
+      string = ''
93
+      extras = ''
94
+      extras << "cc=#{CGI.escape(cc).gsub("+", "%20")}&" unless cc.nil?
95
+      extras << "bcc=#{CGI.escape(bcc).gsub("+", "%20")}&" unless bcc.nil?
96
+      extras << "body=#{CGI.escape(body).gsub("+", "%20")}&" unless body.nil?
97
+      extras << "subject=#{CGI.escape(subject).gsub("+", "%20")}&" unless subject.nil?
98
+      extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
99
+      
100
+      email_address = email_address.to_s
101
+      
102
+      email_address_obfuscated = email_address.dup
103
+      email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
104
+      email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
105
+      
106
+      if encode == "javascript"
107
+        "document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
108
+          string << sprintf("%%%x", c)
109
+        end
110
+        "<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
111
+      elsif encode == "hex"
112
+        email_address_encoded = ''
113
+        email_address_obfuscated.each_byte do |c|
114
+          email_address_encoded << sprintf("&#%d;", c)
115
+        end
116
+        
117
+        protocol = 'mailto:'
118
+        protocol.each_byte { |c| string << sprintf("&#%d;", c) }
119
+        
120
+        email_address.each_byte do |c|
121
+          char = c.chr
122
+          string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
123
+        end
124
+        content_tag "a", name || email_address_encoded, html_options.merge({ "href" => "#{string}#{extras}" })
125
+      else
126
+        content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
127
+      end
128
+    end
129
+    
130
+    private
131
+    
132
+      def cdata_section(content)
133
+        "<![CDATA[#{content}]]>"
134
+      end
135
+      
136
+      def javascript_cdata_section(content) #:nodoc:
137
+        "\n//#{cdata_section("\n#{content}\n//")}\n"
138
+      end
139
+      
140
+      def html_attributes(options)
141
+        unless options.blank?
142
+          attrs = []
143
+          options.each_pair do |key, value|
144
+            if value == true
145
+              attrs << %(#{key}="#{key}") if value
146
+            else
147
+              attrs << %(#{key}="#{value}") unless value.nil?
148
+            end
149
+          end
150
+          " #{attrs.sort * ' '}" unless attrs.empty?
151
+        end
152
+      end
153
+  end
154
+  include TagHelper
155
+  
156
+  # My added helpers
157
+  
158
+  def shorten_words (string, word_limit = 25)
159
+    words = string.split(/\s/)
160
+    if words.size >= word_limit
161
+      words[0,(word_limit-1)].join(" ") + '&hellip;'
162
+    else 
163
+      string
164
+    end
165
+  end
166
+  
167
+  def shorten (string, char_limit = 55)
168
+    chars = string.scan(/.{1,1}/)
169
+    if chars.size >= char_limit
170
+      chars[0,(char_limit-1)].join + '&hellip;'
171
+    else
172
+      "blah2"
173
+    end
174
+  end
175
+  
176
+  def absolute_url(input)
177
+    input.gsub(/(href|src)(\s*=\s*)(["'])(\/.*?)\3/) { $1 + $2 + $3 + "http://brandonmathis.com" + $4 + $3 }
178
+  end
179
+  
180
+  def full_url(input)
181
+    'http://brandonmathis.com'+input
182
+  end
183
+  def rp(input)
184
+    RubyPants.new(input).to_html
185
+  end
186
+  def style_amp(input)
187
+    input.gsub(" & "," <span class='amp'>&</span> ")
188
+  end
189
+end
190
+
0 191
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+!!! 1.1 Transitional
1
+%html(xmlns="http://www.w3.org/1999/xhtml" xml:lang="en")
2
+  %head
3
+    %title= page.title
4
+    - if page.respond_to? :description
5
+      %meta{:name=>"description", :content=>page.description}/
6
+    - if page.respond_to? :keywords
7
+      %meta{:name=>"keywords", :content=>page.keywords}/
8
+  %body
9
+    #header
10
+      .page_width
11
+        %a.title(href="/")Blog
12
+        %ul#header_nav.nav
13
+          %li.alpha
14
+            %a(href="/") Home
15
+    #page
16
+      .page_width
17
+        = content
18
+    #footer
19
+      .page_width Footer
0 20
\ No newline at end of file
1 21
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+!!! 1.1 Transitional
1
+%html(xmlns="http://www.w3.org/1999/xhtml" xml:lang="en")
2
+  %head
3
+    %title= page.title
4
+    - if page.respond_to? :description
5
+      %meta{:name=>"description", :content=>page.description}/
6
+    - if page.respond_to? :keywords
7
+      %meta{:name=>"keywords", :content=>page.keywords}/
8
+  %body
9
+    #header
10
+      .page_width
11
+        %a.title(href="/")Page Title
12
+        %ul#header_nav.nav
13
+          %li.alpha
14
+            %a(href="/") Home
15
+    #page
16
+      .page_width
17
+        = content
18
+    #footer
19
+      .page_width Footer
0 20
\ No newline at end of file
1 21
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+---
1
+title: Hello World!
2
+---
3
+
4
+How's it going?
0 5
\ No newline at end of file
1 6
new file mode 100644
... ...
@@ -0,0 +1,14 @@
0
+---
1
+layout: default
2
+title: Blog Archives
3
+---
4
+%h2 Blog Archives
5
+
6
+- posts = site.posts.group_by { |p| p.date.strftime("%Y") }
7
+- posts.keys.each do |year|
8
+  %h3= year
9
+  %ul
10
+    - posts[year].each do |post|
11
+      %li(class="#{(post.data['link'] ? "link" : nil )}")
12
+        = link_to(post.title, post.url)
13
+        %span.pubdate= post.date.strftime("%d %b, %Y")
0 14
\ No newline at end of file
1 15
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+---
1
+layout: nil
2
+---
3
+<?xml version="1.0" encoding="utf-8"?>
4
+%feed(xmlns="http://www.w3.org/2005/Atom")
5
+  %title Your Name - Your Site
6
+  %link(href="yoursite.com/atom.xml" rel="self")
7
+  %link(href="yoursite.com")
8
+  %updated= Time.now.xmlschema
9
+  %id http://yoursite.com/
10
+  %author
11
+    %name Your Name
12
+    %email user[at]domain.com
13
+  - site.posts[0..14].each do |post|
14
+    %entry
15
+      %title= rp(post.title)
16
+      %link(href="#{full_url(post.url)}")
17
+      %updated=post.date.xmlschema
18
+      %id= full_url(post.id)
19
+      %content(type="html")
20
+        = h(absolute_url(rp(post.content)))
0 21
\ No newline at end of file
1 22
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+---
1
+layout: basic/default
2
+title: Blog
3
+---
4
+.blog
5
+  - site.posts.sort_by(&:date).reverse[0..9].each_with_index do |post,index|
6
+    %h2= link_to(post.title, post.url, {:class=>"title"})
7
+    .article= post.content
0 8
\ No newline at end of file