Skip to content

Commit 670fc5c

Browse files
authored
Add <ruby> element (#25)
1 parent f90627b commit 670fc5c

File tree

4 files changed

+187
-1
lines changed

4 files changed

+187
-1
lines changed

library/HTMLPurifier/HTML5Config.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
class HTMLPurifier_HTML5Config extends HTMLPurifier_Config
44
{
5-
const REVISION = 2018061701;
5+
const REVISION = 2018081401;
66

77
/**
88
* @param string|array|HTMLPurifier_Config $config

library/HTMLPurifier/HTML5Definition.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ public static function setup(HTMLPurifier_HTMLDefinition $def)
1717
// add support for Floating point number attributes
1818
$def->manager->attrTypes->set('Float', new HTMLPurifier_AttrDef_Float());
1919

20+
// add support for Ruby markup
21+
$def->manager->addModule('HTML5_Ruby');
22+
2023
// http://developers.whatwg.org/sections.html
2124
$def->addElement('section', 'Block', 'Flow', 'Common');
2225
$def->addElement('nav', 'Block', 'Flow', 'Common');
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/**
4+
* HTML 5.2 Ruby markup
5+
* https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-ruby-element
6+
*
7+
* Note: {@link HTMLPurifier_HTMLModule_Ruby} implementation is based on
8+
* XHTML 1.1 Ruby Annotation module which differs from HTML5 spec.
9+
*/
10+
class HTMLPurifier_HTMLModule_HTML5_Ruby extends HTMLPurifier_HTMLModule
11+
{
12+
/**
13+
* @type string
14+
*/
15+
public $name = 'HTML5_Ruby';
16+
17+
/**
18+
* @param HTMLPurifier_Config $config
19+
*/
20+
public function setup($config)
21+
{
22+
$this->addElement(
23+
'ruby',
24+
'Inline',
25+
'Custom: ((rb | Inline | #PCDATA)*, (rt | (rp, rt, rp) | rtc))+',
26+
'Common'
27+
);
28+
$this->addElement('rtc', false, 'Custom: (rt | rp | Inline | #PCDATA)*', 'Common');
29+
$this->addElement('rb', false, 'Custom: (Inline | #PCDATA)*', 'Common');
30+
$this->addElement('rt', false, 'Custom: (Inline | #PCDATA)*', 'Common');
31+
$this->addElement('rp', false, 'Custom: (Inline | #PCDATA)*', 'Common');
32+
33+
// <ruby> elements can be nested as children of <rtc>, <rb>, <rt> and <rp>
34+
// https://www.w3.org/TR/2014/NOTE-html-ruby-extensions-20140204/#changes-compared-to-the-current-ruby-model
35+
}
36+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
3+
class HTMLPurifier_HTMLModule_HTML5_RubyTest extends PHPUnit_Framework_TestCase
4+
{
5+
public function getPurifier($config = null)
6+
{
7+
$config = HTMLPurifier_HTML5Config::create($config);
8+
$config->set('Cache.DefinitionImpl', null);
9+
$purifier = new HTMLPurifier($config);
10+
return $purifier;
11+
}
12+
13+
public function rubyInput()
14+
{
15+
return array(
16+
array(
17+
'<ruby>明日<rp>(</rp><rt>あした</rt><rp>)</rp></ruby>',
18+
),
19+
array(
20+
'<ruby>明日<rt>あした</rt></ruby>',
21+
),
22+
array(
23+
'<ruby>明日<rt></rt></ruby>',
24+
),
25+
array(
26+
'<ruby>明日<rp></rp><rt></rt><rp></rp></ruby>',
27+
),
28+
array(
29+
'<ruby>Foo<rp><rt><rp></ruby>',
30+
'<ruby>Foo<rp></rp><rt></rt><rp></rp></ruby>',
31+
),
32+
array(
33+
// Element ruby is missing a required instance of child element rp.
34+
'<ruby>Foo<rp><rt></ruby>',
35+
'',
36+
),
37+
array(
38+
// Element ruby is missing a required instance of one or more of
39+
// the following child elements: rp, rt, rtc.
40+
'<ruby>Foo<rp></rp></ruby>',
41+
'',
42+
),
43+
array(
44+
// Element ruby is missing a required instance of one or more of
45+
// the following child elements: rp, rt, rtc.
46+
'<ruby>Foo</ruby>',
47+
'',
48+
),
49+
array(
50+
'<ruby><rb>Bar</rb><rt>Baz</rt></ruby>',
51+
),
52+
array(
53+
// Text before <rb> element
54+
'<ruby>Foo<rb>Bar</rb><rt>Baz</rt></ruby>',
55+
),
56+
array(
57+
'<ruby><rb>Bar</rb><rp>(</rp><rt>Baz</rt><rp>)</rp></ruby>',
58+
),
59+
array(
60+
// Multiple sequences of <rb><rt> elements
61+
'<ruby><rb>Bar</rb><rt>Baz</rt><rb>Qux</rb><rt>Quux</rt></ruby>',
62+
),
63+
array(
64+
// Text after <rb> element
65+
'<ruby><rb>Bar</rb>Baz<rt>Qux</rt></ruby>',
66+
),
67+
array(
68+
// Multiple consecutive <rb> elements
69+
'<ruby><rb>Bar</rb><rb>Baz</rb><rb>Qux</rb>Quux<rt>Quuz</rt></ruby>',
70+
),
71+
array(
72+
// Element ruby is missing a required instance of one or more of
73+
// the following child elements: rp, rt, rtc.
74+
'<ruby>Foo<rb>Bar</rb><rt>Baz</rt><rb>Qux</rb></ruby>',
75+
'',
76+
),
77+
array(
78+
// Inline element in <ruby>
79+
'<ruby><span>Foo</span><rt></rt></ruby>',
80+
),
81+
array(
82+
// Block-level elements are moved outside <ruby>
83+
'<ruby><div>Foo</div><rt></rt></ruby>',
84+
'<div>Foo</div>',
85+
),
86+
array(
87+
'<ruby><rb>Foo</rb><rt>Bar</rt></ruby>'
88+
),
89+
array(
90+
'<ruby>Foo<rb>Bar</rb><rb>Baz</rb><rb>Qux</rb><rt></rt><rb>Quux</rb><rt></rt></ruby>',
91+
),
92+
array(
93+
// Empty <rtc> element
94+
'<ruby><rtc></rtc></ruby>',
95+
),
96+
array(
97+
// Properly auto-close <rtc> element
98+
'<ruby><rtc>Foo<rtc>Bar</ruby>',
99+
'<ruby><rtc>Foo</rtc><rtc>Bar</rtc></ruby>',
100+
),
101+
array(
102+
// Multiple <rtc> elements
103+
'<ruby><rtc>Foo<rp></rp>Bar<rt></rt></rtc><rtc>Baz</rtc></ruby>',
104+
),
105+
array(
106+
// Block-level elements are moved outside <rtc>
107+
'<ruby><rtc><div>Foo</div></rtc></ruby>',
108+
'<ruby><rtc></rtc></ruby><div>Foo</div>',
109+
),
110+
array(
111+
// <rt> element can only be child of <ruby> and <rtc> elements
112+
'<rt>Foo</rt>',
113+
'Foo',
114+
),
115+
array(
116+
// <rp> element can only be child of <ruby> and <rtc> elements
117+
'<rp>Foo</rp>',
118+
'Foo',
119+
),
120+
array(
121+
// <rb> element can only be child of <ruby> element
122+
'<rb>Foo</rb>',
123+
'Foo',
124+
),
125+
array(
126+
// <rtc> element can only be child of <ruby> element
127+
'<rtc>Foo</rtc>',
128+
'Foo',
129+
),
130+
array(
131+
// Nested <ruby> elements
132+
'<ruby><rtc><ruby>Foo<rt></rt></ruby><rt><ruby>Bar<rt></rt></ruby></rt></rtc></ruby>',
133+
),
134+
);
135+
}
136+
137+
/**
138+
* @param string $input
139+
* @param string $expectedOutput OPTIONAL
140+
* @dataProvider rubyInput
141+
*/
142+
public function testRuby($input, $expectedOutput = null)
143+
{
144+
$output = $this->getPurifier()->purify($input);
145+
$this->assertEquals($expectedOutput !== null ? $expectedOutput : $input, $output);
146+
}
147+
}

0 commit comments

Comments
 (0)