Redmine 4.1.1

This commit is contained in:
Manuel Cillero 2020-11-22 21:20:06 +01:00
parent 33e7b881a5
commit 3d976f1b3b
1593 changed files with 36180 additions and 19489 deletions

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -28,13 +30,13 @@ module Redmine
}
def self.to_text(html)
html = html.gsub(/[\n\r]/, '').squeeze(' ')
html = html.gsub(/[\n\r]/, ' ')
doc = Loofah.document(html)
doc.scrub!(WikiTags.new(tags))
doc.scrub!(:newline_block_elements)
Loofah::Helpers.remove_extraneous_whitespace(doc.text).strip
Loofah.remove_extraneous_whitespace(doc.text(:encode_special_chars => false)).strip.squeeze(' ').gsub(/^ +/, '')
end
class WikiTags < ::Loofah::Scrubber
@ -42,7 +44,7 @@ module Redmine
@direction = :bottom_up
@tags_to_text = tags_to_text || {}
end
def scrub(node)
formatting = @tags_to_text[node.name]
case formatting
@ -52,6 +54,9 @@ module Redmine
when String
node.add_next_sibling Nokogiri::XML::Text.new(formatting, node.document)
node.remove
when Proc
node.add_next_sibling formatting.call(node)
node.remove
else
CONTINUE
end

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -24,10 +26,16 @@ module Redmine
Redmine::WikiFormatting::Macros.available_macros.key?(name.to_sym)
end
def exec_macro(name, obj, args, text)
def exec_macro(name, obj, args, text, options={})
macro_options = Redmine::WikiFormatting::Macros.available_macros[name.to_sym]
return unless macro_options
if options[:inline_attachments] == false
Redmine::WikiFormatting::Macros.inline_attachments = false
else
Redmine::WikiFormatting::Macros.inline_attachments = true
end
method_name = "macro_#{name}"
unless macro_options[:parse_args] == false
args = args.split(',').map(&:strip)
@ -57,7 +65,9 @@ module Redmine
end
@@available_macros = {}
@@inline_attachments = true
mattr_accessor :available_macros
mattr_accessor :inline_attachments
class << self
# Plugins can use this method to define new macros:
@ -67,7 +77,7 @@ module Redmine
# macro :my_macro do |obj, args|
# "My macro output"
# end
#
#
# desc "This is my macro that accepts a block of text"
# macro :my_macro do |obj, args, text|
# "My macro output"
@ -81,7 +91,7 @@ module Redmine
#
# Options:
# * :desc - A description of the macro
# * :parse_args => false - Disables arguments parsing (the whole arguments
# * :parse_args => false - Disables arguments parsing (the whole arguments
# string is passed to the macro)
#
# Macro blocks accept 2 or 3 arguments:
@ -89,7 +99,7 @@ module Redmine
# * args: macro arguments
# * text: the block of text given to the macro (should be present only if the
# macro accepts a block of text). text is a String or nil if the macro is
# invoked without a block of text.
# invoked without a block of text.
#
# Examples:
# By default, when the macro is invoked, the comma separated list of arguments
@ -141,7 +151,7 @@ module Redmine
# If a block of text is given, the closing tag }} must be at the start of a new line.
def macro(name, options={}, &block)
options.assert_valid_keys(:desc, :parse_args)
unless name.to_s.match(/\A\w+\z/)
unless /\A\w+\z/.match?(name.to_s)
raise "Invalid macro name: #{name} (only 0-9, A-Z, a-z and _ characters are accepted)"
end
unless block_given?
@ -162,7 +172,7 @@ module Redmine
# Builtin macros
desc "Sample macro."
macro :hello_world do |obj, args, text|
h("Hello world! Object: #{obj.class.name}, " +
h("Hello world! Object: #{obj.class.name}, " +
(args.empty? ? "Called with no argument" : "Arguments: #{args.join(', ')}") +
" and " + (text.present? ? "a #{text.size} bytes long block of text." : "no block of text.")
)
@ -209,7 +219,7 @@ module Redmine
@included_wiki_pages ||= []
raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.id)
@included_wiki_pages << page.id
out = textilizable(page.content, :text, :attachments => page.attachments, :headings => false)
out = textilizable(page.content, :text, :attachments => page.attachments, :headings => false, :inline_attachments => @@inline_attachments)
@included_wiki_pages.pop
out
end
@ -223,13 +233,14 @@ module Redmine
hide_label = args[1] || args[0] || l(:button_hide)
js = "$('##{html_id}-show, ##{html_id}-hide').toggle(); $('##{html_id}').fadeToggle(150);"
out = ''.html_safe
out << link_to_function(show_label, js, :id => "#{html_id}-show", :class => 'collapsible collapsed')
out << link_to_function(hide_label, js, :id => "#{html_id}-hide", :class => 'collapsible', :style => 'display:none;')
out << content_tag('div', textilizable(text, :object => obj, :headings => false), :id => html_id, :class => 'collapsed-text', :style => 'display:none;')
out << link_to_function(show_label, js, :id => "#{html_id}-show", :class => 'icon icon-collapsed collapsible')
out << link_to_function(hide_label, js, :id => "#{html_id}-hide", :class => 'icon icon-expended collapsible', :style => 'display:none;')
out << content_tag('div', textilizable(text, :object => obj, :headings => false, :inline_attachments => @@inline_attachments), :id => html_id, :class => 'collapsed-text', :style => 'display:none;')
out
end
desc "Displays a clickable thumbnail of an attached image. Examples:\n\n" +
desc "Displays a clickable thumbnail of an attached image.\n" +
"Default size is 200 pixels. Examples:\n\n" +
"{{thumbnail(image.png)}}\n" +
"{{thumbnail(image.png, size=300, title=Thumbnail)}} -- with custom title and size"
macro :thumbnail do |obj, args|
@ -237,9 +248,9 @@ module Redmine
filename = args.first
raise 'Filename required' unless filename.present?
size = options[:size]
raise 'Invalid size parameter' unless size.nil? || size.match(/^\d+$/)
raise 'Invalid size parameter' unless size.nil? || /^\d+$/.match?(size)
size = size.to_i
size = nil unless size > 0
size = 200 unless size > 0
if obj && obj.respond_to?(:attachments) && attachment = Attachment.latest_attach(obj.attachments, filename)
title = options[:title] || attachment.title
thumbnail_url = url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment, :size => size, :only_path => @only_path)
@ -251,6 +262,33 @@ module Redmine
raise "Attachment #{filename} not found"
end
end
desc "Displays an issue link including additional information. Examples:\n\n" +
"{{issue(123)}} -- Issue #123: Enhance macro capabilities\n" +
"{{issue(123, project=true)}} -- Andromeda - Issue #123: Enhance macro capabilities\n" +
"{{issue(123, tracker=false)}} -- #123: Enhance macro capabilities\n" +
"{{issue(123, subject=false, project=true)}} -- Andromeda - Issue #123\n"
macro :issue do |obj, args|
args, options = extract_macro_options(args, :project, :subject, :tracker)
id = args.first
issue = Issue.visible.find_by(id: id)
if issue
# remove invalid options
options.delete_if { |k,v| v != 'true' && v != 'false' }
# turn string values into boolean
options.each do |k, v|
options[k] = v == 'true'
end
link_to_issue(issue, options)
else
# Fall back to regular issue link format to indicate, that there
# should have been something.
"##{id}"
end
end
end
end
end

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -52,24 +54,16 @@ module Redmine
end
class Formatter
include Redmine::WikiFormatting::LinksHelper
alias :inline_restore_redmine_links :restore_redmine_links
def initialize(text)
@text = text
end
def to_html(*args)
html = formatter.render(@text)
# restore wiki links eg. [[Foo]]
html.gsub!(%r{\[<a href="(.*?)">(.*?)</a>\]}) do
"[[#{$2}]]"
end
# restore Redmine links with double-quotes, eg. version:"1.0"
html.gsub!(/(\w):&quot;(.+?)&quot;/) do
"#{$1}:\"#{$2}\""
end
# restore user links with @ in login name eg. [@jsmith@somenet.foo]
html.gsub!(%r{[@\A]<a href="mailto:(.*?)">(.*?)</a>}) do
"@#{$2}"
end
html = inline_restore_redmine_links(html)
html
end
@ -89,14 +83,14 @@ module Redmine
end
def extract_sections(index)
sections = ['', '', '']
sections = [+'', +'', +'']
offset = 0
i = 0
l = 1
inside_pre = false
@text.split(/(^(?:.+\r?\n\r?(?:\=+|\-+)|#+.+|(?:~~~|```).*)\s*$)/).each do |part|
level = nil
if part =~ /\A(~{3,}|`{3,})(\S+)?\s*$/
if part =~ /\A(~{3,}|`{3,})(\s*\S+)?\s*$/
if !inside_pre
inside_pre = true
elsif !$2
@ -141,7 +135,8 @@ module Redmine
:superscript => true,
:no_intra_emphasis => true,
:footnotes => true,
:lax_spacing => true
:lax_spacing => true,
:underline => true
)
end
end

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -19,10 +21,10 @@ module Redmine
module WikiFormatting
module Markdown
module Helper
def wikitoolbar_for(field_id)
def wikitoolbar_for(field_id, preview_url = preview_text_path)
heads_for_wiki_formatter
url = "#{Redmine::Utils.relative_url_root}/help/#{current_language.to_s.downcase}/wiki_syntax_markdown.html"
javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.draw();")
javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.setPreviewUrl('#{escape_javascript preview_url}'); wikiToolbar.draw();")
end
def initial_page_content(page)

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -23,15 +25,27 @@ module Redmine
self.tags = tags.merge(
'b' => {:pre => '**', :post => '**'},
'strong' => {:pre => '**', :post => '**'},
'i' => {:pre => '_', :post => '_'},
'em' => {:pre => '_', :post => '_'},
'i' => {:pre => '*', :post => '*'},
'em' => {:pre => '*', :post => '*'},
'u' => {:pre => '_', :post => '_'},
'strike' => {:pre => '~~', :post => '~~'},
'h1' => {:pre => "\n\n# ", :post => "\n\n"},
'h2' => {:pre => "\n\n## ", :post => "\n\n"},
'h3' => {:pre => "\n\n### ", :post => "\n\n"},
'h4' => {:pre => "\n\n#### ", :post => "\n\n"},
'h5' => {:pre => "\n\n##### ", :post => "\n\n"},
'h6' => {:pre => "\n\n###### ", :post => "\n\n"}
'h6' => {:pre => "\n\n###### ", :post => "\n\n"},
'th' => {:pre => '*', :post => "*\n"},
'td' => {:pre => '', :post => "\n"},
'a' => lambda do |node|
if node.content.present? && node.attributes.key?('href')
%| [#{node.content}](#{node.attributes['href'].value}) |
elsif node.attributes.key?('href')
%| #{node.attributes['href'].value} |
else
node.content
end
end
)
end
end

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -27,9 +29,10 @@ module Redmine
alias :inline_auto_link :auto_link!
alias :inline_auto_mailto :auto_mailto!
alias :inline_restore_redmine_links :restore_redmine_links
# auto_link rule after textile rules so that it doesn't break !image_url! tags
RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto]
RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_restore_redmine_links]
def initialize(*args)
super
@ -62,9 +65,9 @@ module Redmine
@pre_list = []
text = self.dup
rip_offtags text, false, false
before = ''
s = ''
after = ''
before = +''
s = +''
after = +''
i = 0
l = 1
started = false
@ -105,7 +108,7 @@ module Redmine
sections
end
private
private
# Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
# <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
@ -125,6 +128,7 @@ module Redmine
language = $1 || $2
text = $3
if Redmine::SyntaxHighlighting.language_supported?(language)
text.gsub!(/x%x%/, '&')
content = "<code class=\"#{language} syntaxhl\">" +
Redmine::SyntaxHighlighting.highlight_by_language(text, language)
else

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -19,11 +21,11 @@ module Redmine
module WikiFormatting
module Textile
module Helper
def wikitoolbar_for(field_id)
def wikitoolbar_for(field_id, preview_url = preview_text_path)
heads_for_wiki_formatter
# Is there a simple way to link to a public resource?
url = "#{Redmine::Utils.relative_url_root}/help/#{current_language.to_s.downcase}/wiki_syntax_textile.html"
javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.draw();")
javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.setPreviewUrl('#{escape_javascript preview_url}'); wikiToolbar.draw();")
end
def initial_page_content(page)
@ -33,7 +35,8 @@ module Redmine
def heads_for_wiki_formatter
unless @heads_for_wiki_formatter_included
content_for :header_tags do
javascript_include_tag('jstoolbar/jstoolbar-textile.min') +
javascript_include_tag('jstoolbar/jstoolbar') +
javascript_include_tag('jstoolbar/textile') +
javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language.to_s.downcase}") +
javascript_tag("var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};") +
stylesheet_link_tag('jstoolbar')

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -32,7 +34,18 @@ module Redmine
'h3' => {:pre => "\n\nh3. ", :post => "\n\n"},
'h4' => {:pre => "\n\nh4. ", :post => "\n\n"},
'h5' => {:pre => "\n\nh5. ", :post => "\n\n"},
'h6' => {:pre => "\n\nh6. ", :post => "\n\n"}
'h6' => {:pre => "\n\nh6. ", :post => "\n\n"},
'th' => {:pre => '*', :post => "*\n"},
'td' => {:pre => '', :post => "\n"},
'a' => lambda do |node|
if node.content.present? && node.attributes.key?('href')
%| "#{node.content}":#{node.attributes['href'].value} |
elsif node.attributes.key?('href')
%| #{node.attributes['href'].value} |
else
node.content
end
end
)
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# vim:ts=4:sw=4:
# = RedCloth - Textile and Markdown Hybrid for Ruby
#
@ -71,33 +73,33 @@
#
# == Links
#
# To make a hypertext link, put the link text in "quotation
# To make a hypertext link, put the link text in "quotation
# marks" followed immediately by a colon and the URL of the link.
#
# Optional: text in (parentheses) following the link text,
# but before the closing quotation mark, will become a Title
#
# Optional: text in (parentheses) following the link text,
# but before the closing quotation mark, will become a Title
# attribute for the link, visible as a tool tip when a cursor is above it.
#
#
# Example:
#
# "This is a link (This is a title) ":http://www.textism.com
#
#
# Will become:
#
#
# <a href="http://www.textism.com" title="This is a title">This is a link</a>
#
# == Images
#
# To insert an image, put the URL for the image inside exclamation marks.
#
# Optional: text that immediately follows the URL in (parentheses) will
# be used as the Alt text for the image. Images on the web should always
# have descriptive Alt text for the benefit of readers using non-graphical
# Optional: text that immediately follows the URL in (parentheses) will
# be used as the Alt text for the image. Images on the web should always
# have descriptive Alt text for the benefit of readers using non-graphical
# browsers.
#
# Optional: place a colon followed by a URL immediately after the
# Optional: place a colon followed by a URL immediately after the
# closing ! to make the image into a link.
#
#
# Example:
#
# !http://www.textism.com/common/textist.gif(Textist)!
@ -116,13 +118,13 @@
#
# == Defining Acronyms
#
# HTML allows authors to define acronyms via the tag. The definition appears as a
# tool tip when a cursor hovers over the acronym. A crucial aid to clear writing,
# HTML allows authors to define acronyms via the tag. The definition appears as a
# tool tip when a cursor hovers over the acronym. A crucial aid to clear writing,
# this should be used at least once for each acronym in documents where they appear.
#
# To quickly define an acronym in Textile, place the full text in (parentheses)
# To quickly define an acronym in Textile, place the full text in (parentheses)
# immediately following the acronym.
#
#
# Example:
#
# ACLU(American Civil Liberties Union)
@ -145,7 +147,7 @@
# (background:#ddd;color:red). |{}| | | |
#
# == Using RedCloth
#
#
# RedCloth is simply an extension of the String class, which can handle
# Textile formatting. Use it like a String and output HTML with its
# RedCloth#to_html method.
@ -253,7 +255,7 @@ class RedCloth3 < String
# #=>"<h1>A &lt;b&gt;bold&lt;/b&gt; man</h1>"
#
def initialize( string, restrictions = [] )
restrictions.each { |r| method( "#{ r }=" ).call( true ) }
restrictions.each { |r| method( "#{r}=" ).call( true ) }
super( string )
end
@ -268,14 +270,14 @@ class RedCloth3 < String
rules = DEFAULT_RULES if rules.empty?
# make our working copy
text = self.dup
@urlrefs = {}
@shelf = []
textile_rules = [:block_textile_table, :block_textile_lists,
:block_textile_prefix, :inline_textile_image, :inline_textile_link,
:inline_textile_code, :inline_textile_span, :glyphs_textile]
:block_textile_prefix, :inline_textile_image, :inline_textile_code,
:inline_textile_span, :inline_textile_link, :glyphs_textile]
markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
:block_markdown_bq, :block_markdown_lists,
:block_markdown_bq, :block_markdown_lists,
:inline_markdown_reflink, :inline_markdown_link]
@rules = rules.collect do |rule|
case rule
@ -289,8 +291,8 @@ class RedCloth3 < String
end.flatten
# standard clean up
incoming_entities text
clean_white_space text
incoming_entities text
clean_white_space text
# start processor
@pre_list = []
@ -299,7 +301,7 @@ class RedCloth3 < String
escape_html_tags text
# need to do this before #hard_break and #blocks
block_textile_quotes text unless @lite_mode
hard_break text
hard_break text
unless @lite_mode
refs text
blocks text
@ -314,28 +316,23 @@ class RedCloth3 < String
clean_html text if filter_html
text.strip!
text
end
#######
private
#######
private
#
# 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
[[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.
#
@ -357,7 +354,7 @@ class RedCloth3 < String
# Text markup tags, don't conflict with block tags
SIMPLE_HTML_TAGS = [
'tt', 'b', 'i', 'big', 'small', 'em', 'strong', 'dfn', 'code',
'tt', 'b', 'i', 'big', 'small', 'em', 'strong', 'dfn', 'code',
'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'a', 'img', 'br',
'br', 'map', 'q', 'sub', 'sup', 'span', 'bdo'
]
@ -373,14 +370,14 @@ class RedCloth3 < String
['+', 'ins', :limit],
['^', 'sup', :limit],
['~', 'sub', :limit]
]
]
QTAGS_JOIN = QTAGS.map {|rc, ht, rtype| Regexp::quote rc}.join('|')
QTAGS.collect! do |rc, ht, rtype|
rcq = Regexp::quote rc
re =
case rtype
when :limit
case rtype
when :limit
/(^|[>\s\(]) # sta
(?!\-\-)
(#{QTAGS_JOIN}|) # oqs
@ -390,38 +387,38 @@ class RedCloth3 < String
#{rcq}
(#{QTAGS_JOIN}|) # oqa
(?=[[:punct:]]|<|\s|\)|$)/x
else
else
/(#{rcq})
(#{C})
(?::(\S+))?
([[:word:]]|[^\s\-].*?[^\s\-])
#{rcq}/xm
end
#{rcq}/xm
end
[rc, ht, re, rtype]
end
# Elements to handle
GLYPHS = [
# [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1&#8217;' ], # single closing
# [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '&#8217;' ], # single closing
# [ /\'/, '&#8216;' ], # single opening
# [ /</, '&lt;' ], # less-than
# [ />/, '&gt;' ], # greater-than
# [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1&#8221;' ], # double closing
# [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '&#8221;' ], # double closing
# [ /"/, '&#8220;' ], # double opening
# [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
# [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
# [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]+[A-Z0-9])([^<A-Za-z0-9]|$)/, '\1<span class="caps">\2</span>\3', :no_span_caps ], # 3+ uppercase caps
# [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
# [ /\s->\s/, ' &rarr; ' ], # right arrow
# [ /\s-\s/, ' &#8211; ' ], # en dash
# [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
# [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
# [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
# [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
# [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1&#8217;' ], # single closing
# [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '&#8217;' ], # single closing
# [ /\'/, '&#8216;' ], # single opening
# [ /</, '&lt;' ], # less-than
# [ />/, '&gt;' ], # greater-than
# [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1&#8221;' ], # double closing
# [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '&#8221;' ], # double closing
# [ /"/, '&#8220;' ], # double opening
# [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
# [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
# [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]+[A-Z0-9])([^<A-Za-z0-9]|$)/, '\1<span class="caps">\2</span>\3', :no_span_caps ], # 3+ uppercase caps
# [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
# [ /\s->\s/, ' &rarr; ' ], # right arrow
# [ /\s-\s/, ' &#8211; ' ], # en dash
# [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
# [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
# [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
# [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
]
H_ALGN_VALS = {
@ -453,10 +450,10 @@ class RedCloth3 < String
# Search and replace for Textile glyphs (quotes, dashes, other symbols)
def pgl( text )
#GLYPHS.each do |re, resub, tog|
# GLYPHS.each do |re, resub, tog|
# next if tog and method( tog ).call
# text.gsub! re, resub
#end
# end
text.gsub!(/\b([A-Z][A-Z0-9]{1,})\b(?:[(]([^)]*)[)])/) do |m|
"<abbr title=\"#{htmlesc $2}\">#{$1}</abbr>"
end
@ -464,20 +461,19 @@ class RedCloth3 < String
# Parses Textile attribute lists and builds an HTML attribute string
def pba( text_in, element = "" )
return '' unless text_in
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
style << "vertical-align:#{v_align($&)};" if text =~ A_VLGN
end
if text.sub!( /\{([^"}]*)\}/, '' ) && !filter_styles
sanitized = sanitize_styles($1)
style << "#{ sanitized };" unless sanitized.blank?
style << "#{sanitized};" unless sanitized.blank?
end
lang = $1 if
@ -485,13 +481,13 @@ class RedCloth3 < String
cls = $1 if
text.sub!( /\(([^()]+?)\)/, '' )
style << "padding-left:#{ $1.length }em;" if
style << "padding-left:#{$1.length}em;" if
text.sub!( /([(]+)/, '' )
style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
style << "padding-right:#{$1.length}em;" if text.sub!( /([)]+)/, '' )
style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
style << "text-align:#{h_align($&)};" if text =~ A_HLGN
cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
@ -503,18 +499,18 @@ class RedCloth3 < String
id = id.starts_with?('wiki-id-') ? id : "wiki-id-#{id}" if id
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 = +''
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
STYLES_RE = /^(color|width|height|border|background|padding|margin|font|text|float)(-[a-z]+)*:\s*((\d+%?|\d+px|\d+(\.\d+)?em|#[0-9a-f]+|[a-z]+)\s*)+$/i
STYLES_RE = /^(color|(min-|max-)?+(width|height)|border|background|padding|margin|font|text|float)(-[a-z]+)*:\s*((\d+%?|\d+px|\d+(\.\d+)?em|#[0-9a-f]+|[a-z]+)\s*)+$/i
def sanitize_styles(str)
styles = str.split(";").map(&:strip)
@ -525,11 +521,10 @@ class RedCloth3 < String
end
TABLE_RE = /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)(\n\n|\Z)/m
# Parses a Textile table block, building HTML from the result.
def block_textile_table( text )
text.gsub!( TABLE_RE ) do |matches|
# Parses a Textile table block, building HTML from the result.
def block_textile_table( text )
text.gsub!( TABLE_RE ) do |matches|
tatts, fullrow = $~[1..2]
tatts = pba( tatts, 'table' )
tatts = shelve( tatts ) if tatts
@ -538,7 +533,7 @@ class RedCloth3 < String
fullrow.each_line do |row|
ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
cells = []
# the regexp prevents wiki links with a | from being cut as cells
# the regexp prevents wiki links with a | from being cut as cells
row.scan(/\|(_?#{S}#{A}#{C}\. ?)?((\[\[[^|\]]*\|[^|\]]*\]\]|[^|])*?)(?=\|)/) do |modifiers, cell|
ctyp = 'd'
ctyp = 'h' if modifiers && modifiers =~ /^_/
@ -547,12 +542,12 @@ class RedCloth3 < String
catts = pba( modifiers, 'td' ) if modifiers
catts = shelve( catts ) if catts
cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
cells << "\t\t\t<t#{ctyp}#{catts}>#{cell}</t#{ctyp}>"
end
ratts = shelve( ratts ) if ratts
rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
rows << "\t\t<tr#{ratts}>\n#{cells.join("\n")}\n\t\t</tr>"
end
"\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
"\t<table#{tatts}>\n#{rows.join("\n")}\n\t</table>\n\n"
end
end
@ -560,19 +555,19 @@ class RedCloth3 < String
LISTS_CONTENT_RE = /^([#*]+)(#{A}#{C}) (.*)$/m
# Parses Textile lists and generates HTML
def block_textile_lists( text )
def block_textile_lists( text )
text.gsub!( LISTS_RE ) do |match|
lines = match.split( /\n/ )
last_line = -1
depth = []
lines.each_with_index do |line, line_id|
if line =~ LISTS_CONTENT_RE
if line =~ LISTS_CONTENT_RE
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] << "</li>\n\t</#{ lT( depth[i] ) }l>\n\t"
lines[line_id - 1] << "</li>\n\t</#{lT(depth[i])}l>\n\t"
depth.pop
end
end
@ -580,39 +575,38 @@ class RedCloth3 < String
lines[line_id - 1] << '</li>'
end
end
unless depth.last == tl
if depth.last != tl
depth << tl
atts = pba( atts )
atts = shelve( atts ) if atts
lines[line_id] = "\t<#{ lT(tl) }l#{ atts }>\n\t<li>#{ content }"
lines[line_id] = +"\t<#{lT(tl)}l#{atts}>\n\t<li>#{content}"
else
lines[line_id] = "\t\t<li>#{ content }"
lines[line_id] = +"\t\t<li>#{content}"
end
last_line = line_id
else
last_line = line_id
end
if line_id - last_line > 1 or line_id == lines.length - 1
while v = depth.pop
lines[last_line] << "</li>\n\t</#{ lT( v ) }l>"
lines[last_line] << "</li>\n\t</#{lT(v)}l>"
end
end
end
lines.join( "\n" )
end
end
QUOTES_RE = /(^>+([^\n]*?)(\n|$))+/m
QUOTES_CONTENT_RE = /^([> ]+)(.*)$/m
def block_textile_quotes( text )
text.gsub!( QUOTES_RE ) do |match|
lines = match.split( /\n/ )
quotes = ''
quotes = +''
indent = 0
lines.each do |line|
line =~ QUOTES_CONTENT_RE
line =~ QUOTES_CONTENT_RE
bq,content = $1, $2
l = bq.count('>')
if l != indent
@ -633,15 +627,15 @@ class RedCloth3 < String
@
(?=\W)/x
def inline_textile_code( text )
def inline_textile_code( text )
text.gsub!( CODE_RE ) do |m|
before,lang,code,after = $~[1..4]
lang = " lang=\"#{ lang }\"" if lang
rip_offtags( "#{ before }<code#{ lang }>#{ code }</code>#{ after }", false )
lang = " lang=\"#{lang}\"" if lang
rip_offtags( +"#{before}<code#{lang}>#{code}</code>#{after}", false )
end
end
def lT( text )
def lT( text )
text =~ /\#$/ ? 'o' : 'u'
end
@ -676,35 +670,34 @@ class RedCloth3 < String
end
end
block_applied = 0
block_applied = 0
@rules.each do |rule_name|
block_applied += 1 if ( rule_name.to_s.match /^block_/ and method( rule_name ).call( blk ) )
block_applied += 1 if rule_name.to_s.match /^block_/ and method(rule_name).call(blk)
end
if block_applied.zero?
if deep_code
blk = "\t<pre><code>#{ blk }</code></pre>"
blk = "\t<pre><code>#{blk}</code></pre>"
else
blk = "\t<p>#{ blk }</p>"
blk = "\t<p>#{blk}</p>"
end
end
# hard_break blk
blk + "\n#{ code_blk }"
blk + "\n#{code_blk}"
end
end
end.join( "\n\n" ) )
end
def textile_bq( tag, atts, cite, content )
cite, cite_title = check_refs( cite )
cite = " cite=\"#{ cite }\"" if cite
cite = " cite=\"#{cite}\"" if cite
atts = shelve( atts ) if atts
"\t<blockquote#{ cite }>\n\t\t<p#{ atts }>#{ content }</p>\n\t</blockquote>"
"\t<blockquote#{cite}>\n\t\t<p#{atts}>#{content}</p>\n\t</blockquote>"
end
def textile_p( tag, atts, cite, content )
atts = shelve( atts ) if atts
"\t<#{ tag }#{ atts }>#{ content }</#{ tag }>"
"\t<#{tag}#{atts}>#{content}</#{tag}>"
end
alias textile_h1 textile_p
@ -715,35 +708,35 @@ class RedCloth3 < String
alias textile_h6 textile_p
def textile_fn_( tag, num, atts, cite, content )
atts << " id=\"fn#{ num }\" class=\"footnote\""
content = "<sup>#{ num }</sup> #{ content }"
atts << " id=\"fn#{num}\" class=\"footnote\""
content = "<sup>#{num}</sup> #{content}"
atts = shelve( atts ) if atts
"\t<p#{ atts }>#{ content }</p>"
"\t<p#{atts}>#{content}</p>"
end
BLOCK_RE = /^(([a-z]+)(\d*))(#{A}#{C})\.(?::(\S+))? (.*)$/m
def block_textile_prefix( text )
def block_textile_prefix( text )
if text =~ BLOCK_RE
tag,tagpre,num,atts,cite,content = $~[1..6]
atts = pba( atts )
# pass to prefix handler
replacement = nil
if respond_to? "textile_#{ tag }", true
replacement = method( "textile_#{ tag }" ).call( tag, atts, cite, content )
elsif respond_to? "textile_#{ tagpre }_", true
replacement = method( "textile_#{ tagpre }_" ).call( tagpre, num, atts, cite, content )
if respond_to? "textile_#{tag}", true
replacement = method( "textile_#{tag}" ).call( tag, atts, cite, content )
elsif respond_to? "textile_#{tagpre}_", true
replacement = method( "textile_#{tagpre}_" ).call( tagpre, num, atts, cite, content )
end
text.gsub!( $& ) { replacement } if replacement
end
end
SETEXT_RE = /\A(.+?)\n([=-])[=-]* *$/m
def block_markdown_setext( text )
if text =~ SETEXT_RE
tag = if $2 == "="; "h1"; else; "h2"; end
blk, cont = "<#{ tag }>#{ $1 }</#{ tag }>", $'
tag = ($2 == "=" ? "h1" : "h2")
blk, cont = "<#{tag}>#{$1}</#{tag}>", $'
blocks cont
text.replace( blk + cont )
end
@ -757,8 +750,8 @@ class RedCloth3 < String
$/x
def block_markdown_atx( text )
if text =~ ATX_RE
tag = "h#{ $1.length }"
blk, cont = "<#{ tag }>#{ $2 }</#{ tag }>\n\n", $'
tag = "h#{$1.length}"
blk, cont = "<#{tag}>#{$2}</#{tag}>\n\n", $'
blocks cont
text.replace( blk + cont )
end
@ -772,7 +765,7 @@ class RedCloth3 < String
flush_left blk
blocks blk
blk.gsub!( /^(\S)/, "\t\\1" )
"<blockquote>\n#{ blk }\n</blockquote>\n\n"
"<blockquote>\n#{blk}\n</blockquote>\n\n"
end
end
@ -790,10 +783,9 @@ class RedCloth3 < String
def block_markdown_lists( text )
end
def inline_textile_span( text )
def inline_textile_span( text )
QTAGS.each do |qtag_rc, ht, qtag_re, rtype|
text.gsub!( qtag_re ) do |m|
case rtype
when :limit
sta,oqs,qtag,content,oqa = $~[1..6]
@ -808,8 +800,7 @@ class RedCloth3 < String
atts = pba( atts )
atts = shelve( atts ) if atts
"#{ sta }#{ oqs }<#{ ht }#{ atts }>#{ content }</#{ ht }>#{ oqa }"
"#{sta}#{oqs}<#{ht}#{atts}>#{content}</#{ht}>#{oqa}"
end
end
end
@ -826,36 +817,37 @@ class RedCloth3 < String
( # $url
(\/|[a-zA-Z]+:\/\/|www\.|mailto:) # $proto
[[:alnum:]_\/]\S+?
)
)
(\/)? # $slash
([^[:alnum:]_\=\/;\(\)]*?) # $post
([^[:alnum:]_\=\/;\(\)\-]*?) # $post
)
(?=<|\s|$)
/x
#"
def inline_textile_link( text )
/x
def inline_textile_link( text )
text.gsub!( LINK_RE ) do |m|
all,pre,atts,text,title,url,proto,slash,post = $~[1..9]
if text.include?('<br />')
all
else
url, url_title = check_refs( url )
url, url_title = check_refs(url)
title ||= url_title
# Idea below : an URL with unbalanced parethesis and
# ending by ')' is put into external parenthesis
if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
url=url[0..-2] # discard closing parenth from url
post = ")"+post # add closing parenth to post
if url[-1] == ")" and ((url.count("(") - url.count(")")) < 0)
url = url[0..-2] # discard closing parenth from url
post = ")" + post # add closing parenth to post
end
atts = pba( atts )
atts = " href=\"#{ htmlesc url }#{ slash }\"#{ atts }"
atts << " title=\"#{ htmlesc title }\"" if title
atts = shelve( atts ) if atts
url = htmlesc(url.dup)
next all if url.downcase.start_with?('javascript:')
atts = pba(atts)
atts = +" href=\"#{url}#{slash}\"#{atts}"
atts << " title=\"#{htmlesc title}\"" if title
atts = shelve(atts) if atts
external = (url =~ /^https?:\/\//) ? ' class="external"' : ''
"#{ pre }<a#{ atts }#{ external }>#{ text }</a>#{ post }"
"#{pre}<a#{atts}#{external}>#{text}</a>#{post}"
end
end
end
@ -865,9 +857,9 @@ class RedCloth3 < String
[ ]? # opt. space
(?:\n[ ]*)? # one optional newline followed by spaces
\[(.*?)\] # $id
/x
/x
def inline_markdown_reflink( text )
def inline_markdown_reflink( text )
text.gsub!( MARKDOWN_REFLINK_RE ) do |m|
text, id = $~[1..2]
@ -876,12 +868,12 @@ class RedCloth3 < String
else
url, title = check_refs( id )
end
atts = " href=\"#{ url }\""
atts << " title=\"#{ title }\"" if title
atts = " href=\"#{url}\""
atts << " title=\"#{title}\"" if title
atts = shelve( atts )
"<a#{ atts }>#{ text }</a>"
"<a#{atts}>#{text}</a>"
end
end
@ -897,17 +889,17 @@ class RedCloth3 < String
\3 # matching quote
)? # title is optional
\)
/x
/x
def inline_markdown_link( text )
def inline_markdown_link( text )
text.gsub!( MARKDOWN_LINK_RE ) do |m|
text, url, quote, title = $~[1..4]
atts = " href=\"#{ url }\""
atts << " title=\"#{ title }\"" if title
atts = " href=\"#{url}\""
atts << " title=\"#{title}\"" if title
atts = shelve( atts )
"<a#{ atts }>#{ text }</a>"
"<a#{atts}>#{text}</a>"
end
end
@ -920,14 +912,14 @@ class RedCloth3 < String
end
end
def refs_textile( text )
def refs_textile( text )
text.gsub!( TEXTILE_REFS_RE ) do |m|
flag, url = $~[2..3]
@urlrefs[flag.downcase] = [url, nil]
nil
end
end
def refs_markdown( text )
text.gsub!( MARKDOWN_REFS_RE ) do |m|
flag, url = $~[2..3]
@ -937,7 +929,7 @@ class RedCloth3 < String
end
end
def check_refs( text )
def check_refs( text )
ret = @urlrefs[text.downcase] if text
ret || [text, nil]
end
@ -952,17 +944,17 @@ class RedCloth3 < String
\s? # optional space
(?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
\! # closing
(?::#{ HYPERLINK })? # optional href
/x
(?::#{HYPERLINK})? # optional href
/x
def inline_textile_image( text )
def inline_textile_image( text )
text.gsub!( IMAGE_RE ) do |m|
stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8]
htmlesc title
atts = pba( atts )
atts = " src=\"#{ htmlesc url.dup }\"#{ atts }"
atts << " title=\"#{ title }\"" if title
atts << " alt=\"#{ title }\""
atts = +" src=\"#{htmlesc url.dup}\"#{atts}"
atts << " title=\"#{title}\"" if title
atts << " alt=\"#{title}\""
# size = @getimagesize($url);
# if($size) $atts.= " $size[3]";
@ -970,18 +962,22 @@ class RedCloth3 < String
url, url_title = check_refs( url )
next m unless uri_with_safe_scheme?(url)
if href
href = htmlesc(href.dup)
next m if href.downcase.start_with?('javascript:')
end
out = ''
out << "<a#{ shelve( " href=\"#{ href }\"" ) }>" if href
out << "<img#{ shelve( atts ) } />"
out << "</a>#{ href_a1 }#{ href_a2 }" if href
if algn
out = +''
out << "<a#{shelve(" href=\"#{href}\"")}>" if href
out << "<img#{shelve(atts)} />"
out << "</a>#{href_a1}#{href_a2}" if href
if algn
algn = h_align( algn )
if stln == "<p>"
out = "<p style=\"float:#{ algn }\">#{ out }"
out = "<p style=\"float:#{algn}\">#{out}"
else
out = "#{ stln }<span style=\"float:#{ algn }\">#{ out }</span>"
out = "#{stln}<span style=\"float:#{algn}\">#{out}</span>"
end
else
out = stln + out
@ -991,18 +987,18 @@ class RedCloth3 < String
end
end
def shelve( val )
def shelve( val )
@shelf << val
" :redsh##{ @shelf.length }:"
" :redsh##{@shelf.length}:"
end
def retrieve( text )
def retrieve( text )
text.gsub!(/ :redsh#(\d+):/) do
@shelf[$1.to_i - 1] || $&
end
end
def incoming_entities( text )
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
@ -1010,14 +1006,14 @@ class RedCloth3 < String
text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
end
def no_textile( text )
text.gsub!( /(^|\s)==([^=]+.*?)==(\s|$)?/,
'\1<notextile>\2</notextile>\3' )
text.gsub!( /^ *==([^=]+.*?)==/m,
'\1<notextile>\2</notextile>\3' )
def no_textile( text )
text.gsub!(/(^|\s)==([^=]+.*?)==(\s|$)?/,
'\1<notextile>\2</notextile>\3')
text.gsub!(/^ *==([^=]+.*?)==/m,
'\1<notextile>\2</notextile>\3')
end
def clean_white_space( text )
def clean_white_space( text )
# normalize line breaks
text.gsub!( /\r\n/, "\n" )
text.gsub!( /\r/, "\n" )
@ -1032,26 +1028,27 @@ class RedCloth3 < String
end
def flush_left( text )
indt = 0
if text =~ /^ /
while text !~ /^ {#{indt}}[^ ]/
indt += 1
end unless text.empty?
if /(?![\r\n\t ])[[:cntrl:]]/.match?(text)
text.gsub!(/(?![\r\n\t ])[[:cntrl:]]/, '')
end
if /^ +\S/.match?(text)
indt = 0
indt += 1 until /^ {#{indt}}\S/.match?(text)
if indt.nonzero?
text.gsub!( /^ {#{indt}}/, '' )
end
end
end
def footnote_ref( text )
text.gsub!( /\b\[([0-9]+?)\](\s)?/,
'<sup><a href="#fn\1">\1</a></sup>\2' )
def footnote_ref( text )
text.gsub!(/\b\[([0-9]+?)\](\s)?/,
'<sup><a href="#fn\1">\1</a></sup>\2')
end
OFFTAGS = /(code|pre|kbd|notextile)/
OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }\b>)|(<#{ OFFTAGS }\b[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }\b\W|\Z)/mi
OFFTAG_OPEN = /<#{ OFFTAGS }/
OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
OFFTAG_MATCH = /(?:(<\/#{OFFTAGS}\b>)|(<#{OFFTAGS}\b[^>]*>))(.*?)(?=<\/?#{OFFTAGS}\b\W|\Z)/mi
OFFTAG_OPEN = /<#{OFFTAGS}/
OFFTAG_CLOSE = /<\/?#{OFFTAGS}/
HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m
ALLTAG_MATCH = /(<\/?\w[^\n]*?>)|.*?(?=<\/?\w[^\n]*?>|$)/m
@ -1069,7 +1066,7 @@ class RedCloth3 < String
elsif line =~ OFFTAG_CLOSE
codepre -= 1
codepre = 0 if codepre < 0
end
end
elsif codepre.zero?
glyphs_textile( line, level + 1 )
else
@ -1094,29 +1091,29 @@ class RedCloth3 < String
if codepre - used_offtags.length > 0
htmlesc( line, :NoQuotes ) if escape_line
@pre_list.last << line
line = ""
line = +""
else
### htmlesc is disabled between CODE tags which will be parsed with highlighter
### Regexp in formatter.rb is : /<code\s+class="(\w+)">\s?(.+)/m
### NB: some changes were made not to use $N variables, because we use "match"
### and it breaks following lines
htmlesc( aftertag, :NoQuotes ) if aftertag && escape_aftertag && !first.match(/<code\s+class="(\w+)">/)
line = "<redpre##{ @pre_list.length }>"
first.match(/<#{ OFFTAGS }([^>]*)>/)
line = +"<redpre##{@pre_list.length}>"
first.match(/<#{OFFTAGS}([^>]*)>/)
tag = $1
$2.to_s.match(/(class\=("[^"]+"|'[^']+'))/i)
tag << " #{$1}" if $1 && tag == 'code'
@pre_list << "<#{ tag }>#{ aftertag }"
@pre_list << +"<#{tag}>#{aftertag}"
end
elsif $1 and codepre > 0
if codepre - used_offtags.length > 0
htmlesc( line, :NoQuotes ) if escape_line
@pre_list.last << line
line = ""
line = +""
end
codepre -= 1 unless codepre.zero?
used_offtags = {} if codepre.zero?
end
end
line
end
end
@ -1130,7 +1127,7 @@ class RedCloth3 < String
end
end
def inline( text )
def inline( text )
[/^inline_/, /^glyphs_/].each do |meth_re|
@rules.each do |rule_name|
method( rule_name ).call( text ) if rule_name.to_s.match( meth_re )
@ -1138,11 +1135,11 @@ class RedCloth3 < String
end
end
def h_align( text )
def h_align( text )
H_ALGN_VALS[text]
end
def v_align( text )
def v_align( text )
V_ALGN_VALS[text]
end
@ -1156,7 +1153,7 @@ class RedCloth3 < String
'img' => ['src', 'alt', 'title'],
'br' => [],
'i' => nil,
'u' => nil,
'u' => nil,
'b' => nil,
'pre' => nil,
'kbd' => nil,
@ -1181,7 +1178,7 @@ class RedCloth3 < String
'h3' => nil,
'h4' => nil,
'h5' => nil,
'h6' => nil,
'h6' => nil,
'blockquote' => ['cite']
}
@ -1209,8 +1206,7 @@ class RedCloth3 < String
end
end
end
ALLOWED_TAGS = %w(redpre pre code kbd notextile)
def escape_html_tags(text)
text.gsub!(%r{<(\/?([!\w]+)[^<>\n]*)(>?)}) do |m|
@ -1222,4 +1218,3 @@ class RedCloth3 < String
end
end
end