Skip to content

Instantly share code, notes, and snippets.

@mermshaus
Created May 24, 2013 12:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mermshaus/5643279 to your computer and use it in GitHub Desktop.
Save mermshaus/5643279 to your computer and use it in GitHub Desktop.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
<?php
namespace myproject;
use org\ermshaus\MenuGenerator;
error_reporting(-1);
ini_set('display_errors', 1);
require_once __DIR__ . '/org.ermshaus.MenuGenerator.bundle.php';
$menuData = <<<EOT
# Lines starting with a hash sign (#) will be ignored
Home ; /
About ; /about
The Company ; /about/company
Germany ; /germany
Europe ; /europe
ROW ; /about/company/row
Team ; /about/team
Careers ; /about/careers
Services ; /services
What we do ; /services/what-we-do
Our process ; /services/our-process
Testimonials ; /services/testimonials
Portfolio ; /portfolio
Web Design ; /portfolio/webdesign
Development ; /portfolio/development
Identity ; /portfolio/identity
SEO & Internet Marketing ; /portfolio/seo-marketing
Print Design ; /portfolio/print-design
Contact ; /contact
EOT;
$menuGenerator = new MenuGenerator();
$url = 'data://text/plain;base64,' . base64_encode($menuData);
$stream = fopen($url, 'rb');
$menuGenerator->readFlatFile($stream);
fclose($stream);
$menuHtml = $menuGenerator->renderMenu();
$rootUrl = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/');
header('Content-Type: text/html; charset=UTF-8');
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>title</title>
<link href="<?=$rootUrl?>/line25.css" media="screen" rel="stylesheet" type="text/css">
</head>
<body>
<nav>
<?=$menuHtml?>
</nav>
<p style="text-align: center;">
Demo CSS taken from
<a href="http://line25.com/tutorials/how-to-create-a-pure-css-dropdown-menu">line25.com</a>
</p>
</body>
</html>
/*
CSS code taken from:
http://line25.com/tutorials/how-to-create-a-pure-css-dropdown-menu
*/
body, div, h1, h2, h3, h4, h5, h6, p, ul, ol, li, dl, dt, dd, img, form, fieldset, input, textarea, blockquote {
margin: 0; padding: 0; border: 0;
}
body {
background: #909eab url(bg.png);
font-family: Helvetica, sans-serif; font-size: 18px; line-height: 24px;
}
nav {
margin: 100px auto;
text-align: center;
}
nav ul ul {
display: none;
}
nav ul li:hover > ul {
display: block;
}
nav ul {
background: #efefef;
background: linear-gradient(top, #efefef 0%, #bbbbbb 100%);
background: -moz-linear-gradient(top, #efefef 0%, #bbbbbb 100%);
background: -webkit-linear-gradient(top, #efefef 0%,#bbbbbb 100%);
box-shadow: 0px 0px 9px rgba(0,0,0,0.15);
padding: 0 20px;
border-radius: 10px;
list-style: none;
position: relative;
display: inline-table;
}
nav ul:after {
content: ""; clear: both; display: block;
}
nav ul li {
float: left;
}
nav ul li:hover {
background: #4b545f;
background: linear-gradient(top, #4f5964 0%, #5f6975 40%);
background: -moz-linear-gradient(top, #4f5964 0%, #5f6975 40%);
background: -webkit-linear-gradient(top, #4f5964 0%,#5f6975 40%);
}
nav ul li:hover a {
color: #fff;
}
nav ul li a {
display: block; padding: 25px 40px;
color: #757575; text-decoration: none;
}
nav ul ul {
background: #5f6975; border-radius: 0px; padding: 0;
position: absolute; top: 100%;
}
nav ul ul li {
float: none;
border-top: 1px solid #6b727c;
border-bottom: 1px solid #575f6a; position: relative;
}
nav ul ul li a {
padding: 15px 40px;
color: #fff;
}
nav ul ul li a:hover {
background: #4b545f;
}
nav ul ul ul {
position: absolute; left: 100%; top:0;
}
/* mermshaus, additions */
.onpath > a { background: rgba(255, 0, 0, 0.1); }
.selected > a { font-weight: bold; text-decoration: underline; }
<?php
namespace org\ermshaus;
use Closure;
use Exception;
/**
*
*/
class MenuGenerator
{
/**
*
* @var MenuGeneratorConfig
*/
protected $config;
/**
*
* @var array
*/
protected $menuStruct = null;
/**
*
* @param MenuGeneratorConfig $config
*/
public function __construct(MenuGeneratorConfig $config = null)
{
if ($config === null) {
$this->config = new MenuGeneratorConfig();
} else {
$this->config = $config;
}
}
/**
*
* @param array $elements
* @param Closure $funcHasParent
* @param Closure $funcIsChild
* @return array
*/
protected function treeify(array $elements, Closure $funcHasParent, Closure $funcIsChild)
{
$root = array(
'data' => null,
'children' => array()
);
$rec = function ($testElement) use ($elements, &$rec, $funcIsChild) {
$childElements = array();
foreach ($elements as $element) {
if ($funcIsChild($element, $testElement)) {
$childElements[] = array(
'data' => $element,
'children' => $rec($element)
);
}
}
return $childElements;
};
foreach ($elements as $element) {
if (false === $funcHasParent($element)) {
$root['children'][] = array(
'data' => $element,
'children' => $rec($element)
);
}
}
return $root;
}
/**
*
* @param array $tree
* @param Closure $funcNodeToString
* @return string
*/
protected function treeToHtmlList(array $tree, Closure $funcNodeToString, Closure $onListStart = null)
{
if (count($tree['children']) === 0) {
return '';
}
if (null == $onListStart) {
$onListStart = function ($data) {
return '<li>';
};
}
$html = '';
$rec = function ($node, $indent) use (&$rec, &$html, $funcNodeToString, $onListStart) {
$indentation = str_repeat(' ', $indent);
$indentation2 = str_repeat(' ', $indent + 1);
$html .= $indentation . $onListStart($node['data']) . $funcNodeToString($node['data']);
if (count($node['children']) !== 0) {
$html .= "\n" . $indentation2 . '<ul>' . "\n";
}
foreach ($node['children'] as $childNode) {
$html .= $rec($childNode, $indent + 2);
}
if (count($node['children']) !== 0) {
$html .= $indentation2 . '</ul>' . "\n";
$html .= $indentation . '</li>' . "\n";
} else {
$html .= '</li>' . "\n";
}
};
$indent = 1;
$html = '<ul>' . "\n";
foreach ($tree['children'] as $node) {
$rec($node, $indent);
}
$html .= '</ul>' . "\n";
return $html;
}
public function readFlatFile($stream)
{
$menu = array();
$nextId = 1;
$stack = array();
$line = 0;
while (($buffer = fgets($stream)) !== false) {
$line++;
// Skip empty lines
if (trim($buffer) === '') {
continue;
}
if (0 === strpos(trim($buffer), '#')) {
continue;
}
$parts = array();
if (0 === preg_match('/^( *)([^;]+);(.+)$/', rtrim($buffer), $parts)) {
throw new Exception("Malformed line error on line $line");
}
if (strlen($parts[1]) % 4 !== 0) {
throw new Exception(
"Indentation error on line $line. Use multiples of four spaces"
);
}
$level = strlen($parts[1]) / 4;
$title = trim($parts[2]);
$link = trim($parts[3]);
if (count($stack) < $level) {
throw new Exception("Nesting error on line $line: $title");
}
while ($level < count($stack)) {
array_pop($stack);
}
$parentId = null;
if (count($stack) > 0) {
$parentId = $stack[count($stack) - 1];
}
$menu[] = array(
'id' => $nextId,
'parent_id' => $parentId,
'title' => $title,
'link' => $link
);
array_push($stack, $nextId);
$nextId++;
}
$this->menuStruct = $menu;
}
/**
*
*
* Here be dragons.
*
* @return string
* @throws Exception
*/
public function renderMenu()
{
$menu = $this->menuStruct;
if ($menu === null) {
throw new Exception('No menu struct in memory. Did you load one?');
}
$tree = $this->treeify(
$menu,
function ($elem) {
return $elem['parent_id'] !== null;
},
function ($elem, $possibleParentElem) {
return $elem['parent_id'] === $possibleParentElem['id'];
}
);
$isNodeOnActivePath = function ($data, $activePath) use ($menu) {
$idToMenuElement = array();
foreach ($menu as &$elem) {
$idToMenuElement[$elem['id']] = $elem;
}
unset($elem);
$nodeId = $data['id'];
$activeNode = null;
// Get active node
foreach ($menu as $testNode) {
if ($testNode['link'] === $activePath) {
$activeNode = $testNode;
}
}
if ($activeNode['id'] === $nodeId) return true;
if ($activeNode === null) return false;
// Get parent ids for active node
$pids = array();
$x = $activeNode;
while ($x['parent_id'] !== null) {
$x = $idToMenuElement[$x['parent_id']];
$pids[] = $x['id'];
}
if (in_array($nodeId, $pids)) {
return true;
}
return false;
};
$isNodeSelected = function ($data, $activePath) {
if ($data['link'] === $activePath) return true;
return false;
};
$html = $this->treeToHtmlList(
$tree,
function ($data) {
$f = $this->config->getEscapeFunction();
return '<a href="' . $f($this->config->getPathPrefix() . $data['link']) . '">'
. $f($data['title']) . '</a>';
},
function ($data) use ($isNodeOnActivePath, $isNodeSelected) {
$classes = array();
if ($isNodeOnActivePath($data, $this->config->getActivePath())) {
$classes[] = $this->config->getCssClassOnPath();
}
if ($isNodeSelected($data, $this->config->getActivePath())) {
$classes[] = $this->config->getCssClassActive();
}
if (count($classes) > 0) {
return '<li class="' . implode(' ', $classes) . '">';
}
return '<li>';
}
);
return $html;
}
}
/**
*
*/
class MenuGeneratorConfig
{
protected $pathPrefix;
protected $activePath;
protected $cssClassActive = 'selected';
protected $cssClassOnPath = 'onpath';
protected $escapeFunction;
public function __construct()
{
// We'll use our best guess as default
$this->pathPrefix = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/');
$this->activePath = trim(substr($_SERVER['REQUEST_URI'], strlen($this->pathPrefix)));
$this->escapeFunction = function ($s) {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
};
}
public function getPathPrefix()
{
return $this->pathPrefix;
}
public function setPathPrefix($pathPrefix)
{
$this->pathPrefix = $pathPrefix;
}
public function getActivePath()
{
return $this->activePath;
}
public function setActivePath($activePath)
{
$this->activePath = $activePath;
}
public function getCssClassActive()
{
return $this->cssClassActive;
}
public function setCssClassActive($cssClassActive)
{
$this->cssClassActive = $cssClassActive;
}
public function getCssClassOnPath()
{
return $this->cssClassOnPath;
}
public function setCssClassOnPath($cssClassOnPath)
{
$this->cssClassOnPath = $cssClassOnPath;
}
public function getEscapeFunction()
{
return $this->escapeFunction;
}
public function setEscapeFunction(Closure $escapeFunction)
{
$this->escapeFunction = $escapeFunction;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment