Index: /trunk/admin/controller.php
===================================================================
--- /trunk/admin/controller.php	(revision 357)
+++ /trunk/admin/controller.php	(revision 357)
@@ -0,0 +1,25 @@
+<?php
+
+$request_method = '_' . $_SERVER['REQUEST_METHOD'];
+switch ( $request_method )
+{
+	case '_GET' :
+		$params = $_GET;
+	break;
+	case '_POST' :
+		$params = $_POST;
+	break;
+	case '_HEAD' :
+	case '_PUT' :
+	default :
+		echo "controller - request method not avaible";
+		return false;
+}
+
+require_once dirname(__FILE__) . '/inc/Controller.class.php';
+
+$controller = new Controller;
+printf("%s", $controller->exec($$request_method));
+
+exit(0);
+?>
Index: /trunk/admin/inc/hook_admin.inc.php
===================================================================
--- /trunk/admin/inc/hook_admin.inc.php	(revision 312)
+++ /trunk/admin/inc/hook_admin.inc.php	(revision 357)
@@ -77,4 +77,9 @@
 	}
  
+	if (! $GLOBALS['phpgw']->acl->check('site_config_access',1,'admin'))
+	{
+		$file['VoIP settings']         = $GLOBALS['phpgw']->link('/index.php','menuaction=admin.uivoip.edit_conf');
+	}
+
 	/* Do not modify below this line */
 	display_section('admin',$file);
Index: /trunk/admin/inc/class.uivoip.inc.php
===================================================================
--- /trunk/admin/inc/class.uivoip.inc.php	(revision 357)
+++ /trunk/admin/inc/class.uivoip.inc.php	(revision 357)
@@ -0,0 +1,143 @@
+<?php
+
+ /**************************************************************************\
+  * Expresso Livre - Voip - administration                                   *
+  *															                 *
+  * --------------------------------------------                             *
+  *  This program is free software; you can redistribute it and/or modify it *
+  *  under the terms of the GNU General Public License as published by the   *
+  *  Free Software Foundation; either version 2 of the License, or (at your  *
+  *  option) any later version.                                              *
+  \**************************************************************************/
+
+class uivoip
+{
+	var $public_functions = array(
+		'add'      => True,
+		'edit_conf' => True,
+	);
+
+	var $bo;
+
+	final function __construct()
+	{
+		if(!isset($_SESSION['admin']['ldap_host']))
+		{
+			$_SESSION['admin']['server']['ldap_host'] = $GLOBALS['phpgw_info']['server']['ldap_host'];
+			$_SESSION['admin']['server']['ldap_root_dn'] = $GLOBALS['phpgw_info']['server']['ldap_root_dn'];
+			$_SESSION['admin']['server']['ldap_host_pw'] = $GLOBALS['phpgw_info']['server']['ldap_root_pw'];
+			$_SESSION['admin']['server']['ldap_context'] = $GLOBALS['phpgw_info']['server']['ldap_context'];
+		}
+		$this->bo = CreateObject('admin.bovoip');
+	}
+
+	final function edit_conf()
+	{
+		if($GLOBALS['phpgw']->acl->check('applications_access',1,'admin'))
+		{
+			$GLOBALS['phpgw']->redirect_link('/index.php');
+		}		
+
+		$GLOBALS['phpgw_info']['flags']['app_header'] = lang('Admin') .' - ' . lang('Configuration Service VoIP');
+
+		if(!@is_object($GLOBALS['phpgw']->js))
+		{
+			$GLOBALS['phpgw']->js = CreateObject('phpgwapi.javascript');
+		}
+
+		$webserver_url = $GLOBALS['phpgw_info']['server']['webserver_url'];
+		$webserver_url = ( !empty($webserver_url) ) ? $webserver_url : '/';
+
+		if(strrpos($webserver_url,'/') === false || strrpos($webserver_url,'/') != (strlen($webserver_url)-1))
+			$webserver_url .= '/';
+
+		$js = array('connector','xtools','functions');
+		
+		foreach( $js as $tmp )
+			$GLOBALS['phpgw']->js->validate_file('voip',$tmp,'admin');
+		
+		$GLOBALS['phpgw']->common->phpgw_header();
+		echo parse_navbar();
+		echo '<script type="text/javascript">var path_adm="'.$webserver_url .'"</script>';
+
+		$ous = "<option value='-1'>-- ".lang('Select Organization')." --</option>";	
+		if( ($LdapOus = $this->bo->getOuLdap()) )
+		{
+			foreach($LdapOus as $tmp )
+				$ous .= "<option value='".$tmp."'>".$tmp."</option>";
+		}
+		
+		$groups_voip = $this->bo->getConfDB();
+
+		if( is_array($groups_voip) )
+		{
+			$gvoip = explode(',', $groups_voip[0]['config_value']);
+			natcasesort($gvoip);
+			
+			foreach( $gvoip as $tmp )
+				$gvoip .= "<option value='".$tmp."'>".$tmp."</option>";
+		}
+
+		$GLOBALS['phpgw']->template->set_file(array('voip' => 'voip.tpl'));
+		$GLOBALS['phpgw']->template->set_block('voip','voip_page','voip_page');	
+		$GLOBALS['phpgw']->template->set_var(array(
+										'action_url' => $GLOBALS['phpgw']->link('/index.php','menuaction=admin.uivoip.add'),
+										'lang_VoIP_settings' => lang('Configuration Service VoIP'),
+										'lang_Enter_your_VoIP_server_address' => lang('Enter your VoIP server address'),	
+										'lang_Enter_your_VoIP_server_url' => lang('Enter your VoIP server url'),	
+										'lang_Enter_your_VoIP_server_port' => lang('Enter your VoIP server port'),
+										'lang_save' => lang('Save'),
+										'lang_cancel' => lang('Cancel'),
+										'value_voip_server' => ($GLOBALS['phpgw_info']['server']['voip_server']) ? $GLOBALS['phpgw_info']['server']['voip_server'] : '',
+										'value_voip_url' => ($GLOBALS['phpgw_info']['server']['voip_url']) ? $GLOBALS['phpgw_info']['server']['voip_url'] : '',
+										'value_voip_port' => ($GLOBALS['phpgw_info']['server']['voip_port']) ? $GLOBALS['phpgw_info']['server']['voip_port'] : '',
+										'lang_load' => lang('Wait Loading...!'),
+										'lang_grupos_ldap' => 'Grupos Ldap',
+										'lang_grupos_liberados' => 'Grupos Liberados',
+										'lang_groups_ldap' => lang('groups ldap'),
+										'lang_organizations' => lang('Organizations'),
+										'groups_voip' => $gvoip,
+										'ous_ldap' => $ous
+										));
+	
+		$GLOBALS['phpgw']->template->pparse('out','voip_page');
+	}
+	
+	function display_row($label, $value)
+	{
+		$GLOBALS['phpgw']->template->set_var('tr_color',$this->nextmatchs->alternate_row_color());
+		$GLOBALS['phpgw']->template->set_var('label',$label);
+		$GLOBALS['phpgw']->template->set_var('value',$value);
+		$GLOBALS['phpgw']->template->parse('rows','row',True);
+	}
+
+	function add()
+	{
+		
+		if($GLOBALS['phpgw']->acl->check('applications_access',1,'admin'))
+		{
+			$GLOBALS['phpgw']->redirect_link('/index.php');
+		}		
+		
+		if ($_POST['cancel'])
+		{
+			$GLOBALS['phpgw']->redirect_link('/admin/index.php');
+		}
+
+		if ( $_POST['save'] )
+		{
+			$conf['voip_server']= $_POST['voip_server'];
+			$conf['voip_url']	= $_POST['voip_url'];
+			$conf['voip_port']	= $_POST['voip_port'];
+			
+			if( is_array($_POST['voip_groups']) )
+				foreach($_POST['voip_groups'] as $tmp)
+					$conf['voip_groups'] = (count($conf['voip_groups']) > 0 ) ? $conf['voip_groups'] . "," . $tmp : $tmp;
+			
+			$this->bo->setConfDB($conf);
+		}
+
+		$GLOBALS['phpgw']->redirect_link('/admin/index.php');
+	}
+}
+?>
Index: /trunk/admin/inc/class.sovoip.inc.php
===================================================================
--- /trunk/admin/inc/class.sovoip.inc.php	(revision 357)
+++ /trunk/admin/inc/class.sovoip.inc.php	(revision 357)
@@ -0,0 +1,136 @@
+<?php
+
+ /**************************************************************************\
+  * Expresso Livre - Voip - administration                                   *
+  *															                 *
+  * --------------------------------------------                             *
+  *  This program is free software; you can redistribute it and/or modify it *
+  *  under the terms of the GNU General Public License as published by the   *
+  *  Free Software Foundation; either version 2 of the License, or (at your  *
+  *  option) any later version.                                              *
+  \**************************************************************************/
+
+define('PHPGW_INCLUDE_ROOT', '../');
+define('PHPGW_API_INC','../phpgwapi/inc');
+require_once( PHPGW_API_INC . '/class.common.inc.php' );
+
+class sovoip
+{
+	private $db;
+	private $common;
+	private $ldap;
+	private $ldap_host;
+	private $ldap_root_dn;
+	private $ldap_root_pw;
+	private $ldap_context;
+
+	final function __construct()
+	{
+		$this->ldap_host	 = $_SESSION['admin']['server']['ldap_host'];
+		$this->ldap_root_dn	 = $_SESSION['admin']['server']['ldap_root_dn'];
+		$this->ldap_root_pw  = $_SESSION['admin']['server']['ldap_root_pw'];
+		$this->ldap_context  = $_SESSION['admin']['server']['ldap_context'];
+		$this->common = new common();
+	}
+
+	final function __destruct()
+	{
+		if( $this->ldap )
+			ldap_close($this->ldap);
+	}
+
+	public final function getOuLdap()
+	{
+		$this->ldap = $this->common->ldapConnect();
+		
+		if ( $this->ldap )	
+		{
+			$filter = "ou=*";
+			$justthese = array("ou");
+			$search = ldap_list($this->ldap, $this->ldap_context, $filter, $justthese);
+			$entry = ldap_get_entries($this->ldap, $search);
+		}
+
+		if( $entry )
+		{
+			foreach($entry as $tmp)
+				if( $tmp['ou'][0] != "" )
+					$result_ou[] = $tmp['ou'][0];
+		}
+		
+		natcasesort($result_ou);
+
+		return (($result_ou) ? $result_ou : '');
+	}	
+
+	public final function getGroupsLdap($pOrg)
+	{
+		$organization = 'ou=' . $pOrg['ou'] .",". $this->ldap_context;
+ 		
+		$this->ldap = $this->common->ldapConnect($this->ldap_host,$this->ldap_root_dn,$this->ldap_root_pw);
+		
+		if( $this->ldap )	
+		{
+			$filter = "(&(phpgwAccountType=g)(objectClass=posixGroup))";
+			$justthese = array("cn");
+			$search = ldap_list($this->ldap, $organization, $filter, $justthese);
+			$entry = ldap_get_entries( $this->ldap, $search );
+
+			if( $entry )
+			{
+				foreach($entry as $tmp)
+					if( $tmp['cn'][0] != "" )
+						$result_groups[] = $tmp['cn'][0];
+			}
+			
+			natcasesort($result_groups);
+		}
+		
+		return (($result_groups) ? $result_groups : '');
+	}
+
+	public final function setConfDB($pConf)
+	{
+		$this->db = $GLOBALS['phpgw']->db;
+		
+		if( $this->db )
+		{
+			foreach($pConf as $key => $tmp )
+			{
+				$query = "SELECT * FROM phpgw_config WHERE config_app = 'phpgwapi' and config_name ='".trim($key)."'";
+				
+				$this->db->query($query);
+					
+				if(!$this->db->next_record())
+				{
+					$query = "INSERT INTO phpgw_config(config_app,config_name,config_value) VALUES('phpgwapi','".trim($key)."','".$tmp."')";
+					$this->db->query($query);
+				}
+				else
+				{
+					$query = "UPDATE phpgw_config SET config_value = '".$tmp."' WHERE config_app = 'phpgwapi' AND config_name = '".trim($key)."'";
+					$this->db->query($query);
+				}
+			}
+		}	
+	}
+
+	public final function getConfDB()
+	{
+		$this->db = $GLOBALS['phpgw']->db;
+
+		if( $this->db )
+		{
+			$query = "SELECT * FROM phpgw_config WHERE config_app = 'phpgwapi' and config_name = 'voip_groups'";
+			
+			if(!$this->db->query($query))
+				return null;
+			
+			while($this->db->next_record())
+				$result[] = $this->db->row();		
+		}
+
+		return (($result) ? $result : '');		
+	}
+}
+?>
Index: /trunk/admin/inc/controller.xml
===================================================================
--- /trunk/admin/inc/controller.xml	(revision 357)
+++ /trunk/admin/inc/controller.xml	(revision 357)
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<controller>
+	<controller-security>
+		<string-access>act</string-access>
+		<string-delimiter>.</string-delimiter>
+	</controller-security>
+	<controller-contentes>
+		<item param="c" section="css" />
+		<item param="j" section="js" />
+		<item param="p" section="php" />
+		<item param="php" section="php" />
+		<item param="xm" section="xml" />
+		<item param="xs" section="xsl" />
+		<item param="t" section="php" />
+		<item param="$this" section="php" />
+	</controller-contentes>
+	<controller-sections>
+		<css></css>
+		<js path="/var/www/expresso/admin/js/voip" suffix=".js" debug="true">
+			<item ref="connector" js="connector" />
+			<item ref="xtools" js="xtools" />
+			<item ref="functions" js="functions" />
+		</js>
+		<php path="/var/www/expresso/admin/inc" suffix=".class.php">
+			<item ref="db_im" alias="get_preferences" class="db_im" method="get_preferences" prefix="class." suffix=".inc.php"/>
+			<item ref="ldap_im" alias="list_organizations_ldap" class="ldap_im" method="list_organizations_ldap" prefix="class." suffix=".inc.php"/>
+			<item ref="bovoip" alias="getGroupsLdap" class="bovoip" method="getGroupsLdap" prefix="class." suffix=".inc.php"/>
+		</php>
+		<xml></xml>
+		<xsl></xsl>
+	</controller-sections>
+</controller>
Index: /trunk/admin/inc/class.bovoip.inc.php
===================================================================
--- /trunk/admin/inc/class.bovoip.inc.php	(revision 357)
+++ /trunk/admin/inc/class.bovoip.inc.php	(revision 357)
@@ -0,0 +1,61 @@
+<?php
+
+ /**************************************************************************\
+  * Expresso Livre - Voip - administration                                   *
+  *															                 *
+  * --------------------------------------------                             *
+  *  This program is free software; you can redistribute it and/or modify it *
+  *  under the terms of the GNU General Public License as published by the   *
+  *  Free Software Foundation; either version 2 of the License, or (at your  *
+  *  option) any later version.                                              *
+  \**************************************************************************/
+
+require_once "class.sovoip.inc.php";
+
+class bovoip
+{
+	private $so;
+
+	final function __construct()
+	{
+		$this->so = new sovoip(); 	
+	}
+
+	public final function getConf()
+	{
+		$this->so->getConf();
+	}
+
+	public final function getOuLdap()
+	{
+		return $this->so->getOuLdap();
+	}
+
+	public final function getGroupsLdap($pOrg)
+	{
+		$groups = $this->so->getGroupsLdap($pOrg);
+		
+		$group = "<ldap_ou>";
+	
+		if((trim($groups) != '') && (is_array($groups)))
+		{
+			foreach($groups as $tmp)	
+				$group .= "<ou>" . $tmp . "</ou>";
+		}
+
+		$group .= "</ldap_ou>";
+		
+		return $group;
+	}	
+
+	public final function getConfDB()
+	{
+		return $this->so->getConfDB();
+	}
+
+	public final function setConfDB($pConf)
+	{
+		$this->so->setConfDB($pConf);	
+	}
+}
+?>
Index: /trunk/admin/inc/Controller.class.php
===================================================================
--- /trunk/admin/inc/Controller.class.php	(revision 357)
+++ /trunk/admin/inc/Controller.class.php	(revision 357)
@@ -0,0 +1,892 @@
+<?php
+
+class Controller
+{
+	const __CONTROLLER_SECURITY__				= 'controller-security';
+	const __CONTROLLER_CONTENTES__				= 'controller-contentes';
+	const __CONTROLLER_SECTIONS__				= 'controller-sections';
+
+	const __STRING_ACCESS__						= 'string-access';
+	const __STRING_DELIMITER__					= 'string-delimiter';
+
+	const __CONTROLLER_CONTENTES_ITEM__			= 'item';
+	const __CONTROLLER_CONTENTES_ITEM_PARAM__	= 'param';
+	const __CONTROLLER_CONTENTES_ITEM_SECTION__	= 'section';
+
+	const __CONTROLLER_SECTIONS_ITEM__			= 'item';
+
+	private $fatalError							= false;
+
+	public function __construct()
+	{
+		try
+		{
+			$controler_xml = dirname(__FILE__) . '/controller.xml';
+			if ( !file_exists($controler_xml) )
+				throw new Exception(__CLASS__ . ' [ ERROR ] :: the configuration file does not exist');
+
+			$this->dom = new DOMDocument;
+			$this->dom->preserveWhiteSpace = FALSE;
+			$this->dom->load($controler_xml);
+
+			unset($controler_xml);
+
+			$this->controller_security = $this->dom->getElementsByTagName(self::__CONTROLLER_SECURITY__);
+			if ( $this->controller_security->length === (int)0 )
+				throw new Exception(__CLASS__ . ' [ ERROR #0 ] :: the tag "' . self::__CONTROLLER_SECURITY__ . '" does not exist');
+			if ( $this->controller_security->length !== (int)1 )
+				throw new Exception(__CLASS__ . ' [ ERROR #1 ] :: exists more of a tag "' . self::__CONTROLLER_SECURITY__ . '"');
+
+			$this->controller_contentes = $this->dom->getElementsByTagName(self::__CONTROLLER_CONTENTES__);
+			if ( $this->controller_contentes->length === (int)0 )
+				throw new Exception(__CLASS__ . ' [ ERROR #2 ] :: the tag "' . self::__CONTROLLER_CONTENTES__ . '" does not exist');
+			if ( $this->controller_contentes->length !== (int)1 )
+				throw new Exception(__CLASS__ . ' [ ERROR #3 ] :: exists more of a tag "' . self::__CONTROLLER_CONTENTES__ . '"');
+			$this->controller_contentes = $this->controller_contentes->item(0);
+
+			$this->controller_sections = $this->dom->getElementsByTagName("controller-sections");
+			if ( $this->controller_sections->length === (int)0 )
+				throw new Exception(__CLASS__ . ' [ ERROR #4 ] :: the tag "' . self::__CONTROLLER_SECTIONS__ . '" does not exist');
+			if ( $this->controller_sections->length !== (int)1 )
+				throw new Exception(__CLASS__ . ' [ ERROR #5 ] :: exists more of a tag "' . self::__CONTROLLER_SECTIONS__ . '"');
+			$this->controller_sections = $this->controller_sections->item(0);
+
+			$this->string_access = $this->controller_security->item(0)->getElementsByTagName(self::__STRING_ACCESS__);
+			if ( $this->string_access->length === (int)0 )
+				throw new Exception(__CLASS__ . ' [ ERROR #6 ] :: the tag "' . self::__STRING_ACCESS__ . '" does not exist');
+			if ( $this->string_access->length !== (int)1 )
+				throw new Exception(__CLASS__ . ' [ ERROR #7 ] :: exists more of a tag "' . self::__STRING_ACCESS__ . '"');
+			$this->string_access = $this->string_access->item(0)->nodeValue;
+
+			$this->string_delimiter = $this->controller_security->item(0)->getElementsByTagName(self::__STRING_DELIMITER__);
+			( $this->string_delimiter->length === (int)0 )
+				and die(__CLASS__ . ' [ ERROR #8 ] :: the tag "' . self::__STRING_DELIMITER__ . '" does not exist');
+			if ( $this->string_delimiter->length !== (int)1 )
+				throw new Exception(__CLASS__ . ' [ ERROR #9 ] :: exists more of a tag "' . self::__STRING_DELIMITER__ . '"');
+			$this->string_delimiter = $this->string_delimiter->item(0)->nodeValue;
+		}
+		catch(Exception $e)
+		{
+			$this->fatalError = true;
+			return $e->getMessage();
+		}
+	}
+
+	public function __call($name, $arguments)
+	{
+		if ( !$this->fatalError )
+			switch ( $name )
+			{
+				case 'exec' :
+					return $this->_exec($arguments[0]);
+				break;
+				default : return "Method not avaible";
+			}
+	}
+
+	public function __toString()
+	{
+		return __CLASS__;
+	}
+
+	private final function _exec(array &$pRequest)
+	{
+		( $pRequest[$this->string_access] )
+			or die(__CLASS__ . ' [ ERROR #10 ] :: bad string action argument');
+
+		list($section_name, $ref, $alias) = explode($this->string_delimiter, $pRequest[$this->string_access]);
+		unset($pRequest[$this->string_access]);
+
+		$contents_itens = $this->controller_contentes->getElementsByTagName(self::__CONTROLLER_CONTENTES_ITEM__);
+
+		for ( $i = 0; $i < $contents_itens->length && $contents_itens->item($i)->getAttribute(self::__CONTROLLER_CONTENTES_ITEM_PARAM__) != $section_name; $i++ );
+		( !($i < $contents_itens->length) )
+			and die(__CLASS__ . ' [ ERROR #11 ] :: invalid section "' . $section_name . '"');
+
+		$section_name = $contents_itens->item($i)->getAttribute(self::__CONTROLLER_CONTENTES_ITEM_SECTION__);
+
+		$section = $this->controller_sections->getElementsByTagName($section_name);
+		( $section->length === (int)0 )
+			and die(__CLASS__ . ' [ ERROR #12 ] :: the tag "' . $section_name . '" does not exist');
+		( $section->length === (int)1 )
+			or die(__CLASS__ . ' [ ERROR #13 ] :: exists more of a tag "' . $section_name . '"');
+		$section = $section->item(0);
+
+		$section_itens = $section->getElementsByTagName(self::__CONTROLLER_SECTIONS_ITEM__);
+
+		if ( empty($alias) && $alias !== '0' )
+			for ( $i = 0; $i < $section_itens->length && $section_itens->item($i)->getAttribute('ref') != $ref; $i++ );
+		else
+			for ( $i = 0; $i < $section_itens->length && ( $section_itens->item($i)->getAttribute('ref') != $ref || $section_itens->item($i)->getAttribute('alias') !== $alias); $i++ );
+
+		( !($i < $section_itens->length) )
+			and die(__CLASS__ . ' [ ERROR #14 ] :: invalid reference "' . $ref . '"');
+
+		$path = $section_itens->item($i)->getAttribute('path')
+			or $path = $section->getAttribute('path')
+			or die(__CLASS__ . ' [ ERROR #15 ] :: bad path argument');
+
+		$prefix = $section_itens->item($i)->getAttribute('prefix')
+			or $prefix = $section->getAttribute('prefix');
+
+		$suffix = $section_itens->item($i)->getAttribute('suffix')
+			or $suffix = $section->getAttribute('suffix')
+			or die(__CLASS__ . ' [ ERROR #16 ] :: bad suffix argument');
+
+		return $this->$section_name(array("pSectionItem" => $section_itens->item($i), "pPath" => $path, "pPrefix" => $prefix, "pSuffix" => $suffix, "pRequest" => $pRequest));
+	}
+
+	private final function php()
+	{
+		$params = func_get_args();
+		extract($params[0]);
+
+		$class = $pSectionItem->getAttribute('class')
+			and $method = $pSectionItem->getAttribute('method')
+			or die(__CLASS__ . ' [ ERROR #17 ] :: bad class or method argument');
+
+		$file = "{$pPath}/{$pPrefix}{$class}{$pSuffix}";
+
+		file_exists($file)
+			or die(__CLASS__ . ' [ ERROR #18 ] :: the file that has the class was not opened');
+
+		require_once $file;
+
+		$obj = new ReflectionClass($class);
+
+		if ( $pRequest['classCostructor'] )
+		{
+			$obj = $obj->newInstance($pRequest['classCostructor']);
+			unset($pRequest['classCostructor']);
+		}
+		else
+			$obj = $obj->newInstance();
+
+		$method = new ReflectionMethod($class, $method);
+		$result = $method->invoke($obj, $pRequest);
+
+		#$obj = new $class;
+
+		#if ( $pRequest )
+		#	$result = $obj -> $method($pRequest);
+		#else
+		#	$result = $obj -> $method();
+
+		return $result;
+	}
+
+	private final function js()
+	{
+		$params = func_get_args();
+		extract($params[0]);
+
+		$js = $pSectionItem->getAttribute('js')
+			or die(__CLASS__ . ' [ ERROR #18 ] :: bad js argument');
+
+		$file = "{$pPath}/{$pPrefix}{$js}{$pSuffix}";
+
+		file_exists($file)
+			or die(__CLASS__ . ' [ ERROR #19 ] :: the file that has the class was not opened');
+
+		$debug = $pSectionItem->parentNode->getAttribute('debug');
+		if ( $debug && ($debug === 'true') )
+			return file_get_contents($file);
+
+		$packed_file = "{$pPath}/.packer.{$pPrefix}{$js}{$pSuffix}";
+
+		(
+			file_exists($packed_file)
+			and filemtime($packed_file) > filemtime($file)
+			and $packed = file_get_contents($packed_file)
+		)
+		or
+		(
+			$packer = new JavaScriptPacker(file_get_contents($file), 'High ASCII', true, true)
+			and $packed = $packer->pack()
+			and file_put_contents($packed_file, $packed)
+		);
+
+		return $packed;
+	}
+}
+
+class JavaScriptPacker
+{
+	const IGNORE = '$1';
+
+	private $_script = '';
+	private $_encoding = 62;
+	private $_fastDecode = true;
+	private $_specialChars = false;
+
+	private $_parsers = array();
+
+	private $LITERAL_ENCODING = array(
+		'None' => 0,
+		'Numeric' => 10,
+		'Normal' => 62,
+		'High ASCII' => 95
+	);
+
+	public function __construct ($pScript, $pEncoding = 62, $pFastDecode = true, $pSpecialChars = false)
+	{
+		$this->_script = $pScript . "\n";
+		if ( array_key_exists($pEncoding, $this->LITERAL_ENCODING) )
+			$pEncoding = $this->LITERAL_ENCODING[$pEncoding];
+
+		$this->_encoding = min((int)$pEncoding, 95);
+		$this->_fastDecode = $pFastDecode;
+		$this->_specialChars = $pSpecialChars;
+	}
+
+	public function pack()
+	{
+		$this->_addParser('_basicCompression');
+
+		if ( $this->_specialChars )
+			$this->_addParser('_encodeSpecialChars');
+
+		if ( $this->_encoding )
+			$this->_addParser('_encodeKeywords');
+
+		return $this->_pack($this->_script);
+	}
+
+	private function _pack($script)
+   	{
+		for ( $i = 0; isset($this->_parsers[$i]); $i++ )
+			$script = call_user_func(array(&$this,$this->_parsers[$i]), $script);
+
+		return $script;
+	}
+
+	// keep a list of parsing functions, they'll be executed all at once
+	private function _addParser($parser)
+	{
+		$this->_parsers[] = $parser;
+	}
+
+	// zero encoding - just removal of white space and comments
+	private function _basicCompression($script)
+   	{
+		$parser = new ParseMaster();
+		// make safe
+		$parser->escapeChar = '\\';
+		// protect strings
+		$parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE);
+		$parser->add('/"[^"\\n\\r]*"/', self::IGNORE);
+		// remove comments
+		$parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');
+		$parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');
+		// protect regular expressions
+		$parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE
+		$parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE);
+		// remove: ;;; doSomething();
+		if ($this->_specialChars) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/');
+		// remove redundant semi-colons
+		$parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops
+		$parser->add('/;+\\s*([};])/', '$2');
+		// apply the above
+		$script = $parser->exec($script);
+
+		// remove white-space
+		$parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');
+		$parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');
+		$parser->add('/\\s+/', '');
+		// done
+		return $parser->exec($script);
+	}
+
+	private function _encodeSpecialChars($script)
+	{
+		$parser = new ParseMaster();
+		// replace: $name -> n, $$name -> na
+		$parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',
+					 array('fn' => '_replace_name')
+		);
+		// replace: _name -> _0, double-underscore (__name) is ignored
+		$regexp = '/\\b_[A-Za-z\\d]\\w*/';
+		// build the word list
+		$keywords = $this->_analyze($script, $regexp, '_encodePrivate');
+		// quick ref
+		$encoded = $keywords['encoded'];
+
+		$parser->add($regexp,
+			array(
+				'fn' => '_replace_encoded',
+				'data' => $encoded
+			)
+		);
+		return $parser->exec($script);
+	}
+
+	private function _encodeKeywords($script) {
+		// escape high-ascii values already in the script (i.e. in strings)
+		if ($this->_encoding > 62)
+			$script = $this->_escape95($script);
+		// create the parser
+		$parser = new ParseMaster();
+		$encode = $this->_getEncoder($this->_encoding);
+		// for high-ascii, don't encode single character low-ascii
+		$regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/';
+		// build the word list
+		$keywords = $this->_analyze($script, $regexp, $encode);
+		$encoded = $keywords['encoded'];
+
+		// encode
+		$parser->add($regexp,
+			array(
+				'fn' => '_replace_encoded',
+				'data' => $encoded
+			)
+		);
+		if (empty($script)) return $script;
+		else {
+			//$res = $parser->exec($script);
+			//$res = $this->_bootStrap($res, $keywords);
+			//return $res;
+			return $this->_bootStrap($parser->exec($script), $keywords);
+		}
+	}
+
+	private function _analyze($script, $regexp, $encode) {
+		// analyse
+		// retreive all words in the script
+		$all = array();
+		preg_match_all($regexp, $script, $all);
+		$_sorted = array(); // list of words sorted by frequency
+		$_encoded = array(); // dictionary of word->encoding
+		$_protected = array(); // instances of "protected" words
+		$all = $all[0]; // simulate the javascript comportement of global match
+		if (!empty($all)) {
+			$unsorted = array(); // same list, not sorted
+			$protected = array(); // "protected" words (dictionary of word->"word")
+			$value = array(); // dictionary of charCode->encoding (eg. 256->ff)
+			$this->_count = array(); // word->count
+			$i = count($all); $j = 0; //$word = null;
+			// count the occurrences - used for sorting later
+			do {
+				--$i;
+				$word = '$' . $all[$i];
+				if (!isset($this->_count[$word])) {
+					$this->_count[$word] = 0;
+					$unsorted[$j] = $word;
+					// make a dictionary of all of the protected words in this script
+					//  these are words that might be mistaken for encoding
+					//if (is_string($encode) && method_exists($this, $encode))
+					$values[$j] = call_user_func(array(&$this, $encode), $j);
+					$protected['$' . $values[$j]] = $j++;
+				}
+				// increment the word counter
+				$this->_count[$word]++;
+			} while ($i > 0);
+			// prepare to sort the word list, first we must protect
+			//  words that are also used as codes. we assign them a code
+			//  equivalent to the word itself.
+			// e.g. if "do" falls within our encoding range
+			//      then we store keywords["do"] = "do";
+			// this avoids problems when decoding
+			$i = count($unsorted);
+			do {
+				$word = $unsorted[--$i];
+				if (isset($protected[$word]) /*!= null*/) {
+					$_sorted[$protected[$word]] = substr($word, 1);
+					$_protected[$protected[$word]] = true;
+					$this->_count[$word] = 0;
+				}
+			} while ($i);
+
+			// sort the words by frequency
+			// Note: the javascript and php version of sort can be different :
+			// in php manual, usort :
+			// " If two members compare as equal,
+			// their order in the sorted array is undefined."
+			// so the final packed script is different of the Dean's javascript version
+			// but equivalent.
+			// the ECMAscript standard does not guarantee this behaviour,
+			// and thus not all browsers (e.g. Mozilla versions dating back to at
+			// least 2003) respect this.
+			usort($unsorted, array(&$this, '_sortWords'));
+			$j = 0;
+			// because there are "protected" words in the list
+			//  we must add the sorted words around them
+			do {
+				if (!isset($_sorted[$i]))
+					$_sorted[$i] = substr($unsorted[$j++], 1);
+				$_encoded[$_sorted[$i]] = $values[$i];
+			} while (++$i < count($unsorted));
+		}
+		return array(
+			'sorted'  => $_sorted,
+			'encoded' => $_encoded,
+			'protected' => $_protected);
+	}
+
+	private $_count = array();
+	private function _sortWords($match1, $match2) {
+		return $this->_count[$match2] - $this->_count[$match1];
+	}
+
+	// build the boot function used for loading and decoding
+	private function _bootStrap($packed, $keywords) {
+		$ENCODE = $this->_safeRegExp('$encode\\($count\\)');
+
+		// $packed: the packed script
+		$packed = "'" . $this->_escape($packed) . "'";
+
+		// $ascii: base for encoding
+		$ascii = min(count($keywords['sorted']), $this->_encoding);
+		if ($ascii == 0) $ascii = 1;
+
+		// $count: number of words contained in the script
+		$count = count($keywords['sorted']);
+
+		// $keywords: list of words contained in the script
+		foreach ($keywords['protected'] as $i=>$value) {
+			$keywords['sorted'][$i] = '';
+		}
+		// convert from a string to an array
+		ksort($keywords['sorted']);
+		$keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')";
+
+		$encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii);
+		$encode = $this->_getJSFunction($encode);
+		$encode = preg_replace('/_encoding/','$ascii', $encode);
+		$encode = preg_replace('/arguments\\.callee/','$encode', $encode);
+		$inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : '');
+
+		// $decode: code snippet to speed up decoding
+		if ($this->_fastDecode) {
+			// create the decoder
+			$decode = $this->_getJSFunction('_decodeBody');
+			if ($this->_encoding > 62)
+				$decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);
+			// perform the encoding inline for lower ascii values
+			elseif ($ascii < 36)
+				$decode = preg_replace($ENCODE, $inline, $decode);
+			// special case: when $count==0 there are no keywords. I want to keep
+			//  the basic shape of the unpacking funcion so i'll frig the code...
+			if ($count == 0)
+				$decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);
+		}
+
+		// boot function
+		$unpack = $this->_getJSFunction('_unpack');
+		if ($this->_fastDecode) {
+			// insert the decoder
+			$this->buffer = $decode;
+			$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);
+		}
+		$unpack = preg_replace('/"/', "'", $unpack);
+		if ($this->_encoding > 62) { // high-ascii
+			// get rid of the word-boundaries for regexp matches
+			$unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);
+		}
+		if ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) {
+			// insert the encode function
+			$this->buffer = $encode;
+			$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);
+		} else {
+			// perform the encoding inline
+			$unpack = preg_replace($ENCODE, $inline, $unpack);
+		}
+		// pack the boot function too
+		$unpackPacker = new JavaScriptPacker($unpack, 0, false, true);
+		$unpack = $unpackPacker->pack();
+
+		// arguments
+		$params = array($packed, $ascii, $count, $keywords);
+		if ($this->_fastDecode) {
+			$params[] = 0;
+			$params[] = '{}';
+		}
+		$params = implode(',', $params);
+
+		// the whole thing
+		return 'eval(' . $unpack . '(' . $params . "))\n";
+	}
+
+	private $buffer;
+	private function _insertFastDecode($match) {
+		return '{' . $this->buffer . ';';
+	}
+	private function _insertFastEncode($match) {
+		return '{$encode=' . $this->buffer . ';';
+	}
+
+	// mmm.. ..which one do i need ??
+	private function _getEncoder($ascii) {
+		return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ?
+		       '_encode95' : '_encode62' : '_encode36' : '_encode10';
+	}
+
+	// zero encoding
+	// characters: 0123456789
+	private function _encode10($charCode) {
+		return $charCode;
+	}
+
+	// inherent base36 support
+	// characters: 0123456789abcdefghijklmnopqrstuvwxyz
+	private function _encode36($charCode) {
+		return base_convert($charCode, 10, 36);
+	}
+
+	// hitch a ride on base36 and add the upper case alpha characters
+	// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+	private function _encode62($charCode) {
+		$res = '';
+		if ($charCode >= $this->_encoding) {
+			$res = $this->_encode62((int)($charCode / $this->_encoding));
+		}
+		$charCode = $charCode % $this->_encoding;
+
+		if ($charCode > 35)
+			return $res . chr($charCode + 29);
+		else
+			return $res . base_convert($charCode, 10, 36);
+	}
+
+	// use high-ascii values
+	// characters: Â¡Â¢Â£Â€Â¥ÂŠÂ§ÂšÂ©ÂªÂ«Â¬Â­Â®Â¯Â°Â±Â²Â³ÂŽÂµÂ¶Â·ÂžÂ¹ÂºÂ»ÂŒÂœÂŸÂ¿ÃÃÃÃÃÃ
+ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ Ã¡Ã¢Ã£Ã€Ã¥ÃŠÃ§ÃšÃ©ÃªÃ«Ã¬Ã­Ã®Ã¯Ã°Ã±Ã²Ã³ÃŽÃµÃ¶Ã·ÃžÃ¹ÃºÃ»ÃŒÃœÃŸ
+	private function _encode95($charCode) {
+		$res = '';
+		if ($charCode >= $this->_encoding)
+			$res = $this->_encode95($charCode / $this->_encoding);
+
+		return $res . chr(($charCode % $this->_encoding) + 161);
+	}
+
+	private function _safeRegExp($string) {
+		return '/'.preg_replace('/\$/', '\\\$', $string).'/';
+	}
+
+	private function _encodePrivate($charCode) {
+		return "_" . $charCode;
+	}
+
+	// protect characters used by the parser
+	private function _escape($script) {
+		return preg_replace('/([\\\\\'])/', '\\\$1', $script);
+	}
+
+	// protect high-ascii characters already in the script
+	private function _escape95($script) {
+		return preg_replace_callback(
+			'/[\\xa1-\\xff]/',
+			array(&$this, '_escape95Bis'),
+			$script
+		);
+	}
+	private function _escape95Bis($match) {
+		return '\x'.((string)dechex(ord($match)));
+	}
+
+
+	private function _getJSFunction($aName) {
+		if (defined('self::JSFUNCTION'.$aName))
+			return constant('self::JSFUNCTION'.$aName);
+		else
+			return '';
+	}
+
+	// JavaScript Functions used.
+	// Note : In Dean's version, these functions are converted
+	// with 'String(aFunctionName);'.
+	// This internal conversion complete the original code, ex :
+	// 'while (aBool) anAction();' is converted to
+	// 'while (aBool) { anAction(); }'.
+	// The JavaScript functions below are corrected.
+
+	// unpacking function - this is the boot strap function
+	//  data extracted from this packing routine is passed to
+	//  this function when decoded in the target
+	// NOTE ! : without the ';' final.
+	const JSFUNCTION_unpack =
+
+'function($packed, $ascii, $count, $keywords, $encode, $decode) {
+    while ($count--) {
+        if ($keywords[$count]) {
+            $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
+        }
+    }
+    return $packed;
+}';
+/*
+'function($packed, $ascii, $count, $keywords, $encode, $decode) {
+    while ($count--)
+        if ($keywords[$count])
+            $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
+    return $packed;
+}';
+*/
+
+	// code-snippet inserted into the unpacker to speed up decoding
+	const JSFUNCTION_decodeBody =
+//_decode = function() {
+// does the browser support String.replace where the
+//  replacement value is a function?
+
+'    if (!\'\'.replace(/^/, String)) {
+        // decode all the values we need
+        while ($count--) {
+            $decode[$encode($count)] = $keywords[$count] || $encode($count);
+        }
+        // global replacement function
+        $keywords = [function ($encoded) {return $decode[$encoded]}];
+        // generic match
+        $encode = function () {return \'\\\\w+\'};
+        // reset the loop counter -  we are now doing a global replace
+        $count = 1;
+    }
+';
+//};
+/*
+'	if (!\'\'.replace(/^/, String)) {
+        // decode all the values we need
+        while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);
+        // global replacement function
+        $keywords = [function ($encoded) {return $decode[$encoded]}];
+        // generic match
+        $encode = function () {return\'\\\\w+\'};
+        // reset the loop counter -  we are now doing a global replace
+        $count = 1;
+    }';
+*/
+
+	 // zero encoding
+	 // characters: 0123456789
+	 const JSFUNCTION_encode10 =
+'function($charCode) {
+    return $charCode;
+}';//;';
+
+	 // inherent base36 support
+	 // characters: 0123456789abcdefghijklmnopqrstuvwxyz
+	 const JSFUNCTION_encode36 =
+'function($charCode) {
+    return $charCode.toString(36);
+}';//;';
+
+	// hitch a ride on base36 and add the upper case alpha characters
+	// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+	const JSFUNCTION_encode62 =
+'function($charCode) {
+    return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +
+    (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
+}';
+
+	// use high-ascii values
+	// characters: Â¡Â¢Â£Â€Â¥ÂŠÂ§ÂšÂ©ÂªÂ«Â¬Â­Â®Â¯Â°Â±Â²Â³ÂŽÂµÂ¶Â·ÂžÂ¹ÂºÂ»ÂŒÂœÂŸÂ¿ÃÃÃÃÃÃ
+ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ Ã¡Ã¢Ã£Ã€Ã¥ÃŠÃ§ÃšÃ©ÃªÃ«Ã¬Ã­Ã®Ã¯Ã°Ã±Ã²Ã³ÃŽÃµÃ¶Ã·ÃžÃ¹ÃºÃ»ÃŒÃœÃŸ
+	const JSFUNCTION_encode95 =
+'function($charCode) {
+    return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +
+        String.fromCharCode($charCode % _encoding + 161);
+}';
+
+}
+
+
+class ParseMaster {
+	public $ignoreCase = false;
+	public $escapeChar = '';
+
+	// constants
+	const EXPRESSION = 0;
+	const REPLACEMENT = 1;
+	const LENGTH = 2;
+
+	// used to determine nesting levels
+	private $GROUPS = '/\\(/';//g
+	private $SUB_REPLACE = '/\\$\\d/';
+	private $INDEXED = '/^\\$\\d+$/';
+	private $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';
+	private $ESCAPE = '/\\\./';//g
+	private $QUOTE = '/\'/';
+	private $DELETED = '/\\x01[^\\x01]*\\x01/';//g
+
+	public function add($expression, $replacement = '') {
+		// count the number of sub-expressions
+		//  - add one because each pattern is itself a sub-expression
+		$length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string)$expression), $out);
+
+		// treat only strings $replacement
+		if (is_string($replacement)) {
+			// does the pattern deal with sub-expressions?
+			if (preg_match($this->SUB_REPLACE, $replacement)) {
+				// a simple lookup? (e.g. "$2")
+				if (preg_match($this->INDEXED, $replacement)) {
+					// store the index (used for fast retrieval of matched strings)
+					$replacement = (int)(substr($replacement, 1)) - 1;
+				} else { // a complicated lookup (e.g. "Hello $2 $1")
+					// build a function to do the lookup
+					$quote = preg_match($this->QUOTE, $this->_internalEscape($replacement))
+					         ? '"' : "'";
+					$replacement = array(
+						'fn' => '_backReferences',
+						'data' => array(
+							'replacement' => $replacement,
+							'length' => $length,
+							'quote' => $quote
+						)
+					);
+				}
+			}
+		}
+		// pass the modified arguments
+		if (!empty($expression)) $this->_add($expression, $replacement, $length);
+		else $this->_add('/^$/', $replacement, $length);
+	}
+
+	public function exec($string) {
+		// execute the global replacement
+		$this->_escaped = array();
+
+		// simulate the _patterns.toSTring of Dean
+		$regexp = '/';
+		foreach ($this->_patterns as $reg) {
+			$regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';
+		}
+		$regexp = substr($regexp, 0, -1) . '/';
+		$regexp .= ($this->ignoreCase) ? 'i' : '';
+
+		$string = $this->_escape($string, $this->escapeChar);
+		$string = preg_replace_callback(
+			$regexp,
+			array(
+				&$this,
+				'_replacement'
+			),
+			$string
+		);
+		$string = $this->_unescape($string, $this->escapeChar);
+
+		return preg_replace($this->DELETED, '', $string);
+	}
+
+	public function reset() {
+		// clear the patterns collection so that this object may be re-used
+		$this->_patterns = array();
+	}
+
+	// private
+	private $_escaped = array();  // escaped characters
+	private $_patterns = array(); // patterns stored by index
+
+	// create and add a new pattern to the patterns collection
+	private function _add() {
+		$arguments = func_get_args();
+		$this->_patterns[] = $arguments;
+	}
+
+	// this is the global replace function (it's quite complicated)
+	private function _replacement($arguments) {
+		if (empty($arguments)) return '';
+
+		$i = 1; $j = 0;
+		// loop through the patterns
+		while (isset($this->_patterns[$j])) {
+			$pattern = $this->_patterns[$j++];
+			// do we have a result?
+			if (isset($arguments[$i]) && ($arguments[$i] != '')) {
+				$replacement = $pattern[self::REPLACEMENT];
+
+				if (is_array($replacement) && isset($replacement['fn'])) {
+
+					if (isset($replacement['data'])) $this->buffer = $replacement['data'];
+					return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);
+
+				} elseif (is_int($replacement)) {
+					return $arguments[$replacement + $i];
+
+				}
+				$delete = ($this->escapeChar == '' ||
+				           strpos($arguments[$i], $this->escapeChar) === false)
+				        ? '' : "\x01" . $arguments[$i] . "\x01";
+				return $delete . $replacement;
+
+			// skip over references to sub-expressions
+			} else {
+				$i += $pattern[self::LENGTH];
+			}
+		}
+	}
+
+	private function _backReferences($match, $offset) {
+		$replacement = $this->buffer['replacement'];
+		$quote = $this->buffer['quote'];
+		$i = $this->buffer['length'];
+		while ($i) {
+			$replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement);
+		}
+		return $replacement;
+	}
+
+	private function _replace_name($match, $offset){
+		$length = strlen($match[$offset + 2]);
+		$start = $length - max($length - strlen($match[$offset + 3]), 0);
+		return substr($match[$offset + 1], $start, $length) . $match[$offset + 4];
+	}
+
+	private function _replace_encoded($match, $offset) {
+		return $this->buffer[$match[$offset]];
+	}
+
+
+	// php : we cannot pass additional data to preg_replace_callback,
+	// and we cannot use &$this in create_function, so let's go to lower level
+	private $buffer;
+
+	// encode escaped characters
+	private function _escape($string, $escapeChar) {
+		if ($escapeChar) {
+			$this->buffer = $escapeChar;
+			return preg_replace_callback(
+				'/\\' . $escapeChar . '(.)' .'/',
+				array(&$this, '_escapeBis'),
+				$string
+			);
+
+		} else {
+			return $string;
+		}
+	}
+	private function _escapeBis($match) {
+		$this->_escaped[] = $match[1];
+		return $this->buffer;
+	}
+
+	// decode escaped characters
+	private function _unescape($string, $escapeChar) {
+		if ($escapeChar) {
+			$regexp = '/'.'\\'.$escapeChar.'/';
+			$this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0);
+			return preg_replace_callback
+			(
+				$regexp,
+				array(&$this, '_unescapeBis'),
+				$string
+			);
+
+		} else {
+			return $string;
+		}
+	}
+	private function _unescapeBis() {
+		if (!empty($this->_escaped[$this->buffer['i']])) {
+			 $temp = $this->_escaped[$this->buffer['i']];
+		} else {
+			$temp = '';
+		}
+		$this->buffer['i']++;
+		return $this->buffer['escapeChar'] . $temp;
+	}
+
+	private function _internalEscape($string) {
+		return preg_replace($this->ESCAPE, '', $string);
+	}
+}
+
+?>
Index: /trunk/admin/js/voip/functions.js
===================================================================
--- /trunk/admin/js/voip/functions.js	(revision 357)
+++ /trunk/admin/js/voip/functions.js	(revision 357)
@@ -0,0 +1,114 @@
+(function()
+{
+	var _conn = '';
+	var _xtools = '';
+
+	function addGroup()
+	{
+		var select_ldap = document.getElementById('groups_ldap');
+		for(var i = 0; i < select_ldap.options.length ; i++)
+			if( select_ldap.options[i].selected )
+			{
+				var select_voip =  document.getElementById('groups_voip');
+				var flag = false;
+				for(var j = 0; j < select_voip.options.length ; j++ )
+				{
+					if( select_voip.options[j].value === select_ldap.options[i].value )
+						flag = true;
+				}
+				if ( !flag )
+					select_voip.options[select_voip.length] = new Option(select_ldap.options[i].value, select_ldap.options[i].value, false, true);
+			}
+	}
+	
+	function createObject()
+	{
+		if ( typeof(_conn) != "object")
+			_conn = new ADMConnector(path_adm + 'admin' );	
+
+		if ( typeof(_xtools) != "object" )
+			_xtools = new ADMXTools(path_adm + 'admin');
+	}
+
+	function CompleteSelect(data)
+	{
+		var select_ldap = document.getElementById('groups_ldap');
+		data = _xtools.convert(data);
+
+		while( select_ldap.hasChildNodes())
+			select_ldap.removeChild(select_ldap.firstChild);
+		
+		try
+		{
+			if ( data && data.documentElement && data.documentElement.hasChildNodes() )
+			{
+				data = data.documentElement.firstChild;
+				
+				while(data)
+				{
+					select_ldap.options[select_ldap.options.length] = new Option(data.firstChild.nodeValue, data.firstChild.nodeValue, false, false); 
+					data = data.nextSibling;
+				}
+			}
+		}catch(e){}
+
+		styleVisible('hidden');
+	}
+	
+	function SearchOu()
+	{
+		createObject();
+		var organization = "";
+		
+		if( arguments.length > 0 )
+		{
+			var element = arguments[0];
+			styleVisible('visible');
+		}
+
+		if( element.options.length > 0 )
+			for(var i = 0; i < element.options.length ; i++ )
+				if( element.options[i].selected )
+					organization =  element.options[i].value;
+
+		organization = 'ou=' + organization;
+
+		_conn.go('$this.bovoip.getGroupsLdap', CompleteSelect, organization);
+	}
+	
+	function Selected()
+	{
+		var select_voip = document.getElementById('groups_voip');
+		for(var i = 0 ; i < select_voip.options.length; i++ )
+			select_voip.options[i].selected = true;
+	}
+
+	function styleVisible(pVisible)
+	{
+		document.getElementById('admin_span_loading').style.visibility = pVisible;
+	}
+	
+	function removeGroup()
+	{
+		var select_voip = document.getElementById('groups_voip');
+		
+		for(var i = 0 ; i < select_voip.options.length; i++ )
+			if( select_voip.options[i].selected )
+			{
+				select_voip.options[i].parentNode.removeChild(select_voip.options[i]);
+				i--;
+			}
+	}
+
+	function Voip()
+	{
+	
+	}
+
+	Voip.prototype.search	= SearchOu;
+	Voip.prototype.add		= addGroup;
+	Voip.prototype.remove	= removeGroup;
+	Voip.prototype.select_	= Selected;
+	window.voip = new Voip;
+
+})();
Index: /trunk/admin/js/voip/xtools.js
===================================================================
--- /trunk/admin/js/voip/xtools.js	(revision 357)
+++ /trunk/admin/js/voip/xtools.js	(revision 357)
@@ -0,0 +1,151 @@
+(function()
+{
+	var _FILES = [];
+
+	function _convert(pString)
+	{
+		if ( typeof pString != 'string' )
+			return false;
+
+		if ( window.ActiveXObject )
+		{
+			var _xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+			_xmlDoc.async = "false";
+			_xmlDoc.loadXML(pString);
+		}
+		else
+		{
+			var parser = new DOMParser();
+			var _xmlDoc = parser.parseFromString(pString, "text/xml");
+		}
+
+		return _xmlDoc;
+	}
+
+	function _load(pFile)
+	{
+		pFile = this._PATH + 'xsl/' + pFile;
+		if ( !(_FILES[pFile]) )
+		{
+			var _data = null;
+			if ( document.implementation.createDocument )
+				_data = document.implementation.createDocument("", "", null);
+			else
+				_data = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
+
+			_data.async = false;
+			_data.load(pFile + '?' + Date.parse(new Date));
+			_FILES[pFile] = _data;
+		}
+		return _FILES[pFile];
+	}
+
+	function _parse()
+	{
+		if ( arguments.length == 1 )
+		{
+			pXML = _xml('root');
+			pXSL = arguments[0];
+		}
+		else
+		{
+			pXML = arguments[0];
+			pXSL = arguments[1];
+		}
+		switch ( typeof pXML )
+		{
+			case 'object' :
+			break;
+			case 'string' :
+				if ( pXML.indexOf('<') == 0 )
+					pXML = _convert(pXML);
+				else
+					pXML = _load.call(this, pXML);
+			break;
+			default :
+				return {'error':'invalid xml'}
+		}
+		switch ( typeof pXSL )
+		{
+			case 'object' :
+			break;
+			case 'string' :
+				pXSL = _load.call(this, pXSL);
+			break;
+			default :
+				return {'error':'invalid xsl'}
+		}
+
+		var fragment = null;
+		if ( window.XSLTProcessor )
+		{
+			var xslProc = new XSLTProcessor();
+			xslProc.importStylesheet(pXSL);
+
+			if ( (arguments.length == 3) && (typeof arguments[2] == 'object') )
+			{
+				var params = arguments[2];
+				for (var i in params )
+					 xslProc.setParameter(null, i, params[i]);
+			}
+
+			fragment = xslProc.transformToFragment(pXML, document);
+
+			// para retornar valor igual ao ie.
+			var aux = document.createElement("div");
+			aux.appendChild(fragment);
+			fragment = aux.innerHTML;
+		}
+		else
+		{
+			var xslTemplate = new ActiveXObject("MSXML2.XSLTemplate");
+			xslTemplate.stylesheet = pXSL;
+
+			var xslProc = xslTemplate.createProcessor();
+			xslProc.input = pXML;
+
+			if ( (arguments.length == 3) && (typeof arguments[2] == 'object') )
+			{
+				var params = arguments[2];
+				for (var i in params )
+					 xslProc.addParameter(i, params[i], '');
+			}
+
+			xslProc.transform();
+			fragment = xslProc.output;
+		}
+		return fragment;
+	}
+
+	function _xml()
+	{
+		var a = false;
+		if ( document.implementation.createDocument )
+			a = document.implementation.createDocument("", "", null);
+		else if ( ActiveXObject )
+			a = new ActiveXObject("Msxml2.DOMDocument");
+
+		if ( arguments.length == 1 && typeof arguments[0] == 'string' )
+			a.appendChild(a.createElement(arguments[0]));
+		//with ( a )
+		//	appendChild(createProcessingInstruction("xml", "version='1.0'"));
+
+		return a;
+	}
+
+	function XTools()
+	{
+		var _argv = arguments;
+		this._PATH = ( _argv.length > 0 ) ?
+			_argv[0] : '';
+		if ( this._PATH != '' && this._PATH.lastIndexOf('/') != (this._PATH.length - 1) )
+			this._PATH += '/';
+	}
+
+	XTools.prototype.convert = _convert;
+	XTools.prototype.load = _load;
+	XTools.prototype.parse = _parse;
+	XTools.prototype.xml = _xml;
+	window.ADMXTools = XTools;
+}
+)();
Index: /trunk/admin/js/voip/connector.js
===================================================================
--- /trunk/admin/js/voip/connector.js	(revision 357)
+++ /trunk/admin/js/voip/connector.js	(revision 357)
@@ -0,0 +1,295 @@
+(function()
+{
+	var _THREADS = [];
+	var _ie;
+
+	function _config(pObj, pEvent, pHandler)
+	{
+		if ( typeof pObj == 'object' )
+		{
+			if ( pEvent.substring(0, 2) == 'on' )
+				pEvent = pEvent.substring(2, pEvent.length);
+
+			if ( pObj.addEventListener )
+				pObj.addEventListener(pEvent, pHandler, false);
+			else if ( pObj.attachEvent )
+				pObj.attachEvent('on' + pEvent, pHandler);
+		}
+	}
+
+	// xhr = XMLHttpRequest
+	function _xhr()
+	{
+		try
+		{
+			return new XMLHttpRequest();
+		}
+		catch (_e)
+		{
+			_ie = true;
+			try
+			{
+				return new ActiveXObject('Msxml2.XMLHTTP');
+			}
+			catch (_e1)
+			{
+				try
+				{
+					return new ActiveXObject('Microsoft.XMLHTTP');
+				}
+				catch (_e2)
+				{
+					return false;
+				}
+			}
+		}
+	}
+
+	function _HANDLER()
+	{
+		var _ID = arguments[0];
+
+		if  ( _THREADS[_ID] )
+		{
+			if ( _ie && _THREADS[_ID]._XHR.readyState != 4 )
+				return false;
+
+			switch ( _THREADS[_ID]._XHR.readyState )
+			{
+				case 3 :
+					if ( _THREADS[_ID]._HANDLER.stream )
+					{
+						var _data = _THREADS[_ID]._XHR.responseText.substr(_THREADS[_ID]._index).replace(/^ +| +$/g, '');
+						//alert(_data);
+						_THREADS[_ID]._rtlen = _THREADS[_ID]._XHR.responseText.length;
+
+						if ( _THREADS[_ID]._index < _THREADS[_ID]._rtlen && _data.length )
+							try
+							{
+								_THREADS[_ID]._HANDLER.stream(_data);
+							}
+							catch(_e)
+							{
+								//alert("#stream\n\n" + _e + "\n\n" + _e.description);
+							}
+
+						if ( _THREADS[_ID] )
+							_THREADS[_ID]._index = _THREADS[_ID]._rtlen;
+					}
+				break;
+				case 4 :
+					try
+					{
+						switch ( _THREADS[_ID]._XHR.status )
+						{
+							case 200:
+								var _data = ( _THREADS[_ID]._MODE == 'XML' ) ?
+									_THREADS[_ID]._XHR.responseXML :
+									_THREADS[_ID]._XHR.responseText;
+
+								if ( _ie && _THREADS[_ID]._HANDLER.stream )
+									_THREADS[_ID]._HANDLER.stream(_data);
+
+								var _request = ( _THREADS[_ID]._HANDLER.request ) ?
+									_THREADS[_ID]._HANDLER.request : false;
+
+								delete _THREADS[_ID];
+
+								if ( _request )
+									try
+									{
+										_request(_data);
+									}
+									catch(_e)
+									{
+										//alert("#request\n\n" + _e + "\n\n" + _e.description);
+									}
+
+							break; // [case : status 200]
+							case 404:
+								delete _THREADS[_ID];
+								alert('Page Not Found!');
+							break; // [case : status 404]
+							default:
+								delete _THREADS[_ID];
+						}
+					}
+					catch(e)
+					{
+					}
+				break;
+				default :
+			}
+		}
+	}
+
+	function _execute()
+	{
+		var _ID = arguments[0];
+		var _ACTION = 'act=' + _ID;
+		var _TARGET = this._PATH;
+		var _SEND = null;
+
+		if ( _TARGET != '' && _TARGET.lastIndexOf('/') != (_TARGET.length - 1) )
+			_TARGET += '/';
+
+		_TARGET += ( this._CONTROLLER ) ?
+			this._CONTROLLER  : 'controller.php';
+
+		if ( _THREADS[_ID]._METHOD == 'GET' )
+			_TARGET += '?' + _ACTION;
+
+		_THREADS[_ID]._XHR.open(_THREADS[_ID]._METHOD, _TARGET, true);
+
+		if ( _THREADS[_ID]._METHOD == 'POST' )
+		{
+			_THREADS[_ID]._XHR.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
+			_THREADS[_ID]._XHR.setRequestHeader('Cache-Control',  'no-store, no-cache, must-revalidate');
+			_THREADS[_ID]._XHR.setRequestHeader('Cache-Control', 'post-check=0, pre-check=0');
+			_THREADS[_ID]._XHR.setRequestHeader('Pragma', 'no-cache');
+			_SEND = _ACTION + '&' + arguments[1];
+		}
+
+		_THREADS[_ID]._XHR.onreadystatechange = function(){_HANDLER(_ID);};
+		_THREADS[_ID]._XHR.send(_SEND);
+	}
+
+	function usage()
+	{
+		return ""+
+			"Description:\n"+
+			"\t<obj>.go(string access, [mixed handler[, mixed post]])\n\n"+
+			"Parameters:\n"+
+			"\taccess : assinatura de acesso Ã  camada de controle.\n"+
+			"\thandler : uma funÃ§Ã£o a ser executada no fim da requisiÃ§Ã£o\n"+
+			"\t\tou um objeto que poderÃ¡ conter dois Ã­ndices sendo\n"+
+			"\t\tque ambos deverÃ£o ser uma funÃ§Ã£o que serÃ¡ executada\n"+
+			"\t\tconforme o status do objeto xhr, sendo que na posiÃ§Ã£o\n"+
+			"\t\t'stream' serÃ¡ a funÃ§Ã£o a ser executada a cada iteraÃ§Ã£o\n"+
+			"\t\tdo objeto xhr e na posiÃ§Ã£o 'request' serÃ¡ a funÃ§Ã£o\n"+
+			"\t\ta ser executada no fim da requisiÃ§Ã£o.\n"+
+			"\tpost : se especificado deverÃ¡ ser uma query string ou um\n"+
+			"\tXML bem formatado.\n\n";
+	}
+
+	// @PARAM arguments[0] string :
+	//		assinatura de acesso Ã  camada de controle
+	//
+	// @PARAM arguments[1] object :
+	//		OBS : neste caso a conexÃ£o assumirÃ¡ que se trata de uma stream
+	//		objeto contendo dois duas funÃ§Ãµes, sendo,
+	//		no Ã­ndice stream deverÃ¡ conter uma funÃ§Ã£o que serÃ¡ execultada
+	//		a cada mudanÃ§a de status do objeto xhr
+	//	or
+	// @PARAM arguments[1] function : funcÃ£o a ser executada no retorno da
+	//		requisiÃ§Ã£o
+	//		OBS : neste caso a conexÃ£o assumirÃ¡ que se trata de uma
+	//		requisiÃ§Ã£o funÃ§Ã£o que serÃ¡ execultada no final da requisiÃ§Ã£o
+	//
+	// @PARAM arguments[2] string :
+	//		este parÃ¢metro define se a conexÃ£o Ã© via GET ou POST
+	//		caso o parÃ¢metro nÃ£o esteja presente a conexÃ£o serÃ¡ execultada
+	//		via GET, por outro lado, caso ele exista deverÃ¡ ser uma query
+	//		string vÃ¡lida ou um xml bem formatado
+	//
+	function go()
+	{
+		var _argv = arguments;
+		var _argc = _argv.length;
+		var _ID = _argv[0];
+		var _POST;
+		if ( _argc < 1 || _argc > 3 )
+			return {'error' : "#0\n\n" + usage()};
+
+		if ( typeof _ID != 'string' )
+			return {'error' : "#1\n\n" + usage()};
+
+		//if ( _THREADS[_ID] )
+		//	return {'error' : '#2 - there is a equal request running'};
+
+		_THREADS[_ID] = {
+			'_HANDLER'	: {},
+			'_METHOD'	: ( _argv[2] ) ? 'POST' : 'GET',  // [GET | POST]
+			'_MODE'		: null,	// [XML | TEXT]
+			'_TYPE'		: null,	// [4 for request | 3 for stream]
+			'_XHR'		: null	// [4 for request | 3 for stream]
+		};
+
+		if ( _argv[2] )
+			_POST = _argv[2];
+
+		if ( _argv[1] )
+			switch ( typeof _argv[1] )
+			{
+				case 'function' :
+					_THREADS[_ID]._HANDLER = {'request' : _argv[1]};
+				break;
+				case 'object' :
+					for ( var i in _argv[1] )
+						if ( i != 'stream' && i != 'request' )
+						{
+							delete _THREADS[_ID];
+							return {'error' : "#3\n\n" + usage()};
+						}
+						else if ( i == 'stream' )
+						{
+							_THREADS[_ID]._index = 0;
+							_THREADS[_ID]._rtlen = 0;
+						}
+						_THREADS[_ID]._HANDLER = _argv[1];
+				break;
+				case 'string' :
+					if ( _argc == 2 )
+					{
+						_THREADS[_ID]._METHOD = 'POST';
+						_POST = _argv[1];
+					}
+				break;
+				default :
+					//delete _THREADS[_ID];
+					//return {'error' : "#4\n\n" + usage()};
+			}
+
+		if ( !(_THREADS[_ID]._XHR = _xhr()) )
+			return {'error' : "#4 it cannot make a xhr object"};
+
+		( _THREADS[_ID]._METHOD == 'GET' ) ?
+			_execute.call(this, _ID) : _execute.call(this, _ID, _POST);
+		return {'success' : "your thread is running and the response "+
+							"will be manipulated by the handler"};
+	}
+
+	function _abort()
+	{
+		for ( var _ID in _THREADS )
+		{
+			// @TODO
+			// try/catch for unknown error of IE.
+			// Check, store and retrieve the try/catch.
+			try
+			{
+				if ( _THREADS[_ID] && _THREADS[_ID]._XHR && _THREADS[_ID]._XHR.abort )
+					_THREADS[_ID]._XHR.abort();
+
+				delete _THREADS[_ID];
+			}
+			catch(e){}
+		}
+	}
+
+	function Connector()
+	{
+		var _argv = arguments;
+		this._PATH = ( _argv.length > 0 ) ?
+			_argv[0] : '';
+		this._CONTROLLER = ( _argv.length == 2 ) ?
+			_argv[1] : false;
+	}
+
+	Connector.prototype.go = go;
+	Connector.prototype.abort = _abort;
+	window.ADMConnector = Connector;
+
+	_config(window, 'onbeforeunload', _abort);
+}
+)();
Index: /trunk/admin/templates/default/voip.tpl
===================================================================
--- /trunk/admin/templates/default/voip.tpl	(revision 357)
+++ /trunk/admin/templates/default/voip.tpl	(revision 357)
@@ -0,0 +1,66 @@
+<!-- BEGIN voip_page -->
+<form method="POST" action="{action_url}">
+<table align="center" width="60%" cellspacing="2" style="border: 1px solid #000000;">
+	<tr class="th">
+		<td colspan="2">&nbsp;<b>{lang_VoIP_settings}</b></td>
+	</tr>
+
+	<tr class="row_off">
+		<td>{lang_Enter_your_VoIP_server_address}:</td>
+		<td><input name="voip_server" value="{value_voip_server}"  size="40"></td>
+	</tr>   
+	<tr class="row_on">
+		<td>{lang_Enter_your_VoIP_server_url} (Ex.: /telefoniaip/servicos/voip.php):</td>
+		<td><input name="voip_url" value="{value_voip_url}"  size="40"></td>
+	</tr>   
+	<tr class="row_off">
+		<td>{lang_Enter_your_VoIP_server_port}:</td>
+		<td><input name="voip_port" value="{value_voip_port}"></td>
+	</tr>
+	<tr class="th">
+		<td colspan="2">&nbsp;<b>{lang_groups_ldap}</b></td>	
+	</tr>
+	<tr class="row_on">
+		<td colspan="2">
+			{lang_organizations} :
+			&nbsp;
+			<select id="admin_organizations_ldap" name="organizations" onchange="javascript:voip.search(this);">
+				{ous_ldap}
+			</select>
+			<span id="admin_span_loading" style="color:red;visibility:hidden;">&nbsp;{lang_load}</span>
+		</td>
+	</tr>
+	<tr class="row_off">
+		<td colspan="2">
+		<table align="center" cellspacing="0">
+			<tr>
+				<td class="row_off">	
+					{lang_grupos_ldap} :
+					<br/>
+					<select id="groups_ldap" size="10" style="width: 300px" multiple></select>
+				</td>
+				<td class="row_off">
+					<input type="button" value="Adicionar" onclick="javascript:voip.add();" />
+					<br/>
+					<br/>
+					<input type="button" value="Remover" onclick="javascript:voip.remove();" />
+				</td>
+				<td class="row_off">
+					{lang_grupos_liberados} :
+					<br/>
+					<select id="groups_voip" size="10" style="width: 300px" multiple name="voip_groups[]">{groups_voip}</select>
+				</td>
+			</tr>
+		</table>
+		</td>
+	</tr>
+	<tr>
+		<td colspan="2" align="center">
+		  <input type="submit" name="save" value="{lang_save}" onclick="javascript:voip.select_();">
+		  <input type="submit" name="cancel" value="{lang_cancel}">
+		  <br>
+		</td>
+	</tr>
+</table>
+</form>
+<!-- END voip_page -->
Index: /trunk/admin/templates/celepar/config.tpl
===================================================================
--- /trunk/admin/templates/celepar/config.tpl	(revision 64)
+++ /trunk/admin/templates/celepar/config.tpl	(revision 357)
@@ -128,5 +128,5 @@
    </tr>
 
-   <tr class="th">
+   <!--tr class="th">
     <td colspan="2">&nbsp;<b>{lang_VoIP_settings}</b></td>
    </tr>
@@ -143,13 +143,13 @@
     <td>{lang_Enter_your_VoIP_server_port}:</td>
     <td><input name="newsettings[voip_port]" value="{value_voip_port}"></td>
-   </tr>
+   </tr-->
 <!-- END body -->
 
 <!-- BEGIN footer -->
-  <tr class="th">
+  <!--tr class="th">
     <td colspan="2">
 &nbsp;
     </td>
-  </tr>
+  </tr-->
   <tr>
     <td colspan="2" align="center">
