From 8da730253d1830661baa74b355ac8b1d58c77b32 Mon Sep 17 00:00:00 2001 From: franzz Date: Sat, 14 Jun 2014 18:04:27 +0200 Subject: [PATCH] v1.0.0 RC 1 --- .htaccess | 52 +- classmanagement.php | 234 + config.php | 4581 ----------------- .../51e69be6dd3ec278a14f29f16c4015a8.jpeg | Bin images/add_24.png | Bin 1362 -> 0 bytes images/afk.png | Bin 894 -> 0 bytes images/chat_24.png | Bin 1324 -> 0 bytes images/chat_48.png | Bin 2998 -> 0 bytes images/doc_24.png | Bin 1858 -> 0 bytes images/edit_24.png | Bin 945 -> 0 bytes images/error_48.png | Bin 1716 -> 0 bytes images/exit_24.png | Bin 1509 -> 0 bytes images/exit_32.png | Bin 2345 -> 0 bytes images/expand_left_16.png | Bin 616 -> 0 bytes images/expand_right_16.png | Bin 611 -> 0 bytes images/grad_white_transparent_h_50.png | Bin 124 -> 0 bytes images/grad_white_transparent_v_50.png | Bin 108 -> 0 bytes images/help_24.png | Bin 1708 -> 0 bytes images/key_24.png | Bin 1388 -> 0 bytes images/{logo_mu_e_50.png => logo_mic_50.png} | Bin images/logo_mu_25.png | Bin 3021 -> 0 bytes images/logo_obs_24.jpg.old | Bin 876 -> 0 bytes images/logo_pe_24.png | Bin 0 -> 1340 bytes images/logo_pe_48.png | Bin 0 -> 3690 bytes images/{tux_24.png => logo_unknown_24.png} | Bin images/{tux_48.png => logo_unknown_48.png} | Bin images/mail_24.png | Bin 1453 -> 0 bytes images/no_16.gif | Bin 1067 -> 0 bytes images/no_24.png | Bin 1107 -> 0 bytes images/ok_48.png | Bin 2487 -> 0 bytes images/options_24.png | Bin 1663 -> 0 bytes images/options_48.png | Bin 4472 -> 0 bytes images/paste_32.png | Bin 3874 -> 0 bytes images/paste_48.png | Bin 4377 -> 0 bytes images/pdf_24.png | Bin 1632 -> 0 bytes images/ppt_24.png | Bin 1790 -> 0 bytes images/preview_24.png | Bin 1473 -> 0 bytes images/preview_48.png | Bin 4197 -> 0 bytes images/print_24.png | Bin 1642 -> 0 bytes images/processing_16.gif | Bin 723 -> 0 bytes images/processing_48.gif | Bin 14050 -> 0 bytes images/raw_24.png | Bin 1100 -> 0 bytes images/raw_48.png | Bin 2603 -> 0 bytes images/rss_24.png | Bin 1451 -> 0 bytes images/santa_128.png | Bin 25755 -> 0 bytes images/sap_gold_200.jpg | Bin 0 -> 11224 bytes images/save_24.png | Bin 1580 -> 0 bytes images/screenshot_16.png | Bin 329 -> 0 bytes images/screenshot_24.png | Bin 869 -> 0 bytes images/search_48.png | Bin 3773 -> 0 bytes images/share_24.png | Bin 1669 -> 0 bytes images/share_48.png | Bin 4738 -> 0 bytes images/smile_100.gif | Bin 4051 -> 0 bytes images/xls_24.png | Bin 1889 -> 0 bytes images/yes_24.png | Bin 1061 -> 0 bytes images/yes_48.png | Bin 2487 -> 0 bytes images/zip_24.png | Bin 1537 -> 0 bytes inc/auth.php | 25 + inc/databap.php | 2507 +++++++++ inc/fileuploader.php | 158 + inc/mask.php | 231 + inc/mysqlmanager.php | 601 +++ inc/procedure.php | 274 + inc/reader.php | 267 + inc/rss.php | 2 +- inc/searchengine.php | 254 + inc/toolbox.php | 312 ++ index.php | 101 +- jquery/common.js | 791 +++ jquery/databap.js | 894 ++++ jquery/fileuploader.js | 75 +- jquery/handler.js | 1215 ----- jquery/jquery-1.4.4.min.js | 167 - jquery/jquery-1.6.1.min.js | 18 - jquery/jquery.min.js | 7 +- jquery/jquery.mods.js | 145 + jquery/tinyscrollbar.js | 7 +- log.html | 0 masks/add_code.html | 206 - masks/article.html | 21 + masks/chat.html | 337 +- masks/code.html | 330 ++ masks/code_block.html | 2 +- masks/doc.html | 106 +- masks/error.html | 3 - masks/index.html | 82 +- masks/item.html | 6 + masks/list.html | 27 +- masks/logon.html | 92 +- masks/logout.html | 6 + masks/options.html | 177 +- masks/{procedure.html => proc.html} | 1260 ++--- masks/profile.html | 48 +- masks/raw_code.html | 17 + masks/read_code.html | 178 - masks/search.html | 47 +- masks/table.html | 162 + masks/welcome.html | 73 +- readme | 21 +- robots.txt | 2 + style/FontAwesome.otf | Bin 0 -> 75188 bytes style/fa.css | 183 + style/fontawesome-webfont.eot | Bin 0 -> 72449 bytes style/fontawesome-webfont.svg | 504 ++ style/fontawesome-webfont.ttf | Bin 0 -> 141564 bytes style/fontawesome-webfont.woff | Bin 0 -> 83760 bytes style/lightbox.css | 227 + style/pie.htc | 96 + style.css => style/screen.css | 1835 ++++--- 109 files changed, 10834 insertions(+), 8132 deletions(-) create mode 100644 classmanagement.php delete mode 100644 config.php rename {screenshot => docs}/51e69be6dd3ec278a14f29f16c4015a8.jpeg (100%) delete mode 100644 images/add_24.png delete mode 100644 images/afk.png delete mode 100644 images/chat_24.png delete mode 100644 images/chat_48.png delete mode 100644 images/doc_24.png delete mode 100644 images/edit_24.png delete mode 100644 images/error_48.png delete mode 100644 images/exit_24.png delete mode 100644 images/exit_32.png delete mode 100644 images/expand_left_16.png delete mode 100644 images/expand_right_16.png delete mode 100644 images/grad_white_transparent_h_50.png delete mode 100644 images/grad_white_transparent_v_50.png delete mode 100644 images/help_24.png delete mode 100644 images/key_24.png rename images/{logo_mu_e_50.png => logo_mic_50.png} (100%) delete mode 100644 images/logo_mu_25.png delete mode 100644 images/logo_obs_24.jpg.old create mode 100644 images/logo_pe_24.png create mode 100644 images/logo_pe_48.png rename images/{tux_24.png => logo_unknown_24.png} (100%) rename images/{tux_48.png => logo_unknown_48.png} (100%) delete mode 100644 images/mail_24.png delete mode 100644 images/no_16.gif delete mode 100644 images/no_24.png delete mode 100644 images/ok_48.png delete mode 100644 images/options_24.png delete mode 100644 images/options_48.png delete mode 100644 images/paste_32.png delete mode 100644 images/paste_48.png delete mode 100644 images/pdf_24.png delete mode 100644 images/ppt_24.png delete mode 100644 images/preview_24.png delete mode 100644 images/preview_48.png delete mode 100644 images/print_24.png delete mode 100644 images/processing_16.gif delete mode 100644 images/processing_48.gif delete mode 100644 images/raw_24.png delete mode 100644 images/raw_48.png delete mode 100644 images/rss_24.png delete mode 100644 images/santa_128.png create mode 100644 images/sap_gold_200.jpg delete mode 100644 images/save_24.png delete mode 100644 images/screenshot_16.png delete mode 100644 images/screenshot_24.png delete mode 100644 images/search_48.png delete mode 100644 images/share_24.png delete mode 100644 images/share_48.png delete mode 100644 images/smile_100.gif delete mode 100644 images/xls_24.png delete mode 100644 images/yes_24.png delete mode 100644 images/yes_48.png delete mode 100644 images/zip_24.png create mode 100644 inc/auth.php create mode 100644 inc/databap.php create mode 100644 inc/fileuploader.php create mode 100644 inc/mask.php create mode 100644 inc/mysqlmanager.php create mode 100644 inc/procedure.php create mode 100644 inc/reader.php create mode 100644 inc/searchengine.php create mode 100644 inc/toolbox.php create mode 100644 jquery/common.js create mode 100644 jquery/databap.js delete mode 100755 jquery/handler.js delete mode 100755 jquery/jquery-1.4.4.min.js delete mode 100755 jquery/jquery-1.6.1.min.js create mode 100644 jquery/jquery.mods.js create mode 100644 log.html delete mode 100755 masks/add_code.html create mode 100644 masks/article.html create mode 100644 masks/code.html create mode 100644 masks/item.html create mode 100644 masks/logout.html rename masks/{procedure.html => proc.html} (53%) create mode 100644 masks/raw_code.html delete mode 100755 masks/read_code.html create mode 100644 masks/table.html create mode 100644 robots.txt create mode 100644 style/FontAwesome.otf create mode 100644 style/fa.css create mode 100644 style/fontawesome-webfont.eot create mode 100644 style/fontawesome-webfont.svg create mode 100644 style/fontawesome-webfont.ttf create mode 100644 style/fontawesome-webfont.woff create mode 100644 style/lightbox.css create mode 100644 style/pie.htc rename style.css => style/screen.css (59%) 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 85b258765e98da4a678c5ea71e4ac9020e993a7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1362 zcmV-Y1+DstP)P#HoxKqp~qJK+67L6BDxqLm8HwA7 zkBAL2Ocu~33jzbTLZ*w8L3uhr>|nr3d5uz_@9x~&@0vS;CN?=~?{~lN{C?+rzjIC{ zc=3;feLG)d^Ko0(IZUO*22ty9M1xM9)RT1B?L;P_|#^@ zA*&VP5(&KRb~nt=JDhWKn5DJ0`FWHzG~lPlk71%SwxuA;0-Qb#k9F&C#BRsgprBa) z=;%VMUcHUp_>tMkOt4r+(cXRwckYxzub=Inn81;m8vH>ott=wvvH;}Vo19np_yi`$ z$2VcciYQt&k{QS*$R^3=8CfZrH=3KPQBiT(Y%t)n>T2BN8Mv4@{|9jLBs{{yag*47 zkdW{zb23LZLN?CMIM{g$nVIcz$KYTm3JbrnO;6*K`g+`=#YOIb7(k*@;i#`~;Md8? zqbyR6^`DF}ZLMeo`UosB8rT-Xd{`N<8#+2#apJ^xh9^(3v!@6B9D}C-N+n+Q_s2kL zYP-Kowt+eCW$AG_fU*X-TnU^?0@B}Q@2o5u2Or7t<5$qyI#k=(h)gbg0YFk#hA%=x zV~h6fQ`6%V*&xfgojLFTF8l~wyWs-(XctSLRb)<#jOdY`UTz&8#!jsk{XD?dt#DT; z(2c;%h5>Xk=Qehji=YlDr#n2ANC0sRz$S{qrP6~3uV8GY5-IOaQ1!RKpeXpD|>HZ=0r|T9a>t3ej$LPJV1Uv*30D> z$jh5$jx}_lKLfgHo;aM8Uw4xA2<5z`2?(Zo2wF+luC%~RBvLm4`#11+p;SIVRaI}l zMuQX%K$4S#O>((=cR>LsMIBiirJ)|UUgf$wZkH!7dDm|{BW-6va$+io1U}!wM|A#t z8`VXhwzn6lJiy_@2n`5;wzznRo{2SnCkz7Xr%2o!E>FJhK-pd3FUtD?V*2JQly)Xj zYEak`2%^c~MSgx8Zr>j6>FYx#50H=mzlaF@tyJEVdwKcNlA4#Ci@u-`3L&EexJMC| zyiK(bCAtHl=&YEWG$JeOAv7Ap4^$6Df=oTKve1y0mb)u1PC<{2B2|b0(!K@mlfu4? z1wPzNYYR(cYC}_#4%yinOidwMr-NDmaL>p7N!_SQunGcHq83krlaj?|Zb=%pwq?pp+%4C`F{S z5^5EPB1Emi1yED-4+;wUF*D=5Fg}j!v^Xp7MIQ0-*iP-@u2Ol?D=duD^r*-IZN2f3 zF8N=QsPJ985)=GY}_dG+`T&lYuCzP zG+Hn)Fh@gX3f9C(3CKeD84ZMPy)YmSf~tZe$7|USzW1V1(1K=XG=oFP zxNsK8*#N0TP0U+qZH5)a9FvyNzn?ku1rTl~a+T-DzP;HRL>gXT&KE?o68>f2KNK%H U{<~xC>Hq)$07*qoM6N<$g0LuvR{#J2 diff --git a/images/afk.png b/images/afk.png deleted file mode 100644 index d98ad588f8abd3cf580a1c83e1591a389111ff3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 894 zcmV-^1A+XBP)U7CLSaO}rSr+M$7U)l%MEk`?ddFk_QFZ5o1SC{`@EwNLp zBRtTAN;(?mP@82({ni)nK0EZWrpPXU*bkqbzB85g;_uI{)a0<+;LH<;NShKN4L&_F zOgbT$x>Z$kTfq-MI=}F%<+$>HfXR#5uj{_9iG|!cr!sxKkm<%RZS#1towaIgU6akt5)9>Sv zz7S0>q@BOM_NG|evX2)w%N*&95)XiBfGS|9qm7$|3PGhs-E!HrY=i_tf=ri5soCWA zM&r0r*m3%;T7xcgfPeT(PWBLG|cp!`xJNGex=rM zF%^O1IB_y0k+*(ZUpO%s5RN2xYAA&!@70hba9iMd z5Ky5f4A+)dxbaZqQf}LsoQS2xlLPUaW8L=mH;NJ}O|E9|Vj3QXvIo%>=ncbY!ecI* z<#ttKrLHm-ZG1J_+n$v?&!g6~_2<94GNq}B=Uy7|kt-GHjTrPt!$`o|c9r`zn?$0E zxq^qanZ5AKYmdF`(-d0(1a!4|;mp{J$w>Lz&;H1}53Lx!P=W`xM#3 z!R@s2U+#zh_r`r)TP>x}UCf=nw9*(~tGX#o72aT6&mBvsv#%fP{o!abws_G0FAr2) U$Q|k(1poj507*qoM6N<$g4qbO7ytkO diff --git a/images/chat_24.png b/images/chat_24.png deleted file mode 100644 index 52cc1b5164938596b8f4e60384f5260a78d64b7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1324 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 diff --git a/images/doc_24.png b/images/doc_24.png deleted file mode 100644 index 6807539f4bfafd8144b890e4cea564a2a4bc2557..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1858 zcmV-I2fg@-P)*Szs5IH^MHUySkOCyb+ZJ6T~>FwXw!gAu7@`iB^(ntC^ByGLxFe zjCB&zP7*VTrk$}`t2G2;Qw*&N1{MsMSwsjAad|BJ-evbfzk5}Z%s8E%yWiY9``vqf z-#P#9oR7lyOs>`%wbkwBNR(yP^#3>MC+i}s-TdGAQTEA5>gP+6nPdv zARvSD&;JO7+@l#0PMv)JkMF#8VD}FIil~Gu3&P2tkI9$3hjf7|c!m}X)2*OVQEIWq?8M9$-aLzeM7)}BhzB>s+k`hyn z?l=GX?o0a@0I5sMUpD7$`Heaz9{PktJoAJRm%3(QN)^KE3&Q2`!|e&c9|(O1Agd3P zNFwAOoQ=Te4-$wQ>3TT_c;mezzdo=4u&k>+Z(3gQxInBzd5ML56oFW(M6{A<$$C+= zG?tf=GnyotrXR{i@ftjw;~u!jPMfW5MQ&~p%d!xQ#ROojZ%Z{66lh2Udw-UPw@>$B zLvb3B4e)!Mc>1vnPzzpuXd$wCmpj1g!0D2bs3Cex-OVx>6FnZ!{QU^5zu*BXO(JSH z<={_$v!iT9GD!vC^|(>@qya;t_k}j3YS4Le6sGJ1T)94kfx)}z8)D)0&tY=Xh1Xsv zK(--f&TKL>gLm%uc)+sqeP5&)3QOftiP%|{fs>!wVKT&lou?2ad9T@!Lh>6#Yx`a7 z*=oUif9b)R{3JAgVaE!K9?e$<5vxms5I$5E>k+3_!fGuywf?L1YaU=;S9h5J%4Ccl zyEYKOg_|T)Ef3&?+-glk?vi92dAALlDzb6(WIwD0dR)8Si-YxLICbtO5t+sKgco_s z;!thP!M5$&t51FW@!xrXY3-ht#*nQ}8Y~8pVy>~*G7`u%>G*!TU#{pk} z#k4cPZ`Z2w9K8R*iK8_;ck;zX<&-^hF*&2CQpUt#?~^$sf)1$E8VG0!56RREv45L| z-!=7zn_(?VCKWA0*Eeqbt|1qPUcZdCE*EBeBFwr6(bH0o*0wJ%l$DfL&Ckz+%3fW2 zF=F~%H09NKFjd71TQWg0XS6B7~2SXy&2A+4~I1JrL> ziu0|vA(1fB$Tl zo{E7=NzWIo%1i3%>KdbrB{iSw(+Vm$z%QS&pyk>KiGTwrAEZf)pX33C6bA2{^MH{? zpcDm2E14s$Tnf2VL_`k5eo|HC^aNaDiQ_JK_o<$)`t?a5^tY#!8YIr z9dbe?bVH|}Lu!H&nQ1XFq-!A~;&aCBp5w=lH|?vddy&d0+;u4~HE%5!!LN3#LZ{sg zg({k)i9{fgLMWo3llx({$oZZl5mWrh_P=bNT+Pj&eYC!^@@XnPf5+##a=kTLfPR@|<$jHc$z2Dy5)6;XKy}hHOtFyD?)~#F9 zRI;h`uvlYxLCM6(j=j1Mfj|I(h=}d$@3&vSe(h>c*Nv-mXtd|wEoaY@7NQ{sNqBU0 zbjWVE+j@I@ziKDm$ad^@I2=Q1t|C#*O);{ZX wJ0HHpFpQwFv9X3Y{gybhjgOB%(%GZzza*;YQr^Eo*Z=?k07*qoM6N<$f~;J6!2kdN diff --git a/images/edit_24.png b/images/edit_24.png deleted file mode 100644 index ad93f105ecb0f41cdcdd43e69f3048abcb3541a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 945 zcmV;i15W&jP)h`wVZz5Jm!G! zS^3HHY|)mbj?>yt+J4k&)txJ~`3 zM65-foyWD3RoH%@2uVud)P}Xlv=v~+csV>K-N*wJFQ<40AFc&@1Snewp4)-5PgY{< zfvZrD1I}#v78wQSL9&y78Bu`Z|4@8k3=avAG!aO7KoXXq%qGQkl7l^YMNq;A=bB`8 zNm;}?>;T0pjN3Hx`EX4iF5v(%Acg1tWXK}{)HwWjsLn(V=VIrwZPwm-!wOK`Y22Zm z$%m>3;`Yf9g8>l2gNZWecFWm$#og97a}x$AUiZna@g^={kBf@}2c=Gn<{BCMSz(Ly z?fF9n9_N|rQg<6nk-$A5$j8N16!%mz?1CF!>$^tWfb+2FiFuE~9HlrJ0#|A^Xu8X= znK#?4gE@l+9EVb>X6#G0B*b+ZG?s&1RMKvZccB9U&LgIq=3RzmiEtIg8~!G2)~$}X z_<(?Y=d?d9N2jcd1}YOimioU4yR>X*ykDSj7S?8$=3kKM*Mq#KKTjjw_`z}a9g)qx z-I);Y7pPcos+#{tokjE=2H)^Km=Njzol{&vAB(G0V3+;dH7vX@a48jYvVK4M&|vur zc-;x&-HNEEisDlshUo1OjxJ^(6`iC0HR?UO%r zDk0hJMiKaBz<;*;@E}EE9&xXtSu}lWj>>s`;_0LDlZI*8&iP-yoD=YgBXRO7wNflC z8zP>zV!nmy^wEY1)Zup>j8QbEQmLquZleAM7*#BZroW!2azUTi`_fQH2@Ze$tNEm_ zAMh^@kyo;4sbq)}=?^GWS}|$*^t^3}jsgw%1AvbzR0MoQw;C{EKO}HH{Y^$lB#sQW zl;F=#pIWHFAC8e1_j1Vf`tvH2XrM0f?Sd_dwgP}Z2KcF6`y&*Ms8lH3hF=R6sY{$$ z6cT%0h^>?0&;5EjQ-ePg74X?L@b$zcLUoBV8PY24C0YtJ;GYd6=T*u#fnN(3txKGR zMA4Xd>QJ;H!GHhl%wp0PWU0h~8vJ4t__Y$eWJ^^`#A_$fRG^1OV@mWU4&5 zCrCrX9#PB{!)EWYDxq8E zU-Q_lLSw2|PH%qPN2glSVwIc%<~^ zoqeRZl#r#`dnUf&4&I`_|6CzmYYuOx8x<0pwtHPkbdylu>Bi+sTX?$z*_fhA>Cs12 z!ug)S>m2^7))C&xR65xI@*ig8Q$q+ZcI@q9p3^8rFZbNe_?>r$rijL zfw}~P#PB9YOxb97qPZK+;2jIBl^7b9HcSH0G=B>{mEjEDl|Vy+Q92e!cgebk&tPnI z1MhlGj1*J=9>Uwp7-0>UTT6Vb2r4q=WFZiA6T?jvN(H=>d}36h0*3h2T`3a=T%xUI!kn#%%yzHOl}WUdk%l6gd}>WD@8A@nC&InO{Y^n*-UQh ztDwQI!r#LU+c0Lo0VdP66)qvSlxVe}N%aAKt6J^~-df%GlU%$lBv2B(i@jd;QN?O5 z(QHk;laYDRuNTRSC}qPXJYQI|fVlP=C49)8gddF>>l!MuAUNb5khs!mB%U#lXBCr? zrfaUM0Ee%70{lEKwjfy`?WqBq2 zm=fTF0^U=~PV$3a^LzX^r|~!g&kUraV5>UX*g{@TDdhr5HcP^iWiosrejZl~naMkr zi2boK$u!ffyhJ$Li>Iih`}#TNaln7!YHN`!Yl!%E-m2yX-iK-e1I?m#9aDPgG|GJ^vC z6~KSK5mN~vAr(;k)w?8Q^jV=WUl@$b3v-ips)jijhlG)qbG#&sySC%dV0mWPfOk5X zt4S>3qjd?aW~^{Tz)N$pla6|-`0yAO0ck0h6^nryya(|F{0YE+(u?V|kRYCOIzAe$ z!P6~g@V7THYZA1W@UI3B1iDQB3NQeeJTTd6kvwAn0000< KMNUMnLSTZ=_%X=< diff --git a/images/exit_24.png b/images/exit_24.png deleted file mode 100644 index 5e07040f03bce8ed3b172aa6b9aa320603ddebf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1509 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 diff --git a/images/grad_white_transparent_h_50.png b/images/grad_white_transparent_h_50.png deleted file mode 100644 index 859c9f3ab06116a8d5bdafa65ee7ab74c2d54a6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^MnKHU!3HGR9riB)QY^(zo*^7SP{WbZ0puHdx;Tbt zOicds|NsAbHnz6-fIsaXvqZMmOGrr6DQ&DbFfjPDc$=*c&taCHs+0|liH`*t_C7V? UPWz;>3aFjI)78&qol`;+09I5a&;S4c diff --git a/images/grad_white_transparent_v_50.png b/images/grad_white_transparent_v_50.png deleted file mode 100644 index f517bfeff9c5f0ce24725b41180ded1c295476fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^tUzqU!3HF!2nc=vQpTPxjv*Gk$$$R;|6k8$a^TQ` z10T9G4%#2~d#X?(bT{PR5})7vIZgjHjEe&zDT|1^70BUFOboFyt I=akR{0O=1Vy#N3J diff --git a/images/help_24.png b/images/help_24.png deleted file mode 100644 index 312d0f76c62e762ae37af99bb1acb84d66cdf2be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1708 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 P00000NkvXXu0mjfO>xf) diff --git a/images/logo_obs_24.jpg.old b/images/logo_obs_24.jpg.old deleted file mode 100644 index 379a7ca40c9f84d4f3ca4bb8e939f988d52b9e46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 876 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<ECr+Na zbot8FYu9hwy!G(W<0ns_J%91?)yGetzkL1n{m0K=Ab&A3Fhjfr_ZgbM1cClyVqsxs zVF&q(k*OSrnFU!`6%E;h90S=C3x$=88aYIqCNA7~kW<+>=!0ld(M2vX6_bamA3uK>uj2pmT$TD^b#+y>XZ88=D}VWVrRvVvy}DUq_62Cn z^ytfvCkO2*$)0g_>hdHG9V3pjqTVk587BOj{huK|u>M6-*Ds#FqN$djtzXsLtLHej z#4>b?`Bz!ZxKF>_E^XyZ%kJz*ZE{*WtwyGg>+$Jh*6+?sERz+w`%Lio;#bOr4`)d_ zHGfK8|6%@|d4CODMZ$y@`kr03pQY4YFwoKD-rbuzQT{i#&Sp$sdXIN{=d)8EECUw) jYI0|mcy71zt*c1*^68g*M0Qrs^Ggj1E_n8){{Kw?*(Xd9 diff --git a/images/logo_pe_24.png b/images/logo_pe_24.png new file mode 100644 index 0000000000000000000000000000000000000000..63fa4a04b9619748913a025f4a81ed44ab310b0e GIT binary patch literal 1340 zcmV-C1;hG@P)qn%6Z3cO7PnD?SY+<^27B7!3pJ>3IWR87yzaBL<}YgTS(N52%@GW zh-J9ey%Fp2M~dnK=dz|84?oZ1zA}2;AoWP zX*Z-p^j#5W7EONflS7v`z75YCB$(6o)!6PZPS+P@X96;q0AdDHI`05sVs$=VUNj+N z1JQ(mV?BK+&dox7)d)xc@x8m8-X~gijp|E2I!K_c-g;zwQO^DO0To05GiQj9zCAIt zfP#-#&qhv`9{}*_&u8%2fpZA>R0sf}LJjL5Ye1ggk1Gw6p?5}J7-L*&ukVxr+_CP|%#L9xf;62e8w(RYI>sZiD1GZ(NGu)3Y?I*#kz&EMc4j6LH zo?wN>@koDVE?jLO6EK-Fkw}!x*11XnlVS{|ght~ou1DWhp&&LcOVu7-G^{k=}rGoo-z zz_o@3dD!;N0rbWUn2wFvjpHz9+Su#-OcVwc9NV@Qjem7j2$Ib>)hQJKC>hsyT-WVo zBqd0SA))Ig`eO+!ojV1F>0rssdW1^z;a3T@m8E!k$yB`iN(-{Oy3N!(ZFD&&Yvy)4 z9v`e^)}&?M*^aw#xcML)4@(y`p><6Q3iES71gNY)ND0Sv;d-v*dbyHj4*t{6Mu_5Qd z=oycfb%&j!t|#ug)hv}#0qvkKP9JxXWZ-Epd|LUI3ytGFc%I6h@-lp>%`;0sKRg7t# yU;1a*y^jD5o^w9lv%doXLeH1b#kPnX?(E-3_jWnIiJ8#=0000vZ>TspY*pe^mW`-5mlX1ZMuR ze&?O4U%gki>U;0^F7G|~>rp#oh<~o^vuD&T!bvmb6M5-JeK0%tQHge(P7bJmR`36A z`Rl`0cgqMd#t%FRh8Dv0S4^DNS?xdeD9q9&Xas1V=l;(C96J2(WYy;XRDi3WU(wzb zaEdkAQxv8hk!;paNnN0V05y^jbiYv!oCu8Gz~2ha~?4tGu#(4=Z+;@Ql9QtSe8*K7LjApI;0$ zSamhR_w^%N;H>+fpS`Zfd&U6M8Vigl_{oO=n(!l~NuphmM7tvCe-vYS`-DCK5KLk~ za^kUKPl@L?ccRX&&j)KyUi{7T*UzL~p687N18{iDR9Uw3HNEVSXQn>0!C$Hbj6)Op)wn}%;} z=T4qr3ZCw-1?22IA0PR4r@vCfE6f^HAn&J?=|cypX;rHnO7DS)=>>@*0xDpwa@ZmD zOc>U{@Wwg@)cMq944&~&RVr~z$DV+fHg@vzrk%_0JZJh5fGvQOzYN^>UccB2fG1#e z%56`q*;#huPHibrpYc;|_tkv$8bIFz3aVqY7J=FZ*Ky%-<7lqSqS~wZeI2Ta%sp?s zwCbJ9&OGnRrqf@0@rirviS{U-V~Oq)z}R_1<;dYG>*{`e`^Fx7#q@y%hUK!jDott} zRzwui@Yoknl}6N@S;_kzSDZS5!-h2_d3Ud`*7wn-s^ZkK>7p|$(JPA=j$gL(T&R}; z45X8Hb)ObE^%uVz`qrB4`bOW&9opMFtrcEcDY;l^&hN3jguKT2^lD z!m5zZ8m6@lV$LzG)MfpeeoZb@*Kql@#h%-4X*+)F?#DYS$W@;;{iggfg-dx!>eQMqdGL)?RWMcQ`YdGsp;o*zb17OQ0 z!bzY1cDZb4j&IH(syOGc)}?1ngjt7k$+_C+oK44AtaCVPX&>Fp*FSw^e~zY z{P&U$#0XB6*eU89v2~~_kt(mR+eLF-mbRfyUm{5`*E zSlZ3HjcVF|dWZm&OTicO&f=*fh$4b0B#0uyFd~d1Dq%zr#ROsXUYxYBdM~*SqnIz9 zaA+E7`g*9adfOhJdb0y-BZ4p_2t&d!pd1DSQAj1dR|+G3x^O8wx+}?}7Mg!N-*d%4 zO)L$=x;p3b)JqP)ym8fbz4G@@jqdI$wH{Is!qD|=K@<~2Az_rhFa?&pFa;G^f+(UA zML2a#8ribX@Ob+lmrx09D%+;5dL0u+Az=z4iXFdt^360JDBk$P26#h81nPur5dT9W zv0i+D#~xdHu`7oZGEG#XFwK~PXrJ1K{#p1vkw_9s96Nppb-5(gR~=SW*;6dDXxV1! zbD6~R(&tQ4ucqZjf0=Kt-$^C1WPC44h?6Ia-S#3`Cj%C?w2nDr=gk{@fUa&k$Hx-J z5k`9pv06gvE4YudR%=j`zOWKoK0B=ym&SLKMje2cS8u^tM)6aSu@(of@-OqwVT3wU^{1JSkk z-}v#b8l1)RvW{{T)wC)|S}qIN-j^AZ<^=T3zH=Hs$gN=}ugDEX{Ub3;C{qJzw+5w?O&i08COYd!wDoqh%v8+C>#c4-xv%C%an z+cinRXsm=mL>$FvWKk3`2H*3kYs}SV>Y_j_sDaqPfcgS+j-9Yqy#P+qk9WNwG;J15 zLp7*6WI$umXM|;q1D~d28-6+H5nPOmp&VAtYx*av*b$Y=*eJ$Xs`lv@VFE(LhP4U3 zR!2BRoZuv(V!z0$;Av`&K2g_-;!to3Du^n^6SBVNS)i25UNd*wlFi)QDgJ=^x>(hz za~2mzY38i*U(rg4mct}7%f5@S8i>#msS>N=8H3R(N0eP%J-ocUBcY&5O+r#|Yd^Uz zrBDJ)s6-JvcK5J)$;uU1)(58?Rou}%x;`r#ZXBTgSLd`<9)9j^M+A~_sHCEuqNMPr)5}?ke=ntS7;g2u8fk_K9SF4#|+JxZ{y91NguK{uV z*5py6jKLF*J637{Mh(v0gSBWFCoKxWxuhM41w7%NM_$C?P%Nqrb%`}jnl^!4&K$5s zQ3qj2`nE;Roseu;e!6U_EY`w_S7{JJ>i-}4$aumFcU?!@ zh{3gz1R(2s{QCn>bImOefbsTInu2r8oHm|?3oZ@U4{Afi&lc-*7arfU;cfu4rLtH7 ze4`IL7oskPgJ@A4^5B!NVvNBm#Oko>C`U2pefBs;3~txs5Yv$k0!;ifTnJl#xF6SVQm~wb4cYgm;MjkQ{bx?WiPsQSem)8qW z&Y63U54K!@6a+16zo0zs^sCO?u(|sY)q~kNs8ZMQm9tLdwi_-WirssL)a5d)dUqSo zy}E+U+dENU=%6N!nKG9431cXh0-Rb*A+I}Te5#ujYntM$zi4{6aW(@H?)oy!xvv*M zgP)f{XHWIFubKRO5b5;9oCj$KRnC|_m7jm_5_~VyXAP;hKgqetoB>UeAS81M6`|*j zySGL+{^QU}rg7C>hqt%?c1K6BhefclS^F=mteh)@&;BhVCx2t&vK71Dj-B#x9BO06 zjULW-E<1<0pPNlrv5a+YFAyd@yWXj&Dt7x$yX*MVz1Zn@K6TPL^UKTcats|0i(lUN zy3p+FMV_6nI`WiD=WgEGb)Vzo0U>|}A!Ei2<;E+{lg2Ydg1aS{2cwjwb3z|NwPj#u0xOe%D-*hU7RN$*VddQ zl?3X@7xFaL=c&tO88o1ghWZ>LLaAI~*X|NMr3(3ichQEY?zjuUZ#`Y&jz{-0iSUVRFea+3mI~ErrV+9^^Znq>2fm9xR*2VqAPx4J60LQ3Af+vD z{nzjbGxEir;w;5|v@+nsT5Tqy7$BdQOHbVW&`;KO1FrK!IqZFqcIrlqlikaoezK*l zeVL8y9BU*0pcCCb6PLqBw=_2C=X>6Gaz5BhGwa1ZUsQmWNpp@XhvCgZ z5T9lbB3Bp{G8yk4&(nV{t$E_@-r371v`>5_UzOL7ehl8-0^88hv(BHivt!#0K^)I? zs;$mx;e+0vZUlB4Tt_xz{?t^+esAmY$9K)H#~dG_3$9i87y0A>^n2(8L|;f;Wys;D zukm|Qi+0ST_Nwisk!V^F`e56WnHP`^~C`otrJJm zv0|h0|8dM?Ua}?#@}n8q{Zqu(CyN<3Ozv(#PG0pH;jah$FPC>P3KH+TdjJ3c07*qo IM6N<$f^K3hJ^%m! literal 0 HcmV?d00001 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 a4bf2e30d950d7fef5f55ebbd10efb105ec01132..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1453 zcmV;e1ycHnP)8X1&M&#v=pR@ zMxtUM(GMnq5Qw}wiiRL)c$IEjXuEx2yWMWPeQftRvooHV?viZ+#xFX_$(cKI=l?tB z+;i_R_>V8<{{}Gsa^3@V<7|qeBr*(Z(HP#MGMpJ2YgWNpBC49e2?-`%unJH{zQ5M&*=+!(P6St+1$%zU zx>*4>4b!JifvT}aC^Q?Kz=P+=cn-W^GCZfh69nQiU>U|33p579;oyONa5Ww~bDDEK+V2NF}LtV^fa77(Ax`+(a_}R#_>P=Sif-_a%N{E910ob(jnn+7#52a zKmU3Oo|}j8WL`340<)}!ipn~vxhD=V?_t>Y)a(}LE_fc@_Ol2KyJ#WB5OC z9z4CG0;MHOC=%L{-2uD%S1!Q|j!G1I$jN5>;*$=M|OWFZ+1{^n#%^;AqBgSFX{A8lGA> zk4g}ttn^4_XnGfs9XWZOAXXISq(LSK$3#$8b3^L(!~teap$tCSEan$ILjYRjkv?!d z3;S&cnp?ZDymXQAv7&@YSZ`74e~MP`7?9w%r*0u*su{&|CK+X2TiqZzhoS&00kS6( z;KNN~e&JFAoP*+X!!_W+Dd{raSXBx|mZ?Kz5?00m^f9BU1d{R1$!5%)o`6MpDR{m5 zj?_021DGJp)HB%imRL}<6unJ#@VGm1_=h@_uU=-fYnV!7i0em#O10MafjS0c==?Dy zBv8xRaP*P~1=*9Z{gZRjUAH_2un-_?5&?F;DK02pg3gAs`1H#ocx^=qtY!gnC_p<_ zG0t9w46+5>{gL>T~HzYi|=AR1eHa7=2G+q;~n=*Zr650IInXRtvmELw~` zRqvxXFCAGkG5|giX4^z0Sdyp}O%%C|p^;&9bUAUW$${&Q?SZz=KG(ods5KD8HAO>% ztfEtnV1U{@I1W&=yF#?2WKh%`9-EO4e=q=dp9^h{4%9a^s`d7kkuInE?(m4(NJr20 zkb>KO8NJm0UXq~{X(7fecCQ!!s)Fz+twGtV&x(GT$Ec?_)O4q%zuPtB@cMAWA4dIX z2o5hDMYLJN@d9yPjrvH`^syHEqj5RYu`)Rk>F!}T++MgyY(%@iP)tCH6N;G@)zf14 zF#tz*SMqA=|NV%k56-S7GvvefP?W6Es;00000NkvXX Hu0mjfdUmDM diff --git a/images/no_16.gif b/images/no_16.gif deleted file mode 100644 index 130b7e2d214ec198d07aeed5b462597670ce85ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1067 zcmZ?wbhEHb6krfwxXJ(mkEP`wE2%uw)_JL`|I*0xmA>H{Te~-Qj&Ge@-?@2yaP#q-|J%F3_Rxe-{K>+}#h}EX`2UZmvvZKUle?jTp%DWE(}Bc-f*dBG zow<1>ML=nC1Z4Dxd%W(m+t+&A?zM z%fP^V1|b$z%)qc_83Tj38bVA%f`LIaoPlA7F+%KfAUIGM#m*F`83O|v2pD;s7#LXZ z{r~@2je&uEG6Tck3IG59UiAO}-^D;X?l3SM$V)CQDg&zps%EleU?9YYcn)GZBoaXO zKru)djeQ{x8~`901_K6$g&qtH;am(1-mMG_vmP=qsIOyS5Z=bX5Wvg85Ms%|Wbp)s zA%O+fC*_=&lbKYMSdyBe07@{<`8oMT#RZAUsbFd95hbY=z(`?WaLzBNEXqvJC{b_( zrms{T1<$->Jskxj0|N_S8UU#PWl{#Va)*fp9y&s@Zi!1@T*&AVKb?HzNr90|SE_R$ zkMZma$CR$*sl_c)Wj+<77G3hG>0yr9EDt5ko*XSv$?6&7Q x$trdMc>z|}sX`o1ti0lUEYAEw;`3dYdHC2Eow@iXyE1KX-RicnrNxoK8UQ#p(>wqG diff --git a/images/no_24.png b/images/no_24.png deleted file mode 100644 index 217dad24b4e796ae60ea9e520e82bc6de978c255..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1107 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 diff --git a/images/options_24.png b/images/options_24.png deleted file mode 100644 index 993454d33b55d25076cac65aeffca85a740d5337..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1663 zcmV-_27vjAP)75ve!)CjDrDM6F2!Yli8W~z4v|Zdyjxu`w=Uy zEAQzw=cOOMH1PI!-j(R}`VBN?km6NJ)3h5!QOp&DLgB5sxw(sdeSLC#*MNt5Wq=K` zjlA_zr)X1Zv~UhwOuRhb0BdV&u(GlOTU%T9-onBHPM!JDZ7`cNu6n`8-b_T=J(h{gtD$r;&U@#b9W@d%~7;kH93!9jj7{E`} zYpYlbKm-Q|I}rq2lhAkl1Wu8Va_S7&t`oz+zyK^RE&@Z1Kp=qj_I4;KDQWEL>N-9@ zKR+TCi**u-MBmxjDPO6}N&q4>G&BUWE7$2M>yVHTIC}JRP%S87baWJSIvwzMJRk@j zR9>qz)Kpg&jE|33Sz229AZ_8BH*fZJad8pW*49SfyLay zUEbVx?bj;;rBVsAv$IfER<_jA(o%?2UPKnA@H6EL0PF^#kB?6QW*?iBlmyPs&M$%k zQc_d-~`+m;a49s0Dp?X_jfL(Ln5(fPcJ~?~{gM0_x-rj)i$$Eo1 z$OKeWR8ZyR<=;+CO)clC-mf&DXXLZgD45Q(?;%ZKWE*ACnP)SB&*!o2 zOXF(~yLzGidLfKV%!0Al)j$e(%HE#t;;E?#Cipzls;dC}{QPXtrD>?cR_uZ&C|apj ztNkV-BElmfApvk4uq_F!*=~SH@i*+=MuOHr!HL{EV-H5*BefQ$nNH%`bm;|vudlBe zNs9(sK4uOwk2se zm-a=3=|)F$crxA%Bp;yZ;WQLfji*jNgM19TuxvCUFfj0Aq@f@_J|3{Ops1)w6%Y_$ z%7};NgLCK3fv2Y@U0PaNJvca&snu$j&f31t9P%!l+{5STra?J70VD}wRq zTZEQnkqLi)|2Q0UxnpBvbFHnd-wzHBwmCRBTun?&G{frHv4MSFjL&niGKSxI98Hwi z6i&$;$@ufh?}L1@2WXuND4`hkr#ENMsvwimfdxSK8ln4q6bgj~Iai_!4Q_63KceU% zadC0%9zcMVBcO1YXw^)QCA=nxTAYJAt3RNg|ipg2( zgjxsp@jnHV$f{-T<>lpuoVTDB;$6FT!SL`fVAjlHQ6P0uxPQCgd50`<3x(hkGeLV; z;ZzsU&-Z|et5Unl2PmYqk&#g^a&O(#)MRLEY#hMsYtRojao&t@!Po9y9%o9DyE2t& zS1p_sKLO4a8IIfc&`^V@2~(zq%jOy9PG;{KUNQle<59~i^IvLR1*7NVSO@?B002ov JPDHLkV1ig|0wMqa diff --git a/images/options_48.png b/images/options_48.png deleted file mode 100644 index 5bc6bf0e2c56800054ffcc65616527cee090fbfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4472 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 KMNUMnLSTZQPoZ4^ diff --git a/images/paste_32.png b/images/paste_32.png deleted file mode 100644 index 4bd5444599bcb7d82fe42e459866abcb39ae5f74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3874 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 diff --git a/images/paste_48.png b/images/paste_48.png deleted file mode 100644 index 41227c6b2838ce4541aa11d379e4feb26661048c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4377 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 TOn{%900000NkvXXu0mjf-&Q@2 diff --git a/images/pdf_24.png b/images/pdf_24.png deleted file mode 100644 index a06937d3a6ce4708f113bae6ea402d05ea0a35c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1632 zcmV-m2A}zfP)NEv6geau zLP(5PFp|g-5KtOWC=^gZ!y##bR(KpOE!}tS?0n-lvr9oYJNstm?fif99rFzX|4OCA zM8^>&ED%sC$bgagsI1J_H+c-i;a{v#qB=eWPW()!;$3G%b?zR6?#p75!0F)c}C+AI9)p$^H;9n zSzwWXs~3oLbEX(7M6Q^IP*0gL(F54bXY3^Odw&(n=H5IK2NGu_M6b#)6p50!H%1odak7BLqIRgf+z0HQJA2Gy$>?!8 zZ93##{|96&j(=Ud!TZ0r60$Ast@OUXwJJAK3b^MM8gpGvBjh3NK+a=iK}C6~2XJ3r z_%1v3>oRY4lY^1w18#al~y9R7*UCsRy&NdV7iWl zaZxO0hbk+4fLy$wK6>KV@@(bcnXyko_Pj-`WbPy-gBd0fn3T#MH5M}MF0d}0XA&&! zbf#@^T?x{TF}Y)}5`&9LOs5L4xCT^~d4TSh=GNai)Uf>K`0-G%>N5zoUd7z`lR0Ge zri>x~NS7z3&I0^tKLqczlMfgtCQ_qc32t``+4di}JGf8-j0N#{G*opD5Scl<{tp+I z58Jc}vaB?SH($cMh0_c`CkP0^au)YhXDh~h*9iigGM?oOB56A=im|$rNg<>S7Tp>Y_ zi+b(ctyEzl=}S@omQ2L%13K1k4qNl)oKq=BZ&D4wyaiLa%*X^CNpu{3_wlD7qo@Sj zjq4GuAQ#$qWC6tHONNaD-Na$|4I|T3GJ< zsR{|8SLE6WWiNRh0`0d!?b*ZJ_6{YqWr8*~QVhDHEbrhxrHTsmfC+Lk6${mb?*ST{ z@@v9%5kWH;vmnvZj9GK0a2X_k5ZD!Uz|kcO;t3qo zu%D~qA_CAN$ppvIfQoy76ZzFObrGbL^J379!5NdePE%>e@oxPJ&gz$eGi*4hnmX(~ za~8LE?nD{6$z?JL?yh|p`u-aZ#l^HIq}hNPP+L=ZA3(*9Ttqfoo&{;sCUJ*$eP_51 zGsis2MdNW`?VaFWyN=>c7v~cupj%qXowinxNr8ixF6K~HY5=sedCG~0>S}$!v10_N zu8p`TD2ptJw=^^H>_l~U(({lpb{u0%UsKWwf}o|*bcQZvkK;1wO1+W6l~Go5xIwX^ zA`>$m1Cfe{hSpWx2ULZ_5oyW+gT#dk*s*mhhuS&_ZrO&Z+Z~*;i6-kbSL8`a1xo3p z=16)9Y%FPL;EZB`+C?;;O2rCB46lj!faCdQ6uZk9ZH@z=5AbqehOoo)Rf@LYdRc6W6DU z-v`gS2ghkH1(jXAa6Yzb)yk2-9659m{&yL`fC2rpcJKbNF(r;SQg885TwKQt!e&6yN_ZRVC17GODroYwa efa|xU-^9N$j&xNEIWph?0000^-|_0xe((unABywxhHaFKrc2$^fHNEYva#phO*7Q925f>S(!0 zX$x(&MT^=?+D=i2PAgTI)`E%z0Ah+F3vM0NH_UxXE&t?fz|L7lnv)`O^ zcK5u$?|a|pc|U=VjL$@sPpybatL9aSJEmx!sfv;J3n9SI#}5~9_=l_|87)btgrAi0_ z$bl0G;oVd39azx(boCQgE!p57l(l1UvQ~(9uXS2ws(inC8X#LXi z-!I+1Z}~MqTchtxW(C01RCZ#*&E;SW zk02*UaQySjTGssf_4`f3KsKL;INN~EhKa$1Tzws!P0zsHy%F-_l`ym_lx|-DmBo}@ z2g0@BmK>&u_u#ZGR<-US zr#>s%6X={-t0p|55X?1?z}vDFV&VMXKR? zH7FN*ATkCV)kV{Xop*^g0%q6v65cXk?IZB)Ylaky!SIA2J^ebo2iF1dc{Ejksh|)n z39dt!tuRm)(r5vN#<}o)ZvL39M$eo=@V5tGX%ZeT=?lca>d|pi+*_}Pf-swQz_WKV zB&N%xY3yeX!+T^0#KND_y$mS(Xj+AVq%nuuhxWti>HyaNhz8f-QV7LUt;qlJ5VTSr zTkPE3qP1RJm=lv5RPLbo9Txf7%@Bl@6cy&4x1hfL6X28gGBt&YsVF__k42{h`%@@R zmJkI<2a zVaI1e1Zud8Fem-t39#@%xMxnI#2slXzssh_HuCbzC>?FZs6T)p7qaVy=sn^X0gX}7 z5EutM&vdXKxn33e=HDQE{y8{{_prB79c9g%+=)Iy1Ezsv@1U49pgqtGAyJx4i(Ryk z-Tx-kqbIR_Qe=hr$Bc0RSG{2ulvj5W;D@UQpnrQO!h>(X%2C^yQAi`hbg&JVj)h9M z4-Ugg4a45{6x3)nYs^mT3K*Lxlw=Z*)>W?(f18dAjS_q8 zXIG`MhS=P4WrI;NMX53ie9o~i>2x{`=4LJSDIQF9b$D{JXM=e6MgpQ`fC^>PZpbJ9 zTsgtYfH_}4xRLr#pXAF-ONxLr1LfG03UqOvf%61pOK=T72l-xg4MgokxHG3=-|5!x ziMOYW@4y4^51)e4e&i}3g3R<=5WX!Akt0CoG1*7;3~}`iYozi&K^8U=hkM;b*paZ+ z+uz^Q({uSeXZyvA-KP(}b@;F1Pm|C&CxApKs37>`U&3?Y7y)tELdM9XS%@uK$fJ_y zfgPm&sT|pZVyR&EUg_&*pmXJ5U0v_DwYQ)9_u#-lio+aCr5s(?D+kVjX-Zp-W!+pI z(J<}lmtYMI!eL90ZPV4genXu%-@Ve8eE-s=OJ^@%zS7>&(babDT<7`TzP{mPGFeQg z(@wtZ?vEKFq`*HvfOYpIu5W5wwn+c`u@lEzyFNH^B2{RO&z?K1YMLjVOzEj~x|B?% g+(P+?{6EHj0p|f26lsTtp8x;=07*qoM6N<$f_M&jW&i*H diff --git a/images/preview_24.png b/images/preview_24.png deleted file mode 100644 index d89d7340e0243b6158e6d548011e6a39abb4f45a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1473 zcmV;y1wQ(TP)NSo&b1`_S{E4dPqBfvRe+n^A$pXgTVMfti6= zodR-lFnP|L*w%UTiU{COGGb)rn}~)OKPK!u^)0p*Dzy)~u{5b262@TIDd6~V_=`j< zS{5$6K>&9c0PP@?{UENFJ^+ptAgTwl&lQNg*h`L%ymSgUdKCV1=PqxFjJ!zzH_3>B z*}-e)w0#$qwT*bP_Z(8Y0S!_wYckx)a5P z_aPSlfV5o#xOuT~`ose)Z^mY)fZe+>B|JQ;W!0(@V*4xh?JdK)O?x1m7Hoe%%tXS3 z2K;gTFFbjagAIvZIG0xsZDGfcP64T@2$(;Ai8L#+8%W;v znRJkKkRVrHMcXqrGBT=BC|2O-?E0Hd0b8~pV8MbV(u9N}>cIYuh>lx>nZh{mnAHzi zhhU)w!A3VeAc`IQRhXUNYeF{6$#y}vbc_)DK^TLQRW3fXp&htp}woH^&)M2hD z4Ds3ub#(=(;LBdtwd`}FQiQhG}g63q0r)E(N4%@SCF62heo3(fabRW z6Zv3sSy&V}(dD~6UnS1`X#Q%bJ0)nVDL`lE3wTEaVC70btV@t%#;nB{HL3}&Zd#1? z3`AYMCz_k{kd?*3qWN6q}4bO&8?Eu|cbZLN$ou zS{81UFUPCvL63?GIk~6W(a>PQ;Gi1b-b0u=m4Z;{PR7R)fQ|r^uK0G=&E_B+YI^tOy(6WOxE=90$1Krh_gU&VkGy)5<@*Z%6YNzvn7%WAsm{JUK@ zw0fL1Tkt$62;Rxbp=IgmM~Uqj#+fUTC($Uzp&tGHDpXc>pr}Y;y?0MzkjaLWRx1kZ z02ddYn+s*~=W;O5K%>QIhLTyHe)eqmCM1N_rl%hywq5q*qUjzydnU!5J9Q{6Z8b_H z+UHs=8Y~u66Q{OnW^+COrlQ%zVfN?BJX@Fpr9(q8DJ(3gQ6!p8-M(E(OC+5=DixYY z!GBEjH!1!KnnoY~TO@|yP%>GJ*i3JIZ4eRyZ!Q-}-Q8$YtD)8F->Nu9W+9Hw&CFce bBCY=bgy3#iHSNt&00000NkvXXu0mjfQq;iV diff --git a/images/preview_48.png b/images/preview_48.png deleted file mode 100644 index 87bd64507abb5085fb05c896f6348a24990ce252..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4197 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 FV1g~FpM3xT diff --git a/images/santa_128.png b/images/santa_128.png deleted file mode 100644 index 9ea5a3d24a7bd091a9adf835ac2c8cb09cb1d699..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25755 zcmV(^K-IsAP)$@OjSa9Nk3H$r< zKemJI?+f@ik2M{gFmHSdEZ*1)hs|t)B|ArgL2lissq!VNB1aTilJN#eco!twaV*Pn z9Nn^Q-LTsXoBwp-w8q!zb6#Au6V5#Pvk8LFECAkH-wj6{Fb3XUvkQ)&H;x!!ro^NN zyqYrGqbNRAl|fY`kQf7w|K7G8FfAK&6Hmk9MZ-$zrgKjwuit*k!IM(rGgt0{qYfOm zzrWgEJwCGlpkp;MzxP7?Gxz$r<>SrA_6<%7h4bC`p%KrXpd+qql0$|a)9+0&h6bqgzpI5oj zqp%x&n#u_PcuE2Y9v28W`dLx8c)_%A^xJ57!2k{dKt@0$P#au{I^d7U01O#`@9*!n zH;>OG0A5|O9fCoL9XWj>|J%Ap<6eeb>!3+Us$}2eeSR+pBWbT z%mKi>_g)yPX<`jl9^Pu2S|j;_w=wXy%tz7$XF=EJm2}w zZsQ!gm4~lN?)Ys;g0CXw+a6Vx$mb&pKn{mMMdM43ji|A~F-?4Mz@XD~5CA4Ua?}CD z1U3TT=5vple#`S8Y-VR4G408AsP!wdoe=6&$N-A3ovU(d|e)+sz39-9wF z=A~8d`P_D9$d(C`X)ruFI1b~g29Dp)t+H~^tLvB%DdRR8+s+p-97G1NI7J0Q6e0k! zrsMpGF?I*#4bD4i`o9@qe;m1)GlBX2bTZ$fMY~5~;i{{AEF7K&q0mHpoR06$Min?k z^7|)?5uVF&$Yhw4P6N+mI8UV*$LFT3aw8OmVnsD8G>zrirZFtrG!Ag!b1R$2(9m)) z3`Bk4Y_~bT@q(jgy!`BYo7j0r&Gn0dfg|?*T_6@?1_L5TNk^p95sGKmcHLCKCX$IG88~ zGUF#feC|OIZ=J<5HTB2@)!0N^+drR9Jr z3h1#I?7Hd(XnpGO-R~z8w@8t8GAt2~y1O%6&QDzs55S@Pr?>+7`9-TQ)GBjV5nZ|YJ zA3N*GXWnar^N*Uozi-iAJw6ct;GEnt2T5XZ%;QU8(Zz@U^S?Rf@CuttV{9fJ4Vrct z4+JOkP!NqCa?t1;>7Ie#$C$?p7~}(>k7vLcw`|~OR0P22$&m#Zkm1QSxQsx6;0OqY zU;t%k$jL^^pzWIf$n~B2g*z+PZo1V`^!%xpd;y+&f8)OyU~e8KDsV-S00q|W*9Z_m zlQhX8PwwxlYV>q|qO@(xX;M7)RiyQDC@%-}_ZAvnVo39#stjZt?Xm=pgr7yfk4(Z) z$padnjLgz0kW)#JGN=QxWc(~R=n_CYFS&)%qpcg@$>{V$Af3tA+qTopvY9FR#!aW1sobenT^%@;Roo`E zj$s_p1x9g9a_4dwP3G9>q%9HFnH+<9CK)=&2mq;=2X8tF{&*Dp(IM`PM42=+1TZ+r z28|sH}M>{XuvH@V%F2=gMIU5>aPEiLP zuL&WLRkq@2xXt4W1jh`%xVhyekCk0TIR z2CpNX*LgOT1y4siD_ggc*DijS@m(EY#1mkd2GbqKGB{U|Q{F%sdHDj)J67YxpyPm} zKO-!#-vE%#7Fj6Zsg9@e3;N=@%T+}>x;*Idg#8}S6j>OQY)(;J(X^yI&DI$fAXA{Q zcM-r*(^sh2scx(9ecx@m$e_WVXO!=>ks< z$C-cia;SUzCGOdwj6wi&DFpu>&HeLKR%auz3=Bo%;H+B*jW0g~ zfpyDyA(tl*ylpV{4a0GEoxndkUSOXB01XCjt?Ymk4jNzm;m(0eWl6rGD&ns%4|q|? zOJq|06ImTn1s$?^9nuAp>xM=2zx&b!1K@8t++pY&T)M~qJgUS4$N&TY zbSDA;;eZDscm{nQjsPL?h=vE(hcNsPpF0Eu(}F~{2+@%g4D<{-+WT)q{Yy_vN>@j| zAj!Wt&arPxJgXw99`wrYRdZm%5smI07)lNo*Ly7vA*)|LlnSC{#zB8U7u%N7kb zf_``)j>b>qSUQjL3j;mdve?jIzti2*YmLN`DoxS)Rfm^p!eST#ASIv-9L%A}!|{yz z0RkSCSCj==q@ognWmVw!`SGti;73hR7V`3nke^jXyigwWabgIJB*-NQ?dX({KqpNJ z$FsWB9ZfpX4>wAI_ulB8_WCp5J$i&c_4LVyu?to$`PBP@eFlJg{<<8#eEb2y6^3dSXSxc1$K{Of_ zbRa7*q36)E(;R*xXG2dyhu)OUJJT}j&M8Rm3RH#tP+e0Cp^9pBH467@5-Sh;pehvL zm1RB%`@M{Sh&O{=%5DIVzCZv%0>Ri7&BE~Jt(%@Ec@ogbA5P|Z-$;fHC9>Rf7|W(Jc1LHYQylJ; z>r$JbipODEOD#KU;VfR}S0q(cIc5GN9J=@+I0`9bu)(A_-va0!Nbv1l!>m7TL(C39 zdm#v&SrmjpKQz`<@lbUg8XVn%SK$>AAJmj1TcB(3X{u0@PDxLYz93ehs3V=zdGAO{ z>g^j&XGh}q-uJUhzQ6Fw2ielQzwxPNfqe#m`(9ijp%4BXK7DRnZJAsV_Bj-nN7H%M z7tOGN7#SZ}ERit#c5Zh3gBz7*t6Q4XTn$ZQ#y~FG&l@Tq{1nR4VT2*uE1w+jlv+WK1%OI%I4a%s@4;>hZ8KE>1wkh(bZ~mv0Irs zW4bf_jIV?;6oQv z${mQrlXygDVw=bW`69AaTIw54j-+$>o1VYr>yMmx)xDHcfJOITy}#}GFCY610Ka(R zeaWLK=lQ(KQ%%)jj-eTkX9}z@mW4#702qfm@!p;J`sfO^F}dDb)ijZWQ=BLJm{G{WqPG`ACo(|wxJi7@=f2J+9e%1X z_(!B~OUq_IlW3mBOIx&z8C5CxQU z0mYlZOv~b_9D-mtB@HFg-58!;_}cB4{{6s~`DL}1Wxo*# zd1rgk5Xb<|a5BeCn@jxz1L>Jo8;nh@4diAMfGXMcm`^Euwuwi%v!`dN7R{=1;< z_uqx;DF=cauBOBS@N5*MOdlG)9agSb2kAr#7G8KQ%s$-}XKUB3g^eG62**sRg043o zg~p~PV3v*|(}LB*GAtcbV4GbFfr>gPudITQ-wPE8fCw^_2V+P#Q8X$H#e@^hTOfGz zFceFZ3tMg!i)VlRu4DV}_{Qt7?4j%Sw@v@0W1j)wmrs5G-@EV#_|0=mPDPP*v(5Pe zs=&<^OhECTuNqoaIJ3MfV8(mBF;5d;aKY6qGPae!{nlIX^Pm5mEm^XJ!Z&#N*H^Il zC!EXO`5%W_@op|>d)ca`t6=kn%~0DqACA2AhuqQvY{UBX@W2BPKs26&M}Bz|^u2Ny z)HgJ6L)RG!5KwHBW6%I^^!wO`d>OaGV_2lJ8p1&@psPaH;NhAi3q@cHQRoO^h=5>% zu@Jn&$qdV6i}&=e>$`H(;#>B&J^$rnpE5w%X}^HvD?fl+pL+i=9R1(2ZD+0xO1+{d zx{k@c-Q+i76>YnQ9H(l6bod3A^VT^F*ge0!n_qqPRU%;*@_XRJS086hQ)Y8g{m9MC z9N5fRwvR3Q`wD1VyB-d^qOQt?4t=OjbGj2&Ypf49*M!k z2@`ljVol*OXvc&ZSR7`OV_~EFI)-DGY{r>-gPH? z@PP+Gk|a3m-1FhSpM3|?iyj7}kRuNN2LMpEkyZw2++f?261)@j!*JzX@KulDK22g` zl8VV9G<1XxpmZ_S95|3Knmm^;vRu)m&p5%7oxg!D^Hq;uyT5Jw-#YeNMbM4EdkcPj z`N^kxJf0_JO`lqE-M25bKYzyQvP$J%cv%G$S@%Ek1l)exulS6av)G@Wdxl5+KCp}; zC4D#*j?&8h)T+Pm?j4)htIM{)3-7Ok6c0eSvWCmZ+g)8FAc;731!D=s)0x}NzN zI834Jp3ne$0)XZj$TcXA1wm+-Bh-UmJLJd7LH45y!V8XR%Nk>YP` z+_(u|dF2%tiN)b7CoF(d8%>a91r2TjT1#95`UCel0>VM#GyL4GeZvqBw!pUFJm_Q9 zpioVLcJYO_Dl-pfk|QaoyFCH7z|$;(nN9QfP%lH#$Qzo+qHnHY zOwl+uilF#I?4*-Ur6cFoj#_f@s3+= zcIHl>%!byz&&tP4f$D=!0SwrYdTF@X%)t?=r$!*&wwR^5xAQ_a3!cWwtoekCxCeA5 zE0npD1pCnFEDY>m`QGhd74y^)1eUjTz!i5q1%o3=w~CmOJFbCYT&15?2E07tRak9M zLziHHJZ>J8kDbo5+{1PyEa=>_7JAn&f=I(;n0DgV(9p7&DEemr@OvEwKV0^}^{M@B z+>aFiwp%>`CVqSJfk8zPuX7Su5)O3^0lXhxlYw7j40SZ1*a!MY!T=q^xq7WaPX6@l zQ!jb!#dpuV_WEo5=d^~w?rUSsM|=UQW*i1~KJAupIi>)Br5C_Rj**1sbDPvUXs8Ev4aVTdPyP*-wsr7}4sT^>bZ9OQ z@CvUY7LYKabQER)>BZSKY>iK;qS0>$zP#i#u3nR$mh6; zf|NkVM)}MwNO}%$$+wq0cwKCN8~0-a02!ZV-uA5NM*$QxfS2KmtD+bK5;E0D*N3Pf|63|qeD!^q^qCau-n=}5LAm5*zM+L?!fud)st zqliii8N@Uk0zfe}A`NX^=IFT$6mnT96OTC)PX4MC_9Dd;<6t6aj9gAsi3lRc3kZ8P zShb@MzVXoCpf2Qbp1AZVsi32v6w8DuqQuWfHHR)lQc+lb*b{@QFZ~KK;CFUxX_N9= z1cp;4bocZ?$C5vz8ybSS=iEf9PZFAyW}<+Y(4o}|kK*6FVaY?+Cib^+KQ;i+QNSCg z*^alsp&W-=wWi1wX!vS;t3GE~<5L-ZDl)@3#7G2hijVp+kPC3A%o@!@wfd=S0S4k( z=uA4?`}S>6F}NH&hU{~P=37hp=&<7E}%c2mNZ!o1p}T+k1W^YOAQ6no)ym)=Gm4rL6s!}gin?v zFID+c3@w!+1ELFBtbqpLaTH)(LkX#KM2C?zZ$Ryq=b#GGfKp#f#HcVa7)3=|W+4gml^UsYt*-ai1^;opEr>q6Mp)z5q5S*B_#-*cd9~%Jhu8}XNP<`_1?E@!g^ZE%MO_}QV zYULhH_GxHzD#4}}EGUY=EyH&!cyZ9t)GteAi#(Ptu#r?A`l4wVPUe7behtQTy$*Fs z5x6%5B+44xx(TdPkI&S*Yr_CJdM`wq?5Y=uoN?rzI@1GhdOcH6Psn-TiIbj}HL1{%KJ|u_&J# z@MtH5{Msb1Dv!|=NuyEgQK|EmB$6~@aR-Op2_Qco;$svv)VWvUJAk$riDi#q*ysf7nybI1{bw^#uceQVW zUHwB~sUf^cHlvjCaH;@12GSxKRg!(~g-NCR=s*uXpSSeE>qhpsVLw&?Tyop<<7Fm4 ziM)IW<2LwxUI>N5;PH5E(RTZL(Z>(= z^?(}P0?qv^pd!%);c`EZ1ZAcw$l9hwB^rzbp(qCSg(UCzx!gPdG*&lhCbUkS2Ht=l zibd257H4_ghHTM-Y%Yh#is%K*^_yt~< z$u@7>0Z0Z8G7usVXo(sm5U4x)Qz988=7^Cv@M;|DH%j*sA$bRWnIA}sQ&{}F8}_$t zKRy6_{=0t&1_SIQ6j5{0D6MGP8EE1Ojw9Hbs&2sR@qwy&K=XLGqN;dmQmMpB0KqOv zbYcgjbdjFTp|X#5ZG|aDAIvlcp>6eQ*x4I}N@@k!WMvvD}qYJ+p@WJvG^*u1?D1)l-b+gFz@*ec23J?L}@mRCY~RRu&m z48}Fqu_==$K{$Y-QD$O6DNP2+r7)FT4>FD@K1t>c9?hZ)LLjIEn>Y-&!$A8+2u?W+ z)LEzSzOGK*JvfZOu_=#0Qv(#|)7nFxM-cUlWLYe0P-H^+FS(@uNb%o3esIh$o_zW2 z|EKX^T)GKt(T>(+MWml>+yhsBb;0O#NQH26GBdhF4A9B5zsPwy`{_u58H^B&Yt zTYp+bWyQsYt~=W|Z$LxX%=Mgn4**cw-wmV4V8O~7XjO(_LFz-8US0!r4U>4Th|1r{ zI$L&aW3LS6SzpsJPQ%z1s45Ffk)VgSwzjhBiZZbb%;!<56|7kPrKyToF-)ugVqGXu zLufRl!ID`6M)Ee#CgUK*yP*(nLf4@~SKpw6qL#u!$+8S25{EK9f&nOW?T_bqUo1z< z)amwR@tbTwKk)pLN3OerzQfs{zZ#zZ>s|kFKbCIX1q)}6gXL}AfDsHk?11rn&xRO} zz5M}PeEbog9srCU*WB|4Csj7K36i(*Jtz>vx3EPCVz!ptA^@_GGz1Ism8k=}gi zSw`JJXgFuNU zRSx0b9K`VTuovHigU&p0_q}`eZ)D`RQkzHU;Cw*V2iRmhR4WP6iO;hCBg|e+wz!t7!~b0>)3A z2vb{Ip)BkNnkdG%pnu(au;a!1d48yyCA@XKKQIAOQjo<)qMYjdWLaa)6I*#>^H^4m zpAilPsg6&q4#rTFk#M6G!=&2jl*GxnqDZ4D2~MC)=XIc*L=r`wX%xXWi-2J!LtP@O zheSCrh(;6?%SviCv3kzWu0AJ#A2EcVGvEuFwrM1PY#INL37|vay)}f+OL#e_d6bPQ`CY#-f)Layiky$Ni&X z8kGkE0hrJ-p3R*%7s?`j5h3tWNdQr#tVuA4l)HGzayBwN$cyO&;2g7wY|Ok47n9+2e$$#MEn3~ZeUuoWe02VQ))%~;N4 ztYA8h&V7FUy?ieF#fP2@1`j)OAsYQ!j53ZXiWam6CBy(hus)a09Lg>{F?ZEx0RVJd zee2`!weNlvp1S+FlZ`^*H57Ud`eeG}B5{gg1S~xCP#D+TSag!PT1Ae4I?oWZ<-e>Dl4J3t{(CzrrI`dg|@af7>SOELJ-X#1l2<;g#!=>gz&lM z7Lq7#{{cG5MJTkqtfUdB$dgVIixfT3b$BHUBu59DR1yHtJk09Y!ZT0a11vED8eS+# zTR&d!LU8LIXr2Vm5p)5aNMgOZSFO&l$MYS-%rcNB{aAP!&2gM6{8Z5H&|L8&Z)-?7lKrF3qT1pWkDbJ z$Xv`S5`#Esu;Su3S@G4U@pYN2$Q&Mgy%{fjHeH(w0LS87Aq_zF5e2-k=4-Bn#w)G? zNAvJPUMzWMnPeK0iKJ7s9ZAPP00HsS%P%an+}iMvKcNZeEiYm$66Bh%;p$u zUi7|}$)vxGVctUl4AaWXB5>?6$3PevfEbu2dRzbq;&(v+?ttTh8)$)^n62fO7+p;C z2%-UE;~lp|Z63V_Zv2n@VwXS>9UO!WYnMY`Z?~8hE~2=zME;)(zwWjf;RFz-dA%Yj z(a<;sLZJ{ETL%<`oRUw14pa}A9a|vxr$;~@>;VrgPs2;5Iqum60syzA4Y-9d1_A&_ zID}3-87BVx7AH7;hLlC!QpD>|c69PJcmJ9#e*PKmZEA+9F%#@iUBf%x7oWO71;}1+ zi_!{s)X(5+`HvR_AFB%>2B70RcRpQ(j(Rm}ud&A;do(oE)i`8$bje&6F}5VYP!fpl z>E0@}B1g8tr4;aVKA z;?wOYLWLu4R@o7aCMdTbDX)TXq}1gBL{Ajed!0r5J=H zNCtr4&20D$D&ZupG<+kvVuuPiX$G-a$U|3WJ9M>g z6%$jgu;cDzklP!Em7a_AUPW~cUYCL~yCJ|Qdwq~!`5t7Sdk8es04f^6(a$~jdsO{& z3<7{!$QdO7m;?aIM55cN``x2ZcG?--Nu?Og9U2&Mta};J;u1qIzlzWjUgOl%RcL0Q?!BJG)(g<&+q}me5@|%&p(IsD2MH z3W2_^4(RIGL5)W!(IWsre}qE4Q2@|vVlWs6uir1I2aV5)?QM{L@eio)cM|oJ_yaF7 z0A48p;HVM+%uxVP-$SYto;3%`etHW8<{iZCT$Z5#fy|c8u;R{JVeMOQLUlla5Ps*N zj3?d$ViP}&0a|q8@Ik|LK7UC8R{Y4Tz*bX+=j^>d!@N(|1$_IqCz}1TvSj?2rp7rl zCbOoxDpcc=SaLy1ToxHOH%nA9zT2haqmf=3^|UBsPX-`>>W}`Qb8A(W zM{oqxs*J!e`n@m`;jCynJ5BC@H@@K*Q!ckt4VbQ__~1r;OBiAw6A>&)aqK!5nK`i ztM9Fs;KN^j5A1vjDm)5QVq`qgBe9@Dt;#sM4$4w;n$3hff*@}?FL#;J?|)RZ{!I7p zay0(EX#B6^<0E?k1pABuF8lc-@Vg&=9j?3mdC9i(mwP?xo#UGuq`A{4J7XKFB-+7? zT!13ETY1uGvfR-y>f`^30n9x#013d|ZU7m;EHMBr_#gn7c(W*+Gf>zK_V+NFcE+E@ zL=#2_%qSLwzYm5Y)MZR85=p=O6fZ7&m&vv%w26Y(L?9q0AO=wI{t+O?mQE6ulrbOx zV6*|&#oq?$ysv;14k0p3CWp$P@73pE>uujdAqh~YNifkT@dmF%%OJ#h1JW@#@^SQY zhX7z0jNYAl6*1OQs5O@hyGL_Xjn0N_8c1p{C_Ws3zM zVu_gJG66?dz}mNLB*XI}Y6D#_h}1*CA7okpy*auKed!CJuXvmDT!C>D0jZ0{sA67$ zjP66U_)1|Q`EF_;FQ&H9iYA7Y1U%!b-vT)6iy+ZH1{h&T2!Q^VpN3t(xf!a$Dva|o zn1uqss}ca*^{xgYs9-oeZ*a)b-bDsyTk{TV(y8p8Jzve(_tT8+AUE_dxtUKB0O(kH z*+p#O?;hvt&N}hd7ptdUnVvY`Kc=bPnKQXrnlio-%0qq_U5{_0=(9>1;QyNe*d7eP zMYHHp7eE?-`UlZTE`X>$lTNZ-JGMYP8s(;mo>ftq7yZ2!Dq~K11dQb`gR}7iRB}bi z|1xS`M3iEi^mziEl+6?xK*eh%H$a7vbu^pl7CDIYa_)} z82IfkU~H8KW}xC5=a;!hlh6&)-c=k5IIwew=p3raT+w0lm?%2Vl7hpMT+Wn9?3)jp z=CfZ_RrY(+{9lI$`xF2j7oG*ypp_e0M15Ad5<_3ou;%1%qkm58Ar&oehXw7KH(iJ}x|`wH%?e~&@e zCU83s%A#o?U#%q>hr_axGdw8b2vjxi7ZVHvMW$CvXI29}akbm0}vEqc|ZH^^G+ zEZHgK(8q*X?w!Aa{lslBtsEI3D1k?1Jn&BpzzGB#PnN?EMi4lEM*s|?cSL@??vJ*8 z=LP6yp56-V_}h<^R#TXoLAn^ulioyDKI3$M{liW=#BwMU};vx@CvQ%WG1hRw}M6b^M_?8b}r>LNauNPcq z;N&=RcyCpoU)Qde=t6zEMtAHCXR=3UD%x{~sR1bV0OL7n`#^Q+# ztmuux&fx?UGkHjjoe9}lhk|#^B&e*cfV#3EG}hM=8$cl7N1JINvN%Y}Bofih3ZIYo z5i@vh4WNPXegP7RxG;JylM+kJDeMzpn3k}ik-3ovxu*@-j+MZ+EvM0ruPcJi#db3^ z|1_G6;BKR95~l$?LqGt)sDO%GZMRni0mHU9lm?{`l@4l)_*bt5Z-Cn$ zegI`J+y|446f_2?dP$}gN^Y8(L@$1hZW0qX!X;#pQdVR+^tcP1B-wFpeAaP(eJ%pv zxlaoK=ty6BvKM9c)3R5k1)*Rdx7pbOi^fxIX?GL`5?S6el3?lD8mHpOlfX0KK*{!p zdBm$hS-{UCkq{4u!{872m`C%7KEgCON$}C0zqwo0(mv7LYf+!x@S`rg+8vQeqR~62-fjDA65`$@tvM-9@iqe9KGmK1C-; zJ)>LI5Ey9$+t%nZLHcXR28W#pg)=S$UQq=)btdW92Gwu;9wv6I2XEL5LyE?yGF|L< zU;{6et#OeHi0PbdM`~#+I$W!&wk2Ix3hDIq&jWmV7@+Th<8b79nI|kC>_Wcu`MfxY z)zXSw=4vy%`#O&BVxd4u9oK4%W}6f-szt3qF%5D$3i7~qu54e-lz|R#JU&`WP7@98 z(iQH0AdP;p?jHf*rh94q8xy0SwwE=~lA}C>rj_Pn(>#7dYFA>{7%Jo;-K1 zlaEH_!JeM$UI4h`d|8&BaT{uUG6697)f1rZ_iu^kBi}wXBrBeWePw}jRKFK(k#%%5 zej$ZtGRt@>5AXNKU`ad)fe?dw9A>hO;af#BlKNeUgd*2Rf0mc2(T-Ct5w>5r!Q z$pru%s}{6C-MAL!tMVP7YM#|v*e|Af4D`{+08m5#6cahdGX+>R6o*G=vmU z7bsR_;GH+h>RDmJAzZJY2DZNpH5tk;?dS zx}qw~H*NyNPB{ncsk5l&K`cj0W%Ce=B_W4eMaK^s*|-9BEP9cLgI+eNGARD09M3^I zQ>17^EV7_vR-~rJnS9`ZEVgqe>)W#B+s^^q|5d-A{XYM{FBbS%0MK#j!Qih73kK-> z;&DO6tNug_c&=5@pHr7bBVUw^Opyx!q;t$k7huCs5?0$;@JAR_`$PhWnnHuSgn&|k zZqmc39K($ACT@o7M44u8@=@IO21L~k4P(t8fZnCAa%0(_*%T!YRemqHZKU1#1+m8r zcie6+ZWCbfIg+~p5ewr#t)^m9aC7qRC@=K{`Ue2O?)NBP;f(gJqIUEx6IkNZbAY4q zsicki5oYs6mPltHg<_Zb3fmZ-_J6np23EbxXN;|4ll>Y@#dBSI6o&AAjJh#ODK|}? z%*W22&4xE_bPxcSJ_GRR#Xg_(SfTLAWq?r^(D$WdTO>tVtol6lk|a}CE@tL+&~rs@ z5DOpxY`iHehf>h%GoY+o2EPzQVmmMp^MWMES!u4AmzJ7QC68^e;d6e)(N!>aD%njQ z4Q^b<_2qwMEyX@)2zZ6x7DWZ4#YUpU$mI-?#6e_4aFmwI)WSrhl3hVpGO|niqlVuN zggwAPBcF-rA~1eDPn>!lW7Ej^cs)4U^BBt|QaP4LWgv&blu0tg+uNY$o#&vr!q2A! zee96xfS9#hwrvQa$N=u%R+QgxVA6sGys@>F^{-w%*tKQL<mZ$le3px0MNt^8l6Ubgv!#m*r81LbhlW94DRe<+gcz_g(~lF$N39)$l2(0b0s@ zuzfHE8@uCT4I+pvgu^_3KTMiG-;UJR%g6xBy4u>VMHjH*5>#St4ZSOfr_8F?K6fT|q#}(6wn5?|b<^R=;yGOeznFJOM9t zHNjAh`|rsOF{p#cfjdf3kObZhB{Q1^Q*&b2z3wvV8=j8R5gKMDu-2% z9m|@gPPJ99SBiIbKC*26`tL6Rh+c{Sc<7S|fOWGbfZ7~nGhSWKw;wZa0m!gaRu!3) ztf;*s1}N&x%pm|!3_95&+mXn^dc}gWDj8Z(0M>eBh-jkwiqHVA6exKC$=xoD+<^%S zi~MdaZhlhvJ4sFm+rFjPockE8wNgFW8p81hfw(AyIIFL-&g+l_t)g&k+Q6A(@m5 znat%cuUvWmxyK&Me)7&cd!|r_BVZFB4!s-?wly+=eV57F5%;o(lkjbO4IM z0ssPFO{xGr5f0TADl~c#0A2?Pc?2ktIN32kK-E#L6 z(jt{iIbbd%PdXQZP0b>IWMODP0CSs2XJBX~3Hd@1gBFb?QS~QRya8jY{V=nx9LC@U zG)8=U=RlHm4yU0zniUnrrMUzitg3>s)2BnYriN$x`lNh3e&Ov~w*2v?Idi0uO`Dug zoC{doPziym2&8(3A>33wnaT3QIQkDmF+^duL~XbP01CQ0gWMJQ&9(rUbESseXU zVTIMGjMsT37Ene3Kp_BSbx67kMxeZ&+mtC>EzNr3?+%n#OYtlh0O;)MgYI41ozCa) zmTK0$2y-g~P=-T{rfl7o@1hh?DjAuAKLP^%EM6Zqa>?jU>Al?m5V;o&7QMCgjx+aA zNk8^9@YOenc>+^(8|4oAe2GRr89$TD1M~Ww{Pqu};rCyIricv(;OMW+8!)Xr0MT^O zY41-+JBQLxuxz&sP7W=Me$&zM$zXt` z>)K%9oaylXstp*!N2H)tWRs^(wpUGUIUa5HvaHFfSX)hZ+j1GeA{S6Fxrr_yZCY#% z3O>0~frDtLVUwnX7to#ei2{Ncu+_0`>^isQ;Q6Dc{0UM?JQK}zhh!Q)v)jAdzZEGmTFC2a{ zX9pe0JmqDgQwbTL=p;dcFOg;gBMHc4vVd_Zm_6Hh-=bGogOY(+6YHQ74L!x93iP%2 zCwb>knvJ0Hw@V#WC?%{#B76*v{y=%TjqX4$#A4s;U%K?Sk2p@@s)h#mZGZn>8MGf) z7$_~N3jS0Mg?~U>|CRP$k*J%pgW|}d&f4e#C~rE5Qg59KX1G$3E4-5A$9S&ZBU6ps zZVe#*Cz|>FBLLI`Ph>NH{s4D^Wh{|J4Um?guWuN7dpmjO8&9(ElILKKl!a>46yO@V zv=7j3L-UdrV54E(sK^_ApQ15JLD0`yAO!N%1Hd@?G|*!FONc<;6iZv$X*l5sb|UsE|gz3scY)P4nK7EHg!n;;t?t5!g6m z21n`#Zzu$MBEj@b=FD5yu6^nI^XIc$Re%C0p4A)Zd%Cey*N}+*38>BnTcC| zJkQ$jVXGVrh@m9_fV5`oC~^!FpF0kfS8c`Dw*{D86_!{8xxyLMpMu$iL8$d>;4TDl zJ53R-bN-PFAk9V8jR25F>EGRo9vtd&O0@x=x<=sh4hMet3E*j&0E(ueJ2ND){eRrq@g30v@PEkynK}XCRe_U8wkbq8TS|*b>pc z?bZ~6pVmE2n&f!U_?}=8(tUj|N4vYPwFU+{>*mjg;>ZZxv1`{S0|0L=SqTRndLV3G z)AnP(&+~(DSxE7t&rQGlqTTzwn`EBPQhRU;|0pyPxl*z1yF+)I!!o>vkusUrhBWY@ zeSI?Lb(+M&B@yUS|7a8-0N}P9!x0nu{AFzVbvJX<8)T`R1D#0;h4El_6G*poHynD{Kz{u#yWXwU;RYAacRAY}yq!Apim_lP3|#V0a`2 zLjzrW*Xj=#f9r9W*1Haxg8?@i$p2Xblop_{QbfQk(K_A%6@9(j+``$Mg-n@$IA~L* zgNCD#nYvi5Oi3c$Fj+K_g28AU`iEoa0y4CQklTqtmRkH47#*8o?wlr=)=!HEZ!y^!V_#wzX z_9z&AJ(Se}$?pf>wCTW)KMfKMEiizQjNY*k*v2IQJJx`t7oZ%evdp6hi?dR(1S@rw z7+pF}0I0a=`!Mxe*Mh0~0PiIN0Fa7vFx=k>T^m+`@%m#hWyfM@2?v3aFz%9bC}{z< z$73nIOTIypL8+_*Hhmr_3l0Z$<~;CJ*MWyoL8$50DcZEzRT2F?8geiujGs)C;6vIU z=!Wd-_n^?;2D4{1!JLV;P?5|-g)OEYp)Z<&o}m;B$Mc|b*YDe<6GuF&o-zfH`nfL> zadMfA9PjHp^MNf}UcGq21pB_7JNKI5|HlB}?_Li5zgz{4Z?6O+$BX6>1;zI{ ze%Ter`YygWy;@EgM+o)kk3bhIe6VSx|&n?ytFf0SX|v6{litpe!pKD(jduX$E*_9|ZF3gTdQ8 zjsbBbj@x@RR4QnAqtxDpC_hbG#>JDoZ(xKCA@x&^pP`$xx_3Z!)grEU?_@J(Hp9Xh zIGU3M2F4mFxMVbp1O2kAIXm$b zR5W9LYB7oYj0=8w_fKKGC}C93)s9fPvwoZ<h0j&J2$h~ zOOL?V)o;M8au)!6Gzt()jV&tt#Lq;q){kM@gjTN2n9r113qTt)0fHzT72Dz_?I&cr zJA>$kA&lKWoPgfJs0jBmxuRexqiZ{4(dX;2AwGS2BRgj9I4HxBA1Ihe+Z_LGj`a`Y z_l#vkOB!&E4>l=WpAUj{bu7}{>`;4LbTw?KtE+FYqvNQDM@DuYh4=fty*~E;2LKp7 zGEaQ1KEJHSoWfC^WxljVBpO)G8xF_3mo5OY0KfM zcmjeb!e-2!%O{Pkg}U;9*p-V~rO*zdLukN$RP-r|bJx5F%ErZ98R}+AHqO;d45UJK zcU)Ni{d-CP&`JORNaZaAz)s%Nxt$HX^f)vwc>(5yR4CfJ_kQ3U0Rt5^AUBNVo~Ci2 z9pFTD|zhtgob;jK-+x8o{1mb+Sx_yUq& zvmMiBPBJg)gBfma*aaEe$41WnGNcw90(oBm3Pqjg@_D>hS=xJ-x`eYyEfaY2#EH;U z9cE2c0jLW5aFEM5%52Ew3J}ZcFkImD$P3+_jBQ@YDk7dVu2$Npy|q=u;~|k zkz2Bt7EnMpiolHSz)*KP@9pViL+?BfRqy=?X82^V0}+rT<)D^Ta<#UB$*prioq7QG zv`nPNQq0H1F469Of=;QeEk-TW&4bdqqrQF^L%k%r0MSoiWRMlxRzYUX2PkYMXr0ms zXC5($hcT{K5;IP&#{>SlIv%L41y6Z7*Zh9GM`^Zo)v6b| zQ>k;_cK_MOdi+ZaV7+vMuh=>8<@~n(pDUH2Y9?dYTrkierkml5=a{}T3dXj6Fwqc$ zhaL?bM;`~dNEOdBg=H~xqKaJuXMF9Dgsbn;Ro8gS5*{2xrS*# zHgE7$!DO+5!xKdd3aK8WCHlJIdA_F+kleWdeio|dqPb5c})42tL z+g3w4MghvWiJ;Xsf@kam?yIgAsb;^*xa^j263x?Qkfrs6Zgp`oo8tqhQTj2w8ydk; zpUjHa!6<@(kw$Lm+brML398ovGh1un3r9}{|6q!99PMQNY|en_NEUGPb2L7qS;^5v z4`~K38a~j_zg#z2UV#Q`99-n(RYGc}J{e@2o0L)8o063Smq+VM40E_sJ z2CAb#gP`#lr=EI9cjjOk)qVo3p)61Vxe67& zVL`5Fpu$cfV05=p6o!#LG>jjOucDarVhBdtmPp*KE1~#;7LM4Z89oZ_Eb;jUq|ybE z$L}9Wz;G-n&MYz)caR;DaVX%Z&#!(T^@{=ifEVV>Xo7Q(nhbJJ9Bd5l=&t6d|6r6R zgEEl7&!ZOkl(vk|W?C>gC!7@EmtGL=HI`s+;Pet}+5ODBP;c{1g)MfKOwZrf`aZ-9m@sje&wJq`gzB*mXq54-iwV)D=uBeMvGp6?M|~ZnhZdHgyFPy^E=3YYs&OwVjK` z(;}JV8a^#bxo8!Usbe(Lz6lD;-p2131zVDMDCl9wESw0Z&u@WZ`w%#>oaif?&+BX$ zfe6Zf_HenF2@Jm+<5^#>K_*vC>U0*(BCYv|D8d7b2z)-xBxpKK8k43XA7Nc5# zoC`{-5#UtF!a=U6fZ(J_z`|jX6VbC--nDWicd}U#>!tzD&p6JXUM)2t{$v4Q+byvD zl)Ik9yLb+IIyHvK*7JsBp$kZ(;j`4j4QTB=gBrYi51u;^02+>1jON)2M=C9|;qK%< zh%L9+?$-OFiIW9Nc@2aPISwL+pA3QK79RA-tSso|w4rll#K+2mK3?hzj6n#ku2sas z3U{3Z^&N6szFDQ^T<$2h#H3BpFgc0PbhZdlRPxb821Q+t#gb{U1+3V5j#9lw0WzD{ z@%V~&m_F1iVs>gUM;$?$$5p|%&zi@}0~$-M?*WN45Ps)0j`sLShR09~B~y9PTbO)4 zO=c9SvIwc3jPI$d0}ClW8H+*B>eXB}4AE?Yx|KPaW^%)5dC)ZbK2ZQ57oa|L8?5qs z!aj@Nhrf719B<2j#GNR9Nu4=TgiAbtF?Khiz6)=96&j_&Be{*9Fk<@9O~ipa(F-2| zU=(1En=LCP|G`o?0)qD5!?Bkhnk7JMRU}td_+bjx6AR$VWh#&;w1tH)GL?A&Z58)w*e;`jt zJn{rYh^LT<$PhV7oM3yl%*39awWoV}tFFCn-TU$To%7wEwv`Y((=(G?>2}?!uDZ*2 zw%LMVxr`^jx zlGgcGq=c`CA7AY`6Lv zJ5a!fy^9kMt5w%HbxIn?j|0CXa%*Wxmd~A&az^3sQYjYw;b)(X%Fqn2zVga{fjK_e z#A&~Ur#b*{;pX7}bf9^sCP3WjzmeA@s!zmj@tkvCS@-^l8t#`}OQw!3+vM#+hZu9o^8e8jJgxZD8-v*Nn zInl|5!`f1>jW2a^kCUfQM^jHcA)B!6SI(S~){PsgQA&Wa*dshkcI<;6bcI^YZ>_D# z^{ZE({Mxl^=kl>{V80&?iQjoB_)DJ>_tmfK@z;yR=~qVSKi~HL#TF~00C&;G6Z@S5Ti~i1aeU=}GwhZ~EwRxEcNL36ovyI7NgbuAM&>UN~Jd0UcR7sv2U)|UNfd&?!O%W&l`d*9p; z2Ro15yFuUOOFN0wd!y7JhGqWE=NILDAA6E3g`|7&mJC-~T4ZvVq`d?;-b3_slRsitoU|YR}`1cta#O3kn*E1yx`+XMM~9b^!@LA z=N~U#yY|Izi>yqQ%Wjj)gt4(L+wF(R0RN4BqEwPkgE*I<%Dxaqe?82yFT(!Um@HLs z%z9`3mv`jagBXCD(DL7p*&NvN`GegLeOE%dzI`tFF&TnPRKWK-I9Jgsb*uS^0wcw= zo0S@1b&1U?g-oD0A};!*5WCcR)@ZFj>gkwi1R!*rrY?dD7YzqG_^~meXVw9kwgxOQ zg*$oj(S81@-+0KjapR-^ zw!VJ;UBzO2G2twPEnDRfJ&yg{J0;#;FhIWj10D{N8Lc@t1H$}0?&=SEw$R3xEpN|{ z;{i|NyR4k`U5xN+c&%6{--YFvJ0LLc!J3Zpxu}ev#g=hODRgr`V(^CnWHQL z;<|6#mi{tKLtkA->1=nUwb}7o>mApDkI$J#rf0HNd>i+<#XK{jec1aZZ1>vDo1R%c zl=@mIGF3JP))F{FW*vb6j=%V#hxALiUU%1)meMmzOCR~y<>gnOTMq183zcQ#eWwMOu54^o|1jf90d_d(!0Lp2TN-ZB9uSS1fc5iKWtX9n-Q1YJJkyZ>0ZM-P%9>(;nz=vP3QlM1oE3@Y z;BYSG(dYWQZ3WP zxe4^Paj(3GF3|h{Hk;rx>2x$3xrS>}%ka2lIc-)@8helT%ChrQQ*snoq0DAj^}1ZV zaAEM>ix>awW~cLNm1T!Twli%^C4Df_0OK&ag?;%RnP5*ez~}J*{}i10KQ0vhvXNzf zJCS9@Be=O~F1Z(5l~aRM)u+c~eX1sxXB)EAAIZzFUz1y{z7mJhQ7FWEZu;Me*8qNShND&gCBUB%%9&NCY-e~<`^*q@#omlLNC%KQ@t zW$v*BDUDSm0if#v;SXi~f}NQYb&L(ad9{fR@ofAr)&cW7~5CJ#>gtl;1c zY&*DiKDoK+vVjpk!RuhV=R50dN0i@&251dOs_IE>+In2e<*J`v`iND}aX%oog9cr!3^k$Zn_@V`-nf&4QKmNE(%*|!2Q4DV<`uaD&`R%1v>p$_8D|kjWq6DpF zi+rA`;?PU7q!48wyQF!~@0=6vnE^h;I|6g#Va7IU<@#sE{Rt5CU$uuLzdlMGq}M%y zhdhXDO{)dhyHtWsOxI=Rxf3ovFeSGj5wAnqt|yQt&LylgWqq|Jwe_~I^oEX$MA@CA z5QrXP{LXrfSOhW)JEYDxCShEl&i(9|9Naf9?>{^vkDr*6iG34Z>J^u?dhq`@{Sc}? zU15a65J(Cl_hIKdu=DM$j&HU5ZX4QwjMb*p2j9NUMXPK!jq4>$brY=~x>G=s#HaL^ zOnYf!<6sx@yVg`n%~cgdKm62F!dGSi-&k7m-#l~XU$#ag22z*toGaMZsRgjNGOnA2 zDTvJ!B&;uWj*#LmG{HSHz(KC*z;lDcC(lCLex_Xb-S^kZe=%Dq6fg7!*^TaiRX*he zzWWFsdO@tlPFyKUp=52{%H^U=9Gj8xcOQ`|TrO7GdK0$x%_ax|rWmou9>c9L6*>dN z8a?fS)`ly=thi!m--q+KN(?SC0SY%PF$rt2kwFA zfyoyQeM?P314MJQU0Iy zo-OAB@%8tjo{_Bq1pb-Yzu(OtJC>Ep<%oFb8!x|n^K!HK9i0EF1O~w4-FBu0wo_K5 z0|VgvJY}*C#c~C=IT);n$2S0@C&Oj(CT(MItyV5vZADDw8m?CCv&PGLQho_}_XiAC)r@ZW&;9Rcv zV2YHTA;AIN)i%&|;_9qLvN=c!TO5LW_*n+B!|kQZ!m|x>D42ilaf| zM_>ZhDCll={q{Db9e#dm0JQJTrE3MBUJ1vLcK~pV8?aJnnYhBryyq(4R|sqaC{64) z22^w62TetjSO)4pu(&97NIpG)rKP3Ox88W;d!X#q$RwY00;~A`8npm(OonE4R09ZG z5a5Y{ebUWcae@cM0PYEBIXuu981X=@OpjGmPe}^%ntkssGKlV( z0f4BBxS27iqd5%55x6E#zaa9dPdfSVRJoXrO^!t~GYwxEuel#AZ}``sde@;1+OP|U zF%VDT9At#C0GJ9i4PXW`L~#PlFje(2v_w=YXen{WuGFzcoVC9c)PHC|oLWb5uVpn@ zz=v%^grIH&^O3-ZAGU{d=X|%_bFE$?9ky-7?>a0sgVS+Bwrhlk88RPz6e5*0JM=y( zLEW=q1XMmFpBmIr4@Hy$RxUo631uk8L0CaNL_4)l5q?`68}svr4*5!@5_P&=f!o)- z?ESgO)V;PO#J@14uC3&GCQdtD^}r0w_S1vDeuDgEpx#+X3D9oI_U zG%vuvQy_TH3;>-UkBQb%Y~behV?POd^ehPak@x;XdZ^5tA&Dg9_#h{oI>xN7g;J8@#*TV;uo-_vgU2# zZ!5kBlBlDQfy4hLHrZmS*g1%^{1X?2If}@L&B-BU0G*`+QHw_Gor$A7@|cG6 zk?{rr*lBD))N7!NsLYoIMhQ}{@e68{vdu>@be)~SPp*kvD_DSMJ*ayEJ`(;gZKM_ z_oMj&+PgObGZbC1Qi66UNvTv+Pc{YdNASy&PM;~-x^znGF`G}FOTDV@Brw1(!YBje z;4PnK4$F#jqN|(6`Gq(z0E0D!{JsLSYE_$Xo_zgY62M(IAp1^WfZWWaAu#2F3FCRn zVa5^H$)HW56+nU?dcO&(E=NXuF0fqNh4$(8HJ$D4$mRliX^?#2k)&+1f}^XUoVB~7 zAs;*ggcG?L(01BV0mt!vn#DfwASdwvr@$(l|_5Ntl{W zLxM^c&O@TpoRG-vFzM3?^Zx7+&}VZCu_M_I`FuR6#yJY7rpPoUat&I z<&*d%JW>AbT;+$ol@mWP(8uZEA)_3%K>TZ(<)9kI-AjFwpq@eq1ylMd^#pqZ!yu+z#9YB*0FFf? zJTU|b6fshF&ii#h{I$%~`g$PrZMt~)Yy;Qbi0pr@*wPC~d^%3FZjAh;WB{Bay&i~P z3_$j7<^k>p1Ly{~t}u?Vz+Kl0T+WP$Q_+tQ?N5_=`k&hD69MWJmcZXMj9B78ryoGg4gOtro zIa`sbd@=yl{YGYcGtBmBtUoeaUg_;5WEn2D_;vnzW<{QJfLbeG0#(xwDLS4Nx? z7p?y^hlksT0X~BFC-A=$Fc}L6@g9hOD(}BHH0hF{9eh586q_`&RJ3XXN_iBt1>Lqd zTt5o#R(?Ob*WizA&{T30!wQNec+EVEXvbybgb&W01hWu@@+1MnknmE-^A*KxeI+;+ zm+=`8{sxSC6F_!jbO+Fp_&Ecp86e1GVN@TK%TUxS=Gv#EVhq6L6(i1sEMxB(@pXJ6 zxwrJ7J!b$mRM-y3bejPDinZVc;!guNJ&w<3@NoO^pa($cgX6{w`>{{q`*RB-lSe_@ zM-26AC^fN`teXFu1=a1t6mzswNA%8M1gUVl7h?uj(Lkf9$KZpycY*0#a3wVjEemH2 zSVbQ~BYtEyrnk{FSrbVeM}RwlY98K##IIo(K;32<2ro91w-o|Co4X}mjQDI9G$;j? zpN0TWBgFVqlYTB7*%9`8CH{S305PI#95dgJ;$HDE1*rZK9;6HnF$Ur{Fo2VIhR$_Ri7B4&Cbgm$2_rThqU=fC{-zPU2eIC6noGA;G6LhRF#xW! z?JSzt(m-I(#)S+v~I2J)((l^&G?d`JnyT^Dhh=1Q0Am5gq=9ilaUcVhE-xN#>FkAY!}RS3me;STWnkj0YpOWG|GJRIA@%|GRx?c0 zFkWA@pHdNS=kA<0M6amcnM@2Okj^jqrl7Xf&P8Q-A3^9p8m`v=IGmqoc3}CPtnE(SM@eu>ckaS5d#4j7Z+ZpW`+ZaM8pp@ICOFTCbNZao%L#wwCrs_$3)p*T43KX> zL1i&R$U$ObFiBx(=Ai_FF-3!OLODZ_;0>djLW~CcH-Q-%{C~pPSZIdKUQ6C)Cz&Er zY6Sd|8Qqo0MCQ}%NFxUFIy-&A%uxWrN4+q^7853b=mC-Ql{0YNKu~e6QJ87v^ zbmiCxhBNqN0@Vy&b9rWIG2g)9v!fd3=WhgLw;2uPZwVfKyh6I0(BllNi% zX=LhrGz={EYG|jx;Ks#4>PgfnvksyX>qWpN_*+OB5LH>_%@VmbAb7QaX$My@)06%MEvMK9p9>sp;F3&-&^JDB!V}JEwKhPd; e{|kR{pcIb7*YyAZ002ovPDHLkV1hC=H#IP{LfT>g diff --git a/images/sap_gold_200.jpg b/images/sap_gold_200.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b431dda9114b810ef88e15a8ccb1df9898b17936 GIT binary patch literal 11224 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 Jwa>@O{{bgm>6HKg literal 0 HcmV?d00001 diff --git a/images/save_24.png b/images/save_24.png deleted file mode 100644 index a1c516997a1a5ba5ade530ed9957a0ef77af9acb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1580 zcmV+{2GjY8P)i3JKGcUUwri@|-ES#)vEWt*^UG0U=S`$Cqfo0;=6 zKk4>i$v$oI!;yDka9AE71M(FuYzbDC&qrEGU~{{Ym%kdr$E%+4`e^ zrp;jLHe#~1ZAWfx>mm3!2NZGf5UpCokz>CO_K#y9+1jIk`VuhJ5}>t(t!vp2pVx_mgdqOzdxVaz zv#>8)qI(cd(Q`A#dcJ^_Q$dkkkDW!!@C!i#03RRyZRoZc2gueI1#Bw@V<>>Bt)-c* zZ`r#7pgp)UWWwv6=fcl!j~8}_15TSAOUo~?yf_2SX><6Mx_m&Z)=usA|sWL{_zgU~QQl9Ha4V%RJg3eX7?;PMCH zq$<$osL*-*^3cQCHGt}kU|!9m6gAbctu4FZcH6-FIdEPdJbnQlUyw{>aPvy|kqjtP zp-#<0%DPOXrDq_W=8@4+l(^30qwo6$Cl+up4p3PLrko`}V=Y@{Y{P_TM1j5#8ciya zlQmE$sX-M6nT!flq`i=iEN!|F;Aswmsv4Ir{{y}49J+qEJ7`{91K6quQ>vpnXsTu_ z4cn1ac?25@a%dI)==qoQve-f^CyIK7H1pH~uJ;aLll?~=KJ{>5W*P6s0k#x?Db6N9 zgMqEwYJ|G-QxxQ7LlF2l)9@*Zm`d+?o)1?!wGi|T+#$eed~oW?z=9(Tutovpd0;k$ z0bAIr3ImwR&rv9?fajypN<*pz=`)VwUe*NxO)Ln zCIPmWv(;P6!Bl+}PLV)ntIVIfdnXZd`AFlKgX{D6z71+Ur&JgVz#Ed7>TuC zV*K+iT- z64@E4)C#1iW$@8CU$87;>X{kSlf$sjjX)E423BK5Zh8RgQUEtyG3EuJ`}bfkSsse3 zgM2NRObr2Q^4Mkr3v*f*LV_K$llLH4$B{0d1Do!JE?ow-k{Uf1#MB~>yC#xZ@R{aC z3+4SWS=}M^lSWC-C?Iz|7%c(Xj5@ZXsQ?RfkhPisRPqp~4%xtG`Pr zAE78`X?8>|b|EsaUh;u8dU+?SRZ!|P@C|j{a<3l`ygZ)xc+8WrC&oyM%pVgeA2AXl efY^Uo!uk&vplqw$IABiz0000?NMQuI#s2WO#T@H>P?o1PXB$ zctjR6FmMZjFyp1Wb$@_@Ea{HEjtmSN`?>!lvI6-xJY5_^DsCN}PB2l17P!NHlU_01%yE`)!d$-y6 z?g%H$G&iR=Gv9pQym>3`&i`o<;NpiLY`gh^{Vx8_+wV#Q5G0;><0M&@?OX9HS3d^& z3W zD3GQJtt=yJrNlInrU_{pD?e6T-HmRyOPQCjcgXJEpS)mdd*!7ll%m*$zjntm2qGLk za*$qLjK4+hhH8mx{!~f!nu}26T||6Xq5%UprObDEX<-IcqbRM~Xm!*KGow_eRIPng zqtxoG=svw4`V?E8I}b3nI5hTP@P6;N$+$P?!iD#j+tclj@}hj+%#yQf=h(Qt4#2{5%Y6Fz zCzzQgN%-;Jyx;8(*_~NM(r|VZ^X?}&J`4vK_ z6g_?NOtQSRvT*F!%;gqwE`)G+c6K(E#+3i*4XA_8uALh?JacNT_T4*lh%k8DIlOtt*^_MN+*ne%&^=%cNzof4>ZA3FmPk;TQu vsH)GDX?oB#wqn#FAQ5qMFHK};)C~RxveROC#gWV?00000NkvXXu0mjfG2@lD diff --git a/images/search_48.png b/images/search_48.png deleted file mode 100644 index aa3f0874d602b33113678cc456cc40cc16467156..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3773 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 P00000NkvXXu0mjfm3Sh2 diff --git a/images/share_48.png b/images/share_48.png deleted file mode 100644 index 355bd11daf140c34fd969d3f782618927fe29da4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4738 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 QGXMYp07*qoM6N<$f~lHBtpET3 diff --git a/images/smile_100.gif b/images/smile_100.gif deleted file mode 100644 index d0b42842d36cbabe14b9779f491ece0a2d2ba3c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4051 zcmbW4XHXMfv&KV>N(sFaFo2YRbZOG1D+nqmp;%CA2t^iA}t7^ zgLD!JNEZkYihzXvqVJve&fHJ;-sjBjm$T>0{^pt4nLYh}Is;%aH8e2W-tdU2l)T2)2{&5a{woR6AcvvK+Q@;!%B79 z1`s;ylaA`I0RJT_Y8u+JF3+96z;ISj%>tmNqM@Na>;DYktUC1U9ze@V$97R(m!947 z0TANNq3|Lx=bVUMMGL3Z(59&3L!X!DFK}`5@bZaW5|@yaQc}L6qN=8@f6c(q$k@d6 z*6lmiHh1sYK5}~e#M#Bw&G)IFe*iKtC_ExEDmo?>m6ZG@CH3vQf8OWj+QYZ?ONt#d^j?eP#40rCG79KWKl+{&!$6|6gSP0sC(*B7l*G>dZVER)99(PzoiHO^t$fuWP$J zk4!yy^*edhCQg7Ggb*W=Mw+lYq5{Srb$Nk_Cf!E<*YoolDpIzd&p%#_oFj!W&4hxd ztqEmwCbH|E3sCp_0+02{`x#xrWN;ovbzW&2Jl@I4i9`uP{1KtJ)FgYzkJn z!H2_rb^-fGvD=RDtXC(^3f<47Nlr=hO`iv9y8o;QtbuTrsCJ8)POaK@y9PY(nO2;u z7+;sl6k(ZFZMUrbg8z(u8-PtCJzlNbRcRl1_1+?UCsfo$PBx~x$^YWzL}%J8(L~lP zEyEL@sGz+i!Si5C*DdXNn~dLk_^VK@&dCF}@1GK;WL2-sCfEgF=!ayL;&t2!uvPYx7+<~pXELJa6UeM zQU!cH(=HR{d}fc(*Ki6U>{;LY&hVW9THTqn0d7(vvY}1U;)C5dagsjuQrgn5aB3Yt z-4Tr!qSHS5%3Q=o<5%+Vclu(WX%r!cC1Gd42;#`gz^dF}+I=aDDe zX?I{8bz{4d*KI>1L_onlM!cOms?tBA>o~Wqvx?kQf_@Ifg~0sli%~C&`=xqu4vGY2s5Xn~d&h|N zd22dy-Va|nM5CxGo=2`W=&;8T7uwerM#M$2uhxi4G^%7M(rFM-L61)^iakP zq!00_NPYJz- zgo(BIv=)LdL(N?^uo$zQQ-I<(G%Ui~@^&t{dN|0xp+T%>lewtSwvkUZuJCTAzI+X~ z3Vrup9Q9pv^Xqc=wtCr^=e7Jclo!sym}UG-l{`u2@Lj*oc z7OotNBp>g#4&?vnlul&0tIN-g_`uuMDQc75`WUS=eTk$8Sf^LHqNchde$}d^mDf|b z$1-m%AD-@EA{Zc0Baj>>A@SkJ@s;35UgRj$#J}F zNkQRWL%{avPUYlfAqJ!IL2H|a2KaQ-xF<@YG(krq7X^?!4`4F~thl!H*6xY$Lj*Y$>J}%rD+|hgJ{={sO74ZVE6N3P~)s-XfJFkFe)C8wNhh%8cC6nlL88#cIm6JTvN{K$1S%9YWODN73SSlyf`VjBvOmWXqj}hUe;S4BC-rO3DG-;Wh&j!U}UApWEg1U zLinrh7Bz7U=ftuNSM32^#?))uU{{$p6Wm=gkzbjXedOHV4K{Ly=DEu0=24P1ZhT3V zBdlzdwfS7vnOxyd@f`056-YZWKZS;>PQdKlFSdDboB~|xON@2*QRRSM7UW!=#I3el zY3Y*06?}bV&18+u%7$MZyuIX#Z;%*yFGl%TeX9}X42mrkP0_AgtYF}Cjul~4^W^YN z9!#`1inZ=+87M~fceFJf&vn9uX{F^Wu9_|hFwTo(fn^VuKO>{fHsj0VvMHuW<&tX? z-hO_b#Z*L}KB3aciWdovZfiTRW9@b=ZuCaiJsAW=zB<2R%AwdK6yxQMaXe zO1Ur1A{bV-3)6rVh9`e+*2OXp^+#v7JBtlAdVbi=~U)cLM$#UtTOVxgAM*&AZi!J>?c=17&t(6XKau_rd!GIJgrPfxeabS_O|<;en&uB zZPi5V*yHvCNezevkJ}y>3yc(W9TbRJ!Tg3_9+po{vR3_(uwL$SBlOlL?Ca){CdIc9 z3AcaJ5YI^1PS{8I9Ce)nvS_|vn8(r_@gdGRn9Q95c49qL zK4}64Lz9}7`W5dz;ViF0jX!Hv9LX^o$H?WPpj}rkNaTk7jrkMl4xJ|K$eW%8#UkHkH8l3k3tyNRFFH|AnHU{y z1HG2Q?pnWbJv2>>BnP--Bf7`!wk(9qHunq$F@Cbqj{NHwhY^oY%Y@B9j9k}btYWrW zhnJLN4By&szJO><)kL@eH?geV1QMpNHUSm9$rLtnFXXmF|sJ zfF@EZl+!3ag48Y6p$|5M-I=b8tMsguW#n+D@NA%tsWaJX>?4aM35(X&ziQzQOPNc2 zN!IakSu8oS#T37CCH`g>SML>lGSP{h2RoQ%&YAv{cUN^6qraKZj$otc4GE8EF(Csi zhFj>};X*epy?W>sp`XmW5^8VLW#%cx&O;%_|6tQT1>vuKrMAVqI9(xE7cq(o(uHo51M$-l>uaqjWPI3evyW%er=h z<5sgy#gLsXak3Z|=kml#?ph2FD`iC5!^7idK}tr`$IKE&)36gglqA)YzdO~Ry-Fe* HbUOYoM<%4~ diff --git a/images/xls_24.png b/images/xls_24.png deleted file mode 100644 index d99cd8a5af42dcd74392dc2a0d7a02203eefff07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1889 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 diff --git a/images/zip_24.png b/images/zip_24.png deleted file mode 100644 index 0f34d7e8fbe59514e20c2314ae615d443f913f36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1537 zcmV+c2LAbpP)V`uvt$zjh^aKpNhIm4qREk!{@QclLN{e9Zc_PM z)j#uVMl2TVO}E?e3-?R-EfV>AaxhpoL=**XrUh6x&3=oze10p07UZ2kZSiOJWf}#3w=MyeB$W8|&3#?3tUZUIlVIWQ?_d+o@ zu)Vh%JPs#YJvi__W(PLRsD$?J zZniTpn-Y{o<&GGk788LTmoI?F>0&QUucTR~jFSalC>Q2~-w;6%65blW0q|ts)s|aV zdRyrvCu@3;fL-mmkkONqJuB*j!{$lIimCpkyu;*n#=bFA|+;H&LHTZDgHm^Xo&Qne|cq*8vu)Hi< zKn4I$LIwe91G5YUKYrKpYxD9}#b#q;WzqD-57a`vmKcE-q7mLXWN)}pZF0DY45PBB z`US{}w+hLmg;9WA-O=Fs3g0CxJ;=#IIOSYb;CeVIz{1EM*l2|P+|*2~3+yNn6Hw70cBx)2!Un*ZGYC+NIbaf-Xtr3*RDJjKNw|0~4uvKYu{s>;p5|pF zElYY8DSEbYRm5{3i7D^wy1*zvAQ+4(GJ}a`3#V^FW-lK80jf)EN&{nG^&vx%(_%|!-^_PZoY@Zf%& zm6y+?H_`ddwjGU!zW?TO?kLVP@cVfVWSD5SV3f4j=747peg(eLQnqF3GjuqU(E+gX z5{Y46IO!eRw~TY12RL@@g!hrg1^uEV8nQ4job#lC&18a2$A5#P{Wnl!fW~QM@WoTB zAcfI0JlT4#To$%%`?&G+pC^0Zp-TfOEuG>zeE3L*yRf*7X@@y4JID_(D>cJ=r;bBs ze1K012o#s7ZOzLeBgC=py5;otFbm2MvWlG n8W@ho8h@6b1F~Tm!=(QKO^Z@hby=`c00000NkvXXu0mjfu!hdc 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 @@