diff --git a/Michelf/Markdown.php b/Michelf/Markdown.php index c8ced89..ed3dc0c 100644 --- a/Michelf/Markdown.php +++ b/Michelf/Markdown.php @@ -62,6 +62,8 @@ class Markdown implements MarkdownInterface { # Optional filter function for URLs public $url_filter_func = null; + # Optional header id="" generation callback function. + public $header_id_func = null; ### Parser Implementation ### @@ -773,21 +775,46 @@ class Markdown implements MarkdownInterface { return $text; } + protected function _doHeaders_callback_setext($matches) { # Terrible hack to check we haven't found an empty list item. if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) return $matches[0]; $level = $matches[2]{0} == '=' ? 1 : 2; - $block = "".$this->runSpanGamut($matches[1]).""; + + # id attribute generation + $idAtt = $this->_generateIdFromHeaderValue($matches[1]); + + $block = "".$this->runSpanGamut($matches[1]).""; return "\n" . $this->hashBlock($block) . "\n\n"; } protected function _doHeaders_callback_atx($matches) { + + # id attribute generation + $idAtt = $this->_generateIdFromHeaderValue($matches[2]); + $level = strlen($matches[1]); - $block = "".$this->runSpanGamut($matches[2]).""; + $block = "".$this->runSpanGamut($matches[2]).""; return "\n" . $this->hashBlock($block) . "\n\n"; } + protected function _generateIdFromHeaderValue($headerValue) { + + # if a header_id_func property is set, we can use it to automatically + # generate an id attribute. + # + # This method returns a string in the form id="foo", or an empty string + # otherwise. + if (!is_callable($this->header_id_func)) { + return ""; + } + $idValue = call_user_func($this->header_id_func, $headerValue); + if (!$idValue) return ""; + + return ' id="' . $this->encodeAttribute($idValue) . '"'; + + } protected function doLists($text) { # @@ -1682,14 +1709,17 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown { # Expression to use when parsing in a context when no capture is desired protected $id_class_attr_nocatch_re = '\{(?:[ ]*[#.a-z][-_:a-zA-Z0-9=]+){1,}[ ]*\}'; - protected function doExtraAttributes($tag_name, $attr) { + protected function doExtraAttributes($tag_name, $attr, $defaultIdValue = null) { # # Parse attributes caught by the $this->id_class_attr_catch_re expression # and return the HTML-formatted list of attributes. # # Currently supported attributes are .class and #id. # - if (empty($attr)) return ""; + # In addition, this method also supports supplying a default Id value, + # which will be used to populate the id attribute in case it was not + # overridden. + if (empty($attr) && !$defaultIdValue) return ""; # Split on components preg_match_all('/[#.a-z][-_:a-zA-Z0-9=]+/', $attr, $matches); @@ -1710,13 +1740,15 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown { } } + if (!$id) $id = $defaultIdValue; + # compose attributes as string $attr_str = ""; if (!empty($id)) { - $attr_str .= ' id="'.$id.'"'; + $attr_str .= ' id="'.$this->encodeAttribute($id) .'"'; } if (!empty($classes)) { - $attr_str .= ' class="'.implode(" ", $classes).'"'; + $attr_str .= ' class="'. implode(" ", $classes) . '"'; } if (!$this->no_markup && !empty($attributes)) { $attr_str .= ' '.implode(" ", $attributes); @@ -2529,14 +2561,20 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown { protected function _doHeaders_callback_setext($matches) { if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) return $matches[0]; + $level = $matches[3]{0} == '=' ? 1 : 2; - $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]); + + $defaultId = is_callable($this->header_id_func) ? call_user_func($this->header_id_func, $matches[1]) : null; + + $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2], $defaultId); $block = "".$this->runSpanGamut($matches[1]).""; return "\n" . $this->hashBlock($block) . "\n\n"; } protected function _doHeaders_callback_atx($matches) { $level = strlen($matches[1]); - $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]); + + $defaultId = is_callable($this->header_id_func) ? call_user_func($this->header_id_func, $matches[2]) : null; + $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3], $defaultId); $block = "".$this->runSpanGamut($matches[2]).""; return "\n" . $this->hashBlock($block) . "\n\n"; }