"id",
"parent_id" => "parent_id",
"text" => "text",
"link" => "link",
"title" => "title",
"icon" => "icon",
"target" => "target",
"orderfield" => "orderfield",
"expanded" => "expanded"
);
/**
* Names of fields of the i18n table corresponding to $tableName
* @access private
* @var array
*/
var $tableFields_i18n = array(
"language" => "language",
"id" => "id",
"text" => "text",
"title" => "title"
);
/**
* A temporary array to store data retrieved from the DB and to perform the depth-first search
* @access private
* @var array
*/
var $_tmpArray = array();
/**
* The constructor method; it initializates the menu system
* @return void
*/
function LayersMenu(
$abscissaStep = 140,
$ordinateStep = 20,
$thresholdY = 20
) {
$this->_packageName = "PHP Layers Menu";
$this->version = "3.0.2";
$this->copyright = "(C) 2001-2004";
$this->author = "Marco Pratesi - http://www.marcopratesi.it/";
$this->prependedUrl = "";
$this->dirroot = "";
$this->libdir = "lib/";
$this->libjsdir = "libjs/";
$this->libjswww = "libjs/";
$this->imgdir = "images/";
$this->imgwww = "images/";
$this->tpldir = "templates/";
$this->horizontalMenuTpl = $this->dirroot . $this->tpldir . "layersmenu-horizontal_menu.ihtml";
$this->verticalMenuTpl = $this->dirroot . $this->tpldir . "layersmenu-vertical_menu.ihtml";
$this->subMenuTpl = $this->dirroot . $this->tpldir . "layersmenu-sub_menu.ihtml";
$this->menuStructure = "";
$this->separator = "|";
$this->treeMenuSeparator = "|";
$this->treeMenuImagesType = "png";
$this->_treeMenu = array();
$this->_nodesCount = 0;
$this->tree = array();
$this->_maxLevel = array();
$this->_firstLevelCnt = array();
$this->_firstItem = array();
$this->_lastItem = array();
$this->header = "";
$this->numl = 0;
$this->listl = "";
$this->father = "";
$this->moveLayers = "";
$this->_firstLevelMenu = array();
$this->footer = "";
$this->forwardArrow = " -->";
$this->downArrow = " -->";
$this->abscissaStep = $abscissaStep;
$this->ordinateStep = $ordinateStep;
$this->thresholdY = $thresholdY;
}
/**
* The method to set the value of abscissaStep
* @access public
* @return void
*/
function setAbscissaStep($abscissaStep) {
$this->abscissaStep = $abscissaStep;
}
/**
* The method to set the value of ordinateStep
* @access public
* @return void
*/
function setOrdinateStep($ordinateStep) {
$this->ordinateStep = $ordinateStep;
}
/**
* The method to set the value of thresholdY
* @access public
* @return void
*/
function setThresholdY($thresholdY) {
$this->thresholdY = $thresholdY;
}
/**
* The method to set the prepended URL
* @access public
* @return boolean
*/
function setPrependedUrl($prependedUrl) {
// We do not perform any check
$this->prependedUrl = $prependedUrl;
return true;
}
/**
* The method to set the dirroot directory
* @access public
* @return boolean
*/
function setDirroot($dirroot) {
if (!is_dir($dirroot)) {
$this->error("setDirroot: $dirroot is not a directory.");
return false;
}
if (substr($dirroot, -1) != "/") {
$dirroot .= "/";
}
$this->dirroot = $dirroot;
return true;
}
/**
* The method to set the libdir directory
* @access public
* @return boolean
*/
function setLibdir($libdir) {
if (substr($libdir, -1) == "/") {
$libdir = substr($libdir, 0, -1);
}
if (str_replace("/", "", $libdir) == $libdir) {
$libdir = $this->dirroot . $libdir;
}
if (!is_dir($libdir)) {
$this->error("setLibdir: $libdir is not a directory.");
return false;
}
$this->libdir = $libdir . "/";
return true;
}
/**
* The method to set the libjsdir directory
* @access public
* @return boolean
*/
function setLibjsdir($libjsdir) {
if (substr($libjsdir, -1) == "/") {
$libjsdir = substr($libjsdir, 0, -1);
}
if (str_replace("/", "", $libjsdir) == $libjsdir) {
$libjsdir = $this->dirroot . $libjsdir;
}
if (!is_dir($libjsdir)) {
$this->error("setLibjsdir: $libjsdir is not a directory.");
return false;
}
$this->libjsdir = $libjsdir . "/";
return true;
}
/**
* The method to set libjswww
* @access public
* @return void
*/
function setLibjswww($libjswww) {
if (substr($libjswww, -1) != "/") {
$libjswww .= "/";
}
$this->libjswww = $libjswww;
}
/**
* The method to set the imgdir directory
* @access public
* @return boolean
*/
function setImgdir($imgdir) {
if (substr($imgdir, -1) == "/") {
$imgdir = substr($imgdir, 0, -1);
}
if (str_replace("/", "", $imgdir) == $imgdir) {
$imgdir = $this->dirroot . $imgdir;
}
if (!is_dir($imgdir)) {
$this->error("setImgdir: $imgdir is not a directory.");
return false;
}
$this->imgdir = $imgdir . "/";
return true;
}
/**
* The method to set imgwww
* @access public
* @return void
*/
function setImgwww($imgwww) {
if (substr($imgwww, -1) != "/") {
$imgwww .= "/";
}
$this->imgwww = $imgwww;
}
/**
* The method to set the tpldir directory
* @access public
* @return boolean
*/
function setTpldir($tpldir) {
if (substr($tpldir, -1) == "/") {
$tpldir = substr($tpldir, 0, -1);
}
if (str_replace("/", "", $tpldir) == $tpldir) {
$tpldir = $this->dirroot . $tpldir;
}
if (!is_dir($tpldir)) {
$this->error("setTpldir: $tpldir is not a directory.");
return false;
}
$this->tpldir = $tpldir . "/";
// Then we update the default filenames of templates
$this->horizontalMenuTpl = $this->dirroot . $this->tpldir . "layersmenu-horizontal_menu.ihtml";
$this->verticalMenuTpl = $this->dirroot . $this->tpldir . "layersmenu-vertical_menu.ihtml";
$this->subMenuTpl = $this->dirroot . $this->tpldir . "layersmenu-sub_menu.ihtml";
//
return true;
}
/**
* The method to set horizontalMenuTpl
* @access public
* @return boolean
*/
function setHorizontalMenuTpl($horizontalMenuTpl) {
if (str_replace("/", "", $horizontalMenuTpl) == $horizontalMenuTpl) {
$horizontalMenuTpl = $this->tpldir . $horizontalMenuTpl;
}
if (!file_exists($horizontalMenuTpl)) {
$this->error("setHorizontalMenuTpl: file $horizontalMenuTpl does not exist.");
return false;
}
$this->horizontalMenuTpl = $horizontalMenuTpl;
return true;
}
/**
* The method to set verticalMenuTpl
* @access public
* @return boolean
*/
function setVerticalMenuTpl($verticalMenuTpl) {
if (str_replace("/", "", $verticalMenuTpl) == $verticalMenuTpl) {
$verticalMenuTpl = $this->tpldir . $verticalMenuTpl;
}
if (!file_exists($verticalMenuTpl)) {
$this->error("setVerticalMenuTpl: file $verticalMenuTpl does not exist.");
return false;
}
$this->verticalMenuTpl = $verticalMenuTpl;
return true;
}
/**
* The method to set subMenuTpl
* @access public
* @return boolean
*/
function setSubMenuTpl($subMenuTpl) {
if (str_replace("/", "", $subMenuTpl) == $subMenuTpl) {
$subMenuTpl = $this->tpldir . $subMenuTpl;
}
if (!file_exists($subMenuTpl)) {
$this->error("setSubMenuTpl: file $subMenuTpl does not exist.");
return false;
}
$this->subMenuTpl = $subMenuTpl;
return true;
}
/**
* A method to set forwardArrow
* @access public
* @param string $forwardArrow the forward arrow HTML code
* @return void
*/
function setForwardArrow($forwardArrow) {
$this->forwardArrow = $forwardArrow;
}
/**
* The method to set an image to be used for the forward arrow
* @access public
* @param string $forwardArrowImg the forward arrow image filename
* @return boolean
*/
function setForwardArrowImg($forwardArrowImg) {
if (!file_exists($this->imgdir . $forwardArrowImg)) {
$this->error("setForwardArrowImg: file " . $this->imgdir . $forwardArrowImg . " does not exist.");
return false;
}
$foobar = getimagesize($this->imgdir . $forwardArrowImg);
$this->forwardArrow = " imgwww . $forwardArrowImg . "\" width=\"" . $foobar[0] . "\" height=\"" . $foobar[1] . "\" border=\"0\" alt=\" >>\" />";
return true;
}
/**
* A method to set downArrow
* @access public
* @param string $downArrow the down arrow HTML code
* @return void
*/
function setDownArrow($downArrow) {
$this->downArrow = $downArrow;
}
/**
* The method to set an image to be used for the down arrow
* @access public
* @param string $downArrowImg the down arrow image filename
* @return boolean
*/
function setDownArrowImg($downArrowImg) {
if (!file_exists($this->imgdir . $downArrowImg)) {
$this->error("setDownArrowImg: file " . $this->imgdir . $downArrowImg . " does not exist.");
return false;
}
$foobar = getimagesize($this->imgdir . $downArrowImg);
$this->downArrow = " imgwww . $downArrowImg . "\" width=\"" . $foobar[0] . "\" height=\"" . $foobar[1] . "\" border=\"0\" alt=\" >>\" />";
return true;
}
/**
* The method to read the menu structure from a file
* @access public
* @param string $tree_file the menu structure file
* @return boolean
*/
function setMenuStructureFile($tree_file) {
if (!($fd = fopen($tree_file, "r"))) {
$this->error("setMenuStructureFile: unable to open file $tree_file.");
return false;
}
$this->menuStructure = "";
while ($buffer = fgets($fd, 4096)) {
$buffer = ereg_replace(chr(13), "", $buffer); // Microsoft Stupidity Suppression
$this->menuStructure .= $buffer;
}
fclose($fd);
if ($this->menuStructure == "") {
$this->error("setMenuStructureFile: $tree_file is empty.");
return false;
}
return true;
}
/**
* The method to set the menu structure passing it through a string
* @access public
* @param string $tree_string the menu structure string
* @return boolean
*/
function setMenuStructureString($tree_string) {
$this->menuStructure = ereg_replace(chr(13), "", $tree_string); // Microsoft Stupidity Suppression
if ($this->menuStructure == "") {
$this->error("setMenuStructureString: empty string.");
return false;
}
return true;
}
/**
* The method to set the value of separator
* @access public
* @return void
*/
function setSeparator($separator) {
$this->separator = $separator;
}
/**
* The method to set parameters for the DB connection
* @access public
* @param string $dns Data Source Name: the connection string for PEAR DB
* @param bool $persistent DB connections are either persistent or not persistent
* @return boolean
*/
function setDBConnParms($dsn, $persistent=false) {
if (!is_string($dsn)) {
$this->error("initdb: \$dsn is not an string.");
return false;
}
if (!is_bool($persistent)) {
$this->error("initdb: \$persistent is not a boolean.");
return false;
}
$this->dsn = $dsn;
$this->persistent = $persistent;
return true;
}
/**
* The method to set the name of the table storing data describing the menu
* @access public
* @param string
* @return boolean
*/
function setTableName($tableName) {
if (!is_string($tableName)) {
$this->error("setTableName: \$tableName is not a string.");
return false;
}
$this->tableName = $tableName;
return true;
}
/**
* The method to set the name of the i18n table corresponding to $tableName
* @access public
* @param string
* @return boolean
*/
function setTableName_i18n($tableName_i18n) {
if (!is_string($tableName_i18n)) {
$this->error("setTableName_i18n: \$tableName_i18n is not a string.");
return false;
}
$this->tableName_i18n = $tableName_i18n;
return true;
}
/**
* The method to set names of fields of the table storing data describing the menu
* @access public
* @param array
* @return boolean
*/
function setTableFields($tableFields) {
if (!is_array($tableFields)) {
$this->error("setTableFields: \$tableFields is not an array.");
return false;
}
if (count($tableFields) == 0) {
$this->error("setTableFields: \$tableFields is a zero-length array.");
return false;
}
reset ($tableFields);
while (list($key, $value) = each($tableFields)) {
$this->tableFields[$key] = ($value == "") ? "''" : $value;
}
return true;
}
/**
* The method to set names of fields of the i18n table corresponding to $tableName
* @access public
* @param array
* @return boolean
*/
function setTableFields_i18n($tableFields_i18n) {
if (!is_array($tableFields_i18n)) {
$this->error("setTableFields_i18n: \$tableFields_i18n is not an array.");
return false;
}
if (count($tableFields_i18n) == 0) {
$this->error("setTableFields_i18n: \$tableFields_i18n is a zero-length array.");
return false;
}
reset ($tableFields_i18n);
while (list($key, $value) = each($tableFields_i18n)) {
$this->tableFields_i18n[$key] = ($value == "") ? "''" : $value;
}
return true;
}
/**
* The method to parse the current menu structure and correspondingly update related variables
* @access public
* @param string $menu_name the name to be attributed to the menu
* whose structure has to be parsed
* @return void
*/
function parseStructureForMenu(
$menu_name = "" // non consistent default...
) {
$this->_maxLevel[$menu_name] = 0;
$this->_firstLevelCnt[$menu_name] = 0;
$this->_firstItem[$menu_name] = $this->_nodesCount + 1;
$cnt = $this->_firstItem[$menu_name];
$menuStructure = $this->menuStructure;
/* *********************************************** */
/* Partially based on a piece of code taken from */
/* TreeMenu 1.1 - Bjorge Dijkstra (bjorge@gmx.net) */
/* *********************************************** */
while ($menuStructure != "") {
$before_cr = strcspn($menuStructure, "\n");
$buffer = substr($menuStructure, 0, $before_cr);
$menuStructure = substr($menuStructure, $before_cr+1);
if (substr($buffer, 0, 1) != "#") { // non commented item line...
$tmp = rtrim($buffer);
$node = explode($this->separator, $tmp);
for ($i=count($node); $i<=6; $i++) {
$node[$i] = "";
}
$this->tree[$cnt]["level"] = strlen($node[0]);
$this->tree[$cnt]["text"] = $node[1];
$this->tree[$cnt]["link"] = $node[2];
$this->tree[$cnt]["title"] = $node[3];
$this->tree[$cnt]["icon"] = $node[4];
$this->tree[$cnt]["target"] = $node[5];
$this->tree[$cnt]["expanded"] = $node[6];
$cnt++;
}
}
/* *********************************************** */
$this->_lastItem[$menu_name] = count($this->tree);
$this->_nodesCount = $this->_lastItem[$menu_name];
$this->tree[$this->_lastItem[$menu_name]+1]["level"] = 0;
$this->_postParse($menu_name);
}
/**
* The method to parse the current menu table and correspondingly update related variables
* @access public
* @param string $menu_name the name to be attributed to the menu
* whose structure has to be parsed
* @param string $language i18n language; either omit it or pass
* an empty string ("") if you do not want to use any i18n table
* @return void
*/
function scanTableForMenu(
$menu_name = "", // non consistent default...
$language = ""
) {
$this->_maxLevel[$menu_name] = 0;
$this->_firstLevelCnt[$menu_name] = 0;
unset($this->tree[$this->_nodesCount+1]);
$this->_firstItem[$menu_name] = $this->_nodesCount + 1;
/* BEGIN BENCHMARK CODE
$time_start = $this->_getmicrotime();
/* END BENCHMARK CODE */
$db = DB::connect($this->dsn, $this->persistent);
if (DB::isError($db)) {
$this->error("scanTableForMenu: " . $db->getMessage());
}
$dbresult = $db->query("
SELECT " .
$this->tableFields["id"] . " AS id, " .
$this->tableFields["parent_id"] . " AS parent_id, " .
$this->tableFields["text"] . " AS text, " .
$this->tableFields["link"] . " AS link, " .
$this->tableFields["title"] . " AS title, " .
$this->tableFields["icon"] . " AS icon, " .
$this->tableFields["target"] . " AS target, " .
$this->tableFields["expanded"] . " AS expanded
FROM " . $this->tableName . "
WHERE " . $this->tableFields["id"] . " <> 1
ORDER BY " . $this->tableFields["orderfield"] . ", " . $this->tableFields["text"] . " ASC
");
$this->_tmpArray = array();
while ($dbresult->fetchInto($row, DB_FETCHMODE_ASSOC)) {
$this->_tmpArray[$row["id"]]["parent_id"] = $row["parent_id"];
$this->_tmpArray[$row["id"]]["text"] = $row["text"];
$this->_tmpArray[$row["id"]]["link"] = $row["link"];
$this->_tmpArray[$row["id"]]["title"] = $row["title"];
$this->_tmpArray[$row["id"]]["icon"] = $row["icon"];
$this->_tmpArray[$row["id"]]["target"] = $row["target"];
$this->_tmpArray[$row["id"]]["expanded"] = $row["expanded"];
}
if ($language != "") {
$dbresult = $db->query("
SELECT " .
$this->tableFields_i18n["id"] . " AS id, " .
$this->tableFields_i18n["text"] . " AS text, " .
$this->tableFields_i18n["title"] . " AS title
FROM " . $this->tableName_i18n . "
WHERE " . $this->tableFields_i18n["id"] . " <> 1
AND " . $this->tableFields_i18n["language"] . " = '$language'
");
while ($dbresult->fetchInto($row, DB_FETCHMODE_ASSOC)) {
$this->_tmpArray[$row["id"]]["text"] = $row["text"];
$this->_tmpArray[$row["id"]]["title"] = $row["title"];
}
}
unset($dbresult);
unset($row);
$this->_depthFirstSearch($this->_tmpArray, $menu_name, 1, 1);
/* BEGIN BENCHMARK CODE
$time_end = $this->_getmicrotime();
$time = $time_end - $time_start;
print "TIME ELAPSED = " . $time . "\n
";
/* END BENCHMARK CODE */
$this->_lastItem[$menu_name] = count($this->tree);
$this->_nodesCount = $this->_lastItem[$menu_name];
$this->tree[$this->_lastItem[$menu_name]+1]["level"] = 0;
$this->_postParse($menu_name);
}
function _getmicrotime() {
list($usec, $sec) = explode(" ", microtime());
return ((float) $usec + (float) $sec);
}
/**
* Recursive method to perform the depth-first search of the tree data taken from the current menu table
* @access private
* @param array $tmpArray the temporary array that stores data to perform
* the depth-first search
* @param string $menu_name the name to be attributed to the menu
* whose structure has to be parsed
* @param integer $parent_id id of the item whose children have
* to be searched for
* @param integer $level the hierarchical level of children to be searched for
* @return void
*/
function _depthFirstSearch($tmpArray, $menu_name, $parent_id=1, $level) {
reset ($tmpArray);
while (list($id, $foobar) = each($tmpArray)) {
if ($foobar["parent_id"] == $parent_id) {
unset($tmpArray[$id]);
unset($this->_tmpArray[$id]);
$cnt = count($this->tree) + 1;
$this->tree[$cnt]["level"] = $level;
$this->tree[$cnt]["text"] = $foobar["text"];
$this->tree[$cnt]["link"] = $foobar["link"];
$this->tree[$cnt]["title"] = $foobar["title"];
$this->tree[$cnt]["icon"] = $foobar["icon"];
$this->tree[$cnt]["target"] = $foobar["target"];
$this->tree[$cnt]["expanded"] = $foobar["expanded"];
unset($foobar);
if ($id != $parent_id) {
$this->_depthFirstSearch($this->_tmpArray, $menu_name, $id, $level+1);
}
}
}
}
/**
* A method providing parsing needed after both file/string parsing and DB table parsing
* @access private
* @param string $menu_name the name of the menu for which the parsing
* has to be performed
* @return void
*/
function _postParse(
$menu_name = "" // non consistent default...
) {
for ($cnt=$this->_firstItem[$menu_name]; $cnt<=$this->_lastItem[$menu_name]; $cnt++) { // this counter scans all nodes of the new menu
$this->tree[$cnt]["child_of_root_node"] = ($this->tree[$cnt]["level"] == 1);
$this->tree[$cnt]["parsed_text"] = stripslashes($this->tree[$cnt]["text"]);
$this->tree[$cnt]["parsed_link"] = (ereg_replace(" ", "", $this->tree[$cnt]["link"]) == "") ? "#" : $this->prependedUrl . $this->tree[$cnt]["link"];
$this->tree[$cnt]["parsed_title"] = ($this->tree[$cnt]["title"] == "") ? "" : " title=\"" . addslashes($this->tree[$cnt]["title"]) . "\"";
$fooimg = $this->imgdir . $this->tree[$cnt]["icon"];
if ($this->tree[$cnt]["icon"] == "" || !(file_exists($fooimg))) {
$this->tree[$cnt]["parsed_icon"] = "";
} else {
$this->tree[$cnt]["parsed_icon"] = $this->tree[$cnt]["icon"];
$foobar = getimagesize($fooimg);
$this->tree[$cnt]["iconwidth"] = $foobar[0];
$this->tree[$cnt]["iconheight"] = $foobar[1];
}
$this->tree[$cnt]["parsed_target"] = ($this->tree[$cnt]["target"] == "") ? "" : " target=\"" . $this->tree[$cnt]["target"] . "\"";
// $this->tree[$cnt]["expanded"] = ($this->tree[$cnt]["expanded"] == "") ? 0 : $this->tree[$cnt]["expanded"];
$this->_maxLevel[$menu_name] = max($this->_maxLevel[$menu_name], $this->tree[$cnt]["level"]);
if ($this->tree[$cnt]["level"] == 1) {
$this->_firstLevelCnt[$menu_name]++;
}
}
}
/**
* A method providing parsing needed both for horizontal and vertical menus; it can be useful also with the ProcessLayersMenu extended class
* @access public
* @param string $menu_name the name of the menu for which the parsing
* has to be performed
* @return void
*/
function parseCommon(
$menu_name = "" // non consistent default...
) {
for ($cnt=$this->_firstItem[$menu_name]; $cnt<=$this->_lastItem[$menu_name]; $cnt++) { // this counter scans all nodes of the new menu
$this->tree[$cnt]["layer_label"] = "L" . $cnt;
$fooimg = $this->imgdir . $this->tree[$cnt]["parsed_icon"];
if ($this->tree[$cnt]["parsed_icon"] == "" || !(file_exists($fooimg))) {
$this->tree[$cnt]["icontag"] = "";
} else {
$this->tree[$cnt]["icontag"] = "imgwww . $this->tree[$cnt]["parsed_icon"] . "\" width=\"" . $this->tree[$cnt]["iconwidth"] . "\" height=\"" . $this->tree[$cnt]["iconheight"] . "\" border=\"0\" alt=\"O\" /> ";
}
$current_node[$this->tree[$cnt]["level"]] = $cnt;
if ($this->tree[$cnt]["level"] > 1) {
$this->tree[$cnt]["father_node"] = $current_node[$this->tree[$cnt]["level"]-1];
}
$this->father .= ($this->tree[$cnt]["child_of_root_node"]) ? "" : "father['L" . $cnt . "'] = \"" . $this->tree[$this->tree[$cnt]["father_node"]]["layer_label"] . "\";\n";
$this->tree[$cnt]["not_a_leaf"] = ($this->tree[$cnt+1]["level"]>$this->tree[$cnt]["level"] && $cnt<$this->_lastItem[$menu_name]);
// if the above condition is true, the node is not a leaf,
// hence it has at least a child; if it is false, the node is a leaf
if ($this->tree[$cnt]["not_a_leaf"]) {
// initialize the corresponding layer content trought a void string
$this->tree[$cnt]["layer_content"] = "";
// the new layer is accounted for in the layers list
$this->numl++;
$this->listl .= "listl[" . $this->numl . "] = \"" . $this->tree[$cnt]["layer_label"] . "\";\n";
}
/*
if ($this->tree[$cnt]["not_a_leaf"]) {
$this->tree[$cnt]["parsed_link"] = "#";
*/
}
}
/**
* A method needed to update the footer both for horizontal and vertical menus
* @access private
* @param string $menu_name the name of the menu for which the updating
* has to be performed
* @return void
*/
function _updateFooter(
$menu_name = "" // non consistent default...
) {
$t = new Template();
$t->setFile("tplfile", $this->subMenuTpl);
$t->setBlock("tplfile", "template", "template_blck");
$t->setBlock("template", "sub_menu_cell", "sub_menu_cell_blck");
$t->setVar("sub_menu_cell_blck", "");
$t->setVar("abscissaStep", $this->abscissaStep);
for ($cnt=$this->_firstItem[$menu_name]; $cnt<=$this->_lastItem[$menu_name]; $cnt++) {
if ($this->tree[$cnt]["not_a_leaf"]) {
$this->footer .= "\n
\n" . $this->_treeMenu[$menu_name] . " | \n" . "