Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
666 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
This plugin implements task lists, a form of markup compatible with GitHub/GitLab Flavored Markdown and other dialects. | ||
|
||
This plugin requires a `LI` tag to function properly. If there is no `LI` tag defined when the plugin is initialized, the Litedown plugin is automatically loaded. | ||
|
||
Task lists are | ||
|
||
|
||
### Syntax | ||
|
||
```md | ||
- [x] Checked | ||
- [ ] Unchecked | ||
``` | ||
|
||
|
||
### References | ||
|
||
- <https://docs.gitlab.com/ee/user/markdown.html#task-lists> | ||
- <https://github.com/mity/md4c/wiki/Markdown-Syntax:-Task-Lists> | ||
- <https://github.github.com/gfm/#task-list-items-extension-> | ||
|
||
|
||
## Examples | ||
|
||
Note that all of the reference outputs, random IDs have been replaced with a `...` placeholder for convenience. | ||
|
||
```php | ||
$configurator = new s9e\TextFormatter\Configurator; | ||
$configurator->Litedown; | ||
$configurator->TaskLists; | ||
|
||
// Get an instance of the parser and the renderer | ||
extract($configurator->finalize()); | ||
|
||
$text = "- [x] checked\n" | ||
. "- [X] Checked\n" | ||
. "- [ ] unchecked"; | ||
$xml = $parser->parse($text); | ||
$html = $renderer->render($xml); | ||
|
||
echo $html; | ||
``` | ||
```html | ||
<ul><li data-task-id="..."><input data-task-id="..." type="checkbox" checked disabled> checked</li> | ||
<li data-task-id="..."><input data-task-id="..." type="checkbox" checked disabled> Checked</li> | ||
<li data-task-id="..."><input data-task-id="..." type="checkbox" disabled> unchecked</li></ul> | ||
``` | ||
|
||
|
||
### Allow tasks to be toggled | ||
|
||
Setting the `TASKLISTS_EDITABLE` parameter to a non-empty value will make tasks editable. | ||
|
||
```php | ||
$configurator = new s9e\TextFormatter\Configurator; | ||
$configurator->Litedown; | ||
$configurator->TaskLists; | ||
|
||
extract($configurator->finalize()); | ||
|
||
$text = "- [x] checked\n" | ||
. "- [ ] unchecked"; | ||
$xml = $parser->parse($text); | ||
$html = $renderer->render($xml); | ||
|
||
echo $html, "\n\n"; | ||
|
||
// Render it again but make the tasks editable | ||
$renderer->setParameter('TASKLISTS_EDITABLE', '1'); | ||
|
||
echo $renderer->render($xml); | ||
|
||
``` | ||
```html | ||
<ul><li data-task-id="..."><input data-task-id="..." type="checkbox" checked disabled> checked</li> | ||
<li data-task-id="..."><input data-task-id="..." type="checkbox" disabled> unchecked</li></ul> | ||
|
||
<ul><li data-task-id="..."><input data-task-id="..." type="checkbox" checked> checked</li> | ||
<li data-task-id="..."><input data-task-id="..." type="checkbox"> unchecked</li></ul> | ||
``` | ||
|
||
|
||
### Styling task lists | ||
|
||
```css | ||
ul > li[data-task-id] | ||
{ | ||
list-style-type: none; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php declare(strict_types=1); | ||
|
||
/** | ||
* @package s9e\TextFormatter | ||
* @copyright Copyright (c) 2010-2020 The s9e authors | ||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License | ||
*/ | ||
namespace s9e\TextFormatter\Plugins\TaskLists; | ||
|
||
use s9e\TextFormatter\Configurator\Items\Tag; | ||
use s9e\TextFormatter\Plugins\ConfiguratorBase; | ||
|
||
class Configurator extends ConfiguratorBase | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function asConfig() | ||
{ | ||
return; | ||
} | ||
|
||
protected function setUp(): void | ||
{ | ||
if (!isset($this->configurator->tags['LI'])) | ||
{ | ||
$this->configurator->Litedown; | ||
} | ||
|
||
$this->createTaskTag(); | ||
$this->configureListItemTag($this->configurator->tags['LI']); | ||
} | ||
|
||
protected function configureListItemTag(Tag $tag): void | ||
{ | ||
$tag->filterChain->append(Helper::class . '::filterListItem') | ||
->resetParameters() | ||
->addParameterByName('parser') | ||
->addParameterByName('tag') | ||
->addParameterByName('text') | ||
->setJS(file_get_contents(__DIR__ . '/filterListItem.js')); | ||
|
||
$tag->template = preg_replace( | ||
'(<li[^>]*+>(?!<xsl:if test="TASK">)\\K)', | ||
'<xsl:if test="TASK"> | ||
<xsl:attribute name="data-task-id"> | ||
<xsl:value-of select="TASK/@id"/> | ||
</xsl:attribute> | ||
<xsl:attribute name="data-task-state"> | ||
<xsl:value-of select="TASK/@state"/> | ||
</xsl:attribute> | ||
</xsl:if>', | ||
$tag->template | ||
); | ||
} | ||
|
||
protected function createTaskTag(): void | ||
{ | ||
$tag = $this->configurator->tags->add('TASK'); | ||
$tag->attributes->add('id')->filterChain->append('#identifier'); | ||
$tag->attributes->add('state')->filterChain->append('#identifier'); | ||
$tag->template = '<input data-task-id="{@id}" type="checkbox"> | ||
<xsl:if test="@state = \'complete\'"><xsl:attribute name="checked"/></xsl:if> | ||
<xsl:if test="not($TASKLISTS_EDITABLE)"><xsl:attribute name="disabled"/></xsl:if> | ||
</input>'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php declare(strict_types=1); | ||
|
||
/** | ||
* @package s9e\TextFormatter | ||
* @copyright Copyright (c) 2010-2020 The s9e authors | ||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License | ||
*/ | ||
namespace s9e\TextFormatter\Plugins\TaskLists; | ||
|
||
use s9e\TextFormatter\Parser; | ||
use s9e\TextFormatter\Parser\Tag; | ||
|
||
class Helper | ||
{ | ||
public static function filterListItem(Parser $parser, Tag $listItem, string $text): void | ||
{ | ||
// Test whether the list item is followed by a task checkbox | ||
$pos = $listItem->getPos() + $listItem->getLen(); | ||
$pos += strspn($text, ' ', $pos); | ||
$str = substr($text, $pos, 3); | ||
if (!preg_match('/\\[[ Xx]\\]/', $str)) | ||
{ | ||
return; | ||
} | ||
|
||
// Create a tag for the task and assign it a random ID | ||
$taskId = uniqid(); | ||
$taskState = ($str === '[ ]') ? 'incomplete' : 'complete'; | ||
|
||
$task = $parser->addSelfClosingTag('TASK', $pos, 3); | ||
$task->setAttribute('id', $taskId); | ||
$task->setAttribute('state', $taskState); | ||
} | ||
|
||
/** | ||
* Return stats from a parsed representation | ||
* | ||
* @param string $xml Parsed XML | ||
* @return array Number of "complete" and "incomplete" tasks | ||
*/ | ||
public static function getStats(string $xml): array | ||
{ | ||
$stats = ['complete' => 0, 'incomplete' => 0]; | ||
|
||
preg_match_all('(<TASK(?: [^=]++="[^"]*+")*? state="\\K\\w++)', $xml, $m); | ||
foreach ($m[0] as $state) | ||
{ | ||
if (!isset($stats[$state])) | ||
{ | ||
$stats[$state] = 0; | ||
} | ||
++$stats[$state]; | ||
} | ||
|
||
return $stats; | ||
} | ||
|
||
/** | ||
* Mark given task complete in XML | ||
* | ||
* @param string $xml Parsed XML | ||
* @param string $id Task's ID | ||
* @return string Updated XML | ||
*/ | ||
public static function setTaskComplete(string $xml, string $id): string | ||
{ | ||
return self::setTaskState($xml, $id, 'complete', 'x'); | ||
} | ||
|
||
/** | ||
* Mark given task incomplete in XML | ||
* | ||
* @param string $xml Parsed XML | ||
* @param string $id Task's ID | ||
* @return string Updated XML | ||
*/ | ||
public static function setTaskIncomplete(string $xml, string $id): string | ||
{ | ||
return self::setTaskState($xml, $id, 'incomplete', ' '); | ||
} | ||
|
||
/** | ||
* Change the state and marker of given task in XML | ||
* | ||
* @param string $xml Parsed XML | ||
* @param string $id Task's ID | ||
* @param string $state Task's state ("complete" or "incomplete") | ||
* @param string $marker Task marker ("x" or " ") | ||
* @return string Updated XML | ||
*/ | ||
protected static function setTaskState(string $xml, string $id, string $state, string $marker): string | ||
{ | ||
return preg_replace_callback( | ||
'(<TASK(?: [^=]++="[^"]*+")*? id="' . $id . '"\\K([^>]*+)>[^<]*+(?=</TASK>))', | ||
function ($m) use ($state, $marker) | ||
{ | ||
preg_match_all('( ([^=]++)="[^"]*+")', $m[1], $m); | ||
|
||
$attributes = array_combine($m[1], $m[0]); | ||
$attributes['state'] = ' state="' . $state . '"'; | ||
ksort($attributes); | ||
|
||
return implode('', $attributes) . '>[' . $marker . ']'; | ||
}, | ||
$xml | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* @param {!Tag} listItem | ||
* @param {string} text | ||
*/ | ||
function (listItem, text) | ||
{ | ||
// Test whether the list item is followed by a task checkbox | ||
var pos = listItem.getPos() + listItem.getLen(); | ||
while (text.charAt(pos) === ' ') | ||
{ | ||
++pos; | ||
} | ||
var str = text.substr(pos, 3); | ||
if (!/\[[ Xx]\]/.test(str)) | ||
{ | ||
return; | ||
} | ||
|
||
// Create a tag for the task and assign it a random ID | ||
var taskId = Math.random().toString(16).substr(2), | ||
taskState = (str === '[ ]') ? 'incomplete' : 'complete', | ||
task = addSelfClosingTag('TASK', pos, 3); | ||
|
||
task.setAttribute('id', taskId); | ||
task.setAttribute('state', taskState); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.