Skip to content

Commit

Permalink
Add <ruby> element (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
xemlock authored Aug 14, 2018
1 parent f90627b commit 670fc5c
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 1 deletion.
2 changes: 1 addition & 1 deletion library/HTMLPurifier/HTML5Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class HTMLPurifier_HTML5Config extends HTMLPurifier_Config
{
const REVISION = 2018061701;
const REVISION = 2018081401;

/**
* @param string|array|HTMLPurifier_Config $config
Expand Down
3 changes: 3 additions & 0 deletions library/HTMLPurifier/HTML5Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public static function setup(HTMLPurifier_HTMLDefinition $def)
// add support for Floating point number attributes
$def->manager->attrTypes->set('Float', new HTMLPurifier_AttrDef_Float());

// add support for Ruby markup
$def->manager->addModule('HTML5_Ruby');

// http://developers.whatwg.org/sections.html
$def->addElement('section', 'Block', 'Flow', 'Common');
$def->addElement('nav', 'Block', 'Flow', 'Common');
Expand Down
36 changes: 36 additions & 0 deletions library/HTMLPurifier/HTMLModule/HTML5/Ruby.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/**
* HTML 5.2 Ruby markup
* https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-ruby-element
*
* Note: {@link HTMLPurifier_HTMLModule_Ruby} implementation is based on
* XHTML 1.1 Ruby Annotation module which differs from HTML5 spec.
*/
class HTMLPurifier_HTMLModule_HTML5_Ruby extends HTMLPurifier_HTMLModule
{
/**
* @type string
*/
public $name = 'HTML5_Ruby';

/**
* @param HTMLPurifier_Config $config
*/
public function setup($config)
{
$this->addElement(
'ruby',
'Inline',
'Custom: ((rb | Inline | #PCDATA)*, (rt | (rp, rt, rp) | rtc))+',
'Common'
);
$this->addElement('rtc', false, 'Custom: (rt | rp | Inline | #PCDATA)*', 'Common');
$this->addElement('rb', false, 'Custom: (Inline | #PCDATA)*', 'Common');
$this->addElement('rt', false, 'Custom: (Inline | #PCDATA)*', 'Common');
$this->addElement('rp', false, 'Custom: (Inline | #PCDATA)*', 'Common');

// <ruby> elements can be nested as children of <rtc>, <rb>, <rt> and <rp>
// https://www.w3.org/TR/2014/NOTE-html-ruby-extensions-20140204/#changes-compared-to-the-current-ruby-model
}
}
147 changes: 147 additions & 0 deletions tests/HTMLPurifier/HTMLModule/HTML5/RubyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

class HTMLPurifier_HTMLModule_HTML5_RubyTest extends PHPUnit_Framework_TestCase
{
public function getPurifier($config = null)
{
$config = HTMLPurifier_HTML5Config::create($config);
$config->set('Cache.DefinitionImpl', null);
$purifier = new HTMLPurifier($config);
return $purifier;
}

public function rubyInput()
{
return array(
array(
'<ruby>明日<rp>(</rp><rt>あした</rt><rp>)</rp></ruby>',
),
array(
'<ruby>明日<rt>あした</rt></ruby>',
),
array(
'<ruby>明日<rt></rt></ruby>',
),
array(
'<ruby>明日<rp></rp><rt></rt><rp></rp></ruby>',
),
array(
'<ruby>Foo<rp><rt><rp></ruby>',
'<ruby>Foo<rp></rp><rt></rt><rp></rp></ruby>',
),
array(
// Element ruby is missing a required instance of child element rp.
'<ruby>Foo<rp><rt></ruby>',
'',
),
array(
// Element ruby is missing a required instance of one or more of
// the following child elements: rp, rt, rtc.
'<ruby>Foo<rp></rp></ruby>',
'',
),
array(
// Element ruby is missing a required instance of one or more of
// the following child elements: rp, rt, rtc.
'<ruby>Foo</ruby>',
'',
),
array(
'<ruby><rb>Bar</rb><rt>Baz</rt></ruby>',
),
array(
// Text before <rb> element
'<ruby>Foo<rb>Bar</rb><rt>Baz</rt></ruby>',
),
array(
'<ruby><rb>Bar</rb><rp>(</rp><rt>Baz</rt><rp>)</rp></ruby>',
),
array(
// Multiple sequences of <rb><rt> elements
'<ruby><rb>Bar</rb><rt>Baz</rt><rb>Qux</rb><rt>Quux</rt></ruby>',
),
array(
// Text after <rb> element
'<ruby><rb>Bar</rb>Baz<rt>Qux</rt></ruby>',
),
array(
// Multiple consecutive <rb> elements
'<ruby><rb>Bar</rb><rb>Baz</rb><rb>Qux</rb>Quux<rt>Quuz</rt></ruby>',
),
array(
// Element ruby is missing a required instance of one or more of
// the following child elements: rp, rt, rtc.
'<ruby>Foo<rb>Bar</rb><rt>Baz</rt><rb>Qux</rb></ruby>',
'',
),
array(
// Inline element in <ruby>
'<ruby><span>Foo</span><rt></rt></ruby>',
),
array(
// Block-level elements are moved outside <ruby>
'<ruby><div>Foo</div><rt></rt></ruby>',
'<div>Foo</div>',
),
array(
'<ruby><rb>Foo</rb><rt>Bar</rt></ruby>'
),
array(
'<ruby>Foo<rb>Bar</rb><rb>Baz</rb><rb>Qux</rb><rt></rt><rb>Quux</rb><rt></rt></ruby>',
),
array(
// Empty <rtc> element
'<ruby><rtc></rtc></ruby>',
),
array(
// Properly auto-close <rtc> element
'<ruby><rtc>Foo<rtc>Bar</ruby>',
'<ruby><rtc>Foo</rtc><rtc>Bar</rtc></ruby>',
),
array(
// Multiple <rtc> elements
'<ruby><rtc>Foo<rp></rp>Bar<rt></rt></rtc><rtc>Baz</rtc></ruby>',
),
array(
// Block-level elements are moved outside <rtc>
'<ruby><rtc><div>Foo</div></rtc></ruby>',
'<ruby><rtc></rtc></ruby><div>Foo</div>',
),
array(
// <rt> element can only be child of <ruby> and <rtc> elements
'<rt>Foo</rt>',
'Foo',
),
array(
// <rp> element can only be child of <ruby> and <rtc> elements
'<rp>Foo</rp>',
'Foo',
),
array(
// <rb> element can only be child of <ruby> element
'<rb>Foo</rb>',
'Foo',
),
array(
// <rtc> element can only be child of <ruby> element
'<rtc>Foo</rtc>',
'Foo',
),
array(
// Nested <ruby> elements
'<ruby><rtc><ruby>Foo<rt></rt></ruby><rt><ruby>Bar<rt></rt></ruby></rt></rtc></ruby>',
),
);
}

/**
* @param string $input
* @param string $expectedOutput OPTIONAL
* @dataProvider rubyInput
*/
public function testRuby($input, $expectedOutput = null)
{
$output = $this->getPurifier()->purify($input);
$this->assertEquals($expectedOutput !== null ? $expectedOutput : $input, $output);
}
}

0 comments on commit 670fc5c

Please sign in to comment.