lib/bluecloth.rb



#!/usr/bin/ruby

# 
# Bluecloth is a Ruby implementation of Markdown, a text-to-HTML conversion
# tool.
# 
# == Authors
# 
# * Michael Granger <ged@FaerieMUD.org>
# 
# == Contributors
#
# * Martin Chase <stillflame@FaerieMUD.org> - Peer review, helpful suggestions
# * Florian Gross <flgr@ccan.de> - Filter options, suggestions
#
# This product includes software developed by David Loren Parsons
# <http://www.pell.portland.or.us/~orc>.
# 
# == Version
#
# 2.1.0
#
# == Revision
# 
# $Revision: 34dd000f535c $
#
# == License
#
# Copyright (c) 2004-2011, Michael Granger
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the author/s, nor the names of the project's
#   contributors may be used to endorse or promote products derived from this
#   software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
class BlueCloth

	# Release Version
	VERSION = '2.2.0'

	# Version control revision
	REVISION = %q$Revision: 34dd000f535c $

	# The defaults for all supported options.
	DEFAULT_OPTIONS = {
		:alphalists       => true,
		:auto_links       => false,
		:definition_lists => false,
		:divquotes        => false,
		:escape_html      => false,
		:expand_tabs      => true,
		:header_labels    => false,
		:mdtest_1_compat  => false,
		:pandoc_headers   => false,
		:pseudoprotocols  => false,
		:relaxed          => false,
		:remove_images    => false,
		:remove_links     => false,
		:safe_links       => false,
		:smartypants      => true,
		:strict_mode      => true,
		:strikethrough    => true,
		:superscript      => false,
		:tables           => false,
		:tagtext_mode     => false,
		:xml_cdata        => false,
		:footnotes        => false,
	}.freeze

	# The number of characters of the original markdown source to include in the 
	# output of #inspect
	INSPECT_TEXT_LENGTH = 50


	#################################################################
	###	C L A S S   M E T H O D S
	#################################################################

	### Convert the specified +opthash+ into a flags bitmask. If it's already a
	### Fixnum (e.g., if someone passed in an ORed flags argument instead of an
	### opthash), just return it as-is.
	def self::flags_from_opthash( opthash={} )
		return opthash if opthash.is_a?( Integer )

		# Support BlueCloth1-style options
		if opthash == :filter_html || opthash == [:filter_html]
			opthash = { :escape_html => true }
		elsif opthash == :filter_styles
			opthash = {}
		elsif !opthash.is_a?( Hash )
			raise ArgumentError, "option %p not supported" % [ opthash ]
		end

		flags = 0

		if   opthash[:remove_links]     then flags |= MKD_NOLINKS;         end
		if   opthash[:remove_images]    then flags |= MKD_NOIMAGE;         end
		if ! opthash[:smartypants]      then flags |= MKD_NOPANTS;         end
		if   opthash[:escape_html]      then flags |= MKD_NOHTML;          end
		if   opthash[:strict_mode]      then flags |= MKD_STRICT;          end
		if   opthash[:tagtext_mode]     then flags |= MKD_TAGTEXT;         end
		if ! opthash[:pseudoprotocols]  then flags |= MKD_NO_EXT;          end
		if   opthash[:xml_cdata]        then flags |= MKD_CDATA;           end
		if ! opthash[:superscript]      then flags |= MKD_NOSUPERSCRIPT;   end
		if ! opthash[:relaxed]          then flags |= MKD_NORELAXED;       end
		if ! opthash[:tables]           then flags |= MKD_NOTABLES;        end
		if ! opthash[:strikethrough]    then flags |= MKD_NOSTRIKETHROUGH; end
		if   opthash[:header_labels]    then flags |= MKD_TOC;             end
		if   opthash[:mdtest_1_compat]  then flags |= MKD_1_COMPAT;        end
		if   opthash[:auto_links]       then flags |= MKD_AUTOLINK;        end
		if   opthash[:safe_links]       then flags |= MKD_SAFELINK;        end
		if ! opthash[:pandoc_headers]   then flags |= MKD_NOHEADER;        end
		if   opthash[:expand_tabs]      then flags |= MKD_TABSTOP;         end
		if ! opthash[:divquotes]        then flags |= MKD_NODIVQUOTE;      end
		if ! opthash[:alphalists]       then flags |= MKD_NOALPHALIST;     end
		if ! opthash[:definition_lists] then flags |= MKD_NODLIST;         end
		if   opthash[:footnotes]        then flags |= MKD_EXTRA_FOOTNOTE;  end

		return flags
	end


	### Returns a Hash that reflects the settings from the specified +flags+ Integer.
	def self::opthash_from_flags( flags=0 )
		flags = flags.to_i

		opthash = {}
		if  ( flags & MKD_NOLINKS         ).nonzero? then opthash[:remove_links]     = true; end
		if  ( flags & MKD_NOIMAGE         ).nonzero? then opthash[:remove_images]    = true; end
		if !( flags & MKD_NOPANTS         ).nonzero? then opthash[:smartypants]      = true; end
		if  ( flags & MKD_NOHTML          ).nonzero? then opthash[:escape_html]      = true; end
		if  ( flags & MKD_STRICT          ).nonzero? then opthash[:strict_mode]      = true; end
		if  ( flags & MKD_TAGTEXT         ).nonzero? then opthash[:tagtext_mode]     = true; end
		if !( flags & MKD_NO_EXT          ).nonzero? then opthash[:pseudoprotocols]  = true; end
		if  ( flags & MKD_CDATA           ).nonzero? then opthash[:xml_cdata]        = true; end
		if !( flags & MKD_NOSUPERSCRIPT   ).nonzero? then opthash[:superscript]      = true; end
		if !( flags & MKD_NORELAXED       ).nonzero? then opthash[:relaxed]          = true; end
		if !( flags & MKD_NOTABLES        ).nonzero? then opthash[:tables]           = true; end
		if !( flags & MKD_NOSTRIKETHROUGH ).nonzero? then opthash[:strikethrough]    = true; end
		if  ( flags & MKD_TOC             ).nonzero? then opthash[:header_labels]    = true; end
		if  ( flags & MKD_1_COMPAT        ).nonzero? then opthash[:mdtest_1_compat]  = true; end
		if  ( flags & MKD_AUTOLINK        ).nonzero? then opthash[:auto_links]       = true; end
		if  ( flags & MKD_SAFELINK        ).nonzero? then opthash[:safe_links]       = true; end
		if !( flags & MKD_NOHEADER        ).nonzero? then opthash[:pandoc_headers]   = true; end
		if  ( flags & MKD_TABSTOP         ).nonzero? then opthash[:expand_tabs]      = true; end
		if !( flags & MKD_NODIVQUOTE      ).nonzero? then opthash[:divquotes]        = true; end
		if !( flags & MKD_NOALPHALIST     ).nonzero? then opthash[:alphalists]       = true; end
		if !( flags & MKD_NODLIST         ).nonzero? then opthash[:definition_lists] = true; end
		if  ( flags & MKD_EXTRA_FOOTNOTE  ).nonzero? then opthash[:footnotes]        = true; end

		return opthash
	end


	#################################################################
	###	I N S T A N C E   M E T H O D S
	#################################################################

	### Return a human-readable representation of the object suitable for debugging.
	def inspect
		return "#<%s:0x%x text: %p; options: %p>" % [
			self.class.name,
			self.object_id / 2,
			self.text.length > INSPECT_TEXT_LENGTH ?
				self.text[ 0, INSPECT_TEXT_LENGTH - 5] + '[...]' :
				self.text,
			self.options,
		]
	end

end # class BlueCloth

begin
	require 'bluecloth_ext'
rescue LoadError => err
	# If it's a Windows binary gem, try the <major>.<minor> subdirectory
	if RUBY_PLATFORM =~/(mswin|mingw)/i
		major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
			raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
		require "#{major_minor}/bluecloth_ext"
	else
		raise
	end

end




# Set the top-level 'Markdown' constant if it isn't already set
::Markdown = ::BlueCloth unless defined?( ::Markdown )