QuickMenu (Javascript, PHP)
Detail souboru
Zdrojový kód
<?php
/**
* @file QuickMenu.class.php
* Definition of QuickMenu class.
* @author Kamil Dudka <xdudka00@gmail.com>
* @ingroup QuickMenu
*/
/**
* @defgroup QuickMenu
* At http://dudka.cz/QuickMenu may be available abstract, documentation and tutorial for this component.
* @n Used @b CSS @b IDs: qmWrapper, QuickMenu, qmInputBox, qmInput, qmFacade, qmClearSearch, qmActive, qmActiveLink
* @n Used @b CSS @b classes: qmDefault, qmMatch, qmHidden
* @n Used @b images: tree_minus.png, tree_plus.png, tree_item.png, icon_drop.png
*/
/**
* Server part of QuickMenu component.
* @ingroup QuickMenu
*/
class QuickMenu {
private $imgExpanded_;
private $imgCollapsed_;
private $imgNoChildern_;
private $imgClearSearch_;
private $imgExpandAll_;
public function __construct() {
$config = Config::instance();
$imgRoot = $config->webRoot().$config->imgDir();
$this->imgExpanded_ = $imgRoot.'/tree_minus.png';
$this->imgCollapsed_ = $imgRoot.'/tree_plus.png';
$this->imgNoChildern_ = $imgRoot.'/tree_item.png';
$this->imgClearSearch_ = $imgRoot.'/icon_drop.png';
$this->imgExpandAll_ = $imgRoot.'/fast_forward.png';
}
private $visibleDepth_ = 1;
/**
* Menu tree visible depth. Default is 1.
* @return Menu tree visible depth.
*/
public function visibleDepth() {
return $this->visibleDepth_;
}
/**
* Set menu tree visible depth. <b>Experimental!</b><br/>Default is 1.
* @param visibleDepth Menu tree visible depth.
*/
public function setVisibleDepth($visibleDepth) {
$this->visibleDepth_ = $visibleDepth;
}
private $minimalInputLength_ = 1;
/**
* Minimal length of input to invoke search action. Default is 1.
* @return Minimal length of input to invoke search action.
*/
public function minimalInputLength() {
return $this->minimalInputLength_;
}
/**
* Minimal length of input to invoke search action. Default is 1.
* @param minimalInputLength Minimal length of input to invoke search action.
*/
public function setMinimalInputLength($minimalInputLength) {
$this->minimalInputLength_ = $minimalInputLength;
}
private $activeItemHREF_;
/**
* Active menu item HREF attribute.
* @return HREF of active menu item.
*/
public function activeItemHREF() {
return $this->activeItemHREF_;
}
/**
* Set active menu item using HREF attribute. This attribute should be equally to HREF given by addItem() method.
* @param activeItemHREF HREF of menu item to active.
*/
public function setActiveItemHREF($activeItemHREF) {
$this->activeItemHREF_ = $activeItemHREF;
}
/**
* Generate head section of component and send direct to output.
* @remarks
* - Generate link to QuickMenu.class.js
* - Generate initialization inline script
* - Use JSOnloadList singleton to run scripts on page load (at client's side).
*/
public function genHead() {
$config = Config::instance();
$qmScript = $config->webRoot().$config->scriptDir().'/QuickMenu.class.js';
echo ' <script type="text/javascript" src="'.$qmScript.'"></script>'."\n";
?> <script type="text/javascript">
qmFacade = null;
qmInput = null;
qmClear = null;
qmExpandAll = null;
function qmOnload() {
(new Image).src='<?php echo $this->imgExpanded_; ?>';
(new Image).src='<?php echo $this->imgCollapsed_; ?>';
(new Image).src='<?php echo $this->imgNoChildern_; ?>';
(new Image).src='<?php echo $this->imgClearSearch_; ?>';
qm = new QuickMenu('QuickMenu', 'qmInput');
qm.visibleDepth = <?php echo $this->visibleDepth(); ?>;
qm.minimalInputLength = <?php echo $this->minimalInputLength(); ?>;
qm.imgExpanded = '<?php echo $this->imgExpanded_; ?>';
qm.imgCollapsed = '<?php echo $this->imgCollapsed_; ?>';
qm.imgNoChildern = '<?php echo $this->imgNoChildern_; ?>';
qm.init();
qmFacade = document.getElementById('qmFacade');
qmInput = document.getElementById('qmInput');
qmClear = document.getElementById('qmClearInput');
qmExpandAll = document.getElementById('qmExpandAll');
qmFacade.onfocus = function() {
qmInput.style.display = '';
qmClear.style.display = '';
this.style.display = 'none';
qmExpandAll.style.display = 'none';
if (-1==navigator.appName.search(/konqueror/i))
qmInput.focus();
else
// Special version for Konqueror
setTimeout("qmInput.focus()", 100);
}
qmInput.onblur = function() {
if (this.value.length >= qm.minimalInputLength)
return;
this.style.display = 'none';
qmClear.style.display = 'none';
this.value = '';
qmFacade.style.display = '';
qmExpandAll.style.display = '';
}
qmClear.onclick = function() {
qmInput.value = '';
qmInput.blur();
qmInput.onblur();
}
qmExpandAll.onclick = function() {
qmInput.value = '*';
qmFacade.onfocus();
if (-1==navigator.appName.search(/konqueror/i))
qmInput.select();
else
// Special version for Konqueror
setTimeout("qmInput.select()", 200);
}
qmInput.onblur.call(qmInput);
}
</script>
<?php
JSOnloadList::instance()->addFunction('qmOnload');
}
private $currentId = 1; ///< Menu item enumeration id (id=0 ... menu root)
private $mapIdToText = Array(); ///< Menu item text associative array
private $mapIdToHREF = Array(); ///< Menu item HREF associative array
private $mapIdToMenuList = Array(0=>Array()); ///< Menu tree linkage
/**
* Add menu item somewhere to menu
* @param szText Menu item text.
* @param szHREF Menu item HREF. <b>Item HREF is relative to webRoot!!!</b>
* @param idParent Parent menu item ID. ID must be an number previously returned by addItem() method or zero for base level menu item.
* @return Return ID of just added menu item.
*/
public function addItem($szText, $szHREF, $idParent=0) {
$id = $this->currentId++;
$this->mapIdToText[$id] = $szText;
$this->mapIdToHREF[$id] = $szHREF;
$this->mapIdToMenuList[$id] = Array();
$this->mapIdToMenuList[$idParent] []= $id;
return $id;
}
/**
* Extract submenu direct to output
* @param id menu item ID of parent menu item
* @param xmlID (voluntary) xml ID of UL tag to set
*/
private function genSubMenu($id, $xmlId=null) {
$menuList = $this->mapIdToMenuList[$id];
if (0==count($menuList))
return;
// Initiate list
$indentor = Indentor::singleton();
if (isset($xmlId))
$indentor->puts("<ul id=\"$xmlId\">");
else
$indentor->puts('<ul>');
$indentor->enter();
// Generate items
foreach ($menuList as $current)
$this->genMenuItem($current);
// Terminate list
$indentor->leave();
$indentor->puts('</ul>');
}
/**
* Generate menu item and its subitems recursively direct to output.
* @param id menu ID of item to generate
*/
private function genMenuItem($id) {
$indentor = Indentor::singleton();
$indentor->indentDirect();
$szText = $this->mapIdToText[$id];
$szHREF = $this->mapIdToHREF[$id];
$menuList = $this->mapIdToMenuList[$id];
// Is current item active menu item?
$isActive = $szHREF == $this->activeItemHREF_;
$szItemId = ($isActive)?' id="qmActive"':'';
// Initiate list item
echo "<li$szItemId class=\"qmDefault\">";
// Has current item childern
$hasChilds = 0!= count($menuList);
$szImgSrc = ($hasChilds)?
$this->imgExpanded_:
$this->imgNoChildern_;
echo "<img src=\"$szImgSrc\" title=\"$szText\" alt=\"-\" />";
// Generate item link
$szFullHREF = Config::instance()->webRoot().'/'.$szHREF;
$szLinkId = ($isActive)?' id="qmActiveLink"':'';
echo "<a$szLinkId class=\"qmDefault\" href=\"$szFullHREF\">$szText</a>";
if ($hasChilds) {
// Generate subitems recursively
echo "\n";
$indentor->enter();
$this->genSubMenu($id);
$indentor->leave();
$indentor->indentDirect();
}
// Terminate list item
echo "</li>\n";
}
/**
* Generate body section of component and send direct to output.
* @remarks
* - DIV \#qmWrapper
* - DIV \#qmInputBox
* - INPUT \#qmInput
* - INPUT \#qmFacade
* - IMG \#qmClearSearch
* - IMG \#qmExpandAll
* - UL \#QuickMenu (with dynamic content inside)
* - LI (optional item)
* - IMG
* - A
* - UL (optional subitem list)
* - LI ...
* - ...
*/
public function genMenu() {
$indentor = Indentor::singleton();
$indentor->puts('<div id="qmWrapper">');
$indentor->enter();
$indentor->puts('<div id="qmInputBox">');
$indentor->enter();
$indentor->puts('<input type="text" id="qmInput" style="display:none;"/>');
$indentor->puts('<input type="text" id="qmFacade" value="QuickFind" style="display:none;"/>');
$indentor->puts('<img src="'.$this->imgClearSearch_.'" alt="Clear search" id="qmClearInput" style="display:none;"/>');
$indentor->puts('<img src="'.$this->imgExpandAll_.'" alt="Expand all" id="qmExpandAll" style="display:none;"/>');
$indentor->leave();
$indentor->puts('</div>');
$this->genSubMenu(0, 'QuickMenu');
$indentor->leave();
$indentor->puts('</div>');
}
/**
* Add item and its subitems recursively from SimpleXML document tree.
* @param item SimpleXML node to add as item
* @param idParent Parent item ID or zero for base level item.
*/
private function addXmlItem($item, $idParent=0) {
// Localized item text
$lang = LangSelect::singleton()->getLang();
$textArray = $item->xpath('child::qmText[@lang=\''.$lang.'\']');
if (0==count($textArray))
// Implicit item text (not localized)
$textArray = $item->xpath('child::qmText[not(@lang)]');
if (0==count($textArray))
// Any item text (emergency case)
$textArray = $item->xpath('child::qmText');
// Add current item
$hrefArray = $item->xpath('@href');
$id = $this->addItem($textArray[0], $hrefArray[0], $idParent);
// Scan for childern
$childArray = $item->xpath('child::qmItem');
foreach ($childArray as $subItem)
// Direct recursion
$this->addXmlItem($subItem, $id);
}
/**
* Add menu items from XML template.
* DTD available at http://dudka.cz/dtd/QuickMenu.dtd
* @param qmTemplate Path to XML document containing menu template.
* @param idParent Parent item ID or zero for base level item.
*/
public function loadXmlTemplate($qmTemplate, $idParent=0) {
$xml = simplexml_load_file($qmTemplate);
$catArray = $xml->xpath('/QuickMenu/child::qmItem');
foreach ($catArray as $current)
$this->addXmlItem($current, $idParent);
}
}
?>