diff --git a/inc/databap.php b/inc/databap.php index 4bb0eb2..3b03cfd 100755 --- a/inc/databap.php +++ b/inc/databap.php @@ -7,8 +7,8 @@ class Databap extends PhpObject { //Common Constants - const VERSION = '1.1.3'; //Versioning: ..- - const VERSION_DATE = '08/12/2019'; + const VERSION = '1.1.4'; //Versioning: ..- + const VERSION_DATE = '24/09/2020'; const EXPECTED_PAGE_COOKIE = 'exp_page'; const MAIN_SEPARATOR = ' '; const DATE_FORMAT = 'd/m/Y'; @@ -43,7 +43,7 @@ class Databap extends PhpObject const FILE_TABLE = 'files'; const TABL_TABLE = 'tables'; const TITLE_LEN = 200; - + //Code const MAX_LIST_LENGTH = 5; @@ -144,7 +144,7 @@ class Databap extends PhpObject const OPT_STATUS = 7; const OPT_CONSOLE = 8; const OPT_EMAIL = 9; - + //Search Constants const CODE_TYPE = 'c'; const PROC_TYPE = 'p'; @@ -156,7 +156,7 @@ class Databap extends PhpObject self::TABLE_TYPE=>'table', 'table'=>'table', self::DOC_TYPE=>'doc', 'documentation'=>'doc', 'doc'=>'doc', self::ART_TYPE=>'article', 'art'=>'article', 'article'=>'article', - 'n'=>'note', 'note'=>'note', 'note oss'=>'note', + 'n'=>'note', 'note'=>'note', 'note oss'=>'note', 'list'=>'list', 'liste'=>'list', 'chat'=>'chat', 'profil'=>'profile', 'profile'=>'profile', @@ -172,9 +172,9 @@ class Databap extends PhpObject self::TABLE_TYPE=>array('table' => self::TABL_TABLE, 'title' => 'Table')); public static $PAGE_TITLES = array( 'code'=>'Code', 'chat'=>'Chat', 'doc'=>'Documentation', 'error'=>'Page introuvable', 'list'=>'La liste', 'options'=>'Paramètres', 'procedure'=>'Procédure', 'profile'=>'Profil', - 'search'=>'Recherche', 'welcome'=>'Bienvenue', 'table'=>'Table', 'article'=>'Article de blog', + 'search'=>'Recherche', 'welcome'=>'Bienvenue', 'table'=>'Table', 'article'=>'Article de blog', 'logout'=>'Déconnexion', 'note'=>'Note OSS'); - + //Doc constants const DOC_FOLDER = 'docs/'; const DOC_TMP_FOLDER = 'docs/tmp/'; @@ -206,7 +206,7 @@ class Databap extends PhpObject //Hasher $this->oAuth = new Auth(); - + //Browser <> PHP <> MySql synchronization date_default_timezone_set(Settings::TIMEZONE); ini_set('default_charset', Settings::TEXT_ENC); @@ -216,7 +216,7 @@ class Databap extends PhpObject mb_http_input(Settings::TEXT_ENC); mb_language('uni'); mb_regex_encoding(Settings::TEXT_ENC); - + //Databap Settings $this->setUserId(0); $this->sLanguage = $sLanguage; @@ -225,7 +225,7 @@ class Databap extends PhpObject $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 && Settings::DEBUG==true) $this->install(); } - + public static function getSqlOptions() { $asOptions = array(); @@ -272,7 +272,7 @@ class Databap extends PhpObject MySqlManager::getText(self::CHAN_TABLE) => "varchar(50) NOT NULL", MySqlManager::getText(self::OPTNAME_TABLE) => "varchar(100) NOT NULL", MySqlManager::getText(self::OPTVAL_TABLE)=> "varchar(100) NOT NULL", - 'language' => "varchar(2) 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", @@ -304,7 +304,7 @@ class Databap extends PhpObject ); return $asOptions; } - + public static function getTypeInfo($oInfo=array(), $sType='') { if($sType!='') @@ -313,7 +313,7 @@ class Databap extends PhpObject else $asResult = array($sType=>self::$TYPES[$sType]); } else $asResult = self::$TYPES; - + //info if(is_string($oInfo)) $oInfo = array($oInfo); $bUnique = (count($oInfo)==1); @@ -333,21 +333,21 @@ class Databap extends PhpObject return $asResult; } - + private function getPageTitles($sPage='') { $asPageTitles = self::$PAGE_TITLES; 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 @@ -369,7 +369,7 @@ class Databap extends PhpObject $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_EMAIL, $sOptionNameCol=>'email', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_CHAT_HISTO, $sOptionNameCol=>'historique du chat (jours)', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $this->oMySql->insertRow(self::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_CHAT_IMAGES, $sOptionNameCol=>'afficher les images du chat', 'type'=>self::OPT_CHECKBOX, 'language'=>self::LANG_FR)); - + //Select/checkbox and Default Option values: option_name => array(option_value => is_default, ...) $asDefaultValues = array( self::OPT_CONSOLE=>array(true=>0, false=>1), self::OPT_NICKNAME=>array('Utilisateur inconnu'=>true), @@ -390,7 +390,7 @@ class Databap extends PhpObject $this->oMySql->insertRow(self::OPTVAL_TABLE, array($sOptionNameIdCol=>$sOptionNameId, $sOptionValueCol=>$sOptionValue, 'default_value'=>$bDefault, '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)); @@ -406,7 +406,7 @@ class Databap extends PhpObject //Write the SAP blog parser bash script to main folder $this->updateWebCrawler(); } - + public function updateWebCrawler() { $sContent = "#!/bin/bash\n\ncd /var/www\n\n/usr/bin/php -f index.php a=external_access p=blogs auth_token=".self::SYSTEM_USER_ID.'_'.str_replace('$', '\$', $this->generateExternalAccessToken(self::SYSTEM_USER_ID)); @@ -414,7 +414,7 @@ class Databap extends PhpObject $sDesc = $bSuccess?'Fichier écrit avec succès':'Erreur lors de l\'écriture du fichier à la racine de l\'application. Vérifier les droits sur ce dossier'; return $this->getJsonPostResult($bSuccess, $sDesc); } - + public function goLive() { @@ -475,7 +475,7 @@ class Databap extends PhpObject 'cur_date'=>date(self::DATE_FORMAT), 'serv_name'=>$_GET['serv_name']); $oPage->setTag('constants', $this->jsonConvert($asConstants)); - + //Variables $asVars['page_titles'] = $this->getPageTitles(); $asVars['user_id'] = $this->getUserId(); @@ -484,10 +484,10 @@ class Databap extends PhpObject $asVars['opt_console'] = ($this->getUserOptionValue(self::OPT_CONSOLE)==true); $asVars['opt_chat_images'] = ($this->getUserOptionValue(self::OPT_CHAT_IMAGES)==true); $oPage->setTag('variables', $this->jsonConvert($asVars)); - + return $oPage->getMask(); } - + public function getLogonPage($bFirstConn) { $oPage = new Mask('logon'); @@ -576,12 +576,12 @@ class Databap extends PhpObject } $sMessageIdCol = MySqlManager::getId(self::MSG_TABLE); $sChanIdCol = MySqlManager::getId(self::CHAN_TABLE); - $asResult = $this->oMySql->selectRows(array('select'=>array($sMessageIdCol, 'nickname', $sChanIdCol, 'message', 'led'), - 'from'=>self::MSG_TABLE, + $asResult = $this->oMySql->selectRows(array('select'=>array($sMessageIdCol, 'nickname', $sChanIdCol, 'message', 'led'), + 'from'=>self::MSG_TABLE, 'constraint'=>array('message'=>$sRegEx), 'constOpe'=>array('message'=>' REGEXP '))); $asChans = $this->oMySql->selectRows(array('select'=>array($sChanIdCol, 'safe_name'), 'from'=>self::CHAN_TABLE), true, $sChanIdCol); - + $sPattern = '/(https?\:\/\/|www\.)[\S]+\.[a-zA-Z]{2,4}([\S]*)/ui'; foreach($asResult as $iLinkId=>$asRow) { @@ -591,7 +591,7 @@ class Databap extends PhpObject //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 @@ -608,7 +608,7 @@ class Databap extends PhpObject } } } - + return $oFeed->getFeed(); } @@ -644,7 +644,7 @@ class Databap extends PhpObject private function getSAPBlogRss() { $asArticles = array(); - + //SAP Blogs $sSAPDomain = 'https://blogs.sap.com/'; $asBlogTags = array('73554900100700001049', //Lumira @@ -676,7 +676,7 @@ class Databap extends PhpObject { $asArticleInfo['title'] = $asBlogItem['title']; $asArticleInfo['title'] = mb_substr($asArticleInfo['title'], -1)=='.'?mb_substr($asArticleInfo['title'], 0, -1):$asArticleInfo['title']; - + $asArticleInfo['link'] = $asBlogItem['link']; $asArticleInfo['date'] = date(self::DATE_SQL_FORMAT, strtotime($asBlogItem['pubDate'])); $asNames = array_filter(explode(' ', ToolBox::mb_ucwords(trim($asBlogItem['author'])))); @@ -686,7 +686,7 @@ class Databap extends PhpObject $asArticles[] = $asArticleInfo; } } - + //BI Portal $oDom = $this->getRemotePageDom('http://www.biportal.org/sap_bi_blog'); $aoArticles = $oDom->getElementsByTagName('li'); @@ -720,7 +720,7 @@ class Databap extends PhpObject $asArticles[] = $asArticleInfo; } } - + //SAP BW BW $oDom = $this->getRemotePageDom('http://sapbwbw.com/'); $aoArticles = $oDom->getElementsByTagName('header'); @@ -743,7 +743,7 @@ class Databap extends PhpObject $asArticles[] = $asArticleInfo; } } - + return $asArticles; } @@ -769,7 +769,7 @@ class Databap extends PhpObject break; } } - + //Image if($sSource=='') { @@ -788,11 +788,11 @@ class Databap extends PhpObject } if(isset($o9gagImage)) $sSource = $o9gagImage->getAttribute('src'); } - + $asResult = array(); if(isset($sSource)) $asResult = $this->downloadToTmp($sSource); else $asResult['error'] = 'Not safe for work'; - + if($asResult['error']=='') { $asPost['url_img'] = $asResult['out']; @@ -811,12 +811,12 @@ class Databap extends PhpObject @$oDom->loadHTML(file_get_contents($sUrl)); return $oDom->getElementsByTagName('body')->item(0); } - + public function getNote($iNote) { $this->oClassManagement->incClass('note'); $oNote = new Note($iNote); - return $oNote->getNote(); + return $oNote->getNote(); } public function addUser($sFirstName, $sLastName, $iCompanyId, $sEmail='', $iClearance=self::CLEARANCE_MEMBER, $iUserId=0, $bExt=true) @@ -824,10 +824,10 @@ class Databap extends PhpObject $sFirstName = trim(mb_strtolower($sFirstName)); $sLastName = trim(mb_strtolower($sLastName)); $sEmail = trim(mb_strtolower($sEmail)); - + //Get Company $sCompany = $this->oMySql->selectValue(self::COMP_TABLE, MySqlManager::getText(self::COMP_TABLE), $iCompanyId); - + if(!$sCompany) $sDesc = 'ID entreprise inconnu : '.$iCompanyId; else { @@ -842,24 +842,24 @@ class Databap extends PhpObject if($iUserId>0) $asInfo[MySqlManager::getId(self::USER_TABLE)] = $iUserId; $iUserId = $this->oMySql->insertRow(self::USER_TABLE, $asInfo); $bSuccess = ($iUserId>0); - + if($bSuccess) { //Options & default values $sNickName = self::getNickNameFormat($sFirstName); $this->setOptions(array(self::OPT_NICKNAME=>$sNickName, self::OPT_EMAIL=>$sEmail), true, $iUserId); - + //Spread the word if($iUserId!=self::SYSTEM_USER_ID) $this->addMessage('Nouvel utilisateur: '.self::getNameFormat($sFirstName, $sLastName).' ! Son petit nom sur le chat est '.$sNickName, self::MESSAGE_NEWS, self::DEFAULT_CHAN_ID, self::SYSTEM_USER_ID); - + $sDesc = 'Utilisateur '.$iUserId.' ajouté avec succès'; } else $sDesc = 'Erreur lors de l\'ajout de l\'utilisateur '.$sFirstName.' '.$sLastName.' (company : '.$sCompany.')'; } - + return $bExt?$this->getJsonPostResult($bSuccess, $sDesc):$iUserId; } - + public function addCompany($sCompany, $sLogo='', $bExt=true) { $sCompany = trim(mb_strtolower($sCompany)); @@ -879,14 +879,14 @@ class Databap extends PhpObject else $sDesc = 'Erreur lors de la création de l\'entreprise '.$sCompany; } else $sDesc = 'Nom d\'entreprise vide'; - + return $bExt?$this->getJsonPostResult($bSuccess, $sDesc):$iCompanyId; } - + public function assignUser($iUserId, $iCompany, $bExt=true) { $iUserId = $this->oMySql->updateRow(self::USER_TABLE, $iUserId, array(MySqlManager::getId(self::COMP_TABLE)=>$iCompany)); - + return $bExt?$this->getJsonPostResult(true, ''):$iUserId; } @@ -946,21 +946,21 @@ class Databap extends PhpObject { //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(); - + $oResult = array('result'=>'success', 'proc_id'=>$iNewProcId, 'led'=>$asProc['led'], 'name'=>$asUser['name'], 'company'=>$asUser['company']); - + //Add Message in chat if($bCreation) { @@ -1016,7 +1016,7 @@ class Databap extends PhpObject $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); @@ -1029,12 +1029,12 @@ class Databap extends PhpObject //insert into database $sFileName = $asDocInfo['name']; $asFileInfo = pathinfo($sFileName); - + $asDocData['description'] = $asDocInfo['desc']; $asDocData['hash'] = $asFileInfo['filename'];; $asDocData['extension'] = $asFileInfo['extension']; $iDbImageId = $this->oMySql->insertRow(self::FILE_TABLE, $asDocData); - + //Move file $sTempFilePath = self::DOC_TMP_FOLDER.$sFileName; $sFilePath = self::DOC_FOLDER.$sFileName; @@ -1064,12 +1064,12 @@ class Databap extends PhpObject $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 $sFileIdCol = MySqlManager::getId(self::FILE_TABLE); $asFiles = $this->oMySql->selectRows(array('from'=>self::FILE_TABLE, 'constraint'=>array('id_item'=>$iDocId, 'type'=>self::DOC_TYPE))); @@ -1081,20 +1081,20 @@ class Databap extends PhpObject } return $this->getJsonPostResult($bSuccess, $sDesc, $asDoc); } - + public function addTable($sSystem, $sTitle, $sDescription, $sKeyWords, $iPrevTableId, $bSimul=false) { $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)); - + //Check for existing table with the same name if($bCreation && $this->checkValue(self::TABL_TABLE, array('title'=>$sTitle))) $sDesc = ToolBox::findReplaceLinks('Une documentation existe déjà pour la table '.self::getTableFormat($sTitle)); elseif(!$bCreation && $iPrevTableId!=$this->getUpToDateId(self::TABL_TABLE, $sTitle)) $sDesc = ToolBox::findReplaceLinks('Une version plus récente de la table '.self::getTableFormat($sTitle).' existe'); @@ -1103,26 +1103,26 @@ class Databap extends PhpObject //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); } } 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 = !empty($asTable); @@ -1133,27 +1133,27 @@ class Databap extends PhpObject $asTable['title'] = $asTable['table_name']; unset($asTable['timestamp']); $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 = ToolBox::findReplaceLinks('Il existe une documentation plus à jour pour la table '.self::getTableFormat($asTable['title'])); $asTable['warning'] = ($bReadById && $iTableId!=$this->getUpToDateId(self::TABL_TABLE, $iTableId))?$sRightTableLink:''; } else $sDesc = self::NOT_FOUND; - + 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, @@ -1163,7 +1163,7 @@ class Databap extends PhpObject $asResult = $this->oMySql->selectRows($asInfo); return array_shift($asResult); } - + public function getFile($iFileId) { //File info @@ -1173,7 +1173,7 @@ class Databap extends PhpObject $sFilePath = self::DOC_FOLDER.$asFileInfo['hash'].'.'.$asFileInfo['extension']; $sFileFullPath = dirname($_SERVER['SCRIPT_FILENAME'])."/".$sFilePath; $sResult = ''; - + if(!file_exists($sFilePath)) header("HTTP/1.0 404 Not Found"); else { @@ -1223,7 +1223,7 @@ class Databap extends PhpObject return $this->jsonConvert($asResult); } - + private function getItemInfo($sType, $iItemId) { return call_user_func(array($this, 'get'.ucfirst($this->getPagesFromHash($sType)).'Info'), $iItemId); @@ -1251,7 +1251,7 @@ class Databap extends PhpObject { $asSelect = "*"; } - + $asCode = $this->oMySql->selectRow(self::CODE_TABLE, $iCodeId, $asSelect); $asCode['title'] = self::getDescriptionFormat($asCode['description']); $asCode['description'] = self::getDescriptionFormat($asCode['description']); @@ -1279,7 +1279,7 @@ class Databap extends PhpObject $asDoc['led'] = self::getDateFormat($asDoc['led']); return $asDoc; } - + private function getTableInfo($iTableId, $bFormatting=true) { $asTable = $this->oMySql->selectRow(self::TABL_TABLE, $iTableId); @@ -1304,14 +1304,14 @@ class Databap extends PhpObject $asArticle = $this->getArticlesInfo($iArtId); return array_shift($asArticle); } - + private function getArticlesInfo($iArtId=0) { $sIdCol = MySqlManager::getId(self::ART_TABLE); $asInfo = array('from'=>self::ART_TABLE, 'ordrBy'=>array('date', 'desc')); if($iArtId > 0) $asInfo['constraint'] = array($sIdCol => $iArtId); $asArticles = $this->oMySql->selectRows($asInfo); - + $asTransferredArticles = array(); foreach($asArticles as $asArt) { @@ -1322,15 +1322,15 @@ class Databap extends PhpObject $asTransferredInfo['description'] = self::getDescriptionFormat($asArt['title']); $asTransferredInfo['timestamp'] = strtotime($asArt['led']); $asTransferredInfo['led'] = self::getDateFormat($asArt['led']); - + //Domain $asParsedUrl = parse_url($asArt['link']); $asTransferredInfo['domain'] = $asParsedUrl['host']; $asTransferredInfo['company'] = $asTransferredInfo['domain']; - + $asTransferredArticles[] = $asTransferredInfo; } - + return $asTransferredArticles; } @@ -1338,7 +1338,7 @@ class Databap extends PhpObject { $asUserInfo = array(); if($iUserId==-1) $iUserId = $this->getUserId(); - + if(array_key_exists($iUserId, $this->asUsersInfo)) $asUserInfo = $this->asUsersInfo[$iUserId]; else { @@ -1347,7 +1347,7 @@ class Databap extends PhpObject } return $bJson?$this->jsonExport($asUserInfo):$asUserInfo; } - + private function setUserInfo($iUserId=-1) { if($iUserId==-1) $iUserId = $this->getUserId(); @@ -1359,7 +1359,7 @@ class Databap extends PhpObject { $sEmail = $this->getUserOptionValue(self::OPT_EMAIL, $iUserId); $asCompany = $this->oMySql->selectRow(self::COMP_TABLE, $asRow[MySqlManager::getId(self::COMP_TABLE)]); - + $this->asUsersInfo[$iUserId] = array( 'name'=>self::getNameFormat($asRow['first_name'], $asRow['last_name']), 'nickname'=>self::getNickNameFormat($this->getChatNickNames($iUserId)), 'email'=>$sEmail, @@ -1377,15 +1377,15 @@ class Databap extends PhpObject $asUserInfo = $this->getUserInfo(); return $asUserInfo['clearance']; } - + public function checkUserClearance($iClearance) { return ($this->getUserClearance() >= $iClearance); } - + /* Options Management */ //TODO Create class - + private function getAvailableOptions() { $sOptNameIdCol = MySqlManager::getId(self::OPTNAME_TABLE); @@ -1395,23 +1395,23 @@ class Databap extends PhpObject 'constraint'=>array('language'=>$this->sLanguage)); return $this->oMySql->selectRows($asInfo, true, $sOptNameIdCol); } - + private function getDefaultOptionValue($sOptionNameId) { $asDefaultOptions = $this->getDefaultOptionValues($sOptionNameId); $asOptionInfo = array_shift($asDefaultOptions); return $asOptionInfo[MySqlManager::getText(self::OPTVAL_TABLE)]; } - + private function getDefaultOptionValues($oOptionNameIds=array(), $bOptValId=true) { $sOptNameIdCol = MySqlManager::getId(self::OPTNAME_TABLE); $sOptValIdCol = MySqlManager::getId(self::OPTVAL_TABLE); $sOptValueTextCol = MySqlManager::getText(self::OPTVAL_TABLE); - + if(!is_array($oOptionNameIds)) $oOptionNameIds = array($oOptionNameIds); elseif(empty($oOptionNameIds)) $oOptionNameIds = array_keys($this->getAvailableOptions()); - + $asInfo = array('select' => array($sOptNameIdCol, $sOptValueTextCol), 'from' => self::OPTVAL_TABLE, 'constraint'=> array('default_value'=>true, $sOptNameIdCol => "(".implode(", ", $oOptionNameIds).")"), @@ -1420,14 +1420,14 @@ class Databap extends PhpObject if($bOptValId) $asInfo['select'][] = $sOptValIdCol; 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::getText(self::OPTVAL_TABLE)]; } - + private function getUserOptions($oOptionNameIds=array(), $iUserId=0) { $iUserId = $iUserId>0?$iUserId:$this->getUserId(); @@ -1438,10 +1438,10 @@ class Databap extends PhpObject $sOptValueIdColOptTable = MySqlManager::getFullColumnName(self::OPT_TABLE, $sOptValueIdCol); $sOptValueTextCol = MySqlManager::getText(self::OPTVAL_TABLE); $sOptIdCol = MySqlManager::getId(self::OPT_TABLE); - + if(!is_array($oOptionNameIds)) $oOptionNameIds = array($oOptionNameIds); elseif(empty($oOptionNameIds)) $oOptionNameIds = array_keys($this->getAvailableOptions()); - + $asUserinfo = array('select' => array($sOptIdCol, $sOptNameIdColOptTable, $sOptValueIdColOptTable, $sOptValueTextCol), 'from' => self::OPT_TABLE, 'join' => array(self::OPTVAL_TABLE=>MySqlManager::getId(self::OPTVAL_TABLE)), @@ -1449,17 +1449,17 @@ class Databap extends PhpObject 'constOpe' => array($sUserIdCol=>"=", $sOptNameIdColOptTable => " IN "), 'constVar' => true); $asOptions = $this->oMySql->selectRows($asUserinfo, true, $sOptNameIdCol); - + //No user value: adding default value to user values $asDefaultOptions = $this->getDefaultOptionValues(); foreach($oOptionNameIds as $iOptionNameId) { if(!array_key_exists($iOptionNameId, $asOptions)) $asOptions[$iOptionNameId] = $asDefaultOptions[$iOptionNameId]; } - + return $asOptions; } - + public function getOptions() { $sOptNameTextCol = MySqlManager::getText(self::OPTNAME_TABLE); @@ -1494,14 +1494,14 @@ class Databap extends PhpObject } } ksort($asSelectedOptions); - + //Admin options $asAdminOptions = array(); if($this->checkUserClearance(self::CLEARANCE_ADMIN)) { //Companies $asAdminOptions['companies'] = array_map(array('self', 'getCompanyFormat'), $this->oMySql->selectList(self::COMP_TABLE)); - + //User Names $asFirstNames = $this->oMySql->selectList(self::USER_TABLE, 'first_name'); $asLastNames = $this->oMySql->selectList(self::USER_TABLE, 'last_name'); @@ -1517,7 +1517,7 @@ class Databap extends PhpObject $sOptValueTextCol = MySqlManager::getText(self::OPTVAL_TABLE); $sUserIdCol = MySqlManager::getId(self::USER_TABLE); $sOptNameIdCol = MySqlManager::getId(self::OPTNAME_TABLE); - + $iUserId = ($iUserId>0)?$iUserId:$this->getUserId(); $asAvailableOptions = $this->getAvailableOptions(); $asUserOptions = $this->getUserOptions(array_keys($asAvailableOptions), $iUserId); @@ -1544,7 +1544,7 @@ class Databap extends PhpObject $sNewValue = $asNewOptions[$sOptNameId]; break; } - + //Update text if($sNewValue!=$sUserValue) { @@ -1564,13 +1564,13 @@ class Databap extends PhpObject if($sUserValueId==$sDefaultValueId) $sNewValueId = $this->oMySql->insertRow(self::OPTVAL_TABLE, $asData); else $sNewValueId = $this->oMySql->updateRow(self::OPTVAL_TABLE, $sUserValueId, array($sOptValueTextCol=>$sNewValue)); } - + //Update option table $asKeys = array($sUserIdCol=>$iUserId, $sOptNameIdCol=>$sOptNameId); $asData = array($sOptValueIdCol=>$sNewValueId) + $asKeys; $this->oMySql->insertUpdateRow(self::OPT_TABLE, $asData, array_keys($asKeys)); } - + //Spread the word if(!$bSilentUpdate) { @@ -1596,7 +1596,7 @@ class Databap extends PhpObject } } } - } + } public function getProfile($oUser) { @@ -1617,7 +1617,7 @@ class Databap extends PhpObject $asProfile['user'] = $this->getUserInfo($iUserId); unset($asProfile['user']['email']); unset($asProfile['user']['clearance']); - + //History Info $asTables = $this->getTypeInfo('table'); foreach($asTables as $sType=>$sTableName) @@ -1625,11 +1625,11 @@ class Databap extends PhpObject //skip articles for users $bArticle = ($sType==self::ART_TYPE); if($bArticle && $iUserId!= self::SYSTEM_USER_ID) 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('select'=>MySqlManager::getId($sTableName), 'from'=>$sTableName); if(!$bArticle) $asSqlInfo['constraint'] = array(MySqlManager::getId(self::USER_TABLE)=>$iUserId); @@ -1646,7 +1646,7 @@ class Databap extends PhpObject } } if(array_key_exists('history', $asProfile)) krsort($asProfile['history']); - + return $this->jsonExport($asProfile); } @@ -1710,15 +1710,15 @@ class Databap extends PhpObject //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) $bSuccess = true; - + if($bSuccess) { //Return connected channels @@ -1807,7 +1807,7 @@ class Databap extends PhpObject $this->addMessage('quitte '.($asPm['is_pm']?'le chan privé':'#'.$sChanName), self::MESSAGE_CONN, $iChanId); } } - + public function inviteChan($iUserId, $sChanName) { $sSafeChanName = $this->getChanSafeName($sChanName); @@ -1943,10 +1943,10 @@ class Databap extends PhpObject { $sNewStatus = mb_substr($sMessage, 9); $sNewFormatStatus = $sNewStatus; - + //changing Nickname $this->setOptions(array(self::OPT_STATUS=>$sNewStatus)); - + //Message $sType = self::MESSAGE_STATUS; $sChanName = self::DEFAULT_CHAN; @@ -2033,7 +2033,7 @@ class Databap extends PhpObject $oImage = imagecreatefromstring(base64_decode($sSourceString)); $sRawPath = self::DOC_TMP_FOLDER.uniqid().'.jpeg'; imagejpeg($oImage, $sRawPath, 100); - + //Create thumbnail & display on chat $asImage = $this->downloadToTmp($sRawPath); $bSuccess = ($asImage['error']==''); @@ -2071,7 +2071,7 @@ class Databap extends PhpObject { $sType = self::MESSAGE_PRIVATE; } - + //Storing message if($bSuccess) { @@ -2079,7 +2079,7 @@ class Databap extends PhpObject $bSuccess = $this->addMessage($sMessage, $sType, $sChanId); if(!$bSuccess) $sDesc = self::FAIL_INSERT; } - + return $this->getJsonPostResult($bSuccess, $sDesc); } @@ -2197,7 +2197,7 @@ class Databap extends PhpObject $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) { @@ -2225,11 +2225,11 @@ class Databap extends PhpObject $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 + 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) { @@ -2239,7 +2239,7 @@ class Databap extends PhpObject { //Internal links $asMessages['messages'][$iMessageId]['message'] = Toolbox::findReplaceLinks($asMessages['messages'][$iMessageId]['message']); - + //Dynamic chan link $asPatterns = '/(^|\s)#(\w*[^\s]+\w*)/u'; $asLinks = '\1#\2'; @@ -2255,12 +2255,12 @@ class Databap extends PhpObject return $this->jsonExport($asMessages); } - + public function getNews($bExport=true) { $sMsgIdCol = MySqlManager::getId(self::MSG_TABLE); $sMsgTxtCol = MySqlManager::getText(self::MSG_TABLE); - + //News $asInfo['select'] = array($sMsgIdCol, 'nickname', $sMsgTxtCol, 'type', 'led'); $asInfo['from'] = self::MSG_TABLE; @@ -2268,12 +2268,12 @@ class Databap extends PhpObject $asInfo['orderBy'] = array('led'=>'DESC'); if($bExport) $asInfo['limit'] = self::MAX_NB_NEWS; $asNews = $this->oMySql->selectRows($asInfo); - + //Status //$asInfo['select'] = array($sMsgIdCol, 'nickname', "CONCAT(nickname, SPACE(1), ".$sMsgTxtCol." AS ".$sMsgTxtCol, 'led'); $asInfo['constraint'] = array('type'=>self::MESSAGE_STATUS); $asStatus = $this->oMySql->selectRows($asInfo); - + //Sorting //FIXME find a way to do it in SQL $asNews = array_merge($asNews, $asStatus); @@ -2285,7 +2285,7 @@ class Databap extends PhpObject $asNews2[] = $asNews[$iKey]; if($bExport && count($asNews2)==self::MAX_NB_NEWS) break; } - + $asFormatNews = array(); foreach($asNews2 as $asNew) { @@ -2296,7 +2296,7 @@ class Databap extends PhpObject $asFormatNews[$iListId]['message'] = self::getDescriptionFormat($asNew['message']); $asFormatNews[$iListId]['nickname'] = self::getNickNameFormat($asNew['nickname']); } - + $sSuccess = (count($asFormatNews)>0); return $bExport?$this->getJsonPostResult($sSuccess, $sSuccess?'':'Aucune news', array('news'=>$asFormatNews)):$asFormatNews; } @@ -2353,7 +2353,7 @@ class Databap extends PhpObject 'afk' => $asUser['afk'] ); } - + return $bJson?$this->jsonExport($asConnectedUsers):$asConnectedUsers; } @@ -2370,7 +2370,7 @@ class Databap extends PhpObject $sUserIdCol = MySqlManager::getId(self::USER_TABLE); $sNicknameCol = MySqlManager::getText(self::OPTVAL_TABLE); $sOptionNameId = MySqlManager::getFullColumnName(self::OPT_TABLE, MySqlManager::getId(self::OPTNAME_TABLE)); - + $asInfo = array('select'=>array($sUserIdCol, $sNicknameCol), 'from'=>self::OPT_TABLE, 'join'=> array(self::OPTVAL_TABLE=>MySqlManager::getId(self::OPTVAL_TABLE)), @@ -2418,7 +2418,7 @@ class Databap extends PhpObject 'constraint'=>array('refer_id'=>$iRefCodeId), 'orderBy'=>array('id_code'=>'asc')); $asCodeIds = $this->oMySql->selectRows($asInfo); - + foreach($asCodeIds as $iCodeId) { $asCodeVersions[] = $this->getCodeInfo($iCodeId); @@ -2488,16 +2488,16 @@ class Databap extends PhpObject $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, + $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); @@ -2525,7 +2525,7 @@ class Databap extends PhpObject { $asUsers = $asItemList = array(); $sIdUserCol = MySqlManager::getId(self::USER_TABLE); - + //TODO phrases for all item types? $asTypeTables = $this->getTypeInfo('table'); foreach($asTypeTables as $sType=>$sTable) @@ -2536,7 +2536,7 @@ class Databap extends PhpObject 'from' => $sTable, 'groupBy' => array(($sType==self::ART_TYPE)?$sTableIdCol:'refer_id'), //no versioning for articles 'orderBy' => array('id_item'=>'desc')); - + //Build code info structure $asTypedItemIds = array_filter($this->oMySql->selectRows($asInfo)); //null value returned from query (MAX(...)) foreach($asTypedItemIds as $iItemId) @@ -2545,17 +2545,17 @@ class Databap extends PhpObject $asItem = $this->getItemInfo($sType, $iItemId); $asItem['type'] = $sType; $asItem['id_item'] = $iItemId; - + //Replacing user's id with user's name & company (if not done already) - if(array_key_exists($sIdUserCol, $asItem)) + if(array_key_exists($sIdUserCol, $asItem)) { $iUserId = $asItem[$sIdUserCol]; if(!array_key_exists($iUserId, $asUsers)) $asUsers[$iUserId] = $this->getUserInfo($iUserId); - + $asItem['name'] = $asUsers[$iUserId]['name']; $asItem['company'] = $asUsers[$iUserId]['company']; } - + //Preparing key for reverse sorting $asItemList[$asItem['timestamp'].$asItem['type'].$asItem['id_item']] = $asItem; } @@ -2584,7 +2584,7 @@ class Databap extends PhpObject $sNameToken = ''; $bResetPass = true; $sUserTableId = MySqlManager::getId(self::USER_TABLE); - + //login using form if($sAction!=self::EXT_ACCESS) { @@ -2592,14 +2592,14 @@ class Databap extends PhpObject { $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($iUserId==self::SYSTEM_USER_ID || !$this->oAuth->CheckPassword($sPassToken, $this->oMySql->selectValue(self::USER_TABLE, 'pass', $iUserId))) $iUserId = 0; } @@ -2609,12 +2609,12 @@ class Databap extends PhpObject $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')); if(!empty($asUserInfo)) { $iUserId = $asUserInfo[$sUserTableId]; - + //Reset pass once a day $bResetPass = (mb_substr($asUserInfo['led'], 0, 10) != date(Databap::DATE_SQL_FORMAT)); } @@ -2631,7 +2631,7 @@ class Databap extends PhpObject { $this->setUserId($iUserId); if($bResetPass) $this->resetAuthCookie(); - + //Post-Redirect-Get if user manually logging from logon page if($sNameToken!='') { @@ -2643,12 +2643,12 @@ class Databap extends PhpObject return ($this->getUserId()>0); } - + private static function getLoginToken($sPass) { return md5($sPass.str_replace('https://', 'http://', $_GET['serv_name'])); } - + public function checkSetPass($sToken, $sNewToken) { $bSuccess = false; @@ -2659,10 +2659,10 @@ class Databap extends PhpObject $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 resetPass($iUserId) { $bSuccess = false; @@ -2704,7 +2704,7 @@ class Databap extends PhpObject else $this->addError('generating token : invalid user id "'.$iUserId.'"'); return $sPass; } - + private function generateExternalAccessToken($iUserId) { return $this->oAuth->HashPassword($this->getExternalAccessPass($iUserId)); @@ -2718,18 +2718,18 @@ class Databap extends PhpObject 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']. @@ -2739,7 +2739,7 @@ class Databap extends PhpObject $_SERVER['SERVER_SIGNATURE']. $_SERVER['SERVER_ADMIN']); } - + private function resetAuthCookie() { $iUserId = $this->getUserId(); @@ -2779,7 +2779,7 @@ class Databap extends PhpObject { return $this->jsonExport($this->getArticleInfo($iArtId)); } - + public function redirectArticle($iArtId) { $asArtInfo = $this->getArticleInfo($iArtId); @@ -2804,7 +2804,7 @@ class Databap extends PhpObject { return $this->oMySql->selectValue($sTableName, 'COUNT(1)', $asConstraints); } - + public function getUrlAvailability($sLink) { $bAvailable = !$this->checkValue(self::URL_TABLE, array('phrase'=>$sLink)); @@ -2840,7 +2840,7 @@ class Databap extends PhpObject { //Init Search Engine $this->oSearchEngine = new SearchEngine($this->oMySql); - + $this->oSearchEngine->setWords($sSearchWords); $asResults = $this->oSearchEngine->getResults(); @@ -2852,7 +2852,7 @@ class Databap extends PhpObject $iItemId = $asItemInfo['id_item']; $sType = $asItemInfo['type']; $asItemInfo += $this->getItemInfo($sType, $iItemId); - + //User Info if($sType != self::ART_TYPE) //Already available in item info { @@ -2861,7 +2861,7 @@ class Databap extends PhpObject $asItemInfo['company'] = $asUserInfo['company']; } //TODO: phrase - + $asCompleteResults[] = $asItemInfo; } @@ -2889,7 +2889,7 @@ class Databap extends PhpObject header("Content-Type: text/css"); return $sStyle; } - + public function addUncaughtError($sError) { $this->addError('Uncaught errors:'."\n".$sError); @@ -2921,18 +2921,18 @@ class Databap extends PhpObject { return ToolBox::mb_ucfirst(ToolBox::findReplaceLinks($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'); @@ -2944,7 +2944,7 @@ class Databap extends PhpObject //return htmlspecialchars(json_encode($asData), ENT_NOQUOTES); return json_encode($asData); } - + public static function getMaxSize() { $iPostSize = self::toBytes(ini_get('post_max_size')); @@ -2966,4 +2966,4 @@ class Databap extends PhpObject } } -?> \ No newline at end of file +?> diff --git a/jquery/push.min.js b/jquery/push.min.js new file mode 100644 index 0000000..2e7744c --- /dev/null +++ b/jquery/push.min.js @@ -0,0 +1,39 @@ +/** + * @license + * + * Push v1.0.9 + * ========= + * A compact, cross-browser solution for the JavaScript Notifications API + * + * Credits + * ------- + * Tsvetan Tsvetkov (ttsvetko) + * Alex Gibson (alexgibson) + * + * License + * ------- + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2017 Tyler Nickerson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +!function(i,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(i=i||self).Push=t()}(this,function(){"use strict";var i={errors:{incompatible:"".concat("PushError:"," Push.js is incompatible with browser."),invalid_plugin:"".concat("PushError:"," plugin class missing from plugin manifest (invalid plugin). Please check the documentation."),invalid_title:"".concat("PushError:"," title of notification must be a string"),permission_denied:"".concat("PushError:"," permission request declined"),sw_notification_error:"".concat("PushError:"," could not show a ServiceWorker notification due to the following reason: "),sw_registration_error:"".concat("PushError:"," could not register the ServiceWorker due to the following reason: "),unknown_interface:"".concat("PushError:"," unable to create notification: unknown interface")}};function t(i){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(i){return typeof i}:function(i){return i&&"function"==typeof Symbol&&i.constructor===Symbol&&i!==Symbol.prototype?"symbol":typeof i})(i)}function n(i,t){if(!(i instanceof t))throw new TypeError("Cannot call a class as a function")}function e(i,t){for(var n=0;n0?this._requestWithCallback.apply(this,arguments):this._requestAsPromise()}},{key:"_requestWithCallback",value:function(i,t){var n,e=this,o=this.get(),r=!1,s=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:e._win.Notification.permission;r||(r=!0,void 0===n&&e._win.webkitNotifications&&(n=e._win.webkitNotifications.checkPermission()),n===e.GRANTED||0===n?i&&i():t&&t())};o!==this.DEFAULT?s(o):this._win.webkitNotifications&&this._win.webkitNotifications.checkPermission?this._win.webkitNotifications.requestPermission(s):this._win.Notification&&this._win.Notification.requestPermission?(n=this._win.Notification.requestPermission(s))&&n.then&&n.then(s).catch(function(){t&&t()}):i&&i()}},{key:"_requestAsPromise",value:function(){var i=this,t=this.get(),n=t!==this.DEFAULT,e=this._win.Notification&&this._win.Notification.requestPermission,o=this._win.webkitNotifications&&this._win.webkitNotifications.checkPermission;return new Promise(function(r,s){var c,a=!1,u=function(t){a||(a=!0,!function(t){return t===i.GRANTED||0===t}(t)?s():r())};n?u(t):o?i._win.webkitNotifications.requestPermission(function(i){u(i)}):e?(c=i._win.Notification.requestPermission(u))&&c.then&&c.then(u).catch(s):r()})}},{key:"has",value:function(){return this.get()===this.GRANTED}},{key:"get",value:function(){return this._win.Notification&&this._win.Notification.permission?this._win.Notification.permission:this._win.webkitNotifications&&this._win.webkitNotifications.checkPermission?this._permissions[this._win.webkitNotifications.checkPermission()]:navigator.mozNotification?this.GRANTED:this._win.external&&this._win.external.msIsSiteMode?this._win.external.msIsSiteMode()?this.GRANTED:this.DEFAULT:this.GRANTED}}]),i}(),f=function(){function i(){n(this,i)}return o(i,null,[{key:"isUndefined",value:function(i){return void 0===i}},{key:"isNull",value:function(i){return null===obj}},{key:"isString",value:function(i){return"string"==typeof i}},{key:"isFunction",value:function(i){return i&&"[object Function]"==={}.toString.call(i)}},{key:"isObject",value:function(i){return"object"===t(i)}},{key:"objectMerge",value:function(i,t){for(var n in t)i.hasOwnProperty(n)&&this.isObject(i[n])&&this.isObject(t[n])?this.objectMerge(i[n],t[n]):i[n]=t[n]}}]),i}(),l=function i(t){n(this,i),this._win=t},h=function(i){function t(){return n(this,t),a(this,s(t).apply(this,arguments))}return r(t,l),o(t,[{key:"isSupported",value:function(){return void 0!==this._win.Notification}},{key:"create",value:function(i,t){return new this._win.Notification(i,{icon:f.isString(t.icon)||f.isUndefined(t.icon)||f.isNull(t.icon)?t.icon:t.icon.x32,body:t.body,tag:t.tag,requireInteraction:t.requireInteraction})}},{key:"close",value:function(i){i.close()}}]),t}(),_=function(t){function e(){return n(this,e),a(this,s(e).apply(this,arguments))}return r(e,l),o(e,[{key:"isSupported",value:function(){return void 0!==this._win.navigator&&void 0!==this._win.navigator.serviceWorker}},{key:"getFunctionBody",value:function(i){var t=i.toString().match(/function[^{]+{([\s\S]*)}$/);return null!=t&&t.length>1?t[1]:null}},{key:"create",value:function(t,n,e,o,r){var s=this;this._win.navigator.serviceWorker.register(o),this._win.navigator.serviceWorker.ready.then(function(o){var c={id:t,link:e.link,origin:document.location.href,onClick:f.isFunction(e.onClick)?s.getFunctionBody(e.onClick):"",onClose:f.isFunction(e.onClose)?s.getFunctionBody(e.onClose):""};void 0!==e.data&&null!==e.data&&(c=Object.assign(c,e.data)),o.showNotification(n,{icon:e.icon,body:e.body,vibrate:e.vibrate,tag:e.tag,data:c,requireInteraction:e.requireInteraction,silent:e.silent}).then(function(){o.getNotifications().then(function(i){o.active.postMessage(""),r(i)})}).catch(function(t){throw new Error(i.errors.sw_notification_error+t.message)})}).catch(function(t){throw new Error(i.errors.sw_registration_error+t.message)})}},{key:"close",value:function(){}}]),e}(),v=function(i){function t(){return n(this,t),a(this,s(t).apply(this,arguments))}return r(t,l),o(t,[{key:"isSupported",value:function(){return void 0!==this._win.navigator.mozNotification}},{key:"create",value:function(i,t){var n=this._win.navigator.mozNotification.createNotification(i,t.body,t.icon);return n.show(),n}}]),t}(),d=function(i){function t(){return n(this,t),a(this,s(t).apply(this,arguments))}return r(t,l),o(t,[{key:"isSupported",value:function(){return void 0!==this._win.external&&void 0!==this._win.external.msIsSiteMode}},{key:"create",value:function(i,t){return this._win.external.msSiteModeClearIconOverlay(),this._win.external.msSiteModeSetIconOverlay(f.isString(t.icon)||f.isUndefined(t.icon)?t.icon:t.icon.x16,i),this._win.external.msSiteModeActivate(),null}},{key:"close",value:function(){this._win.external.msSiteModeClearIconOverlay()}}]),t}(),w=function(i){function t(){return n(this,t),a(this,s(t).apply(this,arguments))}return r(t,l),o(t,[{key:"isSupported",value:function(){return void 0!==this._win.webkitNotifications}},{key:"create",value:function(i,t){var n=this._win.webkitNotifications.createNotification(t.icon,i,t.body);return n.show(),n}},{key:"close",value:function(i){i.cancel()}}]),t}();return new(function(){function t(i){n(this,t),this._currentId=0,this._notifications={},this._win=i,this.Permission=new u(i),this._agents={desktop:new h(i),chrome:new _(i),firefox:new v(i),ms:new d(i),webkit:new w(i)},this._configuration={serviceWorker:"/serviceWorker.min.js",fallback:function(i){}}}return o(t,[{key:"_closeNotification",value:function(t){var n=!0,e=this._notifications[t];if(void 0!==e){if(n=this._removeNotification(t),this._agents.desktop.isSupported())this._agents.desktop.close(e);else if(this._agents.webkit.isSupported())this._agents.webkit.close(e);else{if(!this._agents.ms.isSupported())throw n=!1,new Error(i.errors.unknown_interface);this._agents.ms.close()}return n}return!1}},{key:"_addNotification",value:function(i){var t=this._currentId;return this._notifications[t]=i,this._currentId++,t}},{key:"_removeNotification",value:function(i){var t=!1;return this._notifications.hasOwnProperty(i)&&(delete this._notifications[i],t=!0),t}},{key:"_prepareNotification",value:function(i,t){var n,e=this;return n={get:function(){return e._notifications[i]},close:function(){e._closeNotification(i)}},t.timeout&&setTimeout(function(){n.close()},t.timeout),n}},{key:"_serviceWorkerCallback",value:function(i,t,n){var e=this,o=this._addNotification(i[i.length-1]);navigator&&navigator.serviceWorker&&(navigator.serviceWorker.addEventListener("message",function(i){var t=JSON.parse(i.data);"close"===t.action&&Number.isInteger(t.id)&&e._removeNotification(t.id)}),n(this._prepareNotification(o,t))),n(null)}},{key:"_createCallback",value:function(i,t,n){var e,o=this,r=null;if(t=t||{},e=function(i){o._removeNotification(i),f.isFunction(t.onClose)&&t.onClose.call(o,r)},this._agents.desktop.isSupported())try{r=this._agents.desktop.create(i,t)}catch(e){var s=this._currentId,c=this.config().serviceWorker;this._agents.chrome.isSupported()&&this._agents.chrome.create(s,i,t,c,function(i){return o._serviceWorkerCallback(i,t,n)})}else this._agents.webkit.isSupported()?r=this._agents.webkit.create(i,t):this._agents.firefox.isSupported()?this._agents.firefox.create(i,t):this._agents.ms.isSupported()?r=this._agents.ms.create(i,t):(t.title=i,this.config().fallback(t));if(null!==r){var a=this._addNotification(r),u=this._prepareNotification(a,t);f.isFunction(t.onShow)&&r.addEventListener("show",t.onShow),f.isFunction(t.onError)&&r.addEventListener("error",t.onError),f.isFunction(t.onClick)&&r.addEventListener("click",t.onClick),r.addEventListener("close",function(){e(a)}),r.addEventListener("cancel",function(){e(a)}),n(u)}n(null)}},{key:"create",value:function(t,n){var e,o=this;if(!f.isString(t))throw new Error(i.errors.invalid_title);return e=this.Permission.has()?function(i,e){try{o._createCallback(t,n,i)}catch(i){e(i)}}:function(e,r){o.Permission.request().then(function(){o._createCallback(t,n,e)}).catch(function(){r(i.errors.permission_denied)})},new Promise(e)}},{key:"count",value:function(){var i,t=0;for(i in this._notifications)this._notifications.hasOwnProperty(i)&&t++;return t}},{key:"close",value:function(i){var t;for(t in this._notifications)if(this._notifications.hasOwnProperty(t)&&this._notifications[t].tag===i)return this._closeNotification(t)}},{key:"clear",value:function(){var i,t=!0;for(i in this._notifications)this._notifications.hasOwnProperty(i)&&(t=t&&this._closeNotification(i));return t}},{key:"supported",value:function(){var i=!1;for(var t in this._agents)this._agents.hasOwnProperty(t)&&(i=i||this._agents[t].isSupported());return i}},{key:"config",value:function(i){return(void 0!==i||null!==i&&f.isObject(i))&&f.objectMerge(this._configuration,i),this._configuration}},{key:"extend",value:function(t){var n,e={}.hasOwnProperty;if(!e.call(t,"plugin"))throw new Error(i.errors.invalid_plugin);for(var o in e.call(t,"config")&&f.isObject(t.config)&&null!==t.config&&this.config(t.config),n=new(0,t.plugin)(this.config()))e.call(n,o)&&f.isFunction(n[o])&&(this[o]=n[o])}}]),t}())("undefined"!=typeof window?window:global)}); +//# sourceMappingURL=push.min.js.map diff --git a/jquery/push.min.js.map b/jquery/push.min.js.map new file mode 100644 index 0000000..8d28da4 --- /dev/null +++ b/jquery/push.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"push.min.js","sources":["../src/push/Messages.js","../src/push/Permission.js","../src/push/Util.js","../src/agents/AbstractAgent.js","../src/agents/DesktopAgent.js","../src/agents/MobileChromeAgent.js","../src/agents/MobileFirefoxAgent.js","../src/agents/MSAgent.js","../src/agents/WebKitAgent.js","../src/index.js","../src/push/Push.js"],"sourcesContent":["// @flow\nconst errorPrefix = 'PushError:';\n\nexport default {\n errors: {\n incompatible: `${errorPrefix} Push.js is incompatible with browser.`,\n invalid_plugin: `${errorPrefix} plugin class missing from plugin manifest (invalid plugin). Please check the documentation.`,\n invalid_title: `${errorPrefix} title of notification must be a string`,\n permission_denied: `${errorPrefix} permission request declined`,\n sw_notification_error: `${errorPrefix} could not show a ServiceWorker notification due to the following reason: `,\n sw_registration_error: `${errorPrefix} could not register the ServiceWorker due to the following reason: `,\n unknown_interface: `${errorPrefix} unable to create notification: unknown interface`\n }\n};\n","// @flow\nimport type { Global } from 'types';\n\nexport default class Permission {\n // Private members\n _permissions: string[];\n _win: Global;\n\n // Public members\n GRANTED: string;\n DEFAULT: string;\n DENIED: string;\n\n constructor(win: Global) {\n this._win = win;\n this.GRANTED = 'granted';\n this.DEFAULT = 'default';\n this.DENIED = 'denied';\n this._permissions = [this.GRANTED, this.DEFAULT, this.DENIED];\n }\n\n /**\n * Requests permission for desktop notifications\n * @param {Function} onGranted - Function to execute once permission is granted\n * @param {Function} onDenied - Function to execute once permission is denied\n * @return {void, Promise}\n */\n request(onGranted: () => void, onDenied: () => void) {\n return arguments.length > 0\n ? this._requestWithCallback(...arguments)\n : this._requestAsPromise();\n }\n\n /**\n * Old permissions implementation deprecated in favor of a promise based one\n * @deprecated Since V1.0.4\n * @param {Function} onGranted - Function to execute once permission is granted\n * @param {Function} onDenied - Function to execute once permission is denied\n * @return {void}\n */\n _requestWithCallback(onGranted: () => void, onDenied: () => void) {\n const existing = this.get();\n\n var resolved = false;\n var resolve = (result = this._win.Notification.permission) => {\n if (resolved) return;\n resolved = true;\n if (typeof result === 'undefined' && this._win.webkitNotifications)\n result = this._win.webkitNotifications.checkPermission();\n if (result === this.GRANTED || result === 0) {\n if (onGranted) onGranted();\n } else if (onDenied) onDenied();\n };\n var request;\n\n /* Permissions already set */\n if (existing !== this.DEFAULT) {\n resolve(existing);\n } else if (\n this._win.webkitNotifications &&\n this._win.webkitNotifications.checkPermission\n ) {\n /* Safari 6+, Legacy webkit browsers */\n this._win.webkitNotifications.requestPermission(resolve);\n } else if (\n this._win.Notification &&\n this._win.Notification.requestPermission\n ) {\n /* Safari 12+ */\n /* This resolve argument will only be used in Safari */\n /* CHrome, instead, returns a Promise */\n request = this._win.Notification.requestPermission(resolve);\n if (request && request.then) {\n /* Chrome 23+ */\n request.then(resolve).catch(function() {\n if (onDenied) onDenied();\n });\n }\n } else if (onGranted) {\n /* Let the user continue by default */\n onGranted();\n }\n }\n\n /**\n * Requests permission for desktop notifications in a promise based way\n * @return {Promise}\n */\n _requestAsPromise(): Promise {\n const existing = this.get();\n\n let isGranted = result => result === this.GRANTED || result === 0;\n\n /* Permissions already set */\n var hasPermissions = existing !== this.DEFAULT;\n\n /* Safari 6+, Chrome 23+ */\n var isModernAPI =\n this._win.Notification && this._win.Notification.requestPermission;\n\n /* Legacy webkit browsers */\n var isWebkitAPI =\n this._win.webkitNotifications &&\n this._win.webkitNotifications.checkPermission;\n\n return new Promise((resolvePromise, rejectPromise) => {\n var resolved = false;\n var resolver = result => {\n if (resolved) return;\n resolved = true;\n isGranted(result) ? resolvePromise() : rejectPromise();\n };\n var request;\n\n if (hasPermissions) {\n resolver(existing);\n } else if (isWebkitAPI) {\n this._win.webkitNotifications.requestPermission(result => {\n resolver(result);\n });\n } else if (isModernAPI) {\n /* Safari 12+ */\n /* This resolver argument will only be used in Safari */\n /* CHrome, instead, returns a Promise */\n request = this._win.Notification.requestPermission(resolver);\n if (request && request.then) {\n /* Chrome 23+ */\n request.then(resolver).catch(rejectPromise);\n }\n } else resolvePromise();\n });\n }\n\n /**\n * Returns whether Push has been granted permission to run\n * @return {Boolean}\n */\n has() {\n return this.get() === this.GRANTED;\n }\n\n /**\n * Gets the permission level\n * @return {Permission} The permission level\n */\n get() {\n let permission;\n\n /* Safari 6+, Chrome 23+ */\n if (this._win.Notification && this._win.Notification.permission)\n permission = this._win.Notification.permission;\n else if (\n this._win.webkitNotifications &&\n this._win.webkitNotifications.checkPermission\n )\n /* Legacy webkit browsers */\n permission = this._permissions[\n this._win.webkitNotifications.checkPermission()\n ];\n else if (navigator.mozNotification)\n /* Firefox Mobile */\n permission = this.GRANTED;\n else if (this._win.external && this._win.external.msIsSiteMode)\n /* IE9+ */\n permission = this._win.external.msIsSiteMode()\n ? this.GRANTED\n : this.DEFAULT;\n else permission = this.GRANTED;\n\n return permission;\n }\n}\n","// @flow\nexport default class Util {\n static isUndefined(obj) {\n return obj === undefined;\n }\n\n static isNull(obs) {\n return obj === null;\n }\n\n static isString(obj) {\n return typeof obj === 'string';\n }\n\n static isFunction(obj) {\n return obj && {}.toString.call(obj) === '[object Function]';\n }\n\n static isObject(obj) {\n return typeof obj === 'object';\n }\n\n static objectMerge(target, source) {\n for (var key in source) {\n if (\n target.hasOwnProperty(key) &&\n this.isObject(target[key]) &&\n this.isObject(source[key])\n ) {\n this.objectMerge(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n }\n}\n","// @flow\nimport type { Global } from 'types';\n\nexport default class AbstractAgent {\n _win: Global;\n\n constructor(win: Global) {\n this._win = win;\n }\n}\n","// @flow\nimport { AbstractAgent } from 'agents';\nimport { Util } from 'push';\nimport type { PushOptions, GenericNotification, Global } from 'types';\n\n/**\n * Notification agent for modern desktop browsers:\n * Safari 6+, Firefox 22+, Chrome 22+, Opera 25+\n */\nexport default class DesktopAgent extends AbstractAgent {\n _win: Global;\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return this._win.Notification !== undefined;\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title: string, options: PushOptions) {\n return new this._win.Notification(title, {\n icon:\n Util.isString(options.icon) ||\n Util.isUndefined(options.icon) ||\n Util.isNull(options.icon)\n ? options.icon\n : options.icon.x32,\n body: options.body,\n tag: options.tag,\n requireInteraction: options.requireInteraction\n });\n }\n\n /**\n * Close a given notification\n * @param notification - notification to close\n */\n close(notification: GenericNotification) {\n notification.close();\n }\n}\n","// @flow\nimport { Util, Messages } from 'push';\nimport { AbstractAgent } from 'agents';\nimport type { Global, GenericNotification, PushOptions } from 'types';\n\n/**\n * Notification agent for modern desktop browsers:\n * Safari 6+, Firefox 22+, Chrome 22+, Opera 25+\n */\nexport default class MobileChromeAgent extends AbstractAgent {\n _win: Global;\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return (\n this._win.navigator !== undefined &&\n this._win.navigator.serviceWorker !== undefined\n );\n }\n\n /**\n * Returns the function body as a string\n * @param func\n */\n getFunctionBody(func: () => void) {\n const str = func.toString().match(/function[^{]+{([\\s\\S]*)}$/);\n return typeof str !== 'undefined' && str !== null && str.length > 1\n ? str[1]\n : null;\n }\n\n /**\n * Creates a new notification\n * @param id ID of notification\n * @param title Title of notification\n * @param options Options object\n * @param serviceWorker ServiceWorker path\n * @param callback Callback function\n */\n create(\n id: number,\n title: string,\n options: PushOptions,\n serviceWorker: string,\n callback: (GenericNotification[]) => void\n ) {\n /* Register ServiceWorker */\n this._win.navigator.serviceWorker.register(serviceWorker);\n\n this._win.navigator.serviceWorker.ready\n .then(registration => {\n /* Local data the service worker will use */\n let localData = {\n id: id,\n link: options.link,\n origin: document.location.href,\n onClick: Util.isFunction(options.onClick)\n ? this.getFunctionBody(options.onClick)\n : '',\n onClose: Util.isFunction(options.onClose)\n ? this.getFunctionBody(options.onClose)\n : ''\n };\n\n /* Merge the local data with user-provided data */\n if (options.data !== undefined && options.data !== null)\n localData = Object.assign(localData, options.data);\n\n /* Show the notification */\n registration\n .showNotification(title, {\n icon: options.icon,\n body: options.body,\n vibrate: options.vibrate,\n tag: options.tag,\n data: localData,\n requireInteraction: options.requireInteraction,\n silent: options.silent\n })\n .then(() => {\n registration.getNotifications().then(notifications => {\n /* Send an empty message so the ServiceWorker knows who the client is */\n registration.active.postMessage('');\n\n /* Trigger callback */\n callback(notifications);\n });\n })\n .catch(function(error) {\n throw new Error(\n Messages.errors.sw_notification_error +\n error.message\n );\n });\n })\n .catch(function(error) {\n throw new Error(\n Messages.errors.sw_registration_error + error.message\n );\n });\n }\n\n /**\n * Close all notification\n */\n close() {\n // Can't do this with service workers\n }\n}\n","// @flow\nimport { AbstractAgent } from 'agents';\nimport type { Global, PushOptions } from 'types';\n\n/**\n * Notification agent for modern desktop browsers:\n * Safari 6+, Firefox 22+, Chrome 22+, Opera 25+\n */\nexport default class MobileFirefoxAgent extends AbstractAgent {\n _win: Global;\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return this._win.navigator.mozNotification !== undefined;\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title: string, options: PushOptions) {\n let notification = this._win.navigator.mozNotification.createNotification(\n title,\n options.body,\n options.icon\n );\n\n notification.show();\n\n return notification;\n }\n}\n","// @flow\nimport { AbstractAgent } from 'agents';\nimport { Util } from 'push';\nimport type { PushOptions, Global } from 'types';\n\n/**\n * Notification agent for IE9\n */\nexport default class MSAgent extends AbstractAgent {\n _win: Global;\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return (\n this._win.external !== undefined &&\n this._win.external.msIsSiteMode !== undefined\n );\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title: string, options: PushOptions) {\n /* Clear any previous notifications */\n this._win.external.msSiteModeClearIconOverlay();\n\n this._win.external.msSiteModeSetIconOverlay(\n Util.isString(options.icon) || Util.isUndefined(options.icon)\n ? options.icon\n : options.icon.x16,\n title\n );\n\n this._win.external.msSiteModeActivate();\n\n return null;\n }\n\n /**\n * Close a given notification\n * @param notification - notification to close\n */\n close() {\n this._win.external.msSiteModeClearIconOverlay();\n }\n}\n","// @flow\nimport { AbstractAgent } from 'agents';\nimport type { Global, GenericNotification, PushOptions } from 'types';\n\n/**\n * Notification agent for old Chrome versions (and some) Firefox\n */\nexport default class WebKitAgent extends AbstractAgent {\n _win: Global;\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return this._win.webkitNotifications !== undefined;\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title: string, options: PushOptions) {\n let notification = this._win.webkitNotifications.createNotification(\n options.icon,\n title,\n options.body\n );\n\n notification.show();\n\n return notification;\n }\n\n /**\n * Close a given notification\n * @param notification - notification to close\n */\n close(notification: GenericNotification) {\n notification.cancel();\n }\n}\n","// @flow\nimport { Push } from 'push';\n\nexport default new Push(typeof window !== 'undefined' ? window : global);\n","// @flow\nimport { Messages, Permission, Util } from 'push';\nimport type { PluginManifest, GenericNotification, PushOptions } from 'types';\n\n/* Import notification agents */\nimport {\n DesktopAgent,\n MobileChromeAgent,\n MobileFirefoxAgent,\n MSAgent,\n WebKitAgent\n} from 'agents';\n\nexport default class Push {\n // Private members\n _agents: {\n desktop: DesktopAgent,\n chrome: MobileChromeAgent,\n firefox: MobileFirefoxAgent,\n ms: MSAgent,\n webkit: WebKitAgent\n };\n _configuration: {\n serviceWorker: string,\n fallback: ({}) => void\n };\n _currentId: number;\n _notifications: {};\n _win: {};\n\n // Public members\n Permission: Permission;\n\n constructor(win: {}) {\n /* Private variables */\n\n /* ID to use for new notifications */\n this._currentId = 0;\n\n /* Map of open notifications */\n this._notifications = {};\n\n /* Window object */\n this._win = win;\n\n /* Public variables */\n this.Permission = new Permission(win);\n\n /* Agents */\n this._agents = {\n desktop: new DesktopAgent(win),\n chrome: new MobileChromeAgent(win),\n firefox: new MobileFirefoxAgent(win),\n ms: new MSAgent(win),\n webkit: new WebKitAgent(win)\n };\n\n this._configuration = {\n serviceWorker: '/serviceWorker.min.js',\n fallback: function(payload) {}\n };\n }\n\n /**\n * Closes a notification\n * @param id ID of notification\n * @returns {boolean} denotes whether the operation was successful\n * @private\n */\n _closeNotification(id: number | string) {\n let success = true;\n const notification = this._notifications[id];\n\n if (notification !== undefined) {\n success = this._removeNotification(id);\n\n /* Safari 6+, Firefox 22+, Chrome 22+, Opera 25+ */\n if (this._agents.desktop.isSupported())\n this._agents.desktop.close(notification);\n else if (this._agents.webkit.isSupported())\n /* Legacy WebKit browsers */\n this._agents.webkit.close(notification);\n else if (this._agents.ms.isSupported())\n /* IE9 */\n this._agents.ms.close();\n else {\n success = false;\n throw new Error(Messages.errors.unknown_interface);\n }\n\n return success;\n }\n\n return false;\n }\n\n /**\n * Adds a notification to the global dictionary of notifications\n * @param {Notification} notification\n * @return {Integer} Dictionary key of the notification\n * @private\n */\n _addNotification(notification: GenericNotification) {\n const id = this._currentId;\n this._notifications[id] = notification;\n this._currentId++;\n return id;\n }\n\n /**\n * Removes a notification with the given ID\n * @param {Integer} id - Dictionary key/ID of the notification to remove\n * @return {Boolean} boolean denoting success\n * @private\n */\n _removeNotification(id: number | string) {\n let success = false;\n\n if (this._notifications.hasOwnProperty(id)) {\n /* We're successful if we omit the given ID from the new array */\n delete this._notifications[id];\n success = true;\n }\n\n return success;\n }\n\n /**\n * Creates the wrapper for a given notification\n *\n * @param {Integer} id - Dictionary key/ID of the notification\n * @param {Map} options - Options used to create the notification\n * @returns {Map} wrapper hashmap object\n * @private\n */\n _prepareNotification(id: number, options: PushOptions) {\n let wrapper;\n\n /* Wrapper used to get/close notification later on */\n wrapper = {\n get: () => {\n return this._notifications[id];\n },\n\n close: () => {\n this._closeNotification(id);\n }\n };\n\n /* Autoclose timeout */\n if (options.timeout) {\n setTimeout(() => {\n wrapper.close();\n }, options.timeout);\n }\n\n return wrapper;\n }\n\n /**\n * Find the most recent notification from a ServiceWorker and add it to the global array\n * @param notifications\n * @private\n */\n _serviceWorkerCallback(\n notifications: GenericNotification[],\n options: PushOptions,\n resolve: ({} | null) => void\n ) {\n let id = this._addNotification(notifications[notifications.length - 1]);\n\n /* Listen for close requests from the ServiceWorker */\n if (navigator && navigator.serviceWorker) {\n navigator.serviceWorker.addEventListener('message', event => {\n const data = JSON.parse(event.data);\n\n if (data.action === 'close' && Number.isInteger(data.id))\n this._removeNotification(data.id);\n });\n\n resolve(this._prepareNotification(id, options));\n }\n\n resolve(null);\n }\n\n /**\n * Callback function for the 'create' method\n * @return {void}\n * @private\n */\n _createCallback(\n title: string,\n options: PushOptions,\n resolve: ({} | null) => void\n ) {\n let notification = null;\n let onClose;\n\n /* Set empty settings if none are specified */\n options = options || {};\n\n /* onClose event handler */\n onClose = id => {\n /* A bit redundant, but covers the cases when close() isn't explicitly called */\n this._removeNotification(id);\n if (Util.isFunction(options.onClose)) {\n options.onClose.call(this, notification);\n }\n };\n\n /* Safari 6+, Firefox 22+, Chrome 22+, Opera 25+ */\n if (this._agents.desktop.isSupported()) {\n try {\n /* Create a notification using the API if possible */\n notification = this._agents.desktop.create(title, options);\n } catch (e) {\n const id = this._currentId;\n const sw = this.config().serviceWorker;\n const cb = notifications =>\n this._serviceWorkerCallback(\n notifications,\n options,\n resolve\n );\n /* Create a Chrome ServiceWorker notification if it isn't supported */\n if (this._agents.chrome.isSupported()) {\n this._agents.chrome.create(id, title, options, sw, cb);\n }\n }\n /* Legacy WebKit browsers */\n } else if (this._agents.webkit.isSupported())\n notification = this._agents.webkit.create(title, options);\n else if (this._agents.firefox.isSupported())\n /* Firefox Mobile */\n this._agents.firefox.create(title, options);\n else if (this._agents.ms.isSupported())\n /* IE9 */\n notification = this._agents.ms.create(title, options);\n else {\n /* Default fallback */\n options.title = title;\n this.config().fallback(options);\n }\n\n if (notification !== null) {\n const id = this._addNotification(notification);\n const wrapper = this._prepareNotification(id, options);\n\n /* Notification callbacks */\n if (Util.isFunction(options.onShow))\n notification.addEventListener('show', options.onShow);\n\n if (Util.isFunction(options.onError))\n notification.addEventListener('error', options.onError);\n\n if (Util.isFunction(options.onClick))\n notification.addEventListener('click', options.onClick);\n\n notification.addEventListener('close', () => {\n onClose(id);\n });\n\n notification.addEventListener('cancel', () => {\n onClose(id);\n });\n\n /* Return the wrapper so the user can call close() */\n resolve(wrapper);\n }\n\n /* By default, pass an empty wrapper */\n resolve(null);\n }\n\n /**\n * Creates and displays a new notification\n * @param {Array} options\n * @return {Promise}\n */\n create(title: string, options: {}): Promise {\n let promiseCallback;\n\n /* Fail if no or an invalid title is provided */\n if (!Util.isString(title)) {\n throw new Error(Messages.errors.invalid_title);\n }\n\n /* Request permission if it isn't granted */\n if (!this.Permission.has()) {\n promiseCallback = (resolve: () => void, reject: string => void) => {\n this.Permission\n .request()\n .then(() => {\n this._createCallback(title, options, resolve);\n })\n .catch(() => {\n reject(Messages.errors.permission_denied);\n });\n };\n } else {\n promiseCallback = (resolve: () => void, reject: string => void) => {\n try {\n this._createCallback(title, options, resolve);\n } catch (e) {\n reject(e);\n }\n };\n }\n\n return new Promise(promiseCallback);\n }\n\n /**\n * Returns the notification count\n * @return {Integer} The notification count\n */\n count() {\n let count = 0;\n let key;\n\n for (key in this._notifications)\n if (this._notifications.hasOwnProperty(key)) count++;\n\n return count;\n }\n\n /**\n * Closes a notification with the given tag\n * @param {String} tag - Tag of the notification to close\n * @return {Boolean} boolean denoting success\n */\n close(tag: string) {\n let key, notification;\n\n for (key in this._notifications) {\n if (this._notifications.hasOwnProperty(key)) {\n notification = this._notifications[key];\n\n /* Run only if the tags match */\n if (notification.tag === tag) {\n /* Call the notification's close() method */\n return this._closeNotification(key);\n }\n }\n }\n }\n\n /**\n * Clears all notifications\n * @return {Boolean} boolean denoting whether the clear was successful in closing all notifications\n */\n clear() {\n let key,\n success = true;\n\n for (key in this._notifications)\n if (this._notifications.hasOwnProperty(key))\n success = success && this._closeNotification(key);\n\n return success;\n }\n\n /**\n * Denotes whether Push is supported in the current browser\n * @returns {boolean}\n */\n supported() {\n let supported = false;\n\n for (var agent in this._agents)\n if (this._agents.hasOwnProperty(agent))\n supported = supported || this._agents[agent].isSupported();\n\n return supported;\n }\n\n /**\n * Modifies settings or returns all settings if no parameter passed\n * @param settings\n */\n config(settings?: {}) {\n if (\n typeof settings !== 'undefined' ||\n (settings !== null && Util.isObject(settings))\n )\n Util.objectMerge(this._configuration, settings);\n\n return this._configuration;\n }\n\n /**\n * Copies the functions from a plugin to the main library\n * @param plugin\n */\n extend(manifest: PluginManifest) {\n var plugin,\n Plugin,\n hasProp = {}.hasOwnProperty;\n\n if (!hasProp.call(manifest, 'plugin')) {\n throw new Error(Messages.errors.invalid_plugin);\n } else {\n if (\n hasProp.call(manifest, 'config') &&\n Util.isObject(manifest.config) &&\n manifest.config !== null\n ) {\n this.config(manifest.config);\n }\n\n Plugin = manifest.plugin;\n plugin = new Plugin(this.config());\n\n for (var member in plugin) {\n if (\n hasProp.call(plugin, member) &&\n Util.isFunction(plugin[member])\n )\n // $FlowFixMe\n this[member] = plugin[member];\n }\n }\n }\n}\n"],"names":["errors","incompatible","invalid_plugin","invalid_title","permission_denied","sw_notification_error","sw_registration_error","unknown_interface","Permission","win","_win","GRANTED","DEFAULT","DENIED","_permissions","this","onGranted","onDenied","arguments","length","_requestWithCallback","_requestAsPromise","request","existing","get","resolved","resolve","result","_this","Notification","permission","webkitNotifications","checkPermission","requestPermission","then","hasPermissions","isModernAPI","isWebkitAPI","Promise","resolvePromise","rejectPromise","resolver","_this2","isGranted","navigator","mozNotification","external","msIsSiteMode","Util","obj","undefined","obs","toString","call","_typeof","target","source","key","hasOwnProperty","isObject","objectMerge","AbstractAgent","DesktopAgent","title","options","icon","isString","isUndefined","isNull","x32","body","tag","requireInteraction","notification","close","MobileChromeAgent","serviceWorker","func","str","match","id","callback","register","ready","registration","localData","link","origin","document","location","href","onClick","isFunction","getFunctionBody","onClose","data","Object","assign","showNotification","vibrate","silent","getNotifications","notifications","active","postMessage","error","Error","Messages","message","MobileFirefoxAgent","createNotification","show","MSAgent","msSiteModeClearIconOverlay","msSiteModeSetIconOverlay","x16","msSiteModeActivate","WebKitAgent","cancel","_currentId","_notifications","_agents","desktop","chrome","firefox","ms","webkit","_configuration","fallback","payload","success","_removeNotification","isSupported","wrapper","_closeNotification","timeout","setTimeout","_addNotification","addEventListener","event","JSON","parse","action","Number","isInteger","_prepareNotification","_this3","create","e","sw","config","_serviceWorkerCallback","onShow","onError","promiseCallback","has","reject","_this4","_createCallback","count","supported","agent","settings","manifest","plugin","hasProp","member","Plugin","window","global"],"mappings":"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"} \ No newline at end of file diff --git a/masks/chat.html b/masks/chat.html index aaac08a..5ec6bf1 100755 --- a/masks/chat.html +++ b/masks/chat.html @@ -24,7 +24,7 @@
- +
@@ -80,7 +80,7 @@ databap.pageInit = function() databap.consts.chanPrefix = 'chan_'; databap.consts.chanQuitPrefix = 'quit_chan_'; databap.consts.chanUnreadPrefix = 'unread_chan_'; - + //Page variables self.tmp('sending_msg', 'boolean'); self.tmp('refresh', 'boolean'); @@ -90,47 +90,50 @@ databap.pageInit = function() self.tmp('last_message_id', '0'); self.tmp('scrolling', 'boolean'); databap.tmp('tab_info', 'object'); - + //Main elements $MsgInput = databap.getMainElem('#message'); $MsgInputBox = databap.getMainElem('#chat_input'); - + //Bind message box key events databap.getMainElem('#chat_form').submit(function(e){e.preventDefault();}); $MsgInput.bind('keydown', function(e){if(e.which == 9)e.preventDefault();}); $MsgInput.keyup(function(e){keyController(e);}); - + //Loading the chat self.initScrollBar('#chat_container', '#chat_messages_box', '#chat_messages'); - + //Loading Chans setChanButton(); databap.vars.chans_list[databap.consts.all_chan_id] = databap.consts.all_chan_text; databap.tmp(['unread_msg', databap.consts.all_chan_text], 0); joinChan(databap.consts.default_chan, true); - + //window focus $(window).focus(onPageFocus); $(window).blur(onPageBlur); - + //Message Input Focus $MsgInput.focus(); //Init sidebar events initSideBar(); - + //Setup Image upload initImageUpload(); - + //Init's end databap.setInitEnd(true); + + //Notification Permission + if(!Push.Permission.has()) Push.Permission.request(()=>{}, ()=>{}); }; //On resize databap.onResize = function() { self.setScrollBarSize('maximize'); - + //Message Input //$('#message').width($('#chat_input').width() - $('#message_img').outerWidth(true)- 1); }; @@ -152,7 +155,7 @@ databap.onKeyDown = function(e) }; function initImageUpload() -{ +{ //Load Uploader self.tmp('uploader', new qq.FileUploader ( @@ -161,13 +164,13 @@ function initImageUpload() action: databap.getActionLink('upload_image'), allowedExtensions: databap.consts.authorized_img_exts, sizeLimit: parseInt(databap.consts.max_size)*100, - template: '
' + + template: '
' + '
' + 'Glisser les images ici' + '
' + '' + '
' + - '
    ' + + '
      ' + '
      ', onComplete:function(id, fileName, result) { @@ -189,17 +192,17 @@ function initImageUpload() } )); $('.uploader_box').append($('#message_input').children()); - + //Prompt file explorer $('#upload_img').click(function(){$('#c1_file').click();}); - + //Clipboard handling $(function(){$.pasteimage(function(sSource) { //Parse the uri to strip out "base64" var sSourceSplit = sSource.split("base64,"); var sSourceString = sSourceSplit[1]; - + //Write base64-encoded string into input field postMessage('/imgsrc '+sSourceString); });}); @@ -219,7 +222,7 @@ function initSideBar() .addClass('round') .closest('.sidebar_box').find('.sidebar_box_content') .hide('fast'); - + $This .toggleClass('round') .closest('.sidebar_box').find('.sidebar_box_content') @@ -268,11 +271,11 @@ function getNews() }); } else $('
      ', {'class':'new'}).hide().text('Aucune news.').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'))); }, @@ -325,7 +328,7 @@ function setChanButton() var $This = $(this); joinChan($This.val()); $This.val(''); - + } if(typeof e.keyCode == 'undefined' || e.keyCode == 13) { @@ -336,7 +339,7 @@ function setChanButton() }) .blur(function(){$(this).keyup();}) ).hide() - ); + ); } function getChanKeyName(sChanName) @@ -367,7 +370,7 @@ function joinChan(sChanName, bFirstConn, asAttendees, bSwitchOnJoin) asAttendees = asAttendees || []; bSwitchOnJoin = (typeof bSwitchOnJoin === 'undefined')?true:bSwitchOnJoin; var sChanName = ucwords(sChanName); - + if(sChanName!='') { //Join for the first time @@ -393,10 +396,10 @@ function joinChan(sChanName, bFirstConn, asAttendees, bSwitchOnJoin) if(!getChanName(iChanId)) addChanTab(iChanId, sReturnedChanName, data.channel_tab_names[iChanId], !bSwitchOnJoin); } ); - + //Full reload of messages refresh_chat(true); - + //Switch to the right chan switchChan(bSwitchOnJoin?data.current_chan_id:null); } @@ -416,7 +419,7 @@ function joinChan(sChanName, bFirstConn, asAttendees, bSwitchOnJoin) switchChan(bSwitchOnJoin?getChanKeyName(sChanName):null); } } - + //Focus input bar $('#message').focus(); } @@ -449,26 +452,26 @@ function switchChan(sChanKeyName) { var sChanName = currentChan(getChanName(sChanKeyName)); sChanKeyName = sChanKeyName || getChanKeyName(sChanName); - + //Unread msg - if(databap.vars.focus) + if(databap.vars.focus) { databap.tmp(['unread_msg', sChanName], 0); databap.tmp(['unread_msg', databap.consts.all_chan_text], 0); } syncUnreadMsg(); - + //Switch current channel button to active $('#chat_chan').find('span[id^="'+databap.consts.chanPrefix+'"]').removeClass('active').addClass('clickable'); $('#'+databap.consts.chanPrefix+sChanKeyName).addClass('active').removeClass('clickable'); - + //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(); - + //Update scrollbar databap.updateScrollBar(self.tmp('scrolling')?'relative':'bottom'); - + //Show Current channel members databap.getMainElem('#connected_users').find('p').hide(); databap.getMainElem('#connected_users').find('p.class_'+sChanKeyName).show(); @@ -511,7 +514,7 @@ function addChanTab(sChanKeyName, sChanName, sChanTabName, bLightUp) sChanTabName = sChanTabName || sChanName; databap.vars.chans_list[sChanKeyName] = sChanName; databap.tmp(['unread_msg', sChanName], 0); - + //Add channel tab var sChanId = databap.consts.chanPrefix+sChanKeyName; if(!$('#'+sChanId).length) @@ -524,8 +527,8 @@ function addChanTab(sChanKeyName, sChanName, sChanTabName, bLightUp) .append(''+sChanTabName+'') .append('') .append($('').click(function(e){e.stopPropagation();quitChan($(this).attr('id').substr(databap.consts.chanQuitPrefix.length));})); - - + + //Add channel tab name and bind delete button $('#join_chan').before($ChanButton); } @@ -549,16 +552,16 @@ function quitChan(sChanKeyName) databap.showError(textStatus); } ); - + //Delete tab $('#'+databap.consts.chanPrefix+sChanKeyName).fadeOut('fast', function(){$(this).remove();}); - + //Delete from list delete databap.vars.chans_list[sChanKeyName]; - + //Switch back to another chan if(sChanName == currentChan()) switchChan(Object.keys(databap.vars.chans_list)[1]); - + //Delete messages databap.getMainElem('#chat_messages').find('p.class_'+sChanKeyName).remove(); } @@ -614,7 +617,7 @@ function keyController(e) else if(chat_message == '/help') displayHelp(); else if(chat_message.substr(0, 8) == '/invite ') invite($.trim(chat_message.substr(8)), currentChan()); else if(chat_message != '') bSuccess = postMessage(); - + if(bSuccess) { databap.vars.prev_chat_value = chat_message; @@ -644,11 +647,11 @@ function keyController(e) $This = databap.getMainElem('#message'); var sChatMsg = $This.val(); var iChatMsgLen = sChatMsg.length; - + if(!e.altKey && iChatMsgLen>0) { var iCurPos, iFirstPos, iOffset, sWord, sWordLen, bInvite; - + //Loading previous data bLooping = !$.isEmptyObject(databap.tmp('tab_info')); if(bLooping) @@ -665,7 +668,7 @@ function keyController(e) iFirstPos = sChatMsg.substr(0, iCurPos).lastIndexOf(' ') + 1; iOffset = sChatMsg.substr(iFirstPos).indexOf(' '); sWord = sChatMsg.substr(iFirstPos, (iOffset==-1)?iChatMsgLen:iOffset); - + //@ excluded if(sWord.substr(0, 1)=='@') { @@ -675,35 +678,35 @@ function keyController(e) } sWordLen = sWord.length; bInvite = (sChatMsg.substr(0, 7) == '/invite'); - + //No guess if not at least one letter if(sWordLen>0 || bInvite) { //turn to safename for matching var sSafeWord = getSafeNickname(sWord); var iSafeWordLen = sSafeWord.length; - + //List of current channel users var asUserList = databap.tmp(['users', bInvite?databap.consts.all_chan_id:getChanKeyName(currentChan())]); var asSafeUserList = Object.keys(asUserList); var iUserListLen = asSafeUserList.length; - + //Starting list where we left off var iCount = 0; var iIndex = 0; - if(bLooping) iIndex = (Number(array_search(databap.tmp(['tab_info', 'safe_nickname']), asSafeUserList)) + 1)%iUserListLen; - + if(bLooping) iIndex = (Number(array_search(databap.tmp(['tab_info', 'safe_nickname']), asSafeUserList)) + 1)%iUserListLen; + //Looping on user names to find the one(s) starting with the searched word while(iCount < iUserListLen) { //Checking for first letters of user names if(sSafeWord == asSafeUserList[iIndex].substr(0, sSafeWord.length)) break; - + //Looping to the top of the list once finished iIndex = (iIndex + 1)%iUserListLen; iCount++; } - + if(iCount !== iUserListLen) { //Replace text in message input box @@ -713,7 +716,7 @@ function keyController(e) var sNickName = asUserList[sSafeNickName]; $This.val(sBeforeWord+sNickName+sAfterWord); $This.setCursorPosition((sBeforeWord+sNickName).length); - + //Save value for tab-loops if(bLooping) databap.tmp(['tab_info', 'safe_nickname'], sSafeNickName); //not saving index in case of user list refresh else databap.tmp(['tab_info'], {'chat_msg':sChatMsg, 'cur_pos':iCurPos, 'first_pos':iFirstPos, 'word':sWord, 'safe_nickname':sSafeNickName}); @@ -734,7 +737,7 @@ function keyController(e) { $this.width(100); } - else if(iInputSize > maxSize) + else if(iInputSize > maxSize) { $this.width(maxSize); } @@ -771,7 +774,7 @@ function refresh_chat(bReset) { //Update last read message id self.tmp('last_message_id', Math.max(result.last_message_id, prevLastMsgId)); - + //Display messages $.each ( @@ -781,14 +784,14 @@ function refresh_chat(bReset) addMessage(message_info, bReset); } ); - + //Image display setImageDisplay(); - + //News if(databap.tmp('get_news') === true && !bReset) getNews(); databap.tmp('get_news', false); - + //Nicknames changes if(updateUsersList === true || bReset) { @@ -818,22 +821,25 @@ 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 = ''; var sUnreadChanName, iUnreadCount; var bSystemMsg = false; var bImage = false; + var sNotif = ''; switch(message_info.msg_class) { case databap.consts.msg_types.user: msg_body = sMsgPrefix+''+message_info.message+''; + sNotif = message_info.nickname+' : '+message_info.message; break; case databap.consts.msg_types.news: databap.tmp('get_news', true); msg_body = ''+message_info.nickname+' a une news : '+message_info.message+' '; + sNotif = message_info.nickname+' a une news : '+message_info.message; break; case databap.consts.msg_types.add.code: var url = databap.getInternalLink('code', message_info.message); @@ -859,6 +865,7 @@ function addMessage(message_info, bReset) msgTargetUser = message_info.message.substr(1, slicePos - 1); var msg = message_info.message.slice(slicePos + 1); msg_body = ''+message_info.nickname+' @'+msgTargetUser+'>'+msg+''; + sNotif = message_info.nickname+' @'+msgTargetUser+' : '+msg; break; case databap.consts.msg_types.nick: msg_body = ''+message_info.message+' ('+message_info.name+')'; @@ -875,6 +882,7 @@ function addMessage(message_info, bReset) var msg = ''; msg_body = sMsgPrefix+''+msg+''; bImage = true; + sNotif = message_info.nickname+' a posté une image'; break; case databap.consts.msg_types.reboot: if(!bReset) @@ -915,7 +923,7 @@ function addMessage(message_info, bReset) msg_body = ''+message_info.nickname+' a modifié la table '+message_info.description+''; break; } - + if(msg_body != '') { //Hide image button @@ -929,7 +937,7 @@ function addMessage(message_info, bReset) $Banner.find('.banner_title').width(message_info.message.width - 31); $Msg.find('a').addClass('img_box').append($Banner); } - + //Adding message to channel panel $Message = $('

      ', {'class':message_info.msg_class+' class_'+sChanKeyName+' hide round_right'}) .append($('', {'class':'time'}).text(((message_info.date==databap.consts.cur_date)?'':message_info.date+' - ')+message_info.time)) @@ -937,15 +945,28 @@ function addMessage(message_info, bReset) $Message.find('.chan_link').click(function(){joinChan($(this).find('.chan_text').text());}); $Message.find('.user').click(setPm); databap.getMainElem('#chat_messages').append($Message); - + //Unread messages if(!bReset && !bSystemMsg /* && message_info.id_user!=databap.vars.user_id */) { sUnreadChanName = getChanName(message_info.id_chan); iUnreadCount = (databap.tmp(['unread_msg', sUnreadChanName]) || 0) + 1; databap.tmp(['unread_msg', sUnreadChanName], iUnreadCount); + + //Desktop Notification + if(!databap.vars.focus && sNotif != '' && Push.Permission.has()) { + Push.create('Databap - '+message_info.nickname, { + body: sNotif, + icon: databap.consts.app_image_folder+(databap.tmp(['logos', message_info.id_user]) || 'logo_25.png'), + timeout: 4000, + onClick: function () { + window.focus(); + this.close(); + } + }); + } } - + //Move to bottom if user is typing if(message_info.id_user===databap.vars.user_id) self.tmp('scrolling', false); } @@ -963,7 +984,7 @@ function setImageDisplay() $Images.find('.banner .fa').toggleClass('fa-c-image', !self.vars2('opt_chat_images')); $Images.find('.banner .fa').toggleClass('fa-c-close', self.vars2('opt_chat_images')); $Images.find('.banner .button').attr('title', (self.vars2('opt_chat_images')?'Cacher':'Afficher')+' les images'); - + databap.updateScrollBar('bottom'); } @@ -982,7 +1003,7 @@ function refresh_users() databap.getMainElem('#connected_users').empty(); databap.tmp('users', {}); databap.tmp('id_users', {}); - + $.each ( result, @@ -1000,7 +1021,8 @@ function refresh_users() databap.tmp(['users', sChankeyName, sSafeNickName], sNickName); databap.tmp(['users', databap.consts.all_chan_id, sSafeNickName], sNickName); databap.tmp(['id_users', sNickName], user_info.id_user); - + databap.tmp(['logos', user_info.id_user], user_info.logo); + //Append name to user list box var sProfileLink = databap.getInternalLink('profil', user_info.id_user); var sMission = user_info.status || ''; @@ -1034,7 +1056,7 @@ function refresh_users() function getSafeNickname(sNickName) { - return sNickName.stripVowelAccent().toLowerCase(); + return sNickName.stripVowelAccent().toLowerCase(); } function setPm() @@ -1059,14 +1081,14 @@ function displayHelp() { //Get template $Help = $('#help').clone().attr('id', 'help_'+Math.floor((Math.random()*1000))); - + //Visible on all channels $Help.find('p').addClass('class_'+databap.consts.all_chan_id); $Help.appendTo('#chat_messages').slideDown('fast', function() { databap.updateScrollBar('bottom'); }); - + //Close button activation $Help.find('#close_help').click(function() { @@ -1076,4 +1098,4 @@ function displayHelp() }); }); } - \ No newline at end of file + diff --git a/masks/index.html b/masks/index.html index f4a70ea..52057c4 100755 --- a/masks/index.html +++ b/masks/index.html @@ -11,6 +11,7 @@ +