Adds multi-levels blockquotes support by using > at the beginning of lines.

Textile is preserved inside quoted text.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1479 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2008-05-30 16:35:36 +00:00
parent 4311ecbc04
commit 88dea1a06d
35 changed files with 160 additions and 2 deletions

View File

@ -299,6 +299,8 @@ class RedCloth < String
hard_break text
unless @lite_mode
refs text
# need to do this before text is split by #blocks
block_textile_quotes text
blocks text
end
inline text
@ -577,6 +579,29 @@ class RedCloth < String
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 = ''
indent = 0
lines.each do |line|
line =~ QUOTES_CONTENT_RE
bq,content = $1, $2
l = bq.count('>')
if l != indent
quotes << ("\n\n" + (l>indent ? '<blockquote>' * (l-indent) : '</blockquote>' * (indent-l)) + "\n\n")
indent = l
end
quotes << (content + "\n")
end
quotes << ("\n" + '</blockquote>' * indent + "\n\n")
quotes
end
end
CODE_RE = /(\W)
@
(?:\|(\w+?)\|)?

View File

@ -45,7 +45,7 @@ module Redmine
# 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>
def hard_break( text )
text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks
text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks
end
# Patch to add code highlighting support to RedCloth

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

View File

@ -498,6 +498,37 @@ jsToolBar.prototype.elements.ol = {
}
}
// spacer
jsToolBar.prototype.elements.space3 = {type: 'space'}
// bq
jsToolBar.prototype.elements.bq = {
type: 'button',
title: 'Quote',
fn: {
wiki: function() {
this.encloseLineSelection('','',function(str) {
str = str.replace(/\r/g,'');
return str.replace(/(\n|^) *([^\n]*)/g,"$1> $2");
});
}
}
}
// unbq
jsToolBar.prototype.elements.unbq = {
type: 'button',
title: 'Unquote',
fn: {
wiki: function() {
this.encloseLineSelection('','',function(str) {
str = str.replace(/\r/g,'');
return str.replace(/(\n|^) *[>]? *([^\n]*)/g,"$1$2");
});
}
}
}
// pre
jsToolBar.prototype.elements.pre = {
type: 'button',
@ -508,7 +539,7 @@ jsToolBar.prototype.elements.pre = {
}
// spacer
jsToolBar.prototype.elements.space3 = {type: 'space'}
jsToolBar.prototype.elements.space4 = {type: 'space'}
// wiki page
jsToolBar.prototype.elements.link = {

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Záhlaví 2';
jsToolBar.strings['Heading 3'] = 'Záhlaví 3';
jsToolBar.strings['Unordered list'] = 'Seznam';
jsToolBar.strings['Ordered list'] = 'Uspořádaný seznam';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Předformátovaný text';
jsToolBar.strings['Wiki link'] = 'Vložit odkaz na Wiki stránku';
jsToolBar.strings['Image'] = 'Vložit obrázek';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Overskrift 2';
jsToolBar.strings['Heading 3'] = 'Overskrift 3';
jsToolBar.strings['Unordered list'] = 'Unummereret list';
jsToolBar.strings['Ordered list'] = 'Nummereret list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatteret tekst';
jsToolBar.strings['Wiki link'] = 'Link til en Wiki side';
jsToolBar.strings['Image'] = 'Billede';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Überschrift 2. Ordnung';
jsToolBar.strings['Heading 3'] = 'Überschrift 3. Ordnung';
jsToolBar.strings['Unordered list'] = 'Aufzählungsliste';
jsToolBar.strings['Ordered list'] = 'Nummerierte Liste';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Präformatierter Text';
jsToolBar.strings['Wiki link'] = 'Verweis (Link) zu einer Wiki-Seite';
jsToolBar.strings['Image'] = 'Grafik';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Otsikko 2';
jsToolBar.strings['Heading 3'] = 'Otsikko 3';
jsToolBar.strings['Unordered list'] = 'Järjestämätön lista';
jsToolBar.strings['Ordered list'] = 'Järjestetty lista';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Ennaltamuotoiltu teksti';
jsToolBar.strings['Wiki link'] = 'Linkki Wiki sivulle';
jsToolBar.strings['Image'] = 'Kuva';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Titre niveau 2';
jsToolBar.strings['Heading 3'] = 'Titre niveau 3';
jsToolBar.strings['Unordered list'] = 'Liste à puces';
jsToolBar.strings['Ordered list'] = 'Liste numérotée';
jsToolBar.strings['Quote'] = 'Citer';
jsToolBar.strings['Unquote'] = 'Supprimer citation';
jsToolBar.strings['Preformatted text'] = 'Texte préformaté';
jsToolBar.strings['Wiki link'] = 'Lien vers une page Wiki';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Fejléc 2';
jsToolBar.strings['Heading 3'] = 'Fejléc 3';
jsToolBar.strings['Unordered list'] = 'Felsorolás';
jsToolBar.strings['Ordered list'] = 'Számozott lista';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Előreformázott szöveg';
jsToolBar.strings['Wiki link'] = 'Link egy Wiki oldalra';
jsToolBar.strings['Image'] = 'Kép';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = '見出し 2';
jsToolBar.strings['Heading 3'] = '見出し 3';
jsToolBar.strings['Unordered list'] = '順不同リスト';
jsToolBar.strings['Ordered list'] = '番号つきリスト';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = '整形済みテキスト';
jsToolBar.strings['Wiki link'] = 'Wiki ページへのリンク';
jsToolBar.strings['Image'] = '画像';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Nenumeruotas sąrašas';
jsToolBar.strings['Ordered list'] = 'Numeruotas sąrašas';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatuotas tekstas';
jsToolBar.strings['Wiki link'] = 'Nuoroda į Wiki puslapį';
jsToolBar.strings['Image'] = 'Paveikslas';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Overskrift 2';
jsToolBar.strings['Heading 3'] = 'Overskrift 3';
jsToolBar.strings['Unordered list'] = 'Punktliste';
jsToolBar.strings['Ordered list'] = 'Nummerert liste';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatert tekst';
jsToolBar.strings['Wiki link'] = 'Lenke til Wiki-side';
jsToolBar.strings['Image'] = 'Bilde';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -11,6 +11,8 @@ jsToolBar.strings['Heading 2'] = 'Cabeçalho 2';
jsToolBar.strings['Heading 3'] = 'Cabeçalho 3';
jsToolBar.strings['Unordered list'] = 'Lista não ordenada';
jsToolBar.strings['Ordered list'] = 'Lista ordenada';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Texto pré-formatado';
jsToolBar.strings['Wiki link'] = 'Link para uma página Wiki';
jsToolBar.strings['Image'] = 'Imagem';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Заголовок 2';
jsToolBar.strings['Heading 3'] = 'Заголовок 3';
jsToolBar.strings['Unordered list'] = 'Маркированный список';
jsToolBar.strings['Ordered list'] = 'Нумерованный список';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Заранее форматированный текст';
jsToolBar.strings['Wiki link'] = 'Ссылка на страницу в Wiki';
jsToolBar.strings['Image'] = 'Вставка изображения';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'หัวข้อ 2';
jsToolBar.strings['Heading 3'] = 'หัวข้อ 3';
jsToolBar.strings['Unordered list'] = 'รายการ';
jsToolBar.strings['Ordered list'] = 'ลำดับเลข';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'รูปแบบข้อความคงที่';
jsToolBar.strings['Wiki link'] = 'เชื่อมโยงไปหน้า Wiki อื่น';
jsToolBar.strings['Image'] = 'รูปภาพ';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = '標題 2';
jsToolBar.strings['Heading 3'] = '標題 3';
jsToolBar.strings['Unordered list'] = '項目清單';
jsToolBar.strings['Ordered list'] = '編號清單';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = '格式化文字';
jsToolBar.strings['Wiki link'] = '連結至 Wiki 頁面';
jsToolBar.strings['Image'] = '圖片';

View File

@ -9,6 +9,8 @@ jsToolBar.strings['Heading 2'] = 'Heading 2';
jsToolBar.strings['Heading 3'] = 'Heading 3';
jsToolBar.strings['Unordered list'] = 'Unordered list';
jsToolBar.strings['Ordered list'] = 'Ordered list';
jsToolBar.strings['Quote'] = 'Quote';
jsToolBar.strings['Unquote'] = 'Remove Quote';
jsToolBar.strings['Preformatted text'] = 'Preformatted text';
jsToolBar.strings['Wiki link'] = 'Link to a Wiki page';
jsToolBar.strings['Image'] = 'Image';

View File

@ -153,6 +153,8 @@ input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
fieldset {border: 1px solid #e4e4e4; margin:0;}
legend {color: #484848;}
hr { width: 100%; height: 1px; background: #ccc; border: 0;}
blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
blockquote blockquote { margin-left: 0;}
textarea.wiki-edit { width: 99%; }
li p {margin-top: 0;}
div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}

View File

@ -84,6 +84,12 @@
.jstb_ol {
background-image: url(../images/jstoolbar/bt_ol.png);
}
.jstb_bq {
background-image: url(../images/jstoolbar/bt_bq.png);
}
.jstb_unbq {
background-image: url(../images/jstoolbar/bt_bq_remove.png);
}
.jstb_pre {
background-image: url(../images/jstoolbar/bt_pre.png);
}

View File

@ -178,6 +178,46 @@ class ApplicationHelperTest < HelperTestCase
assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
end
def test_blockquote
# orig raw text
raw = <<-RAW
John said:
> Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
> Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
> * Donec odio lorem,
> * sagittis ac,
> * malesuada in,
> * adipiscing eu, dolor.
>
> >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
> Proin a tellus. Nam vel neque.
He's right.
RAW
# expected html
expected = <<-EXPECTED
<p>John said:</p>
<blockquote>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
<ul>
<li>Donec odio lorem,</li>
<li>sagittis ac,</li>
<li>malesuada in,</li>
<li>adipiscing eu, dolor.</li>
</ul>
<blockquote>
<p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
</blockquote>
<p>Proin a tellus. Nam vel neque.</p>
</blockquote>
<p>He's right.</p>
EXPECTED
assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
end
def test_macro_hello_world
text = "{{hello_world}}"
assert textilizable(text).match(/Hello world!/)