TableOfContent.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <?php
  2. /**
  3. *
  4. * @package Pico
  5. * @subpackage mcb_TableOfContent
  6. * @version 0.3.1
  7. * @author mcbSolutions.at <dev@mcbsolutions.at>
  8. * @author notabug.org/ohnonot
  9. *
  10. * ## Changelog
  11. *
  12. * + 2016-11-17 Upgrade to AbstractPicoPlugin for Pico 1.0
  13. * + 2016-11-19
  14. * + Removed some parameters
  15. * + Added the test page
  16. * + 2019-11-24 Much hacked to be included in my (ohnonot) tagblog theme
  17. *
  18. *
  19. */
  20. class TableOfContent extends AbstractPicoPlugin {
  21. // only act if this var is set to "true"
  22. private $yeah = "false";
  23. // default settings
  24. private $depth = 3;
  25. private $min_headers = 3;
  26. private $top_txt = 'Top';
  27. private $caption = "Table of contents";
  28. private $template = "all"; // config to valid twig template to only act on that template - defaults to all templates.
  29. // internal
  30. private $toc = '';
  31. private $xpQuery;
  32. private $content;
  33. public function onConfigLoaded(&$config)
  34. {
  35. if(isset($config['TableOfContent']['depth'])) $this->depth = &$config['TableOfContent']['depth'];
  36. if(isset($config['TableOfContent']['min_headers'])) $this->min_headers = &$config['TableOfContent']['min_headers'];
  37. if(isset($config['TableOfContent']['top_txt' ])) $this->top_txt = &$config['TableOfContent']['top_txt'];
  38. if(isset($config['TableOfContent']['caption' ])) $this->caption = &$config['TableOfContent']['caption'];
  39. if(isset($config['TableOfContent']['template' ])) $this->template = &$config['TableOfContent']['template'];
  40. for ($i=1; $i <= $this->depth; $i++) {
  41. $this->xpQuery[] = "//h$i";
  42. }
  43. $this->xpQuery = join("|", $this->xpQuery);
  44. }
  45. public function onMetaParsed(array $meta)
  46. {
  47. if($meta['template'] === $this->template || $this->template === "all" ) $this->yeah = "true";
  48. }
  49. private function makeToc(&$content)
  50. {
  51. //get the headings
  52. if(preg_match_all('/<h[1-'.$this->depth.']{1,1}[^>]*>.*?<\/h[1-'.$this->depth.']>/s',$content,$headers) === false)
  53. return "";
  54. //create the toc
  55. $this->headers = $headers;
  56. $heads = implode("\n",$headers[0]);
  57. $heads = preg_replace('/<a.+?\/a>/','',$heads);
  58. $heads = preg_replace('/<h([1-6]).+?id="?/','<li class="toc$1"><a href="#',$heads);
  59. $heads = preg_replace('/<\/h[1-6]>/','</a></li>',$heads);
  60. $cap = $this->caption =='' ? "" : '<p id="toc-header">'.$this->caption.'</p>';
  61. return '<div id="toc">'.$cap.'<ul>'.$heads.'</ul></div>';
  62. }
  63. public function onContentParsed(&$content)
  64. {
  65. if($this->yeah != "true") return;
  66. if(trim($content)=="")
  67. return;
  68. // enable user error handling
  69. // libxml_use_internal_errors(true);
  70. // Workaround from cbuckley:
  71. // "... an alternative is to prepend the HTML with an XML encoding declaration, provided that the
  72. // document doesn't already contain one:
  73. //
  74. // http://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly
  75. $dom = new DOMDocument('1.0', 'utf-8');
  76. $html = $dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);
  77. if(!$html)
  78. {
  79. foreach (libxml_get_errors() as $error) {
  80. // handle errors here
  81. $this->content['err'][] = $error;
  82. }
  83. libxml_clear_errors();
  84. return;
  85. }
  86. $xp = new DOMXPath($dom);
  87. $nodes =$xp->query($this->xpQuery);
  88. if($nodes->length < $this->min_headers)
  89. return;
  90. // add missing id's to the h tags
  91. $id = 0;
  92. foreach($nodes as $i => $sort)
  93. {
  94. if (isset($sort->tagName) && $sort->tagName !== '')
  95. {
  96. if($sort->getAttribute('id') === "")
  97. {
  98. ++$id;
  99. $sort->setAttribute('id', "c$id");
  100. $a = $dom->createElement('a', $this->top_txt);
  101. $a->setAttribute('href', '#');
  102. $a->setAttribute('class', 'toc-nav');
  103. $sort->appendChild($a);
  104. }
  105. }
  106. }
  107. $content = preg_replace(array("/<(!DOCTYPE|\?xml).+?>/", "/<\/?(html|body)>/"),
  108. array( "", ""),
  109. $dom->saveHTML());
  110. $this->toc = $this->makeToc($content);
  111. }
  112. public function onPageRendering(&$twig, &$twigVariables, &$templateName)
  113. {
  114. if($this->yeah != "true") return;
  115. $twigVariables['tableofcontent'] = $this->toc;
  116. }
  117. /* debug
  118. public function onPageRendered(&$output)
  119. {
  120. $output = $output . "<pre style=\"background-color:white;\">".htmlentities(print_r($this,1))."</pre>";
  121. }*/
  122. }