diff --git a/.htaccess b/.htaccess index 622a726..46358a5 100644 --- a/.htaccess +++ b/.htaccess @@ -8,53 +8,47 @@ RewriteEngine on #ErrorDocument 404 /databap/index.php?p=error&test=%{REQUEST_URI} #ErrorDocument 500 /databap/index.php?p=error -#reading procedure bytes (by id/phrase) -RewriteRule ^(.*)(p|proc|procedure)\-([0-9]+)$ index.php?p=procedure&proc_id=$3 [L] +#Read procedure (by id) +RewriteRule ^(p|proc|procedure)\-([0-9]+)$ index.php?p=procedure&id=$2 [L] -#Article (by id) -RewriteRule ^(.*)(a|art|article)\-(.+)$ index.php?a=art_redirect&id=$3 [L] +#Add procedure +RewriteRule ^(add_proc|add_procedure|p\-|proc\-?|procedure\-?)$ index.php?p=procedure [L] -#Doc (by id) -RewriteRule ^(.*)(d|doc|documentation)\-(.+)$ index.php?p=doc&doc_id=$3 [L] +#Read doc (by id) +RewriteRule ^(d|doc|documentation)\-([0-9]+)$ index.php?p=doc&id=$2 [L] -#reading code bytes (by id/phrase) -RewriteRule ^(.*)(c|code)\-(.+)$ index.php?p=read_code&code=$3 [L] +#Add doc +RewriteRule ^(d|doc|documentation)$ index.php?p=doc [L] -#search (by phrase) -RewriteRule ^(.*)(recherche|search)\-(.+)$ index.php?p=search&search_words=$3 [L] - -#Add Procedure -RewriteRule ^(.*)(add_proc|add_procedure|p\-|proc|proc\-|procedure|procedure\-)$ index.php?p=procedure [L] +#Read code (by id/phrase) +RewriteRule ^(c|code)\-(.+)$ index.php?p=code&id=$2 [L] #Add Code -RewriteRule ^(.*)(add_code|c\-|code|code\-)$ index.php?p=add_code [L] +RewriteRule ^(c\-?|code\-?)$ index.php?p=code [L] -#Add Doc -RewriteRule ^(.*)doc$ index.php?p=doc [L] +#Read article (by id) +RewriteRule ^(a|art|article)\-([0-9]+)$ index.php?a=art_redirect&id=$2 [L] + +#search (by phrase) +RewriteRule ^(recherche|search|r|s)\-(.+)$ index.php?p=search&search_words=$2 [L] #Liste -RewriteRule ^(.*)liste$ index.php?p=list [L] +RewriteRule ^(liste?)$ index.php?p=list [L] #Profile (by id) -RewriteRule ^(.*)(p|profil)\-(.+)$ index.php?p=profile&profile_user=$3 [L] +RewriteRule ^(u|user|profile?)\-(.+)$ index.php?p=profile&id=$2 [L] #Profile (current user) -RewriteRule ^(.*)(p\-|profil|profil\-)$ index.php?p=profile&profile_user= [L] +RewriteRule ^(u\-?|user\-?|profile?\-?)$ index.php?p=profile&id= [L] #Chat (by message id) -RewriteRule ^(.*)chat\-(.+)$ index.php?p=chat&id=$2 [L] +RewriteRule ^chat\-(.+)$ index.php?p=chat&id=$1 [L] #Chat (current chat) -RewriteRule ^(.*)(chat|chat\-)$ index.php?p=chat [L] +RewriteRule ^(chat|chat\-)$ index.php?p=chat [L] #Options -RewriteRule ^(.*)options$ index.php?p=options [L] - -#RSS Feed (by category) -RewriteRule ^(.*)rss\-(.+)$ index.php?p=rss&cat=$2 [L] - -#RSS Feed (all) -RewriteRule ^(.*)rss$ index.php?p=rss [L] +RewriteRule ^options$ index.php?p=options [L] #Eg #prevent www. @@ -62,7 +56,7 @@ RewriteRule ^(.*)rss$ index.php?p=rss [L] #RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L] #E.g RewriteRule ^(.*)\.html $1.php #reading code bytes (by id) -#RewriteRule ^(.*)code\-([0-9]+)$ index.php?p=read_code&id_code=$2 [L] +#RewriteRule ^(.*)code\-([0-9]+)$ index.php?p=code&id_code=$2 [L] # Jul - deny access to the .git directory RewriteRule \.git - [F,L] diff --git a/classmanagement.php b/classmanagement.php new file mode 100644 index 0000000..99720ef --- /dev/null +++ b/classmanagement.php @@ -0,0 +1,234 @@ +asIncFiles = array(); + + //try to include default files + $this->incFile(self::SETTINGS_FILE); + $this->incClass(self::TOOLBOX_CLASS); + + //Include main class + $this->incClass($sMainClass); + } + + function __destruct() + { + parent::__destruct(); + } + + public function incClass($sClassName) + { + return $this->incFile(self::INC_FOLDER.$sClassName.self::INC_EXT); + } + + public function incFile($sFilePath, $bMandatory=true) + { + $bIncluded = false; + if(!in_array($sFilePath, $this->asIncFiles)) + { + $sMissingFile = 'File "'.$sFilePath.'" missing.'; + if(file_exists($sFilePath)) + { + $bIncluded = require_once($sFilePath); + $this->asIncFiles[] = $sFilePath; + } + elseif($bMandatory) + { + die($sMissingFile.' Stopping process.'); + } + else + { + $this->addError($sMissingFile); + } + } + return $bIncluded; + } +} + +/** + * + * @author franzz + * @version 1.0a + */ +class PhpObject +{ + //Log file name + const LOG_FILENAME = 'log.html'; + + //Message types + const NOTICE_TAB = 'Notice'; + const WARNING_TAB = 'Warning'; + const ERROR_TAB = 'Error'; + const ALL_TAB = 'All'; + + //Extraction mode + const MODE_ARRAY = 0; + const MODE_TEXT = 1; + const MODE_HTML = 2; + const MODE_FILE = 3; + + //Class variables + private $asMessageStack; + private $iExtractMode; + private $sChildClass; + protected $bDebug; + + function __construct($sClass='', $bDebug=false, $iExtractMode=self::MODE_FILE) + { + $this->resetMessageStack(); + $this->iExtractMode = $iExtractMode; + $this->sChildClass = $sClass; + $this->bDebug = $bDebug; + } + + private static function getLogPath() + { + return dirname(__FILE__).'/'.self::LOG_FILENAME; + } + + private function resetMessageStack($sType=self::ALL_TAB) + { + if($sType==self::ALL_TAB) + { + $this->resetMessageStack(self::NOTICE_TAB); + $this->resetMessageStack(self::WARNING_TAB); + $this->resetMessageStack(self::ERROR_TAB); + } + else + { + $this->asMessageStack[$sType] = array(); + } + } + + protected function addNotice($sNotice) + { + $this->addMessage(self::NOTICE_TAB, $sNotice); + } + + protected function addWarning($sWarning) + { + $this->addMessage(self::WARNING_TAB, $sWarning); + } + + protected function addError($sError) + { + $this->addMessage(self::ERROR_TAB, $sError); + } + + private function addMessage($sType, $sMessage) + { + $this->asMessageStack[$sType][] = $sMessage; + } + + protected function getCleanMessageStack($sType=self::ALL_TAB) + { + $asMessages = ($sType==self::ALL_TAB)?$this->asMessageStack:$this->asMessageStack[$sType]; + $this->resetMessageStack($sType); + + return $this->glueMessages($asMessages); + } + + protected function cleanMessageStack() + { + $sErrorStack = $this->getCleanMessageStack($this->bDebug?self::ALL_TAB:self::ERROR_TAB); + if($sErrorStack!='') + { + switch($this->iExtractMode) + { + case self::MODE_TEXT: + echo $sErrorStack; + break; + case self::MODE_HTML: + echo $sErrorStack; + break; + case self::MODE_ARRAY: + break; + case self::MODE_FILE: + @file_put_contents(self::getLogPath(), "\n\n".$this->sChildClass.' - '.date('r')."\n".$sErrorStack, FILE_APPEND); + break; + } + } + } + + protected function getCleanMessageStacks($aoExtsources, $sType=self::ALL_TAB) + { + $aoExtsources[] = $this; + $aoMessages = array(); + foreach($aoExtsources as $oExtSource) + { + $oMessages = $oExtSource->getCleanMessageStack($sType); + if($oMessages!='') + { + $aoMessages[get_class($oExtSource)] = $oMessages; + } + } + return $this->glueMessages($aoMessages); + } + + private function glueMessages($asMessages) + { + switch($this->iExtractMode) + { + case self::MODE_TEXT: + $oMessageStack = self::recursiveImplode("\n", $asMessages); + break; + case self::MODE_HTML: + $oMessageStack = self::recursiveImplode('
', $asMessages); + break; + case self::MODE_ARRAY: + $oMessageStack = $asMessages; + break; + case self::MODE_FILE: + $oMessageStack = self::recursiveImplode("\n", $asMessages); + break; + } + return $oMessageStack; + } + + private static function flattenMessageStack($asTab, $sGlobalKey='') + { + $asFlatTab = array(); + foreach($asTab as $oKey=>$oRow) + { + $sKey = is_numeric($oKey)?$sGlobalKey:$oKey.' - '; + if(is_array($oRow)) + { + $asFlatTab = array_merge($asFlatTab, self::flattenMessageStack($oRow, $sKey)); + } + else + { + $asFlatTab[] = $sKey.$oRow; + } + } + return $asFlatTab; + } + + private static function recursiveImplode($sGlue, $asTab) + { + $asTab = self::flattenMessageStack($asTab); + return implode($sGlue, $asTab); + } + + function __destruct() + { + $this->cleanMessageStack(); + } +} + +?> \ No newline at end of file diff --git a/config.php b/config.php deleted file mode 100644 index 1e1cf3e..0000000 --- a/config.php +++ /dev/null @@ -1,4581 +0,0 @@ - wait 1sec -> if(focus) -> expand {-5 lines, + 5 lines} - * Translations in EN / ES - */ - -/** - * Manage includes - * @author franzz - */ -class ClassManagement extends PhpObject -{ - const INC_FOLDER = 'inc/'; - const INC_EXT = '.php'; - const SETTINGS_FILE = 'settings.php'; - - private $asIncFiles; - - function __construct() - { - parent::__construct(); - $this->asIncFiles = array(); - $this->incFile(self::SETTINGS_FILE); - } - - function __destruct() - { - parent::__destruct(); - } - - public function incClass($sClassName) - { - return $this->incFile(self::INC_FOLDER.$sClassName.self::INC_EXT); - } - - public function incFile($sFilePath, $bMandatory=true) - { - $bIncluded = false; - if(!in_array($sFilePath, $this->asIncFiles)) - { - $sMissingFile = 'File "'.$sFilePath.'" missing.'; - if(file_exists($sFilePath)) - { - $bIncluded = require_once($sFilePath); - $this->asIncFiles[] = $sFilePath; - } - elseif($bMandatory) - { - die($sMissingFile.' Stopping process.'); - } - else - { - $this->addError($sMissingFile); - } - } - return $bIncluded; - } -} - -/** - * Common class for all classes. - * Handles the error log process - * @author franzz - */ -class PhpObject -{ - //Log file name - const LOG_FILENAME = 'log.html'; - - //Message types - const NOTICE_TAB = 'Notice'; - const WARNING_TAB = 'Warning'; - const ERROR_TAB = 'Error'; - const ALL_TAB = 'All'; - - //Extraction mode - const MODE_ARRAY = 0; - const MODE_TEXT = 1; - const MODE_HTML = 2; - const MODE_FILE = 3; - - //Class variables - private $asMessageStack; - private $iExtractMode; - private $bDebug; - - function __construct($iExtractMode=self::MODE_FILE) - { - $this->resetMessageStack(); - $this->iExtractMode = $iExtractMode; - $this->setDebug(false); - } - - protected function setDebug($bSwitch) - { - $this->bDebug = $bSwitch; - } - - private static function getLogPath() - { - return dirname(__FILE__).'/'.self::LOG_FILENAME; - } - - private function resetMessageStack($sType=self::ALL_TAB) - { - if($sType==self::ALL_TAB) - { - $this->resetMessageStack(self::NOTICE_TAB); - $this->resetMessageStack(self::WARNING_TAB); - $this->resetMessageStack(self::ERROR_TAB); - } - else - { - $this->asMessageStack[$sType] = array(); - } - } - - protected function addNotice($sNotice) - { - $this->addTrace(self::NOTICE_TAB, $sNotice); - } - - protected function addWarning($sWarning) - { - $this->addTrace(self::WARNING_TAB, $sWarning); - } - - protected function addError($sError) - { - $this->addTrace(self::ERROR_TAB, $sError); - } - - private function addTrace($sType, $sMessage) - { - $this->asMessageStack[$sType][] = $sMessage; - } - - protected function getCleanMessageStack($sType=self::ALL_TAB) - { - $asMessages = ($sType==self::ALL_TAB)?$this->asMessageStack:$this->asMessageStack[$sType]; - $this->resetMessageStack($sType); - - return $this->glueMessages($asMessages); - } - - protected function getCleanMessageStacks($aoExtsources, $sType=self::ALL_TAB) - { - $aoExtsources[] = $this; - $aoMessages = array(); - foreach($aoExtsources as $oExtSource) - { - $oMessages = $oExtSource->getCleanMessageStack($sType); - if($oMessages!='') - { - $aoMessages[get_class($oExtSource)] = $oMessages; - } - } - return $this->glueMessages($aoMessages); - } - - private function glueMessages($asMessages) - { - switch($this->iExtractMode) - { - case self::MODE_TEXT: - $oMessageStack = self::recursiveImplode("\n", $asMessages); - break; - case self::MODE_HTML: - $oMessageStack = self::recursiveImplode('
', $asMessages); - break; - case self::MODE_ARRAY: - $oMessageStack = $asMessages; - break; - case self::MODE_FILE: - $oMessageStack = self::recursiveImplode("\n", $asMessages); - break; - } - return $oMessageStack; - } - - private static function flattenMessageStack($asTab, $sGlobalKey='') - { - $asFlatTab = array(); - foreach($asTab as $oKey=>$oRow) - { - $sKey = is_numeric($oKey)?$sGlobalKey:$oKey.' - '; - if(is_array($oRow)) - { - $asFlatTab = array_merge($asFlatTab, self::flattenMessageStack($oRow, $sKey)); - } - else - { - $asFlatTab[] = $sKey.$oRow; - } - } - return $asFlatTab; - } - - private static function recursiveImplode($sGlue, $asTab) - { - $asTab = self::flattenMessageStack($asTab); - return implode($sGlue, $asTab); - } - - function __destruct() - { - $sErrorStack = $this->getCleanMessageStack($this->bDebug?self::ALL_TAB:self::ERROR_TAB); - if($sErrorStack!='') - { - switch($this->iExtractMode) - { - case self::MODE_TEXT: - echo $sErrorStack; - break; - case self::MODE_HTML: - echo $sErrorStack; - break; - case self::MODE_ARRAY: - break; - case self::MODE_FILE: - @file_put_contents(self::getLogPath(), "\n\n".date('r')."\n".$sErrorStack, FILE_APPEND); - break; - } - } - } -} - -/** - * Application Core - * @author franzz - */ -class Databap extends PhpObject -{ - //Common Constants - const EXPECTED_PAGE_COOKIE = 'exp_page'; - const MAIN_SEPARATOR = ' '; - const DATE_FORMAT = 'd/m/Y'; - const TIME_FORMAT = 'H:i:s'; - const DATE_TIME_FORMAT = 'd/m/Y H:i:s'; - const DATE_COMPACT_FORMAT = 'YmdHis'; - const DATE_TIME_SQL_FORMAT = 'Y-m-d H:i:s'; - const DATE_SQL_FORMAT = 'Y-m-d'; - const HISTORY_LENGTH = 10; - const STYLE_PATH = 'style.css'; - public static $UPLOAD_IMG_EXTS = array('jpg', 'jpeg', 'gif', 'png'); - public static $UPLOAD_DOC_EXTS = array('jpg', 'jpeg', 'gif', 'png', 'doc', 'ppt', 'pdf', 'xls', 'docx', 'pptx', 'xlsx'); - const ID_SEPARATOR = '_'; - - //Code - const MAX_LIST_LENGTH = 5; - - //Authorizations - const CLEARANCE_MEMBER = 0; - const CLEARANCE_ADMIN = 9; - const EXT_ACCESS = 'external_access'; - const USER_COOKIE_ID = 'id_user'; - const USER_COOKIE_PASS = 'checksum'; - const COOKIE_LIFETIME = 7; - const TOKEN_LENGTH = 32; - - //HTTP Requests response - const DISCONNECTED = '__DISCONNECTED__'; - const ERROR = '__ERROR__'; - const SUCCESS = '__SUCCESS__'; - - //Chat Constants - //TODO Transfer these constants to chat page - const MESSAGE_USER = 'U'; - const MESSAGE_ADD_CODE = 'A'; - const MESSAGE_EDIT_CODE = 'E'; - const MESSAGE_ADD_PROC = 'PA'; - const MESSAGE_EDIT_PROC = 'PE'; - const MESSAGE_ADD_DOC = 'DA'; - const MESSAGE_EDIT_DOC = 'DE'; - const MESSAGE_ACTION = 'M'; - const MESSAGE_PRIVATE = 'P'; - const MESSAGE_IMG = 'I'; - const MESSAGE_9GAG = '9'; - const MESSAGE_NICK = 'N'; - const MESSAGE_STATUS = 'S'; - const MESSAGE_CONN = 'C'; - const MESSAGE_INVITE = 'V'; - const MESSAGE_REBOOT = 'R'; - const MESSAGE_ARTICLE = 'B'; - const DEFAULT_COMPANY_LOGO = 'tux_24.png'; - const DEFAULT_CHAN = 'Principal'; - const ALL_CHAN_ID = 1; - const ALL_CHAN_TEXT = 'A.l.l_C.h.a.n_I.n.c.l.u.d.e.d'; - const KEEP_ALIVE = 600; //seconds - const REBOOT_DELAY = 15; //seconds - const PM_SEP = '___'; - const CHAT_IMG_MAX_WIDTH = 700; - const CHAT_IMG_MAX_HEIGHT = 2000; - const JSON_PREFIX = '[\-JSON-/]'; - - //Options Constants - const LANG_FR = 'FR'; - const OPT_TEXT = 'T'; - const OPT_SELECT = 'S'; - const OPT_NICKNAME = 1; - const OPT_BG = 2; - const OPT_BRIGHT_BG = 3; - const OPT_HOVER = 4; - const OPT_TOKEN = 5; - const OPT_IMAGE_CHAT = 6; - const OPT_STATUS = 7; - - //Search Constants - const PROC_TYPE = 'p'; - const CODE_TYPE = 'c'; - const DOC_TYPE = 'd'; - const ART_TYPE = 'a'; - - //Doc constants - const DOC_FOLDER = 'docs/'; - const DOC_TMP_FOLDER = 'docs/tmp/'; - - //Objects - private $oMySql; - private $oProcedure; - private $oSearchEngine; - private $oClassManagement; - - //Variables - private $iUserId; - private $asUserInfo; - private $sLanguage; - - function __construct($oClassManagement) - { - parent::__construct(); - $this->oClassManagement = $oClassManagement; - - //Browser <> PHP <> MySql synchronization - date_default_timezone_set(Settings::TIMEZONE); - ini_set('default_charset', Settings::TEXT_ENC); - header('Content-Type: text/html; charset='.Settings::TEXT_ENC); - mb_internal_encoding(Settings::TEXT_ENC); - mb_http_output(Settings::TEXT_ENC); - mb_http_input(Settings::TEXT_ENC); - mb_language('uni'); - mb_regex_encoding(Settings::TEXT_ENC); - - //Passing settings down to mySQL - $this->oMySql = new MySqlManager(Settings::DB_SERVER, Settings::DB_LOGIN, Settings::DB_PASS, Settings::DB_NAME, Settings::SQL_ENC); - if($this->oMySql->sDbState == MySqlManager::DB_NO_DATA) $this->install(); - - //Init other variables - $this->oProcedure = new Procedure($this->oMySql); - $this->setUserId(0); - $this->sLanguage = self::LANG_FR; - $this->oSearchEngine = new SearchEngine($this->oMySql); - } - - private function install() - { - //Install DB - $this->oMySql->install(); - - //Options - $sOptionNameCol = MySqlManager::getText(MySqlManager::OPTNAME_TABLE); - $sOptionNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); - $iNicknameOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_NICKNAME, $sOptionNameCol=>'pseudo du chat', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); - $iBgColorOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_BG, $sOptionNameCol=>'couleur de fond', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); - $iBgColorOpt2Id = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_BRIGHT_BG, $sOptionNameCol=>'couleur de fond 2 (claire)', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); - $iHoverColorOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_HOVER, $sOptionNameCol=>'couleur de survol', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); - $iHoverColorOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_TOKEN, $sOptionNameCol=>'token', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); - $iChatImageOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_IMAGE_CHAT, $sOptionNameCol=>'image du chat', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); - $iChatImageOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_STATUS, $sOptionNameCol=>'Mission en cours', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); - - //Insert default and all channels - $this->oMySql->insertRow(MySqlManager::CHAN_TABLE, array('safe_name'=>self::getChanSafeName(self::ALL_CHAN_TEXT), MySqlManager::getText(MySqlManager::CHAN_TABLE)=>self::ALL_CHAN_TEXT)); - $this->oMySql->insertRow(MySqlManager::CHAN_TABLE, array('safe_name'=>self::getChanSafeName(self::DEFAULT_CHAN), MySqlManager::getText(MySqlManager::CHAN_TABLE)=>self::DEFAULT_CHAN)); - - //Install default users : admin and test - $iAdminId = $this->addUser('françois', 'lutran', 'cgi', 'francois@lutran.fr', self::CLEARANCE_ADMIN); - $this->addUser('test', 'test', 'test', 'test@test.com'); - - //Write the SAP blog parser bash script to main folder - @file_put_contents('sap_website_parser.sh', "#!/bin/bash\n\n/usr/bin/php -f index.php a=external_access p=sap_blog auth_token=".$iAdminId.'_'.$this->generateToken($iAdminId)); - } - - private function setUserId($iUserId) - { - $this->iUserId = $iUserId; - $this->asUserInfo = ($iUserId>0)?$this->getUserInfo($iUserId):array(); - } - - public function getUserId() - { - return $this->iUserId; - } - - public function getPage($sPage, $asVars) - { - $oPage = new Mask('index'); - $oPage->setTag('text_enc', Settings::TEXT_ENC); - $oPage->setTag('first_page', $sPage); - $oPage->setTag('index_link', $asVars['serv_name']); - $oPage->setTag('line_break', "Global databap constants\n"); - - //Rss Link - $oPage->setTag('rss_link', $this->getUserOptionValue(self::OPT_TOKEN)); - - //Pass GET variables down to jQuery - $asVars['user_id'] = $this->getUserId(); - $sLastKey = end(array_keys($asVars)); - foreach($asVars as $sName => $sValue) - { - $oPage->addInstance('VARS', array('var_name'=>$sName, 'var_value'=>$sValue, 'coma'=>$sName==$sLastKey?'':', ')); - } - - //Pages - $asMaskPaths = glob(Mask::MASK_FOLDER.'*.html'); - $asMaskNames = array_map('basename', $asMaskPaths, array_fill(1, count($asMaskPaths), '.html')); - $sLastKey = end(array_keys($asMaskNames)); - foreach($asMaskNames as $sKey=>$sMaskName) - { - $oPage->addInstance('PAGES', array('page_name'=>$sMaskName, 'page_value'=>$sMaskName, 'coma'=>$sKey==$sLastKey?'':', ')); - } - - //Constants - $asConstants = array( 'disconnected'=>self::DISCONNECTED, - 'error'=>self::ERROR, - 'keep_alive'=>self::KEEP_ALIVE, - 'opt_type_text'=>self::OPT_TEXT, - 'opt_type_select'=>self::OPT_SELECT, - 'max_size'=>self::getMaxSize(), - 'authorized_img_exts'=>self::$UPLOAD_IMG_EXTS, - 'authorized_file_exts'=>self::$UPLOAD_DOC_EXTS, - 'id_sep'=>self::ID_SEPARATOR, - 'mask_folder'=>Mask::MASK_FOLDER, - 'image_folder'=>Procedure::IMAGE_FOLDER, - 'image_folder_tmp'=>Procedure::IMAGE_FOLDER_TMP, - 'image_folder_thumb'=>Procedure::IMAGE_FOLDER_THUMB, - 'default_chan'=>self::DEFAULT_CHAN, - 'all_chan_id'=>self::ALL_CHAN_ID, - 'all_chan_text'=>self::ALL_CHAN_TEXT, - 'pm_separator'=>self::PM_SEP, - 'reboot_delay'=>self::REBOOT_DELAY); - $oPage->setTag('constants', $this->jsonConvert($asConstants)); - - return $oPage->getMask(); - } - - public function getRss($sCat='') - { - //Class Feed - $this->oClassManagement->incClass('rss'); - - //Building header - $asDesc = array - ( - 'title'=>'Flux RSS Databap'.($sCat==''?'':' - '.$sCat), - 'link'=>$_GET['serv_name'].'rss'.($sCat==''?'':'-'.$sCat), - 'copyright'=>'Powered by Francois Lutran. RSS Feed Generator under GPLv3 License', - 'description'=>'Flux RSS du chat'.($sCat==''?'':', section '.$sCat), - 'language'=>'fr', - 'webmaster_mail'=>'francois@lutran.fr' - ); - $oFeed = new Feed($asDesc); - - //Building items - switch(mb_strtolower($sCat)) - { - case '9gag': - $sRegEx = '^(https?://|www\\.)(.*)9gag\\.com'; - break; - case 'youtube': - $sRegEx = '^(https?://|www\\.)(.*)youtube\\.com'; - break; - case '': - $sRegEx = '^(https?://|www\\.)[\.A-Za-z0-9\-]+\\.[a-zA-Z]{2,4}'; - break; - default: - $sRegEx = '^(https?://|www\\.)(.*)'.$sCat.'(.*)\\.[a-zA-Z]{2,4}'; - break; - } - $asResult = $this->oMySql->selectRows(array('select'=>array('id_message', 'nickname', 'message', 'led'), 'from'=>'messages', 'constraint'=>array('message'=>$sRegEx), 'constOpe'=>array('message'=>' REGEXP '))); - $sPattern = '/(https?\:\/\/|www\.)[\S]+\.[a-zA-Z]{2,4}([\S]*)/ui'; - - foreach($asResult as $iLinkId=>$asRow) - { - //get link out of message - preg_match($sPattern, $asRow['message'], $asMatches); - $asRow['link'] = $asMatches[0]; - - //add item - $asItem = array - ( - 'title'=>'Lien'.($sCat==''?'':' - '.$sCat).' #'.($iLinkId+1), - 'category'=>$sCat, - 'description'=>'Lien posté par '.self::getNickNameFormat($asRow['nickname']).' à '.self::getDateFormat($asRow['led']).' : '.$asRow['message'].'', - 'author'=>$asRow['nickname'], - 'link'=>$_GET['serv_name'].'chat-'.$asRow['id_message'], - 'pub_date'=>$asRow['led'], - 'guid'=>$_GET['serv_name'].'chat-'.$asRow['id_message'] - ); - $oFeed->addItem($asItem); - } - - return $oFeed->getFeed(); - } - - public function syncSapBlog() - { - //Get articles list - $asArticles = $this->getSAPBlogRss(); - - //Update and spread the news - $asResult = array(); - if(date('N')>5) $asResult[] = "Pas de mise-à-jour pendant le week end !"; - else - { - foreach($asArticles as $asArticle) - { - $iArticleId = $this->oMySql->selectValue(MySqlManager::ART_TABLE, MySqlManager::getId(MySqlManager::ART_TABLE), array('title'=>$asArticle['title'])); - - if($iArticleId==0) - { - $iArticleId = $this->oMySql->insertRow(MySqlManager::ART_TABLE, $asArticle); - $this->addMessage($iArticleId, self::MESSAGE_ARTICLE, self::ALL_CHAN_ID); - $this->oSearchEngine->buildIndex($iArticleId, self::ART_TYPE); - $sResult = 'ADDED'; - } - else $sResult = 'OK'; - $asResult[$iArticleId] = $sResult.' | '.implode(' | ', $asArticle); - } - } - return MySqlManager::implodeAll($asResult, '->', "\n"); - } - - private function getSAPBlogRss() - { - $sSAPDomain = 'http://scn.sap.com'; - $sSAPBlogUrl = $sSAPDomain.'/community/data-warehousing/bw/blog'; - - //TODO add http://www.biportal.org/sap_bi_blog - - $oDom = $this->getRemotePageDom($sSAPBlogUrl); - $aoArticles = $oDom->getElementsByTagName('header'); - - $asArticles = array(); - foreach($aoArticles as $oArticle) - { - if($oArticle->getAttribute('class')=='jive-blog-post-subject') - { - $asArticleInfo = array(); - $aoLinks = $oArticle->getElementsByTagName('a'); - foreach($aoLinks as $oLink) - { - switch($oLink->getAttribute('class')) - { - //Title & link - case 'font-color-normal': - $asArticleInfo['title'] = ucfirst(trim($oLink->nodeValue)); - $asArticleInfo['title'] = mb_substr($asArticleInfo['title'], -1)=='.'?mb_substr($asArticleInfo['title'], 0, -1):$asArticleInfo['title']; - $asArticleInfo['link'] = $oLink->getAttribute('href'); - $asArticleInfo['date'] = mb_substr(str_replace(array($sSAPBlogUrl.'/', '/'), array('', '-'), $asArticleInfo['link']), 0, 10); - break; - //Author - case 'jiveTT-hover-user jive-username-link': - $asNames = array_filter(explode(' ', ToolBox::mb_ucwords(trim($oLink->nodeValue)))); - $asArticleInfo['first_name'] = array_shift($asNames); - $asArticleInfo['last_name'] = implode('-', $asNames); - $asArticleInfo['email'] = $sSAPDomain.$oLink->getAttribute('href'); - break; - } - } - $asArticles[] = $asArticleInfo; - } - } - - return $asArticles; - } - - private function get9gagPost($sUrl) - { - $asPost = array('url'=>$sUrl); - $oDom = $this->getRemotePageDom($sUrl); - - $oDivs = $oDom->getElementsByTagName('section'); - foreach($oDivs as $oPost) {if($oPost->getAttribute('id')=='individual-post') break;} - - //Title - $asPost['title'] = $oPost->getElementsByTagName('h2')->item(0)->nodeValue; - - //Image - $oImages = $oPost->getElementsByTagName('img'); - foreach($oImages as $oImage) - { - switch($oImage->getAttribute('class')) - { - case 'badge-item-animated-img': - $o9gagImage = $oImage; - break 2; - case 'badge-item-img': - $o9gagImage = $oImage; - break; - } - } - $asResult = $this->downloadToTmp($o9gagImage->getAttribute('src')); - - if($asResult['error']=='') - { - $asPost['url_img'] = $asResult['out']; - $asPost['width'] = $asResult['width']; - $asPost['height'] = $asResult['height']; - } - - return $asPost; - } - - private function getRemotePageDom($sUrl) - { - $oDom = new DOMDocument(); - @$oDom->loadHTML(file_get_contents($sUrl)); - return $oDom->getElementsByTagName('body')->item(0); - } - - public function addUser($sFirstName, $sLastName, $sCompany, $sEmail='', $iClearance=self::CLEARANCE_MEMBER) - { - $sFirstName = mb_strtolower($sFirstName); - $sLastName = mb_strtolower($sLastName); - $sCompany = mb_strtolower($sCompany); - $sEmail = mb_strtolower($sEmail); - - //Checking company existency in company table - $sCompanyTextCol = MySqlManager::getText(MySqlManager::COMP_TABLE); - $iCompanyId = $this->oMySql->selectInsert(MySqlManager::COMP_TABLE, array($sCompanyTextCol=>mb_strtolower($sCompany), 'logo'=>self::DEFAULT_COMPANY_LOGO), array($sCompanyTextCol)); - - $asInfo = array('first_name'=>$sFirstName, - 'last_name'=>$sLastName, - 'email'=>$sEmail, - MySqlManager::getId(MySqlManager::COMP_TABLE)=>$iCompanyId, - 'clearance'=>$iClearance, - 'led'=>'0000-00-00 00:00:00'); - $iUserId = $this->oMySql->insertRow(MySqlManager::USER_TABLE, $asInfo); - - //Admin Options - $sOptionTable = MySqlManager::OPT_TABLE; - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $sOptNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); - $sOptionTextCol = MySqlManager::getText(MySqlManager::OPT_TABLE); - $this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_NICKNAME, $sOptionTextCol=>$sFirstName)); - //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_BG, $sOptionTextCol=>'#04357B')); - //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_BRIGHT_BG, $sOptionTextCol=>'#D9E5F2')); - //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_HOVER, $sOptionTextCol=>'#EFAB00')); - $this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_TOKEN, $sOptionTextCol=>$this->generateRssLink($iUserId))); - //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_IMAGE_CHAT, $sOptionTextCol=>'images/sap_gold_332.jpg')); - - return $iUserId; - } - - public function resetToken() - { - $asRs = $this->oMySql->selectRows(array('from'=>'`options`', 'constraint'=>array('id_option_name'=>self::OPT_TOKEN))); - foreach($asRs as $asR) - { - $sTokenLink = $this->generateRssLink($asR['id_user']); - $this->oMySql->updateRow('`options`', array('id_option'=>$asR['id_option']), array('`option`'=>$sTokenLink)); - } - return $asRs; - } - - private function generateRssLink($iUserId=0) - { - if($iUserId==0) $iUserId = $this->getUserId(); - return $_GET['serv_name'].'?a='.self::EXT_ACCESS.'&p=rss&auth_token='.$iUserId.'_'.$this->generateToken($iUserId); - } - - private function generateToken($iUserId) - { - $sEncryptedToken = ''; - if($iUserId>0) - { - $asUser = $this->oMySql->selectRow(MySqlManager::USER_TABLE, $iUserId, array('first_name', 'last_name', 'email', MySqlManager::getId(MySqlManager::COMP_TABLE))); - $sEncryptedToken = self::encryptPassword($asUser['first_name'].$asUser['last_name'].$asUser['email'].$asUser[MySqlManager::getId(MySqlManager::COMP_TABLE)]); - } - else - { - $this->addError('generating token : invalid user id "'.$iUserId.'"'); - } - return $sEncryptedToken; - } - - public function buildCompleteIndex() - { - $asSearchTypes = array( self::CODE_TYPE=>MySqlManager::CODE_TABLE, - self::PROC_TYPE=>MySqlManager::PROC_TABLE, - self::ART_TYPE=>MySqlManager::ART_TABLE, - self::DOC_TYPE=>MySqlManager::DOC_TABLE); - - $this->oMySql->emptyTable(MySqlManager::SEARCH_TABLE); - foreach($asSearchTypes as $sSearchType=>$sSearchTable) - { - $asItemIds = $this->oMySql->selectRows(array('select'=>MySqlManager::getId($sSearchTable), 'from'=>$sSearchTable)); - foreach($asItemIds as $iItemId) $this->oSearchEngine->buildIndex($iItemId, $sSearchType); - } - } - - //insert new code / version - public function addCode($asData) - { - $sContent = $asData['content']; - $sDescription = $asData['description']; - $sLink = str_replace(' ', '_', $asData['link']); - - //insert value in db - $asInsert = array( MySqlManager::getText(MySqlManager::CODE_TABLE)=>$sContent, - 'description'=>$sDescription, - 'id_user'=>$this->getUserId()); - $iCodeId = $this->oMySql->insertRow(MySqlManager::CODE_TABLE, $asInsert); - - //insert link if exists - if($sLink!='') - { - $asInsert = array(MySqlManager::getId(MySqlManager::CODE_TABLE)=>$iCodeId, 'phrase'=>$sLink); - $this->oMySql->insertRow(MySqlManager::URL_TABLE, $asInsert); - } - - //refer id update - $this->oMySql->updateRow(MySqlManager::CODE_TABLE, $iCodeId, array('refer_id'=>$iCodeId)); - - //Add message - $this->addMessage($iCodeId, self::MESSAGE_ADD_CODE, self::ALL_CHAN_ID); - - //Add record in Search Table - $this->oSearchEngine->buildIndex($iCodeId, self::CODE_TYPE); - - return $iCodeId; - } - - public function editCode($oCode, $sContent) - { - $asRef = $this->getCodeInfo($oCode, 'first'); - $iVersion = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'COUNT(id_code)', array('refer_id'=>$asRef['id_code']))+1; - $asEdit = array('code'=>$sContent, - 'description'=>$asRef['description'].' (v'.$iVersion.')', - 'id_user'=>$this->iUserId, - 'refer_id'=>$asRef['id_code']); - - $iCodeId = $this->oMySql->insertRow(MySqlManager::CODE_TABLE, $asEdit); - - //Add message - $this->addMessage($iCodeId, self::MESSAGE_EDIT_CODE, self::ALL_CHAN_ID); - - //Add record in Search Table - $this->oSearchEngine->buildIndex($iCodeId, self::CODE_TYPE); - - return $iCodeId; - } - - public function addProcedure($asPost) - { - $asPost['id_user'] = $this->getUserId(); - $this->oProcedure->inputForm($asPost); - $asErrors = $this->oProcedure->checkIntegrity(); - if(count($asErrors)==0) - { - //Previous procedure Id - $iPrevProcId = isset($asPost['procedure_id'])?$asPost['procedure_id']:0; - - //Load procedure into database - $bCreation = $this->oProcedure->saveProcedure($iPrevProcId); - - //Extract extra data - $asProc = $this->oProcedure->getProcedure(); - $iNewProcId = $asProc['proc_id']; - $asUser = $this->getUserInfo($this->getUserId()); - - $oResult = array('result'=>'success', - 'proc_id'=>$iNewProcId, - 'led'=>$asProc['led'], - 'name'=>$asUser['name'], - 'company'=>$asUser['company']); - - //Add Message in chat - if($bCreation) - { - $this->addMessage($iNewProcId, self::MESSAGE_ADD_PROC, self::ALL_CHAN_ID); - } - else - { - $this->addMessage($iNewProcId, self::MESSAGE_EDIT_PROC, self::ALL_CHAN_ID); - } - - //Add record in Search Table - $this->oSearchEngine->buildIndex($iNewProcId, self::PROC_TYPE); - } - else - { - $oResult = array('result'=>'fail', 'errors'=>$asErrors); - } - - return self::jsonExport($oResult); - } - - public function getProcedure($iProcId) - { - $this->oProcedure->loadProcedure($iProcId); - $asProc = $this->oProcedure->getProcedure(); - - //Adding user info - $asUser = $this->getUserInfo($asProc['id_user']); - $asProc['name'] = $asUser['name']; - $asProc['company'] = $asUser['company']; - - return self::jsonExport($asProc); - } - - public function addDoc($asPost) - { - //Previous Doc Id - $iPrevDocId = $asPost['doc_id']; - $bCreation = ($iPrevDocId==0); - - //User - $iUserId = $this->getUserId(); - - //Titles - $sTitle = isset($asPost['title'])?$asPost['title']:''; - $sDescription = isset($asPost['description'])?$asPost['description']:''; - - //Get docs - $sImagePattern = '/c1_(?P\d+)_image_(?P\w+)/u'; - foreach($asPost as $sFormId=>$sValue) - { - preg_match($sImagePattern, $sFormId, $asMatches); - if($asMatches['doc_info'] == 'name' || $asMatches['doc_info'] == 'desc') - { - $asDocs[$asMatches['doc_id']][$asMatches['doc_info']] = $sValue; - } - } - - //Load doc into database - $asData = array('title'=>$sTitle, 'description'=>$sDescription, MySqlManager::getId(MySqlManager::USER_TABLE)=>$iUserId); - $iDbDocId = $this->oMySql->insertRow(MySqlManager::DOC_TABLE, $asData); - - //Load doc files into database - $asDocData[MySqlManager::getId(MySqlManager::DOC_TABLE)] = $iDbDocId; - foreach($asDocs as $asDocInfo) - { - //insert into database - $sFileName = $asDocInfo['name']; - $asDocData['description'] = $asDocInfo['desc']; - $asDocData['file_name'] = $sFileName; - $iDbImageId = $this->oMySql->insertRow(MySqlManager::FILE_TABLE, $asDocData); - - //Move file - $sTempFilePath = self::DOC_TMP_FOLDER.$sFileName; - $sFilePath = self::DOC_FOLDER.$sFileName; - if(!rename($sTempFilePath, $sFilePath)) - { - $this->addError('Unmoveable file : '.$sTempFilePath); - } - } - - //Add this doc to the group - $iReferId = $bCreation?$iDbDocId:$this->oMySql->selectValue(MySqlManager::DOC_TABLE, 'refer_id', $iPrevDocId); - $this->oMySql->updateRow(MySqlManager::DOC_TABLE, $iDbDocId, array('refer_id'=>$iReferId)); - - //Add Message in chat - $this->addMessage($iDbDocId, $bCreation?self::MESSAGE_ADD_DOC:self::MESSAGE_EDIT_DOC, self::ALL_CHAN_ID); - - //Add record in Search Table - $this->oSearchEngine->buildIndex($iDbDocId, self::DOC_TYPE); - - return self::jsonExport(array('result'=>'success','doc_id'=>$iDbDocId)); - } - - public function getDoc($iDocId) - { - //Extract doc data - $asDoc = $this->oMySql->selectRow(MySqlManager::DOC_TABLE, $iDocId); - - //Extract extra data - $asUser = $this->getUserInfo($asDoc['id_user']); - $asDoc['name'] = $asUser['name']; - $asDoc['company'] = $asUser['company']; - - //Extract doc files - $asFiles = $this->oMySql->selectRows(array('from'=>MySqlManager::FILE_TABLE, 'constraint'=>array('id_doc'=>$iDocId))); - foreach($asFiles as $asFile) - { - $asDoc['files'][$asFile[MySqlManager::getId(MySqlManager::FILE_TABLE)]] = array('description'=>$asFile['description'], 'ext'=>pathinfo($asFile['file_name'], PATHINFO_EXTENSION)); - } - - return self::jsonExport($asDoc); - } - - public function getFile($iFileId) - { - $sFileName = $this->oMySql->selectValue(MySqlManager::FILE_TABLE, 'file_name', $iFileId); - $sFilePath = self::DOC_FOLDER.$sFileName; - $sFileFullPath = dirname($_SERVER['SCRIPT_FILENAME'])."/".$sFilePath; - - if(!file_exists($sFilePath)) - { - die(); - } - else - { - //get mime type - if(class_exists('finfo')) - { - $oFileInfo = new finfo(FILEINFO_MIME); - $sMimetype = $oFileInfo->file($sFileFullPath); - } - else - { - $sMimetype = 'application/force-download'; - } - - //set headers - header('Pragma: public'); - header('Expires: 0'); - header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - header('Cache-Control: public'); - header('Content-Description: File Transfer'); - header('Content-Type: '.$sMimetype); - header('Content-Disposition: attachment; filename='.$sFileName); - header('Content-Transfer-Encoding: binary'); - header('Content-Length: '.@filesize($sFilePath)); - - // download - if ($oFile = @fopen($sFilePath, 'rb')) - { - while(!feof($oFile)) - { - print(fread($oFile, 1024*8)); - flush(); - if(connection_status() != 0) - { - @fclose($oFile); - } - } - @fclose($oFile); - } - } - } - - public function uploadImage() - { - $oFileUploader = new fileUploader(Procedure::IMAGE_FOLDER_TMP, self::$UPLOAD_IMG_EXTS); - $asResult = $oFileUploader->handleUpload(); - - return $this->jsonConvert($asResult); - } - - public function uploadDoc() - { - $oFileUploader = new fileUploader(self::DOC_TMP_FOLDER, self::$UPLOAD_DOC_EXTS); - $asResult = $oFileUploader->handleUpload(); - - return $this->jsonConvert($asResult); - } - - private function getCodeInfo($oCode, $sMode='', $bCode=false) - { - $iCodeId = is_numeric($oCode)?$oCode:$this->getIdCodeFromPhrase($oCode); - - //Mode (get last or first id_code) - if($sMode!='') - { - $this->switchCodeId($iCodeId, $sMode); - } - - //Efficient request : don't request for the entire code - if(!$bCode) - { - $asSelect = MySqlManager::getTablecolumns(MySqlManager::CODE_TABLE); - unset($asSelect[MySqlManager::getText(MySqlManager::CODE_TABLE)]); - $asSelect = array_keys($asSelect); - } - else - { - $asSelect = "*"; - } - - $asCode = $this->oMySql->selectRow(MySqlManager::CODE_TABLE, $iCodeId, $asSelect);$this->addError($asCode); - $asCode['description'] = self::getDescriptionFormat($asCode['description']); - $asCode['timestamp'] = strtotime($asCode['led']); - $asCode['led'] = self::getDateFormat($asCode['led']); - return $asCode; - } - - private function getProcInfo($iProcId) - { - $asProc = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $iProcId); - $asProc['title'] = self::getDescriptionFormat($asProc['title']); - $asProc['description'] = self::getDescriptionFormat($asProc['description']); - $asProc['timestamp'] = strtotime($asProc['led']); - $asProc['led'] = self::getDateFormat($asProc['led']); - return $asProc; - } - - private function getDocInfo($iDocId) - { - $asDoc = $this->oMySql->selectRow(MySqlManager::DOC_TABLE, $iDocId); - $asDoc['title'] = self::getDescriptionFormat($asDoc['title']); - $asDoc['description'] = self::getDescriptionFormat($asDoc['description']); - $asDoc['timestamp'] = strtotime($asDoc['led']); - $asDoc['led'] = self::getDateFormat($asDoc['led']); - return $asDoc; - } - - private function getArticleInfo($iArtId) - { - $asArt = $this->oMySql->selectRow(MySqlManager::ART_TABLE, $iArtId); - $asTransferredInfo = array('link_art'=>$asArt['link'], 'art_title'=>$asArt['title'], 'link_auth'=>$asArt['email']); - $asTransferredInfo['art_date'] = self::getDateFormat($asArt['date'], self::DATE_FORMAT); - $asTransferredInfo['name'] = self::getNameFormat($asArt['first_name'], $asArt['last_name']); - $asTransferredInfo['description'] = self::getDescriptionFormat($asArt['title']); - $asTransferredInfo['timestamp'] = strtotime($asArt['led']); - $asTransferredInfo['led'] = self::getDateFormat($asArt['led']); - $asTransferredInfo['company'] = 'SAP'; - return $asTransferredInfo; - } - - public function getUserInfo($iUserId, $bJson=false) - { - if($iUserId==$this->getUserId() && !empty($this->asUserInfo)) - { - $asUserInfo = $this->asUserInfo; - } - else - { - $asRow = $this->oMySql->selectRow(MySqlManager::USER_TABLE, $iUserId); - $asCompany = $this->oMySql->selectRow(MySqlManager::COMP_TABLE, $asRow[MySqlManager::getId(MySqlManager::COMP_TABLE)]); - $asUserInfo = array( 'name'=>self::getNameFormat($asRow['first_name'], $asRow['last_name']), - 'nickname'=>self::getNickNameFormat($this->getChatNickNames($iUserId)), - 'email'=>$asRow['email'], - 'company'=>self::getCompanyFormat($asCompany[MySqlManager::getText(MySqlManager::COMP_TABLE)]), - 'status'=>$this->getDescriptionFormat($this->getUserOptionValue(self::OPT_STATUS, $iUserId)), - 'logo'=>$asCompany['logo'], - 'clearance'=>$asRow['clearance'], - 'user_led'=>self::getDateFormat($asRow['led'], self::DATE_FORMAT)); - } - return $bJson?$this->jsonExport($asUserInfo):$asUserInfo; - } - - public function getUserClearance() - { - $asUserInfo = $this->getUserInfo($this->getUserId()); - return $asUserInfo['clearance']; - } - - public function getOptions() - { - $sOptValueIdCol = MySqlManager::getId(MySqlManager::OPTVAL_TABLE); - $sOptionTextCol = MySqlManager::getText(MySqlManager::OPT_TABLE); - $sOptNameTextCol = MySqlManager::getText(MySqlManager::OPTNAME_TABLE); - $sOptValueTextCol = MySqlManager::getText(MySqlManager::OPTVAL_TABLE); - - //Available Options - $asAvailableOptions = $this->getAvailableOptions(); - - //User options - $asUserOptions = $this->getUserOptions(); - - //Build Options panel - $asSelectedOptions = array(); - foreach($asAvailableOptions as $sOptNameId=>$asOption) - { - $asSelectedOptions[$sOptNameId]['option_name'] = self::getDescriptionFormat($asOption[$sOptNameTextCol]); - $asSelectedOptions[$sOptNameId]['user_value_id'] = array_key_exists($sOptNameId, $asUserOptions)?$asUserOptions[$sOptNameId][$sOptValueIdCol]:0; - $asSelectedOptions[$sOptNameId]['user_value'] = array_key_exists($sOptNameId, $asUserOptions)?$asUserOptions[$sOptNameId][$sOptionTextCol]:''; - $asSelectedOptions[$sOptNameId]['type'] = $asOption['type']; - if($asOption['type']==self::OPT_SELECT) - { - $asOptionValuesInfo = array('select'=>array($sOptValueIdCol, $sOptValueTextCol), - 'from'=>MySqlManager::OPTVAL_TABLE, - 'constraint'=>array('language'=>$this->sLanguage, - MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>$sOptNameId), - 'orderBy'=>array('led'=>'ASC')); - $asSelectedOptions[$sOptNameId]['select'] = $this->oMySql->selectRows($asOptionValuesInfo, true, $sOptValueIdCol); - } - } - return $this->jsonExport($asSelectedOptions); - } - - public function setOptions($asUserOptions, $bSilentUpdate=true) - { - foreach($this->getAvailableOptions() as $sOptNameId=>$asOption) - { - if(array_key_exists($sOptNameId, $asUserOptions)) - { - switch($asOption['type']) - { - case self::OPT_SELECT: //insert id of option value table - $sUpdateCol = MySqlManager::getId(MySqlManager::OPTVAL_TABLE); - break; - case self::OPT_TEXT: //insert value - $sUpdateCol = MySqlManager::getText(MySqlManager::OPT_TABLE); - break; - default: - continue 2; - } - $sNewValue = $asUserOptions[$sOptNameId]; - - //Check identical data - $asKeys = array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->getUserId(), MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>$sOptNameId); - $asData = array($sUpdateCol=>$sNewValue) + $asKeys; - $sOldValue = $this->oMySql->selectValue(MySqlManager::OPT_TABLE, $sUpdateCol, $asKeys); - - //Update value - if($sOldValue!=$sNewValue) - { - //Update value - $this->oMySql->insertUpdateRow(MySqlManager::OPT_TABLE, $asData, array_keys($asKeys)); - - //Spread the word - if(!$bSilentUpdate) - { - //TODO rassembler les messages similaires dans une fonction de template - switch($sOptNameId) - { - case self::OPT_NICKNAME: - $sType = self::MESSAGE_NICK; - $sChanName = self::ALL_CHAN_TEXT; - $sMessage = $sOldValue.' a changé son pseudo en '.$sNewValue; - break; - case self::OPT_STATUS: - $sType = self::MESSAGE_STATUS; - $sChanName = self::ALL_CHAN_TEXT; - $sMessage = 'est sur une nouvelle mission : '.$this->getDescriptionFormat($sNewValue); - break; - default: - continue 2; - } - $sChanId = $this->getChanId($sChanName); - $this->addMessage($sMessage, $sType, $sChanId); - } - } - } - } - } - - private function getAvailableOptions() - { - $sOptNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); - $sOptNameTextCol = MySqlManager::getText(MySqlManager::OPTNAME_TABLE); - $asInfo = array('select'=>array($sOptNameIdCol, $sOptNameTextCol, 'type'), - 'from'=>MySqlManager::OPTNAME_TABLE, - 'constraint'=>array('language'=>$this->sLanguage)); - return $this->oMySql->selectRows($asInfo, true, $sOptNameIdCol); - } - - private function getUserOptionValue($sOptionNameId, $iUserId=0) - { - $asUserOptions = $this->getUserOptions($sOptionNameId, $iUserId); - $asOptionInfo = array_shift($asUserOptions); - return $asOptionInfo['option']; - } - - private function getUserOptions($oOptionNameIds=array(), $iUserId=0) - { - $iUserId = $iUserId>0?$iUserId:$this->getUserId(); - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $sOptNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); - $sOptValueIdCol = MySqlManager::getId(MySqlManager::OPTVAL_TABLE); - $sOptionTextCol = MySqlManager::getText(MySqlManager::OPT_TABLE); - - if(!is_array($oOptionNameIds)) - { - $oOptionNameIds = array($oOptionNameIds); - } - - $asUserinfo = array('select'=>array($sOptNameIdCol, $sOptValueIdCol, $sOptionTextCol), - 'from'=>MySqlManager::OPT_TABLE, - 'constraint'=>array($sUserIdCol=>$iUserId)); - if(count($oOptionNameIds)>0) - { - $asUserinfo['constraint'][$sOptNameIdCol] = "(".implode(", ", $oOptionNameIds).")"; - $asUserinfo['constOpe'] = array($sUserIdCol=>"=", $sOptNameIdCol=>" IN "); - $asUserinfo['constVar'] = true; - } - return $this->oMySql->selectRows($asUserinfo, true, $sOptNameIdCol); - } - - public function getProfile($oUser) - { - switch($oUser) - { - case '': - $iUserId = $this->getUserId(); - break; - case is_numeric($oUser): - $iUserId = $oUser; - break; - case is_string($oUser): - $oRes = $this->oMySql->selectValue(MySqlManager::USER_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE), array('user'=>$oUser)); - $iUserId = !$oRes?$this->getUserId():$oRes; - break; - default: - $iUserId = $this->getUserId(); - break; - } - - //User Info - $asProfile = $this->getUserInfo($iUserId); - - //History Info - $iFullCodeId = MySqlManager::getId(MySqlManager::CODE_TABLE, true); - $iFullLedName = MySqlManager::getFullColumnName(MySqlManager::CODE_TABLE, 'led'); - $asInfo = array('select'=>array($iFullCodeId, 'description', 'refer_id', $iFullLedName, 'phrase'), - 'from'=>MySqlManager::CODE_TABLE, - 'join'=>array(MySqlManager::URL_TABLE=>MySqlManager::getId(MySqlManager::CODE_TABLE)), - 'constraint'=>array('id_user'=>$iUserId), - 'orderBy'=>array('led'=>'DESC')); - - $asHistory = $this->oMySql->selectRows($asInfo); - foreach($asHistory as $asCode) - { - $asProfile['history'][$asCode['id_code']]['action'] = (($asCode['refer_id']==$asCode['id_code'])?'Création':'Modification').' de code'; - $asProfile['history'][$asCode['id_code']]['date'] = self::getDateFormat($asCode['led']); - $asProfile['history'][$asCode['id_code']]['description'] = $asCode['description']; - $asProfile['history'][$asCode['id_code']]['phrase'] = ($asCode['phrase']=='')?$asCode['id_code']:$asCode['phrase']; - } - return $this->jsonExport($asProfile); - } - - private static function isPmChan($sChanSafeName) - { - $asResult = array('is_pm'=>false, 'chan_name'=>$sChanSafeName); - - preg_match('/(?P\d+)'.self::PM_SEP.'(?P\d+)/u', $sChanSafeName, $asMatch); - if(!empty($asMatch)) - { - $asResult['is_pm'] = true; - $asResult['from'] = $asMatch['from']; - $asResult['to'] = $asMatch['to']; - if($asMatch['from'] > $asMatch['to']) $asResult['chan_name'] = $asMatch['to'].self::PM_SEP.$asMatch['from']; - else $asResult['chan_name'] = $asMatch['from'].self::PM_SEP.$asMatch['to']; - } - - return $asResult; - } - - private function checkChanAuth($sChanSafeName, $iUserId=0) - { - $asUserInfo = ($iUserId > 0)?$this->getUserInfo($iUserId):array('company'=>''); - $asCompanies = $this->oMySql->selectRows(array('select'=>MySqlManager::getText(MySqlManager::COMP_TABLE), 'from'=>MySqlManager::COMP_TABLE)); - $iCompChan = array_search($sChanSafeName, array_map(array('self', 'getChanSafeName'), $asCompanies)); - $asPm = $this->isPmChan($sChanSafeName); - - return $sChanSafeName!='' && //Empty channel name - ($iCompChan===false || $asUserInfo['company']==self::getCompanyFormat($asCompanies[$iCompChan])) && //Test Company Channel - (!$asPm['is_pm'] || $iUserId==$asPm['from'] || $iUserId==$asPm['to']); //Test PM - } - - public function joinChan($sChanName, $bFirstConn=false, $asAttendees=array()) - { - $asResult = array('success'=>self::ERROR); - $sSafeChanName = self::getChanSafeName($sChanName); - - //Authorization to join channel - $asMessages = array(); - if($this->checkChanAuth($sSafeChanName, $this->getUserId())) - { - //On first connection, display on-join message for all channels - if($bFirstConn) - { - $asConnChans = $this->getChannels($this->getUserId()); - $bFirstChan = true; - foreach($asConnChans as $iConnChanId=>$sConnChanName) - { - //Checking once for all channels - if(!$bFirstChan || !$this->isUserConnected($iConnChanId)) - { - $asMessages[$iConnChanId] = $sConnChanName; - } - else break; - $bFirstChan = false; - } - } - - //Add connection link in DB - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); - - $asData = array('safe_name'=>$sSafeChanName, MySqlManager::getText(MySqlManager::CHAN_TABLE)=>$sChanName); - $iChanId = $this->oMySql->insertUpdateRow(MySqlManager::CHAN_TABLE, $asData, array('safe_name'), false); - - //Is user already connected to this chan - $bConnectedUser = $this->isUserConnected($iChanId); - $iConnId = $this->oMySql->insertUpdateRow(MySqlManager::CONN_TABLE, array($sUserIdCol=>$this->getUserId(), $sChanIdCol=>$iChanId), array($sUserIdCol, $sChanIdCol), false); - if($iConnId>0) $asResult['success'] = self::SUCCESS; - - if($asResult['success']==self::SUCCESS) - { - //Return connected channels - $asResult['channels'] = $this->getChannels($this->getUserId()); - - //Add tab title (customized) - //TODO delete this shit and insert the channel type into channels DB table - foreach($asResult['channels'] as $iConnectedChanId=>$sConnectedChanName) - { - $asPm = $this->isPmChan($sConnectedChanName); - $asResult['channel_tab_names'][$iConnectedChanId] = $asPm['is_pm']?$this->getChatNickNames($asPm['from']==$this->getUserId()?$asPm['to']:$asPm['from']):$sConnectedChanName; - } - $asResult['current_chan_id'] = $iChanId; - - //Communicate on user's connection - if(!$bConnectedUser) - { - $asMessages[$iChanId] = $this->oMySql->selectValue(MySqlManager::CHAN_TABLE, MySqlManager::getText(MySqlManager::CHAN_TABLE), $iChanId); - } - $sStatus = $this->getUserOptionValue(self::OPT_STATUS); - $asUserInfo = $this->getUserInfo($this->getUserId()); - foreach($asMessages as $iChanId=>$sMsgChanName) - { - $asPm = $this->isPmChan($sMsgChanName); - $this->addMessage(' de '.$asUserInfo['company'].' ('.($sStatus==''?'aucune mission en cours':$this->getDescriptionFormat($sStatus)).') rejoint '.($asPm['is_pm']?'le chan privé':'#'.$sMsgChanName), self::MESSAGE_CONN, $iChanId); - } - - //Send invites to attendees - if(!empty($asAttendees)) - { - $asUserInfo = $this->getUserInfo($this->getUserId()); - foreach($asAttendees as $sAttendee) - { - if($this->checkChanAuth($sSafeChanName, $sAttendee)) - { - $this->addMessage($sAttendee, self::MESSAGE_INVITE, $iChanId); - } - } - } - - //Update chan leds - $this->pingChans(); - } - } - - return self::jsonExport($asResult); - } - - private function isUserConnected($iChanId=0, $iUserId=0) - { - $iUserId = $iUserId>0?$iUserId:$this->getUserId(); - - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $asInfo = array('select' => MySqlManager::getId(MySqlManager::CONN_TABLE), - 'from' => MySqlManager::CONN_TABLE, - 'constraint'=> array($sUserIdCol=>$iUserId, 'led'=>"DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)"), - 'constOpe' => array($sUserIdCol=>'=', 'led'=>'>'), - 'constVar' => true, - 'groupBy' => $sUserIdCol); - - //Add chan constraint - if($iChanId>0) - { - $asInfo['constraint'][MySqlManager::getId(MySqlManager::CHAN_TABLE)] = $iChanId; - $asInfo['constOpe'][MySqlManager::getId(MySqlManager::CHAN_TABLE)] = '='; - } - return (count($this->oMySql->selectRows($asInfo))>0); - } - - public function pingChans() - { - $aiConstraints = array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->getUserId()); - $asUpdates = array('led'=>date(self::DATE_TIME_SQL_FORMAT)); - $this->oMySql->updateRows(MySqlManager::CONN_TABLE, $aiConstraints, $asUpdates); - } - - public function quitChan($sChanName) - { - $iChanId = $this->getChanId($sChanName); - $iConnId = $this->getConnId($iChanId); - if($iConnId>0) - { - $this->oMySql->deleteRow(MySqlManager::CONN_TABLE, $iConnId); - $asPm = $this->isPmChan($sChanName); - $this->addMessage('quitte '.($asPm['is_pm']?'le chan privé':'#'.$sChanName), self::MESSAGE_CONN, $iChanId); - } - } - - private function getActiveChannels() - { - //Get connected users - $asActiveChanUsers = $this->getConnectedUsers(false, false); - $asActiveChans = array_keys($asActiveChanUsers); - - //Get Channel names - $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); - $asChanNames = $this->oMySql->selectRows(array('select'=>array($sChanIdCol, MySqlManager::getText(MySqlManager::CHAN_TABLE)), 'from'=>MySqlManager::CHAN_TABLE), true, $sChanIdCol); - - foreach($asActiveChans as $iChanId) - { - $asChannels[$asChanNames[$iChanId]] = count($asActiveChanUsers[$iChanId]); - } - - //remove restricted channels - $asPublicChannels = array(); - foreach($asChannels as $sChanName=>$iChanCount) - { - if($this->checkChanAuth($this->getChanSafeName($sChanName))) $asPublicChannels[$sChanName] = $iChanCount; - } - - return $asPublicChannels; - } - - private function getChannels($iUserId=0) - { - $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE, true); - $sChanNameCol = MySqlManager::getText(MySqlManager::CHAN_TABLE); - - $asInfo = array('select'=>array($sChanIdCol, $sChanNameCol), - 'from'=> MySqlManager::CONN_TABLE, - 'join'=> array(MySqlManager::CHAN_TABLE=>MySqlManager::getId(MySqlManager::CHAN_TABLE)), - 'orderBy'=> array(MySqlManager::getId(MySqlManager::CONN_TABLE)=>'ASC')); - if($iUserId > 0) $asInfo['constraint'] = array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$iUserId); - - $asChannels = $this->oMySql->selectRows($asInfo, true, MySqlManager::getId(MySqlManager::CHAN_TABLE)); - - //remove restricted channels - $asPublicChannels = array(); - foreach($asChannels as $iChanId=>$sChanName) - { - if($this->checkChanAuth($this->getChanSafeName($sChanName), $iUserId)) $asPublicChannels[$iChanId] = $sChanName; - } - return $asPublicChannels; - } - - private function getChanId($sChanName) - { - $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); - $sSafeChanName = self::getChanSafeName($sChanName); - $iChanId = $this->oMySql->selectValue(MySqlManager::CHAN_TABLE, $sChanIdCol, array('safe_name'=>$sSafeChanName)); - if($iChanId==0) - { - - $this->addError('No channel id found with channel name (safe): '.$sSafeChanName.', unsafe: '.$sChanName); - } - return $iChanId; - } - - private function getConnId($iChanId, $iUserId=0) - { - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $sConnIdCol = MySqlManager::getId(MySqlManager::CONN_TABLE); - $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); - - if($iUserId==0) $iUserId = $this->getUserId(); - $iConnId = $this->oMySql->selectValue(MySqlManager::CONN_TABLE, $sConnIdCol, array($sUserIdCol=>$iUserId, $sChanIdCol=>$iChanId)); - if($iConnId==0) - { - $this->addError('No connection found for user: '.$iUserId.' and channel id: '.$iChanId); - } - return $iConnId; - } - - public function addChatMessage($sMessage, $sChanName) - { - $sMessage = htmlspecialchars($sMessage); - $sType = self::MESSAGE_USER; - if(mb_substr($sMessage, 0, 1) == '/') - { - if(mb_substr($sMessage, 0, 4) == '/me ') - { - $sType = self::MESSAGE_ACTION; - $sMessage = mb_substr($sMessage, 3); - } - elseif(mb_substr($sMessage, 0, 6) == '/slap ') - { - $sType = self::MESSAGE_ACTION; - $sMessage = ' fout une grosse tarte à '.mb_substr($sMessage, 5); - } - elseif(mb_substr($sMessage, 0, 4) == '/bs ') - { - $sType = self::MESSAGE_ACTION; - $sMessage = ' bitch-slaps '.mb_substr($sMessage, 3); - } - elseif(mb_substr($sMessage, 0, 6) == '/kick ') - { - $sType = self::MESSAGE_ACTION; - $sMessage = ' met un coup de pied au cul de '.mb_substr($sMessage, 5); - } - elseif(mb_substr($sMessage, 0, 6) == '/nick ' && mb_strlen($sMessage)>6) - { - $sNewNick = $this->getNickNameFormat(mb_substr($sMessage, 5)); - $sOldNick = $this->getNickNameFormat($this->getChatNickNames($this->getUserId())); - - //changing Nickname - $this->setOptions(array(self::OPT_NICKNAME=>$sNewNick)); - - //Message - $sType = self::MESSAGE_NICK; - $sChanName = self::ALL_CHAN_TEXT; - $sMessage = $sOldNick.' a changé son pseudo en '.$sNewNick; - } - elseif(mb_substr($sMessage, 0, 9) == '/mission ' && mb_strlen($sMessage)>9) - { - $sNewStatus = mb_substr($sMessage, 9); - $sNewFormatStatus = $this->getDescriptionFormat($sNewStatus); - - //changing Nickname - $this->setOptions(array(self::OPT_STATUS=>$sNewStatus)); - - //Message - $sType = self::MESSAGE_STATUS; - $sChanName = self::ALL_CHAN_TEXT; - $sMessage = 'est sur une nouvelle mission : '.$sNewFormatStatus; - } - elseif(mb_substr($sMessage, 0, 6) == '/mail ' && mb_strlen($sMessage)>6) - { - $sImagePattern = '/\/mail (?P\w+) (?P.*)/u'; - preg_match($sImagePattern, $sMessage, $asMatches); - - //Looking for user Id - $asContraints = array( 'LOWER(`'.MySqlManager::getText(MySqlManager::OPT_TABLE).'`)'=>mb_strtolower($asMatches['nickname']), - MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>self::OPT_NICKNAME); - $iUserIdTo = $this->oMySql->selectValue(MySqlManager::OPT_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE), $asContraints); - - //Handling mail - if($iUserIdTo>0) - { - if(trim($asMatches['message'])!='') - { - if($this->sendPM($iUserIdTo, $asMatches['message'])) - { - $sMessage = 'a envoyé un mail à '.$asMatches['nickname']; - } - else - { - $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (raison inconnue, voir log)'; - } - } - else - { - $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (message vide)'; - } - } - else - { - $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (pseudo inconnu)'; - } - $sType = self::MESSAGE_ACTION; - } - elseif(mb_substr($sMessage, 0, 5) == '/mean') - { - $sPageContent = file_get_contents('http://www.randominsults.net/'); - $sStartText = ''; - $sEndText = ''; - $iStartPos = mb_strpos($sPageContent, $sStartText); - $iEndPos = mb_strpos($sPageContent, $sEndText); - - $sMessage = mb_substr($sPageContent, $iStartPos + mb_strlen($sStartText), $iEndPos - $iStartPos); - } - elseif(mb_substr($sMessage, 0, 5) == '/like') - { - $sType = self::MESSAGE_ACTION; - $sMessage = ' plussoie'; - } - elseif(mb_substr($sMessage, 0, 4) == '/now') - { - $sType = self::MESSAGE_ACTION; - $asWeekDays = array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'); - $asMonths = array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'); - $sMessage = ' a demandé l\'heure. Pour sa gouverne, il est exactement '.date(self::TIME_FORMAT). - ', le '.$asWeekDays[date('N')-1].' '.date('j').' '.$asMonths[date('n')-1].' '.date('Y').' (semaine '.date('W').')'; - } - elseif($sMessage == '/channels' || mb_substr($sMessage, 0, 5) == '/list' || $sMessage == '/chans') - { - //Always at least one channel open (the one the message is sent from) - $sMessage = ' a demandé les chans disponibles. Pour sa gouverne, les chans ayant des membres connectés sont #'.MySqlManager::implodeAll($this->getActiveChannels(), ' (', ', #', '', ')'); - $sType = self::MESSAGE_ACTION; - } - elseif(mb_substr($sMessage, 0, 5) == '/img ' && mb_strlen($sMessage)>5) - { - $sUrl = trim(mb_substr($sMessage, 4)); - $asResult = $this->downloadToTmp($sUrl); - if($asResult['error']=='') - { - $sMessage = $this->getJsonMessage(array($asResult['out'], $asResult['width'], $asResult['height'], $sUrl)); - $sType = self::MESSAGE_IMG; - } - } - elseif(mb_substr($sMessage, 0, 6) == '/9gag ' && mb_strlen($sMessage)>6) - { - $sMessage = $this->getJsonMessage($this->get9gagPost(trim(mb_substr($sMessage, 6)))); - $sType = self::MESSAGE_9GAG; - } - elseif(mb_substr($sMessage, 0, 7) == '/reboot' && $this->getUserClearance()==self::CLEARANCE_ADMIN) - { - $sMessage = 'L\'administrateur a demandé un reboot. Votre page va se rafraichir automatiquement dans '.self::REBOOT_DELAY.' secondes.'; - $sType = self::MESSAGE_REBOOT; - } - } - elseif(mb_substr($sMessage, 0, 1) == '@' && mb_strpos($sMessage, ' ')>1) - { - $sType = self::MESSAGE_PRIVATE; - } - $sChanId = $this->getChanId($sChanName); - return $this->addMessage($sMessage, $sType, $sChanId); - } - - private function getJsonMessage($asData) - { - return self::JSON_PREFIX.$this->jsonConvert($asData); - } - - private function downloadToTmp($sUrl) - { - $sFileInfo = pathinfo($sUrl); - $sImageExt = mb_strtolower($sFileInfo['extension']); - $sFilePath = self::DOC_TMP_FOLDER.uniqid().'.'.$sImageExt; - return ToolBox::createThumbnail($sUrl, self::CHAT_IMG_MAX_WIDTH, self::CHAT_IMG_MAX_HEIGHT, $sFilePath); - } - - private function sendPM($iUserIdTo, $sMessage) - { - $bSuccess = false; - $asUserFrom = $this->getUserInfo($this->getUserId()); - $asUserTo = $this->getUserInfo($iUserIdTo); - $sFrom = $asUserFrom['name'].' '; - - $sTo = $asUserTo['name'].' <'.$asUserTo['email'].'>'; - $sMessage .= "\n\n\n".'Ne répondez pas à ce mail. Connectez-vous sur Databap.'; - - $sResult = ToolBox::sendMail($sFrom, $sTo, 'Databap ', 'Databap PM', $sMessage); - $bSuccess = ($sResult==''); - if(!$bSuccess) $this->addError($sResult); - return $bSuccess; - } - - private function addMessage($sMessage, $sType, $iChanId) - { - $bResult = false; - if($iChanId>0) - { - $asInsert = array( MySqlManager::getId(MySqlManager::USER_TABLE) => $this->getUserId(), - 'nickname' => $this->getChatNickNames($this->getUserId()), - MySqlManager::getId(MySqlManager::CHAN_TABLE) => $iChanId, - MySqlManager::getText(MySqlManager::MSG_TABLE) => $sMessage, - 'type' => $sType, - 'date' => 'CURDATE()'); - $bResult = $this->oMySql->insertRow(MySqlManager::MSG_TABLE, $asInsert); - } - else - { - $this->addError('Message deleted: No channel found'); - } - return $bResult; - } - - public function getMessages($iFirstMsgId="0") - { - //Update chan ping - $this->pingChans(); - - //Get messages and users' infos - $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE, true); - $sChanTextCol = MySqlManager::getText(MySqlManager::CHAN_TABLE); - $sMsgIdCol = MySqlManager::getId(MySqlManager::MSG_TABLE, true); - $sMsgTextCol = MySqlManager::getText(MySqlManager::MSG_TABLE); - $sMsgTableLed = MySqlManager::getFullColumnName(MySqlManager::MSG_TABLE, 'led'); - $sMsgTableChanIdCol = MySqlManager::getFullColumnName(MySqlManager::MSG_TABLE, MySqlManager::getId(MySqlManager::CHAN_TABLE)); - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE, true); - $sMsgTableUserIdCol = MySqlManager::getFullColumnName(MySqlManager::MSG_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE)); - $sUserTableUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE, true); - $sConnTableUserIdCol = MySqlManager::getFullColumnName(MySqlManager::CONN_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE)); - $sConnTableChanIdCol = MySqlManager::getFullColumnName(MySqlManager::CONN_TABLE, MySqlManager::getId(MySqlManager::CHAN_TABLE)); - - //channel related messages - $sCurDate = date(Databap::DATE_SQL_FORMAT); - $asInfo = array('select' => array($sMsgTableChanIdCol, $sMsgIdCol, $sMsgTextCol, 'type', $sMsgTableLed, 'first_name', 'last_name', $sUserIdCol, 'nickname'), - 'from' => MySqlManager::CONN_TABLE, - 'joinOn' => array( MySqlManager::MSG_TABLE =>array($sMsgTableChanIdCol, '=', $sConnTableChanIdCol), - MySqlManager::USER_TABLE=>array($sUserTableUserIdCol, '=', $sMsgTableUserIdCol)), - 'constraint'=> array($sConnTableUserIdCol=>$this->getUserId(), $sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate), - 'constOpe' => array($sConnTableUserIdCol=>'=', $sMsgIdCol=>'>', 'date'=>'=')); - $asSqlMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); - - //Global messages - $asInfo['from'] = MySqlManager::MSG_TABLE; - $asInfo['joinOn'] = array(MySqlManager::USER_TABLE=>array($sUserTableUserIdCol, '=', $sMsgTableUserIdCol)); - $asInfo['constraint'] = array($sMsgTableChanIdCol=>self::ALL_CHAN_ID, $sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate); - $asInfo['constOpe'] = array($sMsgTableChanIdCol=>'=', $sMsgIdCol=>'>', 'date'=>'='); - $asSqlGlobalMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); - - //Invites messages - $asInfo['constraint'] = array($sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate, 'type'=>self::MESSAGE_INVITE, 'message'=>$this->getUserId()); - $asInfo['constOpe'] = array($sMsgIdCol=>'>', 'date'=>'=', 'type'=>'=', 'message'=>'='); - $asSqlInviteMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); - - //Merge flows - if(!empty($asSqlGlobalMessages)) $asSqlMessages = array_diff_key($asSqlMessages, $asSqlGlobalMessages) + $asSqlGlobalMessages; - if(!empty($asSqlInviteMessages)) $asSqlMessages = array_diff_key($asSqlMessages, $asSqlInviteMessages) + $asSqlInviteMessages; - - //Sort messages - ksort($asSqlMessages); - - //Sort out messages for Json Export - $iPrefixLen = mb_strlen(self::JSON_PREFIX); - $asMessages = array('messages'=>array(), 'last_message_id'=>0); - foreach($asSqlMessages as $iMessageId=>$asMessageInfo) - { - $iChanId = $asMessageInfo[MySqlManager::getId(MySqlManager::CHAN_TABLE)]; - $iUserId = $asMessageInfo[MySqlManager::getId(MySqlManager::USER_TABLE)]; - $sMessageType = $asMessageInfo['type']; - $asMessages['messages'][$iMessageId]['id_chan'] = $iChanId; - $asMessages['messages'][$iMessageId]['message'] = $asMessageInfo[$sMsgTextCol]; - $asMessages['messages'][$iMessageId]['msg_class'] = $sMessageType; - $asMessages['messages'][$iMessageId]['time'] = self::getDateFormat($asMessageInfo['led'], self::TIME_FORMAT); - $asMessages['messages'][$iMessageId]['name'] = self::getNameFormat($asMessageInfo['first_name'], $asMessageInfo['last_name']); - $asMessages['messages'][$iMessageId]['nickname'] = self::getNickNameFormat($asMessageInfo['nickname']); - - //generated messages - if($sMessageType==self::MESSAGE_ADD_CODE || $sMessageType==self::MESSAGE_EDIT_CODE) - { - $asCode = $this->getCodeInfo($asMessages['messages'][$iMessageId]['message']); - $asMessages['messages'][$iMessageId]['description'] = $asCode['description']; - } - elseif($sMessageType==self::MESSAGE_ADD_PROC || $sMessageType==self::MESSAGE_EDIT_PROC) - { - $asProc = $this->getProcInfo($asMessages['messages'][$iMessageId]['message']); - $asMessages['messages'][$iMessageId]['description'] = $asProc['title']; - } - elseif($sMessageType==self::MESSAGE_ADD_DOC || $sMessageType==self::MESSAGE_EDIT_DOC) - { - $asDoc = $this->getDocInfo($asMessages['messages'][$iMessageId]['message']); - $asMessages['messages'][$iMessageId]['description'] = $asDoc['title']; - } - elseif($sMessageType==self::MESSAGE_ARTICLE) - { - $asTransferredInfo = $this->getArticleInfo($asMessages['messages'][$iMessageId]['message']); - $asMessages['messages'][$iMessageId] = array_merge($asMessages['messages'][$iMessageId], $asTransferredInfo); - } - - //Switch Chan ID with name for the user to join - if($sMessageType==self::MESSAGE_INVITE) - { - $asMessages['messages'][$iMessageId]['id_chan'] = $this->oMySql->selectValue(MySqlManager::CHAN_TABLE, 'safe_name', $iChanId); - } - - //Json message - if(mb_substr($asMessages['messages'][$iMessageId]['message'], 0, $iPrefixLen) == self::JSON_PREFIX) - { - $asMessages['messages'][$iMessageId]['message'] = json_decode(mb_substr($asMessages['messages'][$iMessageId]['message'], $iPrefixLen)); - } - else //Normal message - { - //TODO Internal links - - //Dynamic web link - $asPatterns = array('`((?:https?|ftp)://\S+[[:alnum:]]/?)`sui', '`((?$1 ', '$1'); - $asMessages['messages'][$iMessageId]['message'] = preg_replace($asPatterns, $asLinks, $asMessages['messages'][$iMessageId]['message']); - - //Dynamic chan link - $asPatterns = '/(^|\s)#(\w*[^\s]+\w*)/u'; - $asLinks = '\1#\2'; - $asMessages['messages'][$iMessageId]['message'] = preg_replace($asPatterns, $asLinks, $asMessages['messages'][$iMessageId]['message']); - } - } - - //Set last message Id (if new messages since $iFirstMsgId) - if(!empty($asMessages['messages'])) - { - $asMessages['last_message_id'] = max(array_keys($asMessages['messages'])); - } - - return $this->jsonExport($asMessages); - } - - private function getConnectedChans($iuserId=0) - { - $iuserId = $iuserId>0?$iuserId:$this->getUserId(); - - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $asInfo = array('select' => array(MySqlManager::getId(MySqlManager::CHAN_TABLE, true), MySqlManager::getText(MySqlManager::CHAN_TABLE)), - 'from' => MySqlManager::CONN_TABLE, - 'join' => array(MySqlManager::CHAN_TABLE=>MySqlManager::getId(MySqlManager::CHAN_TABLE)), - 'constraint'=> array($sUserIdCol=>$iUserId, 'led'=>"DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)"), - 'constOpe' => array($sUserIdCol=>'=', 'led'=>'>'), - 'constVar' => true); - - return $this->oMySql->selectRows($asInfo, true, MySqlManager::getId(MySqlManager::CONN_TABLE)); - } - - public function getConnectedUsers($bUserRestricted=false, $bJson=true) - { - $sQuery = " SELECT /* config.php 1313 */ conn2.id_channel, conn2.id_user, conn2.led, IF(DATE_SUB(NOW(), INTERVAL 1 MINUTE) < conn2.led, 0, 1) AS afk, - users.first_name, users.last_name, companies.company, companies.logo, option_nickname.option AS nickname, option_status.option AS status - FROM connections AS conn1 - LEFT JOIN connections AS conn2 ON conn2.id_channel = conn1.id_channel - LEFT JOIN users ON users.id_user = conn2.id_user - LEFT JOIN companies ON companies.id_company = users.id_company - LEFT JOIN `options` AS option_nickname ON option_nickname.id_user = users.id_user AND option_nickname.id_option_name = ".self::OPT_NICKNAME." - LEFT JOIN `options` AS option_status ON option_status.id_user = users.id_user AND option_status.id_option_name = ".self::OPT_STATUS." - WHERE conn2.led > DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)". - ($bUserRestricted?"AND conn1.id_user = ".$this->getUserId():"")." - GROUP BY conn2.id_channel, conn2.id_user - ORDER BY option_nickname.option ASC"; - $asUserChannels = $this->oMySql->getArrayQuery($sQuery, true); - - //Last messages - $iUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $asLastMsg = $this->oMySql->selectRows(array('select'=>array($iUserIdCol, 'MAX(led)'), 'from'=>MySqlManager::MSG_TABLE), true, $iUserIdCol); - - $asConnectedUsers = array(); - foreach($asUserChannels as $asUser) - { - $sChanId = $asUser[MySqlManager::getId(MySqlManager::CHAN_TABLE)]; - $iUserId = $asUser[$iUserIdCol]; - $asConnectedUsers[$sChanId][$iUserId]['id_user'] = $iUserId; - $asConnectedUsers[$sChanId][$iUserId]['name'] = self::getNameFormat($asUser['first_name'], $asUser['last_name']); - $asConnectedUsers[$sChanId][$iUserId]['company'] = self::getCompanyFormat($asUser['company']); - $asConnectedUsers[$sChanId][$iUserId]['status'] = $asUser['status']; - $asConnectedUsers[$sChanId][$iUserId]['logo'] = $asUser['logo']; - $asConnectedUsers[$sChanId][$iUserId]['nickname'] = self::getNickNameFormat($asUser['nickname']==''?$asUser['first_name']:$asUser['nickname']); - $asConnectedUsers[$sChanId][$iUserId]['last_activity'] = array_key_exists($iUserId, $asLastMsg)?self::getDateFormat($asLastMsg[$iUserId], self::TIME_FORMAT):''; - $asConnectedUsers[$sChanId][$iUserId]['ping'] = self::getDateFormat($asUser['led'], self::TIME_FORMAT); - $asConnectedUsers[$sChanId][$iUserId]['afk'] = $asUser['afk']; - } - return $bJson?$this->jsonExport($asConnectedUsers):$asConnectedUsers; - } - - private function getChatNickNames($iUserId=0) - { - $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); - $sNicknameCol = MySqlManager::getText(MySqlManager::OPT_TABLE); - $asConstraints = array(MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>self::OPT_NICKNAME); - - if($iUserId>0) - { - $asConstraints[MySqlManager::getId(MySqlManager::USER_TABLE)] = $iUserId; - $oResult = $this->oMySql->selectValue(MySqlManager::OPT_TABLE, $sNicknameCol, $asConstraints); - } - else - { - $asInfo = array('select'=>array($sUserIdCol, $sNicknameCol), - 'from'=>MySqlManager::OPT_TABLE, - 'constraint'=>$asConstraints); - $oResult = $this->oMySql->selectRows($asInfo, true, $sUserIdCol); - } - return $oResult; - } - - private function switchCodeId(&$iCodeId, $sMode) - { - switch($sMode) - { - case 'first': - $iCodeId = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'refer_id', $iCodeId); - break; - case 'last': - $iRefId = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'refer_id', $iCodeId); - $iCodeId = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'MAX(id_code)', array('refer_id'=>$iRefId)); - break; - } - } - - private function getIdCodeFromPhrase($sPhrase) - { - $iCodeId = $this->oMySql->selectValue(MySqlManager::URL_TABLE, MySqlManager::getId(MySqlManager::CODE_TABLE), array('phrase'=>$sPhrase)); - $this->switchCodeId($iCodeId, 'last'); - return $iCodeId; - } - - private function getPhraseFromIdCode($iCodeId) - { - $this->switchCodeId($iCodeId, 'first'); - return $this->oMySql->selectValue(MySqlManager::URL_TABLE, 'phrase', array('id_code'=>$iCodeId)); - } - - private function getCodeVersions($iRefCodeId) - { - $asInfo = array('select'=>array(MySqlManager::getId(MySqlManager::CODE_TABLE)), - 'from'=>MySqlManager::CODE_TABLE, - 'constraint'=>array('refer_id'=>$iRefCodeId), - 'orderBy'=>array('id_code'=>'asc')); - $asCodeIds = $this->oMySql->selectRows($asInfo); - - $asCodeVersions = array(); - foreach($asCodeIds as $iCodeId) - { - $asCodeVersions[] = $this->getCodeInfo($iCodeId); - } - return $asCodeVersions; - } - - public function getColoredCode($oCode) - { - $asCode = $this->getCodeInfo($oCode, '', true); - - //code - $oReader = new Reader($asCode['code']); - $asCode['code'] = $oReader->getColoredCode(); - - //phrase - $sPhrase = $this->getPhraseFromIdCode($asCode['id_code']); - if($sPhrase !== false) - { - $asCode['phrase'] = $sPhrase; - } - - //user - $asUsers[$asCode['id_user']] = $this->getUserInfo($asCode['id_user']); - $asCode = array_merge($asCode, $asUsers[$asCode['id_user']]); - - //versions - $asCodeVersions = $this->getCodeVersions($asCode['refer_id']); - $iCodeRowId = 0; - foreach($asCodeVersions as $iRowId=>$asCodeVersion) - { - if($asCodeVersion['id_code']==$asCode['id_code']) $iCodeRowId = $iRowId; - } - $asCode['truncated'] = ($iCodeRowId > self::MAX_LIST_LENGTH)?$asCodeVersions[$iCodeRowId-1-self::MAX_LIST_LENGTH]['id_code']:0; - foreach($asCodeVersions as $iRowId=>$asCodeVersion) - { - if($iCodeRowId - $iRowId <= self::MAX_LIST_LENGTH) - { - if(!array_key_exists($asCodeVersion['id_user'], $asUsers)) - { - $asUsers[$asCodeVersion['id_user']] = $this->getUserInfo($asCodeVersion['id_user']); - } - - if($asCodeVersion['id_code']!=$asCode['id_code']) - { - $asVersionInfo = array_merge($asCodeVersion, $asUsers[$asCodeVersion['id_user']]); - $asCode['other_versions'][] = $asVersionInfo; - } - } - } - - return $this->jsonExport($asCode); - } - - public function getNudeCode($oCode) - { - $asCode = $this->getCodeInfo($oCode, '', true); - //$sCode = Reader::convText2Html($asCode['code']); - return $asCode['code']; - } - - public function getRawCode($oCode) - { - $asCode = $this->getCodeInfo($oCode, '', true); - $asUser = $this->getUserInfo($asCode['id_user']); - $sEncodedCode = $this->jsonConvert($asCode['code']); - $sEncodedDesc = $this->jsonConvert($asCode['description']); - - return ' - - - - - - Databap • - - -

-					
-				
-				';
-	}
-
-	public function getSavedCode($oCode)
-	{
-		$asCode = $this->getCodeInfo($oCode, '', true);
-		$sPhrase = $this->getPhraseFromIdCode($asCode['id_code']);
-		if(!$sPhrase)
-		{
-			$sPhrase = 'code_numero_'.$asCode['id_code'];
-		}
-		
-		//set headers
-		header('Pragma: public');
-		header('Expires: 0');
-		header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
-		header('Cache-Control: public');
-		header('Content-Description: File Transfer');
-		header('Content-Type: application/force-download');
-		header('Content-Disposition: attachment; filename='.$sPhrase.'.abap');
-		header('Content-Transfer-Encoding: binary');
-		//header('Content-Length: '.filesize($sFilePath));
-
-		return $asCode['code'];
-	}
-
-	public function getPrintCode($oCode)
-	{
-		return $this->getRawCode($oCode).'';
-	}
-	
-	public function getItemList()
-	{
-		$sIdCodeCol = MySqlManager::getId(MySqlManager::CODE_TABLE);
-		$sIdProcCol = MySqlManager::getId(MySqlManager::PROC_TABLE);
-		$sIdArtCol = MySqlManager::getId(MySqlManager::ART_TABLE);
-		$sIdDocCol = MySqlManager::getId(MySqlManager::DOC_TABLE);
-		$sIdCodeColFull = MySqlManager::getId(MySqlManager::CODE_TABLE, true);
-		$sIdUserCol = MySqlManager::getId(MySqlManager::USER_TABLE);
-		$sIdUserColFull = MySqlManager::getId(MySqlManager::USER_TABLE, true);
-		$sLedColFull = MySqlManager::getFullColumnName(MySqlManager::CODE_TABLE, 'led') ;
-		
-		//Get code Ids
-		$asInfo = array('select'=>array("MAX($sIdCodeCol) AS id_item"), //selecting last version among codes having the same refer_id
-						'from'=>MySqlManager::CODE_TABLE,
-						'groupBy'=>array('refer_id'),
-						'orderBy'=>array('id_item'=>'desc'));
-		$asItemIds[Databap::CODE_TYPE] = $this->oMySql->selectRows($asInfo);
-		
-		//get Proc Ids
-		$asInfo = array('select'=>array("MAX($sIdProcCol) AS id_item"),
-						'from'=>MySqlManager::PROC_TABLE,
-						'constraint'=>array('refer_id'=>"0"), //meaning that there is no newer version 
-						'orderBy'=>array('id_item'=>'desc'));
-		$asItemIds[Databap::PROC_TYPE] = $this->oMySql->selectRows($asInfo);
-
-		//get Article Ids
-		$asInfo = array('select'=>array($sIdArtCol." AS id_item"),
-						'from'=>MySqlManager::ART_TABLE,
-						'orderBy'=>array('id_item'=>'desc'));
-		$asItemIds[Databap::ART_TYPE] = $this->oMySql->selectRows($asInfo);
-		
-		//Get documentation
-		$asInfo = array('select'=>array("MAX($sIdDocCol) AS id_item"), //selecting last version among docs having the same refer_id
-						'from'=>MySqlManager::DOC_TABLE,
-						'groupBy'=>array('refer_id'),
-						'orderBy'=>array('id_item'=>'desc'));
-		$asItemIds[Databap::DOC_TYPE] = $this->oMySql->selectRows($asInfo);
-		
-		$asTypeFunc = array(Databap::CODE_TYPE=>'Code', Databap::PROC_TYPE=>'Proc', Databap::ART_TYPE=>'Article', Databap::DOC_TYPE=>'Doc');
-		
-		//Build code info structure
-		//TODO phrases for all item types?
-		$asUsers = $asCode = $asItemList = array();
-		foreach($asItemIds as $sType=>$asTypedItemIds)
-		{
-			$asTypedItemIds = array_filter($asTypedItemIds); //null value returned from query (MAX(...))
-			foreach($asTypedItemIds as $iItemId)
-			{
-				$asItem = call_user_func(array($this, 'get'.$asTypeFunc[$sType].'Info'), $iItemId);
-				$asItem['type'] = $sType;
-				$asItem['id_item'] = $iItemId;
-				if(array_key_exists($sIdUserCol, $asItem)) //replacing user's id with user's name & company (if not done already)
-				{
-					$iUserId = $asItem[$sIdUserCol];
-					if(!array_key_exists($iUserId, $asUsers)) $asUsers[$iUserId] = $this->getUserInfo($iUserId);
-					$asItem['name'] = $asUsers[$iUserId]['name'];
-					$asItem['company'] = $asUsers[$iUserId]['company'];
-				}
-				$asItemList[$asItem['timestamp'].$asItem['type'].$asItem['id_item']] = $asItem;
-			}
-		}
-		krsort($asItemList);
-		return $this->jsonExport($asItemList);
-	}
-
-	public function getCodeBlock()
-	{
-		$oMask = new Mask();
-		$oMask->initFile('code_block');
-		return $oMask->getMask();
-	}
-
-	public static function checkNoPassResetAction($sAction)
-	{
-		return in_array($sAction, array('messages', 'upload_image', 'user_info', 'rss'));
-	}
-	
-	public function logMeIn($sName, $sCompany, $sToken, $sAction)
-	{
-		$iUserId = 0;
-		$bResetPass = true;
-		$sUserTableId = MySqlManager::getId(MySqlManager::USER_TABLE);
-		$sCompanyTableId = MySqlManager::getId(MySqlManager::COMP_TABLE);
-		$sCompanyTableText = MySqlManager::getText(MySqlManager::COMP_TABLE);
-
-		//login using form
-		if($sName!='' && $sCompany!='')
-		{
-			$asNames = explode(' ', mb_strtolower($sName));
-			$sCompany = mb_strtolower($sCompany);
-			
-			//Get Company's Id
-			$iCompanyId = $this->oMySql->selectValue(MySqlManager::COMP_TABLE, $sCompanyTableId, array($sCompanyTableText=>$sCompany)); 
-			
-			$asConsts = array('first_name'=>$asNames[0], 'last_name'=>$asNames[1], $sCompanyTableId=>$iCompanyId);
-			$iUserId = $this->oMySql->selectValue(MySqlManager::USER_TABLE, $sUserTableId, $asConsts);
-			if(!$iUserId)
-			{
-				$asInvConsts = array('first_name'=>$asNames[1], 'last_name'=>$asNames[0], $sCompanyTableId=>$iCompanyId);
-				$iUserId = $this->oMySql->selectValue(MySqlManager::USER_TABLE, $sUserTableId, $asInvConsts);
-			}
-		}
-		//auto login by cookie
-		elseif(isset($_COOKIE[self::USER_COOKIE_ID]))
-		{
-			$asConstraints = array(	$sUserTableId=>$_COOKIE[self::USER_COOKIE_ID],
-									'pass'=>$_COOKIE[self::USER_COOKIE_PASS]);
-			
-			$asUserInfo = $this->oMySql->selectRow(MySqlManager::USER_TABLE, $asConstraints, array($sUserTableId, 'led'));
-			$iUserId = $asUserInfo[$sUserTableId];
-			
-			//Check pass reset necessity
-			$bResetPass = (mb_substr($asUserInfo['led'], 0, 10) != date(Databap::DATE_SQL_FORMAT));
-		}
-		//Login using Token (limited access)
-		elseif($sAction==self::EXT_ACCESS && $sToken!='')
-		{
-			$iUserId = $this->checkToken($sToken);
-			$bResetPass = false;
-		}
-		
-		if($iUserId>0)
-		{
-			$this->setUserId($iUserId);
-			if(!self::checkNoPassResetAction($sAction) && $bResetPass)
-			{
-				$this->resetPassword();
-			}
-		}
-
-		return ($this->getUserId()>0);
-	}
-	
-	public function checkToken($sKey)
-	{
-		$iUserId = mb_strstr($sKey, '_', true);
-		$sToken = mb_substr($sKey, mb_strlen($iUserId)+1);
-		return (mb_strlen($sToken)==self::TOKEN_LENGTH && $this->generateToken($iUserId)==$sToken && $this->checkValue(MySqlManager::USER_TABLE, $iUserId))?$iUserId:0;
-	}
-	
-	public function resetNecessary($iUserId)
-	{
-		$sLed = mb_substr($this->oMySql->selectValue(MySqlManager::USER_TABLE, 'led', $iUserId), 0, 10);
-		return $sLed != date(Databap::DATE_SQL_FORMAT);
-	}
-	
-	public function resetPassword()
-	{
-		$iUserId = $this->getUserId();
-		$sNewPass = self::getCookiePass();
-		$iTimeLimit = time()+60*60*24*self::COOKIE_LIFETIME;
-		$this->oMySql->updateRow(MySqlManager::USER_TABLE, $iUserId, array('pass'=>$sNewPass));
-		setcookie(self::USER_COOKIE_ID, $iUserId, $iTimeLimit);
-		setcookie(self::USER_COOKIE_PASS, $sNewPass, $iTimeLimit);
-	}
-
-	public function logMeOut()
-	{
-		$this->disconnectChat();
-		$this->setUserId(0);
-		setcookie(self::USER_COOKIE_ID, '', time()-60*60);
-		setcookie(self::USER_COOKIE_PASS, '', time()-60*60);
-	}
-
-	/* Not needed so far
-	public function setExpectedPage($sExpectedPage)
-	{
-		setcookie(self::EXPECTED_PAGE_COOKIE, $sExpectedPage, time()+60*60);
-	}
-
-	public function redirectExpectedPage()
-	{
-		if(array_key_exists(self::EXPECTED_PAGE_COOKIE, $_COOKIE))
-		{
-			$sLocation = $_COOKIE[self::EXPECTED_PAGE_COOKIE];
-			setcookie(self::EXPECTED_PAGE_COOKIE, '', time()-60*60);
-			header('Location:'.$sLocation);
-		}
-	}
-	*/
-	
-	public function redirectArticle($iArtId)
-	{
-		$asArtInfo = $this->getArticleInfo($iArtId);
-		header('Location:'.$asArtInfo['link_art']);
-	}
-	
-	public function disconnectChat()
-	{
-		$sTime = $this->oMySql->selectRows(array('select'=>'DATE_SUB(NOW(), INTERVAL '.self::KEEP_ALIVE.' SECOND)'));
-
-		//Is the user connected?
-		$sUserIdColName = MySqlManager::getId(MySqlManager::USER_TABLE);
-		$bConnected = $this->oMySql->selectRows(array('select'=>array('COUNT(1)'), 'from'=>MySqlManager::CONN_TABLE, 'constraint'=>array($sUserIdColName=>$this->getUserId(), 'led'=>$sTime), 'constOpe'=>array($sUserIdColName=>'=', 'led'=>'>')));
-		if($bConnected)
-		{
-			$this->oMySql->updateRows(MySqlManager::CONN_TABLE, array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->getUserId()), array('led'=>$sTime));
-			$this->addMessage('se déconnecte', self::MESSAGE_CONN, self::ALL_CHAN_ID);
-		}
-	}
-
-	public function checkValue($sTableName, $asConstraints)
-	{
-		return $this->oMySql->selectValue($sTableName, 'COUNT(1)', $asConstraints);
-	}
-
-	private static function getCookiePass()
-	{
-		return self::encryptPassword(	$_SERVER['HTTP_USER_AGENT'].
-		$_SERVER['REMOTE_ADDR'].
-		$_SERVER['REQUEST_TIME'].
-		mb_strstr(microtime(), ' ', true).
-		$_SERVER['SERVER_SIGNATURE'].
-		$_SERVER['SERVER_ADMIN']);
-	}
-
-	private static function encryptPassword($sPass)
-	{
-		$sRandomText = 'F_RA-1H"2{bvj)5f?0sd3r#fP,K]U|w}hGiN@(sZ.sDe!7*x/:Mq+&';
-		for($iIndex=0; $iIndex < mb_strlen($sPass); $iIndex++)
-		{
-			$sPass[$iIndex] = $sRandomText[$iIndex%mb_strlen($sRandomText)] ^ $sPass[$iIndex];
-		}
-		return md5($sPass);
-	}
-	
-	public function resetChanSafeNames()
-	{
-		$iChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE);
-		$asChans = $this->oMySql->selectRows(array('select'=>array($iChanIdCol, MySqlManager::getText(MySqlManager::CHAN_TABLE)), 'from'=>MySqlManager::CHAN_TABLE), true, $iChanIdCol);
-		
-		$asResult = array();
-		foreach($asChans as $iChanId=>$sChanName)
-		{
-			$asResult[$iChanId] = ($this->oMySql->updateRow(MySqlManager::CHAN_TABLE, $iChanId, array('safe_name'=>self::getChanSafeName($sChanName))) > 0)?'Fixed':'Not Fixed';
-		}
-		return MySqlManager::implodeAll($asResult, ' : ', "\n", 'Result reset ID channel ', '.');
-	}
-	
-	public static function getChanSafeName($sChanName)
-	{
-		//TODO replace unsafe chars with unique id
-		$sChanSafeName = preg_replace('/[^a-z0-9]/u', '_', mb_strtolower($sChanName));
-		
-		//Sort PM chans
-		$asPm = self::isPmChan($sChanSafeName);
-		if(!$asPm['is_pm']) $sChanSafeName = $asPm['chan_name'];
-		
-		return $sChanSafeName;
-	}
-
-	public function getResults($sSearchWords)
-	{
-		$this->oSearchEngine->setWords($sSearchWords);
-		$asResults = $this->oSearchEngine->getResults();
-		
-		//complementary infos
-		$asCompleteResults = array();
-		foreach($asResults as $iCodeId=>$asCodeInfo)
-		{
-			//phrase
-			$sPhrase = $this->getPhraseFromIdCode($asCodeInfo['id_code']);
-			if($sPhrase !== false)
-			{
-				$asCodeInfo['phrase'] = $sPhrase; 
-			}
-			$asCompleteResults[$iCodeId] = $asCodeInfo;
-		}
-		
-		return $this->jsonExport($asCompleteResults);
-	}
-	
-	public function getStyleSheet()
-	{
-		$sStyle = file_get_contents(self::STYLE_PATH);
-		$asDefaultColors = array(	self::OPT_BG=>'#04357B',
-									self::OPT_BRIGHT_BG=>'#D9E5F2',
-									self::OPT_HOVER=>'#EFAB00',
-									self::OPT_IMAGE_CHAT=>'images/sap_gold_332.jpg');
-		
-		//Inserting Color Ids
-		foreach($asDefaultColors as $iOptionNameId=>$sDefaultColor)							
-		{							
-			$asColorAnchors[$iOptionNameId] = '[OPT#'.$iOptionNameId.']';
-		}
-		$sStyle = str_replace($asDefaultColors, $asColorAnchors, $sStyle);
-		
-		//Switching color Ids with user colors
-		$asOptionvalues = $this->getUserOptions(array_keys($asDefaultColors));
-		foreach($asColorAnchors as $iOptionNameId=>$sColorAnchor)
-		{
-			$sOptionValue = (array_key_exists($iOptionNameId, $asOptionvalues) && $asOptionvalues[$iOptionNameId]['option']!='')?$asOptionvalues[$iOptionNameId]['option']:$asDefaultColors[$iOptionNameId];
-			$sStyle = str_replace($sColorAnchor, $sOptionValue, $sStyle);
-		}
-		
-		//setting header content type
-		header("Content-Type: text/css");
-		return $sStyle;
-	}
-
-	public static function getDateFormat($oTime, $sFormat=Databap::DATE_TIME_FORMAT)
-	{
-		$iTimeStamp = is_numeric($oTime)?$oTime:strtotime($oTime);
-		return date($sFormat, $iTimeStamp);
-	}
-	
-	public static function getNameFormat($sFirstName, $sLastName)
-	{
-		return ToolBox::capitalizeWords(trim($sFirstName.' '.$sLastName), ' -');;
-	}
-	
-	public static function getNickNameFormat($sNickName)
-	{
-		$sNickName = ToolBox::capitalizeWords(trim($sNickName), ' -');
-		return str_replace(' ', '_', $sNickName);
-	}
-
-	public static function getCompanyFormat($sCompany)
-	{
-		return ToolBox::mb_ucwords($sCompany);
-	}
-	
-	public static function getDescriptionFormat($sDescription)
-	{
-		return ucfirst($sDescription);
-	}
-	
-	public static function jsonExport($asData)
-	{
-		header('Content-type: application/json');
-		return self::jsonConvert($asData);
-	}
-	
-	public static function jsonConvert($asData)
-	{
-		//return htmlspecialchars(json_encode($asData), ENT_NOQUOTES);
-		return json_encode($asData);
-	}
-	
-	public static function getMaxSize()
-	{
-		$iPostSize = self::toBytes(ini_get('post_max_size'));
-		$iUploadSize = self::toBytes(ini_get('upload_max_filesize'));
-		
-		return min($iPostSize, $iUploadSize);
-	}
-	
-	public static function toBytes($str)
-	{
-		$val = trim($str);
-		$last = mb_strtolower($str[mb_strlen($str)-1]);
-		switch($last) {
-			case 'g': $val *= 1024;
-			case 'm': $val *= 1024;
-			case 'k': $val *= 1024;		
-		}
-		return $val;
-	}
-}
-
-/**
- * TODO
- * Code Manager
- * @author franzz
- */
-class Code extends PhpObject
-{
-	//Objects
-	private $oMySql;
-	
-	//Database tables and columns
-	private $sProcTable;
-	
-	
-	function __construct($oMySql)
-	{
-		parent::__construct();
-		$this->oMySql = $oMySql;
-	}
-}
-
-/**
- * Procedure Manager
- * @author franzz
- */
-class Procedure extends PhpObject
-{
-	//Constants
-	const THUMB_MAX_SIZE = 100;
-	const IMAGE_FOLDER = 'screenshot/';
-	const IMAGE_FOLDER_TMP = 'screenshot/tmp/';
-	const IMAGE_FOLDER_THUMB = 'screenshot/thumbnails/';
-	
-	//Objects
-	private $oMySql;
-
-	//Procedure Elements
-	private $iProcId;
-	private $iUserId;
-	private $sTitle;
-	private $sDescription;
-	private $dLed;
-	private $asSteps; // [id_step]=>text
-	private $asImages; // [id_step][id_image]=>array('name'=>file name, 'desc'=> description)
-	
-	function __construct($oMySql)
-	{
-		parent::__construct();
-		$this->oMySql = $oMySql;
-		$this->iPrevProcId = 0;
-		$this->setProcedureId(0);
-		$this->sTitle = '';
-		$this->sDescription = '';
-		$this->dLed = '';
-		$this->asSteps = array();
-		$this->asImages = array();
-	}
-	
-	public function getProcedureId()
-	{
-		return $this->iProcId;
-	}
-	
-	public function getProcedureLed()
-	{
-		return $this->dLed;
-	}
-	
-	public function setProcedureId($iProcId)
-	{
-		$this->iProcId = $iProcId;
-	}
-	
-	public function inputForm($asPost)
-	{
-		//User
-		$this->iUserId = $asPost['id_user'];
-		
-		//Titles
-		$this->sTitle = isset($asPost['title'])?$asPost['title']:'';
-		$this->sDescription = isset($asPost['description'])?$asPost['description']:'';
-		
-		//Steps
-		$sStepPattern = '/c(?P\d+)_step_text/u';
-		$sImagePattern = '/c(?P\d+)_(?P\d+)_image_(?P\w+)/u';
-		foreach($asPost as $sFormId=>$sValue)
-		{
-			//Step Text
-			preg_match($sStepPattern, $sFormId, $asMatches);
-			if(isset($asMatches['step_id']))
-			{
-				$this->asSteps[$asMatches['step_id']] = $sValue;
-			}
-			else
-			{
-				preg_match($sImagePattern, $sFormId, $asMatches);
-				if
-				(
-					isset($asMatches['step_id'], $asMatches['image_id'], $asMatches['image_info']) &&
-					($asMatches['image_info'] == 'name' || $asMatches['image_info']== 'desc')
-				)
-				{
-					$this->asImages[$asMatches['step_id']][$asMatches['image_id']][$asMatches['image_info']] = $sValue;					
-				}
-			}
-		}
-	}
-	
-	private function isImageSaved($sFileName, $bInTemp=false)
-	{
-		$sPath = $bInTemp?self::IMAGE_FOLDER_TMP:self::IMAGE_FOLDER;
-		return file_exists($sPath.$sFileName);
-	}
-	
-	public function checkIntegrity()
-	{
-		$asErrors = array();
-		
-		//Check title
-		if($this->sTitle=='') $asErrors['title'] = 'Un titre est nécessaire';
-		
-		//Check steps
-		if(count($this->asSteps)==0) 
-		{
-			$asErrors['step'] = 'Au moins une étape est nécessaire';
-		}
-		else
-		{
-			foreach($this->asSteps as $iStepId=>$sText)
-			{
-				if($sText=='')
-				{
-					$asErrors['step_'.$iStepId] = 'Une étape ne possède pas de texte';
-				}
-			}
-		}
-		
-		//Check images
-		foreach($this->asImages as $iStepId=>$asImages)
-		{
-			foreach($asImages as $iImageId=>$asFileInfo)
-			{
-				//file exists in temp or already stored
-				if(!$this->isImageSaved($asFileInfo['name']) && !$this->isImageSaved($asFileInfo['name'], true))
-				{
-					$asErrors['image_'.$iStepId.'_'.$iImageId] = 'Une image n\'a pas été téléchargée';
-				}
-				if($asFileInfo['desc']=='')
-				{
-					$asErrors['image_'.$iStepId.'_'.$iImageId] = 'Une image n\'a pas de nom (description)';
-				}
-			}
-		}
-		return $asErrors;
-	}
-	
-	public function saveProcedure($iPrevProcId)
-	{
-		//Add new procedure
-		$asData = array('title'=>$this->sTitle, 'description'=>$this->sDescription, MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->iUserId);
-		$iDbProcId = $this->oMySql->insertRow(MySqlManager::PROC_TABLE, $asData);
-		
-		//Add new steps
-		$asStepData = $asImgData = array(MySqlManager::getId(MySqlManager::PROC_TABLE)=>$iDbProcId);
-		foreach($this->asSteps as $iStepId=>$sStepDesc)
-		{
-			$asStepData['description'] = $sStepDesc;
-			$iDbStepId = $this->oMySql->insertRow(MySqlManager::STEP_TABLE, $asStepData);
-			
-			//Add new images
-			$asImgData[MySqlManager::getId(MySqlManager::STEP_TABLE)] = $iDbStepId;
-			foreach($this->asImages[$iStepId] as $asImageInfo)
-			{
-				$asImgData['description'] = $asImageInfo['desc'];
-				$asImgData['file_name'] = $asImageInfo['name'];
-				$iDbImageId = $this->oMySql->insertRow(MySqlManager::IMG_TABLE, $asImgData);
-				$this->saveImage($asImgData['file_name']);
-			}
-		}
-		
-		//Add this procedure to the group
-		$bCreation = ($iPrevProcId==0);
-		//$this->oMySql->deleteRow(MySqlManager::PROC_TABLE, $this->iPrevProcId);
-		//$this->oMySql->updateRow(MySqlManager::PROC_TABLE, $iPrevProcId, array('refer_id'=>$iDbProcId));
-		$iReferId = $bCreation?$iDbProcId:$this->oMySql->selectValue(MySqlManager::PROC_TABLE, 'refer_id', $iPrevProcId);
-		$this->oMySql->updateRow(MySqlManager::PROC_TABLE, $iDbProcId, array('refer_id'=>$iReferId));
-		
-		//Update new Procedure Id
-		//$this->setProcedureId($iDbProcId);
-		
-		//Reload Procedure
-		$this->loadProcedure($iDbProcId);
-		
-		//return creation or edition
-		return $bCreation;
-	}
-	
-	private function saveImage($sFileName)
-	{
-		if(!$this->isImageSaved($sFileName))
-		{
-			$sTempFilePath = self::IMAGE_FOLDER_TMP.$sFileName;
-			$sFilePath = self::IMAGE_FOLDER.$sFileName;
-			
-			//Real Size Image
-			if(!rename($sTempFilePath, $sFilePath))
-			{
-				$this->addError('Unmoveable file : '.$sTempFilePath);
-			}
-			
-			//Thumbnail picture
-			$asResult = ToolBox::createThumbnail($sFilePath, self::THUMB_MAX_SIZE, self::THUMB_MAX_SIZE, self::IMAGE_FOLDER_THUMB);
-			if($asResult['error']!='') $this->addError('Unable to create thumbnail : '.$asResult['out']. '('.$asResult['error'].')');
-		}
-	}
-	
-	public function loadProcedure($iProcId)
-	{
-		//Column names
-		$sProcIdCol = MySqlManager::getId(MySqlManager::PROC_TABLE);
-		$sStepIdCol = MySqlManager::getId(MySqlManager::STEP_TABLE);
-		$sImageIdCol = MySqlManager::getId(MySqlManager::IMG_TABLE);
-		
-		//Get last id available
-		$this->setProcedureId($iProcId);
-		$this->refreshProcId();
-		$asInfo = array('from'=>MySqlManager::PROC_TABLE, 'constraint'=>array($sProcIdCol=>$this->getProcedureId()));
-		
-		//Load procedure info
-		$asResult = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $asInfo['constraint']);
-		$this->sTitle = $asResult['title'];
-		$this->sDescription = $asResult['description'];
-		$this->iUserId = $asResult[MySqlManager::getId(MySqlManager::USER_TABLE)];
-		$this->dLed = $asResult['led'];
-		
-		//Load steps info
-		$asInfo['from'] = MySqlManager::STEP_TABLE;
-		$this->asSteps = $this->oMySql->selectRows($asInfo, true, $sStepIdCol);
-		
-		//Load images info
-		$asInfo['from'] = MySqlManager::IMG_TABLE;
-		foreach($this->oMySql->selectRows($asInfo) as $asRow)
-		{
-			$this->asImages[$asRow[$sStepIdCol]][$asRow[$sImageIdCol]]['name'] = $asRow['file_name'];
-			$this->asImages[$asRow[$sStepIdCol]][$asRow[$sImageIdCol]]['desc'] = $asRow['description'];
-		}
-	}
-	
-	public function getProcedure()
-	{
-		// [id_step]=>text
-		// [id_step][id_image]=>array('name'=>file name, 'desc'=> description)
-		/*
-		$asSteps = $asImages = array();
-		$iLocalStepId = 1;
-		foreach($this->asSteps as $iStepId=>$sStepDesc)
-		{
-			$asSteps[$iLocalStepId] = $sStepDesc;
-			if(array_key_exists($iStepId, $this->asImages))
-			{
-				$asImages[] = 
-			}
-			$iLocalStepId++;
-		}*/
-		return array(	'proc_id'=>$this->iProcId,
-						'id_user'=>$this->iUserId,
-						'title'=>Databap::getDescriptionFormat($this->sTitle), 
-						'description'=>Databap::getDescriptionFormat($this->sDescription),
-						'led'=>Databap::getDateFormat($this->dLed),
-						'steps'=>$this->asSteps,
-						'images'=>$this->asImages);
-	}
-	
-	private function refreshProcId()
-	{
-		/*
-		$iReferId = $this->getProcedureId();
-		do
-		{
-			$iProcId = $iReferId;
-			$iReferId = $this->oMySql->selectValue(MySqlManager::PROC_TABLE, 'refer_id', $iProcId);
-		}
-		while($iReferId!=0);
-		$this->setProcedureId($iProcId);
-		*/
-		$iReferId = $this->oMySql->selectValue(MySqlManager::PROC_TABLE, 'refer_id', $this->getProcedureId());
-		$sSelect = "MAX(".MySqlManager::getId(MySqlManager::PROC_TABLE).")";
-		$iProcId = $this->oMySql->selectValue(MySqlManager::PROC_TABLE, $sSelect, array('refer_id'=>$iReferId));
-		$this->setProcedureId($iProcId);
-	}
-}
-
-/* if feeling the need of creating a tables for code lines :
- codes :
- ALTER TABLE codes ADD line_low int(10) AFTER code;
- ALTER TABLE codes ADD line_high int(10) AFTER line_low;
-
- lines :
- id_line
- id_code
- line
- */
-
-/**
- * Search Engine : Search through all types of databap documents
- * 
- * Procedure to add a new type in the search
- * - Add type in the index builder : SearchEngine->buildIndex()
- * - Add type in the rebuild index function : Databap->buildCompleteIndex()
- * - Add type in item info : SearchEngine->setItemInfo()
- * - Add type in .htaccess
- * - Add type in getItemList() and create a getinfo()
- * - consider the message types : add + edit in Databap->getMessages() and in chat.html
- */
-class SearchEngine extends PhpObject
-{
-	//Objects
-	private $oMySql;
-
-	//variables
-	private $asWords;
-	private $iLevelMax;
-	private $asItemRanks;
-	private $asItemInfos;
-	private $asUserInfos;
-
-	//Constants
-	const RESULT_A_PAGE = 20;
-	const KEYWORDS_SEPARATOR = ' ';
-
-	function __construct($oMySql)
-	{
-		parent::__construct();
-		$this->oMySql = $oMySql;
-		$this->asWords = $this->setWords('');
-		$this->asItemRanks = array();
-		$this->asItemInfos = array();
-		$this->asUserInfos = array();
-	}
-	
-	public function buildIndex($iItemId, $sType)
-	{
-		//Build keywords
-		switch($sType)
-		{
-			case Databap::CODE_TYPE:
-				$asItemData = $this->oMySql->selectRow(MySqlManager::CODE_TABLE, $iItemId);
-				$asWords = array($asItemData[MySqlManager::getText(MySqlManager::CODE_TABLE)], $asItemData['description']);
-				break;
-			case Databap::PROC_TYPE:
-				$sItemIdCol = MySqlManager::getId(MySqlManager::PROC_TABLE);
-				$asItemData = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $iItemId);
-				$asItemStepsData = $this->oMySql->selectRows(array('select'=>'description', 'from'=>MySqlManager::STEP_TABLE, 'constraint'=>array($sItemIdCol=>$iItemId)));	
-				$asWords = array('desc'=>$asItemData['description'], 'title'=>$asItemData['title']) + $asItemStepsData;
-				break;
-			case Databap::ART_TYPE:
-				$asItemData = $this->oMySql->selectRow(MySqlManager::ART_TABLE, $iItemId);
-				$asWords = array($asItemData['first_name'], $asItemData['last_name'], $asItemData['title']);
-				break;
-			case Databap::DOC_TYPE:
-				$sItemIdCol = MySqlManager::getId(MySqlManager::DOC_TABLE);
-				$asItemData = $this->oMySql->selectRow(MySqlManager::DOC_TABLE, $iItemId);
-				$asItemFilesData = $this->oMySql->selectRows(array('select'=>'description', 'from'=>MySqlManager::FILE_TABLE, 'constraint'=>array($sItemIdCol=>$iItemId)));	
-				$asWords = array('desc'=>$asItemData['description'], 'title'=>$asItemData['title']) + $asItemFilesData;
-				break;
-			default:
-				$this->addError('function '.__FUNCTION__.'(): Incorrect type "'.$sType.'"');
-				break;
-		}
-		$sWords = implode(self::KEYWORDS_SEPARATOR, $asWords);
-		$sWords = mb_strtolower(str_replace("\n", self::KEYWORDS_SEPARATOR, $sWords));
-		$sWords = preg_replace('/(\W+)/u', self::KEYWORDS_SEPARATOR, $sWords); //remove all non-word characters
-		
-		//Add / Modify search database
-		$asData = array('id_item'=>$iItemId, 'type'=>$sType, 'refer_id'=>(array_key_exists('refer_id', $asItemData)?$asItemData['refer_id']:0), 'keywords'=>$sWords);
-		$this->oMySql->insertUpdateRow(MySqlManager::SEARCH_TABLE, $asData, array('id_item', 'type'));
-	}
-
-	public function setWords($sSearchWords)
-	{
-		$this->asWords = $this->getParsedWords($sSearchWords);
-		$this->iLevelMax = count($this->asWords);
-		$this->setResults();
-	}
-	
-	/**
-	 * TODO Customized item preview
-		$sCodeLines = implode("\n...\n", $this->getCodeInfo($iItemId, 'code'));
-		$oCode = new Reader($sCodeLines);
-		$sCodeLines = $oCode->getColoredCode();
-	 */
-	private function setItemInfo($iSearchId, $iItemType, $iItemId)
-	{
-		if(!array_key_exists($iSearchId, $this->asItemInfos))
-		{
-			$this->asItemInfos[$iSearchId] = array('type'=>$iItemType, 'id_item'=>$iItemId);
-			switch($iItemType)
-			{
-				case Databap::CODE_TYPE:
-					$sItemTable = MySqlManager::CODE_TABLE;
-					$asItemFields = array(MySqlManager::getId(MySqlManager::USER_TABLE), 'description', 'led');
-					break;
-				case Databap::PROC_TYPE:
-					$sItemTable = MySqlManager::PROC_TABLE;
-					$asItemFields = array(MySqlManager::getId(MySqlManager::USER_TABLE), 'title AS description', 'led');
-					break;
-				case Databap::ART_TYPE:
-					$sItemTable = MySqlManager::ART_TABLE;
-					$asItemFields = array('first_name', 'last_name', 'title AS description', 'led');
-					break;
-				case Databap::DOC_TYPE:
-					$sItemTable = MySqlManager::DOC_TABLE;
-					$asItemFields = array(MySqlManager::getId(MySqlManager::USER_TABLE), 'title AS description', 'led');
-					break;
-			}
-			$this->asItemInfos[$iSearchId] += $this->oMySql->selectRow($sItemTable, $iItemId, $asItemFields);
-		}
-	}
-	
-	private function getItemInfo($iSearchId, $sInfoName)
-	{
-		if(array_key_exists($iSearchId, $this->asItemInfos) && array_key_exists($sInfoName, $this->asItemInfos[$iSearchId]))
-			return $this->asItemInfos[$iSearchId][$sInfoName];
-		else return false;		
-	}
-	
-	private function setUserInfo($iUserId)
-	{
-		if(!array_key_exists($iUserId, $this->asUserInfos) && $iUserId > 0)
-		{
-			$sCompanyIdCol = MySqlManager::getId(MySqlManager::COMP_TABLE);
-			$this->asUserInfos[$iUserId] = $this->oMySql->selectRow
-			(
-					MySqlManager::USER_TABLE,
-					$iUserId,
-					array('first_name', 'last_name', $sCompanyIdCol)
-			);
-				
-			$this->asUserInfos[$iUserId]['company'] = $this->oMySql->selectValue(MySqlManager::COMP_TABLE, MySqlManager::getText(MySqlManager::COMP_TABLE), $this->asUserInfos[$iUserId][$sCompanyIdCol]);
-			unset($this->asUserInfos[$iUserId][$sCompanyIdCol]);
-		}
-	}
-	
-	private function getUserInfo($iUserId, $sInfoName)
-	{
-		if(array_key_exists($iUserId, $this->asUserInfos) && array_key_exists($sInfoName, $this->asUserInfos[$iUserId]))
-			return $this->asUserInfos[$iUserId][$sInfoName];
-		else return false;
-	}
-
-	private function setResults()
-	{
-		if($this->iLevelMax > 0)
-		{
-			//set Results and Ranking
-			$aiLevels = range(1, $this->iLevelMax);
-			arsort($aiLevels);
-			foreach($aiLevels as $iLevel)
-			{
-				//all possibilies at level $iLevel
-				$iIndex = 0;
-				while(($iIndex + $iLevel) <= $this->iLevelMax)
-				{
-					//building query
-					$asSequence = array_slice($this->asWords, $iIndex, $iLevel);
-					$this->oMySql->cleanSql($asSequence);
-					
-					//$sRegExp = implode('(.{0,2})', $asSequence);
-					$sRegExp = implode(self::KEYWORDS_SEPARATOR.'?', $asSequence);
-					$sSequence = implode(self::KEYWORDS_SEPARATOR, $asSequence);
-					
-					//TODO replace with selectRow()
-					$sQuery = "SELECT id_search, id_item, type, keywords FROM searchs WHERE keywords REGEXP '{$sRegExp}'";
-					
-					//search sequence
-					$asItems = $this->oMySql->getArrayQuery($sQuery, true);
-					foreach($asItems as $asItem)
-					{
-						$iSearchId = $asItem['id_search']; 
-						$iItemId = $asItem['id_item'];;
-						$iItemType = $asItem['type'];
-						$sWords = $asItem['keywords'];
-						
-						//Calculte bonus points
-						$sWords = str_replace(self::KEYWORDS_SEPARATOR.$sSequence.self::KEYWORDS_SEPARATOR,	self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iSeqCount);
-						$sWords = str_replace(self::KEYWORDS_SEPARATOR.$sSequence,							self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iStaCount);
-						$sWords = str_replace($sSequence.self::KEYWORDS_SEPARATOR,							self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iEndCount);
-						$iBonus = $iSeqCount*5 + $iStaCount*2 + $iEndCount;
-						
-						$this->incItemRank($iSearchId, $iLevel*10+$iBonus);
-						$this->setItemInfo($iSearchId, $iItemType, $iItemId);
-						$this->setUserInfo($this->getItemInfo($iSearchId, MySqlManager::getId(MySqlManager::USER_TABLE)));
-					}
-					$iIndex++;
-				}
-			}
-		}
-	}
-	
-	public function getResults()
-	{
-		$asResult = array();
-		
-		//Mixing info
-		arsort($this->asItemRanks);
-		foreach($this->asItemRanks as $iSearchId=>$iRank)
-		{
-			$iUserId = $this->getItemInfo($iSearchId, MySqlManager::getId(MySqlManager::USER_TABLE));
-			$sFirstName = $this->getUserInfo($iUserId, 'first_name')?$this->getUserInfo($iUserId, 'first_name'):$this->getItemInfo($iSearchId, 'first_name');
-			$sLastName = $this->getUserInfo($iUserId, 'last_name')?$this->getUserInfo($iUserId, 'last_name'):$this->getItemInfo($iSearchId, 'last_name');
-			$sCompany = $this->getUserInfo($iUserId, 'company')?$this->getUserInfo($iUserId, 'company'):'SAP';
-			$asResult[] = array('id_item'=>$this->getItemInfo($iSearchId, 'id_item'),
-								'type'=>$this->getItemInfo($iSearchId, 'type'),
-								'description'=>/*'['.$iRank.'] '.*/$this->getItemInfo($iSearchId, 'description'),
-								'name'=>Databap::getNameFormat($sFirstName, $sLastName),
-								'company'=>Databap::getCompanyFormat($sCompany),
-								'led'=>Databap::getDateFormat($this->getItemInfo($iSearchId, 'led')));
-		}
-		return $asResult;
-	}
-
-	private function incItemRank($iSearchId, $iRank)
-	{
-		if(array_key_exists($iSearchId, $this->asItemRanks))
-		{
-			$this->asItemRanks[$iSearchId] += $iRank;
-		}
-		else
-		{
-			$this->asItemRanks[$iSearchId] = $iRank;
-		}
-	}
-
-	private function getParsedWords($sSearchWords)
-	{
-		return array_unique(array_filter(explode(' ', $sSearchWords), array($this, 'checkSearchedWords')));
-	}
-
-	private function checkSearchedWords($sWord)
-	{
-		return (mb_strlen($sWord) >= 2);
-	}
-}
-
-/**
- * Abap Reader
- * @author franzz
- */
-class Reader
-{
-	//objects
-	//private $oMask;
-
-	//general
-	private $sCode;
-	private $bImprovedColoring;
-
-	//Special Wors / Chars
-	private $asAbap;
-
-	//constants
-	const CASE_SENSITIVE = false;
-	const NOT_FOUND = '* Code introuvable...';
-
-	function __construct($sCode, $bImprovedColoring = false)
-	{
-		//settings
-		$this->bImprovedColoring = $bImprovedColoring;
-
-		//get code in db
-		$this->sCode = $sCode;
-		if($this->sCode=='')
-		{
-			$this->sCode = self::NOT_FOUND;
-		}
-
-		//ABAP key words and chars
-		$this->asAbap = array
-		(
-			'word'=>array
-			(
-				'wCore'=>array(	/*'<icon>','<itab>',*/'ABBREVIATED','ABSTRACT','ABSTRACTFINAL','ACCEPT','ACCEPTING','ACCORDING','ACTUAL','ADD',
-								'ADD-CORRESPONDING','ADDITIONS','ADJACENT','ALIASES','ALL','ALLOCATE','ANALYZER', 'AND','APPEND','APPENDING','AS',
-								'ASCENDING','ASCENDINGDESCENDING','AT','ATTRIBUTE','AUTHORITY-CHECK','BACKGOUND','BEFORE','BEGIN','BETWEEN',
-								'BIGLITTLE','BINARY','BIT','BLANK','BLOCK','BREAK-POINT','BY','BYPASSING','BYTE','BYTECHARACTER','CASE',
-								'CASERESPECTING','CASTING','CENTERED','CHAIN','CHANGE','CHANGING','CHARACTER','CHECK','CHECKBOX',
-								'CHECKBOXSYMBOLICONLINE','CLASS','CLASS-DATA','CLASS-EVENTS','CLASS-METHODS','CLEANUP','CLEAR','CLIENT','CLOCK',
-								'CODE','COL_BACKGROUND','COL_HEADING','COL_NORMAL','COL_TOTAL','COLLECT','COLOR','COLUMN','COMMENT','COMMIT',
-								'COMMON','COMMUNICATION','COMPARING','COMPONENTS','COMPUTE','CONCATENATE','CONDENSE','CONSTANTS','CONTEXT',
-								'CONTEXTS','CONTINUE','CONTROL','CONTROLS','CONVERSION','CONVERT','COUNTRY','COUNTY','CURRENT','CURSOR',
-								'CUSTOMER-FUNCTION','DATA','DATABASE','DATASET','DATE','DEALLOCATE','DECIMALS','DEFAULT','DEFAULTUTF-8NON-UNICODE',
-								'DEFERRED','DEFINE','DEFINING','DEFINITION','DELETE','DELETING','DEMAND','DESCENDING','DESCRIBE','DESTINATION',
-								'DIALOG','DIRECTORY','DISTANCE','DISTINCT','DIVIDE-CORRESPONDING','DO','DUPLICATE','DUPLICATES','DURING','DYNAMIC',
-								'EDIT','EDITOR-CALL','ELSE','ELSEIF','ENCODING','END','ENDAT','ENDCASE','ENDCATCH','ENDCHAIN','ENDCLASS','ENDDO',
-								'ENDEXEC','ENDFORM','ENDFUNCTION','ENDIAN','ENDIF','ENDING','ENDINTERFACE','ENDLOOP','ENDMETHOD','ENDMODULE',
-								'ENDON','ENDPROVIDE','ENDSELECT','ENDTRY','ENDWHILE','END-OF_FILE','END-OF-DEFINITION','END-OF-PAGE',
-								'END-OF-SELECTION','ENTRIES','EQ','ERRORS','EVENT','EVENTS','EXCEPTION','EXCEPTIONS','EXCEPTION-TABLE','EXCLUDE',
-								'EXCLUDING','EXEC','EXIT','EXIT-COMMAND','EXPORT','EXPORTING','EXTENDED','EXTRACT','FETCH','FIELD','FIELD-GROUPS',
-								'FIELDSNO','FIELD-SYMBOLS','FILTER','FINAL','FIND','FIRST','FOR','FORM','FORMAT','FORWARDBACKWARD','FOUND','FRAME',
-								'FREE','FRIENDS','FROM','FUNCTION','FUNCTION-POOL','GE','GET','GIVING','GROUP','GT','HANDLER','HASHED','HAVING',
-								'HEADER','HEADING','HELP-ID','HIDE','HIGHLOW','HOLD','HOTSPOT','ID','IF','IGNORING','IMMEDIATELY','IMPLEMENTATION',
-								'IMPORT','IMPORTING','IN','INCREMENT','INDEX','INDEX-LINE','INHERITING','INIT','INITIAL','INITIALIZATION','INNER',
-								'INNERLEFT','INSERT','INSTANCES','INTENSIFIED','INTERFACE','INTERFACES','INTERVALS','INTO','INTOAPPENDING',
-								'INVERTED-DATE','IS','ITAB','JOIN','KEEPING','KEY','KEYS','KIND','LANGUAGE','LAST','LE','LEAVE','LEFT',
-								'LEFT-JUSTIFIED','LEFTRIGHT','LEFTRIGHTCIRCULAR','LEGACY','LENGTH','LIKE','LINE','LINE-COUNT','LINES','LINES',
-								'LINE-SELECTION','LINE-SIZE','LIST','LIST-PROCESSING','LOAD','LOAD-OF-PROGRAM','LOCAL','LOCALE','LOOP','LT',
-								'MARGIN','MARK','MASK','MATCH','MAXIMUM','MEMORY','MESSAGE','MESSAGE-ID','MESSAGES','METHOD','METHODS','MOD','MODE',
-								'MODEIN','MODIF','MODIFIER','MODIFY','MODULE','MOVE','MOVE-CORRESPONDING','MULTIPLY-CORRESPONDING','NE','NEW',
-								'NEW-LINE','NEW-PAGE','NEXT','NODES','NODETABLE','NO-DISPLAY','NO-EXTENSION','NO-GAP','NO-GAPS',
-								'NO-HEADINGWITH-HEADING','NON','NO-SCROLLING','NO-SCROLLINGSCROLLING','NOT','NO-TITLEWITH-TITLE','NO-ZERO','NUMBER',
-								'OBJECT','OBLIGATORY','OCCURENCE','OCCURENCES','OCCURS','OF','OFF','OFFSET','ON','ONLY','ONOFF','OPEN','OPTION',
-								'OPTIONAL','OR','ORDER','OTHERS','OUTPUT-LENGTH','OVERLAY','PACK','PACKAGE','PAGE','PAGELAST','PAGEOF','PAGEPAGE',
-								'PAGES','PARAMETER','PARAMETERS','PARAMETER-TABLE','PART','PERFORM','PERFORMING','PFN','PF-STATUS','PLACES',
-								'POS_HIGH','POS_LOW','POSITION','POSITIONS','PRIMARY','PRINT','PRINT-CONTROL','PRIVATE','PROCESS','PROGRAM',
-								'PROPERTY','PROTECTED','PROVIDE','PUBLIC','PUBLICPROTECTEDPRIVATE','PUSHBUTTON','PUT','QUICKINFO','RADIOBUTTON',
-								'RAISE','RAISING','RANGE','RANGES','READ','RECEIVE','RECEIVING','REDEFINITION','REFERENCE','REFRESH','REJECT',
-								'RENAMING','REPLACE','REPLACEMENT','REPORT','RESERVE','RESET','RESOLUTION','RESULTS','RETURN','RETURNING','RIGHT',
-								'RIGHT-JUSTIFIED','ROLLBACK','ROWS','RUN','SCAN','SCREEN','SCREEN-GROUP1','SCREEN-GROUP2','SCREEN-GROUP3',
-								'SCREEN-GROUP4','SCREEN-GROUP5','SCREEN-INPUT','SCREEN-INTENSIFIED','SCROLL','SCROLL-BOUNDARY','SEARCH','SECTION',
-								'SELECT','SELECTION','SELECTIONS','SELECTION-SCREEN','SELECTION-SET','SELECTION-TABLE','SELECT-OPTIONS','SEND',
-								'SEPARATED','SET','SHARED','SHIFT','SIGN','SINGLE','SINGLEDISTINCT','SIZE','SKIP','SORT','SORTABLE','SPACE',
-								'SPECIFIED','SPLIT','SQL','STABLE','STAMP','STANDARD','START','STARTING','START-OF-SELECTION','STATICS','STEP-LOOP',
-								'STOP','STRLEN','STRUCTURE','SUBMIT','SUBTRACT','SUBTRACT-CORRESPONDING','SUFFIX','SUPPLY','SUPPRESS','SYMBOLS',
-								'SYSTEM-EXCEPTIONS','TABLE','TABLES','TABLEVIEW','TASK','TEXT','THEN','TIME','TIMES','TITLE','TITLEBAR','TO',
-								'TOPIC','TOP-OF-PAGE','TRAILING','TRANSACTION','TRANSFER','TRANSLATE','TRY','TYPE','TYPELIKE','TYPE-POOL',
-								'TYPE-POOLS','TYPES','ULINE','UNION','UNIQUE','UNTIL','UP','UPDATE','UPPERLOWER','USER-COMMAND','USING','VALUE',
-								'VALUES','VARY','VARYING','VERSION','VIA','WAIT','WHEN','WHERE','WHILE','WINDOW','WITH','WORK','WRITE','ZONE','ADD',
-								'ADD-CORRESPONDING','APPEND','BREAK-POINT','CORRESPONDING','FIELDS','ASSIGN','AVG','BACK','CALL','CATCH','CHANGE',
-								'CHECK','CLEAR','CLOSE','COMMIT','COMPUTE','COUNT','CREATE','DATASET','DELETE','DESCRIBE','DIVIDE','DO','ELSE',
-								'ELSEIF','EXIT','FIND','FOR','FORMAT','GET','IF','INCLUDE','LOOP','MAX','MESSAGE','METHOD','MIN','MODIFY','MOVE',
-								'MULTIPLY','NEW-PAGE','OBJECT','OPEN','ORDER','READ','SORT','SUM','TRANSLATE','ULINE','WORK','WRITE','XSTRLEN',
-								'ABS','ACOS','AFTER','ALL','AND','AS','ASIN','ASSIGNING','AT','ATAN','BEGIN','BUFFER','BY','CA','CEIL','CENTERED',
-								'CN','CO','COLOR','COMPARING','COMPONENT','COS','COSH','CP','CS','DEFAULT','END','EQ','EXP','FLOOR','FRAC','GE',
-								'GROUP','GT','ICON','IN','INDEX','INNER','INTENSIFIED','INTO','IS','JOIN','LE','LEADING','LEAVE','LEFT','LIKE',
-								'LOG','LOG10','LOWER','LT','M','MOD','NA','NE','NOT','NP','NS','O','OF','OFF','ON','OR','OTHERS','OUTER','REF',
-								'RIGHT','SIGN','SIN','SINH','SQRT','TABLE','TABLENAME','TAN','TANH','TITLE','TO','TRAILING','TRUNC','TYPE','UPPER',
-								'USING','VALUE','WITH', 'TRANSPORTING', 'TYPE-POOLS'),
-				'wBwCore'=>array('SOURCE_PACKAGE', 'RESULT_PACKAGE', '', ''),
-				'cOperator'=>array('(', ')', ',', '.', ':', '-', '~', '[', ']', '<', '>'),
-				'cCalculation'=>array('+', '-', '*', '/', '=', '(', ')'),
-				'cComment'=>array('*'),
-				'cPartComment'=>array('"'),
-				'cString'=>array('"', '''),
-				'iNumber'=>array('1', '2', '3', '4', '5', '6', '7', '8', '9', '0'),
-				'wExpand'=>array('loop'=>'endloop', 'if'=>'endif'),
-				'wGlobal'=>array('SY'),
-				'wCodePart'=>array('main', 'global', 'global2')
-			),
-			'color'=>array
-			(
-				'wCore'=>'core',
-				'wBwCore'=>'core',
-				'cOperator'=>'operator',
-				'cCalculation'=>'operator',
-				'cComment'=>'comment',
-				'cPartComment'=>'comment',
-				'cString'=>'string',
-				'iNumber'=>'number',
-				'wExpand'=>'expand',
-				'wGlobal'=>'glogal',
-				'wCodePart'=>'code_part'
-			)
-		);
-	}
-
-	private function getWords($sKey)
-	{
-		return $this->asAbap['word'][$sKey];
-	}
-
-	private function getColor($sKey)
-	{
-		return $this->asAbap['color'][$sKey];
-	}
-
-	private function addColoring()
-	{
-		//Safe characters
-		$sCode = $this->convText2Html($this->sCode);
-		$sServName = $_GET['serv_name'];
-		$sSafeRexExServName = preg_quote(str_replace('http://', '', $sServName), '/');
-		
-		$asLines = explode("\n", $sCode);
-		$asLineClasses = array();
-		$iOffSet = 0;
-		
-		//building lines
-		foreach($asLines as $iLineNb=>$sLine)
-		{
-			/*	Code ref:
-			 *	
  • - * - * - - * - * #line# - *
  • - */ - $sLiClass = $sExpandSign = $sCodeClass = $sExpandHtml = $sExpandButton = ''; - $sFirstWord = $this->getFirstWord($sLine); - $sFirstChar = $this->getFirstChar($sLine); - - //Apply current classes - $sLiClass = implode(' ', $asLineClasses); - - //Expanding starting/ending point - if(in_array($sFirstWord, array_keys($this->getWords('wExpand')))) //start - { - $iLoopId = count($asLineClasses)+$iOffSet; - $asLineClasses[] = 'loop'.$iLoopId; - $sExpandButton = ''; - } - elseif(in_array($sFirstWord, $this->getWords('wExpand'))) //end - { - array_pop($asLineClasses); - $iOffSet++; - $sExpandButton = ''; - } - - //Enlarge line - if(mb_strpos($sLine, $sServName) !== false) - { - $sLiClass .= ' bigline'; - } - - //comments (entire line) - $sCodeClass = (in_array($sFirstChar, $this->getWords('cComment')))?'comment':'code'; - - $asColoredLines[] = '
  • '.$sExpandButton.''.$sLine.'
  • '; - } - $sCode = implode('', $asColoredLines); - - //Strings - foreach($this->getWords('cString') as $sStringWord) - { - $sPattern = '/>([^<]*?)'.$sStringWord.'([^<]*?)'.$sStringWord.'/u'; - $sCode = preg_replace($sPattern, '>$1'.$sStringWord.'$2'.$sStringWord.'', $sCode); - } - - //Part comment - $sPattern = '/>([^<]*)\"\;([^<]*)\<\/span\>\<\/li\>/u'; - $sCode = preg_replace($sPattern, '>$1"$2', $sCode); - - //Internal Url - //$sPattern = '/>([^<]*)'.preg_quote($_GET['serv_name'], '/').'\/r\-([^<]*)/u'; - $sPattern = '/>([^<\/]*)(http:\/\/'.$sSafeRexExServName.'|'.$sSafeRexExServName.')(c|code|p|proc|procedure)\-([^<]*)/u'; - $sCode = preg_replace($sPattern, '>$1', $sCode); - - //Global / Main - $sPattern = '/>\*\ \[('.implode('|', $this->getWords('wCodePart')).')\]/u'; - $sCode = preg_replace($sPattern, '>> $1', $sCode); - - //Core Words - foreach($this->getWords('wCore') as $sCoreWord) - { - $sCoreWord = mb_strtolower($sCoreWord); - $sPattern = '/>(([^<]*)([^\w&<]{1})|.{0})('.$sCoreWord.')([\W])/u'; - $sCode = preg_replace($sPattern, '>$1$4$5', $sCode); - } - //$sCoreWords = str_replace(' ', '\ ', implode('|', array_map('mb_strtolower', $this->getWords('wCore')))); - //$sPattern = '/>(([^<]*)([^\w<]{1})|.{0})('.$sCoreWords.')(?=[\W])/U'; - //$sCode = preg_replace($sPattern, '>$1$4', $sCode); - - //Operators - //TODO distinguer operator / calculation - $asOperators = array_unique(array_merge($this->getWords('cOperator'), $this->getWords('cCalculation'))); - foreach($asOperators as $sOpWord) - { - $sPattern = '/>([^<]*)\\'.$sOpWord.'/u'; - $sCode = preg_replace($sPattern, '>$1'.$sOpWord.'', $sCode); - } - //$sPattern = '/>([^<]*)['.implode(array_map('mb_strtolower', ($this->getWords('cOperator')))).']/'; - //echo $sPattern; - //$sCode = preg_replace($sPattern, '>$1$2$3', $sCode); - - //Numbers - $sPreChar = '\\'.implode('\\', $this->getWords('cCalculation')).'\s'; - $sPattern = '/>((([^<]*)['.$sPreChar.']{1})|.{0})([0-9]+)([\W])/u'; - $sCode = preg_replace($sPattern, '>$1$4$5', $sCode); - - return $sCode; - } - - private static function getFirstWord($sText) - { - return mb_strstr(str_replace('.', ' ', trim($sText)), ' ', true); - } - - private static function getFirstChar($sText) - { - return mb_substr(str_replace('.', ' ', trim($sText)), 0, 1); - } - - private function addColor(&$sText, $sWord, $sClassColor) - { - $sText = str_replace($sWord, ''.$sWord.'', $sText); - } - - private static function getColoredWord($sWord, $sClass) - { - return ''.$sWord.''; - } - - public static function convText2Html($sCode) - { - return htmlspecialchars(mb_strtolower($sCode), ENT_QUOTES); - } - - public function getColoredCode() - { - return $this->addColoring(); - } -} - -/** - * Mask Reader - * @author franzz - * - */ -class Mask extends PhpObject -{ - public $sMaskName; - public $sFilePath; - private $sMask; - private $asTags; - private $asPartsSource; - private $aoInstances; - - const MASK_FOLDER = 'masks/'; - const START_TAG = 'START'; - const END_TAG = 'END'; - const TAG_MARK = '#'; - - public function __construct($sFileName='') - { - //init - parent::__construct(); - $this->sMaskName = ''; - $this->sFilePath = ''; - $this->sMask = ''; - $this->asTags = array(); - $this->asPartsSource = array(); - $this->aoInstances = array(); - $this->sFilePath = ''; - - //load file - if($sFileName!='') - { - $this->initFile($sFileName); - } - } - - public function initFile($sFileName) - { - $sFilePath = self::MASK_FOLDER.basename($sFileName).'.html'; - if(file_exists($sFilePath)) - { - $this->sFilePath = $sFilePath; - $sSource = file_get_contents($this->sFilePath); - $this->initMask($sFileName, $sSource); - } - else - { - $this->addError('Fichier introuvable à l\'adresse : '.$sFilePath); - } - } - - public function initFileFromString($sSource, $sPartName='', $iInstanceNb=0) - { - $this->initMask($sPartName.' (from row) '.$iInstanceNb, $sSource); - } - - private function initMask($sMaskName, $sSource) - { - $this->sMaskName = $sMaskName; - $this->sMask = $sSource; - $this->setParts(); - } - - private function setParts() - { - while(preg_match('/\<\!-- \[PART\] (?P\S+) \[START\] --\>/u', $this->sMask, $asMatch)) - { - $sPartName = $asMatch['part']; - - $this->asPartsSource[$sPartName] = $this->getCleanPart($sPartName); - $this->aoInstances[$sPartName] = array(); - } - } - - private function getCleanPart($sPartName) - { - $iStartPos = $this->getPartStartPos($sPartName); - $iEndPos = $this->getPartEndPos($sPartName); - $sPart = mb_substr($this->sMask, $iStartPos, $iEndPos-$iStartPos); - $sExtendedPart = $this->getPartPattern($sPartName, self::START_TAG).$sPart. $this->getPartPattern($sPartName, self::END_TAG); - $this->sMask = str_replace($sExtendedPart, $this->getPartTagPattern($sPartName), $this->sMask); - return $sPart; - } - - private function getPartStartPos($sPartName) - { - $sPartStartPattern = $this->getPartPattern($sPartName, self::START_TAG); - return mb_strpos($this->sMask, $sPartStartPattern) + mb_strlen($sPartStartPattern); - } - - private function getPartEndPos($sPartName) - { - $sPartEndPattern = $this->getPartPattern($sPartName, self::END_TAG); - return mb_strpos($this->sMask, $sPartEndPattern); - } - - private function getPartPattern($sPartName, $sAction) - { - return ''; - } - - private function getPartTagPattern($sPartName, $bMark=true) - { - $sPartTag = 'PART '.$sPartName; - return $bMark?$this->addTagMark($sPartTag):$sPartTag; - } - - public function addInstance($sPartName, $asTags) - { - $this->newInstance($sPartName); - foreach($asTags as $sTagName=>$sTagValue) - { - $this->setInstanceTag($sPartName, $sTagName, $sTagValue); - } - } - - public function newInstance($sPartName) - { - //Finding the part - $oMask = &$this->findPart($this, $sPartName); - - //Retrieving source html - $sPartSource = $oMask->asPartsSource[$sPartName]; - - //Creating new instance - $oInstance = new Mask(); - $oInstance->initFileFromString($sPartSource, $sPartName); - $oMask->aoInstances[$sPartName][] = $oInstance; - } - - public function setInstanceTag($sPartName, $sTagName, $sTagValue) - { - $oMask = &$this->findPart($this, $sPartName); - $oMask->getCurrentInstance($sPartName)->setTag($sTagName, $sTagValue); - } - - private function &findPart($oMask, $sPartName) - { - if(array_key_exists($sPartName, $oMask->aoInstances)) - { - return $oMask; - } - else //not tested - { - foreach($oMask->aoInstances as $sLevelPartName=>$aoInstances) - { - if(!empty($aoInstances)) - { - //take last instances - return $this->findPart($oMask->getCurrentInstance($sLevelPartName), $sPartName); - } - } - } - $this->addError('No part found : '.$sPartName); - } - - private function getCurrentInstance($sPartName) - { - if(!empty($this->aoInstances[$sPartName])) - { - return end($this->aoInstances[$sPartName]); - } - else - { - return false; - } - } - - public function setTag($sTagName, $sTagValue) - { - $this->asTags[$sTagName] = $sTagValue; - } - - public function getMask() - { - $sCompletedMask = $this->sMask; - - //build parts - foreach($this->aoInstances as $sPart=>$aoParts) - { - $sTagValue = ''; - foreach($aoParts as $oInstance) - { - $sTagValue .= $oInstance->getMask(); - } - $this->setTag($this->getPartTagPattern($sPart, false), $sTagValue); - } - - //replace tags - if(!empty($this->asTags)) - { - $asTags = $this->addTagMark(array_keys($this->asTags)); - $sCompletedMask = str_replace($asTags, $this->asTags, $sCompletedMask); - } - return $sCompletedMask; - } - - private function addTagMark($oData) - { - return ToolBox::array_map_encapsulate($oData, self::TAG_MARK); - } -} - -class fileUploader -{ - const GET_NAME = 'qqfile'; - - private $asAllowedExtensions; - private $iSizeLimit; - private $sFolderPath; - - function __construct($sFolderPath, $asAllowedExtensions = array()/*, $sizeLimit = 10485760*/) - { - $this->sFolderPath = $sFolderPath; - $this->asAllowedExtensions = array_map("mb_strtolower", $asAllowedExtensions); - //$this->iSizeLimit = $sizeLimit; - //$this->checkServerSettings(); - $this->iSizeLimit = Databap::getMaxSize(); - - /* - if(isset($_GET[self::GET_NAME])) - { - //$this->file = new qqUploadedFileXhr(); - } - elseif(isset($_FILES[self::GET_NAME])) { - $this->file = new qqUploadedFileForm(); - } - else - { - $this->file = false; - } - */ - } - - /* - private function checkServerSettings() - { - $postSize = $this->toBytes(ini_get('post_max_size')); - $uploadSize = $this->toBytes(ini_get('upload_max_filesize')); - - - if($postSize < $this->iSizeLimit || $uploadSize < $this->iSizeLimit){ - $size = max(1, $this->iSizeLimit / 1024 / 1024) . 'M'; - die("{'error':'increase post_max_size and upload_max_filesize to $size'}"); - } - } - - private function toBytes($str) - { - $val = trim($str); - $last = mb_strtolower($str[mb_strlen($str)-1]); - switch($last) { - case 'g': $val *= 1024; - case 'm': $val *= 1024; - case 'k': $val *= 1024; - } - return $val; - } - */ - - function save($sFileName) - { - $oInput = fopen("php://input", "r"); - $sTemp = tmpfile(); - $iRealSize = stream_copy_to_stream($oInput, $sTemp); - fclose($oInput); - - if($iRealSize != $this->getSize()) - { - return false; - } - - $sPath = $this->sFolderPath.$sFileName; - $oTarget = fopen($sPath, "w"); - fseek($sTemp, 0, SEEK_SET); - stream_copy_to_stream($sTemp, $oTarget); - fclose($oTarget); - - return true; - } - - function getName() - { - return isset($_GET[self::GET_NAME])?$_GET[self::GET_NAME]:false; - } - - function getSize() - { - if(isset($_SERVER["CONTENT_LENGTH"])) - { - return (int)$_SERVER["CONTENT_LENGTH"]; - } - else - { - throw new Exception('Getting content length is not supported.'); - } - } - - //Returns array('success'=>true) or array('error'=>'error message') - function handleUpload(/*$sUploadDir, $replaceOldFile = false*/) - { - if(!is_writable($this->sFolderPath)) - { - return array('error' => "Erreur serveur. Le dossier est protégé en écriture"); - } - if(!$this->getName()) - { - return array('error' => 'Aucun fichier téléchargé'); - } - - $iSize = $this->getSize(); - if($iSize == 0) - { - return array('error' => 'Fichier vide'); - } - if($iSize > $this->iSizeLimit) - { - return array('error' => 'Fichier trop volumineux'); - } - - $asPathInfo = pathinfo($this->getName()); - $sExt = mb_strtolower($asPathInfo['extension']); - $sExt = ($sExt=='jpg')?'jpeg':$sExt; - if($this->asAllowedExtensions && !in_array($sExt, $this->asAllowedExtensions)) - { - $sAuthorizedExts = implode(', ', $this->asAllowedExtensions); - return array('error' => 'Le fichier a une extension invalide, les extensions autorisées sont : '.$sAuthorizedExts); - } - - // - - - //$sFileName = $asPathInfo['filename']; - $sFileName = md5(uniqid()); - /* - if(!$replaceOldFile){ - // don't overwrite previous files that were uploaded - //while (file_exists($sUploadDir . $sFileName . '.' . $sExt)) { - // $sFileName .= rand(10, 99); - //} - if(file_exists($sUploadDir . $sFileName . '.' . $sExt)) - { - $sFileName .= date(Databap::DATE_COMPACT_FORMAT); - } - } - */ - - if($this->save($sFileName.'.'.$sExt)) - { - return array('success'=>true, 'file_name'=>$sFileName.'.'.$sExt); - } - else - { - return array('error'=> 'Erreur server. Impossible de sauvegarder le fichier.'); - } - } -} - -/** - * MySql query manager and generator - * @author franzz - */ -class MySqlManager extends PhpObject -{ - const DB_NO_CONN = 'ERR_1'; - const DB_NO_DATA = 'ERR_2'; - const ID_TAG = 'id_'; - - const USER_TABLE = 'users'; - const COMP_TABLE = 'companies'; - const CODE_TABLE = 'codes'; - const URL_TABLE = 'urls'; - const MSG_TABLE = 'messages'; - const CHAN_TABLE = 'channels'; - const CONN_TABLE = 'connections'; - const ART_TABLE = 'articles'; - - const OPT_TABLE = 'options'; - const OPTVAL_TABLE = 'option_values'; - const OPTNAME_TABLE = 'option_names'; - - const PROC_TABLE = 'procedures'; - const STEP_TABLE = 'steps'; - const IMG_TABLE = 'images'; - - const DOC_TABLE = 'docs'; - const FILE_TABLE = 'files'; - - const SEARCH_TABLE = 'searchs'; - - public $sDbState; - private $oConnection; - private $sDatabase; //TODO Variable ? - private $bTrace; - - public function __construct($sDbServer, $sLogin, $sPass, $sDatabase, $sEncoding) - { - parent::__construct(); - $this->sDatabase = $sDatabase; - //$this->oConnection = mysql_connect(self::DB_SERVER, self::DB_LOGIN, self::DB_PASS); - $this->oConnection = new mysqli($sDbServer, $sLogin,$sPass); - $this->syncPhpParams($sEncoding); - - /* - $dsn = 'mysql:dbname='.$this->sDatabase.';host='.self::DB_SERVER; - try {$dbh = new PDO($dsn, self::DB_LOGIN, self::DB_PASS);} - catch (PDOException $e) {$this->addError('Connexion échouée : ' . $e->getMessage());} - */ - - $this->setTrace(false); - //if(!$this->oConnection) - if($this->oConnection->connect_error) - { - //$this->addError('bug connection'); - $this->addError('bug connection : '.$this->oConnection->connect_error); - $this->sDbState = self::DB_NO_CONN; - } - else - { - //if(!mysql_select_db($this->sDatabase, $this->oConnection)) - if(!$this->oConnection->select_db($this->sDatabase)) - { - $this->addError('bug selecting database. Installing...'); - $this->sDbState = self::DB_NO_DATA; - } - } - } - - private function syncPhpParams($sEncoding) - { - //Characters encoding - $this->oConnection->set_charset($sEncoding); //SET NAMES - - //Time zone - $oNow = new DateTime(); - $iMins = $oNow->getOffset() / 60; - $iSign = ($iMins < 0)?-1:1; - $iMins = abs($iMins); - $iHours = floor($iMins / 60); - $iMins -= $iHours * 60; - $sOffset = sprintf('%+d:%02d', $iHours*$iSign, $iMins); - $this->setQuery("SET time_zone='{$sOffset}';"); - } - - public function __destruct() - { - parent::__destruct(); - //mysql_close($this->oConnection); - $this->oConnection->close(); - } - - public function setTrace($bAction) - { - $this->bTrace = $bAction; - } - - public static function getTables() - { - $oReflect = new ReflectionClass(__CLASS__); - $asConstants = $oReflect->getConstants(); - $sTableTag = '_TABLE'; - $asTables = array(); - foreach($asConstants as $sConstant=>$sConstantValue) - { - if(mb_strpos($sConstant, $sTableTag)!==false && mb_strpos($sConstant, $sTableTag)==(mb_strlen($sConstant) - mb_strlen($sTableTag))) - { - $asTables[] = $sConstantValue; - } - } - return $asTables; - } - - public function install() - { - //Create Database - $this->setQuery("DROP DATABASE IF EXISTS ".$this->sDatabase); - $this->setQuery("CREATE DATABASE ".$this->sDatabase." DEFAULT CHARACTER SET ".Settings::SQL_ENC." DEFAULT COLLATE ".Settings::SQL_ENC."_general_ci"); - //mysql_select_db($this->sDatabase, $this->oConnection); - $this->oConnection->select_db($this->sDatabase); - - //Create tables - @array_walk($this->getInstallQueries(), array($this, 'setQuery')); - } - - //For debug purposes - public function getFullInstallQuery() - { - $asInstallQueries = $this->getInstallQueries(); - return str_replace("\n", "
    ", implode(";\n\n", $asInstallQueries))."\n\n"; - } - - private function getInstallQueries() - { - $asTables = $this->getTables(); - $asInstallQueries = array_map(array($this, 'getInstallQuery'), $asTables); - $asAlterQueries = $this->getForeignKeyQueries($asTables); - return array_merge($asInstallQueries, $asAlterQueries); - } - - private function getInstallQuery($sTableName) - { - $asTableColumns = $this->getTableColumns($sTableName); - $sQuery = "\n".$this->implodeAll($asTableColumns, "` ", "\n", "`", ",")."\n".implode(", \n", $this->getTableConstraints($sTableName)); - return "CREATE TABLE `{$sTableName}` ({$sQuery})"; - } - - private function getForeignKeyQueries($asTableNames) - { - $asForeignKeyQueries = array(); - foreach($asTableNames as $sTableName) - { - $asTableColumns = array_keys(self::getTablecolumns($sTableName)); - foreach($asTableColumns as $sColumnName) - { - if(self::isId($sColumnName) && $sColumnName!=self::getId($sTableName)) - { - $asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD INDEX(`".$sColumnName."`)"; - $asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD FOREIGN KEY (`".$sColumnName."`) REFERENCES ".self::getTable($sColumnName)."(`".$sColumnName."`)"; - } - } - } - return $asForeignKeyQueries; - } - - private function setQuery($sQuery, $sTypeQuery=__FUNCTION__) - { - $this->getQuery($sQuery, $sTypeQuery); - //return (mysql_affected_rows()!=0); - return ($this->oConnection->affected_rows!=0); - } - - private function getQuery($sQuery, $sTypeQuery=__FUNCTION__) - { - $sQuery = str_replace(array("\n", "\t"), array(" ", ""), $sQuery); - //$oResult = mysql_query($sQuery, $this->oConnection); - - if($this->bTrace) - { - $this->setDebug(true); - $this->addNotice($sQuery); - } - - if(!($oResult = $this->oConnection->query($sQuery))) - { - $this->addError("\nErreur SQL : \n".str_replace("\t", "", $sQuery)."\n\n".str_replace(array("\t", "\n"), "", $this->oConnection->error)); - } - return $oResult; - } - - public function getArrayQuery($sQuery, $bStringOnly=false, $sGroupBy='', $sTypeQuery=__FUNCTION__) - { - $iIndex = 0; - $iColumnCount = 0; - $asResult = array(); - $oResult = $this->getQuery($sQuery, true, $sTypeQuery); - if($oResult!==false) - { - //while($asCurrentRow = mysql_fetch_array($oResult)) - while($asCurrentRow = $oResult->fetch_array()) - { - if($bStringOnly) - { - $asCurrentRow = $this->arrayKeyFilter($asCurrentRow, 'is_string'); - } - - //Add table reel keys - if($sGroupBy!='' && array_key_exists($sGroupBy, $asCurrentRow)) - { - $iRowKey = $asCurrentRow[$sGroupBy]; - unset($asCurrentRow[$sGroupBy]); - } - else - { - $iRowKey = $iIndex; - } - - //For first loop, check table width - if($iIndex==0) - { - $iColumnCount = count($asCurrentRow); - } - - //One column case : collapse a level - if($iColumnCount==1) - { - $asCurrentRow = array_shift($asCurrentRow); - } - - $asResult[$iRowKey] = $asCurrentRow; - $iIndex++; - } - } - return $asResult; - } - - private function getMaxIncrementedValue($sTable) - { - return $this->selectValue($sTable, "MAX(".$this->getId($sTable).")"); - } - - public static function getId($sTableName, $bFull=false) - { - $sColumnName = self::ID_TAG.self::getText($sTableName); - return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName; - } - - public static function getText($sTableName, $bFull=false) - { - $sColumnName = mb_substr(str_replace('`', '', $sTableName), 0, -1); - $sColumnName = mb_substr($sColumnName, -2)=='ie'?mb_substr($sColumnName, 0, -2).'y':$sColumnName; - return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName; - } - - public static function getFullColumnName($sTableName, $sColumnName) - { - return $sTableName.".".$sColumnName; - } - - private static function isId($sColumnName, $sTableName='') - { - $asTables = ($sTableName=='')?self::getTables():array($sTableName); - $asTableIds = array_map(array('self', 'getId'), $asTables); - return in_array($sColumnName, $asTableIds); - } - - private static function getTable($sTableId) - { - $asTables = self::getTables(); - $asTableIds = array_map(array('self', 'getId'), $asTables); - if(in_array($sTableId, $asTableIds)) return $asTables[array_search($sTableId, $asTableIds)]; - else - { - $this->addError('Id '.$sTableId.' présent dans aucune table'); - return false; - } - } - - public static function getTablecolumns($sTableName) - { - $asTableColumns = array(self::getId($sTableName)); - switch($sTableName) - { - case self::USER_TABLE: - $asTableColumns[] = 'first_name'; - $asTableColumns[] = 'last_name'; - $asTableColumns[] = 'email'; - $asTableColumns[] = self::getId(self::COMP_TABLE); - $asTableColumns[] = 'pass'; - $asTableColumns[] = 'clearance'; - break; - case self::COMP_TABLE: - $asTableColumns[] = self::getText(self::COMP_TABLE); - $asTableColumns[] = 'logo'; - break; - case self::CODE_TABLE: - $asTableColumns[] = self::getText(self::CODE_TABLE); - $asTableColumns[] = 'description'; - $asTableColumns[] = self::getId(self::USER_TABLE); - $asTableColumns[] = 'refer_id'; - break; - case self::URL_TABLE: - $asTableColumns[] = self::getId(self::CODE_TABLE);; - $asTableColumns[] = 'phrase'; - break; - case self::MSG_TABLE: - $asTableColumns[] = self::getId(self::USER_TABLE); - $asTableColumns[] = 'nickname'; - $asTableColumns[] = self::getId(self::CHAN_TABLE); - $asTableColumns[] = self::getText(self::MSG_TABLE); - $asTableColumns[] = 'type'; - $asTableColumns[] = 'date'; - break; - case self::CHAN_TABLE: - $asTableColumns[] = 'safe_name'; - $asTableColumns[] = self::getText(self::CHAN_TABLE); - break; - case self::CONN_TABLE: - $asTableColumns[] = self::getId(self::USER_TABLE); - $asTableColumns[] = self::getId(self::CHAN_TABLE); - break; - case self::OPT_TABLE: - $asTableColumns[] = self::getId(self::USER_TABLE); - $asTableColumns[] = self::getId(self::OPTNAME_TABLE); - $asTableColumns[] = self::getId(self::OPTVAL_TABLE); - $asTableColumns[] = self::getText(self::OPT_TABLE); - break; - case self::OPTNAME_TABLE: - $asTableColumns[] = self::getText(self::OPTNAME_TABLE); - $asTableColumns[] = 'type'; - $asTableColumns[] = 'language'; - break; - case self::OPTVAL_TABLE: - $asTableColumns[] = self::getId(self::OPTNAME_TABLE); - $asTableColumns[] = self::getText(self::OPTVAL_TABLE); - $asTableColumns[] = 'language'; - break; - case self::PROC_TABLE: - $asTableColumns[] = self::getId(self::USER_TABLE); - $asTableColumns[] = 'title'; - $asTableColumns[] = 'description'; - $asTableColumns[] = 'refer_id'; - break; - case self::STEP_TABLE: - $asTableColumns[] = self::getId(self::PROC_TABLE); - $asTableColumns[] = 'description'; - break; - case self::IMG_TABLE: - $asTableColumns[] = self::getId(self::PROC_TABLE); - $asTableColumns[] = self::getId(self::STEP_TABLE); - $asTableColumns[] = 'description'; - $asTableColumns[] = 'file_name'; - break; - case self::DOC_TABLE: - $asTableColumns[] = self::getId(self::USER_TABLE); - $asTableColumns[] = 'title'; - $asTableColumns[] = 'description'; - $asTableColumns[] = 'refer_id'; - break; - case self::FILE_TABLE: - $asTableColumns[] = self::getId(self::DOC_TABLE); - $asTableColumns[] = 'description'; - $asTableColumns[] = 'file_name'; - break; - case self::SEARCH_TABLE: - $asTableColumns[] = 'id_item'; - $asTableColumns[] = 'refer_id'; - $asTableColumns[] = 'type'; - $asTableColumns[] = 'keywords'; - break; - case self::ART_TABLE: - $asTableColumns[] = 'title'; - $asTableColumns[] = 'link'; - $asTableColumns[] = 'date'; - $asTableColumns[] = 'first_name'; - $asTableColumns[] = 'last_name'; - $asTableColumns[] = 'email'; - break; - default: - return false; - } - $asTableColumns[] = 'led'; - $asTableName = array_fill(0, count($asTableColumns), $sTableName); - return array_combine($asTableColumns, array_map(array('self', 'getColumnType'), $asTableColumns, $asTableName)); - } - - private static function getColumnType($sColumnName, $sTableName) - { - $sColumnType = ''; - switch($sColumnName) - { - case self::getText(self::USER_TABLE): //TODO delete use and field - $sColumnType = "varchar(50) NOT NULL"; - break; - case 'first_name': - $sColumnType = "varchar(20) NOT NULL"; - break; - case 'last_name': - $sColumnType = "varchar(20) NOT NULL"; - break; - case 'nickname': - $sColumnType = "varchar(50) NOT NULL"; - break; - case 'email': - $sColumnType = "varchar(100) NOT NULL"; - break; - case 'pass': - $sColumnType = "varchar(128) NOT NULL"; - break; - case 'clearance': - $sColumnType = "int(1) NOT NULL"; - break; - case self::getText(self::CODE_TABLE): - $sColumnType = "longtext NOT NULL"; - break; - case 'title': - $sColumnType = "varchar(200) NOT NULL"; - break; - case 'description': - $sColumnType = "varchar(500) NOT NULL"; - break; - case 'link': - $sColumnType = "varchar(200) NOT NULL"; - break; - case 'refer_id': - $sColumnType = "int(10) UNSIGNED NOT NULL"; - break; - case 'phrase': - $sColumnType = "varchar(50) NOT NULL"; - break; - case self::getText(self::MSG_TABLE): - $sColumnType = "varchar(500) NOT NULL"; - break; - case 'type': - $sColumnType = "varchar(2) NOT NULL"; - break; - case 'safe_name': - $sColumnType = "varchar(50) NOT NULL"; - break; - case self::getText(self::CHAN_TABLE): - $sColumnType = "varchar(50) NOT NULL"; - break; - case self::getText(self::OPT_TABLE): - $sColumnType = "varchar(100) NOT NULL"; - break; - case self::getText(self::OPTNAME_TABLE): - $sColumnType = "varchar(100) NOT NULL"; - break; - case self::getText(self::OPTVAL_TABLE): - $sColumnType = "varchar(50) NOT NULL"; - break; - case 'language': - $sColumnType = "varchar(2) NOT NULL"; - break; - case 'file_name': - $sColumnType = "varchar(40) NOT NULL"; - break; - case 'preview_name': - $sColumnType = "varchar(40) NOT NULL"; - break; - case 'id_item': - $sColumnType = "int(10) UNSIGNED NOT NULL"; - break; - case 'keywords': - $sColumnType = "longtext NOT NULL"; - break; - case self::getText(self::COMP_TABLE): - $sColumnType = "varchar(30) NOT NULL"; - break; - case 'logo': - $sColumnType = "varchar(20) NOT NULL"; - break; - case 'date': - $sColumnType = "date NOT NULL"; - break; - case 'led': - $sColumnType = "TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP"; - break; - case self::isId($sColumnName, $sTableName): - $sColumnType = "int(10) UNSIGNED auto_increment"; - break; - case self::isId($sColumnName): - $sColumnType = "int(10) UNSIGNED"; - break; - } - return $sColumnType; - } - - private static function getTableConstraints($sTableName) - { - //Primary key - $asTableConstraints = array('PRIMARY' => "PRIMARY KEY (`".self::getId($sTableName)."`)"); - - //Foreign keys - //Foreign keys applied using ALTER TABLE syntax at the end to prevent scheduling CREATE TABLE queries - - //Other constraints - switch($sTableName) - { - case self::USER_TABLE : - $asTableConstraints[] = "UNIQUE KEY `user_first_and_last_name` (`first_name`, `last_name`)"; - break; - case self::URL_TABLE : - $asTableConstraints[] = "UNIQUE KEY `uni_phrase` (`phrase`)"; - break; - case self::MSG_TABLE : - $asTableConstraints[] = "INDEX(`date`)"; - break; - case self::ART_TABLE: - $asTableConstraints[] = "INDEX(`title`)"; - break; - - } - return $asTableConstraints; - } - - private function addQuotes($oData) - { - //TODO remake - $asTrustedFunc = array('CURDATE()', 'NOW()'); - $sChar = "'"; - if(is_array($oData)) - { - $asChar = array_fill(1, count($oData), $sChar); - return array_combine(array_keys($oData), array_map(array($this, 'addQuotes'), $oData, $asChar)); - } - else - { - if(in_array($oData, $asTrustedFunc)) return $oData; - else return $sChar.$oData.$sChar; - } - } - - private function getLastId() - { - return $this->oConnection->insert_id; - } - - public function insertRow($sTableName, $asData) - { - $this->cleanSql($sTableName); - $this->cleanSql($asData); - - $asQueryValues = $this->addQuotes($asData); - $sQuery = "INSERT INTO ".$sTableName." (`".implode("`, `", array_keys($asQueryValues))."`) VALUES (".implode(", ", $asQueryValues).")"; - return $this->setQuery($sQuery)?$this->getLastId():0; - } - - public function updateRow($sTableName, $asConstraints, $asData) - { - return $this->updateRows($sTableName, $asConstraints, $asData, 1); - } - - public function updateRows($sTableName, $asConstraints, $asData, $iLimit=0) - { - if(!is_array($asConstraints)) - { - $asConstraints = array($this->getId($sTableName)=>$asConstraints); - } - - $this->cleanSql($sTableName); - $this->cleanSql($iTableId); - $this->cleanSql($asData); - $this->cleanSql($asConstraints); - $asQueryValues = $this->addQuotes($asData); - $asConstraintsValues = $this->addQuotes($asConstraints); - $this->addColumnSelectors($asQueryValues); - $this->addColumnSelectors($asConstraintsValues); - - $sLimit = $iLimit>0?" LIMIT $iLimit":""; - $sQuery = "UPDATE {$sTableName} ". - "SET ".$this->implodeAll($asQueryValues, " = ", ", ")." ". - "WHERE ".$this->implodeAll($asConstraintsValues, " = ", " AND ").$sLimit; - - $iResult = false; - if($this->setQuery($sQuery)) - { - $iResult = ($iLimit==1)?$this->selectValue($sTableName, $this->getId($sTableName), $asConstraints):true; - } - return $iResult; - } - - public function insertUpdateRow($sTableName, $asData, $asKeys=array(), $bUpdate=true) - { - $sTableIdName = self::getId($sTableName); - - //check for data in the db - if($asKeys==array()) - { - $asKeys[] = $sTableIdName; - } - $asValues = array_intersect_key($asData, array_flip($asKeys)); - $iTableId = $this->selectValue($sTableName, $sTableIdName, $asValues); - - //insert - if(!$iTableId) - { - $iTableId = $this->insertRow($sTableName, $asData); - } - //Update - elseif($bUpdate) - { - if(array_key_exists($sTableIdName, $asData)) - { - unset($asData[$sTableIdName]); - } - $iTableId = $this->updateRow($sTableName, $iTableId, $asData); - } - return $iTableId; - } - - public function selectInsert($sTableName, $asData, $asKeys=array()) - { - return $this->insertUpdateRow($sTableName, $asData, $asKeys, false); - } - - public function deleteRow($sTableName, $iTableId) - { - $this->cleanSql($sTableName); - $this->cleanSql($iTableId); - - //linked tables - switch($sTableName) - { - case self::CODE_TABLE : - $asTables = array($sTableName, self::URL_TABLE); - break; - case self::PROC_TABLE : - $asTables = array($sTableName, self::STEP_TABLE, self::IMG_TABLE); - break; - case is_string($sTableName) : - $asTables = array($sTableName); - break; - case is_array($sTableName): - $asTables = $sTableName; - break; - default: - $asTables = array(); - } - foreach($asTables as $sTable) - { - $this->setQuery("DELETE FROM ".$sTable." WHERE ".$this->getId($sTableName)." = ".$iTableId); - } - } - - public function emptyTable($sTableName) - { - $this->cleanSql($sTableName); - $this->setQuery("TRUNCATE ".$sTableName); - } - - public function selectRows($asInfo, $bStringOnly=true, $sGroupBy='') - { - $asAttributes = array('select'=>"SELECT", 'from'=>"FROM", 'join'=>"LEFT JOIN", 'joinOn'=>"LEFT JOIN", 'constraint'=>"WHERE", 'groupBy'=>"GROUP BY", 'orderBy'=>"ORDER BY", 'limit'=>'LIMIT'); - $asRowSeparators = array('select'=>", ", 'from'=>"", 'join'=>" LEFT JOIN ", 'joinOn'=>" LEFT JOIN ", 'constraint'=>" AND ", 'groupBy'=>", ", 'orderBy'=>", ", 'limit'=>""); - $asOperators = array('constraint'=>" = ", 'orderBy'=>" ", 'join'=>" USING(", 'joinOn'=>" ON "); - $asEndOfStatement = array('constraint'=>"", 'orderBy'=>"", 'join'=>")", 'joinOn'=>""); - - //$sQuery = "/* ".str_replace(array("\n", "\t"), '', print_r($asInfo, true))." */"; - $sQuery = ""; - foreach($asAttributes as $sStatement => $sKeyWord) - { - $asSelection = array_key_exists($sStatement, $asInfo)?$asInfo[$sStatement]:array(); - if(!is_array($asSelection)) - { - $asSelection = array($asSelection); - } - - //if provided values - if(!empty($asSelection)) - { - $this->cleanSql($asSelection); - - if($sStatement=='constraint' && !array_key_exists('constVar', $asInfo)) - { - $asSelection = $this->addQuotes($asSelection); - } - $this->addColumnSelectors($asSelection); - - $sQuery .= " ".$sKeyWord." "; - - //in case of double value input - if(array_key_exists($sStatement, $asOperators)) - { - if($sStatement=='constraint' && array_key_exists('constOpe', $asInfo)) - { - $asOperators[$sStatement] = $asInfo['constOpe']; - } - $sQuery .= $this->implodeAll($asSelection, $asOperators[$sStatement], $asRowSeparators[$sStatement], "", $asEndOfStatement[$sStatement]); - } - else - { - $sQuery .= implode($asRowSeparators[$sStatement], $asSelection); - } - } - //default value for select - elseif($sStatement=='select') - { - $sQuery .= " ".$sKeyWord." * "; - } - } - return $this->getArrayQuery($sQuery, $bStringOnly, $sGroupBy); - } - - private function addColumnSelectors(&$asSelection) - { - //TODO get rid of this - $sSqlWord = 'option'; - $sKey = array_search($sSqlWord, $asSelection); - if($sKey!==false) - { - $asSelection[$sKey] = "`".$asSelection[$sKey]."`"; - } - elseif(array_key_exists($sSqlWord, $asSelection)) - { - $asSelection["`".$sSqlWord."`"] = $asSelection[$sSqlWord]; - unset($asSelection[$sSqlWord]); - } - } - - public function selectRow($sTableName, $asConstraints=array(), $sColumnName='*') - { - if(!is_array($asConstraints)) - { - $asConstraints = array($this->getId($sTableName)=>$asConstraints); - } - $asResult = $this->selectRows(array('select'=>$sColumnName, 'from'=>$sTableName, 'constraint'=>$asConstraints)); - $iCountNb = count($asResult); - switch($iCountNb) - { - case 0 : - return false; - case $iCountNb > 1 : - $this->addError('Trop de résultats pour un selectRow() : '.$iCountNb.' lignes. Table: '.$sTableName.', contrainte: '.self::implodeAll($asConstraints, '=', ' ').', colonne: '.$sColumnName); - break; - } - return array_shift($asResult); - } - - public function selectValue($sTableName, $sColumnName, $oConstraints=array()) - { - if(!is_array($oConstraints)) - { - $oConstraints = array($this->getId($sTableName)=>$oConstraints); - } - return $this->selectRow($sTableName, $oConstraints, $sColumnName); - } - - public function pingValue($sTableName, $oConstraints) - { - return $this->selectValue($sTableName, 'COUNT(1)', $oConstraints); - } - - public function cleanSql(&$oData) - { - //self::cleanData($oData, 'mysql_real_escape_string'); - //$oData = self::cleanData($oData, 'mysql_real_escape_string'); - $this->cleanData($oData); - $oData = $this->cleanData($oData); - } - - public static function implodeAll($asText, $asKeyValueSeparator='', $sRowSeparator='', $sKeyPre='', $sValuePost=false) - { - if($sValuePost===false) - { - $sValuePost = $sKeyPre; - } - $asCombinedText = array(); - - //if unique value for key value separator - if(!is_array($asKeyValueSeparator) && !empty($asText)) - { - $asKeyValueSeparator = array_combine(array_keys($asText), array_fill(0, count($asText), $asKeyValueSeparator)); - } - - foreach($asText as $sKey=>$sValue) - { - $asCombinedText[] = $sKeyPre.$sKey.$asKeyValueSeparator[$sKey].(is_array($sValue)?implode($sValue):$sValue).$sValuePost; - } - return implode($sRowSeparator, $asCombinedText); - } - - public static function arrayKeyFilter($asArray, $sCallBack) - { - $asValidKeys = array_flip(array_filter(array_keys($asArray), $sCallBack)); - return array_intersect_key($asArray, $asValidKeys); - } - - public function cleanData($oData) - { - if(!is_array($oData)) - { - return $this->oConnection->real_escape_string($oData); - } - elseif(count($oData)>0) - { - $asKeys = array_map(array($this, 'cleanData'), array_keys($oData)); - $asValues = array_map(array($this, 'cleanData'), $oData); - return array_combine($asKeys, $asValues); - } - } -} - -/** - * ToolBox - Only static functions missing from php librairy - * @author franzz - */ -class ToolBox -{ - function __construct(){} - - public function toString($sSep, $asData) - { - if(is_array($asData) && count($asData)>0) - { - $sData = $sSep; - foreach($asData as $oData) - { - $sData .= self::toString($oData).$sSep; - } - return $sData; - } - else return $asData; - } - - public static function cleanPost(&$asData) - { - //get rid of magic quotes - if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) - { - $asData = self::cleanData($asData, 'stripslashes'); - } - } - - public static function cleanData($oData, $sCleaningFunc) - { - if(!is_array($oData)) - { - return call_user_func($sCleaningFunc, $oData); - } - elseif(count($oData)>0) - { - $asCleaningFunc = array_fill(1, count($oData), $sCleaningFunc); - $asKeys = array_map(array('self', 'cleanData'), array_keys($oData), $asCleaningFunc); - $asValues = array_map(array('self', 'cleanData'), $oData, $asCleaningFunc); - return array_combine($asKeys, $asValues); - } - } - - public static function fixGlobalVars($argv) - { - //Add CLI arguments - if(defined('STDIN')) parse_str(implode('&', array_slice($argv, 1)), $_GET); - - //Add Server Name - $sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD']; - $sAppPath = 'http://'.str_replace('http://', '', $sServerName.dirname($_SERVER['SCRIPT_NAME'])); - $_GET['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':''); - } - - public static function array_map_encapsulate($oData, $sChar) - { - if(is_array($oData)) - { - $asChar = array_fill(1, count($oData), $sChar); - return array_combine(array_keys($oData), array_map(array('self', 'array_map_encapsulate'), $oData, $asChar)); - } - else - { - return $sChar.$oData.$sChar; - } - } - - public static function capitalizeWords($acText, $sCharList = '') - { - // Use ToolBox::mb_ucwords if no delimiters are given - if($sCharList=='') { - return ToolBox::mb_ucwords($acText); - } - - // Go through all characters - $capitalizeNext = true; - $max = mb_strlen($acText); - for ($i = 0; $i < $max; $i++) - { - if(mb_strpos($sCharList, $acText[$i]) !== false) - { - $capitalizeNext = true; - } - elseif($capitalizeNext) - { - $capitalizeNext = false; - $acText[$i] = mb_strtoupper($acText[$i]); - } - } - - return $acText; - } - - public static function sendMail($sFrom, $sTo, $sReplyTo, $sSubject, $sMessage) - { - $sResult = ''; - if($sFrom!='' && $sTo!='' && $sReplyTo!='' && $sSubject!='' && $sMessage!='') - { - $sHtmlMessage = str_replace("\n", '
    ', $sMessage); - $sPlainMessage = strip_tags(str_replace('
    ', "\n", $sHtmlMessage)); - - $iBoundary = uniqid('HTMLEMAIL'); - $sHeaders = 'From: '.$sFrom."\r\n". - 'Reply-To: '.$sReplyTo."\r\n". - 'MIME-Version: 1.0'."\r\n". - 'Content-Type: multipart/alternative;'. - 'boundary = '.$iBoundary."\r\n\r\n". - 'MIME encoded Message'. - '--'.$iBoundary."\r\n". - 'Content-Type: text/plain; charset=UTF-8'."\r\n". - 'Content-Transfer-Encoding: base64'."\r\n\r\n". - chunk_split(base64_encode($sPlainMessage)). - '--'.$iBoundary."\r\n". - 'Content-Type: text/html; charset=UTF-8'."\r\n". - 'Content-Transfer-Encoding: base64'."\r\n\r\n". - chunk_split(base64_encode($sHtmlMessage)); - - if(!mail($sTo, $sSubject, '', $sHeaders)) - { - $sResult = 'Email: An unknown error occured'; - } - } - else - { - $sResult = 'Email: Some fields were empty'; - } - } - - public static function createThumbnail($sInPath, $iMaxWidth, $iMaxHeight, $sOutPath='', $bDeleteIn=false) - { - $asResult = array('error'=>''); - - //Look up the extension to choose the image creator - //TODO use MIME types - $sInInfo = pathinfo($sInPath); - $sInName = mb_strtolower($sInInfo['basename']); - $sImageExt = mb_strtolower($sInInfo['extension']); - $sImageExt = ($sImageExt=='jpg')?'jpeg':$sImageExt; - - //New Destination folder - if($sOutPath=='') $sOutPath = $sInPath; - elseif(mb_substr($sOutPath, -1)=='/') $sOutPath .= $sInName; - - //New sizes - if(in_array($sImageExt, Databap::$UPLOAD_IMG_EXTS)) - { - list($iWidth, $iHeight) = getimagesize($sInPath); - if($iWidth > $iMaxWidth || $iHeight > $iMaxHeight) - { - $dResizeDeltaWidth = $iWidth - $iMaxWidth; - $dResizeDeltaHeight = $iHeight - $iMaxHeight; - if($dResizeDeltaWidth > $dResizeDeltaHeight) - { - $iResizedWidth = $iMaxWidth; - $iResizedHeight = ($iResizedWidth / $iWidth) * $iHeight; - } - else - { - $iResizedHeight = $iMaxHeight; - $iResizedWidth = ($iResizedHeight / $iHeight) * $iWidth; - } - - //create image from source - $oSource = call_user_func('imagecreatefrom'.$sImageExt, $sInPath); - - //Resize - $oThumb = imagecreatetruecolor($iResizedWidth, $iResizedHeight); - imagecopyresized($oThumb, $oSource, 0, 0, 0, 0, $iResizedWidth, $iResizedHeight, $iWidth, $iHeight); - - //Save - if(file_exists($sOutPath)) unlink($sOutPath); - if(!call_user_func_array('image'.$sImageExt, array($oThumb, $sOutPath))) - { - $asResult['error'] = 'Unable to create thumbnail : '.$sOutPath; - } - } - elseif($sInPath != $sOutPath) - { - $iResizedWidth = $iWidth; - $iResizedHeight = $iHeight; - if(!copy($sInPath, $sOutPath)) $asResult['error'] = 'Copy failed from '.$sInPath.' to '.$sOutPath; - } - $asResult['width'] = $iResizedWidth; - $asResult['height'] = $iResizedHeight; - $asResult['out'] = $sOutPath; - } - else $asResult['error'] = 'Wrong file type'; - - if($bDeleteIn && $asResult['error']=='' && $sInPath != $sOutPath) unlink($sInPath); - - return $asResult; - } - - public static function utf8_compliant($sText) - { - if(strlen($sText) == 0) return true; - return (preg_match('/^.{1}/us', $sText, $ar) == 1); - } - - public static function mb_ucwords($sText) - { - return mb_convert_case($sText, MB_CASE_TITLE, "UTF-8"); - } - - public static function file_get_contents_utf8($oFile) - { - $sContent = file_get_contents($oFile); - return mb_convert_encoding($sContent, 'UTF-8', mb_detect_encoding($sContent, 'UTF-8, ISO-8859-1', true)); - } -} - -/* Debug */ - -function pre($sText, $sMode='return', $bDie=false, $sTitle='Test') -{ - $sLog = '
    - '.$sTitle.' -
    '.print_r($sText, true).'
    -
    '; - switch($sMode) - { - case 'echo': - echo $sLog; - break; - case 'return': - if($bDie) echo $sLog; - break; - case 'log': - file_put_contents('log.html', ($sTitle!=''?$sTitle." :\n":'').print_r($sText, true)."\n\n", FILE_APPEND); - break; - default: - break; - } - - if($bDie) - { - die('[die() called by the test function '.__FUNCTION__.'()]'); - } - - return $sLog; -} - -function dlog($sText, $sTitle='Test') -{ - pre($sText, 'log', false, date('d/m/Y H:m:i').' - '.$sTitle, false); -} - -?> \ No newline at end of file diff --git a/screenshot/51e69be6dd3ec278a14f29f16c4015a8.jpeg b/docs/51e69be6dd3ec278a14f29f16c4015a8.jpeg similarity index 100% rename from screenshot/51e69be6dd3ec278a14f29f16c4015a8.jpeg rename to docs/51e69be6dd3ec278a14f29f16c4015a8.jpeg diff --git a/images/add_24.png b/images/add_24.png deleted file mode 100644 index 85b2587..0000000 Binary files a/images/add_24.png and /dev/null differ diff --git a/images/afk.png b/images/afk.png deleted file mode 100644 index d98ad58..0000000 Binary files a/images/afk.png and /dev/null differ diff --git a/images/chat_24.png b/images/chat_24.png deleted file mode 100644 index 52cc1b5..0000000 Binary files a/images/chat_24.png and /dev/null differ diff --git a/images/chat_48.png b/images/chat_48.png deleted file mode 100644 index 3d67d25..0000000 Binary files a/images/chat_48.png and /dev/null differ diff --git a/images/doc_24.png b/images/doc_24.png deleted file mode 100644 index 6807539..0000000 Binary files a/images/doc_24.png and /dev/null differ diff --git a/images/edit_24.png b/images/edit_24.png deleted file mode 100644 index ad93f10..0000000 Binary files a/images/edit_24.png and /dev/null differ diff --git a/images/error_48.png b/images/error_48.png deleted file mode 100644 index 445702c..0000000 Binary files a/images/error_48.png and /dev/null differ diff --git a/images/exit_24.png b/images/exit_24.png deleted file mode 100644 index 5e07040..0000000 Binary files a/images/exit_24.png and /dev/null differ diff --git a/images/exit_32.png b/images/exit_32.png deleted file mode 100644 index 8a7a53a..0000000 Binary files a/images/exit_32.png and /dev/null differ diff --git a/images/expand_left_16.png b/images/expand_left_16.png deleted file mode 100644 index 1c94dc8..0000000 Binary files a/images/expand_left_16.png and /dev/null differ diff --git a/images/expand_right_16.png b/images/expand_right_16.png deleted file mode 100644 index 5b63bb1..0000000 Binary files a/images/expand_right_16.png and /dev/null differ diff --git a/images/grad_white_transparent_h_50.png b/images/grad_white_transparent_h_50.png deleted file mode 100644 index 859c9f3..0000000 Binary files a/images/grad_white_transparent_h_50.png and /dev/null differ diff --git a/images/grad_white_transparent_v_50.png b/images/grad_white_transparent_v_50.png deleted file mode 100644 index f517bfe..0000000 Binary files a/images/grad_white_transparent_v_50.png and /dev/null differ diff --git a/images/help_24.png b/images/help_24.png deleted file mode 100644 index 312d0f7..0000000 Binary files a/images/help_24.png and /dev/null differ diff --git a/images/key_24.png b/images/key_24.png deleted file mode 100644 index ecd4565..0000000 Binary files a/images/key_24.png and /dev/null differ diff --git a/images/logo_mu_e_50.png b/images/logo_mic_50.png similarity index 100% rename from images/logo_mu_e_50.png rename to images/logo_mic_50.png diff --git a/images/logo_mu_25.png b/images/logo_mu_25.png deleted file mode 100644 index df4c10b..0000000 Binary files a/images/logo_mu_25.png and /dev/null differ diff --git a/images/logo_obs_24.jpg.old b/images/logo_obs_24.jpg.old deleted file mode 100644 index 379a7ca..0000000 Binary files a/images/logo_obs_24.jpg.old and /dev/null differ diff --git a/images/logo_pe_24.png b/images/logo_pe_24.png new file mode 100644 index 0000000..63fa4a0 Binary files /dev/null and b/images/logo_pe_24.png differ diff --git a/images/logo_pe_48.png b/images/logo_pe_48.png new file mode 100644 index 0000000..41ed448 Binary files /dev/null and b/images/logo_pe_48.png differ diff --git a/images/tux_24.png b/images/logo_unknown_24.png similarity index 100% rename from images/tux_24.png rename to images/logo_unknown_24.png diff --git a/images/tux_48.png b/images/logo_unknown_48.png similarity index 100% rename from images/tux_48.png rename to images/logo_unknown_48.png diff --git a/images/mail_24.png b/images/mail_24.png deleted file mode 100644 index a4bf2e3..0000000 Binary files a/images/mail_24.png and /dev/null differ diff --git a/images/no_16.gif b/images/no_16.gif deleted file mode 100644 index 130b7e2..0000000 Binary files a/images/no_16.gif and /dev/null differ diff --git a/images/no_24.png b/images/no_24.png deleted file mode 100644 index 217dad2..0000000 Binary files a/images/no_24.png and /dev/null differ diff --git a/images/ok_48.png b/images/ok_48.png deleted file mode 100644 index 31e0b7d..0000000 Binary files a/images/ok_48.png and /dev/null differ diff --git a/images/options_24.png b/images/options_24.png deleted file mode 100644 index 993454d..0000000 Binary files a/images/options_24.png and /dev/null differ diff --git a/images/options_48.png b/images/options_48.png deleted file mode 100644 index 5bc6bf0..0000000 Binary files a/images/options_48.png and /dev/null differ diff --git a/images/paste_32.png b/images/paste_32.png deleted file mode 100644 index 4bd5444..0000000 Binary files a/images/paste_32.png and /dev/null differ diff --git a/images/paste_48.png b/images/paste_48.png deleted file mode 100644 index 41227c6..0000000 Binary files a/images/paste_48.png and /dev/null differ diff --git a/images/pdf_24.png b/images/pdf_24.png deleted file mode 100644 index a06937d..0000000 Binary files a/images/pdf_24.png and /dev/null differ diff --git a/images/ppt_24.png b/images/ppt_24.png deleted file mode 100644 index 42e0d83..0000000 Binary files a/images/ppt_24.png and /dev/null differ diff --git a/images/preview_24.png b/images/preview_24.png deleted file mode 100644 index d89d734..0000000 Binary files a/images/preview_24.png and /dev/null differ diff --git a/images/preview_48.png b/images/preview_48.png deleted file mode 100644 index 87bd645..0000000 Binary files a/images/preview_48.png and /dev/null differ diff --git a/images/print_24.png b/images/print_24.png deleted file mode 100644 index a9adbe1..0000000 Binary files a/images/print_24.png and /dev/null differ diff --git a/images/processing_16.gif b/images/processing_16.gif deleted file mode 100644 index c160783..0000000 Binary files a/images/processing_16.gif and /dev/null differ diff --git a/images/processing_48.gif b/images/processing_48.gif deleted file mode 100644 index f8bc3ea..0000000 Binary files a/images/processing_48.gif and /dev/null differ diff --git a/images/raw_24.png b/images/raw_24.png deleted file mode 100644 index 57c335e..0000000 Binary files a/images/raw_24.png and /dev/null differ diff --git a/images/raw_48.png b/images/raw_48.png deleted file mode 100644 index b183541..0000000 Binary files a/images/raw_48.png and /dev/null differ diff --git a/images/rss_24.png b/images/rss_24.png deleted file mode 100644 index 5475f29..0000000 Binary files a/images/rss_24.png and /dev/null differ diff --git a/images/santa_128.png b/images/santa_128.png deleted file mode 100644 index 9ea5a3d..0000000 Binary files a/images/santa_128.png and /dev/null differ diff --git a/images/sap_gold_200.jpg b/images/sap_gold_200.jpg new file mode 100644 index 0000000..b431dda Binary files /dev/null and b/images/sap_gold_200.jpg differ diff --git a/images/save_24.png b/images/save_24.png deleted file mode 100644 index a1c5169..0000000 Binary files a/images/save_24.png and /dev/null differ diff --git a/images/screenshot_16.png b/images/screenshot_16.png deleted file mode 100644 index 225370b..0000000 Binary files a/images/screenshot_16.png and /dev/null differ diff --git a/images/screenshot_24.png b/images/screenshot_24.png deleted file mode 100644 index 476af87..0000000 Binary files a/images/screenshot_24.png and /dev/null differ diff --git a/images/search_48.png b/images/search_48.png deleted file mode 100644 index aa3f087..0000000 Binary files a/images/search_48.png and /dev/null differ diff --git a/images/share_24.png b/images/share_24.png deleted file mode 100644 index 359ae9b..0000000 Binary files a/images/share_24.png and /dev/null differ diff --git a/images/share_48.png b/images/share_48.png deleted file mode 100644 index 355bd11..0000000 Binary files a/images/share_48.png and /dev/null differ diff --git a/images/smile_100.gif b/images/smile_100.gif deleted file mode 100644 index d0b4284..0000000 Binary files a/images/smile_100.gif and /dev/null differ diff --git a/images/xls_24.png b/images/xls_24.png deleted file mode 100644 index d99cd8a..0000000 Binary files a/images/xls_24.png and /dev/null differ diff --git a/images/yes_24.png b/images/yes_24.png deleted file mode 100644 index 16fa275..0000000 Binary files a/images/yes_24.png and /dev/null differ diff --git a/images/yes_48.png b/images/yes_48.png deleted file mode 100644 index 31e0b7d..0000000 Binary files a/images/yes_48.png and /dev/null differ diff --git a/images/zip_24.png b/images/zip_24.png deleted file mode 100644 index 0f34d7e..0000000 Binary files a/images/zip_24.png and /dev/null differ diff --git a/inc/auth.php b/inc/auth.php new file mode 100644 index 0000000..fdfe892 --- /dev/null +++ b/inc/auth.php @@ -0,0 +1,25 @@ +self::COST)); + } + + public static function CheckPassword($sPass, $sHash) + { + return password_verify($sPass, $sHash); + } +} + +?> \ No newline at end of file diff --git a/inc/databap.php b/inc/databap.php new file mode 100644 index 0000000..858d9d3 --- /dev/null +++ b/inc/databap.php @@ -0,0 +1,2507 @@ +..- + const EXPECTED_PAGE_COOKIE = 'exp_page'; + const MAIN_SEPARATOR = ' '; + const DATE_FORMAT = 'd/m/Y'; + const TIME_FORMAT = 'H:i:s'; + const DATE_TIME_FORMAT = 'd/m/Y H:i:s'; + const DATE_COMPACT_FORMAT = 'YmdHis'; + const DATE_TIME_SQL_FORMAT = 'Y-m-d H:i:s'; + const DATE_SQL_FORMAT = 'Y-m-d'; + const HISTORY_LENGTH = 10; + const STYLE_PATH = 'style/screen.css'; + public static $UPLOAD_IMG_EXTS = array('jpg', 'jpeg', 'gif', 'png'); + public static $UPLOAD_DOC_EXTS = array('jpg', 'jpeg', 'gif', 'png', 'doc', 'ppt', 'pdf', 'xls', 'docx', 'pptx', 'xlsx'); + const ID_SEPARATOR = '_'; + + //Database constant + const USER_TABLE = 'users'; + const COMP_TABLE = 'companies'; + const CODE_TABLE = 'codes'; + const URL_TABLE = 'urls'; + const MSG_TABLE = 'messages'; + const CHAN_TABLE = 'channels'; + const CONN_TABLE = 'connections'; + const ART_TABLE = 'articles'; + const OPT_TABLE = 'options'; + const OPTVAL_TABLE = 'option_values'; + const OPTNAME_TABLE = 'option_names'; + const PROC_TABLE = 'procedures'; + const STEP_TABLE = 'steps'; + const IMG_TABLE = 'images'; + const DOC_TABLE = 'docs'; + const FILE_TABLE = 'files'; + const SEARCH_TABLE = 'searchs'; + const TABL_TABLE = 'tables'; + + //Code + const MAX_LIST_LENGTH = 5; + + //Authorizations + const MEMBER_ACTIVE = 1; + const MEMBER_INACTIVE = 0; + const CLEARANCE_MEMBER = 0; + const CLEARANCE_ADMIN = 9; + const EXT_ACCESS = 'external_access'; + const USER_COOKIE_ID = 'count'; + const USER_COOKIE_PASS = 'checksum'; + const COOKIE_LIFESPAN = 7; + const FIRST_LAST_SEP = ' '; + const NAME_PASS_SEP = '-'; + + //HTTP Requests response + const DISCONNECTED = '__DISCONNECTED__'; + const ERROR = '__ERROR__'; + const SUCCESS = '__SUCCESS__'; + + //Chat Constants + //TODO Transfer these constants to chat page + const MESSAGE_USER = 'U'; + const MESSAGE_ADD_CODE = 'A'; + const MESSAGE_EDIT_CODE = 'E'; + const MESSAGE_ADD_PROC = 'PA'; + const MESSAGE_EDIT_PROC = 'PE'; + const MESSAGE_ADD_DOC = 'DA'; + const MESSAGE_EDIT_DOC = 'DE'; + const MESSAGE_ADD_TABLE = 'TA'; + const MESSAGE_EDIT_TABLE = 'TE'; + const MESSAGE_ACTION = 'M'; + const MESSAGE_PRIVATE = 'P'; + const MESSAGE_IMG = 'I'; + const MESSAGE_9GAG = '9'; + const MESSAGE_NICK = 'N'; + const MESSAGE_STATUS = 'S'; + const MESSAGE_CONN = 'C'; + const MESSAGE_INVITE = 'V'; + const MESSAGE_REBOOT = 'R'; + const MESSAGE_ARTICLE = 'B'; + const MESSAGE_NEWS = 'NW'; + const DEFAULT_COMPANY_LOGO = 'logo_unknown_24.png'; + const ALL_CHAN_ID = 1; + const ALL_CHAN_TEXT = 'A.l.l_C.h.a.n_I.n.c.l.u.d.e.d'; + const DEFAULT_CHAN_ID = 2; + const DEFAULT_CHAN = 'Principal'; + const KEEP_ALIVE = 600; //seconds + const REBOOT_DELAY = 15; //seconds + const PM_SEP = '___'; + const CHAT_IMG_MAX_WIDTH = 700; + const CHAT_IMG_MAX_HEIGHT = 1080; + const JSON_PREFIX = '[\-JSON-/]'; + + //Options Name Id Constants + const LANG_FR = 'FR'; + const OPT_TEXT = 'T'; + const OPT_PASS = 'P'; + const OPT_SELECT = 'S'; + const OPT_NICKNAME = 1; + const OPT_BG = 2; + const OPT_BRIGHT_BG = 3; + const OPT_HOVER = 4; + const OPT_IMAGE_CHAT = 6; + const OPT_STATUS = 7; + const OPT_CONSOLE = 8; + + //Options Values Id Constants + const OPT_CONSOLE_YES = 1; + const OPT_CONSOLE_NO = 2; + + //Options Values Constants + const OPT_VAL_YES = 'oui'; + const OPT_VAL_NO = 'non'; + + //Search Constants + const CODE_TYPE = 'c'; + const PROC_TYPE = 'p'; + const ART_TYPE = 'a'; + const DOC_TYPE = 'd'; + const TABLE_TYPE = 't'; + public static $HASH_TO_PAGE = array(self::CODE_TYPE=>'code', 'code'=>'code', + self::PROC_TYPE=>'proc', 'proc'=>'proc', 'procedure'=>'proc', + self::TABLE_TYPE=>'table', 'table'=>'table', + self::DOC_TYPE=>'doc', 'doc'=>'doc', 'documentation'=>'doc', + self::ART_TYPE=>'article', 'art'=>'article', 'article'=>'article', + 'list'=>'list', 'liste'=>'list', + 'chat'=>'chat', + 'profil'=>'profile', 'profile'=>'profile', + 'options'=>'options', + 's'=>'search', 'r'=>'search', 'search'=>'search', 'recherche'=>'search', + 'accueil'=>'welcome', + 'erreur'=>'error', + 'logout'=>'logout'); + + //Doc constants + const DOC_FOLDER = 'docs/'; + const DOC_TMP_FOLDER = 'docs/tmp/'; + const DOC_THUMB_FOLDER = 'docs/thumb/'; + + //Objects + private $oMySql; + private $oSearchEngine; + private $oClassManagement; + private $oAuth; + + //Variables + private $iUserId; + private $asUserInfo; + private $sLanguage; + + /** + * Constructor + * @param ClassManagement $oClassManagement + */ + function __construct($oClassManagement) + { + parent::__construct(__CLASS__, Settings::DEBUG); + $this->oClassManagement = $oClassManagement; + $this->oClassManagement->incClass('auth'); + $this->oClassManagement->incClass('mysqlmanager'); + $this->oClassManagement->incClass('procedure'); + $this->oClassManagement->incClass('searchengine'); + $this->oClassManagement->incClass('mask'); + + //Hasher + $this->oAuth = new Auth(); + + //Browser <> PHP <> MySql synchronization + date_default_timezone_set(Settings::TIMEZONE); + ini_set('default_charset', Settings::TEXT_ENC); + header('Content-Type: text/html; charset='.Settings::TEXT_ENC); + mb_internal_encoding(Settings::TEXT_ENC); + mb_http_output(Settings::TEXT_ENC); + mb_http_input(Settings::TEXT_ENC); + mb_language('uni'); + mb_regex_encoding(Settings::TEXT_ENC); + + //Passing settings down to mySQL + $this->oMySql = new MySqlManager(Settings::DB_SERVER, Settings::DB_LOGIN, Settings::DB_PASS, Settings::DB_NAME, self::getSqlOptions(), Settings::DB_ENC); + if($this->oMySql->sDbState == MySqlManager::DB_NO_DATA) $this->install(); + + //Init other variables + $this->setUserId(0); + $this->sLanguage = self::LANG_FR; + $this->oSearchEngine = new SearchEngine($this->oMySql); + } + + public static function getSqlOptions() + { + $asOptions = array(); + $asOptions['tables'] = array + ( + self::USER_TABLE => array('first_name', 'last_name', 'email', MySqlManager::getId(self::COMP_TABLE), 'pass', 'auth_cookie', 'active', 'clearance'), + self::COMP_TABLE => array(MySqlManager::getText(self::COMP_TABLE), 'logo'), + self::CODE_TABLE => array(MySqlManager::getText(self::CODE_TABLE), 'description', MySqlManager::getId(self::USER_TABLE), 'refer_id'), + self::URL_TABLE => array(MySqlManager::getId(self::CODE_TABLE), 'phrase'), + self::MSG_TABLE => array(MySqlManager::getId(self::USER_TABLE), 'nickname', MySqlManager::getId(self::CHAN_TABLE), MySqlManager::getText(self::MSG_TABLE), 'type', 'date'), + self::CHAN_TABLE => array('safe_name', MySqlManager::getText(self::CHAN_TABLE)), + self::CONN_TABLE => array(MySqlManager::getId(self::USER_TABLE), MySqlManager::getId(self::CHAN_TABLE)), + self::OPT_TABLE => array(MySqlManager::getId(self::USER_TABLE), MySqlManager::getId(self::OPTNAME_TABLE), MySqlManager::getId(self::OPTVAL_TABLE), MySqlManager::getText(self::OPT_TABLE)), + self::OPTNAME_TABLE => array(MySqlManager::getText(self::OPTNAME_TABLE), 'type', 'language'), + self::OPTVAL_TABLE => array(MySqlManager::getId(self::OPTNAME_TABLE), MySqlManager::getText(self::OPTVAL_TABLE), 'language'), + self::PROC_TABLE => array(MySqlManager::getId(self::USER_TABLE), 'title', 'description', 'refer_id'), + self::STEP_TABLE => array(MySqlManager::getId(self::PROC_TABLE), 'description'), + self::IMG_TABLE => array(MySqlManager::getId(self::PROC_TABLE), MySqlManager::getId(self::STEP_TABLE), 'description', 'file_name'), + self::DOC_TABLE => array(MySqlManager::getId(self::USER_TABLE), 'title', 'description', 'refer_id'), + self::FILE_TABLE => array(MySqlManager::getId(self::DOC_TABLE), 'description', 'file_name'), + self::SEARCH_TABLE => array('id_item', 'refer_id', 'type', 'keywords'), + self::ART_TABLE => array('title', 'link', 'date', 'first_name', 'last_name', 'email'), + self::TABL_TABLE => array(MySqlManager::getId(self::USER_TABLE), 'title', 'description', 'system', 'keywords', 'refer_id') + ); + $asOptions['types'] = array + ( + 'first_name' => "varchar(20) NOT NULL", + 'last_name' => "varchar(20) NOT NULL", + 'nickname' => "varchar(50) NOT NULL", + 'email' => "varchar(100) NOT NULL", + 'pass' => "varchar(255) NOT NULL", + 'auth_cookie' => "varchar(255) NOT NULL", + 'active' => "tinyint(1) DEFAULT ".self::MEMBER_ACTIVE, + 'clearance' => "int(1) DEFAULT ".self::CLEARANCE_MEMBER, + MySqlManager::getText(self::CODE_TABLE) => "longtext NOT NULL", + 'title' => "varchar(200) NOT NULL", + 'description' => "varchar(500) NOT NULL", + 'link' => "varchar(200) NOT NULL", + 'refer_id' => "int(10) UNSIGNED NOT NULL", + 'phrase' => "varchar(50) NOT NULL", + MySqlManager::getText(self::MSG_TABLE) => "varchar(500) NOT NULL", + 'type' => "varchar(2) NOT NULL", + 'safe_name' => "varchar(50) NOT NULL", + MySqlManager::getText(self::CHAN_TABLE) => "varchar(50) NOT NULL", + MySqlManager::getText(self::OPT_TABLE) => "varchar(100) NOT NULL", + MySqlManager::getText(self::OPTNAME_TABLE) => "varchar(100) NOT NULL", + MySqlManager::getText(self::OPTVAL_TABLE)=> "varchar(50) NOT NULL", + 'language' => "varchar(2) NOT NULL", + 'file_name' => "varchar(40) NOT NULL", + 'preview_name' => "varchar(40) NOT NULL", + 'id_item' => "int(10) UNSIGNED NOT NULL", + 'keywords' => "longtext NOT NULL", + MySqlManager::getText(self::COMP_TABLE) => "varchar(30) NOT NULL", + 'logo' => "varchar(20) NOT NULL", + 'date' => "date NOT NULL", + 'system' => "varchar(3)" + ); + $asOptions['constraints'] = array + ( + self::USER_TABLE => "UNIQUE KEY `user_first_and_last_name` (`first_name`, `last_name`)", + self::URL_TABLE => "UNIQUE KEY `uni_phrase` (`phrase`)", + self::MSG_TABLE => "INDEX(`date`)", + self::ART_TABLE => "INDEX(`title`)" + ); + $asOptions['cascading_delete'] = array + ( + self::CODE_TABLE => array(self::URL_TABLE), + self::PROC_TABLE => array(self::STEP_TABLE, self::IMG_TABLE) + ); + return $asOptions; + } + + //TODO @idea make getParam($sType, $sKey='') + private function getItemTables($sType='') + { + $asItemTables = array( self::CODE_TYPE=>self::CODE_TABLE, + self::PROC_TYPE=>self::PROC_TABLE, + self::ART_TYPE=>self::ART_TABLE, + self::DOC_TYPE=>self::DOC_TABLE, + self::TABLE_TYPE=>self::TABL_TABLE); + if($sType!='' && !array_key_exists($sType, $asItemTables)) $this->addError('Type "'.$sType.'" inconnu'); + return $sType==''?$asItemTables:$asItemTables[$sType]; + } + + private function getPageTitles($sPage='') + { + $asPageTitles = array( 'code'=>'Code', 'chat'=>'Chat', 'doc'=>'Documentation', 'error'=>'Page introuvable', + 'list'=>'La liste', 'options'=>'Paramètres', 'proc'=>'Procédure', 'profile'=>'Profil', + 'search'=>'Recherche', 'welcome'=>'Bienvenue', 'table'=>'Table ECC / BW', 'article'=>'Blog SAP', 'logout'=>'Déconnexion'); + if($sPage!='' && !array_key_exists($sPage, $asPageTitles)) $this->addError('Page "'.$sPage.'" inconnu'); + return $sPage==''?$asPageTitles:$asPageTitles[$sPage]; + } + + private function getPagesFromHash($sHash='') + { + $asHashToPage = self::$HASH_TO_PAGE; + if($sHash!='' && !array_key_exists($sHash, $asHashToPage)) $this->addError('Hash "'.$sHash.'" inconnu'); + return $sHash==''?$asHashToPage:$asHashToPage[$sHash]; + } + + private function install() + { + //Install DB + $this->oMySql->install(); + + //Options + $sOptionNameCol = MySqlManager::getText(self::OPTNAME_TABLE); + $sOptionValueCol = MySqlManager::getText(self::OPTVAL_TABLE); + $sOptionNameIdCol = MySqlManager::getId(self::OPTNAME_TABLE); + $sOptionValueIdCol = MySqlManager::getId(self::OPTVAL_TABLE); + $iNicknameOptId = $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_NICKNAME, $sOptionNameCol=>'pseudo du chat', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); + $iBgColorOptId = $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_BG, $sOptionNameCol=>'couleur de fond', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); + $iBgColorOpt2Id = $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_BRIGHT_BG, $sOptionNameCol=>'couleur de fond 2 (claire)', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); + $iHoverColorOptId = $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_HOVER, $sOptionNameCol=>'couleur de survol', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); + $iChatImageOptId = $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_IMAGE_CHAT, $sOptionNameCol=>'image du chat', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); + $iStatusOptId = $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_STATUS, $sOptionNameCol=>'mission en cours', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); + $iConsoleOptId = $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_CONSOLE, $sOptionNameCol=>'afficher la console du chat', 'type'=>self::OPT_SELECT, 'language'=>self::LANG_FR)); + + //Console Option + $asConsoleValues = array(self::OPT_CONSOLE_YES=>self::OPT_VAL_YES, self::OPT_CONSOLE_NO=>self::OPT_VAL_NO); + foreach($asConsoleValues as $sConsoleValId=>$sConsoleValue){$this->oMySql->insertRow(self::OPTVAL_TABLE, array($sOptionValueIdCol=>$sConsoleValId, $sOptionNameIdCol=>self::OPT_CONSOLE, $sOptionValueCol=>$sConsoleValue, 'language'=>self::LANG_FR));} + + //Insert default and all channels + $this->oMySql->insertRow(self::CHAN_TABLE, array('safe_name'=>self::getChanSafeName(self::ALL_CHAN_TEXT), MySqlManager::getText(self::CHAN_TABLE)=>self::ALL_CHAN_TEXT)); + $this->oMySql->insertRow(self::CHAN_TABLE, array('safe_name'=>self::getChanSafeName(self::DEFAULT_CHAN), MySqlManager::getText(self::CHAN_TABLE)=>self::DEFAULT_CHAN)); + + //Install default users : admin and test + $iAdminId = $this->addUser('françois', 'lutran', 'cgi', 'francois@lutran.fr', self::CLEARANCE_ADMIN); + $this->addUser('test', 'test', 'test', 'test@test.com'); + + //Write the SAP blog parser bash script to main folder + @file_put_contents('sap_website_parser.sh', "#!/bin/bash\n\n/usr/bin/php -f index.php a=external_access p=sap_blog auth_token=".$iAdminId.'_'.$this->generateExternalAccessToken($iAdminId)); + } + + private function setUserId($iUserId) + { + $this->iUserId = $iUserId; + $this->asUserInfo = ($iUserId>0)?$this->getUserInfo($iUserId):array(); + } + + public function getUserId() + { + return $this->iUserId; + } + + public function getPage($sPage, $asVars=array()) + { + $oPage = new Mask('index'); + $oPage->setTag('version', self::VERSION); + $oPage->setTag('text_enc', Settings::TEXT_ENC); + $oPage->setTag('index_link', $_GET['serv_name']); + $oPage->setTag('rss_link', $this->generateExternalAccessLink('rss', $this->getUserid())); + + //Constants + $asConstants = array( 'version'=>self::VERSION, + 'default_page'=>$sPage, + 'disconnected'=>self::DISCONNECTED, + 'success'=>self::SUCCESS, + 'error'=>self::ERROR, + 'keep_alive'=>self::KEEP_ALIVE, + 'opt_type_text'=>self::OPT_TEXT, + 'opt_type_pass'=>self::OPT_PASS, + 'opt_type_select'=>self::OPT_SELECT, + 'max_size'=>self::getMaxSize(), + 'authorized_img_exts'=>self::$UPLOAD_IMG_EXTS, + 'authorized_file_exts'=>self::$UPLOAD_DOC_EXTS, + 'id_sep'=>self::ID_SEPARATOR, + 'mask_folder'=>Mask::MASK_FOLDER, + 'image_folder'=>Procedure::IMAGE_FOLDER, + 'image_folder_tmp'=>Procedure::IMAGE_FOLDER_TMP, + 'image_folder_thumb'=>Procedure::IMAGE_FOLDER_THUMB, + 'default_chan'=>self::DEFAULT_CHAN, + 'all_chan_id'=>self::ALL_CHAN_ID, + 'all_chan_text'=>self::ALL_CHAN_TEXT, + 'pm_separator'=>self::PM_SEP, + 'reboot_delay'=>self::REBOOT_DELAY, + 'versionHtml'=>$this->getItemBlock()); + $oPage->setTag('constants', $this->jsonConvert($asConstants)); + + //Variables + $asVars['page_titles'] = $this->getPageTitles(); + $asVars['user_id'] = $this->getUserId(); + $asVars['hash_to_page'] = $this->getPagesFromHash(); + $asVars['page_to_hash'] = array_flip($asVars['hash_to_page']); + $oPage->setTag('variables', $this->jsonConvert($asVars)); + + return $oPage->getMask(); + } + + public function getLogonPage($bFirstConn) + { + $oPage = new Mask('logon'); + $oPage->setTag('feedback', $bFirstConn?'':'Données incorrectes'); + $oPage->setTag('version', self::VERSION); + $oPage->setTag('name_pass_sep', self::NAME_PASS_SEP); + return $oPage->getMask(); + } + + public function getRss($sCat='') + { + //Class Feed + $this->oClassManagement->incClass('rss'); + + //Building header + $asDesc = array + ( + 'title'=>'Flux RSS Databap'.($sCat==''?'':' - '.$sCat), + 'link'=>$this->getInternalLink('rss', $sCat), + 'copyright'=>'Powered by Francois Lutran. RSS Feed Generator under GPLv3 License', + 'description'=>'Flux RSS du chat'.($sCat==''?'':', section '.$sCat), + 'language'=>'fr', + 'webmaster_mail'=>'francois@lutran.fr' + ); + $oFeed = new Feed($asDesc); + + //Building items + switch(mb_strtolower($sCat)) + { + case '9gag': + $sRegEx = '(https?://|www\\.)(.*)9gag\\.com'; + break; + case 'youtube': + $sRegEx = '(https?://|www\\.)(.*)youtube\\.com'; + break; + case '': + $sRegEx = '(https?://|www\\.)[\.A-Za-z0-9\-]+\\.[a-zA-Z]{2,4}'; + break; + default: + $sRegEx = '(https?://|www\\.)(.*)'.$sCat.'(.*)\\.[a-zA-Z]{2,4}'; + break; + } + $asResult = $this->oMySql->selectRows(array('select'=>array('id_message', 'nickname', 'message', 'led'), 'from'=>'messages', 'constraint'=>array('message'=>$sRegEx), 'constOpe'=>array('message'=>' REGEXP '))); + $sPattern = '/(https?\:\/\/|www\.)[\S]+\.[a-zA-Z]{2,4}([\S]*)/ui'; + + foreach($asResult as $iLinkId=>$asRow) + { + //get link out of message + preg_match($sPattern, $asRow['message'], $asMatches); + $asRow['link'] = (substr($asMatches[0], 0, 4)=='http')?$asMatches[0]:'http://'.$asMatches[0]; + + //add item + $sChatlink = $this->getInternalLink('chat', $asRow['id_message']); + $asItem = array + ( + 'title'=>'Lien'.($sCat==''?'':' - '.$sCat).' #'.($iLinkId+1), + 'category'=>$sCat, + 'description'=>'Lien posté par '.self::getNickNameFormat($asRow['nickname']).' à '.self::getDateFormat($asRow['led']).' : '.$asRow['message'].'', + 'author'=>$asRow['nickname'], + 'link'=>$sChatlink, + 'pub_date'=>$asRow['led'], + 'guid'=>$sChatlink + ); + $oFeed->addItem($asItem); + } + + return $oFeed->getFeed(); + } + + public function syncSapBlog() + { + //Get articles list + $asArticles = $this->getSAPBlogRss(); + + //Update and spread the news + $asResult = array(); + if(date('N')>5 || date('G')>19) $asResult[] = "Pas de mise-à-jour pendant le week end ou en soirée."; + else + { + foreach($asArticles as $asArticle) + { + $iArticleId = $this->oMySql->selectValue(self::ART_TABLE, MySqlManager::getId(self::ART_TABLE), array('title'=>$asArticle['title'])); + + if($iArticleId==0) + { + $iArticleId = $this->oMySql->insertRow(self::ART_TABLE, $asArticle); + $this->addMessage($iArticleId, self::MESSAGE_ARTICLE, self::DEFAULT_CHAN_ID); + $this->oSearchEngine->buildIndex($iArticleId, self::ART_TYPE); + $sResult = 'ADDED'; + } + else $sResult = 'OK'; + $asResult[$iArticleId] = $sResult.' | '.implode(' | ', $asArticle); + } + } + return MySqlManager::implodeAll($asResult, '->', "\n"); + } + + private function getSAPBlogRss() + { + $sSAPDomain = 'http://scn.sap.com'; + $asBlogPaths = array( '/community/data-warehousing/bw/blog', + '/community/data-warehousing/blog/', + '/community/bw-hana/blog'); + + //TODO add http://www.biportal.org/sap_bi_blog + foreach($asBlogPaths as $sSAPBlogPath) + { + $sSAPBlogUrl = $sSAPDomain.$sSAPBlogPath; + $oDom = $this->getRemotePageDom($sSAPBlogUrl); + $aoArticles = $oDom->getElementsByTagName('header'); + + $asArticles = array(); + foreach($aoArticles as $oArticle) + { + if($oArticle->getAttribute('class')=='jive-blog-post-subject') + { + $asArticleInfo = array(); + $aoLinks = $oArticle->getElementsByTagName('a'); + foreach($aoLinks as $oLink) + { + switch($oLink->getAttribute('class')) + { + //Title & link + case 'font-color-normal': + $asArticleInfo['title'] = ucfirst(trim($oLink->nodeValue)); + $asArticleInfo['title'] = mb_substr($asArticleInfo['title'], -1)=='.'?mb_substr($asArticleInfo['title'], 0, -1):$asArticleInfo['title']; + $asArticleInfo['link'] = $oLink->getAttribute('href'); + $asArticleInfo['date'] = mb_substr(str_replace(array($sSAPBlogUrl.'/', '/'), array('', '-'), $asArticleInfo['link']), 0, 10); + break; + //Author + case 'jiveTT-hover-user jive-username-link': + $asNames = array_filter(explode(' ', ToolBox::mb_ucwords(trim($oLink->nodeValue)))); + $asArticleInfo['first_name'] = array_shift($asNames); + $asArticleInfo['last_name'] = implode('-', $asNames); + $asArticleInfo['email'] = $sSAPDomain.$oLink->getAttribute('href'); + break; + } + } + $asArticles[] = $asArticleInfo; + } + } + } + return $asArticles; + } + + private function get9gagPost($sUrl) + { + $asPost = array('url'=>$sUrl); + $oDom = $this->getRemotePageDom($sUrl); + + $oDivs = $oDom->getElementsByTagName('section'); + foreach($oDivs as $oPost) {if($oPost->getAttribute('id')=='individual-post') break;} + + //Title + $asPost['title'] = $oPost->getElementsByTagName('h2')->item(0)->nodeValue; + + //Image + $oImages = $oPost->getElementsByTagName('img'); + foreach($oImages as $oImage) + { + switch($oImage->getAttribute('class')) + { + case 'badge-item-animated-img': + $o9gagImage = $oImage; + break 2; + case 'badge-item-img': + $o9gagImage = $oImage; + break; + } + } + $asResult = $this->downloadToTmp($o9gagImage->getAttribute('src')); + + if($asResult['error']=='') + { + $asPost['url_img'] = $asResult['out']; + $asPost['width'] = $asResult['width']; + $asPost['height'] = $asResult['height']; + } + + return $asPost; + } + + private function getRemotePageDom($sUrl) + { + $oDom = new DOMDocument(); + @$oDom->loadHTML(file_get_contents($sUrl)); + return $oDom->getElementsByTagName('body')->item(0); + } + + public function addUser($sFirstName, $sLastName, $sCompany, $sEmail='', $iClearance=self::CLEARANCE_MEMBER) + { + $sFirstName = mb_strtolower($sFirstName); + $sLastName = mb_strtolower($sLastName); + $sCompany = mb_strtolower($sCompany); + $sEmail = mb_strtolower($sEmail); + + //Checking company existency in company table + $sCompanyTextCol = MySqlManager::getText(self::COMP_TABLE); + $iCompanyId = $this->oMySql->selectInsert(self::COMP_TABLE, array($sCompanyTextCol=>$sCompany, 'logo'=>self::DEFAULT_COMPANY_LOGO), array($sCompanyTextCol)); + + $asInfo = array('first_name'=>$sFirstName, + 'last_name'=>$sLastName, + 'email'=>$sEmail, + 'active'=>self::MEMBER_ACTIVE, + MySqlManager::getId(self::COMP_TABLE)=>$iCompanyId, + 'pass'=>$this->oAuth->HashPassword(self::getLoginToken($sCompany)), + 'clearance'=>$iClearance, + 'led'=>'0000-00-00 00:00:00'); + $iUserId = $this->oMySql->insertRow(self::USER_TABLE, $asInfo); + + //Admin Options + $sOptionTable = self::OPT_TABLE; + $sUserIdCol = MySqlManager::getId(self::USER_TABLE); + $sOptNameIdCol = MySqlManager::getId(self::OPTNAME_TABLE); + $sOptionTextCol = MySqlManager::getText(self::OPT_TABLE); + $this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_NICKNAME, $sOptionTextCol=>$sFirstName)); + //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_BG, $sOptionTextCol=>'#04357B')); + //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_BRIGHT_BG, $sOptionTextCol=>'#D9E5F2')); + //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_HOVER, $sOptionTextCol=>'#EFAB00')); + //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_TOKEN, $sOptionTextCol=>$this->generateExternalAccessLink('rss', $iUserId))); + //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_IMAGE_CHAT, $sOptionTextCol=>'images/sap_gold_332.jpg')); + + return $iUserId; + } + + public function buildCompleteIndex() + { + $this->oMySql->emptyTable(self::SEARCH_TABLE); + foreach($this->getItemTables() as $sSearchType=>$sSearchTable) + { + $asItemIds = $this->oMySql->selectRows(array('select'=>MySqlManager::getId($sSearchTable), 'from'=>$sSearchTable)); + foreach($asItemIds as $iItemId) $this->oSearchEngine->buildIndex($iItemId, $sSearchType); + } + } + + //insert new code / version + public function addCode($asData) + { + $sContent = $asData['content']; + $sDescription = $asData['description']; + $sLink = str_replace(' ', '_', $asData['link']); + + //insert value in db + $asInsert = array( MySqlManager::getText(self::CODE_TABLE)=>$sContent, + 'description'=>$sDescription, + 'id_user'=>$this->getUserId()); + $iCodeId = $this->oMySql->insertRow(self::CODE_TABLE, $asInsert); + + //insert link if exists + if($sLink!='') + { + $asInsert = array(MySqlManager::getId(self::CODE_TABLE)=>$iCodeId, 'phrase'=>$sLink); + $this->oMySql->insertRow(self::URL_TABLE, $asInsert); + } + + //refer id update + $this->oMySql->updateRow(self::CODE_TABLE, $iCodeId, array('refer_id'=>$iCodeId)); + + //Add message + $this->addMessage($iCodeId, self::MESSAGE_ADD_CODE, self::DEFAULT_CHAN_ID); + + //Add record in Search Table + $this->oSearchEngine->buildIndex($iCodeId, self::CODE_TYPE); + + return $iCodeId; + } + + public function editCode($oCode, $sContent) + { + $asRef = $this->getCodeInfo($oCode, 'first'); + $iVersion = $this->oMySql->selectValue(self::CODE_TABLE, 'COUNT(id_code)', array('refer_id'=>$asRef['id_code']))+1; + $asEdit = array('code'=>$sContent, + 'description'=>$asRef['description'].' (v'.$iVersion.')', + 'id_user'=>$this->iUserId, + 'refer_id'=>$asRef['id_code']); + + $iCodeId = $this->oMySql->insertRow(self::CODE_TABLE, $asEdit); + + //Add message + $this->addMessage($iCodeId, self::MESSAGE_EDIT_CODE, self::DEFAULT_CHAN_ID); + + //Add record in Search Table + $this->oSearchEngine->buildIndex($iCodeId, self::CODE_TYPE); + + return $iCodeId; + } + + public function addProcedure($asPost) + { + $oProcedure = new Procedure($this->oMySql); + $asPost['id_user'] = $this->getUserId(); + $oProcedure->inputForm($asPost); + $asErrors = $oProcedure->checkIntegrity(); + if(count($asErrors)==0) + { + //Previous procedure Id + $iPrevProcId = isset($asPost['procedure_id'])?$asPost['procedure_id']:0; + + //Load procedure into database + $bCreation = $oProcedure->saveProcedure($iPrevProcId); + + //Extract extra data + $asProc = $oProcedure->getProcedure(); + $iNewProcId = $asProc['proc_id']; + $asUser = $this->getUserInfo($this->getUserId()); + + $oResult = array('result'=>'success', + 'proc_id'=>$iNewProcId, + 'led'=>$asProc['led'], + 'name'=>$asUser['name'], + 'company'=>$asUser['company']); + + //Add Message in chat + if($bCreation) + { + $this->addMessage($iNewProcId, self::MESSAGE_ADD_PROC, self::DEFAULT_CHAN_ID); + } + else + { + $this->addMessage($iNewProcId, self::MESSAGE_EDIT_PROC, self::DEFAULT_CHAN_ID); + } + + //Add record in Search Table + $this->oSearchEngine->buildIndex($iNewProcId, self::PROC_TYPE); + } + else + { + $oResult = array('result'=>'fail', 'errors'=>$asErrors); + } + + return self::jsonExport($oResult); + } + + public function getProcedure($iProcId) + { + $oProcedure = new Procedure($this->oMySql); + $oProcedure->loadProcedure($iProcId); + $asProc = $oProcedure->getProcedure(); + + //Adding user info + $asUser = $this->getUserInfo($asProc['id_user']); + $asProc['name'] = $asUser['name']; + $asProc['company'] = $asUser['company']; + + return self::jsonExport($asProc); + } + + public function addDoc($asPost) + { + //Previous Doc Id + $iPrevDocId = $asPost['doc_id']; + $bCreation = ($iPrevDocId==0); + + //User + $iUserId = $this->getUserId(); + + //Titles + $sTitle = isset($asPost['title'])?$asPost['title']:''; + $sDescription = isset($asPost['description'])?$asPost['description']:''; + + //Get docs + $sImagePattern = '/c1_(?P\d+)_image_(?P\w+)/u'; + foreach($asPost as $sFormId=>$sValue) + { + preg_match($sImagePattern, $sFormId, $asMatches); + if($asMatches['doc_info'] == 'name' || $asMatches['doc_info'] == 'desc') + { + $asDocs[$asMatches['doc_id']][$asMatches['doc_info']] = $sValue; + } + } + + //Load doc into database + $asData = array('title'=>$sTitle, 'description'=>$sDescription, MySqlManager::getId(self::USER_TABLE)=>$iUserId); + $iDbDocId = $this->oMySql->insertRow(self::DOC_TABLE, $asData); + + //Load doc files into database + $asDocData[MySqlManager::getId(self::DOC_TABLE)] = $iDbDocId; + foreach($asDocs as $asDocInfo) + { + //insert into database + $sFileName = $asDocInfo['name']; + $asDocData['description'] = $asDocInfo['desc']; + $asDocData['file_name'] = $sFileName; + $iDbImageId = $this->oMySql->insertRow(self::FILE_TABLE, $asDocData); + + //Move file + $sTempFilePath = self::DOC_TMP_FOLDER.$sFileName; + $sFilePath = self::DOC_FOLDER.$sFileName; + if(!rename($sTempFilePath, $sFilePath)) + { + $this->addError('Unmoveable file : '.$sTempFilePath); + } + } + + //Add this doc to the group + $iReferId = $bCreation?$iDbDocId:$this->oMySql->selectValue(self::DOC_TABLE, 'refer_id', $iPrevDocId); + $this->oMySql->updateRow(self::DOC_TABLE, $iDbDocId, array('refer_id'=>$iReferId)); + + //Add Message in chat + $this->addMessage($iDbDocId, $bCreation?self::MESSAGE_ADD_DOC:self::MESSAGE_EDIT_DOC, self::DEFAULT_CHAN_ID); + + //Add record in Search Table + $this->oSearchEngine->buildIndex($iDbDocId, self::DOC_TYPE); + + return self::jsonExport(array('result'=>'success','doc_id'=>$iDbDocId)); + } + + public function getDoc($iDocId) + { + //Extract doc data + $asDoc = $this->oMySql->selectRow(self::DOC_TABLE, $iDocId); + $asDoc['description'] = self::getDescriptionFormat($asDoc['description']); + $asDoc['title'] = self::getDescriptionFormat($asDoc['title']); + $asDoc['led'] = self::getDateFormat($asDoc['led']); + + //Extract extra data + $asUser = $this->getUserInfo($asDoc[MySqlManager::getId(self::USER_TABLE)]); + $asDoc['name'] = $asUser['name']; + $asDoc['company'] = $asUser['company']; + + //Extract doc files + $asFiles = $this->oMySql->selectRows(array('from'=>self::FILE_TABLE, 'constraint'=>array('id_doc'=>$iDocId))); + foreach($asFiles as $asFile) + { + $asDoc['files'][$asFile[MySqlManager::getId(self::FILE_TABLE)]] = array('description'=>self::getDescriptionFormat($asFile['description']), + 'ext'=>pathinfo($asFile['file_name'], PATHINFO_EXTENSION)); + } + + return self::jsonExport($asDoc); + } + + public function addTable($sSystem, $sTitle, $sDescription, $sKeyWords, $iPrevTableId=0) + { + $bCreation = ($iPrevTableId==0); + $iDbTableId = 0; + $sDesc = ''; + + //Previous table info + if(!$bCreation) $asPrevTableInfo = $this->getTableInfo($iPrevTableId, false); + + //New table info + $sTitle = mb_strtolower($bCreation?trim($sTitle):$asPrevTableInfo['title']); + $sDescription = mb_strtolower(trim($sDescription)); + $sRightTableLink = 'table '.self::getTableFormat($sTitle).''; + + //Check for existing table with the same name + if($bCreation && $this->checkValue(self::TABL_TABLE, array('title'=>$sTitle))) $sDesc = 'Une documentation existe déjà pour la '.$sRightTableLink; + elseif(!$bCreation && $iPrevTableId!=$this->getUpToDateId(self::TABL_TABLE, $sTitle)) $sDesc = 'Une version plus récente de cette documentation existe : '.$sRightTableLink; + else + { + //Load table into database + $asData = array(MySqlManager::getId(self::USER_TABLE)=>$this->getUserId(), 'title'=>$sTitle, 'description'=>$sDescription, 'system'=>$sSystem, 'keywords'=>$sKeyWords); + $iDbTableId = $this->oMySql->insertRow(self::TABL_TABLE, $asData); + + if(!$iDbTableId) $sDesc = 'Erreur inconnue dans la base de données'; + else + { + //Add the group refer id + $iReferId = $bCreation?$iDbTableId:$this->oMySql->selectValue(self::TABL_TABLE, 'refer_id', $iPrevTableId); + $this->oMySql->updateRow(self::TABL_TABLE, $iDbTableId, array('refer_id'=>$iReferId)); + + //Add Message in chat + $this->addMessage($iDbTableId, $bCreation?self::MESSAGE_ADD_TABLE:self::MESSAGE_EDIT_TABLE, self::DEFAULT_CHAN_ID); + + //Add record in Search Table + $this->oSearchEngine->buildIndex($iDbTableId, self::TABLE_TYPE); + } + } + return $this->getJsonPostResult($iDbTableId>0, $sDesc, array('id'=>$iDbTableId, 'name'=>$sTitle)); + } + + public function getTable($oTableId) + { + $bReadById = is_numeric($oTableId); + $iTableId = $bReadById?$oTableId:$this->getUpToDateId(self::TABL_TABLE, mb_strtolower($oTableId)); + + //Extract table data + $asTable = $this->getTableInfo($iTableId); + $bSuccess = array_key_exists(MySqlManager::getId(self::TABL_TABLE), $asTable); + $sDesc = ''; + if($bSuccess) + { + unset($asTable['timestamp']); + $asTable['date'] = self::getDateFormat($asTable['led'], self::DATE_FORMAT); + $asTable['id'] = $asTable[MySqlManager::getId(self::TABL_TABLE)]; + + //Extract extra data + $asUser = $this->getUserInfo($asTable[MySqlManager::getId(self::USER_TABLE)]); + $asTable['name'] = $asUser['name']; + $asTable['company'] = $asUser['company']; + + //Out of date warning + $sRightTableLink = 'table '.self::getTableFormat($asTable['title']).''; + $asTable['warning'] = ($bReadById && $iTableId!=$this->getUpToDateId(self::TABL_TABLE, $iTableId))?'Il existe une documentation plus à jour pour la '.$sRightTableLink:''; + } + else $sDesc = 'Table '.$oTableId.' introuvable'; + + return $this->getJsonPostResult($bSuccess, $sDesc, $asTable); + } + + //TODO good idea ? If yes, apply to all types. Put in MySqlManager ? involve switchCodeId() + private function getUpToDateId($sTableName, $oId) + { + if(is_numeric($oId)) $asConstraint = array('refer_id'=>$this->oMySql->selectValue(self::TABL_TABLE, 'refer_id', $oId)); //by Id + else $asConstraint = array('title'=>$oId); //by phrase + + $sItemIdCol = 'id_item'; + $asInfo = array('select'=>array("MAX(".MySqlManager::getId($sTableName).") AS ".$sItemIdCol), //selecting last version among codes having the same refer_id + 'from'=>$sTableName, + 'constraint'=>$asConstraint, + 'groupBy'=>array('refer_id'), + 'orderBy'=>array($sItemIdCol=>'desc')); + return array_shift($this->oMySql->selectRows($asInfo)); + } + + public function getFile($iFileId) + { + $sFileName = $this->oMySql->selectValue(self::FILE_TABLE, 'file_name', $iFileId); + $sFilePath = self::DOC_FOLDER.$sFileName; + $sFileFullPath = dirname($_SERVER['SCRIPT_FILENAME'])."/".$sFilePath; + + if(!file_exists($sFilePath)) + { + die(); + } + else + { + //get mime type + if(class_exists('finfo')) + { + $oFileInfo = new finfo(FILEINFO_MIME); + $sMimetype = $oFileInfo->file($sFileFullPath); + } + else + { + $sMimetype = 'application/force-download'; + } + + //set headers + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Cache-Control: public'); + header('Content-Description: File Transfer'); + header('Content-Type: '.$sMimetype); + header('Content-Disposition: attachment; filename='.$sFileName); + header('Content-Transfer-Encoding: binary'); + header('Content-Length: '.@filesize($sFilePath)); + + // download + if ($oFile = @fopen($sFilePath, 'rb')) + { + while(!feof($oFile)) + { + print(fread($oFile, 1024*8)); + flush(); + if(connection_status() != 0) + { + @fclose($oFile); + } + } + @fclose($oFile); + } + } + } + + public function uploadImage() + { + $this->oClassManagement->incClass('fileuploader'); + $oFileUploader = new fileUploader(Procedure::IMAGE_FOLDER_TMP, self::$UPLOAD_IMG_EXTS); + $asResult = $oFileUploader->handleUpload(); + + return $this->jsonConvert($asResult); + } + + public function uploadDoc() + { + $this->oClassManagement->incClass('fileuploader'); + $oFileUploader = new fileUploader(self::DOC_TMP_FOLDER, self::$UPLOAD_DOC_EXTS); + $asResult = $oFileUploader->handleUpload(); + + return $this->jsonConvert($asResult); + } + + //TODO: one function only with a type parameter + private function getCodeInfo($oCode, $sMode='', $bCode=false) + { + $iCodeId = is_numeric($oCode)?$oCode:$this->getIdCodeFromPhrase($oCode); + + //Mode (get last or first id_code) + if($sMode!='') + { + $this->switchCodeId($iCodeId, $sMode); + } + + //Efficient request : don't request for the entire code + if(!$bCode) + { + $asSelect = $this->oMySql->getTablecolumns(self::CODE_TABLE); + unset($asSelect[MySqlManager::getText(self::CODE_TABLE)]); + $asSelect = array_keys($asSelect); + } + else + { + $asSelect = "*"; + } + + $asCode = $this->oMySql->selectRow(self::CODE_TABLE, $iCodeId, $asSelect); + $asCode['description'] = self::getDescriptionFormat($asCode['description']); + $asCode['timestamp'] = strtotime($asCode['led']); + $asCode['led'] = self::getDateFormat($asCode['led']); + return $asCode; + } + + private function getProcInfo($iProcId) + { + $asProc = $this->oMySql->selectRow(self::PROC_TABLE, $iProcId); + $asProc['title'] = self::getDescriptionFormat($asProc['title']); + $asProc['description'] = self::getDescriptionFormat($asProc['description']); + $asProc['timestamp'] = strtotime($asProc['led']); + $asProc['led'] = self::getDateFormat($asProc['led']); + return $asProc; + } + + private function getDocInfo($iDocId) + { + $asDoc = $this->oMySql->selectRow(self::DOC_TABLE, $iDocId); + $asDoc['title'] = self::getDescriptionFormat($asDoc['title']); + $asDoc['description'] = self::getDescriptionFormat($asDoc['description']); + $asDoc['timestamp'] = strtotime($asDoc['led']); + $asDoc['led'] = self::getDateFormat($asDoc['led']); + return $asDoc; + } + + private function getTableInfo($iTableId, $bFormatting=true) + { + $asTable = $this->oMySql->selectRow(self::TABL_TABLE, $iTableId); + $asTable['timestamp'] = strtotime($asTable['led']); + if($bFormatting) + { + $asTable['system'] = self::getTableFormat($asTable['system']); + $asTable['title'] = self::getTableFormat($asTable['title']); + $asTable['description'] = self::getDescriptionFormat($asTable['description']); + $asTable['led'] = self::getDateFormat($asTable['led']); + } + return $asTable; + } + + private function getArticleInfo($iArtId) + { + $asArt = $this->oMySql->selectRow(self::ART_TABLE, $iArtId); + $asTransferredInfo = array('link_art'=>$asArt['link'], 'art_title'=>$asArt['title'], 'link_auth'=>$asArt['email']); + $asTransferredInfo['art_date'] = self::getDateFormat($asArt['date'], self::DATE_FORMAT); + $asTransferredInfo['name'] = self::getNameFormat($asArt['first_name'], $asArt['last_name']); + $asTransferredInfo['description'] = self::getDescriptionFormat($asArt['title']); + $asTransferredInfo['timestamp'] = strtotime($asArt['led']); + $asTransferredInfo['led'] = self::getDateFormat($asArt['led']); + $asTransferredInfo['company'] = 'SAP'; + return $asTransferredInfo; + } + + public function getUserInfo($iUserId, $bJson=false) + { + if($iUserId==$this->getUserId() && !empty($this->asUserInfo)) + { + $asUserInfo = $this->asUserInfo; + } + else + { + $asRow = $this->oMySql->selectRow(self::USER_TABLE, $iUserId); + $asCompany = $this->oMySql->selectRow(self::COMP_TABLE, $asRow[MySqlManager::getId(self::COMP_TABLE)]); + $asUserInfo = array( 'name'=>self::getNameFormat($asRow['first_name'], $asRow['last_name']), + 'nickname'=>self::getNickNameFormat($this->getChatNickNames($iUserId)), + 'email'=>$asRow['email'], + 'company'=>self::getCompanyFormat($asCompany[MySqlManager::getText(self::COMP_TABLE)]), + 'status'=>$this->getDescriptionFormat($this->getUserOptionValue(self::OPT_STATUS, $iUserId)), + 'logo'=>$asCompany['logo'], + 'clearance'=>$asRow['clearance'], + 'user_led'=>self::getDateFormat($asRow['led'], self::DATE_FORMAT)); + } + return $bJson?$this->jsonExport($asUserInfo):$asUserInfo; + } + + public function getUserClearance() + { + $asUserInfo = $this->getUserInfo($this->getUserId()); + return $asUserInfo['clearance']; + } + + public function getOptions() + { + $sOptValueIdCol = MySqlManager::getId(self::OPTVAL_TABLE); + $sOptionTextCol = MySqlManager::getText(self::OPT_TABLE); + $sOptNameTextCol = MySqlManager::getText(self::OPTNAME_TABLE); + $sOptValueTextCol = MySqlManager::getText(self::OPTVAL_TABLE); + + //Available Options + $asAvailableOptions = $this->getAvailableOptions(); + + //User options + $asUserOptions = $this->getUserOptions(); + + //Build Options panel + $asSelectedOptions = array(); + foreach($asAvailableOptions as $sOptNameId=>$asOption) + { + $asSelectedOptions[$sOptNameId]['option_name'] = self::getDescriptionFormat($asOption[$sOptNameTextCol]); + $asSelectedOptions[$sOptNameId]['user_value_id'] = array_key_exists($sOptNameId, $asUserOptions)?$asUserOptions[$sOptNameId][$sOptValueIdCol]:0; + $asSelectedOptions[$sOptNameId]['user_value'] = array_key_exists($sOptNameId, $asUserOptions)?$asUserOptions[$sOptNameId][$sOptionTextCol]:''; + $asSelectedOptions[$sOptNameId]['type'] = $asOption['type']; + if($asOption['type']==self::OPT_SELECT) + { + $asOptionValuesInfo = array('select'=>array($sOptValueIdCol, $sOptValueTextCol), + 'from'=>self::OPTVAL_TABLE, + 'constraint'=>array('language'=>$this->sLanguage, + MySqlManager::getId(self::OPTNAME_TABLE)=>$sOptNameId), + 'orderBy'=>array('led'=>'ASC')); + $asSelectedOptions[$sOptNameId]['select'] = $this->oMySql->selectRows($asOptionValuesInfo, true, $sOptValueIdCol); + } + } + return $this->jsonExport($asSelectedOptions); + } + + public function setOptions($asUserOptions, $bSilentUpdate=true) + { + foreach($this->getAvailableOptions() as $sOptNameId=>$asOption) + { + if(array_key_exists($sOptNameId, $asUserOptions)) + { + switch($asOption['type']) + { + case self::OPT_SELECT: //insert id of option value table + $sUpdateCol = MySqlManager::getId(self::OPTVAL_TABLE); + break; + case self::OPT_TEXT: //insert value + $sUpdateCol = MySqlManager::getText(self::OPT_TABLE); + break; + default: + continue 2; + } + $sNewValue = $asUserOptions[$sOptNameId]; + + //Check identical data + $asKeys = array(MySqlManager::getId(self::USER_TABLE)=>$this->getUserId(), MySqlManager::getId(self::OPTNAME_TABLE)=>$sOptNameId); + $asData = array($sUpdateCol=>$sNewValue) + $asKeys; + $sOldValue = $this->oMySql->selectValue(self::OPT_TABLE, $sUpdateCol, $asKeys); + + //Update value + if($sOldValue!=$sNewValue) + { + //Update value + $this->oMySql->insertUpdateRow(self::OPT_TABLE, $asData, array_keys($asKeys)); + + //Spread the word + if(!$bSilentUpdate) + { + //TODO rassembler les messages similaires dans une fonction de template + switch($sOptNameId) + { + case self::OPT_NICKNAME: + $sType = self::MESSAGE_NICK; + $sChanName = self::DEFAULT_CHAN; + $sMessage = $sOldValue.' a changé son pseudo en '.$sNewValue; + break; + case self::OPT_STATUS: + $sType = self::MESSAGE_STATUS; + $sChanName = self::DEFAULT_CHAN; + $sMessage = 'est sur une nouvelle mission : '.$this->getDescriptionFormat($sNewValue); + break; + default: + continue 2; + } + $sChanId = $this->getChanId($sChanName); + $this->addMessage($sMessage, $sType, $sChanId); + } + } + } + } + } + + private function getAvailableOptions() + { + $sOptNameIdCol = MySqlManager::getId(self::OPTNAME_TABLE); + $sOptNameTextCol = MySqlManager::getText(self::OPTNAME_TABLE); + $asInfo = array('select'=>array($sOptNameIdCol, $sOptNameTextCol, 'type'), + 'from'=>self::OPTNAME_TABLE, + 'constraint'=>array('language'=>$this->sLanguage)); + return $this->oMySql->selectRows($asInfo, true, $sOptNameIdCol); + } + + private function getUserOptionValue($sOptionNameId, $iUserId=0) + { + $asUserOptions = $this->getUserOptions($sOptionNameId, $iUserId); + $asOptionInfo = array_shift($asUserOptions); + return $asOptionInfo[MySqlManager::getId(self::OPTVAL_TABLE)]; + } + + private function getUserOptions($oOptionNameIds=array(), $iUserId=0) + { + $iUserId = $iUserId>0?$iUserId:$this->getUserId(); + $sUserIdCol = MySqlManager::getId(self::USER_TABLE); + $sOptNameIdCol = MySqlManager::getId(self::OPTNAME_TABLE); + $sOptValueIdCol = MySqlManager::getId(self::OPTVAL_TABLE); + $sOptionTextCol = MySqlManager::getText(self::OPT_TABLE); + + if(!is_array($oOptionNameIds)) + { + $oOptionNameIds = array($oOptionNameIds); + } + + $asUserinfo = array('select'=>array($sOptNameIdCol, $sOptValueIdCol, $sOptionTextCol), + 'from'=>self::OPT_TABLE, + 'constraint'=>array($sUserIdCol=>$iUserId)); + if(count($oOptionNameIds)>0) + { + $asUserinfo['constraint'][$sOptNameIdCol] = "(".implode(", ", $oOptionNameIds).")"; + $asUserinfo['constOpe'] = array($sUserIdCol=>"=", $sOptNameIdCol=>" IN "); + $asUserinfo['constVar'] = true; + } + $asOptions = $this->oMySql->selectRows($asUserinfo, true, $sOptNameIdCol); + foreach($asOptions as $iOptionId=>$asOption) + { + if(!$asOption[$sOptValueIdCol]) $asOptions[$iOptionId][$sOptValueIdCol] = $asOptions[$iOptionId][$sOptionTextCol]; + } + return $asOptions; + } + + public function getProfile($oUser) + { + switch($oUser) + { + case '': + $iUserId = $this->getUserId(); + break; + case is_numeric($oUser): + $iUserId = $oUser; + break; + case is_string($oUser): + $oRes = $this->oMySql->selectValue(self::OPT_TABLE, MySqlManager::getId(self::USER_TABLE), array('option'=>mb_strtolower($oUser), MySqlManager::getId(self::OPTNAME_TABLE)=>self::OPT_NICKNAME)); + $iUserId = !$oRes?$this->getUserId():$oRes; + break; + default: + $iUserId = $this->getUserId(); + break; + } + + //User Info + $asProfile['user'] = $this->getUserInfo($iUserId); + + //History Info + $asTables = $this->getItemTables(); + foreach($asTables as $sType=>$sTableName) + { + //Skip articles + if($sTableName==self::ART_TABLE) continue; + + //Add Text + $sText = mb_strtolower($this->getPageTitles($this->getPagesFromHash($sType))); + if(!$sText) $this->addError('Pas de texte pour le type "'.$sType.'"'); + + //Loop through items + $asSqlInfo = array('from'=>$sTableName, 'constraint'=>array(MySqlManager::getId(self::USER_TABLE)=>$iUserId)); + $asHistory = $this->oMySql->selectRows($asSqlInfo); + foreach($asHistory as $asInfo) + { + $sLed = $asInfo['led']; + $iItemId = $asInfo[MySqlManager::getId($sTableName)]; + $asProfile['history'][$sLed]['type'] = $sType; + $asProfile['history'][$sLed]['id'] = $iItemId; + $asProfile['history'][$sLed]['action'] = (($asInfo['refer_id']==$iItemId)?'Création':'Modification').' de '.$sText; + $asProfile['history'][$sLed]['date'] = self::getDateFormat($sLed); + $asProfile['history'][$sLed]['title'] = array_key_exists('title', $asInfo)?$asInfo['title']:$asInfo['description']; + } + } + + krsort($asProfile['history']); + return $this->jsonExport($asProfile); + } + + private static function isPmChan($sChanSafeName) + { + $asResult = array('is_pm'=>false, 'chan_name'=>$sChanSafeName); + + preg_match('/(?P\d+)'.self::PM_SEP.'(?P\d+)/u', $sChanSafeName, $asMatch); + if(!empty($asMatch)) + { + $asResult['is_pm'] = true; + $asResult['from'] = $asMatch['from']; + $asResult['to'] = $asMatch['to']; + if($asMatch['from'] > $asMatch['to']) $asResult['chan_name'] = $asMatch['to'].self::PM_SEP.$asMatch['from']; + else $asResult['chan_name'] = $asMatch['from'].self::PM_SEP.$asMatch['to']; + } + + return $asResult; + } + + private function checkChanAuth($sChanSafeName, $iUserId=0) + { + $asUserInfo = ($iUserId > 0)?$this->getUserInfo($iUserId):array('company'=>''); + $asCompanies = $this->oMySql->selectRows(array('select'=>MySqlManager::getText(self::COMP_TABLE), 'from'=>self::COMP_TABLE)); + $iCompChan = array_search($sChanSafeName, array_map(array('self', 'getChanSafeName'), $asCompanies)); + $asPm = $this->isPmChan($sChanSafeName); + + return $sChanSafeName!='' && //Empty channel name + ($iCompChan===false || $asUserInfo['company']==self::getCompanyFormat($asCompanies[$iCompChan])) && //Test Company Channel + (!$asPm['is_pm'] || $iUserId==$asPm['from'] || $iUserId==$asPm['to']); //Test PM + } + + public function joinChan($sChanName, $bFirstConn=false, $asAttendees=array()) + { + $asResult = array('success'=>self::ERROR); + $sSafeChanName = self::getChanSafeName($sChanName); + + //Authorization to join channel + $asMessages = array(); + if($this->checkChanAuth($sSafeChanName, $this->getUserId())) + { + //On first connection, display on-join message for all channels + if($bFirstConn) + { + $asConnChans = $this->getChannels($this->getUserId()); + $bFirstChan = true; + foreach($asConnChans as $iConnChanId=>$sConnChanName) + { + //Checking once for all channels + if(!$bFirstChan || !$this->isUserConnected($iConnChanId)) + { + $asMessages[$iConnChanId] = $sConnChanName; + } + else break; + $bFirstChan = false; + } + } + + //Add connection link in DB + $sUserIdCol = MySqlManager::getId(self::USER_TABLE); + $sChanIdCol = MySqlManager::getId(self::CHAN_TABLE); + + $asData = array('safe_name'=>$sSafeChanName, MySqlManager::getText(self::CHAN_TABLE)=>$sChanName); + $iChanId = $this->oMySql->insertUpdateRow(self::CHAN_TABLE, $asData, array('safe_name'), false); + + //Is user already connected to this chan + $bConnectedUser = $this->isUserConnected($iChanId); + $iConnId = $this->oMySql->insertUpdateRow(self::CONN_TABLE, array($sUserIdCol=>$this->getUserId(), $sChanIdCol=>$iChanId), array($sUserIdCol, $sChanIdCol), false); + if($iConnId>0) $asResult['success'] = self::SUCCESS; + + if($asResult['success']==self::SUCCESS) + { + //Return connected channels + $asResult['channels'] = $this->getChannels($this->getUserId()); + + //Add tab title (customized) + //FIXME delete this shit and insert the channel type into channels DB table + foreach($asResult['channels'] as $iConnectedChanId=>$sConnectedChanName) + { + $asPm = $this->isPmChan($sConnectedChanName); + $asResult['channel_tab_names'][$iConnectedChanId] = $asPm['is_pm']?$this->getChatNickNames($asPm['from']==$this->getUserId()?$asPm['to']:$asPm['from']):$sConnectedChanName; + } + $asResult['current_chan_id'] = $iChanId; + + //Communicate on user's connection + if(!$bConnectedUser) + { + $asMessages[$iChanId] = $this->oMySql->selectValue(self::CHAN_TABLE, MySqlManager::getText(self::CHAN_TABLE), $iChanId); + } + $sStatus = $this->getUserOptionValue(self::OPT_STATUS); + $asUserInfo = $this->getUserInfo($this->getUserId()); + foreach($asMessages as $iChanId=>$sMsgChanName) + { + $asPm = $this->isPmChan($sMsgChanName); + $this->addMessage(' de '.$asUserInfo['company'].' ('.($sStatus==''?'aucune mission en cours':$this->getDescriptionFormat($sStatus)).') rejoint '.($asPm['is_pm']?'le chan privé':'#'.$sMsgChanName), self::MESSAGE_CONN, $iChanId); + } + + //Send invites to attendees + if(!empty($asAttendees)) + { + $asUserInfo = $this->getUserInfo($this->getUserId()); + foreach($asAttendees as $sAttendee) + { + if($this->checkChanAuth($sSafeChanName, $sAttendee)) + { + $this->addMessage($sAttendee, self::MESSAGE_INVITE, $iChanId); + } + } + } + + //Update chan leds + $this->pingChans(); + } + } + + return self::jsonExport($asResult); + } + + private function isUserConnected($iChanId=0, $iUserId=0) + { + $iUserId = $iUserId>0?$iUserId:$this->getUserId(); + + $sUserIdCol = MySqlManager::getId(self::USER_TABLE); + $asInfo = array('select' => MySqlManager::getId(self::CONN_TABLE), + 'from' => self::CONN_TABLE, + 'constraint'=> array($sUserIdCol=>$iUserId, 'led'=>"DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)"), + 'constOpe' => array($sUserIdCol=>'=', 'led'=>'>'), + 'constVar' => true, + 'groupBy' => $sUserIdCol); + + //Add chan constraint + if($iChanId>0) + { + $asInfo['constraint'][MySqlManager::getId(self::CHAN_TABLE)] = $iChanId; + $asInfo['constOpe'][MySqlManager::getId(self::CHAN_TABLE)] = '='; + } + return (count($this->oMySql->selectRows($asInfo))>0); + } + + public function pingChans() + { + $aiConstraints = array(MySqlManager::getId(self::USER_TABLE)=>$this->getUserId()); + $asUpdates = array('led'=>date(self::DATE_TIME_SQL_FORMAT)); + $this->oMySql->updateRows(self::CONN_TABLE, $aiConstraints, $asUpdates); + } + + public function quitChan($sChanName) + { + $iChanId = $this->getChanId($sChanName); + $iConnId = $this->getConnId($iChanId); + if($iConnId>0) + { + $this->oMySql->deleteRow(self::CONN_TABLE, $iConnId); + $asPm = $this->isPmChan($sChanName); + $this->addMessage('quitte '.($asPm['is_pm']?'le chan privé':'#'.$sChanName), self::MESSAGE_CONN, $iChanId); + } + } + + private function getActiveChannels() + { + //Get connected users + $asActiveChanUsers = $this->getConnectedUsers(false, false); + $asActiveChans = array_keys($asActiveChanUsers); + + //Get Channel names + $sChanIdCol = MySqlManager::getId(self::CHAN_TABLE); + $asChanNames = $this->oMySql->selectRows(array('select'=>array($sChanIdCol, MySqlManager::getText(self::CHAN_TABLE)), 'from'=>self::CHAN_TABLE), true, $sChanIdCol); + + foreach($asActiveChans as $iChanId) + { + $asChannels[$asChanNames[$iChanId]] = count($asActiveChanUsers[$iChanId]); + } + + //remove restricted channels + $asPublicChannels = array(); + foreach($asChannels as $sChanName=>$iChanCount) + { + if($this->checkChanAuth($this->getChanSafeName($sChanName))) $asPublicChannels[$sChanName] = $iChanCount; + } + + return $asPublicChannels; + } + + private function getChannels($iUserId=0) + { + $sChanIdCol = MySqlManager::getId(self::CHAN_TABLE, true); + $sChanNameCol = MySqlManager::getText(self::CHAN_TABLE); + + $asInfo = array('select'=>array($sChanIdCol, $sChanNameCol), + 'from'=> self::CONN_TABLE, + 'join'=> array(self::CHAN_TABLE=>MySqlManager::getId(self::CHAN_TABLE)), + 'orderBy'=> array(MySqlManager::getId(self::CONN_TABLE)=>'ASC')); + if($iUserId > 0) $asInfo['constraint'] = array(MySqlManager::getId(self::USER_TABLE)=>$iUserId); + + $asChannels = $this->oMySql->selectRows($asInfo, true, MySqlManager::getId(self::CHAN_TABLE)); + + //remove restricted channels + $asPublicChannels = array(); + foreach($asChannels as $iChanId=>$sChanName) + { + if($this->checkChanAuth($this->getChanSafeName($sChanName), $iUserId)) $asPublicChannels[$iChanId] = $sChanName; + } + return $asPublicChannels; + } + + private function getChanId($sChanName) + { + $sChanIdCol = MySqlManager::getId(self::CHAN_TABLE); + $sSafeChanName = self::getChanSafeName($sChanName); + $iChanId = $this->oMySql->selectValue(self::CHAN_TABLE, $sChanIdCol, array('safe_name'=>$sSafeChanName)); + if($iChanId==0) + { + $this->addError('No channel id found with channel name (safe): '.$sSafeChanName.', unsafe: '.$sChanName); + } + return $iChanId; + } + + private function getConnId($iChanId, $iUserId=0) + { + $sUserIdCol = MySqlManager::getId(self::USER_TABLE); + $sConnIdCol = MySqlManager::getId(self::CONN_TABLE); + $sChanIdCol = MySqlManager::getId(self::CHAN_TABLE); + + if($iUserId==0) $iUserId = $this->getUserId(); + $iConnId = $this->oMySql->selectValue(self::CONN_TABLE, $sConnIdCol, array($sUserIdCol=>$iUserId, $sChanIdCol=>$iChanId)); + if($iConnId==0) + { + $this->addError('No connection found for user: '.$iUserId.' and channel id: '.$iChanId); + } + return $iConnId; + } + + public function addChatMessage($sMessage, $sChanName) + { + $sMessage = htmlspecialchars($sMessage); + $sType = self::MESSAGE_USER; + if(mb_substr($sMessage, 0, 1) == '/') + { + if(mb_substr($sMessage, 0, 4) == '/me ') + { + $sType = self::MESSAGE_ACTION; + $sMessage = mb_substr($sMessage, 3); + } + elseif(mb_substr($sMessage, 0, 6) == '/slap ') + { + $sType = self::MESSAGE_ACTION; + $sMessage = ' fout une grosse tarte à '.mb_substr($sMessage, 5); + } + elseif(mb_substr($sMessage, 0, 4) == '/bs ') + { + $sType = self::MESSAGE_ACTION; + $sMessage = ' bitch-slaps '.mb_substr($sMessage, 3); + } + elseif(mb_substr($sMessage, 0, 6) == '/kick ') + { + $sType = self::MESSAGE_ACTION; + $sMessage = ' met un coup de pied au cul de '.mb_substr($sMessage, 5); + } + elseif(mb_substr($sMessage, 0, 6) == '/nick ' && mb_strlen($sMessage)>6) + { + $sNewNick = $this->getNickNameFormat(mb_substr($sMessage, 5)); + $sOldNick = $this->getNickNameFormat($this->getChatNickNames($this->getUserId())); + + //changing Nickname + $this->setOptions(array(self::OPT_NICKNAME=>$sNewNick)); + + //Message + $sType = self::MESSAGE_NICK; + $sChanName = self::ALL_CHAN_TEXT; + $sMessage = $sOldNick.' a changé son pseudo en '.$sNewNick; + } + elseif(mb_substr($sMessage, 0, 9) == '/mission ' && mb_strlen($sMessage)>9) + { + $sNewStatus = mb_substr($sMessage, 9); + $sNewFormatStatus = $this->getDescriptionFormat($sNewStatus); + + //changing Nickname + $this->setOptions(array(self::OPT_STATUS=>$sNewStatus)); + + //Message + $sType = self::MESSAGE_STATUS; + $sChanName = self::DEFAULT_CHAN; + $sMessage = 'est sur une nouvelle mission : '.$sNewFormatStatus; + } + elseif(mb_substr($sMessage, 0, 6) == '/mail ' && mb_strlen($sMessage)>6) + { + $sImagePattern = '/\/mail (?P\w+) (?P.*)/u'; + preg_match($sImagePattern, $sMessage, $asMatches); + + //Looking for user Id + $iUserIdTo = $this->getUserIdFromNickName($asMatches['nickname']); + + //Handling mail + if($iUserIdTo>0) + { + if(trim($asMatches['message'])!='') + { + if($this->sendPM($iUserIdTo, $asMatches['message'])) + { + $sMessage = 'a envoyé un mail à '.$asMatches['nickname']; + } + else + { + $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (raison inconnue, voir log)'; + } + } + else + { + $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (message vide)'; + } + } + else + { + $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (pseudo inconnu)'; + } + $sType = self::MESSAGE_ACTION; + } + elseif(mb_substr($sMessage, 0, 5) == '/mean') + { + $sPageContent = file_get_contents('http://www.randominsults.net/'); + $sStartText = ''; + $sEndText = ''; + $iStartPos = mb_strpos($sPageContent, $sStartText); + $iEndPos = mb_strpos($sPageContent, $sEndText); + + $sMessage = mb_substr($sPageContent, $iStartPos + mb_strlen($sStartText), $iEndPos - $iStartPos); + } + elseif(mb_substr($sMessage, 0, 5) == '/like') + { + $sType = self::MESSAGE_ACTION; + $sMessage = ' plussoie'; + } + elseif(mb_substr($sMessage, 0, 4) == '/now') + { + $sType = self::MESSAGE_ACTION; + $asWeekDays = array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'); + $asMonths = array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'); + $sMessage = ' a demandé l\'heure. Pour sa gouverne, il est exactement '.date(self::TIME_FORMAT). + ', le '.$asWeekDays[date('N')-1].' '.date('j').' '.$asMonths[date('n')-1].' '.date('Y').' (semaine '.date('W').')'; + } + elseif($sMessage == '/channels' || mb_substr($sMessage, 0, 5) == '/list' || $sMessage == '/chans') + { + //Always at least one channel open (the one the message is sent from) + $sMessage = ' a demandé les chans disponibles. Pour sa gouverne, les chans ayant des membres connectés sont #'.MySqlManager::implodeAll($this->getActiveChannels(), ' (', ', #', '', ')'); + $sType = self::MESSAGE_ACTION; + } + elseif(mb_substr($sMessage, 0, 5) == '/img ' && mb_strlen($sMessage)>5) + { + $sUrl = trim(mb_substr($sMessage, 4)); + $asResult = $this->downloadToTmp($sUrl); + if($asResult['error']=='') + { + $sMessage = $this->getJsonMessage(array($asResult['out'], $asResult['width'], $asResult['height'], $sUrl)); + $sType = self::MESSAGE_IMG; + } + } + elseif(mb_substr($sMessage, 0, 6) == '/9gag ' && mb_strlen($sMessage)>6) + { + $sMessage = $this->getJsonMessage($this->get9gagPost(trim(mb_substr($sMessage, 6)))); + $sType = self::MESSAGE_9GAG; + } + elseif(mb_substr($sMessage, 0, 7) == '/reboot' && $this->getUserClearance()==self::CLEARANCE_ADMIN) + { + $sMessage = 'L\'administrateur a demandé un reboot. Votre page va se rafraichir automatiquement dans '.self::REBOOT_DELAY.' secondes.'; + $sType = self::MESSAGE_REBOOT; + $sChanName = self::ALL_CHAN_TEXT; + } + elseif(mb_substr($sMessage, 0, 8) == '/invite ' && mb_strlen($sMessage)>8) + { + $sSafeChanName = $this->getChanSafeName($sChanName); + $iUserId = $this->getUserIdFromNickName(trim(mb_substr($sMessage, 8))); + if($iUserId>0 && $this->checkChanAuth($sSafeChanName, $iUserId)) + { + $sType = self::MESSAGE_INVITE; + $sMessage = $iUserId; + } + } + elseif(mb_substr($sMessage, 0, 6) == '/news ' && mb_strlen($sMessage)>6) + { + $sType = self::MESSAGE_NEWS; + $sMessage = trim(substr($sMessage, 5)); + } + } + elseif(mb_substr($sMessage, 0, 1) == '@' && mb_strpos($sMessage, ' ')>1) + { + $sType = self::MESSAGE_PRIVATE; + } + $sChanId = $this->getChanId($sChanName); + return $this->addMessage($sMessage, $sType, $sChanId); + } + + private function getJsonMessage($asData) + { + return self::JSON_PREFIX.$this->jsonConvert($asData); + } + + private function downloadToTmp($sUrl) + { + $sFileInfo = pathinfo($sUrl); + $sImageExt = mb_strtolower($sFileInfo['extension']); + $sFilePath = self::DOC_TMP_FOLDER.uniqid().'.'.$sImageExt; + return ToolBox::createThumbnail($sUrl, self::CHAT_IMG_MAX_WIDTH, self::CHAT_IMG_MAX_HEIGHT, $sFilePath, false, Databap::$UPLOAD_IMG_EXTS); + } + + private function sendPM($iUserIdTo, $sMessage) + { + $bSuccess = false; + $asUserFrom = $this->getUserInfo($this->getUserId()); + $asUserTo = $this->getUserInfo($iUserIdTo); + $sFrom = $asUserFrom['name'].' '; + + $sTo = $asUserTo['name'].' <'.$asUserTo['email'].'>'; + $sMessage .= "\n\n\n".'Ne répondez pas à ce mail. Connectez-vous sur Databap.'; + + $sResult = ToolBox::sendMail($sFrom, 'Databap PM', $sMessage, $sTo, array(), false); + $bSuccess = ($sResult==ToolBox::MAIL_SUCCESS); + if(!$bSuccess) $this->addError($sResult); + return $bSuccess; + } + + private function addMessage($sMessage, $sType, $iChanId) + { + $bResult = false; + if($iChanId>0) + { + $asInsert = array( MySqlManager::getId(self::USER_TABLE) => $this->getUserId(), + 'nickname' => $this->getChatNickNames($this->getUserId()), + MySqlManager::getId(self::CHAN_TABLE) => $iChanId, + MySqlManager::getText(self::MSG_TABLE) => $sMessage, + 'type' => $sType, + 'date' => 'CURDATE()'); + $bResult = $this->oMySql->insertRow(self::MSG_TABLE, $asInsert); + } + else + { + $this->addError('Message deleted: No channel found'); + } + return $bResult; + } + + public function getMessages($iFirstMsgId="0") + { + //Update chan ping + $this->pingChans(); + + //Get messages and users' infos + $sChanIdCol = MySqlManager::getId(self::CHAN_TABLE, true); + $sChanTextCol = MySqlManager::getText(self::CHAN_TABLE); + $sMsgIdCol = MySqlManager::getId(self::MSG_TABLE, true); + $sMsgTextCol = MySqlManager::getText(self::MSG_TABLE); + $sMsgTableLed = MySqlManager::getFullColumnName(self::MSG_TABLE, 'led'); + $sMsgTableChanIdCol = MySqlManager::getFullColumnName(self::MSG_TABLE, MySqlManager::getId(self::CHAN_TABLE)); + $sUserIdCol = MySqlManager::getId(self::USER_TABLE, true); + $sMsgTableUserIdCol = MySqlManager::getFullColumnName(self::MSG_TABLE, MySqlManager::getId(self::USER_TABLE)); + $sUserTableUserIdCol = MySqlManager::getId(self::USER_TABLE, true); + $sConnTableUserIdCol = MySqlManager::getFullColumnName(self::CONN_TABLE, MySqlManager::getId(self::USER_TABLE)); + $sConnTableChanIdCol = MySqlManager::getFullColumnName(self::CONN_TABLE, MySqlManager::getId(self::CHAN_TABLE)); + + //channel related messages + $sCurDate = date(Databap::DATE_SQL_FORMAT); + $asInfo = array('select' => array($sMsgTableChanIdCol, $sMsgIdCol, $sMsgTextCol, 'type', $sMsgTableLed, 'first_name', 'last_name', $sUserIdCol, 'nickname'), + 'from' => self::CONN_TABLE, + 'joinOn' => array( self::MSG_TABLE =>array($sMsgTableChanIdCol, '=', $sConnTableChanIdCol), + self::USER_TABLE=>array($sUserTableUserIdCol, '=', $sMsgTableUserIdCol)), + 'constraint'=> array($sConnTableUserIdCol=>$this->getUserId(), $sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate), + 'constOpe' => array($sConnTableUserIdCol=>'=', $sMsgIdCol=>'>', 'date'=>'=')); + $asSqlMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); + + //Global messages + $asInfo['from'] = self::MSG_TABLE; + $asInfo['joinOn'] = array(self::USER_TABLE=>array($sUserTableUserIdCol, '=', $sMsgTableUserIdCol)); + $asInfo['constraint'] = array($sMsgTableChanIdCol=>self::ALL_CHAN_ID, $sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate); + $asInfo['constOpe'] = array($sMsgTableChanIdCol=>'=', $sMsgIdCol=>'>', 'date'=>'='); + $asSqlGlobalMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); + + //Invites messages + $asInfo['constraint'] = array($sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate, 'type'=>self::MESSAGE_INVITE, 'message'=>$this->getUserId()); + $asInfo['constOpe'] = array($sMsgIdCol=>'>', 'date'=>'=', 'type'=>'=', 'message'=>'='); + $asSqlInviteMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); + + //Merge flows + if(!empty($asSqlGlobalMessages)) $asSqlMessages = array_diff_key($asSqlMessages, $asSqlGlobalMessages) + $asSqlGlobalMessages; + if(!empty($asSqlInviteMessages)) $asSqlMessages = array_diff_key($asSqlMessages, $asSqlInviteMessages) + $asSqlInviteMessages; + + //Sort messages + ksort($asSqlMessages); + + //Sort out messages for Json Export + $iPrefixLen = mb_strlen(self::JSON_PREFIX); + $iConsoleDisplay = $this->getUserOptionValue(self::OPT_CONSOLE); + $asMessages = array('messages'=>array(), 'last_message_id'=>0); + foreach($asSqlMessages as $iMessageId=>$asMessageInfo) + { + //Connection message filter + if($iConsoleDisplay==self::OPT_CONSOLE_NO && $asMessageInfo['type']==self::MESSAGE_CONN) continue; + + //General message info + $iChanId = $asMessageInfo[MySqlManager::getId(self::CHAN_TABLE)]; + $iUserId = $asMessageInfo[MySqlManager::getId(self::USER_TABLE)]; + $sMessageType = $asMessageInfo['type']; + $asMessages['messages'][$iMessageId]['id_chan'] = $iChanId; + $asMessages['messages'][$iMessageId]['message'] = $asMessageInfo[$sMsgTextCol]; + $asMessages['messages'][$iMessageId]['msg_class'] = $sMessageType; + $asMessages['messages'][$iMessageId]['time'] = self::getDateFormat($asMessageInfo['led'], self::TIME_FORMAT); + $asMessages['messages'][$iMessageId]['name'] = self::getNameFormat($asMessageInfo['first_name'], $asMessageInfo['last_name']); + $asMessages['messages'][$iMessageId]['nickname'] = self::getNickNameFormat($asMessageInfo['nickname']); + + //generated messages + switch($sMessageType) + { + case self::MESSAGE_ADD_CODE: + case self::MESSAGE_EDIT_CODE: + $asCode = $this->getCodeInfo($asMessages['messages'][$iMessageId]['message']); + $asMessages['messages'][$iMessageId]['description'] = $asCode['description']; + break; + case self::MESSAGE_ADD_PROC: + case self::MESSAGE_EDIT_PROC: + $asProc = $this->getProcInfo($asMessages['messages'][$iMessageId]['message']); + $asMessages['messages'][$iMessageId]['description'] = $asProc['title']; + break; + case self::MESSAGE_ADD_DOC: + case self::MESSAGE_EDIT_DOC: + $asDoc = $this->getDocInfo($asMessages['messages'][$iMessageId]['message']); + $asMessages['messages'][$iMessageId]['description'] = $asDoc['title']; + break; + case self::MESSAGE_ADD_TABLE: + case self::MESSAGE_EDIT_TABLE: + $asTable = $this->getTableInfo($asMessages['messages'][$iMessageId]['message']); + $asMessages['messages'][$iMessageId]['description'] = $asTable['title']; + break; + case self::MESSAGE_ARTICLE: + $asTransferredInfo = $this->getArticleInfo($asMessages['messages'][$iMessageId]['message']); + $asMessages['messages'][$iMessageId] = array_merge($asMessages['messages'][$iMessageId], $asTransferredInfo); + break; + case self::MESSAGE_INVITE: //Switch Chan ID with name for the user to join + $asMessages['messages'][$iMessageId]['id_chan'] = $this->oMySql->selectValue(self::CHAN_TABLE, 'safe_name', $iChanId); + break; + } + + //Json message + if(mb_substr($asMessages['messages'][$iMessageId]['message'], 0, $iPrefixLen) == self::JSON_PREFIX) + { + $asMessages['messages'][$iMessageId]['message'] = json_decode(mb_substr($asMessages['messages'][$iMessageId]['message'], $iPrefixLen)); + } + else //Normal message + { + //Internal links + $asMessages['messages'][$iMessageId]['message'] = Toolbox::findReplaceLinks($asMessages['messages'][$iMessageId]['message']); + + //Dynamic chan link + $asPatterns = '/(^|\s)#(\w*[^\s]+\w*)/u'; + $asLinks = '\1#\2'; + $asMessages['messages'][$iMessageId]['message'] = preg_replace($asPatterns, $asLinks, $asMessages['messages'][$iMessageId]['message']); + } + } + + //Set last message Id (if new messages since $iFirstMsgId) + if(!empty($asMessages['messages'])) + { + $asMessages['last_message_id'] = max(array_keys($asMessages['messages'])); + } + + return $this->jsonExport($asMessages); + } + + public function getNews() + { + $sMsgIdCol = MySqlManager::getId(self::MSG_TABLE); + + $asInfo['select'] = array($sMsgIdCol, 'nickname', MySqlManager::getText(self::MSG_TABLE), 'led'); + $asInfo['from'] = self::MSG_TABLE; + $asInfo['constraint'] = array('type'=>self::MESSAGE_NEWS); + $asInfo['orderBy'] = array('led'=>'DESC'); + $asInfo['limit'] = 3; + $asNews = $this->oMySql->selectRows($asInfo); + + foreach($asNews as $asNew) + { + $iMsgId = '-'.$asNew[$sMsgIdCol]; + $asFormatNews[$iMsgId]['time_desc'] = ToolBox::getDateTimeDesc($asNew['led']); + $asFormatNews[$iMsgId]['message'] = self::getDescriptionFormat($asNew['message']); + $asFormatNews[$iMsgId]['nickname'] = self::getNickNameFormat($asNew['nickname']); + } + + $sSuccess = (count($asFormatNews)>0); + return $this->getJsonPostResult($sSuccess, $sSuccess?'':'Aucune news', array('news'=>$asFormatNews)); + } + + private function getConnectedChans($iuserId=0) + { + $iuserId = $iuserId>0?$iuserId:$this->getUserId(); + + $sUserIdCol = MySqlManager::getId(self::USER_TABLE); + $asInfo = array('select' => array(MySqlManager::getId(self::CHAN_TABLE, true), MySqlManager::getText(self::CHAN_TABLE)), + 'from' => self::CONN_TABLE, + 'join' => array(self::CHAN_TABLE=>MySqlManager::getId(self::CHAN_TABLE)), + 'constraint'=> array($sUserIdCol=>$iUserId, 'led'=>"DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)"), + 'constOpe' => array($sUserIdCol=>'=', 'led'=>'>'), + 'constVar' => true); + + return $this->oMySql->selectRows($asInfo, true, MySqlManager::getId(self::CONN_TABLE)); + } + + public function getConnectedUsers($bUserRestricted=false, $bJson=true) + { + $sQuery = " SELECT /* config.php 1313 */ conn2.id_channel, conn2.id_user, conn2.led, IF(DATE_SUB(NOW(), INTERVAL 1 MINUTE) < conn2.led, 0, 1) AS afk, + users.first_name, users.last_name, companies.company, companies.logo, option_nickname.option AS nickname, option_status.option AS status + FROM connections AS conn1 + LEFT JOIN connections AS conn2 ON conn2.id_channel = conn1.id_channel + LEFT JOIN users ON users.id_user = conn2.id_user + LEFT JOIN companies ON companies.id_company = users.id_company + LEFT JOIN `options` AS option_nickname ON option_nickname.id_user = users.id_user AND option_nickname.id_option_name = ".self::OPT_NICKNAME." + LEFT JOIN `options` AS option_status ON option_status.id_user = users.id_user AND option_status.id_option_name = ".self::OPT_STATUS." + WHERE conn2.led > DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)". + ($bUserRestricted?"AND conn1.id_user = ".$this->getUserId():"")." + GROUP BY conn2.id_channel, conn2.id_user + ORDER BY option_nickname.option ASC"; + $asUserChannels = $this->oMySql->getArrayQuery($sQuery, true); + + //Last messages + $iUserIdCol = MySqlManager::getId(self::USER_TABLE); + $asLastMsg = $this->oMySql->selectRows(array('select'=>array($iUserIdCol, 'MAX(led)'), 'from'=>self::MSG_TABLE), true, $iUserIdCol); + + $asConnectedUsers = array(); + foreach($asUserChannels as $asUser) + { + $sChanId = $asUser[MySqlManager::getId(self::CHAN_TABLE)]; + $iUserId = $asUser[$iUserIdCol]; + $sNickName = $asUser['nickname']; + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['id_user'] = $iUserId; + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['name'] = self::getNameFormat($asUser['first_name'], $asUser['last_name']); + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['company'] = self::getCompanyFormat($asUser['company']); + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['status'] = $asUser['status']; + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['logo'] = $asUser['logo']; + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['nickname'] = self::getNickNameFormat($sNickName==''?$asUser['first_name']:$sNickName); + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['last_activity'] = array_key_exists($iUserId, $asLastMsg)?self::getDateFormat($asLastMsg[$iUserId], self::TIME_FORMAT):''; + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['ping'] = self::getDateFormat($asUser['led'], self::TIME_FORMAT); + $asConnectedUsers[$sChanId][$sNickName.$iUserId]['afk'] = $asUser['afk']; + } + return $bJson?$this->jsonExport($asConnectedUsers):$asConnectedUsers; + } + + private function getUserIdFromNickName($sNickName) + { + $asContraints = array( 'LOWER(`'.MySqlManager::getText(self::OPT_TABLE).'`)' => mb_strtolower($sNickName), + MySqlManager::getId(self::OPTNAME_TABLE) => self::OPT_NICKNAME); + return $this->oMySql->selectValue(self::OPT_TABLE, MySqlManager::getId(self::USER_TABLE), $asContraints); + } + + private function getChatNickNames($iUserId=0) + { + $sUserIdCol = MySqlManager::getId(self::USER_TABLE); + $sNicknameCol = MySqlManager::getText(self::OPT_TABLE); + $asConstraints = array(MySqlManager::getId(self::OPTNAME_TABLE)=>self::OPT_NICKNAME); + + if($iUserId>0) + { + $asConstraints[MySqlManager::getId(self::USER_TABLE)] = $iUserId; + $oResult = $this->oMySql->selectValue(self::OPT_TABLE, $sNicknameCol, $asConstraints); + } + else + { + $asInfo = array('select'=>array($sUserIdCol, $sNicknameCol), + 'from'=>self::OPT_TABLE, + 'constraint'=>$asConstraints); + $oResult = $this->oMySql->selectRows($asInfo, true, $sUserIdCol); + } + return $oResult; + } + + private function switchCodeId(&$iCodeId, $sMode) + { + switch($sMode) + { + case 'first': + $iCodeId = $this->oMySql->selectValue(self::CODE_TABLE, 'refer_id', $iCodeId); + break; + case 'last': + $iRefId = $this->oMySql->selectValue(self::CODE_TABLE, 'refer_id', $iCodeId); + $iCodeId = $this->oMySql->selectValue(self::CODE_TABLE, 'MAX(id_code)', array('refer_id'=>$iRefId)); + break; + } + } + + private function getIdCodeFromPhrase($sPhrase) + { + $iCodeId = $this->oMySql->selectValue(self::URL_TABLE, MySqlManager::getId(self::CODE_TABLE), array('phrase'=>$sPhrase)); + $this->switchCodeId($iCodeId, 'last'); + return $iCodeId; + } + + private function getPhraseFromIdCode($iCodeId) + { + $this->switchCodeId($iCodeId, 'first'); + return $this->oMySql->selectValue(self::URL_TABLE, 'phrase', array('id_code'=>$iCodeId)); + } + + private function getCodeVersions($iRefCodeId) + { + $asCodeVersions = array(); + if($iRefCodeId>0) + { + $asInfo = array('select'=>array(MySqlManager::getId(self::CODE_TABLE)), + 'from'=>self::CODE_TABLE, + 'constraint'=>array('refer_id'=>$iRefCodeId), + 'orderBy'=>array('id_code'=>'asc')); + $asCodeIds = $this->oMySql->selectRows($asInfo); + + foreach($asCodeIds as $iCodeId) + { + $asCodeVersions[] = $this->getCodeInfo($iCodeId); + } + } + return $asCodeVersions; + } + + public function getColoredCode($oCode) + { + $asCode = $this->getCodeInfo($oCode, '', true); + + //code + $this->oClassManagement->incClass('reader'); + $oReader = new Reader($asCode['code']); + $asCode['code'] = $oReader->getColoredCode(); + + //phrase + $sPhrase = $this->getPhraseFromIdCode($asCode['id_code']); + if($sPhrase !== false) + { + $asCode['phrase'] = $sPhrase; + } + + //user + $asUsers[$asCode['id_user']] = $this->getUserInfo($asCode['id_user']); + $asCode = array_merge($asCode, $asUsers[$asCode['id_user']]); + + //versions + $asCodeVersions = $this->getCodeVersions($asCode['refer_id']); + $iCodeRowId = 0; + foreach($asCodeVersions as $iRowId=>$asCodeVersion) + { + if($asCodeVersion['id_code']==$asCode['id_code']) $iCodeRowId = $iRowId; + } + $asCode['truncated'] = ($iCodeRowId > self::MAX_LIST_LENGTH)?$asCodeVersions[$iCodeRowId - 1 - self::MAX_LIST_LENGTH]['id_code']:0; + foreach($asCodeVersions as $iRowId=>$asCodeVersion) + { + if($iCodeRowId - $iRowId <= self::MAX_LIST_LENGTH) + { + if(!array_key_exists($asCodeVersion['id_user'], $asUsers)) + { + $asUsers[$asCodeVersion['id_user']] = $this->getUserInfo($asCodeVersion['id_user']); + } + + if($asCodeVersion['id_code']!=$asCode['id_code']) + { + $asVersionInfo = array_merge($asCodeVersion, $asUsers[$asCodeVersion['id_user']]); + $asCode['other_versions'][] = $asVersionInfo; + } + } + } + + return $this->jsonExport($asCode); + } + + public function getNudeCode($oCode) + { + $asCode = $this->getCodeInfo($oCode, '', true); + //$sCode = Reader::convText2Html($asCode['code']); + return $asCode['code']; + } + + public function getRawCode($oCode, $bPrint=false) + { + $asCode = $this->getCodeInfo($oCode, '', true); + $asUser = $this->getUserInfo($asCode['id_user']); + $sEncodedCode = $this->jsonConvert($asCode['code']); + $sEncodedDesc = $this->jsonConvert($asCode['description']); + + $oRawCodePage = new Mask('raw_code'); + $oRawCodePage->setTags(array( 'text_enc'=>Settings::TEXT_ENC, + 'author'=>$asUser['name'].' ('.$asUser['company'].')', + 'content'=>$sEncodedCode, + 'title'=>$sEncodedDesc, + 'print'=>$bPrint?'true':'false')); + return $oRawCodePage->getMask(); + } + + public function getSavedCode($oCode) + { + $asCode = $this->getCodeInfo($oCode, '', true); + $sPhrase = $this->getPhraseFromIdCode($asCode['id_code']); + if(!$sPhrase) + { + $sPhrase = 'code_numero_'.$asCode['id_code']; + } + + //set headers + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Cache-Control: public'); + header('Content-Description: File Transfer'); + header('Content-Type: application/force-download'); + header('Content-Disposition: attachment; filename='.$sPhrase.'.abap'); + header('Content-Transfer-Encoding: binary'); + //header('Content-Length: '.filesize($sFilePath)); + + return $asCode['code']; + } + + public function getItemList() + { + $sIdCodeCol = MySqlManager::getId(self::CODE_TABLE); + $sIdProcCol = MySqlManager::getId(self::PROC_TABLE); + $sIdArtCol = MySqlManager::getId(self::ART_TABLE); + $sIdDocCol = MySqlManager::getId(self::DOC_TABLE); + $sIdTableCol = MySqlManager::getId(self::TABL_TABLE); + $sIdCodeColFull = MySqlManager::getId(self::CODE_TABLE, true); + $sIdUserCol = MySqlManager::getId(self::USER_TABLE); + $sIdUserColFull = MySqlManager::getId(self::USER_TABLE, true); + $sLedColFull = MySqlManager::getFullColumnName(self::CODE_TABLE, 'led') ; + + //Get code Ids + $asInfo = array('select'=>array("MAX($sIdCodeCol) AS id_item"), //selecting last version among codes having the same refer_id + 'from'=>self::CODE_TABLE, + 'groupBy'=>array('refer_id'), + 'orderBy'=>array('id_item'=>'desc')); + $asItemIds[Databap::CODE_TYPE] = $this->oMySql->selectRows($asInfo); + + //get Proc Ids + $asInfo = array('select'=>array("MAX($sIdProcCol) AS id_item"), + 'from'=>self::PROC_TABLE, + 'groupBy'=>array('refer_id'), + 'orderBy'=>array('id_item'=>'desc')); + $asItemIds[Databap::PROC_TYPE] = $this->oMySql->selectRows($asInfo); + + //get Article Ids + $asInfo = array('select'=>array($sIdArtCol." AS id_item"), + 'from'=>self::ART_TABLE, + 'orderBy'=>array('id_item'=>'desc')); + $asItemIds[Databap::ART_TYPE] = $this->oMySql->selectRows($asInfo); + + //Get documentation + $asInfo = array('select'=>array("MAX($sIdDocCol) AS id_item"), //selecting last version among docs having the same refer_id + 'from'=>self::DOC_TABLE, + 'groupBy'=>array('refer_id'), + 'orderBy'=>array('id_item'=>'desc')); + $asItemIds[Databap::DOC_TYPE] = $this->oMySql->selectRows($asInfo); + + //Get tables + $asInfo = array('select'=>array("MAX($sIdTableCol) AS id_item"), //selecting last version among tables having the same refer_id + 'from'=>self::TABL_TABLE, + 'groupBy'=>array('refer_id'), + 'orderBy'=>array('id_item'=>'desc')); + $asItemIds[Databap::TABLE_TYPE] = $this->oMySql->selectRows($asInfo); + + //Build code info structure + //TODO phrases for all item types? + $asUsers = $asCode = $asItemList = array(); + foreach($asItemIds as $sType=>$asTypedItemIds) + { + $asTypedItemIds = array_filter($asTypedItemIds); //null value returned from query (MAX(...)) + foreach($asTypedItemIds as $iItemId) + { + $asItem = call_user_func(array($this, 'get'.ucfirst($this->getPagesFromHash($sType)).'Info'), $iItemId); + $asItem['type'] = $sType; + $asItem['id_item'] = $iItemId; + if(array_key_exists($sIdUserCol, $asItem)) //replacing user's id with user's name & company (if not done already) + { + $iUserId = $asItem[$sIdUserCol]; + if(!array_key_exists($iUserId, $asUsers)) $asUsers[$iUserId] = $this->getUserInfo($iUserId); + $asItem['name'] = $asUsers[$iUserId]['name']; + $asItem['company'] = $asUsers[$iUserId]['company']; + } + $asItemList[$asItem['timestamp'].$asItem['type'].$asItem['id_item']] = $asItem; + } + } + krsort($asItemList); + return $this->jsonExport($asItemList); + } + + public function getCodeBlock() + { + $oMask = new Mask(); + $oMask->initFile('code_block'); + $oMask->setTag('item', $this->getItemBlock()); + return $oMask->getMask(); + } + + private function getItemBlock() + { + $oMask = new Mask('item'); + return $oMask->getMask(); + } + + public static function checkNoAuthCookieResetAction($sAction) + { + return in_array($sAction, array('messages', 'upload_image', 'user_info', 'rss')); + } + + public function logMeIn($sToken, $sAction) + { + $iUserId = 0; + $bResetPass = true; + $sUserTableId = MySqlManager::getId(self::USER_TABLE); + + //login using form + if($sAction!=self::EXT_ACCESS) + { + if($sToken!='') + { + $sNameToken = strstr($sToken, self::NAME_PASS_SEP, true); + $sPassToken = substr(strstr($sToken, self::NAME_PASS_SEP), 1); + + //Check name + //TODO replace SPACE(1) with self::FIRST_LAST_SEP (separate first and last name on logon page) + $asConsts = array('MD5(CONCAT(first_name, SPACE(1), last_name))'=>$sNameToken, 'active'=>self::MEMBER_ACTIVE); + $asInvConsts = array('MD5(CONCAT(last_name, SPACE(1), first_name))'=>$sNameToken, 'active'=>self::MEMBER_ACTIVE); + $iUserId = $this->oMySql->selectValue(self::USER_TABLE, $sUserTableId, $asConsts); + if(!$iUserId) $iUserId = $this->oMySql->selectValue(self::USER_TABLE, $sUserTableId, $asInvConsts); + + //Check Pass + if(!$this->oAuth->CheckPassword($sPassToken, $this->oMySql->selectValue(self::USER_TABLE, 'pass', $iUserId))) $iUserId = 0; + } + //auto login by cookie + elseif(isset($_COOKIE[self::USER_COOKIE_ID])) + { + $asConstraints = array( $sUserTableId=>$_COOKIE[self::USER_COOKIE_ID], + 'auth_cookie'=>$_COOKIE[self::USER_COOKIE_PASS], + 'active'=>self::MEMBER_ACTIVE); + + $asUserInfo = $this->oMySql->selectRow(self::USER_TABLE, $asConstraints, array($sUserTableId, 'led')); + $iUserId = $asUserInfo[$sUserTableId]; + + //Check pass reset necessity + $bResetPass = (mb_substr($asUserInfo['led'], 0, 10) != date(Databap::DATE_SQL_FORMAT)); + } + } + //Login using Token (limited access) + elseif($sToken!='') + { + $iUserId = $this->checkExternalAccessToken($sToken); + $bResetPass = false; + } + + if($iUserId>0) + { + $this->setUserId($iUserId); + if(!self::checkNoAuthCookieResetAction($sAction) && $bResetPass) + { + $this->resetAuthCookie(); + } + } + + return ($this->getUserId()>0); + } + + private static function getLoginToken($sPass) + { + return md5($sPass.$_GET['serv_name']); + } + + public function checkSetPass($sToken, $sNewToken) + { + $bSuccess = false; + $sDesc = ''; + if($this->oAuth->CheckPassword($sToken, $this->oMySql->selectValue(self::USER_TABLE, 'pass', $this->getUserId()))) + { + $bSuccess = $this->oMySql->updateRow(self::USER_TABLE, $this->getUserId(), array('pass'=>$this->oAuth->HashPassword($sNewToken))); + $sDesc = $bSuccess?'Mot de passe modifié avec succès':'Une erreur au niveau de la base données est apparue'; + } + else $sDesc = 'Le mot de passe actuel est erroné'; + + return $this->getJsonPostResult($bSuccess, $sDesc); + } + + public function resetAllPass() //to be deleted in v1.0.1 + { + $asInfo = array('select'=>array(MySqlManager::getId(self::USER_TABLE, true), MySqlManager::getText(self::COMP_TABLE)), + 'from'=> self::USER_TABLE, + 'join'=> array(self::COMP_TABLE=>MySqlManager::getId(self::COMP_TABLE))); + $asUsers = $this->oMySql->selectRows($asInfo); + foreach($asUsers as $asUser) + { + $sToken = $this->oAuth->HashPassword(self::getLoginToken($asUser[MySqlManager::getText(self::COMP_TABLE)])); + $iUserId = $asUser[MySqlManager::getId(self::USER_TABLE)]; + $this->oMySql->updateRow(self::USER_TABLE, $iUserId, array('pass'=>$sToken)); + } + return 'OK'; + } + + private function getExternalAccessPass($iUserId) + { + $sPass = ''; + if($iUserId>0) + { + $asUser = $this->oMySql->selectRow(self::USER_TABLE, $iUserId, array('first_name', 'last_name', 'email', MySqlManager::getId(self::COMP_TABLE))); + $sCompanyName = $this->oMySql->selectvalue(self::COMP_TABLE, MySqlManager::getText(self::COMP_TABLE), $asUser[MySqlManager::getId(self::COMP_TABLE)]); + $sPass = $asUser['first_name'].$asUser['last_name'].$asUser['email'].$sCompanyName; + } + else $this->addError('generating token : invalid user id "'.$iUserId.'"'); + return $sPass; + } + + private function generateExternalAccessToken($iUserId) + { + return $this->oAuth->HashPassword($this->getExternalAccessPass($iUserId)); + } + + private function checkExternalAccessToken($sKey) + { + $iUserId = mb_strstr($sKey, '_', true); + $asConstraints = array(MySqlManager::getId(self::USER_TABLE)=>$iUserId, 'active'=>self::MEMBER_ACTIVE); + $sToken = mb_substr($sKey, mb_strlen($iUserId)+1); + return ($this->checkValue(self::USER_TABLE, $asConstraints) + && $this->oAuth->CheckPassword($this->getExternalAccessPass($iUserId), $sToken))?$iUserId:0; + } + + private function generateExternalAccessLink($sType, $iUserId=0) + { + if($iUserId==0) $iUserId = $this->getUserId(); + return $_GET['serv_name'].'?a='.self::EXT_ACCESS.'&p='.$sType.'&auth_token='.$iUserId.'_'.$this->generateExternalAccessToken($iUserId); + } + + private function getInternalLink($sPage, $oId=0) + { + return $_GET['serv_name'].'#'.$sPage.($oId?'-'.$oId:''); + } + + private function getAuthCookie() + { + return $this->oAuth->HashPassword( $_SERVER['HTTP_USER_AGENT']. + $_SERVER['REMOTE_ADDR']. + $_SERVER['REQUEST_TIME']. + mb_strstr(microtime(), ' ', true). + $_SERVER['SERVER_SIGNATURE']. + $_SERVER['SERVER_ADMIN']); + } + + private function resetAuthCookie() + { + $iUserId = $this->getUserId(); + $sNewPass = $this->getAuthCookie(); + $iTimeLimit = time()+60*60*24*self::COOKIE_LIFESPAN; + $this->oMySql->updateRow(self::USER_TABLE, $iUserId, array('auth_cookie'=>$sNewPass)); + setcookie(self::USER_COOKIE_ID, $iUserId, $iTimeLimit); + setcookie(self::USER_COOKIE_PASS, $sNewPass, $iTimeLimit); + } + + public function logMeOut() + { + $this->disconnectChat(); + $this->setUserId(0); + setcookie(self::USER_COOKIE_ID, '', time()-60*60); + setcookie(self::USER_COOKIE_PASS, '', time()-60*60); + } + + /* Not needed so far + public function setExpectedPage($sExpectedPage) + { + setcookie(self::EXPECTED_PAGE_COOKIE, $sExpectedPage, time()+60*60); + } + + public function redirectExpectedPage() + { + if(array_key_exists(self::EXPECTED_PAGE_COOKIE, $_COOKIE)) + { + $sLocation = $_COOKIE[self::EXPECTED_PAGE_COOKIE]; + setcookie(self::EXPECTED_PAGE_COOKIE, '', time()-60*60); + header('Location:'.$sLocation); + } + } + */ + + public function getArticle($iArtId) + { + return $this->jsonExport($this->getArticleInfo($iArtId)); + } + + public function redirectArticle($iArtId) + { + $asArtInfo = $this->getArticleInfo($iArtId); + header('Location:'.$asArtInfo['link_art']); + } + + public function disconnectChat() + { + $sTime = $this->oMySql->selectRows(array('select'=>'DATE_SUB(NOW(), INTERVAL '.self::KEEP_ALIVE.' SECOND)')); + + //Is the user connected? + $sUserIdColName = MySqlManager::getId(self::USER_TABLE); + $bConnected = $this->oMySql->selectRows(array('select'=>array('COUNT(1)'), 'from'=>self::CONN_TABLE, 'constraint'=>array($sUserIdColName=>$this->getUserId(), 'led'=>$sTime), 'constOpe'=>array($sUserIdColName=>'=', 'led'=>'>'))); + if($bConnected) + { + $this->oMySql->updateRows(self::CONN_TABLE, array(MySqlManager::getId(self::USER_TABLE)=>$this->getUserId()), array('led'=>$sTime)); + $this->addMessage('se déconnecte', self::MESSAGE_CONN, self::DEFAULT_CHAN_ID); + } + } + + public function checkValue($sTableName, $asConstraints) + { + return $this->oMySql->selectValue($sTableName, 'COUNT(1)', $asConstraints); + } + + public function resetChanSafeNames() + { + $iChanIdCol = MySqlManager::getId(self::CHAN_TABLE); + $asChans = $this->oMySql->selectRows(array('select'=>array($iChanIdCol, MySqlManager::getText(self::CHAN_TABLE)), 'from'=>self::CHAN_TABLE), true, $iChanIdCol); + + $asResult = array(); + foreach($asChans as $iChanId=>$sChanName) + { + $asResult[$iChanId] = ($this->oMySql->updateRow(self::CHAN_TABLE, $iChanId, array('safe_name'=>self::getChanSafeName($sChanName))) > 0)?'Fixed':'Not Fixed'; + } + return MySqlManager::implodeAll($asResult, ' : ', "\n", 'Result reset ID channel ', '.'); + } + + public static function getChanSafeName($sChanName) + { + //TODO replace unsafe chars with unique id + $sChanSafeName = preg_replace('/[^a-z0-9]/u', '_', mb_strtolower($sChanName)); + + //Sort PM chans + $asPm = self::isPmChan($sChanSafeName); + if(!$asPm['is_pm']) $sChanSafeName = $asPm['chan_name']; + + return $sChanSafeName; + } + + public function getResults($sSearchWords) + { + $this->oSearchEngine->setWords($sSearchWords); + $asResults = $this->oSearchEngine->getResults(); + + //complementary infos + $asCompleteResults = array(); + foreach($asResults as $iItemId=>$asItemInfo) + { + //phrase + $sPhrase = $this->getPhraseFromIdCode($asItemInfo['id_code']); + if($sPhrase !== false) + { + $asItemInfo['phrase'] = $sPhrase; + } + $asCompleteResults[$iItemId] = $asItemInfo; + } + + return $this->jsonExport($asCompleteResults); + } + + public function getStyleSheet() + { + $sStyle = file_get_contents(self::STYLE_PATH); + $asDefaultColors = array( self::OPT_BG=>'#04357B', + self::OPT_BRIGHT_BG=>'#D9E5F2', + self::OPT_HOVER=>'#EFAB00', + self::OPT_IMAGE_CHAT=>'images/sap_gold_332.jpg'); + + //Inserting Color Ids + foreach($asDefaultColors as $iOptionNameId=>$sDefaultColor) + { + $asColorAnchors[$iOptionNameId] = '[OPT#'.$iOptionNameId.']'; + } + $sStyle = str_replace($asDefaultColors, $asColorAnchors, $sStyle); + + //Switching color Ids with user colors + $asOptionvalues = $this->getUserOptions(array_keys($asDefaultColors)); + foreach($asColorAnchors as $iOptionNameId=>$sColorAnchor) + { + $sOptionValue = (array_key_exists($iOptionNameId, $asOptionvalues) && $asOptionvalues[$iOptionNameId]['option']!='')?$asOptionvalues[$iOptionNameId]['option']:$asDefaultColors[$iOptionNameId]; + $sStyle = str_replace($sColorAnchor, $sOptionValue, $sStyle); + } + + //setting header content type + header("Content-Type: text/css"); + return $sStyle; + } + + public static function getDateFormat($oTime, $sFormat=Databap::DATE_TIME_FORMAT) + { + $iTimeStamp = is_numeric($oTime)?$oTime:strtotime($oTime); + return date($sFormat, $iTimeStamp); + } + + public static function getNameFormat($sFirstName, $sLastName) + { + return ToolBox::capitalizeWords(trim($sFirstName.self::FIRST_LAST_SEP.$sLastName), ' -');; + } + + public static function getNickNameFormat($sNickName) + { + $sNickName = ToolBox::capitalizeWords(trim($sNickName), ' -'); + return str_replace(' ', '_', $sNickName); + } + + public static function getCompanyFormat($sCompany) + { + return mb_strlen($sCompany)>3?ToolBox::mb_ucwords($sCompany):mb_strtoupper($sCompany); + } + + public static function getDescriptionFormat($sDescription) + { + return ToolBox::mb_ucfirst($sDescription); + } + + public static function getTableFormat($sTable) + { + return mb_strtoupper($sTable); + } + + public function getJsonPostResult($bSuccess, $sDesc, $asVars=array()) + { + if(!$bSuccess) $this->addError($sDesc); + return self::jsonExport(array('result'=>$bSuccess?self::SUCCESS:self::ERROR, 'desc'=>$sDesc)+$asVars); + } + + public static function jsonExport($asData) + { + header('Content-type: application/json'); + return self::jsonConvert($asData); + } + + public static function jsonConvert($asData) + { + //return htmlspecialchars(json_encode($asData), ENT_NOQUOTES); + return json_encode($asData); + } + + public static function getMaxSize() + { + $iPostSize = self::toBytes(ini_get('post_max_size')); + $iUploadSize = self::toBytes(ini_get('upload_max_filesize')); + + return min($iPostSize, $iUploadSize); + } + + public static function toBytes($str) + { + $val = trim($str); + $last = mb_strtolower($str[mb_strlen($str)-1]); + switch($last) { + case 'g': $val *= 1024; + case 'm': $val *= 1024; + case 'k': $val *= 1024; + } + return $val; + } +} + +?> \ No newline at end of file diff --git a/inc/fileuploader.php b/inc/fileuploader.php new file mode 100644 index 0000000..85f9316 --- /dev/null +++ b/inc/fileuploader.php @@ -0,0 +1,158 @@ +sFolderPath = $sFolderPath; + $this->asAllowedExtensions = array_map("mb_strtolower", $asAllowedExtensions); + //$this->iSizeLimit = $sizeLimit; + //$this->checkServerSettings(); + $this->iSizeLimit = Databap::getMaxSize(); + + /* + if(isset($_GET[self::GET_NAME])) + { + //$this->file = new qqUploadedFileXhr(); + } + elseif(isset($_FILES[self::GET_NAME])) { + $this->file = new qqUploadedFileForm(); + } + else + { + $this->file = false; + } + */ + } + + /* + private function checkServerSettings() + { + $postSize = $this->toBytes(ini_get('post_max_size')); + $uploadSize = $this->toBytes(ini_get('upload_max_filesize')); + + + if($postSize < $this->iSizeLimit || $uploadSize < $this->iSizeLimit){ + $size = max(1, $this->iSizeLimit / 1024 / 1024) . 'M'; + die("{'error':'increase post_max_size and upload_max_filesize to $size'}"); + } + } + + private function toBytes($str) + { + $val = trim($str); + $last = mb_strtolower($str[mb_strlen($str)-1]); + switch($last) { + case 'g': $val *= 1024; + case 'm': $val *= 1024; + case 'k': $val *= 1024; + } + return $val; + } + */ + + function save($sFileName) + { + $oInput = fopen("php://input", "r"); + $sTemp = tmpfile(); + $iRealSize = stream_copy_to_stream($oInput, $sTemp); + fclose($oInput); + + if($iRealSize != $this->getSize()) + { + return false; + } + + $sPath = $this->sFolderPath.$sFileName; + $oTarget = fopen($sPath, "w"); + fseek($sTemp, 0, SEEK_SET); + stream_copy_to_stream($sTemp, $oTarget); + fclose($oTarget); + + return true; + } + + function getName() + { + return isset($_GET[self::GET_NAME])?$_GET[self::GET_NAME]:false; + } + + function getSize() + { + if(isset($_SERVER["CONTENT_LENGTH"])) + { + return (int)$_SERVER["CONTENT_LENGTH"]; + } + else + { + throw new Exception('Getting content length is not supported.'); + } + } + + //Returns array('success'=>true) or array('error'=>'error message') + function handleUpload(/*$sUploadDir, $replaceOldFile = false*/) + { + if(!is_writable($this->sFolderPath)) + { + return array('error' => "Erreur serveur. Le dossier est protégé en écriture"); + } + if(!$this->getName()) + { + return array('error' => 'Aucun fichier téléchargé'); + } + + $iSize = $this->getSize(); + if($iSize == 0) + { + return array('error' => 'Fichier vide'); + } + if($iSize > $this->iSizeLimit) + { + return array('error' => 'Fichier trop volumineux'); + } + + $asPathInfo = pathinfo($this->getName()); + $sExt = mb_strtolower($asPathInfo['extension']); + $sExt = ($sExt=='jpg')?'jpeg':$sExt; + if($this->asAllowedExtensions && !in_array($sExt, $this->asAllowedExtensions)) + { + $sAuthorizedExts = implode(', ', $this->asAllowedExtensions); + return array('error' => 'Le fichier a une extension invalide, les extensions autorisées sont : '.$sAuthorizedExts); + } + + // + + + //$sFileName = $asPathInfo['filename']; + $sFileName = md5(uniqid()); + /* + if(!$replaceOldFile){ + // don't overwrite previous files that were uploaded + //while (file_exists($sUploadDir . $sFileName . '.' . $sExt)) { + // $sFileName .= rand(10, 99); + //} + if(file_exists($sUploadDir . $sFileName . '.' . $sExt)) + { + $sFileName .= date(Databap::DATE_COMPACT_FORMAT); + } + } + */ + + if($this->save($sFileName.'.'.$sExt)) + { + return array('success'=>true, 'file_name'=>$sFileName.'.'.$sExt); + } + else + { + return array('error'=> 'Erreur server. Impossible de sauvegarder le fichier.'); + } + } +} + +?> \ No newline at end of file diff --git a/inc/mask.php b/inc/mask.php new file mode 100644 index 0000000..ee9da43 --- /dev/null +++ b/inc/mask.php @@ -0,0 +1,231 @@ +sMaskName = ''; + $this->sFilePath = ''; + $this->sMask = ''; + $this->asTags = array(); + $this->asPartsSource = array(); + $this->aoInstances = array(); + $this->sFilePath = ''; + + //load file + if($sFileName!='') + { + $this->initFile($sFileName); + } + } + + public static function getMaskFile($sFileName) + { + return self::MASK_FOLDER.$sFileName.self::MASK_EXT; + } + + public function initFile($sFileName) + { + $sFilePath = self::getMaskFile(basename($sFileName)); + if(file_exists($sFilePath)) + { + $this->sFilePath = $sFilePath; + $sSource = file_get_contents($this->sFilePath); + $this->initMask($sFileName, $sSource); + } + else + { + $this->addError('Fichier introuvable à l\'adresse : '.$sFilePath); + } + } + + public function initFileFromString($sSource, $sPartName='', $iInstanceNb=0) + { + $this->initMask($sPartName.' (from row) '.$iInstanceNb, $sSource); + } + + private function initMask($sMaskName, $sSource) + { + $this->sMaskName = $sMaskName; + $this->sMask = $sSource; + $this->setParts(); + } + + private function setParts() + { + while(preg_match('/\<\!-- \[PART\] (?P\S+) \[START\] --\>/u', $this->sMask, $asMatch)) + { + $sPartName = $asMatch['part']; + + $this->asPartsSource[$sPartName] = $this->getCleanPart($sPartName); + $this->aoInstances[$sPartName] = array(); + } + } + + private function getCleanPart($sPartName) + { + $iStartPos = $this->getPartStartPos($sPartName); + $iEndPos = $this->getPartEndPos($sPartName); + $sPart = mb_substr($this->sMask, $iStartPos, $iEndPos-$iStartPos); + $sExtendedPart = $this->getPartPattern($sPartName, self::START_TAG).$sPart. $this->getPartPattern($sPartName, self::END_TAG); + $this->sMask = str_replace($sExtendedPart, $this->getPartTagPattern($sPartName), $this->sMask); + return $sPart; + } + + private function getPartStartPos($sPartName) + { + $sPartStartPattern = $this->getPartPattern($sPartName, self::START_TAG); + return mb_strpos($this->sMask, $sPartStartPattern) + strlen($sPartStartPattern); + } + + private function getPartEndPos($sPartName) + { + $sPartEndPattern = $this->getPartPattern($sPartName, self::END_TAG); + return mb_strpos($this->sMask, $sPartEndPattern); + } + + private function getPartPattern($sPartName, $sAction) + { + return ''; + } + + private function getPartTagPattern($sPartName, $bMark=true) + { + $sPartTag = 'PART '.$sPartName; + return $bMark?$this->addTagMark($sPartTag):$sPartTag; + } + + public function addInstance($sPartName, $asTags) + { + $this->newInstance($sPartName); + foreach($asTags as $sTagName=>$sTagValue) + { + $this->setInstanceTag($sPartName, $sTagName, $sTagValue); + } + } + + public function newInstance($sPartName) + { + //Finding the part ($oMask is a pointer) + $oMask = $this->findPart($this, $sPartName); + if(!$oMask) $this->addError('No part found : '.$sPartName); + + //Retrieving source html + $sPartSource = $oMask->asPartsSource[$sPartName]; + + //Creating new instance + $oInstance = new Mask(); + $oInstance->initFileFromString($sPartSource, $sPartName); + $oMask->aoInstances[$sPartName][] = $oInstance; + } + + public function setInstanceTag($sPartName, $sTagName, $sTagValue) + { + $oMask = $this->findPart($this, $sPartName); + if(!$oMask) $this->addError('No part found : '.$sPartName); + + $oMask->getCurrentInstance($sPartName)->setTag($sTagName, $sTagValue); + } + + private function findPart($oMask, $sPartName) + { + if(array_key_exists($sPartName, $oMask->aoInstances)) + { + return $oMask; + } + else //going deeper to find the part + { + foreach($oMask->aoInstances as $sLevelPartName=>$aoInstances) + { + if(!empty($aoInstances)) + { + //take last instances + $oTmpMask = $this->findPart($oMask->getCurrentInstance($sLevelPartName), $sPartName); + if($oTmpMask) return $oTmpMask; + } + } + } + return false; + } + + private function getCurrentInstance($sPartName) + { + if(!empty($this->aoInstances[$sPartName])) + { + return end($this->aoInstances[$sPartName]); + } + else + { + return false; + } + } + + public function getTags() + { + $sSafeTagMark = preg_quote(self::TAG_MARK); + $sPattern = '/'.$sSafeTagMark.'(?P\w+)'.$sSafeTagMark.'/u'; + preg_match_all($sPattern, $this->sMask, $asMatches); + return array_unique(array_filter($asMatches['tag'])); + } + + public function setTag($sTagName, $sTagValue) + { + $this->asTags[$sTagName] = $sTagValue; + } + + public function setTags($asTags) + { + foreach($asTags as $sTagName=>$sTagValue) $this->setTag($sTagName, $sTagValue); + } + + public function getMask() + { + $sCompletedMask = $this->sMask; + + //build parts + foreach($this->aoInstances as $sPart=>$aoParts) + { + $sTagValue = ''; + foreach($aoParts as $oInstance) + { + $sTagValue .= $oInstance->getMask(); + } + $this->setTag($this->getPartTagPattern($sPart, false), $sTagValue); + } + + //replace tags + if(!empty($this->asTags)) + { + $asTags = $this->addTagMark(array_keys($this->asTags)); + $sCompletedMask = str_replace($asTags, $this->asTags, $sCompletedMask); + } + return $sCompletedMask; + } + + private function addTagMark($oData) + { + return ToolBox::array_map_encapsulate($oData, self::TAG_MARK); + } +} + +?> \ No newline at end of file diff --git a/inc/mysqlmanager.php b/inc/mysqlmanager.php new file mode 100644 index 0000000..342d675 --- /dev/null +++ b/inc/mysqlmanager.php @@ -0,0 +1,601 @@ +array('table_name1'=>array('table_field1', 'table_field2', ...), 'table_name2'=>array(...)), + * 'types'=>array('field1'=>'field_type1', 'field2'=>'field_type2', ...) + * 'constraints'=>array('table_name1'=>'table_contraint1', 'table_name2'=>'table_contraint2', ...), + * 'cascading_delete'=>array('table_name1'=>array('linked_table1', 'linked_table2', ...), 'table_name2'=>...)) + * @var Array + */ + public function __construct($sDbServer, $sLogin, $sPass, $sDatabase, $asOptions, $sEncoding='utf8mb4') + { + parent::__construct(__CLASS__, Settings::DEBUG); + $this->sDatabase = $sDatabase; + $this->asOptions = $asOptions; + //$this->oConnection = mysql_connect(self::DB_SERVER, self::DB_LOGIN, self::DB_PASS); + $this->oConnection = new mysqli($sDbServer, $sLogin, $sPass); + $this->syncPhpParams($sEncoding); + + /* + $dsn = 'mysql:dbname='.$this->sDatabase.';host='.self::DB_SERVER; + try {$dbh = new PDO($dsn, self::DB_LOGIN, self::DB_PASS);} + catch (PDOException $e) {$this->addError('Connexion échouée : ' . $e->getMessage());} + */ + + $this->setTrace(false); + //if(!$this->oConnection) + if($this->oConnection->connect_error) + { + //$this->addError('bug connection'); + $this->addError('bug connection : '.$this->oConnection->connect_error); + $this->sDbState = self::DB_NO_CONN; + } + else + { + //if(!mysql_select_db($this->sDatabase, $this->oConnection)) + if(!$this->oConnection->select_db($this->sDatabase)) + { + $this->addError('bug selecting database. Installing...'); + $this->sDbState = self::DB_NO_DATA; + } + } + } + + private function syncPhpParams($sEncoding) + { + //Characters encoding + $this->oConnection->set_charset($sEncoding); //SET NAMES + + //Time zone + $oNow = new DateTime(); + $iMins = $oNow->getOffset() / 60; + $iSign = ($iMins < 0)?-1:1; + $iMins = abs($iMins); + $iHours = floor($iMins / 60); + $iMins -= $iHours * 60; + $sOffset = sprintf('%+d:%02d', $iHours*$iSign, $iMins); + $this->setQuery("SET time_zone='{$sOffset}';"); + } + + public function __destruct() + { + parent::__destruct(); + //mysql_close($this->oConnection); + $this->oConnection->close(); + } + + public function setTrace($bAction) + { + $this->bTrace = $bAction; + } + + public function getTables() + { + /* + $oReflect = new ReflectionClass(__CLASS__); + $asConstants = $oReflect->getConstants(); + $sTableTag = '_TABLE'; + $asTables = array(); + foreach($asConstants as $sConstant=>$sConstantValue) + { + if(mb_strpos($sConstant, $sTableTag)!==false && mb_strpos($sConstant, $sTableTag)==(mb_strlen($sConstant) - mb_strlen($sTableTag))) + { + $asTables[] = $sConstantValue; + } + } + return $asTables; + */ + return array_keys($this->asOptions['tables']); + } + + public function install() + { + //Create Database + $this->setQuery("DROP DATABASE IF EXISTS ".$this->sDatabase); + $this->setQuery("CREATE DATABASE ".$this->sDatabase." DEFAULT CHARACTER SET ".Settings::DB_ENC." DEFAULT COLLATE ".Settings::DB_ENC."_general_ci"); + //mysql_select_db($this->sDatabase, $this->oConnection); + $this->oConnection->select_db($this->sDatabase); + + //Create tables + @array_walk($this->getInstallQueries(), array($this, 'setQuery')); + } + + //For debug purposes + public function getFullInstallQuery() + { + $asInstallQueries = $this->getInstallQueries(); + return str_replace("\n", "
    ", implode(";\n\n", $asInstallQueries))."\n\n"; + } + + private function getInstallQueries() + { + $asTables = $this->getTables(); + $asInstallQueries = array_map(array($this, 'getInstallQuery'), $asTables); + $asAlterQueries = $this->getForeignKeyQueries($asTables); + return array_merge($asInstallQueries, $asAlterQueries); + } + + private function getInstallQuery($sTableName) + { + $asTableColumns = $this->getTableColumns($sTableName); + $sQuery = "\n".$this->implodeAll($asTableColumns, "` ", "\n", "`", ",")."\n".implode(", \n", $this->getTableConstraints($sTableName)); + return "CREATE TABLE `{$sTableName}` ({$sQuery})"; + } + + private function getForeignKeyQueries($asTableNames) + { + $asForeignKeyQueries = array(); + foreach($asTableNames as $sTableName) + { + $asTableColumns = array_keys($this->getTablecolumns($sTableName)); + foreach($asTableColumns as $sColumnName) + { + if($this->isId($sColumnName) && $sColumnName!=self::getId($sTableName)) + { + $asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD INDEX(`".$sColumnName."`)"; + $asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD FOREIGN KEY (`".$sColumnName."`) REFERENCES ".self::getTable($sColumnName)."(`".$sColumnName."`)"; + } + } + } + return $asForeignKeyQueries; + } + + private function setQuery($sQuery, $sTypeQuery=__FUNCTION__) + { + $this->getQuery($sQuery, $sTypeQuery); + //return (mysql_affected_rows()!=0); + return ($this->oConnection->affected_rows!=0); + } + + private function getQuery($sQuery, $sTypeQuery=__FUNCTION__) + { + $sQuery = str_replace(array("\n", "\t"), array(" ", ""), $sQuery); + //$oResult = mysql_query($sQuery, $this->oConnection); + + if($this->bTrace) + { + $this->bDebug = true; + $this->addNotice($sQuery); + } + + if(!($oResult = $this->oConnection->query($sQuery))) + { + $this->addError("\nErreur SQL : \n".str_replace("\t", "", $sQuery)."\n\n".str_replace(array("\t", "\n"), "", $this->oConnection->error)); + } + return $oResult; + } + + public function getLastError() + { + + } + + public function getArrayQuery($sQuery, $bStringOnly=false, $sGroupBy='', $sTypeQuery=__FUNCTION__) + { + $iIndex = 0; + $iColumnCount = 0; + $asResult = array(); + $oResult = $this->getQuery($sQuery, true, $sTypeQuery); + if($oResult!==false) + { + //while($asCurrentRow = mysql_fetch_array($oResult)) + while($asCurrentRow = $oResult->fetch_array()) + { + if($bStringOnly) $asCurrentRow = $this->arrayKeyFilter($asCurrentRow, 'is_string'); + + //Add table reel keys + if($sGroupBy!='' && array_key_exists($sGroupBy, $asCurrentRow)) + { + $iRowKey = $asCurrentRow[$sGroupBy]; + unset($asCurrentRow[$sGroupBy]); + } + else $iRowKey = $iIndex; + + //For first loop, check table width + if($iIndex==0) $iColumnCount = count($asCurrentRow); + + //One column case : collapse a level + if($iColumnCount==1) $asCurrentRow = array_shift($asCurrentRow); + + $asResult[$iRowKey] = $asCurrentRow; + $iIndex++; + } + } + return $asResult; + } + + private function getMaxIncrementedValue($sTable) + { + return $this->selectValue($sTable, "MAX(".$this->getId($sTable).")"); + } + + public static function getId($sTableName, $bFull=false) + { + $sColumnName = self::ID_TAG.self::getText($sTableName); + return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName; + } + + public static function getText($sTableName, $bFull=false) + { + $sColumnName = mb_substr(str_replace('`', '', $sTableName), 0, -1); + $sColumnName = mb_substr($sColumnName, -2)=='ie'?mb_substr($sColumnName, 0, -2).'y':$sColumnName; + return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName; + } + + public static function getFullColumnName($sTableName, $sColumnName) + { + return $sTableName.".".$sColumnName; + } + + private function isId($sColumnName, $sTableName='') + { + $asTables = ($sTableName=='')?$this->getTables():array($sTableName); + $asTableIds = array_map(array('self', 'getId'), $asTables); + return in_array($sColumnName, $asTableIds); + } + + private function getTable($sTableId) + { + $asTables = $this->getTables(); + $asTableIds = array_map(array('self', 'getId'), $asTables); + if(in_array($sTableId, $asTableIds)) return $asTables[array_search($sTableId, $asTableIds)]; + else + { + $this->addError('Id '.$sTableId.' présent dans aucune table'); + return false; + } + } + + public function getTablecolumns($sTableName) + { + if(!array_key_exists($sTableName, $this->asOptions['tables'])) return false; + + $asTableColumns = array(self::getId($sTableName)); + foreach($this->asOptions['tables'][$sTableName] as $sFieldName) $asTableColumns[] = $sFieldName; + $asTableColumns[] = 'led'; + + $asTableName = array_fill(0, count($asTableColumns), $sTableName); + return array_combine($asTableColumns, array_map(array('self', 'getColumnType'), $asTableColumns, $asTableName)); + } + + private function getColumnType($sColumnName, $sTableName) + { + $sColumnType = ''; + switch($sColumnName) + { + case array_key_exists($sColumnName, $this->asOptions['types']): + $sColumnType = $this->asOptions['types'][$sColumnName]; + break; + case 'led': + $sColumnType = "TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP"; + break; + case $this->isId($sColumnName, $sTableName): + $sColumnType = "int(10) UNSIGNED auto_increment"; + break; + case $this->isId($sColumnName): + $sColumnType = "int(10) UNSIGNED"; + break; + } + return $sColumnType; + } + + private function getTableConstraints($sTableName) + { + //Primary key + $asTableConstraints = array('PRIMARY' => "PRIMARY KEY (`".self::getId($sTableName)."`)"); + + //Foreign keys: applied using ALTER TABLE syntax at the end to prevent scheduling CREATE TABLE queries + + //Other constraints + if(array_key_exists($sTableName, $this->asOptions['constraints'])) $asTableConstraints[] = $this->asOptions['constraints'][$sTableName]; + return $asTableConstraints; + } + + private function addQuotes($oData) + { + //TODO remake + $asTrustedFunc = array('CURDATE()', 'NOW()'); + $sChar = "'"; + if(is_array($oData)) + { + $asChar = array_fill(1, count($oData), $sChar); + return array_combine(array_keys($oData), array_map(array($this, 'addQuotes'), $oData, $asChar)); + } + else + { + if(in_array($oData, $asTrustedFunc)) return $oData; + else return $sChar.$oData.$sChar; + } + } + + private function getLastId() + { + return $this->oConnection->insert_id; + } + + public function insertRow($sTableName, $asData) + { + $this->cleanSql($sTableName); + $this->cleanSql($asData); + + $asQueryValues = $this->addQuotes($asData); + $sQuery = "INSERT INTO ".$sTableName." (`".implode("`, `", array_keys($asQueryValues))."`) VALUES (".implode(", ", $asQueryValues).")"; + return $this->setQuery($sQuery)?$this->getLastId():0; + } + + public function updateRow($sTableName, $asConstraints, $asData) + { + return $this->updateRows($sTableName, $asConstraints, $asData, 1); + } + + public function updateRows($sTableName, $asConstraints, $asData, $iLimit=0) + { + if(!is_array($asConstraints)) + { + $asConstraints = array($this->getId($sTableName)=>$asConstraints); + } + + $this->cleanSql($sTableName); + $this->cleanSql($iTableId); + $this->cleanSql($asData); + $this->cleanSql($asConstraints); + $asQueryValues = $this->addQuotes($asData); + $asConstraintsValues = $this->addQuotes($asConstraints); + $this->addColumnSelectors($asQueryValues); + $this->addColumnSelectors($asConstraintsValues); + + $sLimit = $iLimit>0?" LIMIT $iLimit":""; + $sQuery = "UPDATE {$sTableName} ". + "SET ".$this->implodeAll($asQueryValues, " = ", ", ")." ". + "WHERE ".$this->implodeAll($asConstraintsValues, " = ", " AND ").$sLimit; + + $iResult = false; + if($this->setQuery($sQuery)) + { + $iResult = ($iLimit==1)?$this->selectValue($sTableName, $this->getId($sTableName), $asConstraints):true; + } + return $iResult; + } + + public function insertUpdateRow($sTableName, $asData, $asKeys=array(), $bUpdate=true) + { + $sTableIdName = self::getId($sTableName); + + //check for data in the db + if($asKeys==array()) + { + $asKeys[] = $sTableIdName; + } + $asValues = array_intersect_key($asData, array_flip($asKeys)); + $iTableId = $this->selectValue($sTableName, $sTableIdName, $asValues); + + //insert + if(!$iTableId) + { + $iTableId = $this->insertRow($sTableName, $asData); + } + //Update + elseif($bUpdate) + { + if(array_key_exists($sTableIdName, $asData)) + { + unset($asData[$sTableIdName]); + } + $iTableId = $this->updateRow($sTableName, $iTableId, $asData); + } + return $iTableId; + } + + public function selectInsert($sTableName, $asData, $asKeys=array()) + { + return $this->insertUpdateRow($sTableName, $asData, $asKeys, false); + } + + public function deleteRow($sTableName, $iTableId) + { + $this->cleanSql($sTableName); + $this->cleanSql($iTableId); + + //linked tables + switch($sTableName) + { + case array_key_exists($sTableName, $this->asOptions['cascading_delete']) : + $asTables = array_filter(is_array($sTableName)?$sTableName:array($sTableName) + $this->asOptions['cascading_delete'][$sTableName]); + break; + case is_string($sTableName) : + $asTables = array($sTableName); + break; + case is_array($sTableName): + $asTables = $sTableName; + break; + default: + $asTables = array(); + } + foreach($asTables as $sTable) + { + $this->setQuery("DELETE FROM ".$sTable." WHERE ".$this->getId($sTableName)." = ".$iTableId); + } + } + + public function emptyTable($sTableName) + { + $this->cleanSql($sTableName); + return $this->setQuery("TRUNCATE ".$sTableName); + } + + public function selectRows($asInfo, $bStringOnly=true, $sGroupBy='') + { + $asAttributes = array('select'=>"SELECT", 'from'=>"FROM", 'join'=>"LEFT JOIN", 'joinOn'=>"LEFT JOIN", 'constraint'=>"WHERE", 'groupBy'=>"GROUP BY", 'orderBy'=>"ORDER BY", 'limit'=>'LIMIT'); + $asRowSeparators = array('select'=>", ", 'from'=>"", 'join'=>" LEFT JOIN ", 'joinOn'=>" LEFT JOIN ", 'constraint'=>" AND ", 'groupBy'=>", ", 'orderBy'=>", ", 'limit'=>""); + $asOperators = array('constraint'=>" = ", 'orderBy'=>" ", 'join'=>" USING(", 'joinOn'=>" ON "); + $asEndOfStatement = array('constraint'=>"", 'orderBy'=>"", 'join'=>")", 'joinOn'=>""); + + $sQuery = ""; + foreach($asAttributes as $sStatement => $sKeyWord) + { + $asSelection = array_key_exists($sStatement, $asInfo)?$asInfo[$sStatement]:array(); + if(!is_array($asSelection)) + { + $asSelection = array($asSelection); + } + + //if provided values + if(!empty($asSelection)) + { + $this->cleanSql($asSelection); + + if($sStatement=='constraint' && !array_key_exists('constVar', $asInfo)) + { + $asSelection = $this->addQuotes($asSelection); + } + $this->addColumnSelectors($asSelection); + + $sQuery .= " ".$sKeyWord." "; + + //in case of double value input + if(array_key_exists($sStatement, $asOperators)) + { + if($sStatement=='constraint' && array_key_exists('constOpe', $asInfo)) + { + $asOperators[$sStatement] = $asInfo['constOpe']; + } + $sQuery .= $this->implodeAll($asSelection, $asOperators[$sStatement], $asRowSeparators[$sStatement], "", $asEndOfStatement[$sStatement]); + } + else + { + $sQuery .= implode($asRowSeparators[$sStatement], $asSelection); + } + } + //default value for select + elseif($sStatement=='select') + { + $sQuery .= " ".$sKeyWord." * "; + } + } + return $this->getArrayQuery($sQuery, $bStringOnly, $sGroupBy); + } + + private function addColumnSelectors(&$asSelection) + { + //FIXME get rid of this + $sSqlWord = 'option'; + $sKey = array_search($sSqlWord, $asSelection); + if($sKey!==false) + { + $asSelection[$sKey] = "`".$asSelection[$sKey]."`"; + } + elseif(array_key_exists($sSqlWord, $asSelection)) + { + $asSelection["`".$sSqlWord."`"] = $asSelection[$sSqlWord]; + unset($asSelection[$sSqlWord]); + } + } + + public function selectRow($sTableName, $asConstraints=array(), $sColumnName='*') + { + if(!is_array($asConstraints)) + { + $asConstraints = array($this->getId($sTableName)=>$asConstraints); + } + $asResult = $this->selectRows(array('select'=>$sColumnName, 'from'=>$sTableName, 'constraint'=>$asConstraints)); + $iCountNb = count($asResult); + switch($iCountNb) + { + case 0 : + return false; + case $iCountNb > 1 : + $this->addError('Trop de résultats pour un selectRow() : '.$iCountNb.' lignes. Table: '.$sTableName.', contrainte: '.self::implodeAll($asConstraints, '=', ' ').', colonne: '.$sColumnName); + break; + } + return array_shift($asResult); + } + + public function selectValue($sTableName, $sColumnName, $oConstraints=array()) + { + if(!is_array($oConstraints)) + { + $oConstraints = array($this->getId($sTableName)=>$oConstraints); + } + return $this->selectRow($sTableName, $oConstraints, $sColumnName); + } + + public function pingValue($sTableName, $oConstraints) + { + return $this->selectValue($sTableName, 'COUNT(1)', $oConstraints); + } + + public function cleanSql(&$oData) + { + //self::cleanData($oData, 'mysql_real_escape_string'); + //$oData = self::cleanData($oData, 'mysql_real_escape_string'); + $this->cleanData($oData); + $oData = $this->cleanData($oData); + } + + //TODO déplacer dans ToolBox::implodeALl + public static function implodeAll($asText, $asKeyValueSeparator='', $sRowSeparator='', $sKeyPre='', $sValuePost=false) + { + if($sValuePost===false) + { + $sValuePost = $sKeyPre; + } + $asCombinedText = array(); + + //if unique value for key value separator + if(!is_array($asKeyValueSeparator) && !empty($asText)) + { + $asKeyValueSeparator = array_combine(array_keys($asText), array_fill(0, count($asText), $asKeyValueSeparator)); + } + + foreach($asText as $sKey=>$sValue) + { + $asCombinedText[] = $sKeyPre.$sKey.$asKeyValueSeparator[$sKey].(is_array($sValue)?implode($sValue):$sValue).$sValuePost; + } + return implode($sRowSeparator, $asCombinedText); + } + + public static function arrayKeyFilter($asArray, $sCallBack) + { + $asValidKeys = array_flip(array_filter(array_keys($asArray), $sCallBack)); + return array_intersect_key($asArray, $asValidKeys); + } + + public function cleanData($oData) + { + if(!is_array($oData)) + { + return $this->oConnection->real_escape_string($oData); + } + elseif(count($oData)>0) + { + $asKeys = array_map(array($this, 'cleanData'), array_keys($oData)); + $asValues = array_map(array($this, 'cleanData'), $oData); + return array_combine($asKeys, $asValues); + } + } +} + +?> \ No newline at end of file diff --git a/inc/procedure.php b/inc/procedure.php new file mode 100644 index 0000000..217ca3a --- /dev/null +++ b/inc/procedure.php @@ -0,0 +1,274 @@ +text + private $asImages; // [id_step][id_image]=>array('name'=>file name, 'desc'=> description) + + function __construct($oMySql) + { + parent::__construct(__CLASS__, Settings::DEBUG); + $this->oMySql = $oMySql; + $this->iPrevProcId = 0; + $this->setProcedureId(0); + $this->sTitle = ''; + $this->sDescription = ''; + $this->dLed = ''; + $this->asSteps = array(); + $this->asImages = array(); + } + + public function getProcedureId() + { + return $this->iProcId; + } + + public function getProcedureLed() + { + return $this->dLed; + } + + public function setProcedureId($iProcId) + { + $this->iProcId = $iProcId; + } + + public function inputForm($asPost) + { + //User + $this->iUserId = $asPost['id_user']; + + //Titles + $this->sTitle = isset($asPost['title'])?$asPost['title']:''; + $this->sDescription = isset($asPost['description'])?$asPost['description']:''; + + //Steps + $sStepPattern = '/c(?P\d+)_step_text/u'; + $sImagePattern = '/c(?P\d+)_(?P\d+)_image_(?P\w+)/u'; + foreach($asPost as $sFormId=>$sValue) + { + //Step Text + preg_match($sStepPattern, $sFormId, $asMatches); + if(isset($asMatches['step_id'])) + { + $this->asSteps[$asMatches['step_id']] = $sValue; + } + else + { + preg_match($sImagePattern, $sFormId, $asMatches); + if + ( + isset($asMatches['step_id'], $asMatches['image_id'], $asMatches['image_info']) && + ($asMatches['image_info'] == 'name' || $asMatches['image_info']== 'desc') + ) + { + $this->asImages[$asMatches['step_id']][$asMatches['image_id']][$asMatches['image_info']] = $sValue; + } + } + } + } + + private function isImageSaved($sFileName, $bInTemp=false) + { + $sPath = $bInTemp?self::IMAGE_FOLDER_TMP:self::IMAGE_FOLDER; + return file_exists($sPath.$sFileName); + } + + public function checkIntegrity() + { + $asErrors = array(); + + //Check title + if($this->sTitle=='') $asErrors['title'] = 'Un titre est nécessaire'; + + //Check steps + if(count($this->asSteps)==0) + { + $asErrors['step'] = 'Au moins une étape est nécessaire'; + } + else + { + foreach($this->asSteps as $iStepId=>$sText) + { + if($sText=='') + { + $asErrors['step_'.$iStepId] = 'Une étape ne possède pas de texte'; + } + } + } + + //Check images + foreach($this->asImages as $iStepId=>$asImages) + { + foreach($asImages as $iImageId=>$asFileInfo) + { + //file exists in temp or already stored + if(!$this->isImageSaved($asFileInfo['name']) && !$this->isImageSaved($asFileInfo['name'], true)) + { + $asErrors['image_'.$iStepId.'_'.$iImageId] = 'Une image n\'a pas été téléchargée'; + } + if($asFileInfo['desc']=='') + { + $asErrors['image_'.$iStepId.'_'.$iImageId] = 'Une image n\'a pas de nom (description)'; + } + } + } + return $asErrors; + } + + public function saveProcedure($iPrevProcId) + { + //Add new procedure + $asData = array('title'=>$this->sTitle, 'description'=>$this->sDescription, MySqlManager::getId(Databap::USER_TABLE)=>$this->iUserId); + $iDbProcId = $this->oMySql->insertRow(Databap::PROC_TABLE, $asData); + + //Add new steps + $asStepData = $asImgData = array(MySqlManager::getId(Databap::PROC_TABLE)=>$iDbProcId); + foreach($this->asSteps as $iStepId=>$sStepDesc) + { + $asStepData['description'] = $sStepDesc; + $iDbStepId = $this->oMySql->insertRow(Databap::STEP_TABLE, $asStepData); + + //Add new images + $asImgData[MySqlManager::getId(Databap::STEP_TABLE)] = $iDbStepId; + foreach($this->asImages[$iStepId] as $asImageInfo) + { + $asImgData['description'] = $asImageInfo['desc']; + $asImgData['file_name'] = $asImageInfo['name']; + $iDbImageId = $this->oMySql->insertRow(Databap::IMG_TABLE, $asImgData); + $this->saveImage($asImgData['file_name']); + } + } + + //Add this procedure to the group + $bCreation = ($iPrevProcId==0); + //$this->oMySql->deleteRow(Databap::PROC_TABLE, $this->iPrevProcId); + //$this->oMySql->updateRow(Databap::PROC_TABLE, $iPrevProcId, array('refer_id'=>$iDbProcId)); + $iReferId = $bCreation?$iDbProcId:$this->oMySql->selectValue(Databap::PROC_TABLE, 'refer_id', $iPrevProcId); + $this->oMySql->updateRow(Databap::PROC_TABLE, $iDbProcId, array('refer_id'=>$iReferId)); + + //Update new Procedure Id + //$this->setProcedureId($iDbProcId); + + //Reload Procedure + $this->loadProcedure($iDbProcId); + + //return creation or edition + return $bCreation; + } + + private function saveImage($sFileName) + { + if(!$this->isImageSaved($sFileName)) + { + $sTempFilePath = self::IMAGE_FOLDER_TMP.$sFileName; + $sFilePath = self::IMAGE_FOLDER.$sFileName; + + //Real Size Image + if(!rename($sTempFilePath, $sFilePath)) + { + $this->addError('Unmoveable file : '.$sTempFilePath); + } + + //Thumbnail picture + $asResult = ToolBox::createThumbnail($sFilePath, self::THUMB_MAX_SIZE, self::THUMB_MAX_SIZE, self::IMAGE_FOLDER_THUMB, false, Databap::$UPLOAD_IMG_EXTS); + if($asResult['error']!='') $this->addError('Unable to create thumbnail : '.$asResult['out']. '('.$asResult['error'].')'); + } + } + + public function loadProcedure($iProcId) + { + //Column names + $sProcIdCol = MySqlManager::getId(Databap::PROC_TABLE); + $sStepIdCol = MySqlManager::getId(Databap::STEP_TABLE); + $sImageIdCol = MySqlManager::getId(Databap::IMG_TABLE); + + //Get last id available + $this->setProcedureId($iProcId); + $this->refreshProcId(); + $asInfo = array('from'=>Databap::PROC_TABLE, 'constraint'=>array($sProcIdCol=>$this->getProcedureId())); + + //Load procedure info + $asResult = $this->oMySql->selectRow(Databap::PROC_TABLE, $asInfo['constraint']); + $this->sTitle = $asResult['title']; + $this->sDescription = $asResult['description']; + $this->iUserId = $asResult[MySqlManager::getId(Databap::USER_TABLE)]; + $this->dLed = $asResult['led']; + + //Load steps info + $asInfo['from'] = Databap::STEP_TABLE; + $this->asSteps = $this->oMySql->selectRows($asInfo, true, $sStepIdCol); + + //Load images info + $asInfo['from'] = Databap::IMG_TABLE; + foreach($this->oMySql->selectRows($asInfo) as $asRow) + { + $this->asImages[$asRow[$sStepIdCol]][$asRow[$sImageIdCol]]['name'] = $asRow['file_name']; + $this->asImages[$asRow[$sStepIdCol]][$asRow[$sImageIdCol]]['desc'] = $asRow['description']; + } + } + + public function getProcedure() + { + // [id_step]=>text + // [id_step][id_image]=>array('name'=>file name, 'desc'=> description) + /* + $asSteps = $asImages = array(); + $iLocalStepId = 1; + foreach($this->asSteps as $iStepId=>$sStepDesc) + { + $asSteps[$iLocalStepId] = $sStepDesc; + if(array_key_exists($iStepId, $this->asImages)) + { + $asImages[] = + } + $iLocalStepId++; + }*/ + return array( 'proc_id'=>$this->iProcId, + 'id_user'=>$this->iUserId, + 'title'=>Databap::getDescriptionFormat($this->sTitle), + 'description'=>Databap::getDescriptionFormat($this->sDescription), + 'led'=>Databap::getDateFormat($this->dLed), + 'steps'=>$this->asSteps, + 'images'=>$this->asImages); + } + + private function refreshProcId() + { + /* + $iReferId = $this->getProcedureId(); + do + { + $iProcId = $iReferId; + $iReferId = $this->oMySql->selectValue(Databap::PROC_TABLE, 'refer_id', $iProcId); + } + while($iReferId!=0); + $this->setProcedureId($iProcId); + */ + $iReferId = $this->oMySql->selectValue(Databap::PROC_TABLE, 'refer_id', $this->getProcedureId()); + $sSelect = "MAX(".MySqlManager::getId(Databap::PROC_TABLE).")"; + $iProcId = $this->oMySql->selectValue(Databap::PROC_TABLE, $sSelect, array('refer_id'=>$iReferId)); + $this->setProcedureId($iProcId); + } +} + +?> \ No newline at end of file diff --git a/inc/reader.php b/inc/reader.php new file mode 100644 index 0000000..33ba65b --- /dev/null +++ b/inc/reader.php @@ -0,0 +1,267 @@ +bImprovedColoring = $bImprovedColoring; + + //get code in db + $this->sCode = $sCode; + if($this->sCode=='') + { + $this->sCode = self::NOT_FOUND; + } + + //ABAP key words and chars + $this->asAbap = array + ( + 'word'=>array + ( + 'wCore'=>array( /*'<icon>','<itab>',*/'ABBREVIATED','ABSTRACT','ABSTRACTFINAL','ACCEPT','ACCEPTING','ACCORDING','ACTUAL','ADD', + 'ADD-CORRESPONDING','ADDITIONS','ADJACENT','ALIASES','ALL','ALLOCATE','ANALYZER', 'AND','APPEND','APPENDING','AS', + 'ASCENDING','ASCENDINGDESCENDING','AT','ATTRIBUTE','AUTHORITY-CHECK','BACKGOUND','BEFORE','BEGIN','BETWEEN', + 'BIGLITTLE','BINARY','BIT','BLANK','BLOCK','BREAK-POINT','BY','BYPASSING','BYTE','BYTECHARACTER','CASE', + 'CASERESPECTING','CASTING','CENTERED','CHAIN','CHANGE','CHANGING','CHARACTER','CHECK','CHECKBOX', + 'CHECKBOXSYMBOLICONLINE','CLASS','CLASS-DATA','CLASS-EVENTS','CLASS-METHODS','CLEANUP','CLEAR','CLIENT','CLOCK', + 'CODE','COL_BACKGROUND','COL_HEADING','COL_NORMAL','COL_TOTAL','COLLECT','COLOR','COLUMN','COMMENT','COMMIT', + 'COMMON','COMMUNICATION','COMPARING','COMPONENTS','COMPUTE','CONCATENATE','CONDENSE','CONSTANTS','CONTEXT', + 'CONTEXTS','CONTINUE','CONTROL','CONTROLS','CONVERSION','CONVERT','COUNTRY','COUNTY','CURRENT','CURSOR', + 'CUSTOMER-FUNCTION','DATA','DATABASE','DATASET','DATE','DEALLOCATE','DECIMALS','DEFAULT','DEFAULTUTF-8NON-UNICODE', + 'DEFERRED','DEFINE','DEFINING','DEFINITION','DELETE','DELETING','DEMAND','DESCENDING','DESCRIBE','DESTINATION', + 'DIALOG','DIRECTORY','DISTANCE','DISTINCT','DIVIDE-CORRESPONDING','DO','DUPLICATE','DUPLICATES','DURING','DYNAMIC', + 'EDIT','EDITOR-CALL','ELSE','ELSEIF','ENCODING','END','ENDAT','ENDCASE','ENDCATCH','ENDCHAIN','ENDCLASS','ENDDO', + 'ENDEXEC','ENDFORM','ENDFUNCTION','ENDIAN','ENDIF','ENDING','ENDINTERFACE','ENDLOOP','ENDMETHOD','ENDMODULE', + 'ENDON','ENDPROVIDE','ENDSELECT','ENDTRY','ENDWHILE','END-OF_FILE','END-OF-DEFINITION','END-OF-PAGE', + 'END-OF-SELECTION','ENTRIES','EQ','ERRORS','EVENT','EVENTS','EXCEPTION','EXCEPTIONS','EXCEPTION-TABLE','EXCLUDE', + 'EXCLUDING','EXEC','EXIT','EXIT-COMMAND','EXPORT','EXPORTING','EXTENDED','EXTRACT','FETCH','FIELD','FIELD-GROUPS', + 'FIELDSNO','FIELD-SYMBOLS','FILTER','FINAL','FIND','FIRST','FOR','FORM','FORMAT','FORWARDBACKWARD','FOUND','FRAME', + 'FREE','FRIENDS','FROM','FUNCTION','FUNCTION-POOL','GE','GET','GIVING','GROUP','GT','HANDLER','HASHED','HAVING', + 'HEADER','HEADING','HELP-ID','HIDE','HIGHLOW','HOLD','HOTSPOT','ID','IF','IGNORING','IMMEDIATELY','IMPLEMENTATION', + 'IMPORT','IMPORTING','IN','INCREMENT','INDEX','INDEX-LINE','INHERITING','INIT','INITIAL','INITIALIZATION','INNER', + 'INNERLEFT','INSERT','INSTANCES','INTENSIFIED','INTERFACE','INTERFACES','INTERVALS','INTO','INTOAPPENDING', + 'INVERTED-DATE','IS','ITAB','JOIN','KEEPING','KEY','KEYS','KIND','LANGUAGE','LAST','LE','LEAVE','LEFT', + 'LEFT-JUSTIFIED','LEFTRIGHT','LEFTRIGHTCIRCULAR','LEGACY','LENGTH','LIKE','LINE','LINE-COUNT','LINES','LINES', + 'LINE-SELECTION','LINE-SIZE','LIST','LIST-PROCESSING','LOAD','LOAD-OF-PROGRAM','LOCAL','LOCALE','LOOP','LT', + 'MARGIN','MARK','MASK','MATCH','MAXIMUM','MEMORY','MESSAGE','MESSAGE-ID','MESSAGES','METHOD','METHODS','MOD','MODE', + 'MODEIN','MODIF','MODIFIER','MODIFY','MODULE','MOVE','MOVE-CORRESPONDING','MULTIPLY-CORRESPONDING','NE','NEW', + 'NEW-LINE','NEW-PAGE','NEXT','NODES','NODETABLE','NO-DISPLAY','NO-EXTENSION','NO-GAP','NO-GAPS', + 'NO-HEADINGWITH-HEADING','NON','NO-SCROLLING','NO-SCROLLINGSCROLLING','NOT','NO-TITLEWITH-TITLE','NO-ZERO','NUMBER', + 'OBJECT','OBLIGATORY','OCCURENCE','OCCURENCES','OCCURS','OF','OFF','OFFSET','ON','ONLY','ONOFF','OPEN','OPTION', + 'OPTIONAL','OR','ORDER','OTHERS','OUTPUT-LENGTH','OVERLAY','PACK','PACKAGE','PAGE','PAGELAST','PAGEOF','PAGEPAGE', + 'PAGES','PARAMETER','PARAMETERS','PARAMETER-TABLE','PART','PERFORM','PERFORMING','PFN','PF-STATUS','PLACES', + 'POS_HIGH','POS_LOW','POSITION','POSITIONS','PRIMARY','PRINT','PRINT-CONTROL','PRIVATE','PROCESS','PROGRAM', + 'PROPERTY','PROTECTED','PROVIDE','PUBLIC','PUBLICPROTECTEDPRIVATE','PUSHBUTTON','PUT','QUICKINFO','RADIOBUTTON', + 'RAISE','RAISING','RANGE','RANGES','READ','RECEIVE','RECEIVING','REDEFINITION','REFERENCE','REFRESH','REJECT', + 'RENAMING','REPLACE','REPLACEMENT','REPORT','RESERVE','RESET','RESOLUTION','RESULTS','RETURN','RETURNING','RIGHT', + 'RIGHT-JUSTIFIED','ROLLBACK','ROWS','RUN','SCAN','SCREEN','SCREEN-GROUP1','SCREEN-GROUP2','SCREEN-GROUP3', + 'SCREEN-GROUP4','SCREEN-GROUP5','SCREEN-INPUT','SCREEN-INTENSIFIED','SCROLL','SCROLL-BOUNDARY','SEARCH','SECTION', + 'SELECT','SELECTION','SELECTIONS','SELECTION-SCREEN','SELECTION-SET','SELECTION-TABLE','SELECT-OPTIONS','SEND', + 'SEPARATED','SET','SHARED','SHIFT','SIGN','SINGLE','SINGLEDISTINCT','SIZE','SKIP','SORT','SORTABLE','SPACE', + 'SPECIFIED','SPLIT','SQL','STABLE','STAMP','STANDARD','START','STARTING','START-OF-SELECTION','STATICS','STEP-LOOP', + 'STOP','STRLEN','STRUCTURE','SUBMIT','SUBTRACT','SUBTRACT-CORRESPONDING','SUFFIX','SUPPLY','SUPPRESS','SYMBOLS', + 'SYSTEM-EXCEPTIONS','TABLE','TABLES','TABLEVIEW','TASK','TEXT','THEN','TIME','TIMES','TITLE','TITLEBAR','TO', + 'TOPIC','TOP-OF-PAGE','TRAILING','TRANSACTION','TRANSFER','TRANSLATE','TRY','TYPE','TYPELIKE','TYPE-POOL', + 'TYPE-POOLS','TYPES','ULINE','UNION','UNIQUE','UNTIL','UP','UPDATE','UPPERLOWER','USER-COMMAND','USING','VALUE', + 'VALUES','VARY','VARYING','VERSION','VIA','WAIT','WHEN','WHERE','WHILE','WINDOW','WITH','WORK','WRITE','ZONE','ADD', + 'ADD-CORRESPONDING','APPEND','BREAK-POINT','CORRESPONDING','FIELDS','ASSIGN','AVG','BACK','CALL','CATCH','CHANGE', + 'CHECK','CLEAR','CLOSE','COMMIT','COMPUTE','COUNT','CREATE','DATASET','DELETE','DESCRIBE','DIVIDE','DO','ELSE', + 'ELSEIF','EXIT','FIND','FOR','FORMAT','GET','IF','INCLUDE','LOOP','MAX','MESSAGE','METHOD','MIN','MODIFY','MOVE', + 'MULTIPLY','NEW-PAGE','OBJECT','OPEN','ORDER','READ','SORT','SUM','TRANSLATE','ULINE','WORK','WRITE','XSTRLEN', + 'ABS','ACOS','AFTER','ALL','AND','AS','ASIN','ASSIGNING','AT','ATAN','BEGIN','BUFFER','BY','CA','CEIL','CENTERED', + 'CN','CO','COLOR','COMPARING','COMPONENT','COS','COSH','CP','CS','DEFAULT','END','EQ','EXP','FLOOR','FRAC','GE', + 'GROUP','GT','ICON','IN','INDEX','INNER','INTENSIFIED','INTO','IS','JOIN','LE','LEADING','LEAVE','LEFT','LIKE', + 'LOG','LOG10','LOWER','LT','M','MOD','NA','NE','NOT','NP','NS','O','OF','OFF','ON','OR','OTHERS','OUTER','REF', + 'RIGHT','SIGN','SIN','SINH','SQRT','TABLE','TABLENAME','TAN','TANH','TITLE','TO','TRAILING','TRUNC','TYPE','UPPER', + 'USING','VALUE','WITH', 'TRANSPORTING', 'TYPE-POOLS'), + 'wBwCore'=>array('SOURCE_PACKAGE', 'RESULT_PACKAGE', '', ''), + 'cOperator'=>array('(', ')', ',', '.', ':', '-', '~', '[', ']', '<', '>'), + 'cCalculation'=>array('+', '-', '*', '/', '=', '(', ')'), + 'cComment'=>array('*'), + 'cPartComment'=>array('"'), + 'cString'=>array('"', '''), + 'iNumber'=>array('1', '2', '3', '4', '5', '6', '7', '8', '9', '0'), + 'wExpand'=>array('loop'=>'endloop', 'if'=>'endif'), + 'wGlobal'=>array('SY'), + 'wCodePart'=>array('main', 'global', 'global2') + ), + 'color'=>array + ( + 'wCore'=>'core', + 'wBwCore'=>'core', + 'cOperator'=>'operator', + 'cCalculation'=>'operator', + 'cComment'=>'comment', + 'cPartComment'=>'comment', + 'cString'=>'string', + 'iNumber'=>'number', + 'wExpand'=>'expand', + 'wGlobal'=>'glogal', + 'wCodePart'=>'code_part' + ) + ); + } + + private function getWords($sKey) + { + return $this->asAbap['word'][$sKey]; + } + + private function getColor($sKey) + { + return $this->asAbap['color'][$sKey]; + } + + private function addColoring() + { + //Safe characters + $sCode = $this->convText2Html($this->sCode); + $sServName = $_GET['serv_name']; + $sSafeRexExServName = preg_quote(str_replace('http://', '', $sServName), '/'); + + $asLines = explode("\n", $sCode); + $asLineClasses = array(); + $iOffSet = 0; + + //building lines + foreach($asLines as $iLineNb=>$sLine) + { + /* Code ref: + *
  • + * + * - + * + * #line# + *
  • + */ + $sLiClass = $sExpandSign = $sCodeClass = $sExpandHtml = $sExpandButton = ''; + $sFirstWord = $this->getFirstWord($sLine); + $sFirstChar = $this->getFirstChar($sLine); + + //Apply current classes + $sLiClass = implode(' ', $asLineClasses); + + //Expanding starting/ending point + if(in_array($sFirstWord, array_keys($this->getWords('wExpand')))) //start + { + $iLoopId = count($asLineClasses)+$iOffSet; + $asLineClasses[] = 'loop'.$iLoopId; + $sExpandButton = ''; + } + elseif(in_array($sFirstWord, $this->getWords('wExpand'))) //end + { + array_pop($asLineClasses); + $iOffSet++; + $sExpandButton = ''; + } + + //Enlarge line + //if(mb_strpos($sLine, $sServName) !== false) $sLiClass .= ' bigline'; + + //comments (entire line) + $sCodeClass = (in_array($sFirstChar, $this->getWords('cComment')))?'comment':'code'; + + //Internal & external Urls + $sLine = Toolbox::findReplaceLinks($sLine); + + $asColoredLines[] = '
  • '.$sExpandButton.''.$sLine.'
  • '; + } + $sCode = implode('', $asColoredLines); + + //Strings + foreach($this->getWords('cString') as $sStringWord) + { + $sPattern = '/>([^<]*?)'.$sStringWord.'([^<]*?)'.$sStringWord.'/u'; + $sCode = preg_replace($sPattern, '>$1'.$sStringWord.'$2'.$sStringWord.'', $sCode); + } + + //Part comment + $sPattern = '/>([^<]*)\"\;([^<]*)\<\/span\>\<\/li\>/u'; + $sCode = preg_replace($sPattern, '>$1"$2', $sCode); + + //Global / Main + $sPattern = '/>\*\ \[('.implode('|', $this->getWords('wCodePart')).')\]/u'; + $sCode = preg_replace($sPattern, '>> $1', $sCode); + + //Core Words + foreach($this->getWords('wCore') as $sCoreWord) + { + $sCoreWord = mb_strtolower($sCoreWord); + $sPattern = '/>(([^<]*)([^\w&<]{1})|.{0})('.$sCoreWord.')([\W])/u'; + $sCode = preg_replace($sPattern, '>$1$4$5', $sCode); + } + //$sCoreWords = str_replace(' ', '\ ', implode('|', array_map('mb_strtolower', $this->getWords('wCore')))); + //$sPattern = '/>(([^<]*)([^\w<]{1})|.{0})('.$sCoreWords.')(?=[\W])/U'; + //$sCode = preg_replace($sPattern, '>$1$4', $sCode); + + //Operators + //TODO distinguer operator / calculation + $asOperators = array_unique(array_merge($this->getWords('cOperator'), $this->getWords('cCalculation'))); + foreach($asOperators as $sOpWord) + { + $sPattern = '/>([^<]*)\\'.$sOpWord.'/u'; + $sCode = preg_replace($sPattern, '>$1'.$sOpWord.'', $sCode); + } + //$sPattern = '/>([^<]*)['.implode(array_map('mb_strtolower', ($this->getWords('cOperator')))).']/'; + //echo $sPattern; + //$sCode = preg_replace($sPattern, '>$1$2$3', $sCode); + + //Numbers + $sPreChar = '\\'.implode('\\', $this->getWords('cCalculation')).'\s'; + $sPattern = '/>((([^<]*)['.$sPreChar.']{1})|.{0})([0-9]+)([\W])/u'; + $sCode = preg_replace($sPattern, '>$1$4$5', $sCode); + + return $sCode; + } + + private static function getFirstWord($sText) + { + return mb_strstr(str_replace('.', ' ', trim($sText)), ' ', true); + } + + private static function getFirstChar($sText) + { + return mb_substr(str_replace('.', ' ', trim($sText)), 0, 1); + } + + private function addColor(&$sText, $sWord, $sClassColor) + { + $sText = str_replace($sWord, ''.$sWord.'', $sText); + } + + private static function getColoredWord($sWord, $sClass) + { + return ''.$sWord.''; + } + + public static function convText2Html($sCode) + { + return htmlspecialchars(mb_strtolower($sCode), ENT_QUOTES); + } + + public function getColoredCode() + { + return $this->addColoring(); + } +} + +?> \ No newline at end of file diff --git a/inc/rss.php b/inc/rss.php index b7e29c3..c3c9294 100644 --- a/inc/rss.php +++ b/inc/rss.php @@ -16,7 +16,7 @@ class Feed extends PhpObject { public function __construct($asDesc, $asItems=array()) { - parent::__construct(); + parent::__construct(__CLASS__, Settings::DEBUG); if(!array_key_exists('link', $asDesc)) { $asDesc['link'] = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'/rss'; diff --git a/inc/searchengine.php b/inc/searchengine.php new file mode 100644 index 0000000..651b732 --- /dev/null +++ b/inc/searchengine.php @@ -0,0 +1,254 @@ +getItemTables() + * - Add type in the index builder : SearchEngine->buildIndex() + * - Add type in item info : SearchEngine->setItemInfo() + * - Add type in Databap->getItemList() and create a Databap->getinfo() + * - Add type text in Databap->getProfile() + * - Consider the message types : add + edit in Databap->getMessages() and in chat.html + */ +class SearchEngine extends PhpObject +{ + //Objects + private $oMySql; + + //variables + private $asWords; + private $iLevelMax; + private $asItemRanks; + private $asItemInfos; + private $asUserInfos; + + //Constants + const RESULT_A_PAGE = 20; + const KEYWORDS_SEPARATOR = ' '; + + function __construct($oMySql) + { + parent::__construct(__CLASS__, Settings::DEBUG); + $this->oMySql = $oMySql; + $this->asWords = $this->setWords(''); + $this->asItemRanks = array(); + $this->asItemInfos = array(); + $this->asUserInfos = array(); + } + + public function buildIndex($iItemId, $sType) + { + //Build keywords + switch($sType) + { + case Databap::CODE_TYPE: + $asItemData = $this->oMySql->selectRow(Databap::CODE_TABLE, $iItemId); + $asWords = array($asItemData[MySqlManager::getText(Databap::CODE_TABLE)], $asItemData['description']); + break; + case Databap::PROC_TYPE: + $sItemIdCol = MySqlManager::getId(Databap::PROC_TABLE); + $asItemData = $this->oMySql->selectRow(Databap::PROC_TABLE, $iItemId); + $asItemStepsData = $this->oMySql->selectRows(array('select'=>'description', 'from'=>Databap::STEP_TABLE, 'constraint'=>array($sItemIdCol=>$iItemId))); + $asWords = array('desc'=>$asItemData['description'], 'title'=>$asItemData['title']) + $asItemStepsData; + break; + case Databap::ART_TYPE: + $asItemData = $this->oMySql->selectRow(Databap::ART_TABLE, $iItemId); + $asWords = array($asItemData['first_name'], $asItemData['last_name'], $asItemData['title']); + break; + case Databap::DOC_TYPE: + $sItemIdCol = MySqlManager::getId(Databap::DOC_TABLE); + $asItemData = $this->oMySql->selectRow(Databap::DOC_TABLE, $iItemId); + $asItemFilesData = $this->oMySql->selectRows(array('select'=>'description', 'from'=>Databap::FILE_TABLE, 'constraint'=>array($sItemIdCol=>$iItemId))); + $asWords = array('desc'=>$asItemData['description'], 'title'=>$asItemData['title']) + $asItemFilesData; + break; + case Databap::TABLE_TYPE: + $asItemData = $this->oMySql->selectRow(Databap::TABL_TABLE, $iItemId); + $asWords = array('desc'=>$asItemData['description'], 'title'=>$asItemData['title'], 'keywords'=>$asItemData['keywords'], 'system'=>$asItemData['system']); + break; + default: + $this->addError('function '.__FUNCTION__.'(): Incorrect type "'.$sType.'"'); + break; + } + $sWords = implode(self::KEYWORDS_SEPARATOR, $asWords); + $sWords = mb_strtolower(str_replace("\n", self::KEYWORDS_SEPARATOR, $sWords)); + $sWords = preg_replace('/(\W+)/u', self::KEYWORDS_SEPARATOR, $sWords); //remove all non-word characters + + //Add / Modify search database + $asData = array('id_item'=>$iItemId, 'type'=>$sType, 'refer_id'=>(array_key_exists('refer_id', $asItemData)?$asItemData['refer_id']:$iItemId), 'keywords'=>$sWords); + $this->oMySql->insertUpdateRow(Databap::SEARCH_TABLE, $asData, array('id_item', 'type')); + } + + public function setWords($sSearchWords) + { + $this->asWords = $this->getParsedWords($sSearchWords); + $this->iLevelMax = count($this->asWords); + $this->setResults(); + } + + /** + * TODO Customized item preview + $sCodeLines = implode("\n...\n", $this->getCodeInfo($iItemId, 'code')); + $oCode = new Reader($sCodeLines); + $sCodeLines = $oCode->getColoredCode(); + */ + private function setItemInfo($iSearchId, $iItemType, $iItemId) + { + if(!array_key_exists($iSearchId, $this->asItemInfos)) + { + switch($iItemType) + { + case Databap::CODE_TYPE: + $sItemTable = Databap::CODE_TABLE; + $asItemFields = array(MySqlManager::getId(Databap::USER_TABLE), 'description', 'led'); + break; + case Databap::PROC_TYPE: + $sItemTable = Databap::PROC_TABLE; + $asItemFields = array(MySqlManager::getId(Databap::USER_TABLE), 'title AS description', 'led'); + break; + case Databap::ART_TYPE: + $sItemTable = Databap::ART_TABLE; + $asItemFields = array('first_name', 'last_name', 'title AS description', 'led'); + break; + case Databap::DOC_TYPE: + $sItemTable = Databap::DOC_TABLE; + $asItemFields = array(MySqlManager::getId(Databap::USER_TABLE), 'title AS description', 'led'); + break; + case Databap::TABLE_TYPE: + $sItemTable = Databap::TABL_TABLE; + $asItemFields = array(MySqlManager::getId(Databap::USER_TABLE), 'title AS description', 'led'); + break; + } + $this->asItemInfos[$iSearchId] = $this->oMySql->selectRow($sItemTable, $iItemId, $asItemFields); + $this->asItemInfos[$iSearchId]['type'] = $iItemType; + $this->asItemInfos[$iSearchId]['id_item'] = $iItemId; + } + } + + private function getItemInfo($iSearchId, $sInfoName) + { + if(array_key_exists($iSearchId, $this->asItemInfos) && array_key_exists($sInfoName, $this->asItemInfos[$iSearchId])) + return $this->asItemInfos[$iSearchId][$sInfoName]; + else return false; + } + + private function setUserInfo($iUserId) + { + if($iUserId > 0 && !array_key_exists($iUserId, $this->asUserInfos)) + { + $sCompanyIdCol = MySqlManager::getId(Databap::COMP_TABLE); + $this->asUserInfos[$iUserId] = $this->oMySql->selectRow + ( + Databap::USER_TABLE, + $iUserId, + array('first_name', 'last_name', $sCompanyIdCol) + ); + + $this->asUserInfos[$iUserId]['company'] = $this->oMySql->selectValue(Databap::COMP_TABLE, MySqlManager::getText(Databap::COMP_TABLE), $this->asUserInfos[$iUserId][$sCompanyIdCol]); + unset($this->asUserInfos[$iUserId][$sCompanyIdCol]); + } + } + + private function getUserInfo($iUserId, $sInfoName) + { + if(array_key_exists($iUserId, $this->asUserInfos) && array_key_exists($sInfoName, $this->asUserInfos[$iUserId])) + return $this->asUserInfos[$iUserId][$sInfoName]; + else return false; + } + + private function setResults() + { + if($this->iLevelMax > 0) + { + //set Results and Ranking + $aiLevels = range(1, $this->iLevelMax); + arsort($aiLevels); + foreach($aiLevels as $iLevel) + { + //all possibilies at level $iLevel + $iIndex = 0; + while(($iIndex + $iLevel) <= $this->iLevelMax) + { + //building query + $asSequence = array_slice($this->asWords, $iIndex, $iLevel); + $this->oMySql->cleanSql($asSequence); + + //$sRegExp = implode('(.{0,2})', $asSequence); + $sRegExp = implode(self::KEYWORDS_SEPARATOR.'?', $asSequence); + $sSequence = implode(self::KEYWORDS_SEPARATOR, $asSequence); + + //TODO replace with selectRow() + $sQuery = "SELECT id_search, MAX(id_item) AS id_item, type, keywords FROM searchs WHERE keywords REGEXP '{$sRegExp}' GROUP BY type, refer_id"; + + //search sequence + $asItems = $this->oMySql->getArrayQuery($sQuery, true); + foreach($asItems as $asItem) + { + $iSearchId = $asItem['id_search']; + $iItemId = $asItem['id_item'];; + $iItemType = $asItem['type']; + $sWords = $asItem['keywords']; + + //Calculate bonus points + $sWords = str_replace(self::KEYWORDS_SEPARATOR.$sSequence.self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iSeqCount); + $sWords = str_replace(self::KEYWORDS_SEPARATOR.$sSequence, self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iStaCount); + $sWords = str_replace($sSequence.self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iEndCount); + $iBonus = $iSeqCount*5 + $iStaCount*2 + $iEndCount; + + $this->incItemRank($iSearchId, $iLevel*10+$iBonus); + $this->setItemInfo($iSearchId, $iItemType, $iItemId); + $this->setUserInfo($this->getItemInfo($iSearchId, MySqlManager::getId(Databap::USER_TABLE))); + } + $iIndex++; + } + } + } + } + + public function getResults() + { + $asResult = array(); + + //Mixing info + arsort($this->asItemRanks); + foreach($this->asItemRanks as $iSearchId=>$iRank) + { + $iUserId = $this->getItemInfo($iSearchId, MySqlManager::getId(Databap::USER_TABLE)); + $sFirstName = $this->getUserInfo($iUserId, 'first_name')?$this->getUserInfo($iUserId, 'first_name'):$this->getItemInfo($iSearchId, 'first_name'); + $sLastName = $this->getUserInfo($iUserId, 'last_name')?$this->getUserInfo($iUserId, 'last_name'):$this->getItemInfo($iSearchId, 'last_name'); + $sCompany = $this->getUserInfo($iUserId, 'company')?$this->getUserInfo($iUserId, 'company'):'SAP'; + $asResult[] = array('id_item'=>$this->getItemInfo($iSearchId, 'id_item'), + 'type'=>$this->getItemInfo($iSearchId, 'type'), + 'description'=>$this->getItemInfo($iSearchId, 'description'), + 'rank'=>$iRank, + 'name'=>Databap::getNameFormat($sFirstName, $sLastName), + 'company'=>Databap::getCompanyFormat($sCompany), + 'led'=>Databap::getDateFormat($this->getItemInfo($iSearchId, 'led'))); + } + return $asResult; + } + + private function incItemRank($iSearchId, $iRank) + { + if(array_key_exists($iSearchId, $this->asItemRanks)) + { + $this->asItemRanks[$iSearchId] += $iRank; + } + else + { + $this->asItemRanks[$iSearchId] = $iRank; + } + } + + private function getParsedWords($sSearchWords) + { + return array_unique(array_filter(explode(' ', $sSearchWords), array($this, 'checkSearchedWords'))); + } + + private function checkSearchedWords($sWord) + { + return (mb_strlen($sWord) >= 2); + } +} + +?> \ No newline at end of file diff --git a/inc/toolbox.php b/inc/toolbox.php new file mode 100644 index 0000000..c301033 --- /dev/null +++ b/inc/toolbox.php @@ -0,0 +1,312 @@ +0) + { + $asCleaningFunc = array_fill(1, count($oData), $sCleaningFunc); + $asKeys = array_map(array('self', 'cleanData'), array_keys($oData), $asCleaningFunc); + $asValues = array_map(array('self', 'cleanData'), $oData, $asCleaningFunc); + return array_combine($asKeys, $asValues); + } + } + + public static function fixGlobalVars($argv) + { + //Add CLI arguments + if(defined('STDIN')) mb_parse_str(implode('&', array_slice($argv, 1)), $_GET); + + //Add Server Name + $sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD']; + $sAppPath = 'http://'.str_replace('http://', '', $sServerName.dirname($_SERVER['SCRIPT_NAME'])); + $_GET['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':''); + } + + public static function array_map_encapsulate($oData, $sChar) + { + if(is_array($oData)) + { + $asChar = array_fill(1, count($oData), $sChar); + return array_combine(array_keys($oData), array_map(array('self', 'array_map_encapsulate'), $oData, $asChar)); + } + else + { + return $sChar.$oData.$sChar; + } + } + + public static function capitalizeWords($acText, $sCharList = '') + { + //Use ucwords if no delimiters are given + if($sCharList=='') + { + return Toolbox::mb_ucwords($acText); + } + + // Go through all characters + $capitalizeNext = true; + $max = mb_strlen($acText); + for ($i = 0; $i < $max; $i++) + { + if(mb_strpos($sCharList, $acText[$i]) !== false) + { + $capitalizeNext = true; + } + elseif($capitalizeNext) + { + $capitalizeNext = false; + $acText[$i] = mb_strtoupper($acText[$i]); + } + } + + return $acText; + } + + /** + * + * @param String $sFromName + * @param String $sSubject + * @param String $sMessage + * @param String $sTo + * @param Array $asCc array(name => email) + * @param Boolean $bSelfMail + * @return mixed + */ + public function sendMail($sFromName, $sSubject, $sMessage, $sTo, $asCc=array(), $bSelfMail=true) + { + $asForm = array('api_key'=>Settings::MAIL_API_KEY, + 'app'=>'Wedding', + 'from_name'=>$sFromName, + 'subject'=>$sSubject, + 'msg'=>$sMessage, + 'to_email'=>$sTo, + 'cc_email'=>self::jsonConvert($asCc), + 'self'=>$bSelfMail); + + $oCurl = curl_init(); + curl_setopt($oCurl, CURLOPT_URL, Settings::MAIL_SCRIPT); + curl_setopt($oCurl, CURLOPT_POST, true); + curl_setopt($oCurl, CURLOPT_HEADER, false); + curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($oCurl, CURLOPT_POSTFIELDS, $asForm); + $iResult = curl_exec($oCurl); + curl_close($oCurl); + + return $iResult; + } + + public static function jsonExport($asData) + { + header('Content-type: application/json'); + //return htmlspecialchars(json_encode($asData), ENT_NOQUOTES); + return self::jsonConvert($asData); + } + + public static function jsonConvert($asData) + { + return json_encode($asData); + } + + public static function createThumbnail($sInPath, $iMaxWidth, $iMaxHeight, $sOutPath='', $bDeleteIn=false, $asImageExts=array('jpg', 'jpeg', 'gif', 'png')) + { + $asResult = array('error'=>''); + + //Look up the extension to choose the image creator + //TODO use MIME types + $sInInfo = pathinfo($sInPath); + $sInName = mb_strtolower($sInInfo['basename']); + $sImageExt = mb_strtolower($sInInfo['extension']); + $sImageExt = ($sImageExt=='jpg')?'jpeg':$sImageExt; + + //New Destination folder + if($sOutPath=='') $sOutPath = $sInPath; + elseif(mb_substr($sOutPath, -1)=='/') $sOutPath .= $sInName; + + //New sizes + if(in_array($sImageExt, $asImageExts)) + { + list($iWidth, $iHeight) = getimagesize($sInPath); + if($iWidth > $iMaxWidth || $iHeight > $iMaxHeight) + { + $dResizeDeltaWidth = $iWidth - $iMaxWidth; + $dResizeDeltaHeight = $iHeight - $iMaxHeight; + if($dResizeDeltaWidth > $dResizeDeltaHeight) + { + $iResizedWidth = $iMaxWidth; + $iResizedHeight = ($iResizedWidth / $iWidth) * $iHeight; + } + else + { + $iResizedHeight = $iMaxHeight; + $iResizedWidth = ($iResizedHeight / $iHeight) * $iWidth; + } + + //create image from source + $oSource = call_user_func('imagecreatefrom'.$sImageExt, $sInPath); + + //Resize + $oThumb = imagecreatetruecolor($iResizedWidth, $iResizedHeight); + imagecopyresized($oThumb, $oSource, 0, 0, 0, 0, $iResizedWidth, $iResizedHeight, $iWidth, $iHeight); + + //Save + if(file_exists($sOutPath)) unlink($sOutPath); + if(!call_user_func_array('image'.$sImageExt, array($oThumb, $sOutPath))) + { + $asResult['error'] = 'Unable to create thumbnail : '.$sOutPath; + } + } + elseif($sInPath != $sOutPath) + { + $iResizedWidth = $iWidth; + $iResizedHeight = $iHeight; + if(!copy($sInPath, $sOutPath)) $asResult['error'] = 'Copy failed from '.$sInPath.' to '.$sOutPath; + } + $asResult['width'] = $iResizedWidth; + $asResult['height'] = $iResizedHeight; + $asResult['out'] = $sOutPath; + } + else $asResult['error'] = 'Wrong file type'; + + if($bDeleteIn && $asResult['error']=='' && $sInPath != $sOutPath) unlink($sInPath); + + return $asResult; + } + + public static function utf8_compliant($sText) + { + if(strlen($sText) == 0) return true; + return (preg_match('/^.{1}/us', $sText, $ar) == 1); + } + + public static function mb_ucwords($sText) + { + return mb_convert_case($sText, MB_CASE_TITLE, "UTF-8"); + } + + public static function mb_ucfirst($sText) + { + $sLength = mb_strlen($sText); + $sFirstChar = mb_substr($sText, 0, 1); + $sThen = mb_substr($sText, 1, $sLength - 1); + return mb_strtoupper($sFirstChar).$sThen; + } + + public static function file_get_contents_utf8($oFile) + { + $sContent = file_get_contents($oFile); + return mb_convert_encoding($sContent, 'UTF-8', mb_detect_encoding($sContent, 'UTF-8, ISO-8859-1', true)); + } + + public static function rgbToHex($R, $G, $B) + { + $R = dechex($R); + if(strlen($R)<2) $R='0'.$R; + + $G = dechex($G); + if(strlen($G)<2) $G='0'.$G; + + $B = dechex($B); + if(strlen($B)<2) $B='0'.$B; + + return $R.$G.$B; + } + + public static function setCookie($sCookieName, $sCookieValue, $iDays) + { + $iTimeLimit = time()+60*60*24*$iDays; + setcookie($sCookieName, $sCookieValue, $iTimeLimit); + } + + //TODO implement link pattern + public static function findReplaceLinks($sText, $sLinkPattern='') + { + $sServerPath = substr(str_replace(array('http://', 'https://'), '', $_GET['serv_name']), 0, -1); + + //Phase 1 : Identify internal links : merge cases /#code-1 and code 1, destroy link. + $asPatterns = array(); + foreach(Databap::$HASH_TO_PAGE as $sAlt=>$sFinal) + { + $asPatterns['`(^|\ )(https?://|)'.$sServerPath.'/?\#'.$sAlt.'\-([\d]+)`sui'] = '$1$4'.$sFinal.' $3'; + } + $sText = preg_replace(array_keys($asPatterns), array_values($asPatterns), $sText); + + //Phase 2 : Identify remaining links (external) + $asPatterns = array('`((?:https?|ftp)://\S+[[:alnum:]]/?)`sui', '`((?$1 ', '$1'); + $sText = preg_replace($asPatterns, $asLinks, $sText); + + //Phase 3: rebuild link + $asPatterns = array(); + foreach(Databap::$HASH_TO_PAGE as $sAlt=>$sFinal) + { + $asPatterns['`(^|\ )'.$sAlt.'\ ([\d]+)(\ |)`sui'] = '$1'.$sFinal.' $2$3'; + } + $sText = preg_replace(array_keys($asPatterns), array_values($asPatterns), $sText); + + //@file_put_contents('log.html', "\n\n".date('r')."\n".$test, FILE_APPEND); + + return $sText; + } + + public static function getDateTimeDesc($oTime) + { + $iTimeStamp = is_numeric($oTime)?$oTime:strtotime($oTime); + $sCurTimeStamp = time(); + + $asWeekDays = array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'); + $asMonths = array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'); + $sSep = '|'; + $sFormat = 'Y'.$sSep.'n'.$sSep.'W'.$sSep.'N'.$sSep.'j'.$sSep.'G'; + list($sYear, $sMonth, $sWeek, $sWeekDay, $sDay, $sHour) = explode($sSep, date($sFormat, $iTimeStamp)); + list($sCurYear, $sCurMonth, $sCurWeek, $sCurWeekDay, $sCurDay, $sCurHour) = explode($sSep, date($sFormat, $sCurTimeStamp)); + + $sDesc = ''; + if($iTimeStamp>$sCurTimeStamp) $sDesc = 'dans le futur'; + elseif($sCurTimeStamp-$iTimeStamp<60) $sDesc = 'il y a quelques secondes'; + elseif($sCurTimeStamp-$iTimeStamp<60*10) $sDesc = 'il y a quelques minutes'; + elseif($sCurTimeStamp-$iTimeStamp<60*20) $sDesc = 'il y a un quart d\'heure'; + elseif($sCurTimeStamp-$iTimeStamp<60*50) $sDesc = 'il y a une demi-heure'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*2) $sDesc = 'il y a une heure'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24 && $sDay==$sCurDay) $sDesc = 'à '.$sHour.'h'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24) $sDesc = 'hier'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*7 && $sWeek==$sCurWeek) $sDesc = $asWeekDays[$sWeekDay-1]; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*7) $sDesc = $asWeekDays[$sWeekDay-1].' dernier'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*9) $sDesc = 'il y a une semaine'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*12) $sDesc = 'il y a 10 jours'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*16) $sDesc = 'il y a 2 semaines'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*23) $sDesc = 'il y a 3 semaines'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*31 && $sMonth==$sCurMonth) $sDesc = 'le '.$sDay.' '.$asMonths[$sMonth-1]; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*30*2 && $sMonth==($sCurMonth-1)) $sDesc = 'le mois dernier'; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*365 && $sYear==$sCurYear) $sDesc = 'en '.$asMonths[$sMonth-1]; + elseif($sCurTimeStamp-$iTimeStamp<60*60*24*365) $sDesc = 'en '.$asMonths[$sMonth-1].' '.$sYear; + elseif($sYear==($sCurYear-1)) $sDesc = 'l\'année dernière'; + else $sDesc = 'en '.$sYear; + + //return self::mb_ucfirst($sDesc); + return $sDesc; + } +} + +?> \ No newline at end of file diff --git a/index.php b/index.php index 2ae4a9c..5d633dd 100644 --- a/index.php +++ b/index.php @@ -1,11 +1,28 @@ logMeIn($sAuthName, $sAuthCompany, $sToken, $sAction); +$bUserOk = $oDatabap->logMeIn($sToken, $sAction); //if connected if($bUserOk && $sAction!=Databap::EXT_ACCESS) @@ -60,40 +77,49 @@ if($bUserOk && $sAction!=Databap::EXT_ACCESS) $sResult = $oDatabap->addCode(array('description'=>$sDescription, 'content'=>$sContent, 'link'=>$sLink)); break; case 'edit_code': - $sResult = $oDatabap->editCode($oCode, $sContent); + $sResult = $oDatabap->editCode($oItemId, $sContent); break; case 'read_code': - if($oCode!==false) $sResult = $oDatabap->getColoredCode($oCode); + if($oItemId!==false) $sResult = $oDatabap->getColoredCode($oItemId); break; case 'nude_code': - if($oCode!==false) $sResult = $oDatabap->getNudeCode($oCode); + if($oItemId!==false) $sResult = $oDatabap->getNudeCode($oItemId); break; case 'raw_code': - if($oCode!==false) $sResult = $oDatabap->getRawCode($oCode); + if($oItemId!==false) $sResult = $oDatabap->getRawCode($oItemId); break; case 'dl_code': - if($oCode!==false) $sResult = $oDatabap->getSavedCode($oCode); + if($oItemId!==false) $sResult = $oDatabap->getSavedCode($oItemId); break; case 'dl_file': - $sResult = $oDatabap->getFile($iItemId); + $sResult = $oDatabap->getFile($oItemId); break; case 'print_code': - if($oCode!==false) $sResult = $oDatabap->getPrintCode($oCode); + if($oItemId!==false) $sResult = $oDatabap->getRawCode($oItemId, true); break; case 'add_procedure': $sResult = $oDatabap->addProcedure($_POST); break; case 'get_procedure': - $sResult = $oDatabap->getProcedure($iProcId); + $sResult = $oDatabap->getProcedure($oItemId); break; case 'add_doc': $sResult = $oDatabap->addDoc($_POST); break; case 'get_doc': - $sResult = $oDatabap->getDoc($iItemId); + $sResult = $oDatabap->getDoc($oItemId); + break; + case 'add_table': + $sResult = $oDatabap->addTable($sSystem, $sTitle, $sDescription, $sKeyWords, $oItemId); + break; + case 'get_table': + $sResult = $oDatabap->getTable($oItemId); + break; + case 'get_article': + $sResult = $oDatabap->getArticle($oItemId); break; case 'art_redirect': - $sResult = $oDatabap->redirectArticle($iItemId); + $sResult = $oDatabap->redirectArticle($oItemId); break; case 'upload_image': $sResult = $oDatabap->uploadImage(); @@ -102,16 +128,16 @@ if($bUserOk && $sAction!=Databap::EXT_ACCESS) $sResult = $oDatabap->uploadDoc(); break; case 'url': - $sResult = $oDatabap->checkValue(MySqlManager::URL_TABLE, array('phrase'=>$sLink)); + $sResult = $oDatabap->checkValue(Databap::URL_TABLE, array('phrase'=>$sLink)); break; case 'user_info': $sResult = $oDatabap->getUserInfo($oDatabap->getUserId(), true); break; case 'profile': - $sResult = $oDatabap->getProfile($oUser); + $sResult = $oDatabap->getProfile($oItemId); break; case 'search': - $sResult = $oDatabap->getResults($sSearchWords); + $sResult = $oDatabap->getResults($sKeyWords); break; case 'code_block': $sResult = $oDatabap->getCodeBlock(); @@ -131,6 +157,9 @@ if($bUserOk && $sAction!=Databap::EXT_ACCESS) case 'messages': $sResult = $oDatabap->getMessages($iMessageId); break; + case 'news': + $sResult = $oDatabap->getNews(); + break; case 'connected_users': $sResult = $oDatabap->getConnectedUsers(true); break; @@ -140,6 +169,9 @@ if($bUserOk && $sAction!=Databap::EXT_ACCESS) case 'set_options': $sResult = $oDatabap->setOptions($_POST, false); break; + case 'set_pass': + $sResult = $oDatabap->checkSetPass($sOldToken, $sNewToken); + break; case 'list': $sResult = $oDatabap->getItemList(); break; @@ -157,20 +189,20 @@ if($bUserOk && $sAction!=Databap::EXT_ACCESS) $asInfo = explode('-', strtolower($oUser)); $sResult = 'User added. Id='.$oDatabap->addUser($asInfo[0], $asInfo[1], $asInfo[2]); break; - case 'reset_token': - $sResult = print_r($oDatabap->resetToken(), true); - break; case 'build_index': $oDatabap->buildCompleteIndex(); $sResult = 'Index ok'; break; - case 'install_queries': - $oMySqlInstall = new MySqlManager(); + case 'install_queries': + $oMySqlInstall = new MySqlManager(Settings::DB_SERVER, Settings::DB_LOGIN, Settings::DB_PASS, Settings::DB_NAME, Databap::getSqlOptions(), Settings::DB_ENC); $sResult = $oMySqlInstall->getFullInstallQuery(); break; case 'reset_chan_safe_names': $sResult = $oDatabap->resetChanSafeNames(); break; + case 'init_pass': //TODO delete in v1.0.1 + $sResult = $oDatabap->resetAllPass(); + break; } } } @@ -201,6 +233,7 @@ elseif($bUserOk && $sAction==Databap::EXT_ACCESS) break; } } + elseif($sResult=='') $sResult = 'No Clearance'; } elseif($sAction!='') { @@ -210,15 +243,13 @@ elseif($sAction!='') else { //$oDatabap->setExpectedPage($_SERVER['REQUEST_URI']); - $oPage = new Mask('logon'); - $oPage->setTag('name', $sAuthName); - $oPage->setTag('company', $sAuthCompany); - $sResult = $oPage->getMask(); + $sResult = $oDatabap->getLogonPage($sToken==''); } //clean unwanted error log -ob_end_clean(); -//$sDebug = ob_get_clean(); +//TODO unify file name with PhpObject Class +$sDebug = ob_get_clean(); +if(Settings::DEBUG && $sDebug!='') file_put_contents('log.html', print_r($sDebug, true)."\n\n", FILE_APPEND); echo $sResult; //echo json_encode($sDebug); diff --git a/jquery/common.js b/jquery/common.js new file mode 100644 index 0000000..a0b6c89 --- /dev/null +++ b/jquery/common.js @@ -0,0 +1,791 @@ +/* jQuery functions */ + +(function($) { + $.fn.getCursorPosition = function() { + var input = this.get(0); + if (!input) return; // No (input) element found + if ('selectionStart' in input) { + // Standard-compliant browsers + return input.selectionStart; + } else if (document.selection) { + // IE + input.focus(); + var sel = document.selection.createRange(); + var selLen = document.selection.createRange().text.length; + sel.moveStart('character', -input.value.length); + return sel.text.length - selLen; + } + }; +})(jQuery); + +$.prototype.addButton = function(sType, sTitle, oClickLink, sId, sButtonClass, sTmpVar) +{ + $This = $(this); + var asAttributes = {id:(sId || ''), 'class':'button round'+(typeof sButtonClass != 'undefined'?' '+sButtonClass:'')}; + + //Link + var bLink = (typeof oClickLink == 'string'); + if(bLink) + { + asAttributes.href = oClickLink; + asAttributes.target = '_blank'; + } + var $Button = $('', asAttributes) + .append($('', {'class':'icon fa fa-c-'+sType})) + .append($('', {'class':'value'}).text(sTitle)) + .appendTo($This); + + //Function + if(!bLink) $Button.click(function(e){e.preventDefault();oClickLink();}); + + //TODO delete use of tmp out of databap class context + if(typeof sTmpVar != 'undefined') databap.tmp(sTmpVar, $Button); + + return $This; +}; + +$.prototype.modifyButton = function(aoInfo) +{ + $This = $(this); + $.each(aoInfo, function(sKey, sValue) + { + switch(sKey) + { + case 'type': $This.find('i.icon').removeClass(function(index, css){return (css.match(/\bfa-c-\S+/g) || []).join(' ');}).addClass('fa-c-'+sValue); break; + case 'title': $This.find('span.value').text(sValue); break; + case 'link': $This.attr('href', sValue); break; + case 'action': $This.off('click').click(function(e){e.preventDefault(); sValue();}); break; + case 'id': $This.attr('id', sValue); break; + case 'class': $This.attr('class', 'button round '+sValue); break; + case 'var': databap.tmp(sValue, $This); break; //TODO delete use of tmp out of databap class context + } + }); + return $This; +}; + +$.prototype.getEmptyHeight = function() +{ + var $This = $(this), iTotalHeight = 0; + $This.children(':visible').not('.scrollbar').each(function() + { + //debug('#'+$(this).attr('id')+'|.'+$(this).attr('class')+' gives '+$(this).outerHeight(true)); + iTotalHeight += $(this).outerHeight(true); + }); + return $This.height() - iTotalHeight; +}; + +$.prototype.getEmptyWidth = function() +{ + var $This = $(this), iTotalWidth = 0; + $This.children(':visible').not('.scrollbar').each(function() + { + iTotalWidth += $(this).outerWidth(true); + }); + return $This.width() - iTotalWidth; +}; + +$.prototype.addDefaultValue = function(sDefaultValue, sInitValue) +{ + sInitValue = sInitValue || ''; + return $(this) + .data('default_value', sDefaultValue) + .val(sInitValue==''?sDefaultValue:sInitValue) + .addClass(sInitValue==''?'default_text':'') + .focus(function() + { + var $This = $(this); + if($This.val() == $This.data('default_value')) $This.val(''); + $This.removeClass('default_text'); + }) + .blur(function() + { + var $This = $(this); + if($This.val() == '') $This.val($This.data('default_value')).addClass('default_text'); + }); +}; + +$.prototype.checkForm = function(sSelector) +{ + sSelector = sSelector || 'input[type="password"], input[type="text"], textarea'; + var $This = $(this); + var bOk = true; + $This.find(sSelector).each(function() + { + $This = $(this); + bOk = bOk && $This.val()!='' && $This.val()!=$This.data('default_value'); + }); + return bOk; +}; + +/* Common functions */ + +String.prototype.stripVowelAccent = function() +{ + var sText = this; + var rExps= + [ + {re:/[\xC0-\xC6]/g, ch:'A'}, + {re:/[\xE0-\xE6]/g, ch:'a'}, + {re:/[\xC8-\xCB]/g, ch:'E'}, + {re:/[\xE8-\xEB]/g, ch:'e'}, + {re:/[\xCC-\xCF]/g, ch:'I'}, + {re:/[\xEC-\xEF]/g, ch:'i'}, + {re:/[\xD2-\xD6]/g, ch:'O'}, + {re:/[\xF2-\xF6]/g, ch:'o'}, + {re:/[\xD9-\xDC]/g, ch:'U'}, + {re:/[\xF9-\xFC]/g, ch:'u'}, + {re:/[\xD1]/g, ch:'N'}, + {re:/[\xF1]/g, ch:'n'} + ]; + for(var i=0, len=rExps.length; i 0; +} + +function ucwords(text) +{ + return (t = text + '').replace + ( + /^([a-z])|\s+([a-z])/g, + function($1) + { + return $1.toUpperCase(); + } + ); +} + +function getTimeString() +{ + var currentTime = new Date(); + var hours = currentTime.getHours(); + var minutes = currentTime.getMinutes(); + var secondes = currentTime.getSeconds(); + + hours = sprintf(hours, 2, '0'); + minutes = sprintf(minutes, 2, '0'); + secondes = sprintf(secondes, 2, '0'); + + return hours+':'+minutes+':'+secondes; +} + +function sprintf(value, length, char) +{ + var buffer = ''; + missingCharsNb = Math.max(0, length - value.length); + for(var i=0;i<=missingCharsNb;i++) + { + buffer += char; + } + + return buffer+value; +} + +function chr(codePt) +{ + if (codePt > 0xFFFF) + { + return String.fromCharCode(0xD800 + (codePt >> 10), 0xDC00 + (codePt & 0x3FF)); + } + return String.fromCharCode(codePt); +} + +function explode(delimiter, string, limit) +{ + var emptyArray = {0:''}; + + if(arguments.length < 2 || typeof arguments[0] == 'undefined' || typeof arguments[1] == 'undefined') return null; + + if(delimiter === '' || delimiter === false || delimiter === null) return false; + + if(typeof delimiter == 'function' || typeof delimiter == 'object' || typeof string == 'function' || typeof string == 'object') return emptyArray; + + if(delimiter === true) delimiter = '1'; + + if(!limit) return string.toString().split(delimiter.toString()); + else + { + // support for limit argument + var splitted = string.toString().split(delimiter.toString()); + var partA = splitted.splice(0, limit - 1); + var partB = splitted.join(delimiter.toString()); + partA.push(partB); + return partA; + } +} + +function implode(glue, pieces) +{ + var i = '', retVal = '', tGlue = ''; + + if(arguments.length === 1) + { + pieces = glue; + glue = ''; + } + + if(typeof(pieces) === 'object') + { + if(pieces instanceof Array) return pieces.join(glue); + else + { + for (i in pieces) + { + retVal += tGlue + pieces[i]; + tGlue = glue; + } + return retVal; + } + } + else return pieces; +} + +function file_exists(url) +{ + var req = this.window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); + if(!req) + { + throw new Error('XMLHttpRequest not supported'); + } + + // HEAD Results are usually shorter (faster) than GET + req.open('HEAD', url, false); + req.send(null); if (req.status == 200) + { + return true; + } + + return false; +} + +function basename(path, suffix) +{ + var b = path.replace(/^.*[\/\\]/g, ''); + if (typeof(suffix) == 'string' && b.substr(b.length - suffix.length) == suffix) + { + b = b.substr(0, b.length - suffix.length); + } + return b; +} + +function getElem(aoAnchor, asPath) +{ + return (typeof asPath == 'object' && asPath.length > 1)?getElem(aoAnchor[asPath.shift()], asPath):aoAnchor[(typeof asPath == 'object')?asPath.shift():asPath]; +} + +function setElem(aoAnchor, asPath, oValue) +{ + var asTypes = {boolean:false, string:'', integer:0, int:0, array:[], object:{}}; + if(typeof asPath == 'object' && asPath.length > 1) + { + var nextlevel = asPath.shift(); + if(!(nextlevel in aoAnchor)) aoAnchor[nextlevel] = {}; //Creating a new level + if(typeof aoAnchor[nextlevel] !== 'object') debug('Error - setElem() : Already existing path at level "'+nextlevel+'". Cancelling setElem() action'); + return setElem(aoAnchor[nextlevel], asPath, oValue); + } + else + { + var sKey = (typeof asPath == 'object')?asPath.shift():asPath; + return aoAnchor[sKey] = (!(sKey in aoAnchor) && (oValue in asTypes))?asTypes[oValue]:oValue; + } +} + +/* +//Queue element +( + function($) + { + $.fn.delaying = function(delay, reloadAction, minLength) + { + var options={delay:delay, reload_action:reloadAction, min_length:minLength}; + this.each + ( + function() + { + $(this).data('tsb',new Delayer($(this), options)); + } + ); + return this; + }; + + function Delayer(elem, options) + { + var self = this; + var delays, min_length, previous_values, + temp_values, reload_action, processing_requests; + + this.init = function() + { + delays = options.delay; + min_length = options.min_length; + previous_values = ''; + temp_values = ''; + reload_action = options.reload_action; + processing_requests = false; + }; + + this.request = function(val) + { + var requestOk = false; + + //Prevent from loading request with too few elements or twice the same request + if(val.length > queue.min_length[id] && val != queue.previous_values[id]) + { + //Prevent from spamming server with requests and wait for value stabilization + if(queue.processing_requests[id] == false && val == queue.temp_values[id]) + { + queue.previous_values[id] = val; + requestOk = true; + } + else + { + queue.temp_values[id] = val; + setTimeout(queue.reload_action[id], queue.delays[id]); + } + } + return requestOk; + }; + } + } +) +(jQuery); +*/ + +function debug(text, bQuery) +{ + if(window.console) console.log(text); +} + +function getLoginToken(sPass) +{ + return hex_md5(sPass+window.location.origin+window.location.pathname); +} + +/* md5 */ + +/* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); } +function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); } +function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); } +function hex_hmac_md5(k, d) + { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } +function b64_hmac_md5(k, d) + { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } +function any_hmac_md5(k, d, e) + { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); } + +/* + * Perform a simple self-test to see if the VM is working + */ +function md5_vm_test() +{ + return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72"; +} + +/* + * Calculate the MD5 of a raw string + */ +function rstr_md5(s) +{ + return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); +} + +/* + * Calculate the HMAC-MD5, of a key and some data (raw strings) + */ +function rstr_hmac_md5(key, data) +{ + var bkey = rstr2binl(key); + if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); + return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); +} + +/* + * Convert a raw string to a hex string + */ +function rstr2hex(input) +{ + try { hexcase } catch(e) { hexcase=0; } + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var output = ""; + var x; + for(var i = 0; i < input.length; i++) + { + x = input.charCodeAt(i); + output += hex_tab.charAt((x >>> 4) & 0x0F) + + hex_tab.charAt( x & 0x0F); + } + return output; +} + +/* + * Convert a raw string to a base-64 string + */ +function rstr2b64(input) +{ + try { b64pad } catch(e) { b64pad=''; } + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var output = ""; + var len = input.length; + for(var i = 0; i < len; i += 3) + { + var triplet = (input.charCodeAt(i) << 16) + | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) + | (i + 2 < len ? input.charCodeAt(i+2) : 0); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > input.length * 8) output += b64pad; + else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); + } + } + return output; +} + +/* + * Convert a raw string to an arbitrary string encoding + */ +function rstr2any(input, encoding) +{ + var divisor = encoding.length; + var i, j, q, x, quotient; + + /* Convert to an array of 16-bit big-endian values, forming the dividend */ + var dividend = Array(Math.ceil(input.length / 2)); + for(i = 0; i < dividend.length; i++) + { + dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); + } + + /* + * Repeatedly perform a long division. The binary array forms the dividend, + * the length of the encoding is the divisor. Once computed, the quotient + * forms the dividend for the next step. All remainders are stored for later + * use. + */ + var full_length = Math.ceil(input.length * 8 / + (Math.log(encoding.length) / Math.log(2))); + var remainders = Array(full_length); + for(j = 0; j < full_length; j++) + { + quotient = Array(); + x = 0; + for(i = 0; i < dividend.length; i++) + { + x = (x << 16) + dividend[i]; + q = Math.floor(x / divisor); + x -= q * divisor; + if(quotient.length > 0 || q > 0) + quotient[quotient.length] = q; + } + remainders[j] = x; + dividend = quotient; + } + + /* Convert the remainders to the output string */ + var output = ""; + for(i = remainders.length - 1; i >= 0; i--) + output += encoding.charAt(remainders[i]); + + return output; +} + +/* + * Encode a string as utf-8. + * For efficiency, this assumes the input is valid utf-16. + */ +function str2rstr_utf8(input) +{ + var output = ""; + var i = -1; + var x, y; + + while(++i < input.length) + { + /* Decode utf-16 surrogate pairs */ + x = input.charCodeAt(i); + y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; + if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) + { + x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); + i++; + } + + /* Encode output as utf-8 */ + if(x <= 0x7F) + output += String.fromCharCode(x); + else if(x <= 0x7FF) + output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), + 0x80 | ( x & 0x3F)); + else if(x <= 0xFFFF) + output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), + 0x80 | ((x >>> 6 ) & 0x3F), + 0x80 | ( x & 0x3F)); + else if(x <= 0x1FFFFF) + output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), + 0x80 | ((x >>> 12) & 0x3F), + 0x80 | ((x >>> 6 ) & 0x3F), + 0x80 | ( x & 0x3F)); + } + return output; +} + +/* + * Encode a string as utf-16 + */ +function str2rstr_utf16le(input) +{ + var output = ""; + for(var i = 0; i < input.length; i++) + output += String.fromCharCode( input.charCodeAt(i) & 0xFF, + (input.charCodeAt(i) >>> 8) & 0xFF); + return output; +} + +function str2rstr_utf16be(input) +{ + var output = ""; + for(var i = 0; i < input.length; i++) + output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, + input.charCodeAt(i) & 0xFF); + return output; +} + +/* + * Convert a raw string to an array of little-endian words + * Characters >255 have their high-byte silently ignored. + */ +function rstr2binl(input) +{ + var output = Array(input.length >> 2); + for(var i = 0; i < output.length; i++) + output[i] = 0; + for(var i = 0; i < input.length * 8; i += 8) + output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); + return output; +} + +/* + * Convert an array of little-endian words to a string + */ +function binl2rstr(input) +{ + var output = ""; + for(var i = 0; i < input.length * 32; i += 8) + output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF); + return output; +} + +/* + * Calculate the MD5 of an array of little-endian words, and a bit length. + */ +function binl_md5(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + + a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); + d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); + d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); + d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i+10], 17, -42063); + b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); + d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); + d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); + c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); + d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); + c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); + d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); + c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); + d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); + c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); + d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); + d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); + d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); + d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); + d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); + d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); + d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); + d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + } + return Array(a, b, c, d); +} + +/* + * These functions implement the four basic operations the algorithm uses. + */ +function md5_cmn(q, a, b, x, s, t) +{ + return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); +} +function md5_ff(a, b, c, d, x, s, t) +{ + return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); +} +function md5_gg(a, b, c, d, x, s, t) +{ + return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); +} +function md5_hh(a, b, c, d, x, s, t) +{ + return md5_cmn(b ^ c ^ d, a, b, x, s, t); +} +function md5_ii(a, b, c, d, x, s, t) +{ + return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function bit_rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} \ No newline at end of file diff --git a/jquery/databap.js b/jquery/databap.js new file mode 100644 index 0000000..b5dd266 --- /dev/null +++ b/jquery/databap.js @@ -0,0 +1,894 @@ +/* Handling the communication between php processing server and local ajax engine */ + +/* Databap class */ + +function Databap() +{ + self = this; + + //on load init + this.init = function() + { + //Global constants + self.consts.default_hash = self.vars.page_to_hash[self.consts.default_page]; + self.consts.process_url = 'index.php'; + self.consts.app_image_folder = 'images/'; + self.consts.add_code_text = 'Copier le code ici.'; + self.consts.search_box_text = 'Recherche...'; + self.consts.maxHeight = $(window).height(); + self.consts.maxWidth = $(window).width(); + self.consts.time = (new Date()).getTime(); + self.consts.transTime = 200; + + //Global variables + self.vars2('tmp', 'object'); + self.vars2('loading', 'boolean'); + self.vars2('fade', false); + self.vars2('page_cache', 'object'); + self.vars2('pageIcon', 'string'); + self.vars2('current_page', 'string'); + self.vars2('current_user', 'object'); + self.vars2('code', 'integer'); + self.vars2('id', 0); + self.vars2('phrase', 'string'); + self.vars2('code_container', 'string'); + self.vars2('focus', true); + self.vars2('disconnected', 'boolean'); + self.vars2('mode', 'string'); + self.vars2('search_words', 'string'); + self.vars2('initLoaded', 'boolean'); + self.vars2('chans_list', 'object'); + + //shortcuts + self.$main = $('#main'); + self.$menu = $('#menu'); + self.$title = $('#page_title'); + self.consts.pageMaxHeight = self.$main.height(); + self.consts.pageMaxWidth = self.$main.width(); + + //Global Events Listeners + self.initSearch(); + + //Left menu + self.initMenu(); + + //Events handler + self.resetEventHandlers(); + $(document).keyup(function(e){self.onKeyUp(e);}); + $(document).keydown(function(e){self.onKeyDown(e);}); + $(document).mouseenter(function(){self.onMouveEnter();}); + $(document).mouseleave(function(){self.onMouveLeave();}); + $(window).unload(function(){self.onQuit();}); + //window.onbeforeunload = self.onQuit; + $(window).resize(self.resizeMain).resize(); + $(window).bind('hashchange', self.onHashChange); + + //Init first page + if(self.hash()=='') self.goToInternalLink(self.consts.default_hash); + else self.onHashChange(); + + //Load cache + self.loadCache(); + }; + + this.parseHash = function() + { + var sHash = self.hash(); + var asHash = sHash.split('-'); + var sPage = self.vars.hash_to_page[asHash[0]]; + + var iItemId = (typeof asHash[1] != 'undefined')?asHash[1]:0; + //var iItemId = (typeof asHash[1] != 'undefined' && !isNaN(asHash[1]))?asHash[1]:0; + //var sItemName = (typeof asHash[1] != 'undefined' && isNaN(asHash[1]))?asHash[1]:''; + return {hash:sHash, page:sPage, id:iItemId/*, name:sItemName*/}; + }; + + this.onHashChange = function(aoOptions) + { + aoOptions = aoOptions || {noFade:false, force:false}; + var bNoFade = aoOptions.noFade || false; + var bForce = aoOptions.force || false; + + var sDefaultPage = self.vars.current_page==''?'welcome':'error'; + var asUrlVars = self.parseHash(); + self.switchPage((asUrlVars.hash!='' && typeof asUrlVars.page !== 'undefined')?asUrlVars.page:sDefaultPage, asUrlVars.id, bNoFade, bForce); + }; + + this.hash = function(hash) + { + if(!hash) return window.location.hash.slice(1); + else + { + window.location.hash = (hash.substr(0, 1)=='#'?'':'#')+hash; + + //favicon, bug firefox + var link = $('link[rel="shortcut icon"]').remove().attr("href"); + $('').appendTo('head'); + } + }; + + //TODO Add images, ... + this.loadCache = function() + { + var iDelay = 1; + for(var i in self.vars.page_to_hash) + { + setTimeout('self.addPageToCache("'+i+'", function(){});', iDelay*60*1000); + iDelay += 1; + } + }; + + this.vars2 = function(oVarName, oValue) + { + var asVarName = (typeof oVarName == 'object')?oVarName:[oVarName]; + + //Set, name & type / default value (init) + if(typeof oValue !== 'undefined') setElem(self.vars, copyArray(asVarName), oValue); + + //Get, only name parameter + return getElem(self.vars, asVarName); + }; + + this.tmp = function(sVarName, oValue) + { + var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName]; + asVarName.unshift('tmp'); + return self.vars2(asVarName, oValue); + }; + + this.resetEventHandlers = function() + { + //Reset specific pages event catchers + self.onResize = function(){}; + self.resetSize = function(){}; + self.onKeyUp = function(e){}; + self.onKeyDown = function(e){}; + self.onMouveEnter = function(){}; + self.onMouveLeave = function(){}; + self.onQuit = function(){return true;}; + }; + + this.initMenu = function() + { + //Hover Event: open/close + self.$menu.hover + ( + self.openMenu, + function() + { + closeMenu = true; + if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer); + oMenuTimer = setTimeout(self.closeMenu, 1000); + } + ); + + //Close on click + self.$menu.find('a').click(self.instantCloseMenu); + }; + + this.openMenu = function() + { + //Disable closing + closeMenu = false; + if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer); + + //If not already opened / about to open + if(typeof openedMenu == 'undefined' || openedMenu != true) + { + openedMenu = true; + self.$menu.find('#menu_title').hide(); + + //Main shadow + self.$main.add(self.$title) + .click(self.instantCloseMenu) + .fadeTo('fast', 0.2); + + //Switch useless button + self.$menu.find('.useless_button').removeClass('round').addClass('tiny_top_right'); + + //Move menu items + var iVPos = Math.max(0, $(window).scrollTop() - 46 - 46); + self.$menu.find('#menu_items').css('margin-top', iVPos); + + //Expand menu + self.$menu + .stop() + .animate + ( + {width:'300px'/*, borderWidth:'10px'*/}, + 'fast', + function() + { + self.$menu.find('#menu_box').show('fast'); + } + ); + } + }; + + this.instantCloseMenu = function() + { + closeMenu=true; + if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer); + self.closeMenu(); + }; + + this.closeMenu = function() + { + if(closeMenu == true) + { + if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer); + + //Switch uselecc button + self.$menu.find('.useless_button').removeClass('tiny_top_right').addClass('round'); + + //Main shadow + self.$main.add(self.$title) + .off('click') + .fadeTo('fast', 1); + + //Reduce menu + self.$menu.find('#menu_box').hide(); + self.$menu + .stop() + .animate + ( + {width:'20px'/*, borderWidth:'0'*/}, + 'fast', + function() + { + self.$menu.find('#menu_title').show('fast'); + } + ); + openedMenu = false; + closeMenu = false; + } + }; + + this.setInitEnd = function(bEnded, bAddScrollBar) + { + self.vars.initLoaded = bEnded; + if(self.vars.initLoaded===true) + { + //self.resizeMain(); + self.onResize(); + //setTimeout(self.onResize, 500); + } + }; + + this.getMainElem = function(elem) + { + return self.$main.find(elem); + }; + + //switch to a new page with animation, without processing image + this.switchPage = function(sNextPage, iItemId, bNoFade, bforce) + { + iItemId = iItemId || 0; + bforce = bforce || false; + + //FIXME event object instead of string after cancelling page exit. bug ? channeling from parseHash + if(bforce || typeof sNextPage == 'string' && (sNextPage != self.vars.current_page || iItemId!=self.vars.id)) + { + //Officially quiting page + var bExit = true; + if(self.vars.current_page!='') bExit = self.onQuit(); + if(bExit==false) bExit = confirm('Voulez-vous vraiment quitter la page en cours ?'); + + if(bExit) + { + //Init's Start + self.setInitEnd(false); + + //Reset specific pages event catchers + self.resetEventHandlers(); + + //Switching data + self.vars.current_page = sNextPage; + self.vars.id = iItemId; + /*if(typeof self.vars.page_cache[sNextPage] == 'undefined') + { + self.vars.loading = true; + $.get + ( + self.getPagePath(sNextPage), + function(data) + { + self.vars.loading = false; + self.vars.page_cache[sNextPage] = data; + self.transition(self.switchContent); + } + ); + } + else self.transition(self.switchContent); + */ + self.addPageToCache(sNextPage, function(){self.transition(self.switchContent);}); + } + //fix the hash + else self.goToInternalLink(self.vars.current_page, self.vars.id); + } + }; + + this.addPageToCache = function(sPage, fOnSuccess) + { + if(typeof self.vars.page_cache[sPage] == 'undefined') + { + self.vars.loading = true; + $.get + ( + self.getPagePath(sPage), + function(data) + { + self.vars.loading = false; + self.vars.page_cache[sPage] = data; + fOnSuccess(); + } + ); + } + else fOnSuccess(); + }; + + this.transition = function(fOnFinish) + { + if(self.vars2('fade')) fOnFinish(); + else $.when(self.$main.add(self.$title).stop().fadeTo(self.consts.transTime, 0)).done(fOnFinish); + self.vars2('fade', false); + }; + + this.switchContent = function() + { + //Add page Header + self.$title + .find('.h1_wrap').attr('id', self.vars.current_page+'_title') + .find('#title_text').text(self.vars.page_titles[self.vars.current_page]); + self.$title.fadeTo(self.consts.transTime, 1); + self.vars.pageIcon = 'fa-c-'+self.vars.current_page; + self.pageIcon(self.vars.pageIcon); + + //Page replacement + self.$main.empty().fadeTo(self.consts.transTime, 1).append(self.vars.page_cache[self.vars.current_page]); + + //Browser tab title + self.setTitle(); + + //Reset specific page variables + self.vars2('tmp', {}); + + //Page specific Actions + self.pageInit(); + }; + + this.pageIcon = function(pageIcon, comment) + { + var $Header = $('#'+self.vars.current_page+'_title'); + $Header.find('i') + .attr('class', 'fa fa-fw fa-20') + .addClass(pageIcon); + $Header.find('h1').attr('title', comment || ''); + }; + + this.addBufferIcon = function() + { + self.pageIcon('fa-spin fa-c-loading', 'Processing...'); + }; + + this.addSuccessIcon = function() + { + self.pageIcon('fa-c-ok', 'Opération réussie'); + setTimeout(self.resetIcon, 2000); + }; + + this.addFailIcon = function(sMsg) + { + if(!sMsg) sMsg = 'Echec de connexion. Essaie de refresh la page'; + self.pageIcon('fa-c-ko', sMsg); + setTimeout(self.resetIcon, 5000); + }; + + this.resetIcon = function() + { + self.pageIcon(self.vars.pageIcon, ''); + }; + + this.resizeMain = function() + { + //Maximize main div height to fill up the page + var iPageHeight = $('body').outerHeight(true); + var iWindowHeight = $(window).outerHeight(true); + self.$main.height('+='+(iWindowHeight - iPageHeight)); + self.$menu.height($('#main_container').height()); + + //Update size + self.consts.maxHeight = $(window).height(); + self.consts.maxWidth = $(window).width(); + self.consts.pageMaxHeight = self.$main.height(); + self.consts.pageMaxWidth = self.$main.width(); + + //Page event + self.onResize(); + }; + + this.maximizeElem = function($Elem, bfitToElemContent, $Box) + { + $Box = $Box || self.$main; + bfitToElemContent = bfitToElemContent || false; + + //Maximize to box height + var iDelta = $Box.getEmptyHeight(); + //debug('Giving empty '+iDelta+'px (in #'+$Box.attr('id')+') to #'+$Elem.attr('id')+' (originally : '+$Elem.height()+')'); + + if(iDelta != 0) $Elem.height('+='+iDelta); + //debug('Now, #'+$Elem.attr('id')+' height is '+$Elem.height()+'px'); + + //Minimize to elem height + if(bfitToElemContent) + { + iDelta = $Elem.getEmptyHeight(); + if(iDelta > 0) $Elem.height('-='+iDelta); + //debug('Removing useless '+iDelta+'px from #'+$Elem.attr('id')); + } + }; + + this.saveForm = function(action, $form, fOnSuccess, bProcessIcon, dataType) + { + var sFormVars = $form.serialize(); + if(typeof bProcessIcon != 'undefined') self.addBufferIcon(); + if(!dataType) dataType = 'text'; + + self.vars.loading = true; + $.ajax + ( + { + type: 'POST', + url: self.getActionLink(action), + data: sFormVars, + success:function(result) + { + self.vars.loading = false; + self.vars.disconnected = false; + if(typeof bProcessIcon != 'undefined') self.resetIcon(); + fOnSuccess(result); + }, + error: function(jqXHR, textStatus, errorThrown) + { + self.vars.loading = false; + self.vars.disconnected = true; + debug('Error databap.js 331'); + self.showError(textStatus); + }, + dataType: dataType + } + ); + }; + + this.getInfo = function(action, fOnSuccess, vars, type, fOnError, bProcessIcon) + { + if(!vars) vars = {}; + if(!type) type = 'html'; + if(typeof bProcessIcon != 'undefined') self.addBufferIcon(); + + vars['a'] = action; + self.vars.loading = true; + $.ajax + ( + { + url: self.consts.process_url, + data: vars, + success: function(result) + { + if(result==self.consts.disconnected) databap.refresh(); + else if((result==self.consts.error || typeof result.success != 'undefined' && result.success==self.consts.error) && typeof fOnError !== 'undefined') fOnError(); + else + { + self.vars.loading = false; + self.vars.disconnected = false; + if(typeof bProcessIcon != 'undefined') self.resetIcon(); + fOnSuccess(result); + } + }, + error: function(jqXHR, textStatus, errorThrown) + { + if(jqXHR.responseText==self.consts.disconnected) databap.refresh(); + + self.vars.loading = false; + self.vars.disconnected = true; + debug('Error databap.js 361'); + if(typeof bProcessIcon != 'undefined') self.resetIcon(); + if(!fOnError) + { + self.showError(textStatus); + debug(textStatus+' '+errorThrown); + } + else fOnError(textStatus); + }, + dataType: type + } + ); + }; + + this.getSyncInfo = function(action, vars) + { + if(!vars) vars = {}; + vars['a'] = action; + return $.ajax + ( + { + url: self.consts.process_url, + type: "GET", + data: vars, + dataType: "html", + async: false, + success: function(r){/*debug('getSyncInfo: Ok');*/} + } + ).responseText; + }; + + this.showError = function(textStatus) + { + //debug('ERROR : '+textStatus); + self.addFailIcon(); + }; + + this.getUserInfo = function(fOnSuccess) + { + if(typeof self.vars.current_user.name == 'undefined') + { + self.getInfo + ( + 'user_info', + function(user) + { + self.vars.current_user = user; + fOnSuccess(user); + }, + {}, + 'json' + ); + } + else fOnSuccess(self.vars.current_user); + }; + + this.initSearch = function() + { + $('#form_header').submit(function(event){event.preventDefault();}); + + var bDirectSearch = (self.vars.current_page=='' && self.parseHash().page=='search' && self.parseHash().id!=''); + $('#query') + .addDefaultValue(self.consts.search_box_text, bDirectSearch?self.parseHash().id:'') + .keyup(function(e){if(e.which==13 || e.isTrigger) self.goToInternalLink('search', $('#query').val());}); + + $('#search_btn_submit').click(function(){$('#query').trigger('keyup');}); + }; + + this.search = function() + { + //search words + var searchWords = $.trim($('#query').val()); + + //check already searched words + if((self.tmp('searched_words')=='' || self.tmp('searched_words')!=searchWords) && searchWords.length > 2) + { + //filter requests to not flood the server + if(/*window.tempWords !== undefined && tempWords == searchWords && */!self.vars.loading) + { + //saving searched words + self.tmp('searched_words', searchWords); + self.getInfo + ( + 'search', + function(asItems) + { + $Container = self.$main.find('#list_container'); + $Container.empty(); + $('#search_words').text(searchWords); + $('#search_count').text(asItems.length); + $('#search_multi').text(asItems.length>1?'s':''); + if(asItems.length==0) $Container.append('Aucun résultat trouvé.'); + else + { + for(iRankId in asItems) self.appendItem(asItems[iRankId], $Container, false); + } + self.addSuccessIcon(); + }, + {keywords:searchWords}, + 'json', + function(){}, + true + ); + } + else + { + //tempWords = searchWords; + setTimeout(self.search, 300); + } + } + }; + + this.appendItem = function(asItemInfo, $Container, $bAnimation) + { + //Filling up the item line + var $verHtml = $(self.consts.versionHtml); + $verHtml.find('#description').html(asItemInfo.description); + $verHtml.find('#author_name').html(asItemInfo.name); + $verHtml.find('#author_company').html(asItemInfo.company); + $verHtml.find('#led').html(asItemInfo.led); + + //Rank (search) + if(typeof asItemInfo.rank != 'undefined') + { + $verHtml.find('#description') + .append( + $('', {style:'float:right;'}) + .append($('', {'class':'fa fa-inline fa-c-kpi'})) + .append(asItemInfo.rank) + ); + } + + //Icon + $verHtml.find('.fa').addClass('fa-c-'+asItemInfo.type); + + //Link + var sItemLink = self.getInternalLink(asItemInfo.type, asItemInfo.id_item); + $verHtml.find('#item_link').attr('href', sItemLink).attr('title', 'Lien vers '+sItemLink); + $verHtml = self.setElemTags($verHtml, asItemInfo.id_item, false, asItemInfo.type); + + //Display + $bAnimation = (typeof $bAnimation != 'undefined')?$bAnimation:true; + var $Container = self.getMainElem('#list_container'); + if($bAnimation) $verHtml.hide().appendTo($Container).slideDown('fast'); + else $verHtml.appendTo($Container); + }; + + this.getActionLink = function(sAction, oVars) + { + if(!oVars) oVars = {}; + sVars = ''; + for(i in oVars) + { + sVars += '&'+i+'='+oVars[i]; + } + return self.consts.process_url+'?a='+sAction+sVars; + }; + + this.getInternalLink = function(sPage, iItemId, bAbsolute) + { + bAbsolute = bAbsolute || false; + return (bAbsolute?self.vars.serv_name:'')+'#'+sPage+((iItemId>0 || iItemId!='')?'-'+escape($.trim(iItemId)):''); + }; + + this.goToInternalLink = function(sPage, iItemId, bFade) + { + self.vars2('fade', bFade || false); + + iItemId = iItemId || 0; + var sHash = self.getInternalLink(sPage, iItemId).substr(1); + if(sHash==self.hash()) self.onHashChange({force:true}); + else self.hash(sHash); + }; + + this.refresh = function() + { + self.tmp("refresh", true); + //self.goToInternalLink(databap.vars.current_page, databap.vars.id); + location.reload(true); + }; + + this.goToExternalLink = function(sUrl) + { + window.location = sUrl; + }; + + this.appendCode = function(codeBox, info) + { + self.vars.id = info.id_code; + self.vars.phrase = (!info.phrase)?info.id_code:info.phrase; + //#code_reader + $codeBox = self.setElemTags($(codeBox), [self.vars.id]); + + //About code + self.getMainElem('#'+self.getElemTag('code_lines', self.vars.id)).html(info.code); + self.getMainElem('#'+self.getElemTag('description', self.vars.id)).html(info.description); + + //About author + self.getMainElem('#'+self.getElemTag('author_name', self.vars.id)).text(info.name); + self.getMainElem('#'+self.getElemTag('author_company', self.vars.id)).text(info.company); + self.getMainElem('#'+self.getElemTag('led', self.vars.id)).text(info.led); + self.getMainElem('#'+self.getElemTag('item_link', self.vars.id)) + .prop('href', self.getInternalLink('code', info.id_code)) + .find('.fa').addClass('fa-c-code'); + }; + + this.setElemTags = function($CodeBox, aiIds, bUpdate, sType) + { + self.setElemTag($CodeBox, aiIds, bUpdate, sType); + $CodeBox.find('[id]').each(function(){self.setElemTag($(this), aiIds, bUpdate, sType);}); + return $CodeBox; + }; + + this.setElemTag = function($Tag, aiIds, bUpdate, sType) + { + var sCurTagId = $Tag.attr('id'); + + //Replace only ids existing in aiIds, keep the others + if(bUpdate) + { + var aiCurTagIds = self.getElemIds(sCurTagId); + for(i in aiIds) + { + aiCurTagIds[i] = aiIds[i]; + } + aiIds = aiCurTagIds; + } + + var sNewTagId = self.getElemTag(self.stripElemIds(sCurTagId), aiIds, sType); + + $Tag.attr('id', sNewTagId); + if($Tag.attr('name') != undefined) $Tag.attr('name', sNewTagId); + }; + + this.getElemTag = function(sTag, aiIds, sType) + { + if(!sType) sType = 'c'; + if(typeof aiIds != 'object') aiIds = [aiIds]; + return sType+implode(self.consts.id_sep, aiIds)+self.consts.id_sep+sTag; + }; + + this.getFirstElemId = function(sTag) + { + var aiIds = self.getElemIds(sTag); + return aiIds[0]; + }; + + this.getElemIds = function(sTag) + { + return array_filter(explode(self.consts.id_sep, sTag.substr(1)), isNumeric); + }; + + this.stripElemIds = function(sTag) + { + //var sPattern = 'c\\d+'+self.consts.id_sep; + var sType = sTag.substr(0, 1); + var sPattern = self.getElemTag('', '\\d+', sType); + if(self.checkRegExMatch(sPattern, sTag)) + { + sTag = sTag.substr(self.getElemTag('', self.getElemIds(sTag), sType).length); + } + return sTag; + }; + + this.checkRegExMatch = function(sPattern, sText) + { + var oRegEx = new RegExp(sPattern, 'i'); + return sText.match(oRegEx); + }; + + this.setCodeContainer = function(fOnSuccess, containerId) + { + if(typeof self.vars.code_container === 'undefined' || self.vars.code_container == '') + { + self.getInfo + ( + 'code_block', + function(code_block) + { + self.vars.code_container = code_block; + if(containerId) self.appendContainer(containerId); + fOnSuccess(); + } + ); + } + else + { + if(containerId) self.appendContainer(containerId); + fOnSuccess(); + } + }; + + this.appendContainer = function(containerId, codeId) + { + if(containerId != '') + { + if(!codeId) + { + codeBoxId = 'code_reader'; + container = self.vars.code_container; + } + else + { + codeBoxId = self.getElemTag('code_reader', codeId); + container = self.vars.code_container.replace('id="code_reader"', 'id="'+codeBoxId+'"'); + } + self.getMainElem(containerId).append(container); + return codeBoxId; + } + else + { + return false; + } + }; + + this.getImagePath = function(imageName) + { + return 'url("'+self.consts.app_image_folder+imageName+'")'; + }; + + this.getPagePath = function(sPageName) + { + return self.consts.mask_folder+sPageName+'.html?'+self.consts.time; + }; + + this.addErrorBefore = function(msg, elem) + { + msg = msg+'.'; + self.addMsgBefore(msg, 'error', elem); + }; + + this.addWarningBefore = function(msg, elem) + { + msg = 'Attention : '+msg+'.'; + self.addMsgBefore(msg, 'warning', elem); + }; + + this.addSuccessBefore = function(msg, elem) + { + msg = msg+'.'; + self.addMsgBefore(msg, 'success', elem); + }; + + this.addMsgBefore = function(msg, msgClass, elem) + { + var $msg = $('

    ', {'class':msgClass}).append($('', {'class':'fa fa-inline'})).append(msg); + $msg.hide().insertBefore(elem).slideDown('fast', function(){databap.updateScrollBar('bottom');}).delay(5000).slideUp('fast', function(){$(this).remove();databap.updateScrollBar();}); + }; + + this.setTitle = function(extra) + { + if(!extra) + { + extra = ''; + } + else + { + extra = extra+' | '; + } + var page = self.vars.page_titles[self.vars.current_page]; + document.title = extra+'Databap '+chr('8226')+' '+page; + }; + + this.initScrollBar = function(sBox, sSubBox, sContent) + { + self.tmp('scrollbar', {}); //TODO in page switch + $(sSubBox) + .before('

    ') + .addClass('scrollbar_box round'); + $(sContent).addClass('scrollbar_subbox'); + self.tmp('scrollbar', $(sBox).tinyscrollbar({viewport:sSubBox, overview:sContent})); + }; + + this.setScrollBarSize = function(iBoxSize) + { + var $Box = self.tmp('scrollbar').find('.scrollbar_box'); + if(iBoxSize=='optimize') self.maximizeElem($Box, true); + else if(iBoxSize=='maximize') self.maximizeElem($Box, false); + else $Box.height(iBoxSize); + self.updateScrollBar(); + }; + + this.updateScrollBar = function(sPos) + { + sPos = sPos || 'relative'; + self.tmp('scrollbar').tinyscrollbar_update(sPos); + }; + + this.shareEvent = function(sType, iId) + { + if(iId!='') + { + $('#share') + .hide() + .text(self.getInternalLink(sType, iId, true)) + .addClass('addr') + .slideDown('fast') + .unbind('click'); + } + }; +} \ No newline at end of file diff --git a/jquery/fileuploader.js b/jquery/fileuploader.js index 14bb7e2..6d2828a 100755 --- a/jquery/fileuploader.js +++ b/jquery/fileuploader.js @@ -498,19 +498,20 @@ qq.FileUploader = function(o){ ((typeof o.addSlideText !== 'undefined')?o.addSlideText:'Glisser les images ici') + '
    ' + '' + - ''+ - ((typeof o.addFileText !== 'undefined')?o.addFileText:'Ajouter une image') + - '' + + '
    '+ + ((typeof o.addFileText !== 'undefined')?o.addFileText:'Ajouter une image') + + '' + '
    ' + '
      ' + '', // template for one item in file list fileTemplate:'
    • ' + - '' + + '' + + '' + '' + '' + - 'Annuler' + + '' + 'Echoué' + '
    • ', @@ -853,7 +854,8 @@ qq.UploadButton.prototype = { // in Opera only 'browse' button // is clickable and it is located at // the right side of the input - left: '-297px', + //left: '-297px', + left: '-2px', top: '-2px', width:'auto', fontFamily: 'Arial', @@ -1307,4 +1309,63 @@ qq.extend(qq.UploadHandlerXhr.prototype, { this._xhrs[id] = null; } } -}); \ No newline at end of file +}); + +function addImage(stepId, imageId, imageName, imageDesc) +{ + //get image number + var $ImageBox = databap.getMainElem('#'+databap.getElemTag('uploader_item', [stepId, imageId])); + + //Create new image + var $newImage = $([ '', + '', + '', + '', + ''].join('\n')); + + //Update doc icon + var sExt = imageName.slice(imageName.lastIndexOf('.') + 1); + $ImageBox.find('.fa').eq(0).addClass('fa-c-file-'+sExt); + + //Set image name + $newImage.find('#image_name').val(imageName); + + //Set image description + $newImage.find('#image_desc').val(imageDesc); + + //delete button for this image + $newImage.find('#delete_image').click + ( + function() + { + var stepIds = databap.getElemIds($(this).attr('id')); + removeImage(stepIds[0], stepIds[1]); + } + ); + + //Add ids + $newImage = databap.setElemTags($newImage, [stepId, imageId]); + + //Add Image to the step + $newImage.appendTo($ImageBox); + + //Block exit + databap.tmp('started', true); + + databap.updateScrollBar(); + + return imageId; +} + +function removeImage(iStepId, iImageId) +{ + var sImageTag = databap.getElemTag('uploader_item', [iStepId, iImageId]); + databap.getMainElem('#'+sImageTag).hide + ( + 'fast', + function() + { + $(this).remove(); + } + ); +} \ No newline at end of file diff --git a/jquery/handler.js b/jquery/handler.js deleted file mode 100755 index a20222d..0000000 --- a/jquery/handler.js +++ /dev/null @@ -1,1215 +0,0 @@ -/* Handling the communication between php processing server and local ajax engine */ - -/* Databap class */ - -var databap = -{ - /* Constants defined with PHP - * - pages - * - vars.serv_name - * - consts.opt_type_text - * - consts.opt_type_select - * - consts.max_size - * - consts.authorized_exts - * - consts.mask_folder - */ - - //on load init - init: function (defaultPage) - { - //Global constants - this.consts.default_page = this.pages.chat; - this.consts.process_url = 'index.php'; - this.consts.app_image_folder = 'images/'; - this.consts.add_code_text = 'Copier le code ici.'; - this.consts.search_box_text = 'Recherche...'; - this.consts.versionHtml = ''; - this.consts.maxHeight = $(window).height(); - this.consts.maxWidth = $(window).width(); - this.consts.time = (new Date()).getTime(); - - //Global variables - this.vars.$main = $('#main'); - this.vars.$menu = $('#menu'); - this.initVar('tmp', 'object'); - this.initVar('loading', 'boolean'); - this.initVar('pageIcon', 'string'); - this.initVar('current_page', 'string'); - this.initVar('current_user', 'object'); - this.initVar('profile_user', 'string'); - this.initVar('code', 'integer'); - this.initVar('proc_id', 'integer'); - this.initVar('doc_id', 'integer'); - this.initVar('phrase', 'string'); - this.initVar('code_container', 'string'); - this.initVar('focus', 'boolean', true); - this.initVar('disconnected', 'boolean'); - this.initVar('mode', 'string', ''); - this.initVar('search_words', 'string'); - this.initVar('initLoaded', 'boolean'); - this.initVar('chans_list', 'object'); - - //shortcuts - this.$main = this.vars.$main; - this.$menu = this.vars.$menu; - this.consts.pageMaxHeight = databap.$main.height(); - this.consts.pageMaxWidth = databap.$main.width(); - - //Global Events Listeners - this.initSearch(); - $('#form_header').submit(function(event){event.preventDefault();}); - - //Left menu - this.initMenu(); - - //Events handler - this.initEventHandlers(); - $(document).keyup(function(e){databap.onKeyUp(e);}); - $(document).keydown(function(e){databap.onKeyDown(e);}); - $(document).mouseenter(function(){databap.onMouveEnter();}); - $(document).mouseleave(function(){databap.onMouveLeave();}); - $(window).unload(function(){databap.onQuit();}); - //window.onbeforeunload = databap.onQuit; - $(window).resize(databap.resizeMain); - $(window).resize(); - - //load default page - defaultPage = databap.pages[(defaultPage in databap.pages)?defaultPage:'error']; - databap.switchPage(defaultPage, {}, true); - }, - - initVar: function(varName, type, defaultValue, bForceValue) - { - if(typeof databap.vars[varName] === 'undefined' || bForceValue) - { - switch(type) - { - case 'boolean' :databap.vars[varName] = false;break; - case 'string' :databap.vars[varName] = '';break; - case 'integer' :databap.vars[varName] = 0;break; - case 'array' :databap.vars[varName] = [];break; - case 'object' :databap.vars[varName] = {};break; - default:databap.vars[varName] = ''; - } - if(defaultValue) - { - databap.vars[varName] = defaultValue; - } - } - }, - - tmp: function(sVarName, sType, oValue) - { - //Get - if(typeof sType === 'undefined') - { - return (typeof databap.vars.tmp[sVarName] !== 'undefined')?databap.vars.tmp[sVarName]:'error'; - } - //Init - else if(sType && typeof databap.vars.tmp[sVarName] === 'undefined') - { - switch(sType) - { - case 'boolean' :databap.vars.tmp[sVarName] = false;break; - case 'string' :databap.vars.tmp[sVarName] = '';break; - case 'integer' :databap.vars.tmp[sVarName] = 0;break; - case 'array' :databap.vars.tmp[varName] = [];break; - case 'object' :databap.vars.tmp[sVarName] = {};break; - default:databap.vars.tmp[sVarName] = ''; - } - if(oValue) - { - databap.vars.tmp[sVarName] = oValue; - } - return databap.vars.tmp[sVarName]; - } - //Set - else if(typeof sType !== 'undefined' && typeof databap.vars.tmp[sVarName] !== 'undefined') - { - databap.vars.tmp[sVarName] = sType; - return databap.vars.tmp[sVarName]; - } - return null; - }, - - initEventHandlers: function() - { - //Reset specific pages event catchers - databap.onResize = function(){}; - databap.resetSize = function(){}; - databap.onKeyUp = function(e){}; - databap.onKeyDown = function(e){}; - databap.onMouveEnter = function(){}; - databap.onMouveLeave = function(){}; - databap.onQuit = function(){}; - }, - - initSearch: function() - { - $SearchInputBox = $('#form_header :input#query'); - $SearchInputBox.val(databap.consts.search_box_text); - $SearchInputBox.css('color', databap.vars.search_words==''?'#D9E5F2':'#04357B'); - $SearchInputBox.focus - ( - function() - { - if($(this).val()==databap.consts.search_box_text) $(this).val('').css('color', '#04357B'); - } - ); - $SearchInputBox.blur - ( - function() - { - if($(this).val()=='') $(this).val(databap.consts.search_box_text).css('color', '#D9E5F2');; - } - ); - $SearchInputBox.keyup(function(e){databap.loadSearchPage();}); - $('#form_header :input#search_btn_submit').click(function(){databap.loadSearchPage();}); - }, - - initMenu: function() - { - databap.$menu.hover - ( - databap.openMenu, - function() - { - //if(typeof closeMenu == 'undefined' || closeMenu != true) - //{ - closeMenu = true; - if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer); - oMenuTimer = setTimeout(databap.closeMenu, 1000); - //} - } - ); - databap.$menu.find('#add_ln').click(function(){databap.loadAddPage();}); - databap.$menu.find('#procedure_ln').click(function(){databap.loadProcedurePage();}); - databap.$menu.find('#list_ln').click(function(){databap.loadListPage();}); - databap.$menu.find('#profile_ln').click(function(){databap.loadProfilePage();}); - databap.$menu.find('#chat_ln').click(function(){databap.loadChatPage();}); - databap.$menu.find('#options_ln').click(function(){databap.loadOptionsPage();}); - databap.$menu.find('#doc_ln').click(function(){databap.loadDocPage();}); - databap.$menu.find('#log_me_out_ln').click(function(){databap.logMeOut();}); - - //Close menu on click - databap.$menu.find('li').click(function(){closeMenu=true;if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer);databap.closeMenu();}); - //databap.$menu.click(function(){closeMenu=true;if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer);databap.closeMenu();}); - }, - - openMenu: function() - { - //Disable closing - closeMenu = false; - if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer); - - //If not already opened / about to open - if(typeof openedMenu == 'undefined' || openedMenu != true) - { - openedMenu = true; - databap.$menu.find('#menu_title').hide(); - - //Main shadow - databap.$main.fadeTo('fast', 0.5); - - //Switch useless button - databap.$menu.find('.useless_button').removeClass('round').addClass('tiny_top_right'); - - //Move menu items - var iVPos = Math.max(0, $(window).scrollTop() - 46 - 46); - databap.$menu.find('#menu_items').css('margin-top', iVPos); - - //Expand menu - databap.$menu - .stop() - .animate - ( - {width:'300px'/*, borderWidth:'10px'*/}, - 'fast', - function() - { - databap.$menu.find('#menu_box').show('fast'); - } - ); - } - }, - - closeMenu: function() - { - if(closeMenu == true) - { - if(typeof oMenuTimer != "undefined") clearTimeout(oMenuTimer); - - //Switch uselecc button - databap.$menu.find('.useless_button').removeClass('tiny_top_right').addClass('round'); - - //Main shadow - databap.$main.fadeTo('fast', 1); - - //Reduce menu - databap.$menu - .stop() - .animate - ( - {width:'20px'/*, borderWidth:'0'*/}, - 'fast', - function() - { - databap.$menu.find('#menu_box').hide - ( - 'fast', - function() - { - databap.$menu.find('#menu_title').show('fast'); - } - ); - } - ); - openedMenu = false; - closeMenu = false; - } - }, - - setInitEnd: function(bEnded, bAddScrollBar) - { - //debug('initLoaded='+bEnded); - databap.vars.initLoaded = bEnded; - if(databap.vars.initLoaded===true) - { - //this.resizeMain(); - debug('resizing from setInitEnd'); - databap.onResize(); - //setTimeout(databap.onResize, 500); - } - }, - - addDefaultValue: function($elem, sDefaultValue) - { - $elem.val(sDefaultValue).css('color', '#D9E5F2'); - $elem.focus - ( - function() - { - if($(this).val()==sDefaultValue) $(this).val('').css('color', '#04357B'); - } - ); - $elem.blur - ( - function() - { - if($(this).val()=="") $(this).val(sDefaultValue).css('color', '#D9E5F2');; - } - ); - }, - - getMainElem:function(elem) - { - return databap.$main.find(elem); - }, - - logMeOut:function() - { - databap.getInfo('log_me_out', function(){document.location='';}); - }, - - //switch to a new page with animation, without processing image - switchPage: function(sNextPage, vars, bNoFade) - { - switch_page = true; - - //Init's Start - databap.setInitEnd(false); - - if(vars===undefined) - { - vars = {}; - } - databap.vars.loading = true; - $.get - ( - databap.getPagePath(sNextPage), - vars, - function(data) - { - databap.vars.loading = false; - if(databap.vars.current_page!='') databap.onQuit(); - databap.vars.current_page = sNextPage; - databap.setTitle(); - databap.switchContent(data, bNoFade); - } - ); - }, - - switchContent: function(sContent, bNoFade) - { - if(bNoFade === undefined) - { - //fadeout every elements - index=1; - var $elems = databap.$main.children(); - var count = $elems.size(); - - $elems.fadeOut - ( - 'fast', - function() - { - $(this).remove(); - if(index==count) - { - $(sContent).appendTo(databap.$main); - databap.loadInit(); - } - else index++; - } - ); - } - else if (bNoFade == true) - { - databap.$main.html(sContent); - databap.loadInit(); - } - switch_page = false; - }, - - loadInit: function() - { - //save page icon - this.vars.pageIcon = this.pageIcon(); - - //Reset specific pages event catchers - this.initEventHandlers(); - - //Reset specific page variables - this.initVar('tmp', 'object', {}, true); - - //Page specific Actions - this.pageInit(); - }, - - pageIcon: function(pageIcon, comment) - { - var $title = this.getMainElem('h1'); - if ($title.size() > 0) - { - if(!pageIcon) - { - var url = $title.css('backgroundImage'); - var image = url.slice(url.lastIndexOf('/')); - image = image.substr(1, image.lastIndexOf(')') - 1); - if(image.substr(image.length-1, 1)=='"') image=image.substr(0, image.length - 1); //bug chrome - return image; - } - else - { - $title.css('backgroundImage', databap.getImagePath(pageIcon)); - if(typeof comment !== undefined) - { - $title.attr('title', comment); - } - } - } - }, - - addBufferIcon: function() - { - this.pageIcon('processing_48.gif', 'Processing...'); - }, - - addSuccessIcon: function() - { - this.pageIcon('yes_48.png', 'Opération réussie'); - setTimeout('databap.resetIcon()', 2000); - }, - - addFailIcon: function(sMsg) - { - if(!sMsg) sMsg = 'Echec de connexion. Essaie de refresh la page'; - this.pageIcon('no_48.png', sMsg); - setTimeout('databap.resetIcon()', 5000); - }, - - resetIcon: function() - { - this.pageIcon(this.vars.pageIcon, ''); - }, - - resizeMain: function() - { - //Back to the minimal size - //databap.$main.height('auto'); - //databap.resetSize(); - //debug('Resize Main triggered'); - - //Maximize main div height to fill up the page - var iPageHeight = $('body').outerHeight(true); - var iWindowHeight = $(window).outerHeight(true); - var iDelta = iWindowHeight - iPageHeight; - if(iDelta != 0) - { - databap.$main.height('+='+iDelta); - databap.vars.$menu.height(databap.$main.height()); - } - - //Update size - databap.consts.maxHeight = $(window).height(); - databap.consts.maxWidth = $(window).width(); - databap.consts.pageMaxHeight = databap.$main.height(); - databap.consts.pageMaxWidth = databap.$main.width(); - - //Page event - debug('resizing from resizeMain'); - databap.onResize(); - }, - - saveForm: function(action, $form, fOnSuccess, bProcessIcon, dataType) - { - var sFormVars = $form.serialize(); - if(typeof bProcessIcon != 'undefined') databap.addBufferIcon(); - if(!dataType) dataType = 'text'; - - databap.vars.loading = true; - $.ajax - ( - { - type: 'POST', - url: this.getActionLink(action), - data: sFormVars, - success:function(result) - { - databap.vars.loading = false; - databap.vars.disconnected = false; - if(typeof bProcessIcon != 'undefined') databap.resetIcon(); - fOnSuccess(result); - }, - error: function(jqXHR, textStatus, errorThrown) - { - databap.vars.loading = false; - databap.vars.disconnected = true; - debug('Error handler.js 331'); - databap.showError(textStatus); - }, - dataType: dataType - } - ); - }, - - getInfo: function(action, fOnSuccess, vars, type, fOnError, bProcessIcon) - { - if(!vars) vars = {}; - if(!type) type = 'html'; - if(typeof bProcessIcon != 'undefined') databap.addBufferIcon(); - - vars['a'] = action; - databap.vars.loading = true; - $.ajax - ( - { - url: databap.consts.process_url, - data: vars, - success:function(result) - { - if(result==databap.consts.disconnected) databap.goTo(databap.vars.current_page); - else if((result==databap.consts.error || typeof result.success != 'undefined' && result.success==databap.consts.error) && typeof fOnError !== 'undefined') fOnError(); - else - { - databap.vars.loading = false; - databap.vars.disconnected = false; - if(typeof bProcessIcon != 'undefined') databap.resetIcon(); - fOnSuccess(result); - } - }, - error: function(jqXHR, textStatus, errorThrown) - { - if(jqXHR.responseText==databap.consts.disconnected) databap.goTo(databap.vars.current_page); - - databap.vars.loading = false; - databap.vars.disconnected = true; - debug('Error handler.js 361'); - if(typeof bProcessIcon != 'undefined') databap.resetIcon(); - if(!fOnError) - { - databap.showError(textStatus); - debug(textStatus+' '+errorThrown); - } - else fOnError(textStatus); - }, - dataType: type - } - ); - }, - - getSyncInfo: function(action, vars) - { - if(!vars) vars = {}; - vars['a'] = action; - return $.ajax - ( - { - url: databap.consts.process_url, - type: "GET", - data: vars, - dataType: "html", - async:false, - success: function(r){debug('getSyncInfo: Ok');} - } - ).responseText; - }, - - showError: function(textStatus) - { - //debug('ERROR : '+textStatus); - databap.addFailIcon(); - }, - - getUserInfo: function(fOnSuccess) - { - if(typeof databap.vars.current_user.name == 'undefined') - { - databap.getInfo - ( - 'user_info', - function(user) - { - databap.vars.current_user = user; - fOnSuccess(user); - }, - {}, - 'json' - ); - } - else fOnSuccess(this.vars.current_user); - }, - - /* Loading Pages */ - - loadProfilePage: function() - { - databap.vars.profile_user = ''; - databap.switchPage(databap.pages.profile); - }, - - loadChatPage: function() - { - databap.switchPage(databap.pages.chat); - }, - - loadOptionsPage: function() - { - databap.switchPage(databap.pages.options); - }, - - loadProcedurePage: function(iProcId) - { - //Reset display - databap.vars.mode = ''; - this.vars.proc_id = 0; - - //load new page - if(iProcId && iProcId>0) - { - this.vars.proc_id = iProcId; - } - databap.switchPage(databap.pages.procedure); - }, - - loadDocPage: function() - { - databap.vars.doc_id = 0; - databap.switchPage(databap.pages.doc); - }, - - loadAddPage: function () - { - databap.switchPage(databap.pages.add_code); - }, - - loadListPage: function() - { - databap.switchPage(databap.pages.list); - }, - - loadReadPage: function(idCode) - { - databap.vars.code = idCode; - databap.switchPage(databap.pages.read_code); - }, - - loadSearchPage: function () - { - if(databap.vars.current_page != databap.pages.search) - { - searchedWords = ''; - databap.switchPage(databap.pages.search); - } - else - { - databap.search(); - } - }, - - search:function() - { - //search words - var searchWords = $.trim($('#query').val()); - - //check already searched words - if((window.searchedWords === undefined || searchedWords!=searchWords) && searchWords.length > 2) - { - //filter requests to not flood the server - if(window.tempWords !== undefined && tempWords == searchWords && !databap.vars.loading) - { - //debug('launching search for : '+searchWords); - - //saving searched words - searchedWords = searchWords; - - databap.addBufferIcon(); - databap.getInfo - ( - 'search', - function(asItems) - { - $Container = databap.$main.find('#list_container'); - $Container.empty(); - if(asItems.length==0) - { - $Container.append('Aucun résultat trouvé.'); - } - else - { - for(iRankId in asItems) databap.appendItem(asItems[iRankId], $Container); - } - databap.addSuccessIcon(); - }, - {keywords:searchWords}, - 'json' - ); - } - else - { - //debug('delayed for checking : '+searchWords); - tempWords = searchWords; - setTimeout('databap.loadSearchPage()', 500); - } - } - //else debug('same words'); - }, - - appendItem: function(asItemInfo, $Container) - { - //Filling up the item line - $verHtml = $(databap.consts.versionHtml); - $verHtml.find('#description').html(asItemInfo.description); - $verHtml.find('#author_name').html(asItemInfo.name); - $verHtml.find('#author_company').html(asItemInfo.company); - $verHtml.find('#led').html(asItemInfo.led); - - //Link - var sItemLink = databap.getExternalLink(asItemInfo.type, asItemInfo.id_item); - $verHtml.find('#item_link').attr('href', sItemLink).attr('title', 'Lien vers '+sItemLink); - $verHtml = databap.setElemTags($verHtml, asItemInfo.id_item, false, asItemInfo.type); - - //Display - $verHtml.hide().appendTo(databap.getMainElem('#list_container')).slideDown('fast'); - }, - - getCodeLink: function(code) - { - return databap.getExternalLink('code', code); - }, - - getProcLink: function(nb) - { - return databap.getExternalLink('proc', nb); - }, - - getExternalLink: function(page, id) - { - return databap.vars.serv_name+page+'-'+escape($.trim(id)); - }, - - appendCode: function(codeBox, info) - { - databap.vars.code = info.id_code; - databap.vars.phrase = (!info.phrase)?info.id_code:info.phrase; - //#code_reader - $codeBox = databap.setElemTags($(codeBox), [databap.vars.code]); - - //About code - databap.getMainElem('#'+databap.getElemTag('code_lines', databap.vars.code)).html(info.code); - databap.getMainElem('#'+databap.getElemTag('description', databap.vars.code)).html(info.description); - - //About author - databap.getMainElem('#'+databap.getElemTag('author_name', databap.vars.code)).text(info.name); - databap.getMainElem('#'+databap.getElemTag('author_company', databap.vars.code)).text(info.company); - databap.getMainElem('#'+databap.getElemTag('led', databap.vars.code)).text(info.led); - }, - - setElemTags: function($CodeBox, aiIds, bUpdate, sType) - { - databap.setElemTag($CodeBox, aiIds, bUpdate, sType); - $CodeBox.find('[id]').each(function(){databap.setElemTag($(this), aiIds, bUpdate, sType);}); - return $CodeBox; - }, - - setElemTag: function($Tag, aiIds, bUpdate, sType) - { - var sCurTagId = $Tag.attr('id'); - - //Replace only ids existing in aiIds, keep the others - if(bUpdate) - { - var aiCurTagIds = this.getElemIds(sCurTagId); - for(i in aiIds) - { - aiCurTagIds[i] = aiIds[i]; - } - aiIds = aiCurTagIds; - } - - var sNewTagId = databap.getElemTag(databap.stripElemIds(sCurTagId), aiIds, sType); - - $Tag.attr('id', sNewTagId); - if($Tag.attr('name') != undefined) $Tag.attr('name', sNewTagId); - }, - - getElemTag: function(sTag, aiIds, sType) - { - if(!sType) sType = 'c'; - if(typeof aiIds != 'object') aiIds = [aiIds]; - return sType+implode(databap.consts.id_sep, aiIds)+databap.consts.id_sep+sTag; - }, - - getFirstElemId: function(sTag) - { - var aiIds = this.getElemIds(sTag); - return aiIds[0]; - }, - - getElemIds: function(sTag) - { - return array_filter(explode(databap.consts.id_sep, sTag.substr(1)), isNumeric); - }, - - stripElemIds: function(sTag) - { - //var sPattern = 'c\\d+'+databap.consts.id_sep; - var sType = sTag.substr(0, 1); - var sPattern = this.getElemTag('', '\\d+', sType); - if(this.checkRegExMatch(sPattern, sTag)) - { - sTag = sTag.substr(this.getElemTag('', this.getElemIds(sTag), sType).length); - } - return sTag; - }, - - checkRegExMatch: function(sPattern, sText) - { - var oRegEx = new RegExp(sPattern, 'i'); - return sText.match(oRegEx); - }, - - setCodeContainer: function(fOnSuccess, containerId) - { - if(typeof databap.vars.code_container === 'undefined' || databap.vars.code_container == '') - { - databap.getInfo - ( - 'code_block', - function(code_block) - { - databap.vars.code_container = code_block; - if(containerId) databap.appendContainer(containerId); - fOnSuccess(); - } - ); - } - else - { - if(containerId) databap.appendContainer(containerId); - fOnSuccess(); - } - }, - - appendContainer: function(containerId, codeId) - { - if(containerId != '') - { - if(!codeId) - { - codeBoxId = 'code_reader'; - container = databap.vars.code_container; - } - else - { - codeBoxId = databap.getElemTag('code_reader', codeId); - container = databap.vars.code_container.replace('id="code_reader"', 'id="'+codeBoxId+'"'); - } - databap.getMainElem(containerId).append(container); - return codeBoxId; - } - else - { - return false; - } - }, - - getImagePath: function(imageName) - { - return 'url("'+databap.consts.app_image_folder+imageName+'")'; - }, - - getPagePath: function(sPageName) - { - return databap.consts.mask_folder+sPageName+'.html?'+databap.consts.time; - }, - - addErrorBefore: function(msg, elem) - { - var msg = 'Erreur : '+msg+'.'; - databap.addMsgBefore(msg, 'error', elem); - }, - - addWarningBefore: function(msg, elem) - { - var msg = 'Attention : '+msg+'.'; - databap.addMsgBefore(msg, 'warning', elem); - }, - - addSuccessBefore: function(msg, elem) - { - var msg = 'Succès : '+msg+'.'; - databap.addMsgBefore(msg, 'success', elem); - }, - - addMsgBefore: function(msg, msgClass, elem) - { - var $msg = $('

      '+msg+'

      '); - $msg.hide().insertBefore(elem).slideDown('fast').delay(5000).slideUp('fast', function(){$(this).remove();}); - }, - - setTitle: function(extra) - { - if(!extra) - { - extra = ''; - } - else - { - extra = extra+' | '; - } - var page = ucwords(databap.vars.current_page.replace('_', ' ')); - document.title = extra+'Databap '+chr('8226')+' '+page; - }, - - goTo: function(page) - { - window.location = this.getPageLink(page); - }, - - getPageLink: function(page) - { - return this.vars.serv_name+'?p='+page; - }, - - getActionLink: function(sAction, oVars) - { - if(!oVars) oVars = {}; - sVars = ''; - for(i in oVars) - { - sVars += '&'+i+'='+oVars[i]; - } - return this.consts.process_url+'?a='+sAction+sVars; - } -}; - -/* general methods */ - -String.prototype.stripVowelAccent = function() -{ - var sText = this; - var rExps= - [ - {re:/[\xC0-\xC6]/g, ch:'A'}, - {re:/[\xE0-\xE6]/g, ch:'a'}, - {re:/[\xC8-\xCB]/g, ch:'E'}, - {re:/[\xE8-\xEB]/g, ch:'e'}, - {re:/[\xCC-\xCF]/g, ch:'I'}, - {re:/[\xEC-\xEF]/g, ch:'i'}, - {re:/[\xD2-\xD6]/g, ch:'O'}, - {re:/[\xF2-\xF6]/g, ch:'o'}, - {re:/[\xD9-\xDC]/g, ch:'U'}, - {re:/[\xF9-\xFC]/g, ch:'u'}, - {re:/[\xD1]/g, ch:'N'}, - {re:/[\xF1]/g, ch:'n'} - ]; - for(var i=0, len=rExps.length; i 0; -} - -function ucwords(text) -{ - return (t = text + '').replace - ( - /^([a-z])|\s+([a-z])/g, - function($1) - { - return $1.toUpperCase(); - } - ); -} - -function getTimeString() -{ - var currentTime = new Date(); - var hours = currentTime.getHours(); - var minutes = currentTime.getMinutes(); - var secondes = currentTime.getSeconds(); - - hours = sprintf(hours, 2, '0'); - minutes = sprintf(minutes, 2, '0'); - secondes = sprintf(secondes, 2, '0'); - - return hours+':'+minutes+':'+secondes; -} - -function sprintf(value, length, char) -{ - var buffer = ''; - missingCharsNb = Math.max(0, length - value.length); - for(var i=0;i<=missingCharsNb;i++) - { - buffer += char; - } - - return buffer+value; -} - -function chr(codePt) -{ - if (codePt > 0xFFFF) - { - return String.fromCharCode(0xD800 + (codePt >> 10), 0xDC00 + (codePt & 0x3FF)); - } - return String.fromCharCode(codePt); -} - -function explode(delimiter, string, limit) -{ - var emptyArray = {0:''}; - - if(arguments.length < 2 || typeof arguments[0] == 'undefined' || typeof arguments[1] == 'undefined') {return null;} - - if(delimiter === '' || delimiter === false || delimiter === null) {return false;} - - if(typeof delimiter == 'function' || typeof delimiter == 'object' || typeof string == 'function' || typeof string == 'object') {return emptyArray;} - - if(delimiter === true) {delimiter = '1'; - } - - if(!limit) {return string.toString().split(delimiter.toString());} - else - { - // support for limit argument - var splitted = string.toString().split(delimiter.toString()); - var partA = splitted.splice(0, limit - 1); - var partB = splitted.join(delimiter.toString()); - partA.push(partB); - return partA; - } -} - -function implode(glue, pieces) -{ - var i = '', retVal = '', tGlue = ''; - - if(arguments.length === 1) - { - pieces = glue; - glue = ''; - } - - if(typeof(pieces) === 'object') - { - if(pieces instanceof Array) - { - return pieces.join(glue); - } - else - { - for (i in pieces) - { - retVal += tGlue + pieces[i]; - tGlue = glue; - } - return retVal; - } - } - else - { - return pieces; - } -} - -function file_exists(url) -{ - var req = this.window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); - if(!req) - { - throw new Error('XMLHttpRequest not supported'); - } - - // HEAD Results are usually shorter (faster) than GET - req.open('HEAD', url, false); - req.send(null); if (req.status == 200) - { - return true; - } - - return false; -} - -function basename(path, suffix) -{ - var b = path.replace(/^.*[\/\\]/g, ''); - if (typeof(suffix) == 'string' && b.substr(b.length - suffix.length) == suffix) - { - b = b.substr(0, b.length - suffix.length); - } - return b; -} - -/* -//Queue element -( - function($) - { - $.fn.delaying = function(delay, reloadAction, minLength) - { - var options={delay:delay, reload_action:reloadAction, min_length:minLength}; - this.each - ( - function() - { - $(this).data('tsb',new Delayer($(this), options)); - } - ); - return this; - }; - - function Delayer(elem, options) - { - var self = this; - var delays, min_length, previous_values, - temp_values, reload_action, processing_requests; - - this.init = function() - { - delays = options.delay; - min_length = options.min_length; - previous_values = ''; - temp_values = ''; - reload_action = options.reload_action; - processing_requests = false; - }; - - this.request = function(val) - { - var requestOk = false; - - //Prevent from loading request with too few elements or twice the same request - if(val.length > queue.min_length[id] && val != queue.previous_values[id]) - { - //Prevent from spamming server with requests and wait for value stabilization - if(queue.processing_requests[id] == false && val == queue.temp_values[id]) - { - queue.previous_values[id] = val; - requestOk = true; - } - else - { - queue.temp_values[id] = val; - setTimeout(queue.reload_action[id], queue.delays[id]); - } - } - return requestOk; - }; - } - - - - } -) -(jQuery); -*/ - -function debug(text, bQuery) -{ - if(window.console) console.log(text); -} diff --git a/jquery/jquery-1.4.4.min.js b/jquery/jquery-1.4.4.min.js deleted file mode 100755 index 8f3ca2e..0000000 --- a/jquery/jquery-1.4.4.min.js +++ /dev/null @@ -1,167 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.4 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Thu Nov 11 19:04:53 2010 -0500 - */ -(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h= -h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;kd)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La, -"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this, -e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a, -"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+ -a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/, -C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j, -s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this, -j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length}, -toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j=== --1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false; -if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload", -b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&& -!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&& -l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H
      a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), -k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, -scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= -false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= -1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
      ";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
      t
      ";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= -"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= -c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); -else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; -if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, -attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& -b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; -c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, -arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= -d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ -c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== -8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== -"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ -d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= -B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== -"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== -0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); -(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; -break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, -q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= -l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, -m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== -true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== -g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- -0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== -i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; -if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, -g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); -n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& -function(){var g=k,i=t.createElement("div");i.innerHTML="

      ";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| -p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= -t.createElement("div");g.innerHTML="
      ";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? -function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= -h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): -c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, -2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, -b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& -e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1, -""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div
      ","
      "];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; -else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", -prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| -b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- -1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); -d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, -jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, -zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), -h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); -if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= -d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; -e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, -ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== -"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
      ").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& -!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, -getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", -script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| -!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= -false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; -A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", -b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& -c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| -c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= -encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", -[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), -e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); -if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", -3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, -d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* -Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} -var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; -this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| -this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= -c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, -e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& -c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); -c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ -b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/jquery/jquery-1.6.1.min.js b/jquery/jquery-1.6.1.min.js deleted file mode 100755 index b2ac174..0000000 --- a/jquery/jquery-1.6.1.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * jQuery JavaScript Library v1.6.1 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Thu May 12 15:04:36 2011 -0400 - */ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!cj[a]){var b=f("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),c.body.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write("");b=cl.createElement(a),cl.body.appendChild(b),d=f.css(b,"display"),c.body.removeChild(ck)}cj[a]=d}return cj[a]}function cu(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function ct(){cq=b}function cs(){setTimeout(ct,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g=0===c})}function W(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function O(a,b){return(a&&a!=="*"?a+".":"")+b.replace(A,"`").replace(B,"&")}function N(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function L(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function F(){return!0}function E(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function H(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(H,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=d.userAgent,x,y,z,A=Object.prototype.toString,B=Object.prototype.hasOwnProperty,C=Array.prototype.push,D=Array.prototype.slice,E=String.prototype.trim,F=Array.prototype.indexOf,G={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.1",length:0,size:function(){return this.length},toArray:function(){return D.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?C.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(D.apply(this,arguments),"slice",D.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:C,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!y){y=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",z,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",z),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&H()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):G[A.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!B.call(a,"constructor")&&!B.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||B.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
      a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};f=c.createElement("select"),g=f.appendChild(c.createElement("option")),h=a.getElementsByTagName("input")[0],j={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},h.checked=!0,j.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,j.optDisabled=!g.disabled;try{delete a.test}catch(s){j.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function b(){j.noCloneEvent=!1,a.detachEvent("onclick",b)}),a.cloneNode(!0).fireEvent("onclick")),h=c.createElement("input"),h.value="t",h.setAttribute("type","radio"),j.radioValue=h.value==="t",h.setAttribute("checked","checked"),a.appendChild(h),k=c.createDocumentFragment(),k.appendChild(a.firstChild),j.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",l=c.createElement("body"),m={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"};for(q in m)l.style[q]=m[q];l.appendChild(a),b.insertBefore(l,b.firstChild),j.appendChecked=h.checked,j.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,j.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
      ",j.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
      t
      ",n=a.getElementsByTagName("td"),r=n[0].offsetHeight===0,n[0].style.display="",n[1].style.display="none",j.reliableHiddenOffsets=r&&n[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(i=c.createElement("div"),i.style.width="0",i.style.marginRight="0",a.appendChild(i),j.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(i,null)||{marginRight:0}).marginRight,10)||0)===0),l.innerHTML="",b.removeChild(l);if(a.attachEvent)for(q in{submit:1,change:1,focusin:1})p="on"+q,r=p in a,r||(a.setAttribute(p,"return;"),r=typeof a[p]=="function"),j[q+"Bubbles"]=r;return j}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;return(e.value||"").replace(p,"")}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);c=j&&f.attrFix[c]||c,i=f.attrHooks[c],i||(!t.test(c)||typeof d!="boolean"&&d!==b&&d.toLowerCase()!==c.toLowerCase()?v&&(f.nodeName(a,"form")||u.test(c))&&(i=v):i=w);if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j)return i.get(a,c);h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);c=i&&f.propFix[c]||c,h=f.propHooks[c];return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return a[f.propFix[c]||c]?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=b),a.setAttribute(c,c.toLowerCase()));return c}},f.attrHooks.value={get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return a.value},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=Object.prototype.hasOwnProperty,y=/\.(.*)$/,z=/^(?:textarea|input|select)$/i,A=/\./g,B=/ /g,C=/[^\w\s.|`]/g,D=function(a){return a.replace(C,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=E;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=E);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),D).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem -)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},K=function(c){var d=c.target,e,g;if(!!z.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=J(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:K,beforedeactivate:K,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&K.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&K.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",J(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in I)f.event.add(this,c+".specialChange",I[c]);return z.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return z.test(this.nodeName)}},I=f.event.special.change.filters,I.focus=I.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

      ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
      ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=U.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(W(c[0])||W(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=T.call(arguments);P.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!V[a]?f.unique(e):e,(this.length>1||R.test(d))&&Q.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y=/ jQuery\d+="(?:\d+|null)"/g,Z=/^\s+/,$=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,_=/<([\w:]+)/,ba=/",""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]};bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
      ","
      "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Y,""):null;if(typeof a=="string"&&!bc.test(a)&&(f.support.leadingWhitespace||!Z.test(a))&&!bg[(_.exec(a)||["",""])[1].toLowerCase()]){a=a.replace($,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bj(a,d),e=bk(a),g=bk(d);for(h=0;e[h];++h)bj(e[h],g[h])}if(b){bi(a,d);if(c){e=bk(a),g=bk(d);for(h=0;e[h];++h)bi(e[h],g[h])}}return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument|| -b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bb.test(k))k=b.createTextNode(k);else{k=k.replace($,"<$1>");var l=(_.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=ba.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Z.test(k)&&o.insertBefore(b.createTextNode(Z.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bp.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bo.test(g)?g.replace(bo,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,c){var d,e,g;c=c.replace(br,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bs.test(d)&&bt.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bE=/%20/g,bF=/\[\]$/,bG=/\r?\n/g,bH=/#.*$/,bI=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bJ=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bK=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bL=/^(?:GET|HEAD)$/,bM=/^\/\//,bN=/\?/,bO=/)<[^<]*)*<\/script>/gi,bP=/^(?:select|textarea)/i,bQ=/\s+/,bR=/([?&])_=[^&]*/,bS=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bT=f.fn.load,bU={},bV={},bW,bX;try{bW=e.href}catch(bY){bW=c.createElement("a"),bW.href="",bW=bW.href}bX=bS.exec(bW.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bT)return bT.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
      ").append(c.replace(bO,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bP.test(this.nodeName)||bJ.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bG,"\r\n")}}):{name:b.name,value:c.replace(bG,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bW,isLocal:bK.test(bX[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bZ(bU),ajaxTransport:bZ(bV),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?ca(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=cb(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bI.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bH,"").replace(bM,bX[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bQ),d.crossDomain==null&&(r=bS.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bX[1]&&r[2]==bX[2]&&(r[3]||(r[1]==="http:"?80:443))==(bX[3]||(bX[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bU,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bL.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bN.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bR,"$1_="+x);d.url=y+(y===d.url?(bN.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bV,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bE,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq,cr=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
      ";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){return this[0]?parseFloat(f.css(this[0],d,"padding")):null},f.fn["outer"+c]=function(a){return this[0]?parseFloat(f.css(this[0],d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/jquery/jquery.min.js b/jquery/jquery.min.js index 3883779..fe21ee7 100755 --- a/jquery/jquery.min.js +++ b/jquery/jquery.min.js @@ -1,2 +1,7 @@ -/*! jQuery v1.8.3 jquery.com | jquery.org/license */ +/*! jQuery v2.1.0 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +//!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k="".trim,l={},m=a.document,n="2.1.0",o=function(a,b){return new o.fn.init(a,b)},p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};o.fn=o.prototype={jquery:n,constructor:o,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=o.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return o.each(this,a,b)},map:function(a){return this.pushStack(o.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},o.extend=o.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||o.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(o.isPlainObject(d)||(e=o.isArray(d)))?(e?(e=!1,f=c&&o.isArray(c)?c:[]):f=c&&o.isPlainObject(c)?c:{},g[b]=o.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},o.extend({expando:"jQuery"+(n+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===o.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return a-parseFloat(a)>=0},isPlainObject:function(a){if("object"!==o.type(a)||a.nodeType||o.isWindow(a))return!1;try{if(a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}return!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=o.trim(a),a&&(1===a.indexOf("use strict")?(b=m.createElement("script"),b.text=a,m.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":k.call(a)},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?o.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),o.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||o.guid++,f):void 0},now:Date.now,support:l}),o.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=o.type(a);return"function"===c||o.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="sizzle"+-new Date,t=a.document,u=0,v=0,w=eb(),x=eb(),y=eb(),z=function(a,b){return a===b&&(j=!0),0},A="undefined",B=1<<31,C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=D.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",M=L.replace("w","w#"),N="\\["+K+"*("+L+")"+K+"*(?:([*^$|!~]?=)"+K+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+M+")|)|)"+K+"*\\]",O=":("+L+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+N.replace(3,8)+")*)|.*)\\)|)",P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(O),U=new RegExp("^"+M+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L.replace("w","w*")+")"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=/'|\\/g,ab=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),bb=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{G.apply(D=H.call(t.childNodes),t.childNodes),D[t.childNodes.length].nodeType}catch(cb){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function db(a,b,d,e){var f,g,h,i,j,m,p,q,u,v;if((b?b.ownerDocument||b:t)!==l&&k(b),b=b||l,d=d||[],!a||"string"!=typeof a)return d;if(1!==(i=b.nodeType)&&9!==i)return[];if(n&&!e){if(f=Z.exec(a))if(h=f[1]){if(9===i){if(g=b.getElementById(h),!g||!g.parentNode)return d;if(g.id===h)return d.push(g),d}else if(b.ownerDocument&&(g=b.ownerDocument.getElementById(h))&&r(b,g)&&g.id===h)return d.push(g),d}else{if(f[2])return G.apply(d,b.getElementsByTagName(a)),d;if((h=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(h)),d}if(c.qsa&&(!o||!o.test(a))){if(q=p=s,u=b,v=9===i&&a,1===i&&"object"!==b.nodeName.toLowerCase()){m=ob(a),(p=b.getAttribute("id"))?q=p.replace(_,"\\$&"):b.setAttribute("id",q),q="[id='"+q+"'] ",j=m.length;while(j--)m[j]=q+pb(m[j]);u=$.test(a)&&mb(b.parentNode)||b,v=m.join(",")}if(v)try{return G.apply(d,u.querySelectorAll(v)),d}catch(w){}finally{p||b.removeAttribute("id")}}}return xb(a.replace(P,"$1"),b,d,e)}function eb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function fb(a){return a[s]=!0,a}function gb(a){var b=l.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function hb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function ib(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||B)-(~a.sourceIndex||B);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function jb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function kb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function lb(a){return fb(function(b){return b=+b,fb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function mb(a){return a&&typeof a.getElementsByTagName!==A&&a}c=db.support={},f=db.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},k=db.setDocument=function(a){var b,e=a?a.ownerDocument||a:t,g=e.defaultView;return e!==l&&9===e.nodeType&&e.documentElement?(l=e,m=e.documentElement,n=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){k()},!1):g.attachEvent&&g.attachEvent("onunload",function(){k()})),c.attributes=gb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=gb(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(e.getElementsByClassName)&&gb(function(a){return a.innerHTML="
      ",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=gb(function(a){return m.appendChild(a).id=s,!e.getElementsByName||!e.getElementsByName(s).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==A&&n){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){var c=typeof a.getAttributeNode!==A&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==A?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==A&&n?b.getElementsByClassName(a):void 0},p=[],o=[],(c.qsa=Y.test(e.querySelectorAll))&&(gb(function(a){a.innerHTML="",a.querySelectorAll("[t^='']").length&&o.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||o.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll(":checked").length||o.push(":checked")}),gb(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&o.push("name"+K+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||o.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),o.push(",.*:")})),(c.matchesSelector=Y.test(q=m.webkitMatchesSelector||m.mozMatchesSelector||m.oMatchesSelector||m.msMatchesSelector))&&gb(function(a){c.disconnectedMatch=q.call(a,"div"),q.call(a,"[s!='']:x"),p.push("!=",O)}),o=o.length&&new RegExp(o.join("|")),p=p.length&&new RegExp(p.join("|")),b=Y.test(m.compareDocumentPosition),r=b||Y.test(m.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},z=b?function(a,b){if(a===b)return j=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===t&&r(t,a)?-1:b===e||b.ownerDocument===t&&r(t,b)?1:i?I.call(i,a)-I.call(i,b):0:4&d?-1:1)}:function(a,b){if(a===b)return j=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],k=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:i?I.call(i,a)-I.call(i,b):0;if(f===g)return ib(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)k.unshift(c);while(h[d]===k[d])d++;return d?ib(h[d],k[d]):h[d]===t?-1:k[d]===t?1:0},e):l},db.matches=function(a,b){return db(a,null,null,b)},db.matchesSelector=function(a,b){if((a.ownerDocument||a)!==l&&k(a),b=b.replace(S,"='$1']"),!(!c.matchesSelector||!n||p&&p.test(b)||o&&o.test(b)))try{var d=q.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return db(b,l,null,[a]).length>0},db.contains=function(a,b){return(a.ownerDocument||a)!==l&&k(a),r(a,b)},db.attr=function(a,b){(a.ownerDocument||a)!==l&&k(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!n):void 0;return void 0!==f?f:c.attributes||!n?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},db.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},db.uniqueSort=function(a){var b,d=[],e=0,f=0;if(j=!c.detectDuplicates,i=!c.sortStable&&a.slice(0),a.sort(z),j){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return i=null,a},e=db.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=db.selectors={cacheLength:50,createPseudo:fb,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ab,bb),a[3]=(a[4]||a[5]||"").replace(ab,bb),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||db.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&db.error(a[0]),a},PSEUDO:function(a){var b,c=!a[5]&&a[2];return V.CHILD.test(a[0])?null:(a[3]&&void 0!==a[4]?a[2]=a[4]:c&&T.test(c)&&(b=ob(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ab,bb).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=w[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&w(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==A&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=db.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),t=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&t){k=q[s]||(q[s]={}),j=k[a]||[],n=j[0]===u&&j[1],m=j[0]===u&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[u,n,m];break}}else if(t&&(j=(b[s]||(b[s]={}))[a])&&j[0]===u)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(t&&((l[s]||(l[s]={}))[a]=[u,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||db.error("unsupported pseudo: "+a);return e[s]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?fb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:fb(function(a){var b=[],c=[],d=g(a.replace(P,"$1"));return d[s]?fb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:fb(function(a){return function(b){return db(a,b).length>0}}),contains:fb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:fb(function(a){return U.test(a||"")||db.error("unsupported lang: "+a),a=a.replace(ab,bb).toLowerCase(),function(b){var c;do if(c=n?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===m},focus:function(a){return a===l.activeElement&&(!l.hasFocus||l.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:lb(function(){return[0]}),last:lb(function(a,b){return[b-1]}),eq:lb(function(a,b,c){return[0>c?c+b:c]}),even:lb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:lb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:lb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:lb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function qb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=v++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[u,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[s]||(b[s]={}),(h=i[d])&&h[0]===u&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function rb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function sb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function tb(a,b,c,d,e,f){return d&&!d[s]&&(d=tb(d)),e&&!e[s]&&(e=tb(e,f)),fb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||wb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:sb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=sb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=sb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ub(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],i=g||d.relative[" "],j=g?1:0,k=qb(function(a){return a===b},i,!0),l=qb(function(a){return I.call(b,a)>-1},i,!0),m=[function(a,c,d){return!g&&(d||c!==h)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>j;j++)if(c=d.relative[a[j].type])m=[qb(rb(m),c)];else{if(c=d.filter[a[j].type].apply(null,a[j].matches),c[s]){for(e=++j;f>e;e++)if(d.relative[a[e].type])break;return tb(j>1&&rb(m),j>1&&pb(a.slice(0,j-1).concat({value:" "===a[j-2].type?"*":""})).replace(P,"$1"),c,e>j&&ub(a.slice(j,e)),f>e&&ub(a=a.slice(e)),f>e&&pb(a))}m.push(c)}return rb(m)}function vb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,i,j,k){var m,n,o,p=0,q="0",r=f&&[],s=[],t=h,v=f||e&&d.find.TAG("*",k),w=u+=null==t?1:Math.random()||.1,x=v.length;for(k&&(h=g!==l&&g);q!==x&&null!=(m=v[q]);q++){if(e&&m){n=0;while(o=a[n++])if(o(m,g,i)){j.push(m);break}k&&(u=w)}c&&((m=!o&&m)&&p--,f&&r.push(m))}if(p+=q,c&&q!==p){n=0;while(o=b[n++])o(r,s,g,i);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=E.call(j));s=sb(s)}G.apply(j,s),k&&!f&&s.length>0&&p+b.length>1&&db.uniqueSort(j)}return k&&(u=w,h=t),r};return c?fb(f):f}g=db.compile=function(a,b){var c,d=[],e=[],f=y[a+" "];if(!f){b||(b=ob(a)),c=b.length;while(c--)f=ub(b[c]),f[s]?d.push(f):e.push(f);f=y(a,vb(e,d))}return f};function wb(a,b,c){for(var d=0,e=b.length;e>d;d++)db(a,b[d],c);return c}function xb(a,b,e,f){var h,i,j,k,l,m=ob(a);if(!f&&1===m.length){if(i=m[0]=m[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&c.getById&&9===b.nodeType&&n&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(ab,bb),b)||[])[0],!b)return e;a=a.slice(i.shift().value.length)}h=V.needsContext.test(a)?0:i.length;while(h--){if(j=i[h],d.relative[k=j.type])break;if((l=d.find[k])&&(f=l(j.matches[0].replace(ab,bb),$.test(i[0].type)&&mb(b.parentNode)||b))){if(i.splice(h,1),a=f.length&&pb(i),!a)return G.apply(e,f),e;break}}}return g(a,m)(f,b,!n,e,$.test(a)&&mb(b.parentNode)||b),e}return c.sortStable=s.split("").sort(z).join("")===s,c.detectDuplicates=!!j,k(),c.sortDetached=gb(function(a){return 1&a.compareDocumentPosition(l.createElement("div"))}),gb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||hb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&gb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||hb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),gb(function(a){return null==a.getAttribute("disabled")})||hb(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),db}(a);o.find=t,o.expr=t.selectors,o.expr[":"]=o.expr.pseudos,o.unique=t.uniqueSort,o.text=t.getText,o.isXMLDoc=t.isXML,o.contains=t.contains;var u=o.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(o.isFunction(b))return o.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return o.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return o.filter(b,a,c);b=o.filter(b,a)}return o.grep(a,function(a){return g.call(b,a)>=0!==c})}o.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?o.find.matchesSelector(d,a)?[d]:[]:o.find.matches(a,o.grep(b,function(a){return 1===a.nodeType}))},o.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(o(a).filter(function(){for(b=0;c>b;b++)if(o.contains(e[b],this))return!0}));for(b=0;c>b;b++)o.find(a,e[b],d);return d=this.pushStack(c>1?o.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?o(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=o.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof o?b[0]:b,o.merge(this,o.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:m,!0)),v.test(c[1])&&o.isPlainObject(b))for(c in b)o.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=m.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=m,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):o.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(o):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),o.makeArray(a,this))};A.prototype=o.fn,y=o(m);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};o.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&o(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),o.fn.extend({has:function(a){var b=o(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(o.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?o(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&o.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?o.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(o(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(o.unique(o.merge(this.get(),o(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}o.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return o.dir(a,"parentNode")},parentsUntil:function(a,b,c){return o.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return o.dir(a,"nextSibling")},prevAll:function(a){return o.dir(a,"previousSibling")},nextUntil:function(a,b,c){return o.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return o.dir(a,"previousSibling",c)},siblings:function(a){return o.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return o.sibling(a.firstChild)},contents:function(a){return a.contentDocument||o.merge([],a.childNodes)}},function(a,b){o.fn[a]=function(c,d){var e=o.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=o.filter(d,e)),this.length>1&&(C[a]||o.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return o.each(a.match(E)||[],function(a,c){b[c]=!0}),b}o.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):o.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){o.each(b,function(b,c){var d=o.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&o.each(arguments,function(a,b){var c;while((c=o.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?o.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},o.extend({Deferred:function(a){var b=[["resolve","done",o.Callbacks("once memory"),"resolved"],["reject","fail",o.Callbacks("once memory"),"rejected"],["notify","progress",o.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return o.Deferred(function(c){o.each(b,function(b,f){var g=o.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&o.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?o.extend(a,d):d}},e={};return d.pipe=d.then,o.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&o.isFunction(a.promise)?e:0,g=1===f?a:o.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&o.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;o.fn.ready=function(a){return o.ready.promise().done(a),this},o.extend({isReady:!1,readyWait:1,holdReady:function(a){a?o.readyWait++:o.ready(!0)},ready:function(a){(a===!0?--o.readyWait:o.isReady)||(o.isReady=!0,a!==!0&&--o.readyWait>0||(H.resolveWith(m,[o]),o.fn.trigger&&o(m).trigger("ready").off("ready")))}});function I(){m.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),o.ready()}o.ready.promise=function(b){return H||(H=o.Deferred(),"complete"===m.readyState?setTimeout(o.ready):(m.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},o.ready.promise();var J=o.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===o.type(c)){e=!0;for(h in c)o.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,o.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(o(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};o.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=o.expando+Math.random()}K.uid=1,K.accepts=o.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,o.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(o.isEmptyObject(f))o.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,o.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{o.isArray(b)?d=b.concat(b.map(o.camelCase)):(e=o.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!o.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?o.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}o.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),o.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length; +//while(c--)d=g[c].name,0===d.indexOf("data-")&&(d=o.camelCase(d.slice(5)),P(f,d,e[d]));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=o.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),o.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||o.isArray(c)?d=L.access(a,b,o.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=o.queue(a,b),d=c.length,e=c.shift(),f=o._queueHooks(a,b),g=function(){o.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:o.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),o.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";l.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return m.activeElement}catch(a){}}o.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=o.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof o!==U&&o.event.triggered!==b.type?o.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],n=q=h[1],p=(h[2]||"").split(".").sort(),n&&(l=o.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=o.event.special[n]||{},k=o.extend({type:n,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&o.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(n,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),o.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],n=q=h[1],p=(h[2]||"").split(".").sort(),n){l=o.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||o.removeEvent(a,n,r.handle),delete i[n])}else for(n in i)o.event.remove(a,n+b[j],c,d,!0);o.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,p=[d||m],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||m,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+o.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[o.expando]?b:new o.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:o.makeArray(c,[b]),n=o.event.special[q]||{},e||!n.trigger||n.trigger.apply(d,c)!==!1)){if(!e&&!n.noBubble&&!o.isWindow(d)){for(i=n.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||m)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:n.bindType||q,l=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),l&&l.apply(g,c),l=k&&g[k],l&&l.apply&&o.acceptData(g)&&(b.result=l.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||n._default&&n._default.apply(p.pop(),c)!==!1||!o.acceptData(d)||k&&o.isFunction(d[q])&&!o.isWindow(d)&&(h=d[k],h&&(d[k]=null),o.event.triggered=q,d[q](),o.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=o.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=o.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=o.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((o.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?o(e,this).index(i)>=0:o.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return o.nodeName(a,"table")&&o.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)o.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=o.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&o.nodeName(a,b)?o.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}o.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=o.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||o.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,n=a.length;n>m;m++)if(e=a[m],e||0===e)if("object"===o.type(e))o.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;o.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===o.inArray(e,d))&&(i=o.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f,g,h=o.event.special,i=0;void 0!==(c=a[i]);i++){if(o.acceptData(c)&&(f=c[L.expando],f&&(b=L.cache[f]))){if(d=Object.keys(b.events||{}),d.length)for(g=0;void 0!==(e=d[g]);g++)h[e]?o.event.remove(c,e):o.removeEvent(c,e,b.handle);L.cache[f]&&delete L.cache[f]}delete M.cache[c[M.expando]]}}}),o.fn.extend({text:function(a){return J(this,function(a){return void 0===a?o.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?o.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||o.cleanData(ob(c)),c.parentNode&&(b&&o.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(o.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return o.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(o.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,o.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,k=this.length,m=this,n=k-1,p=a[0],q=o.isFunction(p);if(q||k>1&&"string"==typeof p&&!l.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(k&&(c=o.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=o.map(ob(c,"script"),kb),g=f.length;k>j;j++)h=c,j!==n&&(h=o.clone(h,!0,!0),g&&o.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,o.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&o.contains(i,h)&&(h.src?o._evalUrl&&o._evalUrl(h.src):o.globalEval(h.textContent.replace(hb,"")))}return this}}),o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){o.fn[a]=function(a){for(var c,d=[],e=o(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),o(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d=o(c.createElement(b)).appendTo(c.body),e=a.getDefaultComputedStyle?a.getDefaultComputedStyle(d[0]).display:o.css(d[0],"display");return d.detach(),e}function tb(a){var b=m,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||o(" --> +

      L'article est en cours de chargement...

      + + \ No newline at end of file diff --git a/masks/chat.html b/masks/chat.html index 95fd625..75e67ec 100755 --- a/masks/chat.html +++ b/masks/chat.html @@ -1,44 +1,45 @@
      -
      -

      Chat

      -
      -
      -

      +

      -
      - +
      -

      Manuel d'utilisationx

      +

      Manuel d'utilisation

      +

       

      Message

      EchapSupprime le texte en cours de rédaction

      Flèche hautRépète dernier message envoyé

      @X YVous dites Y à X (pseudo de Y en rouge)

      /me XVotre_pseudo X. Utile lorsque vous voulez parler de vous à la 3ème personne. Par exemple : /me se touche la nouille au taff

      -

      /mail X YVous envoyez à X un email contenant Y (adresse professionel)

      +

      /mail X YVous envoyez à X un email contenant Y (adresse professionnelle)

      +

      /news XVous annoncez le message X (votre message est ajouté à la liste des news automatiquement)

      Tab Chaque hit pré-remplie le message en faisant défiler la liste des connectés. @@ -48,6 +49,7 @@

      Chans

      Les chans sont divisés en trois catégories : les chans privés (PM, pour deux personnes seulement), les chans entreprise (pour toutes les personnes d'une même entreprise) et les autres (chans publiques)

      /join #XVous rejoignez le chan X (si autorisé)

      +

      /invite XVous invitez X à rejoindre le chan (si autorisé)

      /pm #XVous rejoignez le chan privé (vous et X)

      /quit #XVous quittez le chan X

      /chansListe des chans publiques disponibles (aussi accessible via /channels et /list)

      @@ -77,7 +79,8 @@ databap.pageInit = function() //Page variables databap.tmp('sending_msg', 'boolean'); databap.tmp('refresh', 'boolean'); - databap.initVar('unread_msg', 'object'); + self.vars2('unread_msg', 'object'); + databap.tmp('news_period', 10*60*1000); //Main elements $MsgInput = databap.getMainElem('#message'); @@ -90,7 +93,7 @@ databap.pageInit = function() //Loading the chat databap.vars.last_message_id = '0'; - oScrollbar = $('#chat_container').tinyscrollbar({viewport:'#chat_messages_box', overview:'#chat_messages'}); + self.initScrollBar('#chat_container', '#chat_messages_box', '#chat_messages'); //Loading Chans setChanButton(); @@ -99,67 +102,117 @@ databap.pageInit = function() joinChan(databap.consts.default_chan, true); //window focus - //databap.onMouveEnter = function(){if(databap.vars.inputFocus){onPageFocus();}else{$MsgInput.focus();}}; - //databap.onMouveLeave = function(){$MsgInput.focus();onPageBlur();}; $(window).focus(onPageFocus); $(window).blur(onPageBlur); - //$(window).focus(function(){debug('focus');}).blur(function(){debug('blur');}); //Message Input Focus - //$MsgInput.focus(function(){onPageFocus();databap.vars.inputFocus=true;}); - //$MsgInput.blur(function(){onPageBlur();databap.vars.inputFocus=false;}); $MsgInput.focus(); - //databap.onKeyDown = function(e){if(!databap.vars.inputFocus)$MsgInput.focus();}; - //On resize - //databap.resetSize = function(){databap.getMainElem('#chat_messages_box').height(databap.getMainElem('#sidebar').outerHeight());}; - databap.onResize = function() - { - databap.$main.css('overflow', 'hidden'); - - //debug('chat page resize triggered'); - var $cb = databap.getMainElem('#chat_messages_box'); - var iMaxHeight = databap.consts.pageMaxHeight - 15 - ($('#chat_title').outerHeight(true) + $('#chat_chan').outerHeight(true) + $('#chat_room').outerHeight(true) - $('#chat_messages_box').outerHeight(true)); - if($cb.height() != iMaxHeight) - { - //$cb.animate({height: maxHeight}, 400, function(){oScrollbar.tinyscrollbar_update('bottom');resizing=false;}); - $cb.height(iMaxHeight); - oScrollbar.tinyscrollbar_update('bottom'); - } - }; - - //Disconnect from the chat - databap.onQuit = function() - { - if(databap.tmp('refresh') == false) databap.getSyncInfo('disconnect_chat'); - databap.vars.chans_list = {}; - if(typeof oChatTimer != "undefined") clearTimeout(oChatTimer); - if(typeof oUserTimer != "undefined") clearTimeout(oUserTimer); - }; - - databap.onKeyDown = function(e) - { - if(e.keyCode == 116) databap.tmp('refresh', true); - else databap.tmp('refresh', false); - }; - - //Sidebar - databap.getMainElem('.sidebar_box_title').click - ( - function() - { - $(this).toggleClass('round') - .closest('.sidebar_box').find('.sidebar_box_content').toggle('fast', function(){databap.resizeMain('true');}); - } - ); - - //Help - databap.getMainElem('#help_open').click(displayHelp); + //Init sidebar events + initSideBar(); //Init's end databap.setInitEnd(true); }; +//On resize +databap.onResize = function() +{ + self.setScrollBarSize('maximize'); + + //Message Input + $('#message').width($('#chat_input').width() - $('#message_img').outerWidth(true)- 1); +}; + +//Disconnect from the chat +databap.onQuit = function() +{ + if(databap.tmp('refresh') == false) databap.getSyncInfo('disconnect_chat'); + databap.vars.chans_list = {}; + if(typeof oChatTimer != "undefined") clearTimeout(oChatTimer); + if(typeof oUserTimer != "undefined") clearTimeout(oUserTimer); + return true; +}; + +databap.onKeyDown = function(e) +{ + if(e.keyCode == 116) databap.tmp('refresh', true); + else databap.tmp('refresh', false); +}; + +function initSideBar() +{ + //Sidebar Events + $('#sidebar').find('.sidebar_box_title').click + ( + function() + { + $This = $(this); + if(!$This.closest('.sidebar_box').find('.sidebar_box_content').is(":visible")) + { + $('#sidebar').find('.sidebar_box_title').not($This) + .addClass('round') + .closest('.sidebar_box').find('.sidebar_box_content') + .hide('fast'); + + $This + .toggleClass('round') + .closest('.sidebar_box').find('.sidebar_box_content') + .toggle('fast', function() + { + databap.resizeMain('true'); + switch($(this).attr('id')) + { + case 'help_open': $('#help_open').click(); break; + case 'news': getNews(); break; + } + }); + } + } + ); + $('#sidebar').find('.sidebar_box_title').eq(0).click(); + + //Help + databap.getMainElem('#help_open').click(displayHelp); +} + +function getNews() +{ + if(typeof databap.tmp('news_timer') != "undefined") clearTimeout(databap.tmp('news_timer')); + var $NewsBox = $('#news'); + if($NewsBox.is(':visible')) + { + databap.getInfo + ( + 'news', + function(asNews) + { + $.each(asNews.news, function(ikey, asNew) + { + $('
      ', {'class':'new'}).hide() + .append($('

      ') + .append($('', {'class':'fa fa-inline fa-30 fa-c-news'})) + .append($('').text(asNew.message))) + .append($('

      ', {'class':'signature'}) + .text('—'+asNew.nickname) + .append('
      ') + .append($('', {'class':'news_time'}).text(asNew.time_desc))) + .appendTo($NewsBox); + }); + + $NewsBox.find('.loading').hide(); + $NewsBox.find('.new').toggle(); + $NewsBox.find('.new:hidden').remove(); + + //Request in 10mins + databap.tmp('news_timer', setTimeout(getNews, databap.tmp('news_period'))); + }, + {}, + 'json' + ); + } +} + function onPageFocus() { databap.vars.focus=true; @@ -176,31 +229,47 @@ function onPageBlur() function setChanButton() { - var sAddChanText = '+'; - $JoinButton = $('#join_chan'); - if($JoinButton.text() == sAddChanText) - { - $JoinButton - .addClass('active') - .empty() - .append($('#')) - .find('#input_chan').focus().keyup - ( - function(e) + $('#join_chan') + //.click(function(){$('#join_chan_button').click();}) + .append + ( + $('', {id:'join_chan_button'}) + .click(function() { - if(e.keyCode == 13) - { - setChanButton(); - joinChan($(this).val()); - } - } - ); - } - else $JoinButton - .empty() - .text(sAddChanText) - .removeClass('active') - .click(function(){setChanButton();}); + $(this).hide(); + $('#join_chan_input').css('display', ''); + $('#join_chan').addClass('active').find('#input_chan').focus(); + }) + .append(' ') + .append($('', {'class':'fa fa-c-plus'})) + .append(' ') + ) + .append + ( + $('', {id:'join_chan_input'}) + .text('#') + .append + ( + $('', {type:'text', id:'input_chan', 'class':'round'}) + .keyup(function(e) + { + if(e.keyCode == 13) + { + var $This = $(this); + joinChan($This.val()); + $This.val(''); + + } + if(typeof e.keyCode == 'undefined' || e.keyCode == 13) + { + $('#join_chan_input').css('display', 'none'); + $('#join_chan_button').show(); + $('#join_chan').removeClass('active'); + } + }) + .blur(function(){$(this).keyup();}) + ).hide() + ); } function getChanKeyName(sChanName) @@ -306,7 +375,7 @@ function switchChan(sChanKeyName) //Show current channel messages databap.getMainElem('#chat_messages').find('p').hide(); databap.getMainElem('#chat_messages').find('p.class_'+sChanKeyName+', p.class_'+databap.consts.all_chan_id).show(); - oScrollbar.tinyscrollbar_update('bottom'); + databap.updateScrollBar('bottom'); //Show Current channel members databap.getMainElem('#connected_users').find('p').hide(); @@ -358,11 +427,11 @@ function addChanTab(sChanKeyName, sChanName, sChanTabName, bLightUp) //Prepare clickable chan button var sChanQuitId = databap.consts.chanQuitPrefix+sChanKeyName; var sChanUnreadId = databap.consts.chanUnreadPrefix+sChanKeyName; - var $ChanButton = $('#') + var $ChanButton = $('#') .click(function(){$(this).removeClass('light_up');joinChan(databap.vars.chans_list[$(this).attr('id').substr(databap.consts.chanPrefix.length)]);}) .append(''+sChanTabName+'') .append('') - .append($('x').click(function(e){e.stopPropagation();quitChan($(this).attr('id').substr(databap.consts.chanQuitPrefix.length));})); + .append($('').click(function(e){e.stopPropagation();quitChan($(this).attr('id').substr(databap.consts.chanQuitPrefix.length));})); //Add channel tab name and bind delete button @@ -418,7 +487,6 @@ function add_message(e) else if(chat_message != '' && databap.tmp('sending_msg') == false) //send { databap.tmp('sending_msg', true); - //debug(databap.tmp('sending_msg')); databap.saveForm ( 'add_message', @@ -426,7 +494,6 @@ function add_message(e) function(id) { databap.tmp('sending_msg', false); - //debug(databap.tmp('sending_msg')); refresh_chat(); }, true @@ -455,6 +522,11 @@ function add_message(e) } break; case 9 : //tab + /* Action Plan + - Detect current caret position : $this.getCursorPosition() + - Find word wrapped around cursor : while loop until non alphanum char before and after caret position + - + */ //Init $this = databap.getMainElem('#message'); var chat_message = $.trim($this.val()); @@ -465,6 +537,7 @@ function add_message(e) //Finding last word var last_word = (chat_message.lastIndexOf(" ")==-1)?chat_message:chat_message.substr(chat_message.lastIndexOf(" ") + 1); + debug(last_word); if(last_word.substr(0, 1)=='@') last_word = last_word.substr(1); var last_word_len = last_word.length; @@ -540,7 +613,7 @@ function refresh_chat(bReset) { var bReset = (typeof bReset != 'undefined' && bReset == true); if(typeof oChatTimer != "undefined") clearTimeout(oChatTimer); - if(databap.vars.current_page == databap.pages.chat) + if(databap.vars.current_page == 'chat') { if(!databap.vars.loading) { @@ -579,7 +652,7 @@ function refresh_chat(bReset) //Nicknames changes if(updateUsersList === true || prevLastMsgId == 0) { - oScrollbar.tinyscrollbar_update('bottom'); + databap.updateScrollBar('bottom'); refresh_users(); updateUsersList = false; } @@ -605,27 +678,35 @@ function refresh_chat(bReset) function addMessage(message_info, bReset) { + message_info.nickname = message_info.nickname || ''; + var sChanKeyName = message_info.id_chan; + var sMsgPrefix = ''+message_info.nickname+'>'; var msg_body = ''; + switch(message_info.msg_class) { case 'U': - msg_body = ''+message_info.nickname+'>'+message_info.message+''; + msg_body = sMsgPrefix+''+message_info.message+''; + break; + case 'NW': + if(!bReset) getNews(); + msg_body = ''+message_info.nickname+' a une news : '+message_info.message+' '; break; case 'A': - var url = databap.getCodeLink(message_info.message); + var url = databap.getInternalLink('code', message_info.message); msg_body = ''+message_info.nickname+' a ajouté un nouveau code : '+message_info.description+''; break; case 'E': - var url = databap.getCodeLink(message_info.message); + var url = databap.getInternalLink('code', message_info.message); msg_body = ''+message_info.nickname+' a modifié le code '+message_info.description+''; break; case 'PA': - var url = databap.getProcLink(message_info.message); + var url = databap.getInternalLink('proc', message_info.message); msg_body = ''+message_info.nickname+' a ajouté une nouvelle procédure : '+message_info.description+''; break; case 'PE': - var url = databap.getProcLink(message_info.message); + var url = databap.getInternalLink('proc', message_info.message); msg_body = ''+message_info.nickname+' a modifié la procédure '+message_info.description+''; break; case 'M': @@ -647,24 +728,25 @@ function addMessage(message_info, bReset) break; case 'I': var msg = ''; - msg_body = ''+message_info.nickname+'>'+msg+''; + msg_body = sMsgPrefix+''+msg+''; break; case '9': var msg = ''; - msg_body = ''+message_info.nickname+'>'+msg+''; + msg_body = sMsgPrefix+''+msg+''; break; case 'R': if(!bReset) { msg_body = ''+message_info.message+''; - setTimeout('databap.tmp(\'refresh\', true);databap.goTo(databap.vars.current_page);', databap.consts.reboot_delay*1000); + setTimeout(databap.refresh, databap.consts.reboot_delay*1000); } break; case 'V': if(!bReset) joinChan(sChanKeyName, false, [], false); break; case 'B': - msg_body = 'Nouvel article sur le blog BI de SAP : '+message_info.art_title+' '; + var url = databap.getInternalLink('a', message_info.message); + msg_body = 'Nouvel article sur le blog BI de SAP : '+message_info.art_title+' '; msg_body+= 'par '+message_info.name+' ('+message_info.art_date+')'+''; break; case 'S': @@ -672,13 +754,21 @@ function addMessage(message_info, bReset) msg_body = ''+message_info.nickname+' '+message_info.message+''; break; case 'DA': - var url = databap.getExternalLink('doc', message_info.message); + var url = databap.getInternalLink('doc', message_info.message); msg_body = ''+message_info.nickname+' a ajouté une nouvelle documentation : '+message_info.description+''; break; case 'DE': - var url = databap.getExternalLink('doc', message_info.message); + var url = databap.getInternalLink('doc', message_info.message); msg_body = ''+message_info.nickname+' a modifié la documentation '+message_info.description+''; break; + case 'TA': + var url = databap.getInternalLink('table', message_info.message); + msg_body = ''+message_info.nickname+' a ajouté une nouvelle table : '+message_info.description+''; + break; + case 'TE': + var url = databap.getInternalLink('table', message_info.message); + msg_body = ''+message_info.nickname+' a modifié la table '+message_info.description+''; + break; } if(msg_body != '') @@ -693,7 +783,7 @@ function addMessage(message_info, bReset) function refresh_users() { if(typeof oUserTimer != "undefined") clearTimeout(oUserTimer); - if(databap.vars.current_page == databap.pages.chat) + if(databap.vars.current_page == 'chat') { var delay = databap.consts.keep_alive / 10; //seconds databap.getInfo @@ -713,19 +803,20 @@ function refresh_users() $.each ( chan_info, - function(id_user, user_info) + function(key, user_info) { var sNickName = user_info.nickname.replace('"', '\''); users_list.push(sNickName); - var afk = (user_info.afk=='1')?'':''; - var profileLink = databap.getExternalLink('profil', user_info.id_user); - var mission = 'Mission actuelle : '+user_info.status; + var profileLink = databap.getInternalLink('profil', user_info.id_user); + var mission = 'Mission actuelle : '+(user_info.status || 'Aucune'); var pm = 'Cliquez pour lancer un channel privé avec '+user_info.name+' ('+user_info.company+')'; - var user = '

      ' - +''+afk+'' - +''+sNickName+'' - +'

      '; - $user = $(user).css("visibility", "hidden"); + var user = $('

      ', {'class':'connected_user class_'+sChankeyName}) + .append($('', {'class':'connected_user_logo', href:profileLink, title:mission, target:'_blank'}) + .append($('', {src:databap.consts.app_image_folder+user_info.logo})) + .append((user_info.afk=='1')?$('', {class:'fa fa-c-afk afk'}):'')) + .append($('', {'class':'connected_user_name clickable '+sNickName, id:user_info.id_user, title:pm}).text(sNickName)); + + $user = $(user)/*.css("visibility", "hidden")*/; $user.find('.connectedUser').click(joinPmChan); databap.getMainElem('#connected_users').append($user); } @@ -734,7 +825,7 @@ function refresh_users() ); //Resize Nickname - var iMaxSize = 126 - 24 - 5; //boxSize - imageSize - marginLeftSize + /*var iMaxSize = 126 - 24 - 5; //boxSize - imageSize - marginLeftSize $.each ( users_list, @@ -751,7 +842,7 @@ function refresh_users() $nick.parent().css("visibility", "visible"); }); } - ); + );*/ switchChan(); }, @@ -793,9 +884,15 @@ function displayHelp() //Visible on all channels $Help.find('p').addClass('class_'+databap.consts.all_chan_id); - $Help.appendTo('#chat_messages').slideDown('fast', function(){oScrollbar.tinyscrollbar_update('bottom');}); + $Help.appendTo('#chat_messages').slideDown('fast', function() + { + databap.updateScrollBar('bottom'); + }); //Close button activation - $Help.find('#close_help').click(function(){$(this).closest('.help').slideUp('fast', function(){oScrollbar.tinyscrollbar_update('bottom');});}); + $Help.find('#close_help').click(function(){$(this).closest('.help').slideUp('fast', function() + { + databap.updateScrollBar('bottom'); + });}); } \ No newline at end of file diff --git a/masks/code.html b/masks/code.html new file mode 100644 index 0000000..3913069 --- /dev/null +++ b/masks/code.html @@ -0,0 +1,330 @@ +

      +
      +
      +
      +
      +
      +
      +
      + + +
      +
      +
      + \ No newline at end of file diff --git a/masks/code_block.html b/masks/code_block.html index 2b947a9..7c23152 100755 --- a/masks/code_block.html +++ b/masks/code_block.html @@ -1,5 +1,5 @@
      -
      () •
      + [#]item[#]
        Chargement du code...
      diff --git a/masks/doc.html b/masks/doc.html index 43e1fe6..304b085 100755 --- a/masks/doc.html +++ b/masks/doc.html @@ -1,26 +1,26 @@
      -
      - - - - - -
      \ No newline at end of file diff --git a/masks/error.html b/masks/error.html index 4072678..319672d 100755 --- a/masks/error.html +++ b/masks/error.html @@ -1,7 +1,4 @@
      -
      -

      Page introuvable

      -

      Sorry Bro!

      Revenir à la page d'accueil

      diff --git a/masks/index.html b/masks/index.html index 4519c95..21447a6 100755 --- a/masks/index.html +++ b/masks/index.html @@ -1,29 +1,24 @@ - - + + - - - - - - + + + + + + + Databap @@ -34,8 +29,13 @@ @@ -43,26 +43,36 @@ - + +
      +
      +
      +
      +

      + + Chargement... +

      +
      +
      +
      -
      diff --git a/masks/item.html b/masks/item.html new file mode 100644 index 0000000..a6ddf92 --- /dev/null +++ b/masks/item.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/masks/list.html b/masks/list.html index 49d4d30..761df60 100755 --- a/masks/list.html +++ b/masks/list.html @@ -1,27 +1,34 @@
      -
      -

      La liste

      -
      \ No newline at end of file diff --git a/masks/logon.html b/masks/logon.html index 05f28dc..0f99839 100755 --- a/masks/logon.html +++ b/masks/logon.html @@ -1,32 +1,96 @@ - - + + + + - + + + + Databap • Logon - -
      -
      -
      -
      +
      + +
      +
      +
      + +
      - - + + - - + + + + + -
      Nom et prénom Nom & prénom
      SociétéMot de passe
      + + + + + +
      - +
      +
      + \ No newline at end of file diff --git a/masks/logout.html b/masks/logout.html new file mode 100644 index 0000000..9cae72c --- /dev/null +++ b/masks/logout.html @@ -0,0 +1,6 @@ +
      +

      Déconnexion...

      +
      + \ No newline at end of file diff --git a/masks/options.html b/masks/options.html index 77dd049..91fb6e3 100755 --- a/masks/options.html +++ b/masks/options.html @@ -1,64 +1,47 @@
      -
      -

      Options

      -
      -
      -

      Chargement en cours...

      - -
      +
      +

      Options

      +
      +
      +

      Chargement en cours...

      +
      +
      +
      +
      +

      Mot de passe

      +
      +
      + + +
      +
      \ No newline at end of file diff --git a/masks/procedure.html b/masks/proc.html similarity index 53% rename from masks/procedure.html rename to masks/proc.html index 2cfc539..07eb0cb 100755 --- a/masks/procedure.html +++ b/masks/proc.html @@ -1,696 +1,566 @@ -
      -
      -

      Procédure

      -
      -
      - -
      - AperçuModifier - -
      -
      -
      -

      Titre

      - -
      -
      -

      Description

      - -
      -
      -
      -

      -

      Créé par () le

      -

      -
      -
      -
      - Ajouter une étape - Valider - AperçuModifier -
      -
      -
      -
      -
      -
      -
      -
      - - - - - - - - - - - -
      Étape
      -
      -
      -
      - Insérer une étape avant - Insérer une étape après - Supprimer l'étape -
      -
      -
      - - - - - -
      -
      -

      - - -

      -
      -
      - \ No newline at end of file diff --git a/masks/profile.html b/masks/profile.html index 569d114..7b4a0be 100755 --- a/masks/profile.html +++ b/masks/profile.html @@ -1,21 +1,19 @@
      -
      -

      Profil

      -
      -

      Nom :

      -

      Société :

      -

      Mission actuelle :

      +

      Nom :

      +

      Société :

      +

      Mission actuelle :

      Historique

      -
      +
      +
      +
      \ No newline at end of file diff --git a/masks/raw_code.html b/masks/raw_code.html new file mode 100644 index 0000000..813443e --- /dev/null +++ b/masks/raw_code.html @@ -0,0 +1,17 @@ + + + + + + + Databap • + + +
      
      +	
      +
      +
      \ No newline at end of file
      diff --git a/masks/read_code.html b/masks/read_code.html
      deleted file mode 100755
      index 65ef369..0000000
      --- a/masks/read_code.html
      +++ /dev/null
      @@ -1,178 +0,0 @@
      -
      -
      -

      Revue de code

      -
      - -
      -
      -
      -
      -
      -
      - - -
      - Corriger -
      -
      -
      - \ No newline at end of file diff --git a/masks/search.html b/masks/search.html index dcbbe93..ad303f1 100755 --- a/masks/search.html +++ b/masks/search.html @@ -1,22 +1,37 @@