File: /home/blushinc/loleverywhere.com/wp-content/plugins/td-cloud-library/includes/tdb_toc.php
<?php
/* ---------
---- TOC MENU FACTORY CLASS
--------- */
class tdb_toc_menu_factory {
/**
* Add a menu item
*
* @param string $slug
* @param array $attributes
* @return tdb_toc_menu_item
*/
public function createItem( string $slug, array $attributes = [] ) {
$item = new tdb_toc_menu_item($slug, $this);
$this->setItemAttributes($item, $attributes);
return $item;
}
/**
* Set an item's attributes
*
* @param tdb_toc_menu_item $item
* @param array $attributes
* @return void
*/
public function setItemAttributes( tdb_toc_menu_item $item, array $attributes ) {
$attributes = array_merge(
array(
'label' => '',
'level' => 0,
'children' => array(),
'render' => true
),
$attributes
);
$item->setLabel($attributes['label'])
->setLevel($attributes['level'])
->addChildren($attributes['children'])
->setRender($attributes['render']);
}
}
/* ---------
----TOC MENU ITEM CLASS
--------- */
class tdb_toc_menu_item {
protected $slug;
protected $label;
protected $level;
protected $parent;
protected $children;
protected $render;
protected $menu;
/**
* Class constructor
*
* @param string $slug
* @param tdb_toc_menu_factory $menu
*/
public function __construct( string $slug, tdb_toc_menu_factory $menu ) {
$this->slug = $slug;
$this->menu = $menu;
}
/**
* Set an item's parent
*
* @param tdb_toc_menu_item|null $parent
* @return tdb_toc_menu_item
*/
public function setParent( $parent = null ) {
if( $parent === $this ) {
throw new \InvalidArgumentException('Item cannot be a child of itself');
}
$this->parent = $parent;
return $this;
}
/**
* Get an item's parent
*
* @return tdb_toc_menu_item
*/
public function getParent() {
return $this->parent;
}
/**
* Set an item's slug
*
* @param string $slug
* @return tdb_toc_menu_item
*/
public function setSlug( string $slug ) {
$this->slug = $slug;
return $this;
}
/**
* Get an item's slug
*
* @return string
*/
public function getSlug() {
return $this->slug;
}
/**
* Set an item's label
*
* @param string $label
* @return tdb_toc_menu_item
*/
public function setLabel( string $label ) {
$this->label = $label;
return $this;
}
/**
* Get an item's slug
*
* @return string
*/
public function getLabel() {
return $this->label;
}
/**
* Set an item's level
*
* @param int $level
* @return tdb_toc_menu_item
*/
public function setLevel( int $level ) {
$this->level = $level;
return $this;
}
/**
* Get an item's level
*
* @return int
*/
public function getLevel() {
return $this->level;
}
/**
* Add a child to an item
*
* @param string $slug
* @param array $attributes
* @return tdb_toc_menu_item
*/
public function addChild( string $slug, array $attributes = [] ) {
$child = $this->menu->createItem($slug, $attributes);
$child->setParent($this);
$this->children[$child->getSlug()] = $child;
return $child;
}
/**
* Get an item's child
*
* @param string $slug
* @return tdb_toc_menu_item
*/
public function getChild( string $slug ) {
return $this->children[$slug];
}
/**
* Add multiple children to an item
*
* @param array $children
* @param bool $append
* @return tdb_toc_menu_item
*/
public function addChildren( array $children, bool $append = false ) {
if( $append ) {
foreach ( $children as $child ) {
$this->children[] = $child;
}
} else {
$this->children = $children;
}
return $this;
}
/**
* Get all children of an item
*
* @return array
*/
public function getChildren() {
return $this->children;
}
/**
* Set whether an item should be rendered or not
*
* @param bool $render
* @return tdb_toc_menu_item
*/
public function setRender( $render ) {
$this->render = $render;
return $this;
}
/**
* Get the render property of an item
*
* @return bool
*/
public function getRender() {
return $this->render;
}
/**
* Check if an item has children
*
* @return bool
*/
public function hasChildren() {
return !empty($this->children);
}
/**
* Check whether an item should be rendered or not
*
* @return bool
*/
public function isRender() {
return $this->render;
}
}
/* ---------
---- TOC UTILS CLASS
--------- */
class tdb_toc {
/**
* Get menu
*
* @param string $content
* @param int $topLevel
* @param int $depth
* @param array $exclude
* @return tdb_toc_menu_item
*/
static function getMenu( string $content, int $topLevel = 1, int $depth = 6, array $exclude = [] ) {
$menu_factory = new tdb_toc_menu_factory();
$menu = $menu_factory->createItem('TOC', array('render' => false));
if( $content == '' ) {
return $menu;
}
/* -- Parse the content and extract the headings -- */
$headings = self::extractHeadings($content, $topLevel, $depth, $exclude);
if( empty( $headings ) ) {
return $menu;
}
/* -- Set the first item as the last item added -- */
$lastItem = $menu;
/* -- Go through each heading and add it to the menu -- */
foreach ( $headings as $heading ) {
$heading_slug = $heading['slug'];
$heading_label = $heading['label'];
$heading_level = $heading['level'];
/* -- Build the new TOC menu item -- */
$menu_item = array(
'slug' => $heading_slug,
'label' => $heading_label,
'level' => $heading_level,
);
/* -- Determine the parent item and add the new item accordingly -- */
if ( $heading_level == 1 ) {
$parent = $menu;
} else if ( $heading_level == $lastItem->getLevel() ) {
$parent = $lastItem->getParent();
} else if( $heading_level > $lastItem->getLevel() ) {
$parent = $lastItem;
for ($i = $lastItem->getLevel(); $i < ($heading_level - 1); $i++) {
$parent = $parent->addChild('');
$parent->setLevel($i + 1);
$parent->setRender(false);
}
} else {
$parent = $lastItem->getParent();
while ($parent->getLevel() > $heading_level - 1) {
$parent = $parent->getParent();
}
}
$lastItem = $parent->addChild($heading_slug, $menu_item);
}
return $menu;
}
/**
* Create a dummy menu
*
* @return tdb_toc_menu_item
*/
static function getDummyMenu() {
$menu_factory = new tdb_toc_menu_factory();
$menu = $menu_factory->createItem('TOC', array('render' => false));
$item1 = $menu->addChild('example-1', array(
'slug' => 'example-1',
'label' => 'Example 1',
'level' => 1
));
$item2 = $item1->addChild('example-2', array(
'slug' => 'example-2',
'label' => 'Example 2',
'level' => 2
));
$item3 = $item2->addChild('example-3', array(
'slug' => 'example-3',
'label' => 'Example 3',
'level' => 3
));
$item4 = $item3->addChild('example-4', array(
'slug' => 'example-4',
'label' => 'Example 4',
'level' => 4
));
$item5 = $item4->addChild('example-5', array(
'slug' => 'example-5',
'label' => 'Example 5',
'level' => 5
));
$item6 = $item5->addChild('example-6', array(
'slug' => 'example-6',
'label' => 'Example 6',
'level' => 6
));
$item7 = $item2->addChild('example-7', array(
'slug' => 'example-7',
'label' => 'Example 7',
'level' => 2
));
$item8 = $item7->addChild('example-8', array(
'slug' => 'example-8',
'label' => 'Example 8',
'level' => 3
));
$item9 = $menu->addChild('example-9', array(
'slug' => 'example-9',
'label' => 'Example 9',
'level' => 1
));
return $menu;
}
/**
* Get menu html
*
* @param tdb_toc_menu_item $menu_item
* @param bool $hierarchical
* @return string
*/
static function getMenuHTML( tdb_toc_menu_item $menu_item, bool $hierarchical = true ) {
$buffy = '';
if( $hierarchical ) {
$buffy .= self::renderMenu($menu_item, $hierarchical);
} else {
$buffy .= '<ul>';
$buffy .= self::renderMenu($menu_item, $hierarchical);
$buffy .= '</ul>';
}
return $buffy;
}
/**
* Get dummy menu html
*
* @param bool $hierarchical
* @return string
*/
static function getDummyMenuHTML( bool $hierarchical = true ) {
$dummy_menu = self::getDummyMenu();
return self::getMenuHTML($dummy_menu, $hierarchical);
}
/**
* Render menu html
*
* @param tdb_toc_menu_item $menu_item
* @param bool $hierarchical
* @return string
*/
static function renderMenu( tdb_toc_menu_item $menu_item, bool $hierarchical = true ) {
$buffy = '';
if( $menu_item->hasChildren() ) {
if( $hierarchical ) {
$buffy .= '<ul>';
}
$buffy .= self::renderMenuItemChildren($menu_item, $hierarchical);
if( $hierarchical ) {
$buffy .= '</ul>';
}
}
return $buffy;
}
/**
* Render menu item children html
*
* @param tdb_toc_menu_item $menu_item
* @param bool $hierarchical
* @return string
*/
static function renderMenuItemChildren( tdb_toc_menu_item $menu_item, bool $hierarchical = true ) {
$buffy = '';
foreach ( $menu_item->getChildren() as $menu_item_child ) {
$buffy .= self::renderMenuItem($menu_item_child, $hierarchical);
}
return $buffy;
}
/**
* Render menu item html
*
* @param tdb_toc_menu_item $menu_item
* @param bool $hierarchical
* @return string
*/
static function renderMenuItem( tdb_toc_menu_item $menu_item, bool $hierarchical = true ) {
$buffy = '';
if( $menu_item->isRender() ) {
$buffy .= '<li>';
$buffy .= '<a href="#' . $menu_item->getSlug() . '">' . $menu_item->getLabel() . '</a>';
$buffy .= self::renderMenu($menu_item, $hierarchical);
$buffy .= '</li>';
} else {
$buffy .= self::renderMenuItemChildren($menu_item, $hierarchical);
}
return $buffy;
}
/**
* Extract the headings from a content
*
* @param string $content
* @param int $topLevel
* @param int $depth
* @param array $exclude
* @return array
*/
static function extractHeadings( string $content, int $topLevel = 1, int $depth = 6, array $exclude = [] ) {
$headings_list = array();
$headings_slugs = array();
$allowed_headings = array_slice([0, 1, 2, 3, 4, 5, 6], $topLevel, $depth);
$allowed_headings = array_diff($allowed_headings, $exclude);
$pattern = '#(?P<full_heading><(?P<heading_tag>h\d)(?P<heading_classes>[^>]*)(?P<heading_extra>[^>]*)>(?P<heading_contents>.*?)<\/h\d>)#i';
preg_match_all( $pattern, $content, $headings, PREG_SET_ORDER );
if( $headings ) {
foreach ( $headings as $heading ) {
/* -- Get the heading level -- */
$heading_tag = $heading['heading_tag'];
$heading_level = (int)filter_var($heading_tag, FILTER_SANITIZE_NUMBER_INT);
if( !in_array($heading_level, $allowed_headings) ) {
continue;
}
/* -- Build the heading slug (used like an ID) -- */
$heading_label = strip_tags( $heading['heading_contents'] );
if( empty( $heading_label ) ) {
continue;
}
$heading_slug = mb_strtolower(
preg_replace('/([?]|\p{P}|\s)+/u', '-', strip_tags(str_replace(" ", " ", $heading_label)))
);
$heading_slug = str_replace(array("-8211", "-amp", "-8217"), array("", "", ""), trim($heading_slug, '-'));
$heading_slug_new = $heading_slug;
if ( in_array($heading_slug, $headings_slugs) ) {
$heading_slug_new = $heading_slug . '-' . count(array_keys($headings_slugs, $heading_slug));
}
$headings_slugs[] = $heading_slug;
/* -- Build the heading info -- */
$headings_list[] = array(
'slug' => $heading_slug_new,
'label' => $heading_label,
'level' => $heading_level,
'full_heading' => $heading['full_heading'],
'tag' => $heading_tag,
'extra' => $heading['heading_extra']
);
}
}
return $headings_list;
}
}