Redmine 4.1.1
This commit is contained in:
parent
33e7b881a5
commit
3d976f1b3b
1593 changed files with 36180 additions and 19489 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):"(.+?)"/) 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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <b>bold</b> 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’\2' ], # single closing
|
||||
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1’' ], # single closing
|
||||
# [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '’' ], # single closing
|
||||
# [ /\'/, '‘' ], # single opening
|
||||
# [ /</, '<' ], # less-than
|
||||
# [ />/, '>' ], # greater-than
|
||||
# [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing
|
||||
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1”' ], # double closing
|
||||
# [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '”' ], # double closing
|
||||
# [ /"/, '“' ], # double opening
|
||||
# [ /\b( )?\.{3}/, '\1…' ], # 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—' ], # em dash
|
||||
# [ /\s->\s/, ' → ' ], # right arrow
|
||||
# [ /\s-\s/, ' – ' ], # en dash
|
||||
# [ /(\d+) ?x ?(\d+)/, '\1×\2' ], # dimension sign
|
||||
# [ /\b ?[(\[]TM[\])]/i, '™' ], # trademark
|
||||
# [ /\b ?[(\[]R[\])]/i, '®' ], # registered
|
||||
# [ /\b ?[(\[]C[\])]/i, '©' ] # copyright
|
||||
# [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1’\2' ], # single closing
|
||||
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1’' ], # single closing
|
||||
# [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '’' ], # single closing
|
||||
# [ /\'/, '‘' ], # single opening
|
||||
# [ /</, '<' ], # less-than
|
||||
# [ />/, '>' ], # greater-than
|
||||
# [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing
|
||||
# [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1”' ], # double closing
|
||||
# [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '”' ], # double closing
|
||||
# [ /"/, '“' ], # double opening
|
||||
# [ /\b( )?\.{3}/, '\1…' ], # 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—' ], # em dash
|
||||
# [ /\s->\s/, ' → ' ], # right arrow
|
||||
# [ /\s-\s/, ' – ' ], # en dash
|
||||
# [ /(\d+) ?x ?(\d+)/, '\1×\2' ], # dimension sign
|
||||
# [ /\b ?[(\[]TM[\])]/i, '™' ], # trademark
|
||||
# [ /\b ?[(\[]R[\])]/i, '®' ], # registered
|
||||
# [ /\b ?[(\[]C[\])]/i, '©' ] # 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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue