Module: Typogruby

Extended by:
Typogruby
Included in:
Typogruby
Defined in:
lib/typogruby.rb,
lib/version.rb

Overview

A collection of simple helpers for improving web typograhy. Based on TypographyHelper by Luke Hartman and Typogrify.

Examples:

Using all filters

require 'typogruby'
Typogruby.improve('my text')

Using a single filter

Typogruby.initial_quotes('my text')

See Also:

Author:

Constant Summary collapse

VERSION =
'1.0.18'

Instance Method Summary collapse

Instance Method Details

#amp(text) ⇒ String

converts a & surrounded by optional whitespace or a non-breaking space to the HTML entity and surrounds it in a span with a styled class.

Examples:

amp('One & two')
# => 'One <span class="amp">&amp;</span> two'
amp('One &amp; two')
# => 'One <span class="amp">&amp;</span> two'
amp('One &#38; two')
# => 'One <span class="amp">&amp;</span> two'
amp('One&nbsp;&amp;&nbsp;two')
# => 'One&nbsp;<span class="amp">&amp;</span>&nbsp;two'

It won’t mess up & that are already wrapped, in entities or URLs

amp('One <span class="amp">&amp;</span> two')
# => 'One <span class="amp">&amp;</span> two'
amp('&ldquo;this&rdquo; & <a href="/?that&amp;test">that</a>')
# => '&ldquo;this&rdquo; <span class="amp">&amp;</span> <a href="/?that&amp;test">that</a>'

It should ignore standalone amps that are in attributes

amp('<link href="xyz.html" title="One & Two">xyz</link>')
# => '<link href="xyz.html" title="One & Two">xyz</link>'

Parameters:

  • text (String)

    input text

Returns:

  • (String)

    input text with ampersands wrapped



60
61
62
63
64
65
66
67
# File 'lib/typogruby.rb', line 60

def amp(text)
  # $1 is the part before the caps and $2 is the amp match
  exclude_sensitive_tags(text) do |t|
    t.gsub(/(\s|&nbsp;)&(?:amp;|#38;)?(\s|&nbsp;)/) { |str|
      $1 + '<span class="amp">&amp;</span>' + $2
    }.gsub(/(\w+)="(.*?)<span class="amp">&amp;<\/span>(.*?)"/, '\1="\2&amp;\3"')
  end
end

#caps(text) ⇒ String

surrounds two or more consecutive captial letters, perhaps with interspersed digits and periods in a span with a styled class.

Examples:

caps("A message from KU")
# => 'A message from <span class="caps">KU</span>'

Allows digits

caps("A message from 2KU2 with digits")
# => 'A message from <span class="caps">2KU2</span> with digits'

Allows ampersands

caps("A phone bill from AT&T")
# => 'A phone bill from <span class="caps">AT&amp;T</span>'

Ignores HTML attributes

caps('Download <a href="file.doc" title="PDF document">this file</a>')
# => 'Download <a href="file.doc" title="PDF document">this file</a>'

All caps with with apostrophes in them shouldn’t break. Only handles dump apostrophes though.

caps("JIMMY'S")
# => '<span class="caps">JIMMY\\'S</span>'
caps("<i>D.O.T.</i>HE34T<b>RFID</b>")
# => '<i><span class="caps">D.O.T.</span></i><span class="caps">HE34T</span><b><span class="caps">RFID</span></b>'

Sequences of capitalised letters with spaces are not eligible, since they may be names.

caps("L.A.P.D")
# => '<span class="caps">L.A.P.D.</span>'
caps("L. A. Paul")
# => 'L. A. Paul'

Parameters:

  • text (String)

    input text

Returns:

  • (String)

    input text with caps wrapped



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/typogruby.rb', line 159

def caps(text)
  exclude_sensitive_tags(text) do |t|
    # $1 and $2 are excluded HTML tags, $3 is the part before the caps and $4 is the caps match
    t.gsub(%r{
        (<[^/][^>]*?>)|                                      # Ignore any opening tag, so we don't mess up attribute values
        (\s|&nbsp;|^|'|"|>|)                                 # Make sure our capture is preceded by whitespace or quotes
        ([A-Z\d](?:(\.|'|&|&amp;|&\#38;)?[A-Z\d][\.']?){1,}) # Capture capital words, with optional dots, numbers or ampersands in between
        (?!\w)                                               # ...which must not be followed by a word character.
      }x) do |str|
      tag, before, caps = $1, $2, $3

      # Do nothing with the contents if ignored tags, the inside of an opening HTML element
      # so we don't mess up attribute values, or if our capture is only digits.
      if tag || caps =~ /^\d+\.?$/
        str
      elsif $3 =~ /^[\d\.]+$/
        before + caps
      else
        before + '<span class="caps">' + caps + '</span>'
      end
    end
  end
end

#entities(text) ⇒ String

Converts special characters (excluding HTML tags) to HTML entities.

Examples:

entities("Aloë Vera") # => "Alo&euml; Vera"

Parameters:

  • text (String)

    input text

Returns:

  • (String)

    input text with all special characters converted to HTML entities.



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/typogruby.rb', line 218

def entities(text)
  o = ''
  text.scan(/(?x)

      ( <\?(?:[^?]*|\?(?!>))*\?>
      | <!-- (?m:.*?) -->
      | <\/? (?i:a|abbr|acronym|address|applet|area|b|base|basefont|bdo|big|blockquote|body|br|button|caption|center|cite|code|col|colgroup|dd|del|dfn|dir|div|dl|dt|em|fieldset|font|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|hr|html|i|iframe|img|input|ins|isindex|kbd|label|legend|li|link|map|menu|meta|noframes|noscript|object|ol|optgroup|option|p|param|pre|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|ul|var)\b
          (?:[^>"']|"[^"]*"|'[^']*')*
        >
      | &(?:[a-zA-Z0-9]+|\#[0-9]+|\#x[0-9a-fA-F]+);
      )
      |([^<&]+|[<&])

    /x) do |tag, text|
    o << tag.to_s
    o << encode(text.to_s)
  end
  o
end

#improve(text) ⇒ String

main function to do all the functions from the method.

Parameters:

  • text (String)

    input text

Returns:

  • (String)

    input text with all filters applied



242
243
244
# File 'lib/typogruby.rb', line 242

def improve(text)
  initial_quotes(caps(smartypants(widont(amp(text)))))
end

#initial_quotes(text) ⇒ String

encloses initial single or double quote, or their entities (optionally preceeded by a block element and perhaps an inline element) with a span that can be styled.

Examples:

initial_quotes('"With primes"')
# => '<span class="dquo">"</span>With primes"'
initial_quotes("'With single primes'")
# => '<span class="quo">\\'</span>With single primes\\''

With primes and links

initial_quotes('<a href="#">"With primes and a link"</a>')
# => '<a href="#"><span class="dquo">"</span>With primes and a link"</a>'

with Smartypants-quotes

initial_quotes('&#8220;With smartypanted quotes&#8221;')
# => '<span class="dquo">&#8220;</span>With smartypanted quotes&#8221;'

Parameters:

  • text (String)

    input text

Returns:

  • (String)

    input text with initial quotes wrapped



203
204
205
206
207
208
# File 'lib/typogruby.rb', line 203

def initial_quotes(text)
  # $1 is the initial part of the string, $2 is the quote or entitity, and $3 is the double quote
  exclude_sensitive_tags(text) do |t|
    t.gsub(/((?:<(?:h[1-6]|p|li|dt|dd)[^>]*>|^)\s*(?:<(?:a|em|strong|span)[^>]*>)?)('|&#8216;|&lsquo;|("|&#8220;|&ldquo;))/) {$1 + "<span class=\"#{'d' if $3}quo\">#{$2}</span>"}
  end
end

#smartypants(text) ⇒ String

Applies smartypants to a given piece of text

Examples:

smartypants('The "Green" man')
# => 'The &#8220;Green&#8221; man'

Parameters:

  • text (String)

    input text

Returns:

  • (String)

    input text with smartypants applied

See Also:



31
32
33
# File 'lib/typogruby.rb', line 31

def smartypants(text)
  ::RubyPants.new(text).to_html
end

#widont(text) ⇒ String

replaces space(s) before the last word (or tag before the last word) before an optional closing element (a, em, span, strong) before a closing tag (p, h[1-6], li, dt, dd) or the end of the string.

Examples:

widont('A very simple test')
# => 'A very simple&nbsp;test'

Single word items shouldn’t be changed

widont('Test')
# => 'Test'
widont(' Test')
# => ' Test'
widont('<ul><li>Test</p></li><ul>')
# => '<ul><li>Test</p></li><ul>'
widont('<ul><li> Test</p></li><ul>')
# => '<ul><li> Test</p></li><ul>'

Nested tags

widont('<p>In a couple of paragraphs</p><p>paragraph two</p>')
# => '<p>In a couple of&nbsp;paragraphs</p><p>paragraph&nbsp;two</p>'
widont('<h1><a href="#">In a link inside a heading</i> </a></h1>')
# => '<h1><a href="#">In a link inside a&nbsp;heading</i> </a></h1>'
widont('<h1><a href="#">In a link</a> followed by other text</h1>')
# => '<h1><a href="#">In a link</a> followed by other&nbsp;text</h1>'

Empty HTMLs shouldn’t error

widont('<h1><a href="#"></a></h1>')
# => '<h1><a href="#"></a></h1>'

Excluded tags

widont('<div>Divs get no love!</div>')
# => '<div>Divs get no love!</div>'
widont('<pre>Neither do PREs</pre>')
# => '<pre>Neither do PREs</pre>'
widont('<div><p>But divs with paragraphs do!</p></div>')
# => '<div><p>But divs with paragraphs&nbsp;do!</p></div>'

Parameters:

  • text (String)

    input text

Returns:

  • (String)

    input text with non-breaking spaces inserted

See Also:



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/typogruby.rb', line 112

def widont(text)
  exclude_sensitive_tags(text) do |t|
    t.gsub(%r{
      (<[^/][^>]*?>)|                                 # Ignore any opening tag, so we don't mess up attribute values
      ((?:</?(?:a|em|span|strong|i|b)[^>]*>)|[^<>\s]) # must be proceeded by an approved inline opening or closing tag or a nontag/nonspace
      \s+                                             # the space to replace
      (([^<>\s]+)                                     # must be flollowed by non-tag non-space characters
      \s*                                             # optional white space!
      (</(a|em|span|strong|i|b)>\s*)*                 # optional closing inline tags with optional white space after each
      ((</(p|h[1-6]|li|dt|dd)>)|$))                   # end with a closing p, h1-6, li or the end of the string
    }xm) { |match| $1 ? match : $2 + (match.include?('&nbsp;') ? ' ' : '&nbsp;') + $3 } # Make sure to not add another nbsp before one already there
  end
end