diff --git a/app/Resources/public/css/chat.css b/app/Resources/public/css/chat.css index 6f9d2a9e1d..8578b287e3 100644 --- a/app/Resources/public/css/chat.css +++ b/app/Resources/public/css/chat.css @@ -1,13 +1,5 @@ -body{ - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - line-height: 18px; - color: #333333; - margin: 0; - padding: 0; -} .page-chat{ - padding: 10px; + padding: 10px 0; background:#ffffff; } .message-form-chat .nav{ @@ -16,48 +8,7 @@ body{ .message-form-chat .nav-tabs .active a{ background-color: #f6f6f6; } -#clear-chat{ - width:140px; - padding: 13px; - font-size: 14px; - float:right; -} -#clear-chat img{ - width: 20px; - float: left; - margin-right: 5px; -} -#clear-chat a{ - text-decoration: none; -} -#clear-chat a:hover{ - opacity: 0.7; -} -#content-chat{ - background:#ffffff; - padding: 30px; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; -} - -textarea { - width: 52%; - padding: 3%; - height: 100px; - float: left; - border: 0; - background-color: #ffffff; - - font-family: Helvetica, arial, sans-serif; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - border: 1px solid #EEEEEE; - color: #666; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35) inset; -} .message-student .chat-image, .message-teacher .chat-image{ width: 80px; height: auto; @@ -85,7 +36,7 @@ textarea { font-size: 13px; padding: 15px; vertical-align: top; - width: 310px; + width: calc(100% - 100px); } .message-teacher .icon-message{ display: inline-block; @@ -121,7 +72,7 @@ textarea { font-size: 13px; padding: 15px; vertical-align: top; - width: 360px; + width: calc(100% - 100px); } .message-teacher .message-date{ color: #666; @@ -138,121 +89,30 @@ textarea { text-align: right; margin-top: 10px; } -.user-online .profile{ - list-style: none; - padding: 0; - margin:0; -} -.chat-message-block-name { - font-style: italic; - color: #666; -} -.user-online .profile li{ - margin-bottom: 5px; -} -.user-online .profile li a{ - color: #0088AA; - text-decoration: none; -} -.user-online .title{ - background: url(chat.png) no-repeat 0 center; - color: #0088AA; - padding-top: 5px; - padding-left: 30px; - font-weight: bold; - font-size: 16px; - margin-bottom: 15px; - width: 90%; -} -.delete-chat{ - background: #F8F8F8; - padding-bottom: 15px; -} -.list-group { - margin-bottom: 20px; - padding-left: 0; -} -.list-group-item { - position: relative; - display: block; - padding-top: 5px; - padding-bottom: 10px; - padding-left: 10px; - padding-right: 10px; - margin-bottom: -1px; - background-color: #EEEEEE; - border: 1px solid #E2E2E2; - -webkit-border-radius: 10px; + +.chat-user { + background-color: #EEEEEE; + border: 1px solid #E2E2E2; + -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; color: #666; + margin-bottom: -1px; + padding: 10px; } -.user-connected{ - border-right: 1px dashed #cccccc; - margin-right: 5px; -} -li.list-group-item:hover, li.list-group-item:focus { - text-decoration: none; - background-color: #E2E2E2; - cursor: pointer; - border: 1px solid #EEEEEE; -} -li.list-group-item:hover a{ - color: #000; -} -.user-image-chat{ - width: 40px; - height: auto; - max-height: 40px; - display: inline-block; - vertical-align: top; - -webkit-border-radius: 10px; +.chat-user .user-image-chat { + -webkit-border-radius: 10px; -moz-border-radius: 10px; + border: 2px solid #FFF; border-radius: 10px; - border:2px solid #ffffff; + display: inline-block; + height: auto; + margin-right: 10px; + max-height: 40px; float: left; - margin-right: 5px; -} -.noSelect { - user-select: none; - -o-user-select: none; - -moz-user-select: none; - -khtml-user-select: none; - -webkit-user-select: none; -} -.btn-send { - background: #87e0fd; /* Old browsers */ - background: -moz-linear-gradient(top, #87e0fd 0%, #53cbf1 40%, #05abe0 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#87e0fd), color-stop(40%,#53cbf1), color-stop(100%,#05abe0)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #87e0fd 0%,#53cbf1 40%,#05abe0 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #87e0fd 0%,#53cbf1 40%,#05abe0 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #87e0fd 0%,#53cbf1 40%,#05abe0 100%); /* IE10+ */ - background: linear-gradient(to bottom, #87e0fd 0%,#53cbf1 40%,#05abe0 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#87e0fd', endColorstr='#05abe0',GradientType=0 ); /* IE6-9 */ - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; - border:1px solid #53cbf1; - display:inline-block; - color:#ffffff; - font-family:Arial; - font-size:22px; - font-weight:bold; - font-style:normal; - height:80px; - line-height:80px; - width:131px; - text-decoration:none; - text-align:center; - cursor: pointer; -} -.btn-send:hover { - opacity: 0.8; -} -.btn-send:active { - position:relative; - top:1px; + width: 40px; } + .emoticons-chat img{ width: 24px; border: none; @@ -267,21 +127,17 @@ li.list-group-item:hover a{ } .emoji-menu { - margin-left: 0px; - width:468px; - left: 11px !important; + margin-left: -234px; + margin-top: -224px; + width: 468px; } .emoji-wysiwyg-editor-preview, .emoji-wysiwyg-editor { - width: 454px; padding: 3%; height: 50px; - float: left; border: 0; background-color: #ffffff; - - font-family: Helvetica, arial, sans-serif; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; @@ -289,3 +145,19 @@ li.list-group-item:hover a{ color: #666; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35) inset; } + +#chat-tabs .tab-content{ + margin-top: 15px; +} + +.chat-history { + height: 400px; + overflow: auto; +} + +#chat-users { + margin-left: 0; +} +#chat-users div.chat-user > div { + padding: 5px; +} diff --git a/index.php b/index.php index e1424d20cd..bb3235d0c9 100755 --- a/index.php +++ b/index.php @@ -14,7 +14,6 @@ define('CHAMILO_LOAD_WYSIWYG', false); $cidReset = true; require_once 'main/inc/global.inc.php'; -require_once 'main/chat/chat_functions.lib.php'; //require_once 'main/auth/external_login/facebook.inc.php'; // The section (for the tabs). diff --git a/main/chat/chat.php b/main/chat/chat.php index eeed6df290..d8257d9c09 100755 --- a/main/chat/chat.php +++ b/main/chat/chat.php @@ -1,92 +1,58 @@ - - - - - -'.get_lang('Chat').' - '.$mycourseid.' - '.api_get_setting('siteName').''; +$externalJS = [ + 'highlight/highlight.pack.js', + 'jquery-textcomplete/jquery.textcomplete.js', + 'emojione/js/emojione.min.js', + 'jquery-emojiarea/jquery.emojiarea.js' +]; -$groupId = api_get_group_id(); - -// If it is a group chat then the breadcrumbs. -if (!empty($groupId)) { - $group_properties = GroupManager :: get_group_properties($groupId); - $interbreadcrumb[] = array( - 'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(), - 'name' => get_lang('Groups') - ); - $interbreadcrumb[] = array( - 'url' => api_get_path(WEB_CODE_PATH).'group/group_space.php?'.api_get_cidreq(), - 'name' => get_lang('GroupSpace').' '.$group_properties['name'] - ); - $noPHP_SELF = true; - $shortBanner = false; - $add_group_to_title = ' ('.$group_properties['name'].')'; - $groupfilter = 'group_id="'.$groupId.'"'; -} else { - $groupfilter = 'group_id=0'; -} - -if (empty($open_chat_window)) { - Display::display_header($tool_name, 'Chat'); +foreach ($externalJS as $js) { + $htmlHeadXtra[] = api_get_js($js); } -$url = api_get_path(WEB_CODE_PATH).'chat/'; -$params = api_get_cidreq(); +$iconList = []; -echo '
'; -echo ''; -echo ''; -echo ''; -echo ''; -echo '
'; +foreach (Emojione\Emojione::$shortcode_replace as $key => $icon) { + if (!in_array($key, CourseChatUtils::getEmojisToInclude())) { + continue; + } -if (empty($open_chat_window)) { - Display::display_footer(); + $iconList[$key] = strtoupper($icon) . '.png'; } -echo ''; +$view = new Template(get_lang('Chat'), false, false, false, true, false); +$view->assign('icons', $iconList); +$view->assign('emoji_strategy', CourseChatUtils::getEmojiStrategry()); +$view->assign('emoji_smile', \Emojione\Emojione::toImage(':smile:')); + +$template = $view->get_template('chat/chat.tpl'); +$content = $view->fetch($template); + +$view->assign('content', $content); +$view->display_one_col_template(); diff --git a/main/chat/chat_chat.php b/main/chat/chat_chat.php deleted file mode 100755 index d0bef29526..0000000000 --- a/main/chat/chat_chat.php +++ /dev/null @@ -1,217 +0,0 @@ -'.$content[0]; - } - if (isset($_GET['origin']) && $_GET['origin'] == 'whoisonlinejoin') { - //the joiner (we have to delete the chat request to him when he joins the chat) - $track_user_table = Database::get_main_table(TABLE_MAIN_USER); - $sql = "UPDATE $track_user_table SET - chatcall_user_id = '', - chatcall_date = '', - chatcall_text='' - WHERE (user_id = ".$userId.")"; - $result = Database::query($sql); - } - - echo '
'; - echo ' '; - if ($isMaster || $is_courseCoach) { - $rand = mt_rand(1, 1000); - echo '
'; - echo ''. - get_lang('ClearList'). - ''; - echo '
'; - } -} else { - echo '
'; - require 'header_frame.inc.php'; - $message = get_lang('CloseOtherSession'); - Display :: display_error_message($message); -} -require 'footer_frame.inc.php'; diff --git a/main/chat/chat_functions.lib.php b/main/chat/chat_functions.lib.php deleted file mode 100755 index 2a4054830a..0000000000 --- a/main/chat/chat_functions.lib.php +++ /dev/null @@ -1,242 +0,0 @@ - 0) { - foreach ($list_info_user_in_chat as $list_info_user) { - $date_db_date = date('Y-m-d', api_strtotime($list_info_user['last_connection'], 'UTC')); - $date_db_h = date('H', api_strtotime($list_info_user['last_connection'], 'UTC')); - $date_db_m = date('i', api_strtotime($list_info_user['last_connection'], 'UTC')); - $date_db_s = date('s', api_strtotime($list_info_user['last_connection'], 'UTC')); - $date_count_time_seconds = $date_db_h * 3600 + $date_db_m * 60 + $date_db_s; - if ($cd_date == $date_db_date) { - if (($cd_count_time_seconds - $date_count_time_seconds) > 5) { - $tbl_chat_connected = Database::get_course_table(TABLE_CHAT_CONNECTED); - $sql = 'DELETE FROM '.$tbl_chat_connected.' - WHERE - c_id = '.$course_id.' AND - user_id = '.$list_info_user['user_id'].' AND - to_group_id = '.$groupId.' - '; - Database::query($sql); - } - } - } - } -} - -/** - * @return array user list in chat - */ -function users_list_in_chat() -{ - $list_users_in_chat = array(); - $tbl_chat_connected = Database::get_course_table(TABLE_CHAT_CONNECTED); - $course_id = api_get_course_int_id(); - - $session_id = api_get_session_id(); - $group_id = api_get_group_id(); - - if (!empty($group_id)) { - $extra_condition = " WHERE to_group_id = '$group_id'"; - } else{ - $extra_condition = api_get_session_condition($session_id, false); - } - $extra_condition.= " AND c_id = $course_id "; - $sql = 'SELECT user_id, last_connection FROM '.$tbl_chat_connected.$extra_condition; - $result = Database::query($sql); - while ($row = Database::fetch_array($result, 'ASSOC')) { - $list_users_in_chat[] = $row; - } - - return $list_users_in_chat; -} - -/** - * @param string $message - * @param array $_course - * @param int $group_id - * @param int $session_id - * @param bool $preview - */ -function saveMessage($message, $userId, $_course, $session_id, $group_id, $preview = true) -{ - $userInfo = api_get_user_info($userId); - $fullName = $userInfo['complete_name']; - $isMaster = (bool)api_is_course_admin(); - - $document_path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'; - if (!empty($group_id)) { - $group_info = GroupManager :: get_group_properties($group_id); - $basepath_chat = $group_info['directory'].'/chat_files'; - } else { - $basepath_chat = '/chat_files'; - } - $chat_path = $document_path.$basepath_chat.'/'; - - if (!is_dir($chat_path)) { - if (is_file($chat_path)) { - @unlink($chat_path); - } - } - - $date_now = date('Y-m-d'); - $message = trim($message); - $timeNow = date('d/m/y H:i:s'); - - if (!empty($group_id)) { - $basename_chat = 'messages-'.$date_now.'_gid-'.$group_id; - } elseif (!empty($session_id)) { - $basename_chat = 'messages-'.$date_now.'_sid-'.$session_id; - } else { - $basename_chat = 'messages-'.$date_now; - } - - if (!api_is_anonymous()) { - if (!empty($message)) { - Emojione\Emojione::$imagePathPNG = api_get_path(WEB_LIBRARY_PATH).'javascript/emojione/png/'; - Emojione\Emojione::$ascii = true; - - // Parsing emojis - $message = Emojione\Emojione::toImage($message); - // Parsing text to understand markdown (code highlight) - $message = MarkdownExtra::defaultTransform($message); - // Security XSS - $message = Security::remove_XSS($message); - - if ($preview == true) { - return $message; - } - - if (!file_exists($chat_path.$basename_chat.'.log.html')) { - $doc_id = add_document( - $_course, - $basepath_chat . '/' . $basename_chat . '.log.html', - 'file', - 0, - $basename_chat . '.log.html' - ); - api_item_property_update( - $_course, - TOOL_DOCUMENT, - $doc_id, - 'DocumentAdded', - $userId, - $group_id, - null, - null, - null, - $session_id - ); - api_item_property_update( - $_course, - TOOL_DOCUMENT, - $doc_id, - 'invisible', - $userId, - $group_id, - null, - null, - null, - $session_id - ); - item_property_update_on_folder( - $_course, - $basepath_chat, - $userId - ); - } else { - $doc_id = DocumentManager::get_document_id( - $_course, - $basepath_chat.'/'.$basename_chat.'.log.html' - ); - } - - $fp = fopen($chat_path.$basename_chat.'.log.html', 'a'); - $userPhoto = UserManager::getUserPicture($userId, USER_IMAGE_SIZE_MEDIUM); - $filePhoto = ''; - if ($isMaster) { - fputs($fp, '
'.$fullName.'
'.$message.'
'.$timeNow.'
'.$filePhoto.'
'."\n"); - } else { - fputs($fp, '
'.$filePhoto.'
'.$fullName.'
'.$message.'
'.$timeNow.'
'."\n"); - } - fclose($fp); - - $chat_size = filesize($chat_path.$basename_chat.'.log.html'); - - update_existing_document($_course, $doc_id, $chat_size); - item_property_update_on_folder($_course, $basepath_chat, $userId); - } - } -} diff --git a/main/chat/chat_hidden.php b/main/chat/chat_hidden.php deleted file mode 100755 index 98c8397936..0000000000 --- a/main/chat/chat_hidden.php +++ /dev/null @@ -1,130 +0,0 @@ -'".date('Y-m-d H:i:s',time()-60*5)."' $extra_condition"; -$result = Database::query($query); - -$connected_old = isset($_POST['connected_old']) ? intval($_POST['connected_old']) : null; -list($connected_new) = Database::fetch_row($result); - -/*disconnected user of chat*/ -disconnect_user_of_chat(); -require 'header_frame.inc.php'; -?> -
- - -
- alert('".get_lang('ChatDenied')."'); "; - $sql = "update $track_user_table set chatcall_user_id = '', chatcall_date = '', chatcall_text='' WHERE (user_id = $talk_to)"; - $result = Database::query($sql); - } -} -require 'footer_frame.inc.php'; diff --git a/main/chat/chat_message.php b/main/chat/chat_message.php deleted file mode 100755 index c78356aefc..0000000000 --- a/main/chat/chat_message.php +++ /dev/null @@ -1,128 +0,0 @@ - - - close_chat_window(); '; -} else { - api_protect_course_script(); -} - -if (empty($course) || empty($userId)) { - exit; -} - -/* Constants and variables */ -$tbl_user = Database::get_main_table(TABLE_MAIN_USER); -$sent = isset($_REQUEST['sent']) ? $_REQUEST['sent'] : null; - -require 'header_frame.inc.php'; -$chat_size = 0; - -if ($sent) { - saveMessage( - $_POST['message'], - $userId, - $_course, - $session_id, - $group_id, - false - ); -} -?> -
- -
-
- -
-
- - - - - - - -
- - -
-
- -
-
-
-
- - - - -
-
-
-
-
-
-
-
-
-".COURSE_RELATION_TYPE_RRHH." AND - t3.c_id = '".$courseInfo['real_id']."' AND - t2.last_connection>'".$date_inter."' $extra_condition - ORDER BY username"; - - $result = Database::query($sql); - $users = Database::store_result($result); - } else { - $session_condition = api_get_session_condition($session_id, true, false, 't3.session_id'); - // select learners - $query = "SELECT DISTINCT t1.user_id,username,firstname,lastname,picture_uri,email - FROM $tbl_user t1, $tbl_chat_connected t2, $tbl_session_course_user t3 - WHERE - t2.c_id = $course_id AND - t1.user_id=t2.user_id AND t3.user_id=t2.user_id AND - t3.session_id = '".$session_id."' AND - t3.c_id = '".$courseInfo['real_id']."' AND - t2.last_connection>'".$date_inter."' $session_condition - ORDER BY username"; - $result = Database::query($query); - while ($learner = Database::fetch_array($result)) { - $users[$learner['user_id']] = $learner; - } - - // select session coach - $query = "SELECT DISTINCT t1.user_id,username,firstname,lastname,picture_uri,email - FROM $tbl_user t1,$tbl_chat_connected t2,$tbl_session t3 - WHERE - t2.c_id = $course_id AND - t1.user_id=t2.user_id AND - t3.id_coach=t2.user_id AND - t3.id = '".$session_id."' AND - t2.last_connection > '".$date_inter."' - $extra_condition - ORDER BY username"; - $result = Database::query($query); - if ($coach = Database::fetch_array($result)) { - $users[$coach['user_id']] = $coach; - } - - // select session course coach - $query = "SELECT DISTINCT t1.user_id,username,firstname,lastname,picture_uri,email - FROM $tbl_user t1,$tbl_chat_connected t2,$tbl_session_course_user t3 - WHERE - t2.c_id = $course_id AND - t1.user_id=t2.user_id - AND t3.user_id =t2.user_id AND t3.status=2 - AND t3.session_id = '".$session_id."' - AND t3.c_id = '".$course_id."' - AND t2.last_connection>'".$date_inter."' $extra_condition - ORDER BY username"; - - $result = Database::query($query); - $course_coaches = array(); - while ($coaches = Database::fetch_array($result)) { - $users[$coaches['user_id']] = $coaches; - } - } - require 'header_frame.inc.php'; - - ?> -
-
-
-
-
-
    - -
  • - -
    - - - - '18')); - } else { - echo Display::return_icon('students.gif', get_lang('Student'), array('height' => '18')); - } - ?> -
    -
    -
  • - -
-
- - - - \ No newline at end of file diff --git a/main/chat/header_frame.inc.php b/main/chat/header_frame.inc.php deleted file mode 100755 index d7f52f8027..0000000000 --- a/main/chat/header_frame.inc.php +++ /dev/null @@ -1,272 +0,0 @@ -'; - echo "parent.chat_message.location.href='chat_whoisonline.php?".api_get_cidreq()."';"; - echo ''; -} - -/* - * Choose CSS style (platform's, user's, or course's) - */ -$my_style = api_get_visual_theme(); -$chat_size_old = isset($chat_size_old) ? $chat_size_old : null; -$chat_size_new = isset($chat_size_new) ? $chat_size_new : null; -$connected_new = isset($connected_new) ? $connected_new : null; -$connected_old = isset($connected_old) ? $connected_old : null; - -$icons = Emojione\Emojione::$shortcode_replace; -$iconList = array(); -$iconNameToInclude = array( - ':bowtie:', - ':smile:', - ':laughing:', - ':blush:', - ':smiley:', - ':relaxed:', - ':smirk:', - ':heart_eyes:', - ':kissing_heart:', - ':kissing_closed_eyes:', - ':flushed:', - ':relieved:', - ':satisfied:', - ':grin:', - ':wink:', - ':stuck_out_tongue_winking_eye:', - ':stuck_out_tongue_closed_eyes:', - ':grinning:', - ':kissing:', - ':kissing_smiling_eyes:', - ':stuck_out_tongue:', - ':sleeping:', - ':worried:', - ':frowning:', - ':anguished:', - ':open_mouth:', - ':grimacing:', - ':confused:', - ':hushed:', - ':expressionless:', - ':unamused:', - ':sweat_smile:', - ':sweat:', - ':disappointed_relieved:', - ':weary:', - ':pensive:', - ':disappointed:', - ':confounded:', - ':fearful:', - ':cold_sweat:', - ':persevere:', - ':cry:', - ':sob:', - ':joy:', - ':astonished:', - ':scream:', - ':neckbeard:', - ':tired_face:', - ':angry:', - ':rage:', - ':triumph:', - ':sleepy:', - ':yum:', - ':mask:', - ':sunglasses:', - ':dizzy_face:', - ':imp:', - ':smiling_imp:', - ':neutral_face:', - ':no_mouth:', - ':innocent:', - ':alien:', -); - -foreach ($icons as $key => $icon) { - if (in_array($key, $iconNameToInclude)) { - $iconList[$key] = strtoupper($icon) . '.png'; - } -} - -$icons = json_encode($iconList); -$ajax = api_get_path(WEB_AJAX_PATH).'chat.ajax.php?action=preview&'.api_get_cidreq(); -header('Content-Type: text/html; charset=UTF-8'); - -?> - - - -Chat - - - - - - - - - - - - - - - - - - > diff --git a/main/inc/ajax/chat.ajax.php b/main/inc/ajax/chat.ajax.php index 18a4a46dc6..bf45720a11 100755 --- a/main/inc/ajax/chat.ajax.php +++ b/main/inc/ajax/chat.ajax.php @@ -17,16 +17,8 @@ if (api_is_anonymous()) { // Course Chat if ($action == 'preview') { - require_once api_get_path(SYS_CODE_PATH).'chat/chat_functions.lib.php'; - - echo saveMessage( - $_REQUEST['message'], - api_get_user_id(), - api_get_course_info(), - api_get_session_id(), - api_get_group_id(), - true - ); + echo CourseChatUtils::prepareMessage($_REQUEST['message']); + exit; } if (api_get_setting('allow_global_chat') == 'false') { diff --git a/main/inc/ajax/course_chat.ajax.php b/main/inc/ajax/course_chat.ajax.php new file mode 100644 index 0000000000..c855835be2 --- /dev/null +++ b/main/inc/ajax/course_chat.ajax.php @@ -0,0 +1,76 @@ + false]; + +$courseChatUtils = new CourseChatUtils($courseId, $userId, $sessionId, $groupId); + +switch ($_REQUEST['action']) { + case 'track': + $courseChatUtils->keepUserAsConnected(); + $courseChatUtils->disconnectInactiveUsers(); + + $friend = isset($_REQUEST['friend']) ? intval($_REQUEST['friend']) : 0; + $filePath = $courseChatUtils->getFileName(true, $friend); + $newFileSize = file_exists($filePath) ? filesize($filePath) : 0; + $oldFileSize = isset($_GET['size']) ? intval($_GET['size']) : -1; + $newUsersOnline = $courseChatUtils->countUsersOnline(); + $oldUsersOnline = isset($_GET['users_online']) ? intval($_GET['users_online']) : 0; + + $json = [ + 'status' => true, + 'data' => [ + 'chatIsDenied' => $courseChatUtils->isChatDenied(), + 'oldFileSize' => file_exists($filePath) ? filesize($filePath) : 0, + 'history' => $newFileSize !== $oldFileSize ? $courseChatUtils->readMessages(false, $friend) : null, + 'usersOnline' => $newUsersOnline, + 'userList' => $newUsersOnline != $oldUsersOnline ? $courseChatUtils->listUsersOnline() : null, + 'currentFriend' => $friend + ] + ]; + + break; + case 'preview': + $json = [ + 'status' => true, + 'data' => [ + 'message' => CourseChatUtils::prepareMessage($_REQUEST['message']) + ] + ]; + break; + case 'reset': + $friend = isset($_REQUEST['friend']) ? intval($_REQUEST['friend']) : 0; + + $json = [ + 'status' => true, + 'data' => $courseChatUtils->readMessages(true, $friend) + ]; + break; + case 'write': + $friend = isset($_REQUEST['friend']) ? intval($_REQUEST['friend']) : 0; + $writed = $courseChatUtils->saveMessage($_POST['message'], $friend); + + $json = [ + 'status' => $writed, + 'data' => [ + 'writed' => $writed + ] + ]; + break; +} + +header('Content-Type: application/json'); +echo json_encode($json); diff --git a/main/inc/lib/CourseChatUtils.php b/main/inc/lib/CourseChatUtils.php new file mode 100644 index 0000000000..d2f428c5ae --- /dev/null +++ b/main/inc/lib/CourseChatUtils.php @@ -0,0 +1,1843 @@ +courseId = intval($courseId); + $this->userId = intval($userId); + $this->sessionId = intval($sessionId); + $this->groupId = intval($groupId); + } + + /** + * Get the users subscriptions (SessionRelCourseRelUser array or CourseRelUser array) for chat + * @return \Doctrine\Common\Collections\ArrayCollection + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\TransactionRequiredException + */ + private function getUsersSubscriptions() + { + $em = Database::getManager(); + $course = $em->find('ChamiloCoreBundle:Course', $this->courseId); + + if ($this->sessionId) { + $criteria = \Doctrine\Common\Collections\Criteria::create() + ->where( + \Doctrine\Common\Collections\Criteria::expr()->eq("course", $course) + ); + + return $em + ->find('ChamiloCoreBundle:Session', $this->sessionId) + ->getUserCourseSubscriptions() + ->matching($criteria); + } + + return $em + ->find('ChamiloCoreBundle:Course', $course) + ->getUsers(); + } + + /** + * Prepare a message. Clean and insert emojis + * @param string $message The message to prepare + * @return string + */ + public static function prepareMessage($message) + { + if (empty($message)) { + return ''; + } + + Emojione\Emojione::$imagePathPNG = api_get_path(WEB_LIBRARY_PATH) . 'javascript/emojione/png/'; + Emojione\Emojione::$ascii = true; + + $message = trim($message); + $message = nl2br($message); + // Security XSS + $message = Security::remove_XSS($message); + //search urls + $message = preg_replace( + '@((https?://)?([-\w]+\.[-\w\.]+)+\w(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)*)@', + '$1', + $message + ); + // add "http://" if not set + $message = preg_replace( + '/]*href\s*=\s*"((?!https?:\/\/)[^"]*)"[^>]*>/i', + '', + $message + ); + // Parsing emojis + $message = Emojione\Emojione::toImage($message); + // Parsing text to understand markdown (code highlight) + $message = MarkdownExtra::defaultTransform($message); + + return $message; + } + + /** + * Save a chat message in a HTML file + * @param string$message + * @param int $friendId + * @return bool + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\TransactionRequiredException + */ + public function saveMessage($message, $friendId = 0) + { + if (empty($message)) { + return false; + } + + $em = Database::getManager(); + $user = $em->find('ChamiloUserBundle:User', $this->userId); + $courseInfo = api_get_course_info_by_id($this->courseId); + $isMaster = (bool) api_is_course_admin(); + $document_path = api_get_path(SYS_COURSE_PATH) . $courseInfo['path'] . '/document'; + $basepath_chat = '/chat_files'; + + if (!$this->groupId) { + $group_info = GroupManager::get_group_properties($this->groupId); + $basepath_chat = $group_info['directory'] . '/chat_files'; + } + + $chat_path = $document_path . $basepath_chat . '/'; + + if (!is_dir($chat_path)) { + if (is_file($chat_path)) { + @unlink($chat_path); + } + } + + $date_now = date('Y-m-d'); + $timeNow = date('d/m/y H:i:s'); + $basename_chat = 'messages-' . $date_now; + + if ($this->groupId && !$friendId) { + $basename_chat = 'messages-' . $date_now . '_gid-' . $this->groupId; + } elseif ($this->sessionId && !$friendId) { + $basename_chat = 'messages-' . $date_now . '_sid-' . $this->sessionId; + } elseif ($friendId) { + if ($this->userId < $friendId) { + $basename_chat = 'messages-' . $date_now . '_uid-' . $this->userId . '-' . $friendId; + } else { + $basename_chat = 'messages-' . $date_now . '_uid-' . $friendId . '-' . $this->userId; + } + } + + $message = self::prepareMessage($message); + + $fileTitle = $basename_chat . '.log.html'; + $filePath = $basepath_chat . '/' . $fileTitle; + $absoluteFilePath = $chat_path . $fileTitle; + + if (!file_exists($absoluteFilePath)) { + $doc_id = add_document($courseInfo, $filePath, 'file', 0, $fileTitle); + $documentLogTypes = ['DocumentAdded', 'invisible']; + + foreach ($documentLogTypes as $logType) { + api_item_property_update( + $courseInfo, + TOOL_DOCUMENT, + $doc_id, + $logType, + $this->userId, + $this->groupId, + null, + null, + null, + $this->sessionId + ); + } + + item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId); + } else { + $doc_id = DocumentManager::get_document_id($courseInfo, $filePath); + } + + $fp = fopen($absoluteFilePath, 'a'); + $userPhoto = UserManager::getUserPicture($this->userId, USER_IMAGE_SIZE_MEDIUM); + + if ($isMaster) { + $fileContent = ' +
+
+
' . $user->getCompleteName() . '
+
' . $message . '
+
' . $timeNow . '
+
+
+ +
+ '; + } else { + $fileContent = ' +
+ +
+
+
' . $user->getCompleteName() . '
+
' . $message . '
+
' . $timeNow . '
+
+
+ '; + } + + fputs($fp, $fileContent); + fclose($fp); + + $chat_size = filesize($absoluteFilePath); + + update_existing_document($courseInfo, $doc_id, $chat_size); + item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId); + + return true; + } + + /** + * Disconnect a user from course chats + * @param $userId + */ + public static function exitChat($userId) + { + $listCourse = CourseManager::get_courses_list_by_user_id($userId); + + foreach ($listCourse as $course) { + Database::getManager() + ->createQuery(' + DELETE FROM ChamiloCourseBundle:CChatConnected ccc + WHERE ccc.cId = :course AND ccc.userId = :user + ') + ->execute([ + 'course' => intval($course['real_id']), + 'user' => intval($userId) + ]); + } + } + + /** + * Disconnect users who are more than 5 seconds inactive + */ + public function disconnectInactiveUsers() + { + $em = Database::getManager(); + $extraCondition = "AND ccc.toGroupId = {$this->groupId}"; + + if (empty($this->groupId)) { + $extraCondition = "AND ccc.sessionId = {$this->sessionId}"; + } + + $connectedUsers = $em + ->createQuery(" + SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc + WHERE ccc.cId = :course $extraCondition + ") + ->setParameter('course', $this->courseId) + ->getResult(); + + $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC')); + $cd_count_time_seconds = $now->getTimestamp(); + + foreach ($connectedUsers as $connection) { + $date_count_time_seconds = $connection->getLastConnection()->getTimestamp(); + + if (strcmp($now->format('Y-m-d'), $connection->getLastConnection()->format('Y-m-d')) !== 0) { + continue; + } + + if (($cd_count_time_seconds - $date_count_time_seconds) <= 5) { + continue; + } + + $em + ->createQuery(' + DELETE FROM ChamiloCourseBundle:CChatConnected ccc + WHERE ccc.cId = :course AND ccc.userId = :user AND ccc.toGroupId = :group + ') + ->execute([ + 'course' => $this->courseId, + 'user' => $connection->getUserId(), + 'group' => $this->groupId + ]); + } + } + + /** + * Keep registered to a user as connected + * @throws \Doctrine\ORM\NonUniqueResultException + */ + public function keepUserAsConnected() + { + $em = Database::getManager(); + $extraCondition = null; + + if ($this->groupId) { + $extraCondition = 'AND ccc.toGroupId = ' . intval($this->groupId); + } else { + $extraCondition = 'AND ccc.sessionId = ' . intval($this->sessionId); + } + + $currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC')); + + $connection = $em + ->createQuery(" + SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc + WHERE ccc.userId = :user AND ccc.cId = :course $extraCondition + ") + ->setParameters([ + 'user' => $this->userId, + 'course' => $this->courseId + ]) + ->getOneOrNullResult(); + + if ($connection) { + $connection->setLastConnection($currentTime); + $em->merge($connection); + $em->flush(); + + return; + } + + $connection = new \Chamilo\CourseBundle\Entity\CChatConnected(); + $connection + ->setCId($this->courseId) + ->setUserId($this->userId) + ->setLastConnection($currentTime) + ->setSessionId($this->sessionId) + ->setToGroupId($this->groupId); + + $em->persist($connection); + $em->flush(); + } + + /** + * Check if the connection is denied for chat + * @return bool + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\TransactionRequiredException + */ + public function isChatDenied() + { + if (ChamiloSession::read('origin', null) !== 'whoisonline') { + return false; + } + + $talkTo = ChamiloSession::read('target', 0); + + if (!$talkTo) { + return true; + } + + $em = Database::getManager(); + $user = $em->find('ChamiloUserBundle:User', $talkTo); + + if ($user->getChatcallText() === 'DENIED') { + $user + ->setChatcallDate(null) + ->setChatcallUserId(null) + ->setChatcallText(null); + + $em->merge($user); + $em->flush(); + + return true; + } + + return false; + } + + /** + * Get the emoji allowed on course chat + * @return array + */ + public static function getEmojiStrategry() + { + return [ + 'hearts' => ['unicode' => '2665', 'shortname' => ':hearts:', 'aliases' => '', 'keywords' => 'black heart suit cards poker'], + 'joy' => ['unicode' => '1F602', 'shortname' => ':joy:', 'aliases' => '', 'keywords' => 'face with tears of joy cry face haha happy tears tears cry joy happy weep'], + 'unamused' => ['unicode' => '1F612', 'shortname' => ':unamused:', 'aliases' => '', 'keywords' => 'unamused face bored face indifference serious straight face unamused not amused depressed unhappy disapprove lame'], + 'heart_eyes' => ['unicode' => '1F60D', 'shortname' => ':heart_eyes:', 'aliases' => '', 'keywords' => 'smiling face with heart-shaped eyes affection crush face infatuation like love valentines smiling heart lovestruck love flirt smile heart-shaped'], + 'heart' => ['unicode' => '2764', 'shortname' => ':heart:', 'aliases' => '', 'keywords' => 'heavy black heart like love red pink black heart love passion romance intense desire death evil cold valentines'], + 'relaxed' => ['unicode' => '263A', 'shortname' => ':relaxed:', 'aliases' => '', 'keywords' => 'white smiling face blush face happiness massage smile'], + 'ok_hand' => ['unicode' => '1F44C', 'shortname' => ':ok_hand:', 'aliases' => '', 'keywords' => 'ok hand sign fingers limbs perfect okay ok smoke smoking marijuana joint pot 420'], + 'kissing_heart' => ['unicode' => '1F618', 'shortname' => ':kissing_heart:', 'aliases' => '', 'keywords' => 'face throwing a kiss affection face infatuation kiss blowing kiss heart love lips like love valentines'], + 'blush' => ['unicode' => '1F60A', 'shortname' => ':blush:', 'aliases' => '', 'keywords' => 'smiling face with smiling eyes crush embarrassed face flushed happy shy smile smiling smile smiley'], + 'weary' => ['unicode' => '1F629', 'shortname' => ':weary:', 'aliases' => '', 'keywords' => 'weary face face frustrated sad sleepy tired weary sleepy tired tiredness study finals school exhausted'], + 'pensive' => ['unicode' => '1F614', 'shortname' => ':pensive:', 'aliases' => '', 'keywords' => 'pensive face face okay sad pensive thoughtful think reflective wistful meditate serious'], + 'sob' => ['unicode' => '1F62D', 'shortname' => ':sob:', 'aliases' => '', 'keywords' => 'loudly crying face cry face sad tears upset cry sob tears sad melancholy morn somber hurt'], + 'smirk' => ['unicode' => '1F60F', 'shortname' => ':smirk:', 'aliases' => '', 'keywords' => 'smirking face mean prank smile smug smirking smirk smug smile half-smile conceited'], + 'two_hearts' => ['unicode' => '1F495', 'shortname' => ':two_hearts:', 'aliases' => '', 'keywords' => 'two hearts affection like love valentines heart hearts two love emotion'], + 'grin' => ['unicode' => '1F601', 'shortname' => ':grin:', 'aliases' => '', 'keywords' => 'grinning face with smiling eyes face happy joy smile grin grinning smiling smile smiley'], + 'flushed' => ['unicode' => '1F633', 'shortname' => ':flushed:', 'aliases' => '', 'keywords' => 'flushed face blush face flattered flush blush red pink cheeks shy'], + 'thumbsup' => ['unicode' => '1F44D', 'shortname' => ':thumbsup:', 'aliases' => ':+1:', 'keywords' => 'thumbs up sign cool hand like yes'], + 'raised_hands' => ['unicode' => '1F64C', 'shortname' => ':raised_hands:', 'aliases' => '', 'keywords' => 'person raising both hands in celebration gesture hooray winning woot yay banzai'], + 'wink' => ['unicode' => '1F609', 'shortname' => ':wink:', 'aliases' => '', 'keywords' => 'winking face face happy mischievous secret wink winking friendly joke'], + 'information_desk_person' => ['unicode' => '1F481', 'shortname' => ':information_desk_person:', 'aliases' => '', 'keywords' => 'information desk person female girl human woman information help question answer sassy unimpressed attitude snarky'], + 'relieved' => ['unicode' => '1F60C', 'shortname' => ':relieved:', 'aliases' => '', 'keywords' => 'relieved face face happiness massage phew relaxed relieved satisfied phew relief'], + 'see_no_evil' => ['unicode' => '1F648', 'shortname' => ':see_no_evil:', 'aliases' => '', 'keywords' => 'see-no-evil monkey animal monkey nature monkey see eyes vision sight mizaru'], + 'v' => ['unicode' => '270C', 'shortname' => ':v:', 'aliases' => '', 'keywords' => 'victory hand fingers hand ohyeah peace two victory'], + 'pray' => ['unicode' => '1F64F', 'shortname' => ':pray:', 'aliases' => '', 'keywords' => 'person with folded hands highfive hope namaste please wish pray high five hands sorrow regret sorry'], + 'yum' => ['unicode' => '1F60B', 'shortname' => ':yum:', 'aliases' => '', 'keywords' => 'face savouring delicious food face happy joy smile tongue delicious savoring food eat yummy yum tasty savory'], + 'stuck_out_tongue_winking_eye' => ['unicode' => '1F61C', 'shortname' => ':stuck_out_tongue_winking_eye:', 'aliases' => '', 'keywords' => 'face with stuck-out tongue and winking eye childish face mischievous playful prank tongue wink winking kidding silly playful crazy'], + 'notes' => ['unicode' => '1F3B6', 'shortname' => ':notes:', 'aliases' => '', 'keywords' => 'multiple musical notes music score musical music notes music sound melody'], + 'eyes' => ['unicode' => '1F440', 'shortname' => ':eyes:', 'aliases' => '', 'keywords' => 'eyes look peek stalk watch'], + 'smile' => ['unicode' => '1F604', 'shortname' => ':smile:', 'aliases' => '', 'keywords' => 'smiling face with open mouth and smiling eyes face funny haha happy joy laugh smile smiley smiling'], + 'disappointed' => ['unicode' => '1F61E', 'shortname' => ':disappointed:', 'aliases' => '', 'keywords' => 'disappointed face disappointed disappoint frown depressed discouraged face sad upset'], + 'raised_hand' => ['unicode' => '270B', 'shortname' => ':raised_hand:', 'aliases' => '', 'keywords' => 'raised hand female girl woman'], + 'clap' => ['unicode' => '1F44F', 'shortname' => ':clap:', 'aliases' => '', 'keywords' => 'clapping hands sign applause congrats hands praise clapping appreciation approval sound encouragement enthusiasm'], + 'speak_no_evil' => ['unicode' => '1F64A', 'shortname' => ':speak_no_evil:', 'aliases' => '', 'keywords' => 'speak-no-evil monkey animal monkey monkey mouth talk say words verbal verbalize oral iwazaru'], + 100 => ['unicode' => '1F4AF', 'shortname' => ':100:', 'aliases' => '', 'keywords' => 'hundred points symbol numbers perfect score 100 percent a plus perfect school quiz score test exam'], + 'cry' => ['unicode' => '1F622', 'shortname' => ':cry:', 'aliases' => '', 'keywords' => 'crying face face sad sad cry tear weep tears'], + 'rage' => ['unicode' => '1F621', 'shortname' => ':rage:', 'aliases' => '', 'keywords' => 'pouting face angry despise hate mad pout anger rage irate'], + 'tired_face' => ['unicode' => '1F62B', 'shortname' => ':tired_face:', 'aliases' => '', 'keywords' => 'tired face face frustrated sick upset whine exhausted sleepy tired'], + 'scream' => ['unicode' => '1F631', 'shortname' => ':scream:', 'aliases' => '', 'keywords' => 'face screaming in fear face munch scream painting artist alien'], + 'purple_heart' => ['unicode' => '1F49C', 'shortname' => ':purple_heart:', 'aliases' => '', 'keywords' => 'purple heart affection like love valentines purple violet heart love sensitive understanding compassionate compassion duty honor royalty veteran sacrifice'], + 'broken_heart' => ['unicode' => '1F494', 'shortname' => ':broken_heart:', 'aliases' => '', 'keywords' => 'broken heart sad sorry'], + 'kiss' => ['unicode' => '1F48B', 'shortname' => ':kiss:', 'aliases' => '', 'keywords' => 'kiss mark affection face like lips love valentines'], + 'blue_heart' => ['unicode' => '1F499', 'shortname' => ':blue_heart:', 'aliases' => '', 'keywords' => 'blue heart affection like love valentines blue heart love stability truth loyalty trust'], + 'sleepy' => ['unicode' => '1F62A', 'shortname' => ':sleepy:', 'aliases' => '', 'keywords' => 'sleepy face face rest tired sleepy tired exhausted'], + 'sweat_smile' => ['unicode' => '1F605', 'shortname' => ':sweat_smile:', 'aliases' => '', 'keywords' => 'smiling face with open mouth and cold sweat face happy hot smiling cold sweat perspiration'], + 'stuck_out_tongue_closed_eyes' => ['unicode' => '1F61D', 'shortname' => ':stuck_out_tongue_closed_eyes:', 'aliases' => '', 'keywords' => 'face with stuck-out tongue and tightly-closed eyes face mischievous playful prank tongue kidding silly playful ecstatic'], + 'punch' => ['unicode' => '1F44A', 'shortname' => ':punch:', 'aliases' => '', 'keywords' => 'fisted hand sign fist hand'], + 'triumph' => ['unicode' => '1F624', 'shortname' => ':triumph:', 'aliases' => '', 'keywords' => 'face with look of triumph face gas phew triumph steam breath'], + 'sparkling_heart' => ['unicode' => '1F496', 'shortname' => ':sparkling_heart:', 'aliases' => '', 'keywords' => 'sparkling heart affection like love valentines'], + 'smiley' => ['unicode' => '1F603', 'shortname' => ':smiley:', 'aliases' => '', 'keywords' => 'smiling face with open mouth face haha happy joy smiling smile smiley'], + 'sunny' => ['unicode' => '2600', 'shortname' => ':sunny:', 'aliases' => '', 'keywords' => 'black sun with rays brightness weather'], + 'heartpulse' => ['unicode' => '1F497', 'shortname' => ':heartpulse:', 'aliases' => '', 'keywords' => 'growing heart affection like love valentines'], + 'wave' => ['unicode' => '1F44B', 'shortname' => ':wave:', 'aliases' => '', 'keywords' => 'waving hand sign farewell gesture goodbye hands solong'], + 'mask' => ['unicode' => '1F637', 'shortname' => ':mask:', 'aliases' => '', 'keywords' => 'face with medical mask face ill sick sick virus flu medical mask'], + 'heavy_check_mark' => ['unicode' => '2714', 'shortname' => ':heavy_check_mark:', 'aliases' => '', 'keywords' => 'heavy check mark nike ok'], + 'cherry_blossom' => ['unicode' => '1F338', 'shortname' => ':cherry_blossom:', 'aliases' => '', 'keywords' => 'cherry blossom flower nature plant cherry blossom tree flower'], + 'rose' => ['unicode' => '1F339', 'shortname' => ':rose:', 'aliases' => '', 'keywords' => 'rose flowers love valentines rose fragrant flower thorns love petals romance'], + 'persevere' => ['unicode' => '1F623', 'shortname' => ':persevere:', 'aliases' => '', 'keywords' => 'persevering face endure persevere face no sick upset'], + 'revolving_hearts' => ['unicode' => '1F49E', 'shortname' => ':revolving_hearts:', 'aliases' => '', 'keywords' => 'revolving hearts affection like love valentines heart hearts revolving moving circle multiple lovers'], + 'sparkles' => ['unicode' => '2728', 'shortname' => ':sparkles:', 'aliases' => '', 'keywords' => 'sparkles cool shine shiny stars'], + 'confounded' => ['unicode' => '1F616', 'shortname' => ':confounded:', 'aliases' => '', 'keywords' => 'confounded face confused face sick unwell confound amaze perplex puzzle mystify'], + 'tada' => ['unicode' => '1F389', 'shortname' => ':tada:', 'aliases' => '', 'keywords' => 'party popper contulations party party popper tada celebration victory announcement climax congratulations'], + 'no_good' => ['unicode' => '1F645', 'shortname' => ':no_good:', 'aliases' => '', 'keywords' => 'face with no good gesture female girl woman no stop nope don\'t not'], + 'muscle' => ['unicode' => '1F4AA', 'shortname' => ':muscle:', 'aliases' => '', 'keywords' => 'flexed biceps arm flex hand strong muscle bicep'], + 'angry' => ['unicode' => '1F620', 'shortname' => ':angry:', 'aliases' => '', 'keywords' => 'angry face angry livid mad vexed irritated annoyed face frustrated mad'], + 'gun' => ['unicode' => '1F52B', 'shortname' => ':gun:', 'aliases' => '', 'keywords' => 'pistol violence weapon'], + 'cupid' => ['unicode' => '1F498', 'shortname' => ':cupid:', 'aliases' => '', 'keywords' => 'heart with arrow affection heart like love valentines'], + 'sweat' => ['unicode' => '1F613', 'shortname' => ':sweat:', 'aliases' => '', 'keywords' => 'face with cold sweat cold sweat sick anxious worried clammy diaphoresis face hot'], + 'laughing' => ['unicode' => '1F606', 'shortname' => ':laughing:', 'aliases' => ':satisfied:', 'keywords' => 'smiling face with open mouth and tightly-closed ey happy joy lol smiling laughing laugh'], + 'yellow_heart' => ['unicode' => '1F49B', 'shortname' => ':yellow_heart:', 'aliases' => '', 'keywords' => 'yellow heart affection like love valentines yellow gold heart love friendship happy happiness trust compassionate respectful honest caring selfless'], + 'kissing_closed_eyes' => ['unicode' => '1F61A', 'shortname' => ':kissing_closed_eyes:', 'aliases' => '', 'keywords' => 'kissing face with closed eyes affection face infatuation like love valentines kissing kiss passion puckered heart love smooch'], + 'disappointed_relieved' => ['unicode' => '1F625', 'shortname' => ':disappointed_relieved:', 'aliases' => '', 'keywords' => 'disappointed but relieved face face nervous phew sweat disappoint relief'], + 'raising_hand' => ['unicode' => '1F64B', 'shortname' => ':raising_hand:', 'aliases' => '', 'keywords' => 'happy person raising one hand female girl woman hand raise notice attention answer'], + 'fist' => ['unicode' => '270A', 'shortname' => ':fist:', 'aliases' => '', 'keywords' => 'raised fist fingers grasp hand'], + 'green_heart' => ['unicode' => '1F49A', 'shortname' => ':green_heart:', 'aliases' => '', 'keywords' => 'green heart affection like love valentines green heart love nature rebirth reborn jealous clingy envious possessive'], + 'headphones' => ['unicode' => '1F3A7', 'shortname' => ':headphones:', 'aliases' => '', 'keywords' => 'headphone gadgets music score headphone sound music ears beats buds audio listen'], + 'thumbsdown' => ['unicode' => '1F44E', 'shortname' => ':thumbsdown:', 'aliases' => ':-1:', 'keywords' => 'thumbs down sign hand no'], + 'heart_eyes_cat' => ['unicode' => '1F63B', 'shortname' => ':heart_eyes_cat:', 'aliases' => '', 'keywords' => 'smiling cat face with heart-shaped eyes affection animal cats like love valentines lovestruck love heart'], + 'dancer' => ['unicode' => '1F483', 'shortname' => ':dancer:', 'aliases' => '', 'keywords' => 'dancer female fun girl woman dance dancer dress fancy boogy party celebrate ballet tango cha cha music'], + 'skull' => ['unicode' => '1F480', 'shortname' => ':skull:', 'aliases' => ':skeleton:', 'keywords' => 'skull dead skeleton dying'], + 'poop' => ['unicode' => '1F4A9', 'shortname' => ':poop:', 'aliases' => ':shit: :hankey: :poo:', 'keywords' => 'pile of poo poop shit shitface turd poo'], + 'fire' => ['unicode' => '1F525', 'shortname' => ':fire:', 'aliases' => ':flame:', 'keywords' => 'fire cook hot flame'], + 'walking' => ['unicode' => '1F6B6', 'shortname' => ':walking:', 'aliases' => '', 'keywords' => 'pedestrian human man walk pedestrian stroll stride foot feet'], + 'cold_sweat' => ['unicode' => '1F630', 'shortname' => ':cold_sweat:', 'aliases' => '', 'keywords' => 'face with open mouth and cold sweat face nervous sweat exasperated frustrated'], + 'copyright' => ['unicode' => '00A9', 'shortname' => ':copyright:', 'aliases' => '', 'keywords' => 'copyright sign ip license'], + 'penguin' => ['unicode' => '1F427', 'shortname' => ':penguin:', 'aliases' => '', 'keywords' => 'penguin animal nature'], + 'crown' => ['unicode' => '1F451', 'shortname' => ':crown:', 'aliases' => '', 'keywords' => 'crown king kod leader royalty'], + 'open_hands' => ['unicode' => '1F450', 'shortname' => ':open_hands:', 'aliases' => '', 'keywords' => 'open hands sign butterfly fingers'], + 'point_right' => ['unicode' => '1F449', 'shortname' => ':point_right:', 'aliases' => '', 'keywords' => 'white right pointing backhand index direction fingers hand'], + 'heartbeat' => ['unicode' => '1F493', 'shortname' => ':heartbeat:', 'aliases' => '', 'keywords' => 'beating heart affection like love valentines'], + 'dancers' => ['unicode' => '1F46F', 'shortname' => ':dancers:', 'aliases' => '', 'keywords' => 'woman with bunny ears bunny female girls women dancing dancers showgirl playboy costume bunny cancan'], + 'ok_woman' => ['unicode' => '1F646', 'shortname' => ':ok_woman:', 'aliases' => '', 'keywords' => 'face with ok gesture female girl human pink women yes ok okay accept'], + 'pizza' => ['unicode' => '1F355', 'shortname' => ':pizza:', 'aliases' => '', 'keywords' => 'slice of pizza food party pizza pie new york italian italy slice peperoni'], + 'ballot_box_with_check' => ['unicode' => '2611', 'shortname' => ':ballot_box_with_check:', 'aliases' => '', 'keywords' => 'ballot box with check agree ok'], + 'zzz' => ['unicode' => '1F4A4', 'shortname' => ':zzz:', 'aliases' => '', 'keywords' => 'sleeping symbol sleepy tired'], + 'point_left' => ['unicode' => '1F448', 'shortname' => ':point_left:', 'aliases' => '', 'keywords' => 'white left pointing backhand index direction fingers hand'], + 'musical_note' => ['unicode' => '1F3B5', 'shortname' => ':musical_note:', 'aliases' => '', 'keywords' => 'musical note score musical music note music sound'], + 'bow' => ['unicode' => '1F647', 'shortname' => ':bow:', 'aliases' => '', 'keywords' => 'person bowing deeply boy male man sorry bow respect curtsy bend'], + 'fearful' => ['unicode' => '1F628', 'shortname' => ':fearful:', 'aliases' => '', 'keywords' => 'fearful face face nervous oops scared terrified fear fearful scared frightened'], + 'ribbon' => ['unicode' => '1F380', 'shortname' => ':ribbon:', 'aliases' => '', 'keywords' => 'ribbon bowtie decoration girl pink ribbon lace wrap decorate'], + 'joy_cat' => ['unicode' => '1F639', 'shortname' => ':joy_cat:', 'aliases' => '', 'keywords' => 'cat face with tears of joy animal cats haha happy tears happy tears cry joy'], + 'arrow_forward' => ['unicode' => '25B6', 'shortname' => ':arrow_forward:', 'aliases' => '', 'keywords' => 'black right-pointing triangle arrow blue-square'], + 'tongue' => ['unicode' => '1F445', 'shortname' => ':tongue:', 'aliases' => '', 'keywords' => 'tongue mouth playful tongue mouth taste buds food silly playful tease kiss french kiss lick tasty playfulness silliness intimacy'], + 'runner' => ['unicode' => '1F3C3', 'shortname' => ':runner:', 'aliases' => '', 'keywords' => 'runner exercise man walking run runner jog exercise sprint race dash'], + 'point_up' => ['unicode' => '261D', 'shortname' => ':point_up:', 'aliases' => '', 'keywords' => 'white up pointing index direction fingers hand'], + 'airplane' => ['unicode' => '2708', 'shortname' => ':airplane:', 'aliases' => '', 'keywords' => 'airplane flight transportation vehicle airplane plane airport travel airlines fly jet jumbo boeing airbus'], + 'gem' => ['unicode' => '1F48E', 'shortname' => ':gem:', 'aliases' => '', 'keywords' => 'gem stone blue ruby'], + 'person_frowning' => ['unicode' => '1F64D', 'shortname' => ':person_frowning:', 'aliases' => '', 'keywords' => 'person frowning female girl woman dejected rejected sad frown'], + 'hibiscus' => ['unicode' => '1F33A', 'shortname' => ':hibiscus:', 'aliases' => '', 'keywords' => 'hibiscus flowers plant vegetable hibiscus flower warm'], + 'basketball' => ['unicode' => '1F3C0', 'shortname' => ':basketball:', 'aliases' => '', 'keywords' => 'basketball and hoop NBA balls sports basketball bball dribble hoop net swish rip city'], + 'boom' => ['unicode' => '1F4A5', 'shortname' => ':boom:', 'aliases' => '', 'keywords' => 'collision symbol bomb explode explosion boom bang collision fire emphasis wow bam'], + 'nail_care' => ['unicode' => '1F485', 'shortname' => ':nail_care:', 'aliases' => '', 'keywords' => 'nail polish beauty manicure'], + 'dizzy_face' => ['unicode' => '1F635', 'shortname' => ':dizzy_face:', 'aliases' => '', 'keywords' => 'dizzy face dizzy drunk inebriated face spent unconscious xox'], + 'balloon' => ['unicode' => '1F388', 'shortname' => ':balloon:', 'aliases' => '', 'keywords' => 'balloon celebration party balloon birthday celebration helium gas children float'], + 'couple' => ['unicode' => '1F46B', 'shortname' => ':couple:', 'aliases' => '', 'keywords' => 'man and woman holding hands affection date dating human like love marriage people valentines'], + 'dog' => ['unicode' => '1F436', 'shortname' => ':dog:', 'aliases' => '', 'keywords' => 'dog face animal friend nature woof'], + 'sweat_drops' => ['unicode' => '1F4A6', 'shortname' => ':sweat_drops:', 'aliases' => '', 'keywords' => 'splashing sweat symbol water'], + 'star2' => ['unicode' => '1F31F', 'shortname' => ':star2:', 'aliases' => '', 'keywords' => 'glowing star night sparkle glow glowing star five points classic'], + 'hear_no_evil' => ['unicode' => '1F649', 'shortname' => ':hear_no_evil:', 'aliases' => '', 'keywords' => 'hear-no-evil monkey animal monkey monkey ears hear sound kikazaru'], + 'moneybag' => ['unicode' => '1F4B0', 'shortname' => ':moneybag:', 'aliases' => '', 'keywords' => 'money bag coins dollar payment'], + 'beers' => ['unicode' => '1F37B', 'shortname' => ':beers:', 'aliases' => '', 'keywords' => 'clinking beer mugs beverage drink drunk party pub relax beer beers cheers mug toast celebrate pub bar jolly hops clink'], + 'couplekiss' => ['unicode' => '1F48F', 'shortname' => ':couplekiss:', 'aliases' => '', 'keywords' => 'kiss dating like love marriage valentines'], + 'point_down' => ['unicode' => '1F447', 'shortname' => ':point_down:', 'aliases' => '', 'keywords' => 'white down pointing backhand index direction fingers hand'], + 'cloud' => ['unicode' => '2601', 'shortname' => ':cloud:', 'aliases' => '', 'keywords' => 'cloud sky weather'], + 'alien' => ['unicode' => '1F47D', 'shortname' => ':alien:', 'aliases' => '', 'keywords' => 'extraterrestrial alien UFO paul alien ufo'], + 'dizzy' => ['unicode' => '1F4AB', 'shortname' => ':dizzy:', 'aliases' => '', 'keywords' => 'dizzy symbol shoot sparkle star dizzy drunk sick intoxicated squeans starburst star'], + 'heavy_multiplication_x' => ['unicode' => '2716', 'shortname' => ':heavy_multiplication_x:', 'aliases' => '', 'keywords' => 'heavy multiplication x calculation math'], + 'white_check_mark' => ['unicode' => '2705', 'shortname' => ':white_check_mark:', 'aliases' => '', 'keywords' => 'white heavy check mark agree green-square ok'], + 'palm_tree' => ['unicode' => '1F334', 'shortname' => ':palm_tree:', 'aliases' => '', 'keywords' => 'palm tree nature plant vegetable palm tree coconuts fronds warm tropical'], + 'dash' => ['unicode' => '1F4A8', 'shortname' => ':dash:', 'aliases' => '', 'keywords' => 'dash symbol air fast shoo wind'], + 'exclamation' => ['unicode' => '2757', 'shortname' => ':exclamation:', 'aliases' => '', 'keywords' => 'heavy exclamation mark symbol surprise'], + 'soccer' => ['unicode' => '26BD', 'shortname' => ':soccer:', 'aliases' => '', 'keywords' => 'soccer ball balls fifa football sports european football'], + 'microphone' => ['unicode' => '1F3A4', 'shortname' => ':microphone:', 'aliases' => '', 'keywords' => 'microphone PA music sound microphone mic audio sound voice karaoke'], + 'angel' => ['unicode' => '1F47C', 'shortname' => ':angel:', 'aliases' => '', 'keywords' => 'baby angel baby angel halo cupid wings halo heaven wings jesus'], + 'point_up_2' => ['unicode' => '1F446', 'shortname' => ':point_up_2:', 'aliases' => '', 'keywords' => 'white up pointing backhand index direction fingers hand'], + 'snowflake' => ['unicode' => '2744', 'shortname' => ':snowflake:', 'aliases' => '', 'keywords' => 'snowflake christmas cold season weather winter xmas snowflake snow frozen droplet ice crystal cold chilly winter unique special below zero elsa'], + 'astonished' => ['unicode' => '1F632', 'shortname' => ':astonished:', 'aliases' => '', 'keywords' => 'astonished face face xox shocked surprise astonished'], + 'four_leaf_clover' => ['unicode' => '1F340', 'shortname' => ':four_leaf_clover:', 'aliases' => '', 'keywords' => 'four leaf clover lucky nature plant vegetable clover four leaf luck irish saint patrick green'], + 'ghost' => ['unicode' => '1F47B', 'shortname' => ':ghost:', 'aliases' => '', 'keywords' => 'ghost halloween'], + 'princess' => ['unicode' => '1F478', 'shortname' => ':princess:', 'aliases' => '', 'keywords' => 'princess blond crown female girl woman princess royal royalty king queen daughter disney high-maintenance'], + 'cat' => ['unicode' => '1F431', 'shortname' => ':cat:', 'aliases' => '', 'keywords' => 'cat face animal meow'], + 'ring' => ['unicode' => '1F48D', 'shortname' => ':ring:', 'aliases' => '', 'keywords' => 'ring marriage propose valentines wedding'], + 'sunflower' => ['unicode' => '1F33B', 'shortname' => ':sunflower:', 'aliases' => '', 'keywords' => 'sunflower nature plant sunflower sun flower seeds yellow'], + 'o' => ['unicode' => '2B55', 'shortname' => ':o:', 'aliases' => '', 'keywords' => 'heavy large circle circle round'], + 'crescent_moon' => ['unicode' => '1F319', 'shortname' => ':crescent_moon:', 'aliases' => '', 'keywords' => 'crescent moon night moon crescent waxing sky night cheese phase'], + 'gift' => ['unicode' => '1F381', 'shortname' => ':gift:', 'aliases' => '', 'keywords' => 'wrapped present birthday christmas present xmas gift present wrap package birthday wedding'], + 'crying_cat_face' => ['unicode' => '1F63F', 'shortname' => ':crying_cat_face:', 'aliases' => '', 'keywords' => 'crying cat face animal cats sad tears weep cry cat sob tears sad melancholy morn somber hurt'], + 'bouquet' => ['unicode' => '1F490', 'shortname' => ':bouquet:', 'aliases' => '', 'keywords' => 'bouquet flowers nature'], + 'star' => ['unicode' => '2B50', 'shortname' => ':star:', 'aliases' => '', 'keywords' => 'white medium star night yellow'], + 'leaves' => ['unicode' => '1F343', 'shortname' => ':leaves:', 'aliases' => '', 'keywords' => 'leaf fluttering in wind grass lawn nature plant tree vegetable leaves leaf wind float fluttering'], + 'cactus' => ['unicode' => '1F335', 'shortname' => ':cactus:', 'aliases' => '', 'keywords' => 'cactus nature plant vegetable cactus desert drought spike poke'], + 'clubs' => ['unicode' => '2663', 'shortname' => ':clubs:', 'aliases' => '', 'keywords' => 'black club suit cards poker'], + 'diamonds' => ['unicode' => '2666', 'shortname' => ':diamonds:', 'aliases' => '', 'keywords' => 'black diamond suit cards poker'], + 'massage' => ['unicode' => '1F486', 'shortname' => ':massage:', 'aliases' => '', 'keywords' => 'face massage female girl woman'], + 'imp' => ['unicode' => '1F47F', 'shortname' => ':imp:', 'aliases' => '', 'keywords' => 'imp angry devil evil horns cute devil'], + 'red_circle' => ['unicode' => '1F534', 'shortname' => ':red_circle:', 'aliases' => '', 'keywords' => 'large red circle shape'], + 'money_with_wings' => ['unicode' => '1F4B8', 'shortname' => ':money_with_wings:', 'aliases' => '', 'keywords' => 'money with wings bills dollar payment money wings easy spend work lost blown burned gift cash dollar'], + 'football' => ['unicode' => '1F3C8', 'shortname' => ':football:', 'aliases' => '', 'keywords' => 'american football NFL balls sports football ball sport america american'], + 'cyclone' => ['unicode' => '1F300', 'shortname' => ':cyclone:', 'aliases' => '', 'keywords' => 'cyclone blue cloud swirl weather cyclone hurricane typhoon storm ocean'], + 'smirk_cat' => ['unicode' => '1F63C', 'shortname' => ':smirk_cat:', 'aliases' => '', 'keywords' => 'cat face with wry smile animal cats smirk smirking wry confident confidence'], + 'snowman' => ['unicode' => '26C4', 'shortname' => ':snowman:', 'aliases' => '', 'keywords' => 'snowman without snow christmas cold season weather winter xmas'], + 'birthday' => ['unicode' => '1F382', 'shortname' => ':birthday:', 'aliases' => '', 'keywords' => 'birthday cake cake party birthday birth cake dessert wish celebrate'], + 'baby' => ['unicode' => '1F476', 'shortname' => ':baby:', 'aliases' => '', 'keywords' => 'baby boy child infant'], + 'telephone' => ['unicode' => '260E', 'shortname' => ':telephone:', 'aliases' => '', 'keywords' => 'black telephone communication dial technology'], + 'eggplant' => ['unicode' => '1F346', 'shortname' => ':eggplant:', 'aliases' => '', 'keywords' => 'aubergine aubergine food nature vegetable eggplant aubergine fruit purple penis'], + 'gift_heart' => ['unicode' => '1F49D', 'shortname' => ':gift_heart:', 'aliases' => '', 'keywords' => 'heart with ribbon love valentines'], + 'tulip' => ['unicode' => '1F337', 'shortname' => ':tulip:', 'aliases' => '', 'keywords' => 'tulip flowers nature plant tulip flower bulb spring easter'], + 'confetti_ball' => ['unicode' => '1F38A', 'shortname' => ':confetti_ball:', 'aliases' => '', 'keywords' => 'confetti ball festival party party congratulations confetti ball celebrate win birthday new years wedding'], + 'black_small_square' => ['unicode' => '25AA', 'shortname' => ':black_small_square:', 'aliases' => '', 'keywords' => 'black small square '], + 'coffee' => ['unicode' => '2615', 'shortname' => ':coffee:', 'aliases' => '', 'keywords' => 'hot beverage beverage cafe drink espresso'], + 'scream_cat' => ['unicode' => '1F640', 'shortname' => ':scream_cat:', 'aliases' => '', 'keywords' => 'weary cat face animal cats munch weary sleepy tired tiredness study finals school exhausted scream painting artist'], + 'rocket' => ['unicode' => '1F680', 'shortname' => ':rocket:', 'aliases' => '', 'keywords' => 'rocket launch ship staffmode rocket space spacecraft astronaut cosmonaut'], + 'christmas_tree' => ['unicode' => '1F384', 'shortname' => ':christmas_tree:', 'aliases' => '', 'keywords' => 'christmas tree celebration december festival vacation xmas christmas xmas santa holiday winter december santa evergreen ornaments jesus gifts presents'], + 'x' => ['unicode' => '274C', 'shortname' => ':x:', 'aliases' => '', 'keywords' => 'cross mark delete no remove'], + 'knife' => ['unicode' => '1F52A', 'shortname' => ':knife:', 'aliases' => '', 'keywords' => 'hocho '], + 'bangbang' => ['unicode' => '203C', 'shortname' => ':bangbang:', 'aliases' => '', 'keywords' => 'double exclamation mark exclamation surprise'], + 'smile_cat' => ['unicode' => '1F638', 'shortname' => ':smile_cat:', 'aliases' => '', 'keywords' => 'grinning cat face with smiling eyes animal cats cat smile grin grinning'], + 'kissing_cat' => ['unicode' => '1F63D', 'shortname' => ':kissing_cat:', 'aliases' => '', 'keywords' => 'kissing cat face with closed eyes animal cats passion kiss puckered heart love'], + 'doughnut' => ['unicode' => '1F369', 'shortname' => ':doughnut:', 'aliases' => '', 'keywords' => 'doughnut desert food snack sweet doughnut donut pastry fried dessert breakfast police homer sweet'], + 'couple_with_heart' => ['unicode' => '1F491', 'shortname' => ':couple_with_heart:', 'aliases' => '', 'keywords' => 'couple with heart affection dating human like love marriage valentines'], + 'spades' => ['unicode' => '2660', 'shortname' => ':spades:', 'aliases' => '', 'keywords' => 'black spade suit cards poker'], + 'bomb' => ['unicode' => '1F4A3', 'shortname' => ':bomb:', 'aliases' => '', 'keywords' => 'bomb boom explode'], + 'guitar' => ['unicode' => '1F3B8', 'shortname' => ':guitar:', 'aliases' => '', 'keywords' => 'guitar instrument music guitar string music instrument jam rock acoustic electric'], + 'space_invader' => ['unicode' => '1F47E', 'shortname' => ':space_invader:', 'aliases' => '', 'keywords' => 'alien monster arcade game'], + 'maple_leaf' => ['unicode' => '1F341', 'shortname' => ':maple_leaf:', 'aliases' => '', 'keywords' => 'maple leaf canada nature plant vegetable maple leaf syrup canada tree'], + 'pig' => ['unicode' => '1F437', 'shortname' => ':pig:', 'aliases' => '', 'keywords' => 'pig face animal oink'], + 'guardsman' => ['unicode' => '1F482', 'shortname' => ':guardsman:', 'aliases' => '', 'keywords' => 'guardsman british gb male man uk guardsman guard bearskin hat british queen ceremonial military'], + 'fork_and_knife' => ['unicode' => '1F374', 'shortname' => ':fork_and_knife:', 'aliases' => '', 'keywords' => 'fork and knife cutlery kitchen fork knife restaurant meal food eat'], + 'lips' => ['unicode' => '1F444', 'shortname' => ':lips:', 'aliases' => '', 'keywords' => 'mouth kiss mouth'], + 'santa' => ['unicode' => '1F385', 'shortname' => ':santa:', 'aliases' => '', 'keywords' => 'father christmas christmas father christmas festival male man xmas santa saint nick jolly ho ho ho north pole presents gifts naughty nice sleigh father christmas holiday'], + 'beer' => ['unicode' => '1F37A', 'shortname' => ':beer:', 'aliases' => '', 'keywords' => 'beer mug beverage drink drunk party pub relax beer hops mug barley malt yeast portland oregon brewery micro pint boot'], + 'red_car' => ['unicode' => '1F697', 'shortname' => ':red_car:', 'aliases' => '', 'keywords' => 'automobile transportation vehicle'], + 'zap' => ['unicode' => '26A1', 'shortname' => ':zap:', 'aliases' => '', 'keywords' => 'high voltage sign lightning bolt thunder weather'], + 'ocean' => ['unicode' => '1F30A', 'shortname' => ':ocean:', 'aliases' => '', 'keywords' => 'water wave sea water wave ocean wave surf beach tide'], + 'banana' => ['unicode' => '1F34C', 'shortname' => ':banana:', 'aliases' => '', 'keywords' => 'banana food fruit banana peel bunch'], + 'tm' => ['unicode' => '1F1F9-1F1F2', 'shortname' => ':tm:', 'aliases' => '', 'keywords' => 'turkmenistan country nation'], + 'turtle' => ['unicode' => '1F422', 'shortname' => ':turtle:', 'aliases' => '', 'keywords' => 'turtle animal slow turtle shell tortoise chelonian reptile slow snap steady'], + 'movie_camera' => ['unicode' => '1F3A5', 'shortname' => ':movie_camera:', 'aliases' => '', 'keywords' => 'movie camera film record movie camera camcorder video motion picture'], + 'video_game' => ['unicode' => '1F3AE', 'shortname' => ':video_game:', 'aliases' => '', 'keywords' => 'video game PS4 console controller play video game console controller nintendo xbox playstation'], + 'trophy' => ['unicode' => '1F3C6', 'shortname' => ':trophy:', 'aliases' => '', 'keywords' => 'trophy award ceremony contest ftw place win trophy first show place win reward achievement medal'], + 'man' => ['unicode' => '1F468', 'shortname' => ':man:', 'aliases' => '', 'keywords' => 'man classy dad father guy mustashe'], + 'umbrella' => ['unicode' => '2614', 'shortname' => ':umbrella:', 'aliases' => '', 'keywords' => 'umbrella with rain drops rain weather'], + 'tiger' => ['unicode' => '1F42F', 'shortname' => ':tiger:', 'aliases' => '', 'keywords' => 'tiger face animal'], + 'smoking' => ['unicode' => '1F6AC', 'shortname' => ':smoking:', 'aliases' => '', 'keywords' => 'smoking symbol cigarette kills tobacco smoking cigarette smoke cancer lungs inhale tar nicotine'], + 'watermelon' => ['unicode' => '1F349', 'shortname' => ':watermelon:', 'aliases' => '', 'keywords' => 'watermelon food fruit melon watermelon summer fruit large'], + 'person_with_pouting_face' => ['unicode' => '1F64E', 'shortname' => ':person_with_pouting_face:', 'aliases' => '', 'keywords' => 'person with pouting face female girl woman pout sexy cute annoyed'], + 'herb' => ['unicode' => '1F33F', 'shortname' => ':herb:', 'aliases' => '', 'keywords' => 'herb grass lawn medicine plant vegetable weed herb spice plant cook cooking'], + 'footprints' => ['unicode' => '1F463', 'shortname' => ':footprints:', 'aliases' => '', 'keywords' => 'footprints feet'], + 'camera' => ['unicode' => '1F4F7', 'shortname' => ':camera:', 'aliases' => '', 'keywords' => 'camera gadgets photo'], + 'japanese_ogre' => ['unicode' => '1F479', 'shortname' => ':japanese_ogre:', 'aliases' => '', 'keywords' => 'japanese ogre monster japanese oni demon troll ogre folklore monster devil mask theater horns teeth'], + 'cookie' => ['unicode' => '1F36A', 'shortname' => ':cookie:', 'aliases' => '', 'keywords' => 'cookie chocolate food oreo snack cookie dessert biscuit sweet chocolate'], + 'recycle' => ['unicode' => '267B', 'shortname' => ':recycle:', 'aliases' => '', 'keywords' => 'black universal recycling symbol arrow environment garbage trash'], + 'wine_glass' => ['unicode' => '1F377', 'shortname' => ':wine_glass:', 'aliases' => '', 'keywords' => 'wine glass alcohol beverage booze bottle drink drunk fermented glass grapes tasting wine winery'], + 'arrow_right' => ['unicode' => '27A1', 'shortname' => ':arrow_right:', 'aliases' => '', 'keywords' => 'black rightwards arrow blue-square next'], + 'panda_face' => ['unicode' => '1F43C', 'shortname' => ':panda_face:', 'aliases' => '', 'keywords' => 'panda face animal nature panda bear face cub cute endearment friendship love bamboo china black white'], + 'dollar' => ['unicode' => '1F4B5', 'shortname' => ':dollar:', 'aliases' => '', 'keywords' => 'banknote with dollar sign bill currency money dollar united states canada australia banknote money currency paper cash bills'], + 'hamburger' => ['unicode' => '1F354', 'shortname' => ':hamburger:', 'aliases' => '', 'keywords' => 'hamburger food meat hamburger burger meat cow beef'], + 'icecream' => ['unicode' => '1F366', 'shortname' => ':icecream:', 'aliases' => '', 'keywords' => 'soft ice cream desert food hot icecream ice cream dairy dessert cold soft serve cone yogurt'], + 'fries' => ['unicode' => '1F35F', 'shortname' => ':fries:', 'aliases' => '', 'keywords' => 'french fries chips food fries french potato fry russet idaho'], + 'arrow_left' => ['unicode' => '2B05', 'shortname' => ':arrow_left:', 'aliases' => '', 'keywords' => 'leftwards black arrow arrow blue-square previous'], + 'rainbow' => ['unicode' => '1F308', 'shortname' => ':rainbow:', 'aliases' => '', 'keywords' => 'rainbow happy nature photo sky unicorn rainbow color pride diversity spectrum refract leprechaun gold'], + 'earth_asia' => ['unicode' => '1F30F', 'shortname' => ':earth_asia:', 'aliases' => '', 'keywords' => 'earth globe asia-australia east globe international world earth globe space planet asia australia home'], + 'anger' => ['unicode' => '1F4A2', 'shortname' => ':anger:', 'aliases' => '', 'keywords' => 'anger symbol anger angry mad'], + 'swimmer' => ['unicode' => '1F3CA', 'shortname' => ':swimmer:', 'aliases' => '', 'keywords' => 'swimmer sports swimmer swim water pool laps freestyle butterfly breaststroke backstroke'], + 'blossom' => ['unicode' => '1F33C', 'shortname' => ':blossom:', 'aliases' => '', 'keywords' => 'blossom flowers nature yellow blossom daisy flower'], + 'calling' => ['unicode' => '1F4F2', 'shortname' => ':calling:', 'aliases' => '', 'keywords' => 'mobile phone with rightwards arrow at left incoming iphone'], + 'haircut' => ['unicode' => '1F487', 'shortname' => ':haircut:', 'aliases' => '', 'keywords' => 'haircut female girl woman'], + 'heart_decoration' => ['unicode' => '1F49F', 'shortname' => ':heart_decoration:', 'aliases' => '', 'keywords' => 'heart decoration like love purple-square'], + 'cake' => ['unicode' => '1F370', 'shortname' => ':cake:', 'aliases' => '', 'keywords' => 'shortcake desert food cake short dessert strawberry'], + 'lollipop' => ['unicode' => '1F36D', 'shortname' => ':lollipop:', 'aliases' => '', 'keywords' => 'lollipop candy food snack sweet lollipop stick lick sweet sugar candy'], + 'pouting_cat' => ['unicode' => '1F63E', 'shortname' => ':pouting_cat:', 'aliases' => '', 'keywords' => 'pouting cat face animal cats pout annoyed miffed glower frown'], + 'syringe' => ['unicode' => '1F489', 'shortname' => ':syringe:', 'aliases' => '', 'keywords' => 'syringe blood drugs health hospital medicine needle'], + 'registered' => ['unicode' => '00AE', 'shortname' => ':registered:', 'aliases' => '', 'keywords' => 'registered sign alphabet circle'], + 'partly_sunny' => ['unicode' => '26C5', 'shortname' => ':partly_sunny:', 'aliases' => '', 'keywords' => 'sun behind cloud cloud morning nature weather'], + 'iphone' => ['unicode' => '1F4F1', 'shortname' => ':iphone:', 'aliases' => '', 'keywords' => 'mobile phone apple dial gadgets technology'], + 'arrow_backward' => ['unicode' => '25C0', 'shortname' => ':arrow_backward:', 'aliases' => '', 'keywords' => 'black left-pointing triangle arrow blue-square'], + 'whale' => ['unicode' => '1F433', 'shortname' => ':whale:', 'aliases' => '', 'keywords' => 'spouting whale animal nature ocean sea'], + 'envelope' => ['unicode' => '2709', 'shortname' => ':envelope:', 'aliases' => '', 'keywords' => 'envelope communication letter mail postal'], + 'tropical_drink' => ['unicode' => '1F379', 'shortname' => ':tropical_drink:', 'aliases' => '', 'keywords' => 'tropical drink beverage tropical drink mixed pineapple coconut pina fruit umbrella'], + 'cocktail' => ['unicode' => '1F378', 'shortname' => ':cocktail:', 'aliases' => '', 'keywords' => 'cocktail glass alcohol beverage drink drunk cocktail mixed drink alcohol glass martini bar'], + 'hatching_chick' => ['unicode' => '1F423', 'shortname' => ':hatching_chick:', 'aliases' => '', 'keywords' => 'hatching chick born chicken egg chick egg baby bird chicken young woman cute'], + 'smiley_cat' => ['unicode' => '1F63A', 'shortname' => ':smiley_cat:', 'aliases' => '', 'keywords' => 'smiling cat face with open mouth animal cats happy smile smiley cat happy'], + 'fallen_leaf' => ['unicode' => '1F342', 'shortname' => ':fallen_leaf:', 'aliases' => '', 'keywords' => 'fallen leaf leaves nature plant vegetable leaf fall color deciduous autumn'], + 'bear' => ['unicode' => '1F43B', 'shortname' => ':bear:', 'aliases' => '', 'keywords' => 'bear face animal nature'], + 'man_with_turban' => ['unicode' => '1F473', 'shortname' => ':man_with_turban:', 'aliases' => '', 'keywords' => 'man with turban male turban headdress headwear pagri india indian mummy wisdom peace'], + 'monkey' => ['unicode' => '1F412', 'shortname' => ':monkey:', 'aliases' => '', 'keywords' => 'monkey animal nature monkey primate banana silly'], + 'full_moon' => ['unicode' => '1F315', 'shortname' => ':full_moon:', 'aliases' => '', 'keywords' => 'full moon symbol nature yellow moon full sky night cheese phase monster spooky werewolves twilight'], + 'chocolate_bar' => ['unicode' => '1F36B', 'shortname' => ':chocolate_bar:', 'aliases' => '', 'keywords' => 'chocolate bar desert food snack chocolate bar candy coca hershey\'s'], + 'rabbit' => ['unicode' => '1F430', 'shortname' => ':rabbit:', 'aliases' => '', 'keywords' => 'rabbit face animal nature'], + 'musical_score' => ['unicode' => '1F3BC', 'shortname' => ':musical_score:', 'aliases' => '', 'keywords' => 'musical score clef treble music musical score clef g-clef stave staff'], + 'snake' => ['unicode' => '1F40D', 'shortname' => ':snake:', 'aliases' => '', 'keywords' => 'snake animal evil'], + 'bee' => ['unicode' => '1F41D', 'shortname' => ':bee:', 'aliases' => '', 'keywords' => 'honeybee animal insect bee queen buzz flower pollen sting honey hive bumble pollination'], + 'mortar_board' => ['unicode' => '1F393', 'shortname' => ':mortar_board:', 'aliases' => '', 'keywords' => 'graduation cap cap college degree graduation hat school university graduation cap mortarboard academic education ceremony square tassel'], + 'new_moon' => ['unicode' => '1F311', 'shortname' => ':new_moon:', 'aliases' => '', 'keywords' => 'new moon symbol nature moon new sky night cheese phase'], + 'woman' => ['unicode' => '1F469', 'shortname' => ':woman:', 'aliases' => '', 'keywords' => 'woman female girls'], + 'baseball' => ['unicode' => '26BE', 'shortname' => ':baseball:', 'aliases' => '', 'keywords' => 'baseball MLB balls sports'], + 'older_woman' => ['unicode' => '1F475', 'shortname' => ':older_woman:', 'aliases' => ':grandma:', 'keywords' => 'older woman female girl women grandma grandmother'], + 'no_entry_sign' => ['unicode' => '1F6AB', 'shortname' => ':no_entry_sign:', 'aliases' => '', 'keywords' => 'no entry sign denied disallow forbid limit stop no stop entry'], + 'dolphin' => ['unicode' => '1F42C', 'shortname' => ':dolphin:', 'aliases' => '', 'keywords' => 'dolphin animal fins fish flipper nature ocean sea'], + 'books' => ['unicode' => '1F4DA', 'shortname' => ':books:', 'aliases' => '', 'keywords' => 'books library literature'], + 'bikini' => ['unicode' => '1F459', 'shortname' => ':bikini:', 'aliases' => '', 'keywords' => 'bikini beach fashion female girl swimming woman'], + 'tv' => ['unicode' => '1F1F9-1F1FB', 'shortname' => ':tv:', 'aliases' => '', 'keywords' => 'tuvalu country nation'], + 'strawberry' => ['unicode' => '1F353', 'shortname' => ':strawberry:', 'aliases' => '', 'keywords' => 'strawberry food fruit nature strawberry short cake berry'], + 'feet' => ['unicode' => '1F43E', 'shortname' => ':feet:', 'aliases' => '', 'keywords' => 'paw prints animal cat dog footprints paw pet tracking paw prints mark imprints footsteps animal lion bear dog cat raccoon critter feet pawsteps'], + 'family' => ['unicode' => '1F46A', 'shortname' => ':family:', 'aliases' => '', 'keywords' => 'family child dad father home mom mother parents family mother father child girl boy group unit'], + 'hatched_chick' => ['unicode' => '1F425', 'shortname' => ':hatched_chick:', 'aliases' => '', 'keywords' => 'front-facing baby chick baby chicken chick baby bird chicken young woman cute'], + 'nose' => ['unicode' => '1F443', 'shortname' => ':nose:', 'aliases' => '', 'keywords' => 'nose smell sniff'], + 'cherries' => ['unicode' => '1F352', 'shortname' => ':cherries:', 'aliases' => '', 'keywords' => 'cherries food fruit cherry cherries tree fruit pit'], + 'jack_o_lantern' => ['unicode' => '1F383', 'shortname' => ':jack_o_lantern:', 'aliases' => '', 'keywords' => 'jack-o-lantern halloween jack-o-lantern pumpkin halloween holiday carve autumn fall october saints costume spooky horror scary scared dead'], + 'ear_of_rice' => ['unicode' => '1F33E', 'shortname' => ':ear_of_rice:', 'aliases' => '', 'keywords' => 'ear of rice nature plant ear rice food plant seed'], + 'scissors' => ['unicode' => '2702', 'shortname' => ':scissors:', 'aliases' => '', 'keywords' => 'black scissors cut stationery'], + 'frog' => ['unicode' => '1F438', 'shortname' => ':frog:', 'aliases' => '', 'keywords' => 'frog face animal nature'], + 'octopus' => ['unicode' => '1F419', 'shortname' => ':octopus:', 'aliases' => '', 'keywords' => 'octopus animal creature ocean sea'], + 'high_heel' => ['unicode' => '1F460', 'shortname' => ':high_heel:', 'aliases' => '', 'keywords' => 'high-heeled shoe fashion female shoes'], + 'loud_sound' => ['unicode' => '1F50A', 'shortname' => ':loud_sound:', 'aliases' => '', 'keywords' => 'speaker with three sound waves '], + 'top' => ['unicode' => '1F51D', 'shortname' => ':top:', 'aliases' => '', 'keywords' => 'top with upwards arrow above blue-square words'], + 'house_with_garden' => ['unicode' => '1F3E1', 'shortname' => ':house_with_garden:', 'aliases' => '', 'keywords' => 'house with garden home nature plant'], + 'rotating_light' => ['unicode' => '1F6A8', 'shortname' => ':rotating_light:', 'aliases' => '', 'keywords' => 'police cars revolving light 911 ambulance emergency police light police emergency'], + 'lipstick' => ['unicode' => '1F484', 'shortname' => ':lipstick:', 'aliases' => '', 'keywords' => 'lipstick fashion female girl'], + 'ear' => ['unicode' => '1F442', 'shortname' => ':ear:', 'aliases' => '', 'keywords' => 'ear face hear listen sound'], + 'first_quarter_moon' => ['unicode' => '1F313', 'shortname' => ':first_quarter_moon:', 'aliases' => '', 'keywords' => 'first quarter moon symbol nature moon quarter first sky night cheese phase'], + 'pineapple' => ['unicode' => '1F34D', 'shortname' => ':pineapple:', 'aliases' => '', 'keywords' => 'pineapple food fruit nature pineapple pina tropical flower'], + 'elephant' => ['unicode' => '1F418', 'shortname' => ':elephant:', 'aliases' => '', 'keywords' => 'elephant animal nature nose thailand'], + 'athletic_shoe' => ['unicode' => '1F45F', 'shortname' => ':athletic_shoe:', 'aliases' => '', 'keywords' => 'athletic shoe shoes sports'], + 'crystal_ball' => ['unicode' => '1F52E', 'shortname' => ':crystal_ball:', 'aliases' => '', 'keywords' => 'crystal ball disco party'], + 'love_letter' => ['unicode' => '1F48C', 'shortname' => ':love_letter:', 'aliases' => '', 'keywords' => 'love letter affection email envelope like valentines love letter kiss heart'], + 'waxing_gibbous_moon' => ['unicode' => '1F314', 'shortname' => ':waxing_gibbous_moon:', 'aliases' => '', 'keywords' => 'waxing gibbous moon symbol nature'], + 'girl' => ['unicode' => '1F467', 'shortname' => ':girl:', 'aliases' => '', 'keywords' => 'girl female woman'], + 'cool' => ['unicode' => '1F192', 'shortname' => ':cool:', 'aliases' => '', 'keywords' => 'squared cool blue-square words'], + 'white_circle' => ['unicode' => '26AA', 'shortname' => ':white_circle:', 'aliases' => '', 'keywords' => 'medium white circle shape'], + 'poultry_leg' => ['unicode' => '1F357', 'shortname' => ':poultry_leg:', 'aliases' => '', 'keywords' => 'poultry leg food meat poultry leg chicken fried'], + 'speech_balloon' => ['unicode' => '1F4AC', 'shortname' => ':speech_balloon:', 'aliases' => '', 'keywords' => 'speech balloon bubble words speech balloon talk conversation communication comic dialogue'], + 'question' => ['unicode' => '2753', 'shortname' => ':question:', 'aliases' => '', 'keywords' => 'black question mark ornament confused doubt'], + 'tropical_fish' => ['unicode' => '1F420', 'shortname' => ':tropical_fish:', 'aliases' => '', 'keywords' => 'tropical fish animal swim'], + 'older_man' => ['unicode' => '1F474', 'shortname' => ':older_man:', 'aliases' => '', 'keywords' => 'older man human male men'], + 'bride_with_veil' => ['unicode' => '1F470', 'shortname' => ':bride_with_veil:', 'aliases' => '', 'keywords' => 'bride with veil couple marriage wedding bride wedding planning veil gown dress engagement white'], + 'peach' => ['unicode' => '1F351', 'shortname' => ':peach:', 'aliases' => '', 'keywords' => 'peach food fruit nature peach fruit juicy pit'], + 'eyeglasses' => ['unicode' => '1F453', 'shortname' => ':eyeglasses:', 'aliases' => '', 'keywords' => 'eyeglasses accessories eyesight fashion eyeglasses spectacles eye sight nearsightedness myopia farsightedness hyperopia frames vision see blurry contacts'], + 'pencil' => ['unicode' => '1F4DD', 'shortname' => ':pencil:', 'aliases' => '', 'keywords' => 'memo documents paper station write'], + 'spaghetti' => ['unicode' => '1F35D', 'shortname' => ':spaghetti:', 'aliases' => '', 'keywords' => 'spaghetti food italian noodle spaghetti noodles tomato sauce italian'], + 'boy' => ['unicode' => '1F466', 'shortname' => ':boy:', 'aliases' => '', 'keywords' => 'boy guy male man'], + 'black_circle' => ['unicode' => '26AB', 'shortname' => ':black_circle:', 'aliases' => '', 'keywords' => 'medium black circle shape'], + 'book' => ['unicode' => '1F4D6', 'shortname' => ':book:', 'aliases' => '', 'keywords' => 'open book library literature'], + 'pill' => ['unicode' => '1F48A', 'shortname' => ':pill:', 'aliases' => '', 'keywords' => 'pill health medicine'], + 'loudspeaker' => ['unicode' => '1F4E2', 'shortname' => ':loudspeaker:', 'aliases' => '', 'keywords' => 'public address loudspeaker sound volume'], + 'horse' => ['unicode' => '1F434', 'shortname' => ':horse:', 'aliases' => '', 'keywords' => 'horse face animal brown'], + 'milky_way' => ['unicode' => '1F30C', 'shortname' => ':milky_way:', 'aliases' => '', 'keywords' => 'milky way photo space milky galaxy star stars planets space sky'], + 'fish' => ['unicode' => '1F41F', 'shortname' => ':fish:', 'aliases' => '', 'keywords' => 'fish animal food nature'], + 'surfer' => ['unicode' => '1F3C4', 'shortname' => ':surfer:', 'aliases' => '', 'keywords' => 'surfer ocean sea sports surfer surf wave ocean ride swell'], + 'closed_lock_with_key' => ['unicode' => '1F510', 'shortname' => ':closed_lock_with_key:', 'aliases' => '', 'keywords' => 'closed lock with key privacy security'], + 'warning' => ['unicode' => '26A0', 'shortname' => ':warning:', 'aliases' => '', 'keywords' => 'warning sign exclamation wip'], + 'apple' => ['unicode' => '1F34E', 'shortname' => ':apple:', 'aliases' => '', 'keywords' => 'red apple fruit mac apple fruit electronics red doctor teacher school core'], + 'fishing_pole_and_fish' => ['unicode' => '1F3A3', 'shortname' => ':fishing_pole_and_fish:', 'aliases' => '', 'keywords' => 'fishing pole and fish food hobby fish fishing pole'], + 'dress' => ['unicode' => '1F457', 'shortname' => ':dress:', 'aliases' => '', 'keywords' => 'dress clothes fashion'], + 'clapper' => ['unicode' => '1F3AC', 'shortname' => ':clapper:', 'aliases' => '', 'keywords' => 'clapper board film movie record clapper board clapboard movie film take'], + 'man_with_gua_pi_mao' => ['unicode' => '1F472', 'shortname' => ':man_with_gua_pi_mao:', 'aliases' => '', 'keywords' => 'man with gua pi mao boy male skullcap chinese asian qing'], + 'sunrise' => ['unicode' => '1F305', 'shortname' => ':sunrise:', 'aliases' => '', 'keywords' => 'sunrise morning photo vacation view sunrise sun morning color sky'], + 'grapes' => ['unicode' => '1F347', 'shortname' => ':grapes:', 'aliases' => '', 'keywords' => 'grapes food fruit grapes wine vinegar fruit cluster vine'], + 'first_quarter_moon_with_face' => ['unicode' => '1F31B', 'shortname' => ':first_quarter_moon_with_face:', 'aliases' => '', 'keywords' => 'first quarter moon with face nature moon first quarter anthropomorphic face sky night cheese phase'], + 'telephone_receiver' => ['unicode' => '1F4DE', 'shortname' => ':telephone_receiver:', 'aliases' => '', 'keywords' => 'telephone receiver communication dial technology'], + 'eight_spoked_asterisk' => ['unicode' => '2733', 'shortname' => ':eight_spoked_asterisk:', 'aliases' => '', 'keywords' => 'eight spoked asterisk green-square sparkle star'], + 'sos' => ['unicode' => '1F198', 'shortname' => ':sos:', 'aliases' => '', 'keywords' => 'squared sos emergency help red-square words'], + 'koala' => ['unicode' => '1F428', 'shortname' => ':koala:', 'aliases' => '', 'keywords' => 'koala animal nature'], + 'blue_car' => ['unicode' => '1F699', 'shortname' => ':blue_car:', 'aliases' => '', 'keywords' => 'recreational vehicle car suv car wagon automobile'], + 'arrow_down' => ['unicode' => '2B07', 'shortname' => ':arrow_down:', 'aliases' => '', 'keywords' => 'downwards black arrow arrow blue-square'], + 'ramen' => ['unicode' => '1F35C', 'shortname' => ':ramen:', 'aliases' => '', 'keywords' => 'steaming bowl chipsticks food japanese noodle ramen noodles bowl steaming soup'], + 'house' => ['unicode' => '1F3E0', 'shortname' => ':house:', 'aliases' => '', 'keywords' => 'house building building home house home residence dwelling mansion bungalow ranch craftsman'], + 'pig_nose' => ['unicode' => '1F43D', 'shortname' => ':pig_nose:', 'aliases' => '', 'keywords' => 'pig nose animal oink pig nose snout food eat cute oink pink smell truffle'], + 'anchor' => ['unicode' => '2693', 'shortname' => ':anchor:', 'aliases' => '', 'keywords' => 'anchor ferry ship anchor ship boat ocean harbor marina shipyard sailor tattoo'], + 'art' => ['unicode' => '1F3A8', 'shortname' => ':art:', 'aliases' => '', 'keywords' => 'artist palette design draw paint artist palette art colors paint draw brush pastels oils'], + 'chicken' => ['unicode' => '1F414', 'shortname' => ':chicken:', 'aliases' => '', 'keywords' => 'chicken animal cluck chicken hen poultry livestock'], + 'wavy_dash' => ['unicode' => '3030', 'shortname' => ':wavy_dash:', 'aliases' => '', 'keywords' => 'wavy dash draw line'], + 'monkey_face' => ['unicode' => '1F435', 'shortname' => ':monkey_face:', 'aliases' => '', 'keywords' => 'monkey face animal nature'], + 'ok' => ['unicode' => '1F197', 'shortname' => ':ok:', 'aliases' => '', 'keywords' => 'squared ok agree blue-square good yes'], + 'candy' => ['unicode' => '1F36C', 'shortname' => ':candy:', 'aliases' => '', 'keywords' => 'candy desert snack candy sugar sweet hard'], + 'tangerine' => ['unicode' => '1F34A', 'shortname' => ':tangerine:', 'aliases' => '', 'keywords' => 'tangerine food fruit nature tangerine citrus orange'], + 'm' => ['unicode' => '24C2', 'shortname' => ':m:', 'aliases' => '', 'keywords' => 'circled latin capital letter m alphabet blue-circle letter'], + 'bath' => ['unicode' => '1F6C0', 'shortname' => ':bath:', 'aliases' => '', 'keywords' => 'bath clean shower bath tub basin wash bubble soak bathroom soap water clean shampoo lather water'], + 'cow' => ['unicode' => '1F42E', 'shortname' => ':cow:', 'aliases' => '', 'keywords' => 'cow face animal beef ox'], + 'mushroom' => ['unicode' => '1F344', 'shortname' => ':mushroom:', 'aliases' => '', 'keywords' => 'mushroom plant vegetable mushroom fungi food fungus'], + 'mouse' => ['unicode' => '1F42D', 'shortname' => ':mouse:', 'aliases' => '', 'keywords' => 'mouse face animal nature'], + 'large_blue_circle' => ['unicode' => '1F535', 'shortname' => ':large_blue_circle:', 'aliases' => '', 'keywords' => 'large blue circle '], + 'japanese_goblin' => ['unicode' => '1F47A', 'shortname' => ':japanese_goblin:', 'aliases' => '', 'keywords' => 'japanese goblin evil mask red japanese tengu supernatural avian demon goblin mask theater nose frown mustache anger frustration'], + 'moyai' => ['unicode' => '1F5FF', 'shortname' => ':moyai:', 'aliases' => '', 'keywords' => 'moyai island stone'], + 'egg' => ['unicode' => '1F373', 'shortname' => ':egg:', 'aliases' => '', 'keywords' => 'cooking breakfast food egg fry pan flat cook frying cooking utensil'], + 'tennis' => ['unicode' => '1F3BE', 'shortname' => ':tennis:', 'aliases' => '', 'keywords' => 'tennis racquet and ball balls green sports tennis racket racquet ball game net court love'], + 'fireworks' => ['unicode' => '1F386', 'shortname' => ':fireworks:', 'aliases' => '', 'keywords' => 'fireworks carnival congratulations festival photo fireworks independence celebration explosion july 4th rocket sky idea excitement'], + 'racehorse' => ['unicode' => '1F40E', 'shortname' => ':racehorse:', 'aliases' => '', 'keywords' => 'horse animal gamble horse powerful draft calvary cowboy cowgirl mounted race ride gallop trot colt filly mare stallion gelding yearling thoroughbred pony'], + 'bread' => ['unicode' => '1F35E', 'shortname' => ':bread:', 'aliases' => '', 'keywords' => 'bread breakfast food toast wheat bread loaf yeast'], + 'bird' => ['unicode' => '1F426', 'shortname' => ':bird:', 'aliases' => '', 'keywords' => 'bird animal fly nature tweet'], + 'droplet' => ['unicode' => '1F4A7', 'shortname' => ':droplet:', 'aliases' => '', 'keywords' => 'droplet drip faucet water drop droplet h20 water aqua tear sweat rain moisture wet moist spit'], + 'fried_shrimp' => ['unicode' => '1F364', 'shortname' => ':fried_shrimp:', 'aliases' => '', 'keywords' => 'fried shrimp animal food shrimp fried seafood small fish'], + 'key' => ['unicode' => '1F511', 'shortname' => ':key:', 'aliases' => '', 'keywords' => 'key door lock password'], + 'back' => ['unicode' => '1F519', 'shortname' => ':back:', 'aliases' => '', 'keywords' => 'back with leftwards arrow above arrow'], + 'bike' => ['unicode' => '1F6B2', 'shortname' => ':bike:', 'aliases' => '', 'keywords' => 'bicycle bicycle exercise hipster sports bike pedal bicycle transportation'], + 'pencil2' => ['unicode' => '270F', 'shortname' => ':pencil2:', 'aliases' => '', 'keywords' => 'pencil paper stationery write'], + 'shaved_ice' => ['unicode' => '1F367', 'shortname' => ':shaved_ice:', 'aliases' => '', 'keywords' => 'shaved ice desert hot shaved ice dessert treat syrup flavoring'], + 'arrow_right_hook' => ['unicode' => '21AA', 'shortname' => ':arrow_right_hook:', 'aliases' => '', 'keywords' => 'rightwards arrow with hook blue-square'], + 'bulb' => ['unicode' => '1F4A1', 'shortname' => ':bulb:', 'aliases' => '', 'keywords' => 'electric light bulb electricity light idea bulb light'], + 'tophat' => ['unicode' => '1F3A9', 'shortname' => ':tophat:', 'aliases' => '', 'keywords' => 'top hat classy gentleman magic top hat cap beaver high tall stove pipe chimney topper london period piece magic magician'], + 'wolf' => ['unicode' => '1F43A', 'shortname' => ':wolf:', 'aliases' => '', 'keywords' => 'wolf face animal nature'], + 'night_with_stars' => ['unicode' => '1F303', 'shortname' => ':night_with_stars:', 'aliases' => '', 'keywords' => 'night with stars night star cloudless evening planets space sky'], + 'grey_exclamation' => ['unicode' => '2755', 'shortname' => ':grey_exclamation:', 'aliases' => '', 'keywords' => 'white exclamation mark ornament surprise'], + 'alarm_clock' => ['unicode' => '23F0', 'shortname' => ':alarm_clock:', 'aliases' => '', 'keywords' => 'alarm clock time wake'], + 'cop' => ['unicode' => '1F46E', 'shortname' => ':cop:', 'aliases' => '', 'keywords' => 'police officer arrest enforcement law man police'], + 'arrow_lower_left' => ['unicode' => '2199', 'shortname' => ':arrow_lower_left:', 'aliases' => '', 'keywords' => 'south west arrow arrow blue-square'], + 'person_with_blond_hair' => ['unicode' => '1F471', 'shortname' => ':person_with_blond_hair:', 'aliases' => '', 'keywords' => 'person with blond hair male man blonde young western westerner occidental'], + 'jeans' => ['unicode' => '1F456', 'shortname' => ':jeans:', 'aliases' => '', 'keywords' => 'jeans fashion shopping jeans pants blue denim levi\'s levi designer work skinny'], + 'sheep' => ['unicode' => '1F411', 'shortname' => ':sheep:', 'aliases' => '', 'keywords' => 'sheep animal nature sheep wool flock follower ewe female lamb'], + 'golf' => ['unicode' => '26F3', 'shortname' => ':golf:', 'aliases' => '', 'keywords' => 'flag in hole business sports'], + 'arrow_upper_right' => ['unicode' => '2197', 'shortname' => ':arrow_upper_right:', 'aliases' => '', 'keywords' => 'north east arrow blue-square'], + 'cd' => ['unicode' => '1F1E8-1F1E9', 'shortname' => ':cd:', 'aliases' => '', 'keywords' => 'the democratic republic of the congo country nation république démocratique du congo republique democratique du congo'], + 'watch' => ['unicode' => '231A', 'shortname' => ':watch:', 'aliases' => '', 'keywords' => 'watch accessories time'], + 'performing_arts' => ['unicode' => '1F3AD', 'shortname' => ':performing_arts:', 'aliases' => '', 'keywords' => 'performing arts acting drama theater performing arts performance entertainment acting story mask masks'], + 'bug' => ['unicode' => '1F41B', 'shortname' => ':bug:', 'aliases' => '', 'keywords' => 'bug insect nature bug insect virus error'], + 'sushi' => ['unicode' => '1F363', 'shortname' => ':sushi:', 'aliases' => '', 'keywords' => 'sushi food japanese sushi fish raw nigiri japanese'], + 'baby_chick' => ['unicode' => '1F424', 'shortname' => ':baby_chick:', 'aliases' => '', 'keywords' => 'baby chick animal chicken chick baby bird chicken young woman cute'], + 'small_blue_diamond' => ['unicode' => '1F539', 'shortname' => ':small_blue_diamond:', 'aliases' => '', 'keywords' => 'small blue diamond shape'], + 'electric_plug' => ['unicode' => '1F50C', 'shortname' => ':electric_plug:', 'aliases' => '', 'keywords' => 'electric plug charger power'], + 'lock' => ['unicode' => '1F512', 'shortname' => ':lock:', 'aliases' => '', 'keywords' => 'lock password security'], + 'black_square_button' => ['unicode' => '1F532', 'shortname' => ':black_square_button:', 'aliases' => '', 'keywords' => 'black square button frame'], + 'fish_cake' => ['unicode' => '1F365', 'shortname' => ':fish_cake:', 'aliases' => '', 'keywords' => 'fish cake with swirl design food fish cake kamboko swirl ramen noodles naruto'], + 'seedling' => ['unicode' => '1F331', 'shortname' => ':seedling:', 'aliases' => '', 'keywords' => 'seedling grass lawn nature plant seedling plant new start grow'], + 'corn' => ['unicode' => '1F33D', 'shortname' => ':corn:', 'aliases' => '', 'keywords' => 'ear of maize food plant vegetable corn maize food iowa kernel popcorn husk yellow stalk cob ear'], + 'leftwards_arrow_with_hook' => ['unicode' => '21A9', 'shortname' => ':leftwards_arrow_with_hook:', 'aliases' => '', 'keywords' => 'leftwards arrow with hook '], + 'arrow_heading_down' => ['unicode' => '2935', 'shortname' => ':arrow_heading_down:', 'aliases' => '', 'keywords' => 'arrow pointing rightwards then curving downwards arrow blue-square'], + 'ant' => ['unicode' => '1F41C', 'shortname' => ':ant:', 'aliases' => '', 'keywords' => 'ant animal insect ant queen insect team'], + 'checkered_flag' => ['unicode' => '1F3C1', 'shortname' => ':checkered_flag:', 'aliases' => '', 'keywords' => 'chequered flag contest finishline gokart rase checkered chequred race flag finish complete end'], + 'tea' => ['unicode' => '1F375', 'shortname' => ':tea:', 'aliases' => '', 'keywords' => 'teacup without handle bowl breakfast british drink green tea leaf drink teacup hot beverage'], + 'stew' => ['unicode' => '1F372', 'shortname' => ':stew:', 'aliases' => '', 'keywords' => 'pot of food food meat stew hearty soup thick hot pot'], + 'arrow_up' => ['unicode' => '2B06', 'shortname' => ':arrow_up:', 'aliases' => '', 'keywords' => 'upwards black arrow blue-square'], + 'underage' => ['unicode' => '1F51E', 'shortname' => ':underage:', 'aliases' => '', 'keywords' => 'no one under eighteen symbol 18 drink night pub'], + 'snail' => ['unicode' => '1F40C', 'shortname' => ':snail:', 'aliases' => '', 'keywords' => 'snail animal shell slow snail slow escargot french appetizer'], + 'meat_on_bone' => ['unicode' => '1F356', 'shortname' => ':meat_on_bone:', 'aliases' => '', 'keywords' => 'meat on bone food good meat bone animal cooked'], + 'camel' => ['unicode' => '1F42B', 'shortname' => ':camel:', 'aliases' => '', 'keywords' => 'bactrian camel animal hot nature bactrian camel hump desert central asia heat hot water hump day wednesday sex'], + 'necktie' => ['unicode' => '1F454', 'shortname' => ':necktie:', 'aliases' => '', 'keywords' => 'necktie cloth fashion formal shirt suitup'], + 'toilet' => ['unicode' => '1F6BD', 'shortname' => ':toilet:', 'aliases' => '', 'keywords' => 'toilet restroom wc toilet bathroom throne porcelain waste flush plumbing'], + 'a' => ['unicode' => '1F170', 'shortname' => ':a:', 'aliases' => '', 'keywords' => 'negative squared latin capital letter a alphabet letter red-square'], + 'arrow_lower_right' => ['unicode' => '2198', 'shortname' => ':arrow_lower_right:', 'aliases' => '', 'keywords' => 'south east arrow arrow blue-square'], + 'hamster' => ['unicode' => '1F439', 'shortname' => ':hamster:', 'aliases' => '', 'keywords' => 'hamster face animal nature'], + 'fuelpump' => ['unicode' => '26FD', 'shortname' => ':fuelpump:', 'aliases' => '', 'keywords' => 'fuel pump gas station petroleum'], + 'hammer' => ['unicode' => '1F528', 'shortname' => ':hammer:', 'aliases' => '', 'keywords' => 'hammer done judge law ruling tools verdict'], + 'bust_in_silhouette' => ['unicode' => '1F464', 'shortname' => ':bust_in_silhouette:', 'aliases' => '', 'keywords' => 'bust in silhouette human man person user silhouette person user member account guest icon avatar profile me myself i'], + 'up' => ['unicode' => '1F199', 'shortname' => ':up:', 'aliases' => '', 'keywords' => 'squared up with exclamation mark blue-square'], + 'snowboarder' => ['unicode' => '1F3C2', 'shortname' => ':snowboarder:', 'aliases' => '', 'keywords' => 'snowboarder sports winter snow boarding sports freestyle halfpipe board mountain alpine winter'], + 'curly_loop' => ['unicode' => '27B0', 'shortname' => ':curly_loop:', 'aliases' => '', 'keywords' => 'curly loop scribble'], + 'handbag' => ['unicode' => '1F45C', 'shortname' => ':handbag:', 'aliases' => '', 'keywords' => 'handbag accessories accessory bag fashion'], + 'dart' => ['unicode' => '1F3AF', 'shortname' => ':dart:', 'aliases' => '', 'keywords' => 'direct hit bar game direct hit bullseye dart archery game fletching arrow sport'], + 'computer' => ['unicode' => '1F4BB', 'shortname' => ':computer:', 'aliases' => '', 'keywords' => 'personal computer laptop tech'], + 'poodle' => ['unicode' => '1F429', 'shortname' => ':poodle:', 'aliases' => '', 'keywords' => 'poodle 101 animal dog nature poodle dog clip showy sophisticated vain'], + 'cancer' => ['unicode' => '264B', 'shortname' => ':cancer:', 'aliases' => '', 'keywords' => 'cancer cancer crab astrology greek constellation stars zodiac sign sign zodiac horoscope'], + 'rice' => ['unicode' => '1F35A', 'shortname' => ':rice:', 'aliases' => '', 'keywords' => 'cooked rice food rice white grain food bowl'], + 'black_medium_small_square' => ['unicode' => '25FE', 'shortname' => ':black_medium_small_square:', 'aliases' => '', 'keywords' => 'black medium small square '], + 'seat' => ['unicode' => '1F4BA', 'shortname' => ':seat:', 'aliases' => '', 'keywords' => 'seat sit'], + 'shell' => ['unicode' => '1F41A', 'shortname' => ':shell:', 'aliases' => '', 'keywords' => 'spiral shell beach nature sea shell spiral beach sand crab nautilus'], + 'trident' => ['unicode' => '1F531', 'shortname' => ':trident:', 'aliases' => '', 'keywords' => 'trident emblem spear weapon'], + 'hotsprings' => ['unicode' => '2668', 'shortname' => ':hotsprings:', 'aliases' => '', 'keywords' => 'hot springs bath relax warm'], + 'curry' => ['unicode' => '1F35B', 'shortname' => ':curry:', 'aliases' => '', 'keywords' => 'curry and rice food hot indian spicy curry spice flavor food meal'], + 'ice_cream' => ['unicode' => '1F368', 'shortname' => ':ice_cream:', 'aliases' => '', 'keywords' => 'ice cream desert food hot icecream ice cream dairy dessert cold soft serve cone waffle'], + 'diamond_shape_with_a_dot_inside' => ['unicode' => '1F4A0', 'shortname' => ':diamond_shape_with_a_dot_inside:', 'aliases' => '', 'keywords' => 'diamond shape with a dot inside diamond cute cuteness kawaii japanese glyph adorable'], + 'green_apple' => ['unicode' => '1F34F', 'shortname' => ':green_apple:', 'aliases' => '', 'keywords' => 'green apple fruit nature apple fruit green pie granny smith core'], + 'statue_of_liberty' => ['unicode' => '1F5FD', 'shortname' => ':statue_of_liberty:', 'aliases' => '', 'keywords' => 'statue of liberty american newyork'], + 'bus' => ['unicode' => '1F68C', 'shortname' => ':bus:', 'aliases' => '', 'keywords' => 'bus car transportation vehicle bus school city transportation public'], + 'bowling' => ['unicode' => '1F3B3', 'shortname' => ':bowling:', 'aliases' => '', 'keywords' => 'bowling fun play sports bowl bowling ball pin strike spare game'], + 'dolls' => ['unicode' => '1F38E', 'shortname' => ':dolls:', 'aliases' => '', 'keywords' => 'japanese dolls japanese kimono toy dolls japan japanese day girls emperor empress pray blessing imperial family royal'], + 'baby_symbol' => ['unicode' => '1F6BC', 'shortname' => ':baby_symbol:', 'aliases' => '', 'keywords' => 'baby symbol child orange-square baby crawl newborn human diaper small babe'], + 'construction_worker' => ['unicode' => '1F477', 'shortname' => ':construction_worker:', 'aliases' => '', 'keywords' => 'construction worker human male man wip'], + 'custard' => ['unicode' => '1F36E', 'shortname' => ':custard:', 'aliases' => '', 'keywords' => 'custard desert food custard cream rich butter dessert crème brûlée french'], + 'unlock' => ['unicode' => '1F513', 'shortname' => ':unlock:', 'aliases' => '', 'keywords' => 'open lock privacy security'], + 'shirt' => ['unicode' => '1F455', 'shortname' => ':shirt:', 'aliases' => '', 'keywords' => 't-shirt cloth fashion'], + 'credit_card' => ['unicode' => '1F4B3', 'shortname' => ':credit_card:', 'aliases' => '', 'keywords' => 'credit card bill dollar money pay payment credit card loan purchase shopping mastercard visa american express wallet signature'], + 'bento' => ['unicode' => '1F371', 'shortname' => ':bento:', 'aliases' => '', 'keywords' => 'bento box box food japanese bento japanese rice meal box obento convenient lunchbox'], + 'beetle' => ['unicode' => '1F41E', 'shortname' => ':beetle:', 'aliases' => '', 'keywords' => 'lady beetle insect nature lady bug ladybug ladybird beetle cow lady cow insect endearment'], + 'mans_shoe' => ['unicode' => '1F45E', 'shortname' => ':mans_shoe:', 'aliases' => '', 'keywords' => 'mans shoe fashion male'], + 'chestnut' => ['unicode' => '1F330', 'shortname' => ':chestnut:', 'aliases' => '', 'keywords' => 'chestnut food squirrel chestnut roasted food tree'], + 'interrobang' => ['unicode' => '2049', 'shortname' => ':interrobang:', 'aliases' => '', 'keywords' => 'exclamation question mark punctuation surprise wat'], + 'small_red_triangle' => ['unicode' => '1F53A', 'shortname' => ':small_red_triangle:', 'aliases' => '', 'keywords' => 'up-pointing red triangle shape'], + 'heavy_dollar_sign' => ['unicode' => '1F4B2', 'shortname' => ':heavy_dollar_sign:', 'aliases' => '', 'keywords' => 'heavy dollar sign currency money payment dollar currency money cash sale purchase value'], + 'battery' => ['unicode' => '1F50B', 'shortname' => ':battery:', 'aliases' => '', 'keywords' => 'battery energy power sustain'], + 'black_nib' => ['unicode' => '2712', 'shortname' => ':black_nib:', 'aliases' => '', 'keywords' => 'black nib pen stationery'], + 'police_car' => ['unicode' => '1F693', 'shortname' => ':police_car:', 'aliases' => '', 'keywords' => 'police car cars enforcement law transportation vehicle police car emergency ticket citation crime help officer'], + 'honey_pot' => ['unicode' => '1F36F', 'shortname' => ':honey_pot:', 'aliases' => '', 'keywords' => 'honey pot bees sweet honey pot bees pooh bear'], + 'small_orange_diamond' => ['unicode' => '1F538', 'shortname' => ':small_orange_diamond:', 'aliases' => '', 'keywords' => 'small orange diamond shape'], + 'b' => ['unicode' => '1F171', 'shortname' => ':b:', 'aliases' => '', 'keywords' => 'negative squared latin capital letter b alphabet letter red-square'], + 'arrows_clockwise' => ['unicode' => '1F503', 'shortname' => ':arrows_clockwise:', 'aliases' => '', 'keywords' => 'clockwise downwards and upwards open circle arrows sync'], + 'roller_coaster' => ['unicode' => '1F3A2', 'shortname' => ':roller_coaster:', 'aliases' => '', 'keywords' => 'roller coaster carnival fun photo play playground roller coaster amusement park fair ride entertainment'], + 'door' => ['unicode' => '1F6AA', 'shortname' => ':door:', 'aliases' => '', 'keywords' => 'door entry exit house door doorway entrance enter exit entry'], + 'sunrise_over_mountains' => ['unicode' => '1F304', 'shortname' => ':sunrise_over_mountains:', 'aliases' => '', 'keywords' => 'sunrise over mountains photo vacation view sunrise sun morning mountain rural color sky'], + '8ball' => ['unicode' => '1F3B1', 'shortname' => ':8ball:', 'aliases' => '', 'keywords' => 'billiards pool billiards eight ball pool pocket ball cue'], + 'eight_pointed_black_star' => ['unicode' => '2734', 'shortname' => ':eight_pointed_black_star:', 'aliases' => '', 'keywords' => 'eight pointed black star '], + 'musical_keyboard' => ['unicode' => '1F3B9', 'shortname' => ':musical_keyboard:', 'aliases' => '', 'keywords' => 'musical keyboard instrument piano music keyboard piano organ instrument electric'], + 'sparkler' => ['unicode' => '1F387', 'shortname' => ':sparkler:', 'aliases' => '', 'keywords' => 'firework sparkler night shine stars'], + 'small_red_triangle_down' => ['unicode' => '1F53B', 'shortname' => ':small_red_triangle_down:', 'aliases' => '', 'keywords' => 'down-pointing red triangle shape'], + 'arrow_upper_left' => ['unicode' => '2196', 'shortname' => ':arrow_upper_left:', 'aliases' => '', 'keywords' => 'north west arrow blue-square'], + 'left_right_arrow' => ['unicode' => '2194', 'shortname' => ':left_right_arrow:', 'aliases' => '', 'keywords' => 'left right arrow shape'], + 'barber' => ['unicode' => '1F488', 'shortname' => ':barber:', 'aliases' => '', 'keywords' => 'barber pole hair salon style'], + 'large_orange_diamond' => ['unicode' => '1F536', 'shortname' => ':large_orange_diamond:', 'aliases' => '', 'keywords' => 'large orange diamond shape'], + 'hospital' => ['unicode' => '1F3E5', 'shortname' => ':hospital:', 'aliases' => '', 'keywords' => 'hospital building doctor health surgery'], + 'city_dusk' => ['unicode' => '1F306', 'shortname' => ':city_dusk:', 'aliases' => '', 'keywords' => 'cityscape at dusk photo city scape sunset dusk lights evening metropolitan night dark'], + 'scorpius' => ['unicode' => '264F', 'shortname' => ':scorpius:', 'aliases' => '', 'keywords' => 'scorpius scorpius scorpion scorpio astrology greek constellation stars zodiac sign sign zodiac horoscope'], + 'sailboat' => ['unicode' => '26F5', 'shortname' => ':sailboat:', 'aliases' => '', 'keywords' => 'sailboat ship transportation'], + 'tomato' => ['unicode' => '1F345', 'shortname' => ':tomato:', 'aliases' => '', 'keywords' => 'tomato food fruit nature vegetable tomato fruit sauce italian'], + 'sparkle' => ['unicode' => '2747', 'shortname' => ':sparkle:', 'aliases' => '', 'keywords' => 'sparkle green-square stars'], + 'closed_umbrella' => ['unicode' => '1F302', 'shortname' => ':closed_umbrella:', 'aliases' => '', 'keywords' => 'closed umbrella drizzle rain weather umbrella closed rain moisture protection sun ultraviolet uv'], + 'heavy_plus_sign' => ['unicode' => '2795', 'shortname' => ':heavy_plus_sign:', 'aliases' => '', 'keywords' => 'heavy plus sign calculation math'], + 'mega' => ['unicode' => '1F4E3', 'shortname' => ':mega:', 'aliases' => '', 'keywords' => 'cheering megaphone sound speaker volume'], + 'large_blue_diamond' => ['unicode' => '1F537', 'shortname' => ':large_blue_diamond:', 'aliases' => '', 'keywords' => 'large blue diamond shape'], + 'package' => ['unicode' => '1F4E6', 'shortname' => ':package:', 'aliases' => '', 'keywords' => 'package gift mail'], + 'heavy_minus_sign' => ['unicode' => '2796', 'shortname' => ':heavy_minus_sign:', 'aliases' => '', 'keywords' => 'heavy minus sign calculation math'], + 'city_sunset' => ['unicode' => '1F307', 'shortname' => ':city_sunset:', 'aliases' => ':city_sunrise:', 'keywords' => 'sunset over buildings photo city scape sunrise dawn light morning metropolitan rise sun'], + 'soon' => ['unicode' => '1F51C', 'shortname' => ':soon:', 'aliases' => '', 'keywords' => 'soon with rightwards arrow above arrow words'], + 'congratulations' => ['unicode' => '3297', 'shortname' => ':congratulations:', 'aliases' => '', 'keywords' => 'circled ideograph congratulation chinese japanese kanji'], + 'secret' => ['unicode' => '3299', 'shortname' => ':secret:', 'aliases' => '', 'keywords' => 'circled ideograph secret privacy'], + 'no_entry' => ['unicode' => '26D4', 'shortname' => ':no_entry:', 'aliases' => '', 'keywords' => 'no entry bad denied limit privacy security stop'], + 'aries' => ['unicode' => '2648', 'shortname' => ':aries:', 'aliases' => '', 'keywords' => 'aries aries ram astrology greek constellation stars zodiac sign purple-square sign zodiac horoscope'], + 'purse' => ['unicode' => '1F45B', 'shortname' => ':purse:', 'aliases' => '', 'keywords' => 'purse accessories fashion money purse clutch bag handbag coin bag accessory money ladies shopping'], + 'dragon_face' => ['unicode' => '1F432', 'shortname' => ':dragon_face:', 'aliases' => '', 'keywords' => 'dragon face animal chinese green myth nature dragon head fire legendary myth'], + 'leo' => ['unicode' => '264C', 'shortname' => ':leo:', 'aliases' => '', 'keywords' => 'leo leo lion astrology greek constellation stars zodiac sign purple-square sign zodiac horoscope'], + 'ship' => ['unicode' => '1F6A2', 'shortname' => ':ship:', 'aliases' => '', 'keywords' => 'ship titanic transportation ferry ship boat'], + 'white_flower' => ['unicode' => '1F4AE', 'shortname' => ':white_flower:', 'aliases' => '', 'keywords' => 'white flower japanese white flower teacher school grade score brilliance intelligence homework student assignment praise'], + 'id' => ['unicode' => '1F1EE-1F1E9', 'shortname' => ':id:', 'aliases' => '', 'keywords' => 'indonesia country nation'], + 'wedding' => ['unicode' => '1F492', 'shortname' => ':wedding:', 'aliases' => '', 'keywords' => 'wedding affection bride couple groom like love marriage'], + 'boot' => ['unicode' => '1F462', 'shortname' => ':boot:', 'aliases' => '', 'keywords' => 'womans boots fashion shoes'], + 'radio_button' => ['unicode' => '1F518', 'shortname' => ':radio_button:', 'aliases' => '', 'keywords' => 'radio button input'], + 'notebook' => ['unicode' => '1F4D3', 'shortname' => ':notebook:', 'aliases' => '', 'keywords' => 'notebook notes paper record stationery'], + 'gemini' => ['unicode' => '264A', 'shortname' => ':gemini:', 'aliases' => '', 'keywords' => 'gemini gemini twins astrology greek constellation stars zodiac sign sign zodiac horoscope'], + 'bell' => ['unicode' => '1F514', 'shortname' => ':bell:', 'aliases' => '', 'keywords' => 'bell chime christmas notification sound xmas'], + 'boar' => ['unicode' => '1F417', 'shortname' => ':boar:', 'aliases' => '', 'keywords' => 'boar animal nature'], + 'ambulance' => ['unicode' => '1F691', 'shortname' => ':ambulance:', 'aliases' => '', 'keywords' => 'ambulance 911 health ambulance emergency medical help assistance'], + 'mount_fuji' => ['unicode' => '1F5FB', 'shortname' => ':mount_fuji:', 'aliases' => '', 'keywords' => 'mount fuji japan mountain nature photo'], + 'sandal' => ['unicode' => '1F461', 'shortname' => ':sandal:', 'aliases' => '', 'keywords' => 'womans sandal fashion shoes'], + 'round_pushpin' => ['unicode' => '1F4CD', 'shortname' => ':round_pushpin:', 'aliases' => '', 'keywords' => 'round pushpin stationery'], + 'keycap_ten' => ['unicode' => '1F51F', 'shortname' => ':keycap_ten:', 'aliases' => '', 'keywords' => 'keycap ten 10 blue-square numbers'], + 'ledger' => ['unicode' => '1F4D2', 'shortname' => ':ledger:', 'aliases' => '', 'keywords' => 'ledger notes paper'], + 'womans_hat' => ['unicode' => '1F452', 'shortname' => ':womans_hat:', 'aliases' => '', 'keywords' => 'womans hat accessories fashion female'], + 'envelope_with_arrow' => ['unicode' => '1F4E9', 'shortname' => ':envelope_with_arrow:', 'aliases' => '', 'keywords' => 'envelope with downwards arrow above email'], + 'black_joker' => ['unicode' => '1F0CF', 'shortname' => ':black_joker:', 'aliases' => '', 'keywords' => 'playing card black joker cards game poker'], + 'part_alternation_mark' => ['unicode' => '303D', 'shortname' => ':part_alternation_mark:', 'aliases' => '', 'keywords' => 'part alternation mark graph sing song vocal music karaoke cue letter m japanese'], + 'o2' => ['unicode' => '1F17E', 'shortname' => ':o2:', 'aliases' => '', 'keywords' => 'negative squared latin capital letter o alphabet letter red-square'], + 'office' => ['unicode' => '1F3E2', 'shortname' => ':office:', 'aliases' => '', 'keywords' => 'office building building bureau work'], + 'volcano' => ['unicode' => '1F30B', 'shortname' => ':volcano:', 'aliases' => '', 'keywords' => 'volcano nature photo volcano lava magma hot explode'], + 'aquarius' => ['unicode' => '2652', 'shortname' => ':aquarius:', 'aliases' => '', 'keywords' => 'aquarius aquarius water bearer astrology greek constellation stars zodiac sign purple-square sign zodiac horoscope'], + 'taurus' => ['unicode' => '2649', 'shortname' => ':taurus:', 'aliases' => '', 'keywords' => 'taurus purple-square sign taurus bull astrology greek constellation stars zodiac sign zodiac horoscope'], + 'pushpin' => ['unicode' => '1F4CC', 'shortname' => ':pushpin:', 'aliases' => '', 'keywords' => 'pushpin stationery'], + 'violin' => ['unicode' => '1F3BB', 'shortname' => ':violin:', 'aliases' => '', 'keywords' => 'violin instrument music violin fiddle music instrument'], + 'virgo' => ['unicode' => '264D', 'shortname' => ':virgo:', 'aliases' => '', 'keywords' => 'virgo sign virgo maiden astrology greek constellation stars zodiac sign zodiac horoscope'], + 'ski' => ['unicode' => '1F3BF', 'shortname' => ':ski:', 'aliases' => '', 'keywords' => 'ski and ski boot cold sports winter ski downhill cross-country poles snow winter mountain alpine powder slalom freestyle'], + 'taxi' => ['unicode' => '1F695', 'shortname' => ':taxi:', 'aliases' => '', 'keywords' => 'taxi cars transportation uber vehicle taxi car automobile city transport service'], + 'stars' => ['unicode' => '1F320', 'shortname' => ':stars:', 'aliases' => '', 'keywords' => 'shooting star night photo shooting shoot star sky night comet meteoroid'], + 'speedboat' => ['unicode' => '1F6A4', 'shortname' => ':speedboat:', 'aliases' => '', 'keywords' => 'speedboat ship transportation vehicle motor speed ski power boat'], + 'hourglass_flowing_sand' => ['unicode' => '23F3', 'shortname' => ':hourglass_flowing_sand:', 'aliases' => '', 'keywords' => 'hourglass with flowing sand countdown oldschool time'], + 'ferris_wheel' => ['unicode' => '1F3A1', 'shortname' => ':ferris_wheel:', 'aliases' => '', 'keywords' => 'ferris wheel carnival londoneye photo farris wheel amusement park fair ride entertainment'], + 'tent' => ['unicode' => '26FA', 'shortname' => ':tent:', 'aliases' => '', 'keywords' => 'tent camp outdoors photo'], + 'love_hotel' => ['unicode' => '1F3E9', 'shortname' => ':love_hotel:', 'aliases' => '', 'keywords' => 'love hotel affection dating like love hotel love sex romance leisure adultery prostitution hospital birth happy'], + 'church' => ['unicode' => '26EA', 'shortname' => ':church:', 'aliases' => '', 'keywords' => 'church building christ religion'], + 'briefcase' => ['unicode' => '1F4BC', 'shortname' => ':briefcase:', 'aliases' => '', 'keywords' => 'briefcase business documents work'], + 'womans_clothes' => ['unicode' => '1F45A', 'shortname' => ':womans_clothes:', 'aliases' => '', 'keywords' => 'womans clothes fashion woman clothing clothes blouse shirt wardrobe breasts cleavage shopping shop dressing dressed'], + 'dvd' => ['unicode' => '1F4C0', 'shortname' => ':dvd:', 'aliases' => '', 'keywords' => 'dvd cd disc disk'], + 'libra' => ['unicode' => '264E', 'shortname' => ':libra:', 'aliases' => '', 'keywords' => 'libra libra scales astrology greek constellation stars zodiac sign purple-square sign zodiac horoscope'], + 'sagittarius' => ['unicode' => '2650', 'shortname' => ':sagittarius:', 'aliases' => '', 'keywords' => 'sagittarius sagittarius centaur archer astrology greek constellation stars zodiac sign sign zodiac horoscope'], + 'oden' => ['unicode' => '1F362', 'shortname' => ':oden:', 'aliases' => '', 'keywords' => 'oden food japanese oden seafood casserole stew'], + 'game_die' => ['unicode' => '1F3B2', 'shortname' => ':game_die:', 'aliases' => '', 'keywords' => 'game die dice game die dice craps gamble play'], + 'grey_question' => ['unicode' => '2754', 'shortname' => ':grey_question:', 'aliases' => '', 'keywords' => 'white question mark ornament doubts'], + 'fast_forward' => ['unicode' => '23E9', 'shortname' => ':fast_forward:', 'aliases' => '', 'keywords' => 'black right-pointing double triangle blue-square'], + 'flashlight' => ['unicode' => '1F526', 'shortname' => ':flashlight:', 'aliases' => '', 'keywords' => 'electric torch dark'], + 'triangular_flag_on_post' => ['unicode' => '1F6A9', 'shortname' => ':triangular_flag_on_post:', 'aliases' => '', 'keywords' => 'triangular flag on post triangle triangular flag golf post flagpole'], + 'tanabata_tree' => ['unicode' => '1F38B', 'shortname' => ':tanabata_tree:', 'aliases' => '', 'keywords' => 'tanabata tree nature plant tanabata tree festival star wish holiday'], + 'dango' => ['unicode' => '1F361', 'shortname' => ':dango:', 'aliases' => '', 'keywords' => 'dango food dango japanese dumpling mochi balls skewer'], + 'signal_strength' => ['unicode' => '1F4F6', 'shortname' => ':signal_strength:', 'aliases' => '', 'keywords' => 'antenna with bars blue-square'], + 'video_camera' => ['unicode' => '1F4F9', 'shortname' => ':video_camera:', 'aliases' => '', 'keywords' => 'video camera film record'], + 'negative_squared_cross_mark' => ['unicode' => '274E', 'shortname' => ':negative_squared_cross_mark:', 'aliases' => '', 'keywords' => 'negative squared cross mark deny green-square no x'], + 'black_medium_square' => ['unicode' => '25FC', 'shortname' => ':black_medium_square:', 'aliases' => '', 'keywords' => 'black medium square shape'], + 'yen' => ['unicode' => '1F4B4', 'shortname' => ':yen:', 'aliases' => '', 'keywords' => 'banknote with yen sign currency dollar japanese money yen japan japanese banknote money currency paper cash bill'], + 'blowfish' => ['unicode' => '1F421', 'shortname' => ':blowfish:', 'aliases' => '', 'keywords' => 'blowfish food nature ocean sea blowfish pufferfish puffer ballonfish toadfish fugu fish sushi'], + 'white_large_square' => ['unicode' => '2B1C', 'shortname' => ':white_large_square:', 'aliases' => '', 'keywords' => 'white large square shape'], + 'beginner' => ['unicode' => '1F530', 'shortname' => ':beginner:', 'aliases' => '', 'keywords' => 'japanese symbol for beginner badge shield'], + 'school' => ['unicode' => '1F3EB', 'shortname' => ':school:', 'aliases' => '', 'keywords' => 'school building school university elementary middle high college teach education'], + 'new' => ['unicode' => '1F195', 'shortname' => ':new:', 'aliases' => '', 'keywords' => 'squared new blue-square'], + 'clock1' => ['unicode' => '1F550', 'shortname' => ':clock1:', 'aliases' => '', 'keywords' => 'clock face one oclock clock time'], + 'womens' => ['unicode' => '1F6BA', 'shortname' => ':womens:', 'aliases' => '', 'keywords' => 'womens symbol purple-square woman bathroom restroom sign girl female avatar'], + 'running_shirt_with_sash' => ['unicode' => '1F3BD', 'shortname' => ':running_shirt_with_sash:', 'aliases' => '', 'keywords' => 'running shirt with sash pageant play running run shirt cloths compete sports'], + 'radio' => ['unicode' => '1F4FB', 'shortname' => ':radio:', 'aliases' => '', 'keywords' => 'radio communication music podcast program'], + 'on' => ['unicode' => '1F51B', 'shortname' => ':on:', 'aliases' => '', 'keywords' => 'on with exclamation mark with left right arrow abo arrow words'], + 'hourglass' => ['unicode' => '231B', 'shortname' => ':hourglass:', 'aliases' => '', 'keywords' => 'hourglass clock oldschool time'], + 'pisces' => ['unicode' => '2653', 'shortname' => ':pisces:', 'aliases' => '', 'keywords' => 'pisces pisces fish astrology greek constellation stars zodiac sign purple-square sign zodiac horoscope'], + 'nut_and_bolt' => ['unicode' => '1F529', 'shortname' => ':nut_and_bolt:', 'aliases' => '', 'keywords' => 'nut and bolt handy tools'], + 'free' => ['unicode' => '1F193', 'shortname' => ':free:', 'aliases' => '', 'keywords' => 'squared free blue-square words'], + 'bridge_at_night' => ['unicode' => '1F309', 'shortname' => ':bridge_at_night:', 'aliases' => '', 'keywords' => 'bridge at night photo sanfrancisco bridge night water road evening suspension golden gate'], + 'saxophone' => ['unicode' => '1F3B7', 'shortname' => ':saxophone:', 'aliases' => '', 'keywords' => 'saxophone instrument music saxophone sax music instrument woodwind'], + 'white_square_button' => ['unicode' => '1F533', 'shortname' => ':white_square_button:', 'aliases' => '', 'keywords' => 'white square button shape'], + 'mobile_phone_off' => ['unicode' => '1F4F4', 'shortname' => ':mobile_phone_off:', 'aliases' => '', 'keywords' => 'mobile phone off mute'], + 'closed_book' => ['unicode' => '1F4D5', 'shortname' => ':closed_book:', 'aliases' => '', 'keywords' => 'closed book knowledge library read'], + 'european_castle' => ['unicode' => '1F3F0', 'shortname' => ':european_castle:', 'aliases' => '', 'keywords' => 'european castle building history royalty castle european residence royalty disneyland disney fort fortified moat tower princess prince lord king queen fortress nobel stronghold'], + 'clock12' => ['unicode' => '1F55B', 'shortname' => ':clock12:', 'aliases' => '', 'keywords' => 'clock face twelve oclock clock time'], + 'white_medium_square' => ['unicode' => '25FB', 'shortname' => ':white_medium_square:', 'aliases' => '', 'keywords' => 'white medium square shape'], + 'foggy' => ['unicode' => '1F301', 'shortname' => ':foggy:', 'aliases' => '', 'keywords' => 'foggy mountain photo bridge weather fog foggy'], + 'minidisc' => ['unicode' => '1F4BD', 'shortname' => ':minidisc:', 'aliases' => '', 'keywords' => 'minidisc data disc disk record technology'], + 'fire_engine' => ['unicode' => '1F692', 'shortname' => ':fire_engine:', 'aliases' => '', 'keywords' => 'fire engine cars transportation vehicle fire fighter engine truck emergency medical'], + 'clock2' => ['unicode' => '1F551', 'shortname' => ':clock2:', 'aliases' => '', 'keywords' => 'clock face two oclock clock time'], + 'rice_ball' => ['unicode' => '1F359', 'shortname' => ':rice_ball:', 'aliases' => '', 'keywords' => 'rice ball food japanese rice ball white nori seaweed japanese'], + 'wind_chime' => ['unicode' => '1F390', 'shortname' => ':wind_chime:', 'aliases' => '', 'keywords' => 'wind chime ding nature wind chime bell fūrin instrument music spirits soothing protective spiritual sound'], + 'capricorn' => ['unicode' => '2651', 'shortname' => ':capricorn:', 'aliases' => '', 'keywords' => 'capricorn capricorn sea-goat goat-horned astrology greek constellation stars zodiac sign sign zodiac horoscope'], + 'vs' => ['unicode' => '1F19A', 'shortname' => ':vs:', 'aliases' => '', 'keywords' => 'squared vs orange-square words'], + 'melon' => ['unicode' => '1F348', 'shortname' => ':melon:', 'aliases' => '', 'keywords' => 'melon food fruit nature melon cantaloupe honeydew'], + 'trumpet' => ['unicode' => '1F3BA', 'shortname' => ':trumpet:', 'aliases' => '', 'keywords' => 'trumpet brass music trumpet brass music instrument'], + 'school_satchel' => ['unicode' => '1F392', 'shortname' => ':school_satchel:', 'aliases' => '', 'keywords' => 'school satchel bag education student school satchel backpack bag packing pack hike education adventure travel sightsee'], + 'tokyo_tower' => ['unicode' => '1F5FC', 'shortname' => ':tokyo_tower:', 'aliases' => '', 'keywords' => 'tokyo tower japan photo'], + 'station' => ['unicode' => '1F689', 'shortname' => ':station:', 'aliases' => '', 'keywords' => 'station public transportation vehicle station train subway'], + 'end' => ['unicode' => '1F51A', 'shortname' => ':end:', 'aliases' => '', 'keywords' => 'end with leftwards arrow above arrow words'], + 'bamboo' => ['unicode' => '1F38D', 'shortname' => ':bamboo:', 'aliases' => '', 'keywords' => 'pine decoration nature plant vegetable pine bamboo decoration new years spirits harvest prosperity longevity fortune luck welcome farming agriculture'], + 'truck' => ['unicode' => '1F69A', 'shortname' => ':truck:', 'aliases' => '', 'keywords' => 'delivery truck cars transportation truck delivery package'], + 'clock3' => ['unicode' => '1F552', 'shortname' => ':clock3:', 'aliases' => '', 'keywords' => 'clock face three oclock clock time'], + 'six_pointed_star' => ['unicode' => '1F52F', 'shortname' => ':six_pointed_star:', 'aliases' => '', 'keywords' => 'six pointed star with middle dot purple-square'], + 'mag_right' => ['unicode' => '1F50E', 'shortname' => ':mag_right:', 'aliases' => '', 'keywords' => 'right-pointing magnifying glass search zoom detective investigator detail details'], + 'kimono' => ['unicode' => '1F458', 'shortname' => ':kimono:', 'aliases' => '', 'keywords' => 'kimono dress fashion female japanese women'], + 'railway_car' => ['unicode' => '1F683', 'shortname' => ':railway_car:', 'aliases' => '', 'keywords' => 'railway car transportation vehicle railway rail car coach train'], + 'crossed_flags' => ['unicode' => '1F38C', 'shortname' => ':crossed_flags:', 'aliases' => '', 'keywords' => 'crossed flags japan'], + 'sweet_potato' => ['unicode' => '1F360', 'shortname' => ':sweet_potato:', 'aliases' => '', 'keywords' => 'roasted sweet potato food nature sweet potato potassium roasted roast'], + 'white_small_square' => ['unicode' => '25AB', 'shortname' => ':white_small_square:', 'aliases' => '', 'keywords' => 'white small square shape'], + 'date' => ['unicode' => '1F4C5', 'shortname' => ':date:', 'aliases' => '', 'keywords' => 'calendar calendar schedule'], + 'newspaper' => ['unicode' => '1F4F0', 'shortname' => ':newspaper:', 'aliases' => '', 'keywords' => 'newspaper headline press'], + 'no_smoking' => ['unicode' => '1F6AD', 'shortname' => ':no_smoking:', 'aliases' => '', 'keywords' => 'no smoking symbol cigarette no smoking cigarette smoke cancer lungs inhale tar nicotine'], + 'scroll' => ['unicode' => '1F4DC', 'shortname' => ':scroll:', 'aliases' => '', 'keywords' => 'scroll documents'], + 'flags' => ['unicode' => '1F38F', 'shortname' => ':flags:', 'aliases' => '', 'keywords' => 'carp streamer banner carp fish japanese koinobori children kids boys celebration happiness carp streamers japanese holiday flags'], + 'mag' => ['unicode' => '1F50D', 'shortname' => ':mag:', 'aliases' => '', 'keywords' => 'left-pointing magnifying glass search zoom detective investigator detail details'], + 'wheelchair' => ['unicode' => '267F', 'shortname' => ':wheelchair:', 'aliases' => '', 'keywords' => 'wheelchair symbol blue-square disabled'], + 'sake' => ['unicode' => '1F376', 'shortname' => ':sake:', 'aliases' => '', 'keywords' => 'sake bottle and cup beverage drink drunk wine sake wine rice ferment alcohol japanese drink'], + 'arrow_up_down' => ['unicode' => '2195', 'shortname' => ':arrow_up_down:', 'aliases' => '', 'keywords' => 'up down arrow blue-square'], + 'black_large_square' => ['unicode' => '2B1B', 'shortname' => ':black_large_square:', 'aliases' => '', 'keywords' => 'black large square shape'], + 'wrench' => ['unicode' => '1F527', 'shortname' => ':wrench:', 'aliases' => '', 'keywords' => 'wrench diy ikea tools'], + 'construction' => ['unicode' => '1F6A7', 'shortname' => ':construction:', 'aliases' => '', 'keywords' => 'construction sign caution progress wip'], + 'calendar' => ['unicode' => '1F4C6', 'shortname' => ':calendar:', 'aliases' => '', 'keywords' => 'tear-off calendar schedule'], + 'hotel' => ['unicode' => '1F3E8', 'shortname' => ':hotel:', 'aliases' => '', 'keywords' => 'hotel accomodation building checkin whotel hotel motel holiday inn hospital'], + 'satellite' => ['unicode' => '1F4E1', 'shortname' => ':satellite:', 'aliases' => '', 'keywords' => 'satellite antenna communication'], + 'rewind' => ['unicode' => '23EA', 'shortname' => ':rewind:', 'aliases' => '', 'keywords' => 'black left-pointing double triangle blue-square play'], + 'clock4' => ['unicode' => '1F553', 'shortname' => ':clock4:', 'aliases' => '', 'keywords' => 'clock face four oclock clock time'], + 'circus_tent' => ['unicode' => '1F3AA', 'shortname' => ':circus_tent:', 'aliases' => '', 'keywords' => 'circus tent carnival festival party circus tent event carnival big top canvas'], + 'link' => ['unicode' => '1F517', 'shortname' => ':link:', 'aliases' => '', 'keywords' => 'link symbol rings url'], + 'bullettrain_side' => ['unicode' => '1F684', 'shortname' => ':bullettrain_side:', 'aliases' => '', 'keywords' => 'high-speed train transportation vehicle train bullet rail'], + 'mens' => ['unicode' => '1F6B9', 'shortname' => ':mens:', 'aliases' => '', 'keywords' => 'mens symbol restroom toilet wc men bathroom restroom sign boy male avatar'], + 'carousel_horse' => ['unicode' => '1F3A0', 'shortname' => ':carousel_horse:', 'aliases' => '', 'keywords' => 'carousel horse carnival horse photo carousel horse amusement park ride entertainment park fair'], + 'ideograph_advantage' => ['unicode' => '1F250', 'shortname' => ':ideograph_advantage:', 'aliases' => '', 'keywords' => 'circled ideograph advantage chinese get kanji obtain'], + 'atm' => ['unicode' => '1F3E7', 'shortname' => ':atm:', 'aliases' => '', 'keywords' => 'automated teller machine atm cash withdrawal money deposit financial bank adam payday bank blue-square cash money payment'], + 'vhs' => ['unicode' => '1F4FC', 'shortname' => ':vhs:', 'aliases' => '', 'keywords' => 'videocassette oldschool record video'], + 'arrow_double_down' => ['unicode' => '23EC', 'shortname' => ':arrow_double_down:', 'aliases' => '', 'keywords' => 'black down-pointing double triangle arrow blue-square'], + 'clock9' => ['unicode' => '1F558', 'shortname' => ':clock9:', 'aliases' => '', 'keywords' => 'clock face nine oclock clock time'], + 'blue_book' => ['unicode' => '1F4D8', 'shortname' => ':blue_book:', 'aliases' => '', 'keywords' => 'blue book knowledge library read'], + 'arrow_heading_up' => ['unicode' => '2934', 'shortname' => ':arrow_heading_up:', 'aliases' => '', 'keywords' => 'arrow pointing rightwards then curving upwards arrow blue-square'], + 'metro' => ['unicode' => '1F687', 'shortname' => ':metro:', 'aliases' => '', 'keywords' => 'metro blue-square mrt transportation tube underground metro subway underground train'], + 'clock5' => ['unicode' => '1F554', 'shortname' => ':clock5:', 'aliases' => '', 'keywords' => 'clock face five oclock clock time'], + 'wc' => ['unicode' => '1F6BE', 'shortname' => ':wc:', 'aliases' => '', 'keywords' => 'water closet blue-square restroom toilet water closet toilet bathroom throne porcelain waste flush plumbing'], + 'chart_with_upwards_trend' => ['unicode' => '1F4C8', 'shortname' => ':chart_with_upwards_trend:', 'aliases' => '', 'keywords' => 'chart with upwards trend graph'], + 'slot_machine' => ['unicode' => '1F3B0', 'shortname' => ':slot_machine:', 'aliases' => '', 'keywords' => 'slot machine bet gamble vegas slot machine gamble one-armed bandit slots luck'], + 'rice_cracker' => ['unicode' => '1F358', 'shortname' => ':rice_cracker:', 'aliases' => '', 'keywords' => 'rice cracker food japanese rice cracker seaweed food japanese'], + 'page_facing_up' => ['unicode' => '1F4C4', 'shortname' => ':page_facing_up:', 'aliases' => '', 'keywords' => 'page facing up documents'], + 'arrow_up_small' => ['unicode' => '1F53C', 'shortname' => ':arrow_up_small:', 'aliases' => '', 'keywords' => 'up-pointing small red triangle blue-square'], + 'green_book' => ['unicode' => '1F4D7', 'shortname' => ':green_book:', 'aliases' => '', 'keywords' => 'green book knowledge library read'], + 'white_medium_small_square' => ['unicode' => '25FD', 'shortname' => ':white_medium_small_square:', 'aliases' => '', 'keywords' => 'white medium small square shape'], + 'traffic_light' => ['unicode' => '1F6A5', 'shortname' => ':traffic_light:', 'aliases' => '', 'keywords' => 'horizontal traffic light traffic transportation traffic light stop go yield horizontal'], + 'ng' => ['unicode' => '1F1F3-1F1EC', 'shortname' => ':ng:', 'aliases' => '', 'keywords' => 'nigeria country nation'], + 'clock10' => ['unicode' => '1F559', 'shortname' => ':clock10:', 'aliases' => '', 'keywords' => 'clock face ten oclock clock time'], 1234 => ['unicode' => '1F522', 'shortname' => ':1234:', 'aliases' => '', 'keywords' => 'input symbol for numbers blue-square numbers'], + 'convenience_store' => ['unicode' => '1F3EA', 'shortname' => ':convenience_store:', 'aliases' => '', 'keywords' => 'convenience store building'], + 'paperclip' => ['unicode' => '1F4CE', 'shortname' => ':paperclip:', 'aliases' => '', 'keywords' => 'paperclip documents stationery'], + 'name_badge' => ['unicode' => '1F4DB', 'shortname' => ':name_badge:', 'aliases' => '', 'keywords' => 'name badge fire forbid'], + 'clock8' => ['unicode' => '1F557', 'shortname' => ':clock8:', 'aliases' => '', 'keywords' => 'clock face eight oclock clock time'], + 'arrow_down_small' => ['unicode' => '1F53D', 'shortname' => ':arrow_down_small:', 'aliases' => '', 'keywords' => 'down-pointing small red triangle arrow blue-square'], + 'clipboard' => ['unicode' => '1F4CB', 'shortname' => ':clipboard:', 'aliases' => '', 'keywords' => 'clipboard documents stationery'], + 'page_with_curl' => ['unicode' => '1F4C3', 'shortname' => ':page_with_curl:', 'aliases' => '', 'keywords' => 'page with curl documents'], + 'bookmark_tabs' => ['unicode' => '1F4D1', 'shortname' => ':bookmark_tabs:', 'aliases' => '', 'keywords' => 'bookmark tabs favorite'], + 'bank' => ['unicode' => '1F3E6', 'shortname' => ':bank:', 'aliases' => '', 'keywords' => 'bank building'], + 'clock11' => ['unicode' => '1F55A', 'shortname' => ':clock11:', 'aliases' => '', 'keywords' => 'clock face eleven oclock clock time'], + 'e-mail' => ['unicode' => '1F4E7', 'shortname' => ':e-mail:', 'aliases' => ':email:', 'keywords' => 'e-mail symbol communication inbox'], + 'chart_with_downwards_trend' => ['unicode' => '1F4C9', 'shortname' => ':chart_with_downwards_trend:', 'aliases' => '', 'keywords' => 'chart with downwards trend graph'], + 'bullettrain_front' => ['unicode' => '1F685', 'shortname' => ':bullettrain_front:', 'aliases' => '', 'keywords' => 'high-speed train with bullet nose transportation train bullet rail'], + 'bar_chart' => ['unicode' => '1F4CA', 'shortname' => ':bar_chart:', 'aliases' => '', 'keywords' => 'bar chart graph presentation stats'], + 'notebook_with_decorative_cover' => ['unicode' => '1F4D4', 'shortname' => ':notebook_with_decorative_cover:', 'aliases' => '', 'keywords' => 'notebook with decorative cover classroom notes paper record'], + 'ticket' => ['unicode' => '1F3AB', 'shortname' => ':ticket:', 'aliases' => '', 'keywords' => 'ticket concert event pass ticket show entertainment stub admission proof purchase'], + 'information_source' => ['unicode' => '2139', 'shortname' => ':information_source:', 'aliases' => '', 'keywords' => 'information source alphabet blue-square letter'], + 'pouch' => ['unicode' => '1F45D', 'shortname' => ':pouch:', 'aliases' => '', 'keywords' => 'pouch accessories bag pouch bag cosmetic packing grandma makeup'], + 'chart' => ['unicode' => '1F4B9', 'shortname' => ':chart:', 'aliases' => '', 'keywords' => 'chart with upwards trend and yen sign graph green-square'], + 'japanese_castle' => ['unicode' => '1F3EF', 'shortname' => ':japanese_castle:', 'aliases' => '', 'keywords' => 'japanese castle building photo castle japanese residence royalty fort fortified fortress'], + 'cinema' => ['unicode' => '1F3A6', 'shortname' => ':cinema:', 'aliases' => '', 'keywords' => 'cinema blue-square film movie record cinema movie theater motion picture'], + 'clock7' => ['unicode' => '1F556', 'shortname' => ':clock7:', 'aliases' => '', 'keywords' => 'clock face seven oclock clock time'], + 'orange_book' => ['unicode' => '1F4D9', 'shortname' => ':orange_book:', 'aliases' => '', 'keywords' => 'orange book knowledge library read'], + 'restroom' => ['unicode' => '1F6BB', 'shortname' => ':restroom:', 'aliases' => '', 'keywords' => 'restroom blue-square woman man unisex bathroom restroom sign shared toilet'], + 'fountain' => ['unicode' => '26F2', 'shortname' => ':fountain:', 'aliases' => '', 'keywords' => 'fountain photo'], + 'clock6' => ['unicode' => '1F555', 'shortname' => ':clock6:', 'aliases' => '', 'keywords' => 'clock face six oclock clock time'], + 'vibration_mode' => ['unicode' => '1F4F3', 'shortname' => ':vibration_mode:', 'aliases' => '', 'keywords' => 'vibration mode orange-square phone'], + 'ab' => ['unicode' => '1F18E', 'shortname' => ':ab:', 'aliases' => '', 'keywords' => 'negative squared ab alphabet red-square'], + 'postbox' => ['unicode' => '1F4EE', 'shortname' => ':postbox:', 'aliases' => '', 'keywords' => 'postbox email envelope letter'], + 'rice_scene' => ['unicode' => '1F391', 'shortname' => ':rice_scene:', 'aliases' => '', 'keywords' => 'moon viewing ceremony photo moon viewing observing otsukimi tsukimi rice scene festival autumn'], + 'floppy_disk' => ['unicode' => '1F4BE', 'shortname' => ':floppy_disk:', 'aliases' => '', 'keywords' => 'floppy disk oldschool save technology floppy disk storage information computer drive megabyte'], + 'parking' => ['unicode' => '1F17F', 'shortname' => ':parking:', 'aliases' => '', 'keywords' => 'negative squared latin capital letter p alphabet blue-square cars letter'], + 'department_store' => ['unicode' => '1F3EC', 'shortname' => ':department_store:', 'aliases' => '', 'keywords' => 'department store building mall shopping department store retail sale merchandise'], + 'pager' => ['unicode' => '1F4DF', 'shortname' => ':pager:', 'aliases' => '', 'keywords' => 'pager bbcall oldschool'], + 'currency_exchange' => ['unicode' => '1F4B1', 'shortname' => ':currency_exchange:', 'aliases' => '', 'keywords' => 'currency exchange dollar money travel'], + 'bookmark' => ['unicode' => '1F516', 'shortname' => ':bookmark:', 'aliases' => '', 'keywords' => 'bookmark favorite'], + 'triangular_ruler' => ['unicode' => '1F4D0', 'shortname' => ':triangular_ruler:', 'aliases' => '', 'keywords' => 'triangular ruler architect math sketch stationery'], + 'straight_ruler' => ['unicode' => '1F4CF', 'shortname' => ':straight_ruler:', 'aliases' => '', 'keywords' => 'straight ruler stationery'], + 'japan' => ['unicode' => '1F5FE', 'shortname' => ':japan:', 'aliases' => '', 'keywords' => 'silhouette of japan nation'], + 'flower_playing_cards' => ['unicode' => '1F3B4', 'shortname' => ':flower_playing_cards:', 'aliases' => '', 'keywords' => 'flower playing cards playing card flower game august moon special'], + 'u5272' => ['unicode' => '1F239', 'shortname' => ':u5272:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-5272 chinese cut divide kanji pink'], + 'fax' => ['unicode' => '1F4E0', 'shortname' => ':fax:', 'aliases' => '', 'keywords' => 'fax machine communication technology'], + 'izakaya_lantern' => ['unicode' => '1F3EE', 'shortname' => ':izakaya_lantern:', 'aliases' => '', 'keywords' => 'izakaya lantern light izakaya lantern stay drink alcohol bar sake restaurant'], + 'incoming_envelope' => ['unicode' => '1F4E8', 'shortname' => ':incoming_envelope:', 'aliases' => '', 'keywords' => 'incoming envelope email inbox'], + 'mailbox' => ['unicode' => '1F4EB', 'shortname' => ':mailbox:', 'aliases' => '', 'keywords' => 'closed mailbox with raised flag communication email inbox'], + 'lock_with_ink_pen' => ['unicode' => '1F50F', 'shortname' => ':lock_with_ink_pen:', 'aliases' => '', 'keywords' => 'lock with ink pen secret security'], + 'inbox_tray' => ['unicode' => '1F4E5', 'shortname' => ':inbox_tray:', 'aliases' => '', 'keywords' => 'inbox tray documents email'], + 'post_office' => ['unicode' => '1F3E3', 'shortname' => ':post_office:', 'aliases' => '', 'keywords' => 'japanese post office building communication email'], + 'card_index' => ['unicode' => '1F4C7', 'shortname' => ':card_index:', 'aliases' => '', 'keywords' => 'card index business stationery'], + 'cl' => ['unicode' => '1F1E8-1F1F1', 'shortname' => ':cl:', 'aliases' => '', 'keywords' => 'chile country nation'], + 'open_file_folder' => ['unicode' => '1F4C2', 'shortname' => ':open_file_folder:', 'aliases' => '', 'keywords' => 'open file folder documents load'], + 'mahjong' => ['unicode' => '1F004', 'shortname' => ':mahjong:', 'aliases' => '', 'keywords' => 'mahjong tile red dragon chinese game kanji'], + 'ophiuchus' => ['unicode' => '26CE', 'shortname' => ':ophiuchus:', 'aliases' => '', 'keywords' => 'ophiuchus ophiuchus serpent snake astrology greek constellation stars zodiac purple-square sign horoscope'], + 'busstop' => ['unicode' => '1F68F', 'shortname' => ':busstop:', 'aliases' => '', 'keywords' => 'bus stop transportation bus stop city transport transportation'], + 'abc' => ['unicode' => '1F524', 'shortname' => ':abc:', 'aliases' => '', 'keywords' => 'input symbol for latin letters alphabet blue-square'], + 'u7a7a' => ['unicode' => '1F233', 'shortname' => ':u7a7a:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-7a7a chinese empty japanese kanji'], + 'capital_abcd' => ['unicode' => '1F520', 'shortname' => ':capital_abcd:', 'aliases' => '', 'keywords' => 'input symbol for latin capital letters alphabet blue-square words'], + 'factory' => ['unicode' => '1F3ED', 'shortname' => ':factory:', 'aliases' => '', 'keywords' => 'factory building'], + 'u7981' => ['unicode' => '1F232', 'shortname' => ':u7981:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-7981 chinese forbidden japanese kanji limit restricted'], + 'u6e80' => ['unicode' => '1F235', 'shortname' => ':u6e80:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-6e80 chinese full japanese kanji red-square'], + 'heavy_division_sign' => ['unicode' => '2797', 'shortname' => ':heavy_division_sign:', 'aliases' => '', 'keywords' => 'heavy division sign calculation divide math'], + 'file_folder' => ['unicode' => '1F4C1', 'shortname' => ':file_folder:', 'aliases' => '', 'keywords' => 'file folder documents'], + 'symbols' => ['unicode' => '1F523', 'shortname' => ':symbols:', 'aliases' => '', 'keywords' => 'input symbol for symbols blue-square'], + 'arrow_double_up' => ['unicode' => '23EB', 'shortname' => ':arrow_double_up:', 'aliases' => '', 'keywords' => 'black up-pointing double triangle arrow blue-square'], + 'u5408' => ['unicode' => '1F234', 'shortname' => ':u5408:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-5408 chinese japanese join kanji'], + 'u6307' => ['unicode' => '1F22F', 'shortname' => ':u6307:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-6307 chinese green-square kanji point'], + 'abcd' => ['unicode' => '1F521', 'shortname' => ':abcd:', 'aliases' => '', 'keywords' => 'input symbol for latin small letters alphabet blue-square'], + 'mailbox_closed' => ['unicode' => '1F4EA', 'shortname' => ':mailbox_closed:', 'aliases' => '', 'keywords' => 'closed mailbox with lowered flag communication email inbox'], + 'outbox_tray' => ['unicode' => '1F4E4', 'shortname' => ':outbox_tray:', 'aliases' => '', 'keywords' => 'outbox tray email inbox'], + 'sa' => ['unicode' => '1F1F8-1F1E6', 'shortname' => ':sa:', 'aliases' => '', 'keywords' => 'saudi arabia country nation al arabiyah as suudiyah'], + 'u55b6' => ['unicode' => '1F23A', 'shortname' => ':u55b6:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-55b6 japanese opening hours'], + 'u6709' => ['unicode' => '1F236', 'shortname' => ':u6709:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-6709 chinese have kanji orange-square'], + 'accept' => ['unicode' => '1F251', 'shortname' => ':accept:', 'aliases' => '', 'keywords' => 'circled ideograph accept agree chinese good kanji ok yes'], + 'u7121' => ['unicode' => '1F21A', 'shortname' => ':u7121:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-7121 chinese japanese kanji no nothing orange-square'], + 'koko' => ['unicode' => '1F201', 'shortname' => ':koko:', 'aliases' => '', 'keywords' => 'squared katakana koko blue-square destination here japanese katakana'], + 'u7533' => ['unicode' => '1F238', 'shortname' => ':u7533:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-7533 chinese japanese kanji'], + 'u6708' => ['unicode' => '1F237', 'shortname' => ':u6708:', 'aliases' => '', 'keywords' => 'squared cjk unified ideograph-6708 chinese japanese kanji moon orange-square'], + 'hash' => ['unicode' => '0023-20E3', 'shortname' => ':hash:', 'aliases' => '', 'keywords' => 'number sign symbol'], + 'zero' => ['unicode' => '0030-20E3', 'shortname' => ':zero:', 'aliases' => '', 'keywords' => 'digit zero blue-square null numbers'], + 'one' => ['unicode' => '0031-20E3', 'shortname' => ':one:', 'aliases' => '', 'keywords' => 'digit one 1 blue-square numbers'], + 'two' => ['unicode' => '0032-20E3', 'shortname' => ':two:', 'aliases' => '', 'keywords' => 'digit two 2 blue-square numbers prime'], + 'three' => ['unicode' => '0033-20E3', 'shortname' => ':three:', 'aliases' => '', 'keywords' => 'digit three 3 blue-square numbers prime'], + 'four' => ['unicode' => '0034-20E3', 'shortname' => ':four:', 'aliases' => '', 'keywords' => 'digit four 4 blue-square numbers'], + 'five' => ['unicode' => '0035-20E3', 'shortname' => ':five:', 'aliases' => '', 'keywords' => 'digit five blue-square numbers prime'], + 'speaker' => ['unicode' => '1F508', 'shortname' => ':speaker:', 'aliases' => '', 'keywords' => 'speaker sound listen hear noise'], + 'six' => ['unicode' => '0036-20E3', 'shortname' => ':six:', 'aliases' => '', 'keywords' => 'digit six 6 blue-square numbers'], + 'train' => ['unicode' => '1F68B', 'shortname' => ':train:', 'aliases' => '', 'keywords' => 'Tram Car tram rail'], + 'loop' => ['unicode' => '27BF', 'shortname' => ':loop:', 'aliases' => '', 'keywords' => 'double curly loop curly'], + 'seven' => ['unicode' => '0037-20E3', 'shortname' => ':seven:', 'aliases' => '', 'keywords' => 'digit seven 7 blue-square numbers prime'], + 'af' => ['unicode' => '1F1E6-1F1EB', 'shortname' => ':af:', 'aliases' => '', 'keywords' => 'afghanistan country nation afghanestan'], + 'al' => ['unicode' => '1F1E6-1F1F1', 'shortname' => ':al:', 'aliases' => '', 'keywords' => 'albania country nation shqiperia'], + 'eight' => ['unicode' => '0038-20E3', 'shortname' => ':eight:', 'aliases' => '', 'keywords' => 'digit eight 8 blue-square numbers'], + 'dz' => ['unicode' => '1F1E9-1F1FF', 'shortname' => ':dz:', 'aliases' => '', 'keywords' => 'algeria country nation al jaza\'ir al jazair'], + 'ad' => ['unicode' => '1F1E6-1F1E9', 'shortname' => ':ad:', 'aliases' => '', 'keywords' => 'andorra country nation'], + 'nine' => ['unicode' => '0039-20E3', 'shortname' => ':nine:', 'aliases' => '', 'keywords' => 'digit nine 9 blue-square numbers'], + 'ao' => ['unicode' => '1F1E6-1F1F4', 'shortname' => ':ao:', 'aliases' => '', 'keywords' => 'angola country nation'], + 'ag' => ['unicode' => '1F1E6-1F1EC', 'shortname' => ':ag:', 'aliases' => '', 'keywords' => 'antigua and barbuda country nation'], + 'ar' => ['unicode' => '1F1E6-1F1F7', 'shortname' => ':ar:', 'aliases' => '', 'keywords' => 'argentina country nation'], + 'am' => ['unicode' => '1F1E6-1F1F2', 'shortname' => ':am:', 'aliases' => '', 'keywords' => 'armenia country nation hayastan'], + 'au' => ['unicode' => '1F1E6-1F1FA', 'shortname' => ':au:', 'aliases' => '', 'keywords' => 'australia country nation'], + 'at' => ['unicode' => '1F1E6-1F1F9', 'shortname' => ':at:', 'aliases' => '', 'keywords' => 'austria country nation österreich osterreich'], + 'az' => ['unicode' => '1F1E6-1F1FF', 'shortname' => ':az:', 'aliases' => '', 'keywords' => 'azerbaijan country nation azarbaycan'], + 'bs' => ['unicode' => '1F1E7-1F1F8', 'shortname' => ':bs:', 'aliases' => '', 'keywords' => 'the bahamas country nation'], + 'bh' => ['unicode' => '1F1E7-1F1ED', 'shortname' => ':bh:', 'aliases' => '', 'keywords' => 'bahrain country nation al bahrayn'], + 'bd' => ['unicode' => '1F1E7-1F1E9', 'shortname' => ':bd:', 'aliases' => '', 'keywords' => 'bangladesh country nation'], + 'bb' => ['unicode' => '1F1E7-1F1E7', 'shortname' => ':bb:', 'aliases' => '', 'keywords' => 'barbados country nation'], + 'by' => ['unicode' => '1F1E7-1F1FE', 'shortname' => ':by:', 'aliases' => '', 'keywords' => 'belarus country nation byelarus'], + 'be' => ['unicode' => '1F1E7-1F1EA', 'shortname' => ':be:', 'aliases' => '', 'keywords' => 'belgium country nation belgique belgie'], + 'bz' => ['unicode' => '1F1E7-1F1FF', 'shortname' => ':bz:', 'aliases' => '', 'keywords' => 'belize country nation'], + 'bj' => ['unicode' => '1F1E7-1F1EF', 'shortname' => ':bj:', 'aliases' => '', 'keywords' => 'benin country nation'], + 'bt' => ['unicode' => '1F1E7-1F1F9', 'shortname' => ':bt:', 'aliases' => '', 'keywords' => 'bhutan country nation'], + 'bo' => ['unicode' => '1F1E7-1F1F4', 'shortname' => ':bo:', 'aliases' => '', 'keywords' => 'bolivia country nation'], + 'ba' => ['unicode' => '1F1E7-1F1E6', 'shortname' => ':ba:', 'aliases' => '', 'keywords' => 'bosnia and herzegovina country nation bosna i hercegovina'], + 'bw' => ['unicode' => '1F1E7-1F1FC', 'shortname' => ':bw:', 'aliases' => '', 'keywords' => 'botswana country nation'], + 'br' => ['unicode' => '1F1E7-1F1F7', 'shortname' => ':br:', 'aliases' => '', 'keywords' => 'brazil country nation brasil'], + 'bn' => ['unicode' => '1F1E7-1F1F3', 'shortname' => ':bn:', 'aliases' => '', 'keywords' => 'brunei country nation'], + 'bg' => ['unicode' => '1F1E7-1F1EC', 'shortname' => ':bg:', 'aliases' => '', 'keywords' => 'bulgaria country nation'], + 'bf' => ['unicode' => '1F1E7-1F1EB', 'shortname' => ':bf:', 'aliases' => '', 'keywords' => 'burkina faso country nation'], + 'bi' => ['unicode' => '1F1E7-1F1EE', 'shortname' => ':bi:', 'aliases' => '', 'keywords' => 'burundi country nation'], + 'kh' => ['unicode' => '1F1F0-1F1ED', 'shortname' => ':kh:', 'aliases' => '', 'keywords' => 'cambodia country nation kampuchea'], + 'cm' => ['unicode' => '1F1E8-1F1F2', 'shortname' => ':cm:', 'aliases' => '', 'keywords' => 'cameroon country nation'], + 'ca' => ['unicode' => '1F1E8-1F1E6', 'shortname' => ':ca:', 'aliases' => '', 'keywords' => 'canada country nation'], + 'cv' => ['unicode' => '1F1E8-1F1FB', 'shortname' => ':cv:', 'aliases' => '', 'keywords' => 'cape verde country nation cabo verde'], + 'cf' => ['unicode' => '1F1E8-1F1EB', 'shortname' => ':cf:', 'aliases' => '', 'keywords' => 'central african republic country nation'], + 'td' => ['unicode' => '1F1F9-1F1E9', 'shortname' => ':td:', 'aliases' => '', 'keywords' => 'chad country nation tchad'], + 'co' => ['unicode' => '1F1E8-1F1F4', 'shortname' => ':co:', 'aliases' => '', 'keywords' => 'colombia country nation'], + 'km' => ['unicode' => '1F1F0-1F1F2', 'shortname' => ':km:', 'aliases' => '', 'keywords' => 'the comoros country nation'], + 'cr' => ['unicode' => '1F1E8-1F1F7', 'shortname' => ':cr:', 'aliases' => '', 'keywords' => 'costa rica country nation'], + 'ci' => ['unicode' => '1F1E8-1F1EE', 'shortname' => ':ci:', 'aliases' => '', 'keywords' => 'cote d\'ivoire country nation'], + 'hr' => ['unicode' => '1F1ED-1F1F7', 'shortname' => ':hr:', 'aliases' => '', 'keywords' => 'croatia country nation hrvatska'], + 'cu' => ['unicode' => '1F1E8-1F1FA', 'shortname' => ':cu:', 'aliases' => '', 'keywords' => 'cuba country nation'], + 'cy' => ['unicode' => '1F1E8-1F1FE', 'shortname' => ':cy:', 'aliases' => '', 'keywords' => 'cyprus country nation kibris kypros'], + 'cz' => ['unicode' => '1F1E8-1F1FF', 'shortname' => ':cz:', 'aliases' => '', 'keywords' => 'the czech republic country nation ceska republika'], + 'dk' => ['unicode' => '1F1E9-1F1F0', 'shortname' => ':dk:', 'aliases' => '', 'keywords' => 'denmark country nation danmark'], + 'dj' => ['unicode' => '1F1E9-1F1EF', 'shortname' => ':dj:', 'aliases' => '', 'keywords' => 'djibouti country nation'], + 'dm' => ['unicode' => '1F1E9-1F1F2', 'shortname' => ':dm:', 'aliases' => '', 'keywords' => 'dominica country nation'], + 'do' => ['unicode' => '1F1E9-1F1F4', 'shortname' => ':do:', 'aliases' => '', 'keywords' => 'the dominican republic country nation'], + 'tl' => ['unicode' => '1F1F9-1F1F1', 'shortname' => ':tl:', 'aliases' => '', 'keywords' => 'east timor country nation'], + 'ec' => ['unicode' => '1F1EA-1F1E8', 'shortname' => ':ec:', 'aliases' => '', 'keywords' => 'ecuador country nation'], + 'eg' => ['unicode' => '1F1EA-1F1EC', 'shortname' => ':eg:', 'aliases' => '', 'keywords' => 'egypt country nation misr'], + 'sv' => ['unicode' => '1F1F8-1F1FB', 'shortname' => ':sv:', 'aliases' => '', 'keywords' => 'el salvador country nation'], + 'gq' => ['unicode' => '1F1EC-1F1F6', 'shortname' => ':gq:', 'aliases' => '', 'keywords' => 'equatorial guinea country nation guinea ecuatorial'], + 'er' => ['unicode' => '1F1EA-1F1F7', 'shortname' => ':er:', 'aliases' => '', 'keywords' => 'eritrea country nation hagere ertra'], + 'ee' => ['unicode' => '1F1EA-1F1EA', 'shortname' => ':ee:', 'aliases' => '', 'keywords' => 'estonia country nation eesti vabariik'], + 'et' => ['unicode' => '1F1EA-1F1F9', 'shortname' => ':et:', 'aliases' => '', 'keywords' => 'ethiopia country nation ityop\'iya ityopiya'], + 'fj' => ['unicode' => '1F1EB-1F1EF', 'shortname' => ':fj:', 'aliases' => '', 'keywords' => 'fiji country nation'], + 'fi' => ['unicode' => '1F1EB-1F1EE', 'shortname' => ':fi:', 'aliases' => '', 'keywords' => 'finland country nation suomen tasavalta'], + 'ga' => ['unicode' => '1F1EC-1F1E6', 'shortname' => ':ga:', 'aliases' => '', 'keywords' => 'gabon country nation'], + 'gm' => ['unicode' => '1F1EC-1F1F2', 'shortname' => ':gm:', 'aliases' => '', 'keywords' => 'the gambia country nation'], + 'ge' => ['unicode' => '1F1EC-1F1EA', 'shortname' => ':ge:', 'aliases' => '', 'keywords' => 'georgia country nation sak\'art\'velo sakartvelo'], + 'gh' => ['unicode' => '1F1EC-1F1ED', 'shortname' => ':gh:', 'aliases' => '', 'keywords' => 'ghana country nation'], + 'gr' => ['unicode' => '1F1EC-1F1F7', 'shortname' => ':gr:', 'aliases' => '', 'keywords' => 'greece country nation ellas ellada'], + 'gd' => ['unicode' => '1F1EC-1F1E9', 'shortname' => ':gd:', 'aliases' => '', 'keywords' => 'grenada country nation'], + 'gt' => ['unicode' => '1F1EC-1F1F9', 'shortname' => ':gt:', 'aliases' => '', 'keywords' => 'guatemala country nation'], + 'gn' => ['unicode' => '1F1EC-1F1F3', 'shortname' => ':gn:', 'aliases' => '', 'keywords' => 'guinea country nation guinee'], + 'gw' => ['unicode' => '1F1EC-1F1FC', 'shortname' => ':gw:', 'aliases' => '', 'keywords' => 'guinea-bissau country nation guine-bissau guine bissau'], + 'gy' => ['unicode' => '1F1EC-1F1FE', 'shortname' => ':gy:', 'aliases' => '', 'keywords' => 'guyana country nation'], + 'ht' => ['unicode' => '1F1ED-1F1F9', 'shortname' => ':ht:', 'aliases' => '', 'keywords' => 'haiti country nation'], + 'hn' => ['unicode' => '1F1ED-1F1F3', 'shortname' => ':hn:', 'aliases' => '', 'keywords' => 'honduras country nation'], + 'hu' => ['unicode' => '1F1ED-1F1FA', 'shortname' => ':hu:', 'aliases' => '', 'keywords' => 'hungary country nation magyarorszag'], + 'is' => ['unicode' => '1F1EE-1F1F8', 'shortname' => ':is:', 'aliases' => '', 'keywords' => 'iceland country nation lyoveldio island'], + 'in' => ['unicode' => '1F1EE-1F1F3', 'shortname' => ':in:', 'aliases' => '', 'keywords' => 'india country nation bharat'], + 'ir' => ['unicode' => '1F1EE-1F1F7', 'shortname' => ':ir:', 'aliases' => '', 'keywords' => 'iran country nation'], + 'iq' => ['unicode' => '1F1EE-1F1F6', 'shortname' => ':iq:', 'aliases' => '', 'keywords' => 'iraq country nation'], + 'ie' => ['unicode' => '1F1EE-1F1EA', 'shortname' => ':ie:', 'aliases' => '', 'keywords' => 'ireland country nation éire eire'], + 'il' => ['unicode' => '1F1EE-1F1F1', 'shortname' => ':il:', 'aliases' => '', 'keywords' => 'israel country nation yisra\'el yisrael'], + 'jm' => ['unicode' => '1F1EF-1F1F2', 'shortname' => ':jm:', 'aliases' => '', 'keywords' => 'jamaica country nation'], + 'jo' => ['unicode' => '1F1EF-1F1F4', 'shortname' => ':jo:', 'aliases' => '', 'keywords' => 'jordan country nation al urdun'], + 'kz' => ['unicode' => '1F1F0-1F1FF', 'shortname' => ':kz:', 'aliases' => '', 'keywords' => 'kazakhstan country nation qazaqstan'], + 'ke' => ['unicode' => '1F1F0-1F1EA', 'shortname' => ':ke:', 'aliases' => '', 'keywords' => 'kenya country nation'], + 'ki' => ['unicode' => '1F1F0-1F1EE', 'shortname' => ':ki:', 'aliases' => '', 'keywords' => 'kiribati country nation kiribati kiribas'], + 'xk' => ['unicode' => '1F1FD-1F1F0', 'shortname' => ':xk:', 'aliases' => '', 'keywords' => 'kosovo country nation'], + 'kw' => ['unicode' => '1F1F0-1F1FC', 'shortname' => ':kw:', 'aliases' => '', 'keywords' => 'kuwait country nation al kuwayt'], + 'cn' => ['unicode' => '1F1E8-1F1F3', 'shortname' => ':cn:', 'aliases' => '', 'keywords' => 'china china chinese prc zhong guo'], + 'kg' => ['unicode' => '1F1F0-1F1EC', 'shortname' => ':kg:', 'aliases' => '', 'keywords' => 'kyrgyzstan country nation kyrgyz respublikasy'], + 'la' => ['unicode' => '1F1F1-1F1E6', 'shortname' => ':la:', 'aliases' => '', 'keywords' => 'laos country nation'], + 'lv' => ['unicode' => '1F1F1-1F1FB', 'shortname' => ':lv:', 'aliases' => '', 'keywords' => 'latvia country nation latvija'], + 'de' => ['unicode' => '1F1E9-1F1EA', 'shortname' => ':de:', 'aliases' => '', 'keywords' => 'germany flag german nation deutschland'], + 'lb' => ['unicode' => '1F1F1-1F1E7', 'shortname' => ':lb:', 'aliases' => '', 'keywords' => 'lebanon country nation lubnan'], + 'ls' => ['unicode' => '1F1F1-1F1F8', 'shortname' => ':ls:', 'aliases' => '', 'keywords' => 'lesotho country nation'], + 'lr' => ['unicode' => '1F1F1-1F1F7', 'shortname' => ':lr:', 'aliases' => '', 'keywords' => 'liberia country nation'], + 'es' => ['unicode' => '1F1EA-1F1F8', 'shortname' => ':es:', 'aliases' => '', 'keywords' => 'spain flag nation spain españa'], + 'ly' => ['unicode' => '1F1F1-1F1FE', 'shortname' => ':ly:', 'aliases' => '', 'keywords' => 'libya country nation libiyah'], + 'li' => ['unicode' => '1F1F1-1F1EE', 'shortname' => ':li:', 'aliases' => '', 'keywords' => 'liechtenstein country nation'], + 'lt' => ['unicode' => '1F1F1-1F1F9', 'shortname' => ':lt:', 'aliases' => '', 'keywords' => 'lithuania country nation lietuva'], + 'fr' => ['unicode' => '1F1EB-1F1F7', 'shortname' => ':fr:', 'aliases' => '', 'keywords' => 'france banner flag france french nation'], + 'lu' => ['unicode' => '1F1F1-1F1FA', 'shortname' => ':lu:', 'aliases' => '', 'keywords' => 'luxembourg country nation luxembourg letzebuerg'], + 'mk' => ['unicode' => '1F1F2-1F1F0', 'shortname' => ':mk:', 'aliases' => '', 'keywords' => 'macedonia country nation'], + 'mg' => ['unicode' => '1F1F2-1F1EC', 'shortname' => ':mg:', 'aliases' => '', 'keywords' => 'madagascar country nation'], + 'gb' => ['unicode' => '1F1EC-1F1E7', 'shortname' => ':gb:', 'aliases' => '', 'keywords' => 'great britain UK banner britsh flag nation united kingdom england'], + 'mw' => ['unicode' => '1F1F2-1F1FC', 'shortname' => ':mw:', 'aliases' => '', 'keywords' => 'malawi country nation'], + 'my' => ['unicode' => '1F1F2-1F1FE', 'shortname' => ':my:', 'aliases' => '', 'keywords' => 'malaysia country nation'], + 'mv' => ['unicode' => '1F1F2-1F1FB', 'shortname' => ':mv:', 'aliases' => '', 'keywords' => 'maldives country nation dhivehi raajje'], + 'it' => ['unicode' => '1F1EE-1F1F9', 'shortname' => ':it:', 'aliases' => '', 'keywords' => 'italy flag italy italia'], + 'ml' => ['unicode' => '1F1F2-1F1F1', 'shortname' => ':ml:', 'aliases' => '', 'keywords' => 'mali country nation'], + 'mt' => ['unicode' => '1F1F2-1F1F9', 'shortname' => ':mt:', 'aliases' => '', 'keywords' => 'malta country nation'], + 'mh' => ['unicode' => '1F1F2-1F1ED', 'shortname' => ':mh:', 'aliases' => '', 'keywords' => 'the marshall islands country nation'], + 'jp' => ['unicode' => '1F1EF-1F1F5', 'shortname' => ':jp:', 'aliases' => '', 'keywords' => 'japan flag japan nation nippon'], + 'mr' => ['unicode' => '1F1F2-1F1F7', 'shortname' => ':mr:', 'aliases' => '', 'keywords' => 'mauritania country nation muritaniyah'], + 'mu' => ['unicode' => '1F1F2-1F1FA', 'shortname' => ':mu:', 'aliases' => '', 'keywords' => 'mauritius country nation'], + 'mx' => ['unicode' => '1F1F2-1F1FD', 'shortname' => ':mx:', 'aliases' => '', 'keywords' => 'mexico country nation'], + 'kr' => ['unicode' => '1F1F0-1F1F7', 'shortname' => ':kr:', 'aliases' => '', 'keywords' => 'korea flag korea nation'], + 'fm' => ['unicode' => '1F1EB-1F1F2', 'shortname' => ':fm:', 'aliases' => '', 'keywords' => 'micronesia country nation'], + 'md' => ['unicode' => '1F1F2-1F1E9', 'shortname' => ':md:', 'aliases' => '', 'keywords' => 'moldova country nation'], + 'mc' => ['unicode' => '1F1F2-1F1E8', 'shortname' => ':mc:', 'aliases' => '', 'keywords' => 'monaco country nation'], + 'us' => ['unicode' => '1F1FA-1F1F8', 'shortname' => ':us:', 'aliases' => '', 'keywords' => 'united states american banner country flag nation usa united states of america america old glory'], + 'grinning' => ['unicode' => '1F600', 'shortname' => ':grinning:', 'aliases' => '', 'keywords' => 'grinning face face happy joy smile grin grinning smiling smile smiley'], + 'mn' => ['unicode' => '1F1F2-1F1F3', 'shortname' => ':mn:', 'aliases' => '', 'keywords' => 'mongolia country nation mongol uls'], + 'innocent' => ['unicode' => '1F607', 'shortname' => ':innocent:', 'aliases' => '', 'keywords' => 'smiling face with halo angel face halo halo angel innocent ring circle heaven'], + 'me' => ['unicode' => '1F1F2-1F1EA', 'shortname' => ':me:', 'aliases' => '', 'keywords' => 'montenegro country nation crna gora'], + 'smiling_imp' => ['unicode' => '1F608', 'shortname' => ':smiling_imp:', 'aliases' => '', 'keywords' => 'smiling face with horns devil horns horns devil impish trouble'], + 'ma' => ['unicode' => '1F1F2-1F1E6', 'shortname' => ':ma:', 'aliases' => '', 'keywords' => 'morocco country nation al maghrib'], + 'ru' => ['unicode' => '1F1F7-1F1FA', 'shortname' => ':ru:', 'aliases' => '', 'keywords' => 'russia banner flag nation russian'], + 'sunglasses' => ['unicode' => '1F60E', 'shortname' => ':sunglasses:', 'aliases' => '', 'keywords' => 'smiling face with sunglasses cool face smiling sunglasses sun glasses sunny cool smooth'], + 'mz' => ['unicode' => '1F1F2-1F1FF', 'shortname' => ':mz:', 'aliases' => '', 'keywords' => 'mozambique country nation mocambique'], + 'neutral_face' => ['unicode' => '1F610', 'shortname' => ':neutral_face:', 'aliases' => '', 'keywords' => 'neutral face face indifference neutral objective impartial blank'], + 'mm' => ['unicode' => '1F1F2-1F1F2', 'shortname' => ':mm:', 'aliases' => '', 'keywords' => 'myanmar country nation myanma naingngandaw'], + 'expressionless' => ['unicode' => '1F611', 'shortname' => ':expressionless:', 'aliases' => '', 'keywords' => 'expressionless face expressionless blank void vapid without expression face indifferent'], + 'na' => ['unicode' => '1F1F3-1F1E6', 'shortname' => ':na:', 'aliases' => '', 'keywords' => 'namibia country nation'], + 'confused' => ['unicode' => '1F615', 'shortname' => ':confused:', 'aliases' => '', 'keywords' => 'confused face confused confuse daze perplex puzzle indifference skeptical undecided uneasy hesitant'], + 'nr' => ['unicode' => '1F1F3-1F1F7', 'shortname' => ':nr:', 'aliases' => '', 'keywords' => 'nauru country nation'], + 'kissing' => ['unicode' => '1F617', 'shortname' => ':kissing:', 'aliases' => '', 'keywords' => 'kissing face 3 face infatuation like love valentines kissing kiss pucker lips smooch'], + 'np' => ['unicode' => '1F1F3-1F1F5', 'shortname' => ':np:', 'aliases' => '', 'keywords' => 'nepal country nation'], + 'kissing_smiling_eyes' => ['unicode' => '1F619', 'shortname' => ':kissing_smiling_eyes:', 'aliases' => '', 'keywords' => 'kissing face with smiling eyes affection face infatuation valentines kissing kiss smile pucker lips smooch'], + 'nl' => ['unicode' => '1F1F3-1F1F1', 'shortname' => ':nl:', 'aliases' => '', 'keywords' => 'the netherlands country nation nederland holland'], + 'stuck_out_tongue' => ['unicode' => '1F61B', 'shortname' => ':stuck_out_tongue:', 'aliases' => '', 'keywords' => 'face with stuck-out tongue childish face mischievous playful prank tongue silly playful cheeky'], + 'nz' => ['unicode' => '1F1F3-1F1FF', 'shortname' => ':nz:', 'aliases' => '', 'keywords' => 'new zealand country nation aotearoa'], + 'worried' => ['unicode' => '1F61F', 'shortname' => ':worried:', 'aliases' => '', 'keywords' => 'worried face concern face nervous worried anxious distressed nervous tense'], + 'ni' => ['unicode' => '1F1F3-1F1EE', 'shortname' => ':ni:', 'aliases' => '', 'keywords' => 'nicaragua country nation'], + 'frowning' => ['unicode' => '1F626', 'shortname' => ':frowning:', 'aliases' => ':anguished:', 'keywords' => 'frowning face with open mouth aw face frown sad pout sulk glower'], + 'ne' => ['unicode' => '1F1F3-1F1EA', 'shortname' => ':ne:', 'aliases' => '', 'keywords' => 'niger country nation'], + 'anguished' => ['unicode' => '1F627', 'shortname' => ':anguished:', 'aliases' => '', 'keywords' => 'anguished face face nervous stunned pain anguish ouch misery distress grief'], + 'grimacing' => ['unicode' => '1F62C', 'shortname' => ':grimacing:', 'aliases' => '', 'keywords' => 'grimacing face face grimace teeth grimace disapprove pain'], + 'kp' => ['unicode' => '1F1F0-1F1F5', 'shortname' => ':kp:', 'aliases' => '', 'keywords' => 'north korea country nation'], + 'open_mouth' => ['unicode' => '1F62E', 'shortname' => ':open_mouth:', 'aliases' => '', 'keywords' => 'face with open mouth face impressed mouth open jaw gapping surprise wow'], + 'no' => ['unicode' => '1F1F3-1F1F4', 'shortname' => ':no:', 'aliases' => '', 'keywords' => 'norway country nation norge'], + 'hushed' => ['unicode' => '1F62F', 'shortname' => ':hushed:', 'aliases' => '', 'keywords' => 'hushed face face woo quiet hush whisper silent'], + 'om' => ['unicode' => '1F1F4-1F1F2', 'shortname' => ':om:', 'aliases' => '', 'keywords' => 'oman country nation saltanat uman'], + 'sleeping' => ['unicode' => '1F634', 'shortname' => ':sleeping:', 'aliases' => '', 'keywords' => 'sleeping face face sleepy tired sleep sleepy sleeping snore'], + 'pk' => ['unicode' => '1F1F5-1F1F0', 'shortname' => ':pk:', 'aliases' => '', 'keywords' => 'pakistan country nation'], + 'no_mouth' => ['unicode' => '1F636', 'shortname' => ':no_mouth:', 'aliases' => '', 'keywords' => 'face without mouth face hellokitty mouth silent vapid'], + 'pw' => ['unicode' => '1F1F5-1F1FC', 'shortname' => ':pw:', 'aliases' => '', 'keywords' => 'palau country nation belau'], + 'helicopter' => ['unicode' => '1F681', 'shortname' => ':helicopter:', 'aliases' => '', 'keywords' => 'helicopter transportation vehicle helicopter helo gyro gyrocopter'], + 'pa' => ['unicode' => '1F1F5-1F1E6', 'shortname' => ':pa:', 'aliases' => '', 'keywords' => 'panama country nation'], + 'steam_locomotive' => ['unicode' => '1F682', 'shortname' => ':steam_locomotive:', 'aliases' => '', 'keywords' => 'steam locomotive train transportation vehicle locomotive steam train engine'], + 'pg' => ['unicode' => '1F1F5-1F1EC', 'shortname' => ':pg:', 'aliases' => '', 'keywords' => 'papua new guinea country nation papua niu gini'], + 'train2' => ['unicode' => '1F686', 'shortname' => ':train2:', 'aliases' => '', 'keywords' => 'train transportation vehicle train locomotive rail'], + 'py' => ['unicode' => '1F1F5-1F1FE', 'shortname' => ':py:', 'aliases' => '', 'keywords' => 'paraguay country nation'], + 'light_rail' => ['unicode' => '1F688', 'shortname' => ':light_rail:', 'aliases' => '', 'keywords' => 'light rail transportation vehicle train rail light'], + 'tram' => ['unicode' => '1F68A', 'shortname' => ':tram:', 'aliases' => '', 'keywords' => 'tram transportation vehicle tram transportation transport'], + 'pe' => ['unicode' => '1F1F5-1F1EA', 'shortname' => ':pe:', 'aliases' => '', 'keywords' => 'peru country nation'], + 'oncoming_bus' => ['unicode' => '1F68D', 'shortname' => ':oncoming_bus:', 'aliases' => '', 'keywords' => 'oncoming bus transportation vehicle bus school city transportation public'], + 'ph' => ['unicode' => '1F1F5-1F1ED', 'shortname' => ':ph:', 'aliases' => '', 'keywords' => 'the philippines country nation pilipinas'], + 'trolleybus' => ['unicode' => '1F68E', 'shortname' => ':trolleybus:', 'aliases' => '', 'keywords' => 'trolleybus bart transportation vehicle trolley bus city transport transportation'], + 'pl' => ['unicode' => '1F1F5-1F1F1', 'shortname' => ':pl:', 'aliases' => '', 'keywords' => 'poland country nation polska'], + 'minibus' => ['unicode' => '1F690', 'shortname' => ':minibus:', 'aliases' => '', 'keywords' => 'minibus car transportation vehicle bus city transport transportation'], + 'pt' => ['unicode' => '1F1F5-1F1F9', 'shortname' => ':pt:', 'aliases' => '', 'keywords' => 'portugal country nation'], + 'oncoming_police_car' => ['unicode' => '1F694', 'shortname' => ':oncoming_police_car:', 'aliases' => '', 'keywords' => 'oncoming police car enforcement law vehicle police car emergency ticket citation crime help officer'], + 'qa' => ['unicode' => '1F1F6-1F1E6', 'shortname' => ':qa:', 'aliases' => '', 'keywords' => 'qatar country nation dawlat qatar'], + 'oncoming_taxi' => ['unicode' => '1F696', 'shortname' => ':oncoming_taxi:', 'aliases' => '', 'keywords' => 'oncoming taxi cars uber vehicle taxi car automobile city transport service'], + 'tw' => ['unicode' => '1F1F9-1F1FC', 'shortname' => ':tw:', 'aliases' => '', 'keywords' => 'the republic of china country nation'], + 'oncoming_automobile' => ['unicode' => '1F698', 'shortname' => ':oncoming_automobile:', 'aliases' => '', 'keywords' => 'oncoming automobile car transportation vehicle sedan car automobile'], + 'cg' => ['unicode' => '1F1E8-1F1EC', 'shortname' => ':cg:', 'aliases' => '', 'keywords' => 'the republic of the congo country nation'], + 'articulated_lorry' => ['unicode' => '1F69B', 'shortname' => ':articulated_lorry:', 'aliases' => '', 'keywords' => 'articulated lorry cars transportation vehicle truck delivery semi lorry articulated'], + 'ro' => ['unicode' => '1F1F7-1F1F4', 'shortname' => ':ro:', 'aliases' => '', 'keywords' => 'romania country nation'], + 'tractor' => ['unicode' => '1F69C', 'shortname' => ':tractor:', 'aliases' => '', 'keywords' => 'tractor agriculture car farming vehicle tractor farm construction machine digger'], + 'monorail' => ['unicode' => '1F69D', 'shortname' => ':monorail:', 'aliases' => '', 'keywords' => 'monorail transportation vehicle train mono rail transport'], + 'rw' => ['unicode' => '1F1F7-1F1FC', 'shortname' => ':rw:', 'aliases' => '', 'keywords' => 'rwanda country nation'], + 'mountain_railway' => ['unicode' => '1F69E', 'shortname' => ':mountain_railway:', 'aliases' => '', 'keywords' => 'mountain railway transportation mountain railway rail train transport'], + 'kn' => ['unicode' => '1F1F0-1F1F3', 'shortname' => ':kn:', 'aliases' => '', 'keywords' => 'saint kitts and nevis country nation '], + 'suspension_railway' => ['unicode' => '1F69F', 'shortname' => ':suspension_railway:', 'aliases' => '', 'keywords' => 'suspension railway transportation vehicle suspension railway rail train transportation'], + 'lc' => ['unicode' => '1F1F1-1F1E8', 'shortname' => ':lc:', 'aliases' => '', 'keywords' => 'saint lucia country nation'], + 'mountain_cableway' => ['unicode' => '1F6A0', 'shortname' => ':mountain_cableway:', 'aliases' => '', 'keywords' => 'mountain cableway transportation vehicle mountain cable rail train railway'], + 'vc' => ['unicode' => '1F1FB-1F1E8', 'shortname' => ':vc:', 'aliases' => '', 'keywords' => 'saint vincent and the grenadines country nation '], + 'aerial_tramway' => ['unicode' => '1F6A1', 'shortname' => ':aerial_tramway:', 'aliases' => '', 'keywords' => 'aerial tramway transportation vehicle aerial tram tramway cable transport'], + 'ws' => ['unicode' => '1F1FC-1F1F8', 'shortname' => ':ws:', 'aliases' => '', 'keywords' => 'samoa country nation american samoa'], + 'rowboat' => ['unicode' => '1F6A3', 'shortname' => ':rowboat:', 'aliases' => '', 'keywords' => 'rowboat hobby ship sports water boat row oar paddle'], + 'sm' => ['unicode' => '1F1F8-1F1F2', 'shortname' => ':sm:', 'aliases' => '', 'keywords' => 'san marino country nation'], + 'vertical_traffic_light' => ['unicode' => '1F6A6', 'shortname' => ':vertical_traffic_light:', 'aliases' => '', 'keywords' => 'vertical traffic light transportation traffic light stop go yield vertical'], + 'st' => ['unicode' => '1F1F8-1F1F9', 'shortname' => ':st:', 'aliases' => '', 'keywords' => 'sao tome and principe country nation sao tome e principe'], + 'put_litter_in_its_place' => ['unicode' => '1F6AE', 'shortname' => ':put_litter_in_its_place:', 'aliases' => '', 'keywords' => 'put litter in its place symbol blue-square litter waste trash garbage receptacle can'], + 'do_not_litter' => ['unicode' => '1F6AF', 'shortname' => ':do_not_litter:', 'aliases' => '', 'keywords' => 'do not litter symbol bin garbage trash litter garbage waste no can trash'], + 'sn' => ['unicode' => '1F1F8-1F1F3', 'shortname' => ':sn:', 'aliases' => '', 'keywords' => 'senegal country nation'], + 'potable_water' => ['unicode' => '1F6B0', 'shortname' => ':potable_water:', 'aliases' => '', 'keywords' => 'potable water symbol blue-square cleaning faucet liquid restroom potable water drinkable pure clear clean aqua h20'], + 'rs' => ['unicode' => '1F1F7-1F1F8', 'shortname' => ':rs:', 'aliases' => '', 'keywords' => 'serbia country nation srbija'], + 'non-potable_water' => ['unicode' => '1F6B1', 'shortname' => ':non-potable_water:', 'aliases' => '', 'keywords' => 'non-potable water symbol drink faucet tap non-potable water not drinkable dirty gross aqua h20'], + 'sc' => ['unicode' => '1F1F8-1F1E8', 'shortname' => ':sc:', 'aliases' => '', 'keywords' => 'the seychelles country nation seychelles'], + 'no_bicycles' => ['unicode' => '1F6B3', 'shortname' => ':no_bicycles:', 'aliases' => '', 'keywords' => 'no bicycles cyclist prohibited bicycle bike pedal no'], + 'sl' => ['unicode' => '1F1F8-1F1F1', 'shortname' => ':sl:', 'aliases' => '', 'keywords' => 'sierra leone country nation'], + 'bicyclist' => ['unicode' => '1F6B4', 'shortname' => ':bicyclist:', 'aliases' => '', 'keywords' => 'bicyclist bike exercise hipster sports bicyclist road bike pedal bicycle transportation'], + 'sg' => ['unicode' => '1F1F8-1F1EC', 'shortname' => ':sg:', 'aliases' => '', 'keywords' => 'singapore country nation'], + 'mountain_bicyclist' => ['unicode' => '1F6B5', 'shortname' => ':mountain_bicyclist:', 'aliases' => '', 'keywords' => 'mountain bicyclist human sports transportation bicyclist mountain bike pedal bicycle transportation'], + 'sk' => ['unicode' => '1F1F8-1F1F0', 'shortname' => ':sk:', 'aliases' => '', 'keywords' => 'slovakia country nation'], + 'no_pedestrians' => ['unicode' => '1F6B7', 'shortname' => ':no_pedestrians:', 'aliases' => '', 'keywords' => 'no pedestrians crossing rules walking no walk pedestrian stroll stride foot feet'], + 'si' => ['unicode' => '1F1F8-1F1EE', 'shortname' => ':si:', 'aliases' => '', 'keywords' => 'slovenia country nation slovenija'], + 'children_crossing' => ['unicode' => '1F6B8', 'shortname' => ':children_crossing:', 'aliases' => '', 'keywords' => 'children crossing school children kids caution crossing street crosswalk slow'], + 'sb' => ['unicode' => '1F1F8-1F1E7', 'shortname' => ':sb:', 'aliases' => '', 'keywords' => 'the solomon islands country nation'], + 'shower' => ['unicode' => '1F6BF', 'shortname' => ':shower:', 'aliases' => '', 'keywords' => 'shower bath clean wash bathroom shower soap water clean shampoo lather'], + 'so' => ['unicode' => '1F1F8-1F1F4', 'shortname' => ':so:', 'aliases' => '', 'keywords' => 'somalia country nation'], + 'bathtub' => ['unicode' => '1F6C1', 'shortname' => ':bathtub:', 'aliases' => '', 'keywords' => 'bathtub clean shower bath tub basin wash bubble soak bathroom soap water clean shampoo lather water'], + 'za' => ['unicode' => '1F1FF-1F1E6', 'shortname' => ':za:', 'aliases' => '', 'keywords' => 'south africa country nation'], + 'passport_control' => ['unicode' => '1F6C2', 'shortname' => ':passport_control:', 'aliases' => '', 'keywords' => 'passport control blue-square custom passport official travel control foreign identification'], + 'customs' => ['unicode' => '1F6C3', 'shortname' => ':customs:', 'aliases' => '', 'keywords' => 'customs border passport customs travel foreign goods check authority government'], + 'baggage_claim' => ['unicode' => '1F6C4', 'shortname' => ':baggage_claim:', 'aliases' => '', 'keywords' => 'baggage claim airport blue-square transport bag baggage luggage travel'], + 'lk' => ['unicode' => '1F1F1-1F1F0', 'shortname' => ':lk:', 'aliases' => '', 'keywords' => 'sri lanka country nation'], + 'left_luggage' => ['unicode' => '1F6C5', 'shortname' => ':left_luggage:', 'aliases' => '', 'keywords' => 'left luggage blue-square travel bag baggage luggage travel'], + 'sd' => ['unicode' => '1F1F8-1F1E9', 'shortname' => ':sd:', 'aliases' => '', 'keywords' => 'sudan country nation as-sudan'], + 'earth_africa' => ['unicode' => '1F30D', 'shortname' => ':earth_africa:', 'aliases' => '', 'keywords' => 'earth globe europe-africa globe international world earth globe space planet africa europe home'], + 'sr' => ['unicode' => '1F1F8-1F1F7', 'shortname' => ':sr:', 'aliases' => '', 'keywords' => 'suriname country nation'], + 'earth_americas' => ['unicode' => '1F30E', 'shortname' => ':earth_americas:', 'aliases' => '', 'keywords' => 'earth globe americas USA globe international world earth globe space planet north south america americas home'], + 'sz' => ['unicode' => '1F1F8-1F1FF', 'shortname' => ':sz:', 'aliases' => '', 'keywords' => 'swaziland country nation'], + 'globe_with_meridians' => ['unicode' => '1F310', 'shortname' => ':globe_with_meridians:', 'aliases' => '', 'keywords' => 'globe with meridians earth international world earth meridian globe space planet home'], + 'se' => ['unicode' => '1F1F8-1F1EA', 'shortname' => ':se:', 'aliases' => '', 'keywords' => 'sweden country nation sverige'], + 'waxing_crescent_moon' => ['unicode' => '1F312', 'shortname' => ':waxing_crescent_moon:', 'aliases' => '', 'keywords' => 'waxing crescent moon symbol nature moon waxing sky night cheese phase'], + 'ch' => ['unicode' => '1F1E8-1F1ED', 'shortname' => ':ch:', 'aliases' => '', 'keywords' => 'switzerland country nation'], + 'waning_gibbous_moon' => ['unicode' => '1F316', 'shortname' => ':waning_gibbous_moon:', 'aliases' => '', 'keywords' => 'waning gibbous moon symbol nature moon waning gibbous sky night cheese phase'], + 'sy' => ['unicode' => '1F1F8-1F1FE', 'shortname' => ':sy:', 'aliases' => '', 'keywords' => 'syria country nation'], + 'last_quarter_moon' => ['unicode' => '1F317', 'shortname' => ':last_quarter_moon:', 'aliases' => '', 'keywords' => 'last quarter moon symbol nature moon last quarter sky night cheese phase'], + 'tj' => ['unicode' => '1F1F9-1F1EF', 'shortname' => ':tj:', 'aliases' => '', 'keywords' => 'tajikistan country nation jumhurii tojikiston'], + 'waning_crescent_moon' => ['unicode' => '1F318', 'shortname' => ':waning_crescent_moon:', 'aliases' => '', 'keywords' => 'waning crescent moon symbol nature moon crescent waning sky night cheese phase'], + 'tz' => ['unicode' => '1F1F9-1F1FF', 'shortname' => ':tz:', 'aliases' => '', 'keywords' => 'tanzania country nation'], + 'new_moon_with_face' => ['unicode' => '1F31A', 'shortname' => ':new_moon_with_face:', 'aliases' => '', 'keywords' => 'new moon with face nature moon new anthropomorphic face sky night cheese phase'], + 'th' => ['unicode' => '1F1F9-1F1ED', 'shortname' => ':th:', 'aliases' => '', 'keywords' => 'thailand country nation prathet thai'], + 'last_quarter_moon_with_face' => ['unicode' => '1F31C', 'shortname' => ':last_quarter_moon_with_face:', 'aliases' => '', 'keywords' => 'last quarter moon with face nature moon last quarter anthropomorphic face sky night cheese phase'], + 'tg' => ['unicode' => '1F1F9-1F1EC', 'shortname' => ':tg:', 'aliases' => '', 'keywords' => 'togo country nation republique togolaise'], + 'full_moon_with_face' => ['unicode' => '1F31D', 'shortname' => ':full_moon_with_face:', 'aliases' => '', 'keywords' => 'full moon with face night moon full anthropomorphic face sky night cheese phase spooky werewolves monsters'], + 'to' => ['unicode' => '1F1F9-1F1F4', 'shortname' => ':to:', 'aliases' => '', 'keywords' => 'tonga country nation'], + 'sun_with_face' => ['unicode' => '1F31E', 'shortname' => ':sun_with_face:', 'aliases' => '', 'keywords' => 'sun with face morning sun anthropomorphic face sky'], + 'tt' => ['unicode' => '1F1F9-1F1F9', 'shortname' => ':tt:', 'aliases' => '', 'keywords' => 'trinidad and tobago country nation'], + 'evergreen_tree' => ['unicode' => '1F332', 'shortname' => ':evergreen_tree:', 'aliases' => '', 'keywords' => 'evergreen tree nature plant evergreen tree needles christmas'], + 'tn' => ['unicode' => '1F1F9-1F1F3', 'shortname' => ':tn:', 'aliases' => '', 'keywords' => 'tunisia country nation tunis'], + 'deciduous_tree' => ['unicode' => '1F333', 'shortname' => ':deciduous_tree:', 'aliases' => '', 'keywords' => 'deciduous tree nature plant deciduous tree leaves fall color'], + 'tr' => ['unicode' => '1F1F9-1F1F7', 'shortname' => ':tr:', 'aliases' => '', 'keywords' => 'turkey country nation turkiye'], + 'lemon' => ['unicode' => '1F34B', 'shortname' => ':lemon:', 'aliases' => '', 'keywords' => 'lemon fruit nature lemon yellow citrus'], + 'pear' => ['unicode' => '1F350', 'shortname' => ':pear:', 'aliases' => '', 'keywords' => 'pear fruit nature pear fruit shape'], + 'baby_bottle' => ['unicode' => '1F37C', 'shortname' => ':baby_bottle:', 'aliases' => '', 'keywords' => 'baby bottle container food baby bottle milk mother nipple newborn formula'], + 'ug' => ['unicode' => '1F1FA-1F1EC', 'shortname' => ':ug:', 'aliases' => '', 'keywords' => 'uganda country nation'], + 'horse_racing' => ['unicode' => '1F3C7', 'shortname' => ':horse_racing:', 'aliases' => '', 'keywords' => 'horse racing animal betting competition horse race racing jockey triple crown'], + 'ua' => ['unicode' => '1F1FA-1F1E6', 'shortname' => ':ua:', 'aliases' => '', 'keywords' => 'ukraine country nation ukrayina'], + 'rugby_football' => ['unicode' => '1F3C9', 'shortname' => ':rugby_football:', 'aliases' => '', 'keywords' => 'rugby football sports rugby football ball sport team england'], + 'ae' => ['unicode' => '1F1E6-1F1EA', 'shortname' => ':ae:', 'aliases' => '', 'keywords' => 'the united arab emirates country nation'], + 'european_post_office' => ['unicode' => '1F3E4', 'shortname' => ':european_post_office:', 'aliases' => '', 'keywords' => 'european post office building'], + 'rat' => ['unicode' => '1F400', 'shortname' => ':rat:', 'aliases' => '', 'keywords' => 'rat animal mouse rat rodent crooked snitch'], + 'mouse2' => ['unicode' => '1F401', 'shortname' => ':mouse2:', 'aliases' => '', 'keywords' => 'mouse animal nature mouse mice rodent'], + 'uy' => ['unicode' => '1F1FA-1F1FE', 'shortname' => ':uy:', 'aliases' => '', 'keywords' => 'uruguay country nation'], + 'ox' => ['unicode' => '1F402', 'shortname' => ':ox:', 'aliases' => '', 'keywords' => 'ox animal beef cow'], + 'uz' => ['unicode' => '1F1FA-1F1FF', 'shortname' => ':uz:', 'aliases' => '', 'keywords' => 'uzbekistan country nation uzbekiston respublikasi'], + 'water_buffalo' => ['unicode' => '1F403', 'shortname' => ':water_buffalo:', 'aliases' => '', 'keywords' => 'water buffalo animal cow nature ox water buffalo asia bovine milk dairy'], + 'vu' => ['unicode' => '1F1FB-1F1FA', 'shortname' => ':vu:', 'aliases' => '', 'keywords' => 'vanuatu country nation'], + 'cow2' => ['unicode' => '1F404', 'shortname' => ':cow2:', 'aliases' => '', 'keywords' => 'cow animal beef nature ox cow milk dairy beef bessie moo'], + 'va' => ['unicode' => '1F1FB-1F1E6', 'shortname' => ':va:', 'aliases' => '', 'keywords' => 'the vatican city country nation'], + 'tiger2' => ['unicode' => '1F405', 'shortname' => ':tiger2:', 'aliases' => '', 'keywords' => 'tiger animal nature tiger cat striped tony tigger hobs'], + 've' => ['unicode' => '1F1FB-1F1EA', 'shortname' => ':ve:', 'aliases' => '', 'keywords' => 'venezuela country nation'], + 'leopard' => ['unicode' => '1F406', 'shortname' => ':leopard:', 'aliases' => '', 'keywords' => 'leopard animal nature leopard cat spot spotted sexy'], + 'vn' => ['unicode' => '1F1FB-1F1F3', 'shortname' => ':vn:', 'aliases' => '', 'keywords' => 'vietnam country nation viet nam'], + 'rabbit2' => ['unicode' => '1F407', 'shortname' => ':rabbit2:', 'aliases' => '', 'keywords' => 'rabbit animal nature rabbit bunny easter reproduction prolific'], + 'eh' => ['unicode' => '1F1EA-1F1ED', 'shortname' => ':eh:', 'aliases' => '', 'keywords' => 'western sahara country nation aṣ-Ṣaḥrā’ al-gharbīyah sahra gharbiyah'], + 'cat2' => ['unicode' => '1F408', 'shortname' => ':cat2:', 'aliases' => '', 'keywords' => 'cat animal meow pet cat kitten meow'], + 'ye' => ['unicode' => '1F1FE-1F1EA', 'shortname' => ':ye:', 'aliases' => '', 'keywords' => 'yemen country nation al yaman'], + 'dragon' => ['unicode' => '1F409', 'shortname' => ':dragon:', 'aliases' => '', 'keywords' => 'dragon animal chinese green myth nature dragon fire legendary myth'], + 'zm' => ['unicode' => '1F1FF-1F1F2', 'shortname' => ':zm:', 'aliases' => '', 'keywords' => 'zambia country nation'], + 'crocodile' => ['unicode' => '1F40A', 'shortname' => ':crocodile:', 'aliases' => '', 'keywords' => 'crocodile animal nature crocodile croc alligator gator cranky'], + 'zw' => ['unicode' => '1F1FF-1F1FC', 'shortname' => ':zw:', 'aliases' => '', 'keywords' => 'zimbabwe country nation'], + 'whale2' => ['unicode' => '1F40B', 'shortname' => ':whale2:', 'aliases' => '', 'keywords' => 'whale animal nature ocean sea whale blubber bloated fat large massive'], + 'pr' => ['unicode' => '1F1F5-1F1F7', 'shortname' => ':pr:', 'aliases' => '', 'keywords' => 'puerto rico country nation'], + 'ram' => ['unicode' => '1F40F', 'shortname' => ':ram:', 'aliases' => '', 'keywords' => 'ram animal nature sheep ram sheep male horn horns'], + 'ky' => ['unicode' => '1F1F0-1F1FE', 'shortname' => ':ky:', 'aliases' => '', 'keywords' => 'cayman islands country nation'], + 'goat' => ['unicode' => '1F410', 'shortname' => ':goat:', 'aliases' => '', 'keywords' => 'goat animal nature goat sheep kid billy livestock'], + 'bm' => ['unicode' => '1F1E7-1F1F2', 'shortname' => ':bm:', 'aliases' => '', 'keywords' => 'bermuda country nation'], + 'rooster' => ['unicode' => '1F413', 'shortname' => ':rooster:', 'aliases' => '', 'keywords' => 'rooster animal chicken nature rooster cockerel cock male cock-a-doodle-doo crowing'], + 'pf' => ['unicode' => '1F1F5-1F1EB', 'shortname' => ':pf:', 'aliases' => '', 'keywords' => 'french polynesia country nation polynésie française polynesie francaise'], + 'dog2' => ['unicode' => '1F415', 'shortname' => ':dog2:', 'aliases' => '', 'keywords' => 'dog animal doge friend nature pet dog puppy pet friend woof bark fido'], + 'ps' => ['unicode' => '1F1F5-1F1F8', 'shortname' => ':ps:', 'aliases' => '', 'keywords' => 'palestinian authority country nation'], + 'pig2' => ['unicode' => '1F416', 'shortname' => ':pig2:', 'aliases' => '', 'keywords' => 'pig animal nature pig piggy pork ham hog bacon oink slop livestock greed greedy'], + 'nc' => ['unicode' => '1F1F3-1F1E8', 'shortname' => ':nc:', 'aliases' => '', 'keywords' => 'new caledonia country nation nouvelle calédonie caledonie'], + 'dromedary_camel' => ['unicode' => '1F42A', 'shortname' => ':dromedary_camel:', 'aliases' => '', 'keywords' => 'dromedary camel animal desert hot dromedary camel hump desert middle east heat hot water hump day wednesday sex'], + 'sh' => ['unicode' => '1F1F8-1F1ED', 'shortname' => ':sh:', 'aliases' => '', 'keywords' => 'saint helena country nation'], + 'busts_in_silhouette' => ['unicode' => '1F465', 'shortname' => ':busts_in_silhouette:', 'aliases' => '', 'keywords' => 'busts in silhouette group human man person team user silhouette silhouettes people user members accounts relationship shadow'], + 'aw' => ['unicode' => '1F1E6-1F1FC', 'shortname' => ':aw:', 'aliases' => '', 'keywords' => 'aruba country nation'], + 'two_men_holding_hands' => ['unicode' => '1F46C', 'shortname' => ':two_men_holding_hands:', 'aliases' => '', 'keywords' => 'two men holding hands bromance couple friends like love men gay homosexual friends hands holding team unity'], + 'vi' => ['unicode' => '1F1FB-1F1EE', 'shortname' => ':vi:', 'aliases' => '', 'keywords' => 'u.s. virgin islands country nation'], + 'two_women_holding_hands' => ['unicode' => '1F46D', 'shortname' => ':two_women_holding_hands:', 'aliases' => '', 'keywords' => 'two women holding hands couple female friends like love women hands girlfriends friends sisters mother daughter gay homosexual couple unity'], + 'hk' => ['unicode' => '1F1ED-1F1F0', 'shortname' => ':hk:', 'aliases' => '', 'keywords' => 'hong kong country nation xianggang'], + 'thought_balloon' => ['unicode' => '1F4AD', 'shortname' => ':thought_balloon:', 'aliases' => '', 'keywords' => 'thought balloon bubble cloud speech thought balloon comic think day dream wonder'], + 'ac' => ['unicode' => '1F1E6-1F1E8', 'shortname' => ':ac:', 'aliases' => '', 'keywords' => 'ascension country nation'], + 'euro' => ['unicode' => '1F4B6', 'shortname' => ':euro:', 'aliases' => '', 'keywords' => 'banknote with euro sign currency dollar money euro europe banknote money currency paper cash bills'], + 'ms' => ['unicode' => '1F1F2-1F1F8', 'shortname' => ':ms:', 'aliases' => '', 'keywords' => 'montserrat country nation'], + 'pound' => ['unicode' => '1F4B7', 'shortname' => ':pound:', 'aliases' => '', 'keywords' => 'banknote with pound sign bills british currency england money sterling uk pound britain british banknote money currency paper cash bills'], + 'gu' => ['unicode' => '1F1EC-1F1FA', 'shortname' => ':gu:', 'aliases' => '', 'keywords' => 'guam country nation'], + 'mailbox_with_mail' => ['unicode' => '1F4EC', 'shortname' => ':mailbox_with_mail:', 'aliases' => '', 'keywords' => 'open mailbox with raised flag communication email inbox'], + 'gl' => ['unicode' => '1F1EC-1F1F1', 'shortname' => ':gl:', 'aliases' => '', 'keywords' => 'greenland country nation kalaallit nunaat'], + 'mailbox_with_no_mail' => ['unicode' => '1F4ED', 'shortname' => ':mailbox_with_no_mail:', 'aliases' => '', 'keywords' => 'open mailbox with lowered flag email inbox'], + 'nu' => ['unicode' => '1F1F3-1F1FA', 'shortname' => ':nu:', 'aliases' => '', 'keywords' => 'niue country nation'], + 'postal_horn' => ['unicode' => '1F4EF', 'shortname' => ':postal_horn:', 'aliases' => '', 'keywords' => 'postal horn instrument music'], + 'wf' => ['unicode' => '1F1FC-1F1EB', 'shortname' => ':wf:', 'aliases' => '', 'keywords' => 'wallis and futuna country nation'], + 'no_mobile_phones' => ['unicode' => '1F4F5', 'shortname' => ':no_mobile_phones:', 'aliases' => '', 'keywords' => 'no mobile phones iphone mute'], + 'mo' => ['unicode' => '1F1F2-1F1F4', 'shortname' => ':mo:', 'aliases' => '', 'keywords' => 'macau country nation aomen'], + 'twisted_rightwards_arrows' => ['unicode' => '1F500', 'shortname' => ':twisted_rightwards_arrows:', 'aliases' => '', 'keywords' => 'twisted rightwards arrows blue-square'], + 'fo' => ['unicode' => '1F1EB-1F1F4', 'shortname' => ':fo:', 'aliases' => '', 'keywords' => 'faroe islands country nation foroyar'], + 'repeat' => ['unicode' => '1F501', 'shortname' => ':repeat:', 'aliases' => '', 'keywords' => 'clockwise rightwards and leftwards open circle arr loop record'], + 'fk' => ['unicode' => '1F1EB-1F1F0', 'shortname' => ':fk:', 'aliases' => '', 'keywords' => 'falkland islands country nation islas malvinas'], + 'repeat_one' => ['unicode' => '1F502', 'shortname' => ':repeat_one:', 'aliases' => '', 'keywords' => 'clockwise rightwards and leftwards open circle arr blue-square loop'], + 'je' => ['unicode' => '1F1EF-1F1EA', 'shortname' => ':je:', 'aliases' => '', 'keywords' => 'jersey country nation'], + 'arrows_counterclockwise' => ['unicode' => '1F504', 'shortname' => ':arrows_counterclockwise:', 'aliases' => '', 'keywords' => 'anticlockwise downwards and upwards open circle ar blue-square sync'], + 'ai' => ['unicode' => '1F1E6-1F1EE', 'shortname' => ':ai:', 'aliases' => '', 'keywords' => 'anguilla country nation'], + 'low_brightness' => ['unicode' => '1F505', 'shortname' => ':low_brightness:', 'aliases' => '', 'keywords' => 'low brightness symbol summer sun'], + 'gi' => ['unicode' => '1F1EC-1F1EE', 'shortname' => ':gi:', 'aliases' => '', 'keywords' => 'gibraltar country nation'], + 'high_brightness' => ['unicode' => '1F506', 'shortname' => ':high_brightness:', 'aliases' => '', 'keywords' => 'high brightness symbol light summer sun'], + 'mute' => ['unicode' => '1F507', 'shortname' => ':mute:', 'aliases' => '', 'keywords' => 'speaker with cancellation stroke sound volume'], + 'sound' => ['unicode' => '1F509', 'shortname' => ':sound:', 'aliases' => '', 'keywords' => 'speaker with one sound wave speaker volume'], + 'no_bell' => ['unicode' => '1F515', 'shortname' => ':no_bell:', 'aliases' => '', 'keywords' => 'bell with cancellation stroke mute sound volume'], + 'microscope' => ['unicode' => '1F52C', 'shortname' => ':microscope:', 'aliases' => '', 'keywords' => 'microscope experiment laboratory zoomin'], + 'telescope' => ['unicode' => '1F52D', 'shortname' => ':telescope:', 'aliases' => '', 'keywords' => 'telescope space stars'], + 'clock130' => ['unicode' => '1F55C', 'shortname' => ':clock130:', 'aliases' => '', 'keywords' => 'clock face one-thirty clock time'], + 'clock230' => ['unicode' => '1F55D', 'shortname' => ':clock230:', 'aliases' => '', 'keywords' => 'clock face two-thirty clock time'], + 'clock330' => ['unicode' => '1F55E', 'shortname' => ':clock330:', 'aliases' => '', 'keywords' => 'clock face three-thirty clock time'], + 'clock430' => ['unicode' => '1F55F', 'shortname' => ':clock430:', 'aliases' => '', 'keywords' => 'clock face four-thirty clock time'], + 'clock530' => ['unicode' => '1F560', 'shortname' => ':clock530:', 'aliases' => '', 'keywords' => 'clock face five-thirty clock time'], + 'clock630' => ['unicode' => '1F561', 'shortname' => ':clock630:', 'aliases' => '', 'keywords' => 'clock face six-thirty clock time'], + 'clock730' => ['unicode' => '1F562', 'shortname' => ':clock730:', 'aliases' => '', 'keywords' => 'clock face seven-thirty clock time'], + 'clock830' => ['unicode' => '1F563', 'shortname' => ':clock830:', 'aliases' => '', 'keywords' => 'clock face eight-thirty clock time'], + 'clock930' => ['unicode' => '1F564', 'shortname' => ':clock930:', 'aliases' => '', 'keywords' => 'clock face nine-thirty clock time'], + 'clock1030' => ['unicode' => '1F565', 'shortname' => ':clock1030:', 'aliases' => '', 'keywords' => 'clock face ten-thirty clock time'], + 'clock1130' => ['unicode' => '1F566', 'shortname' => ':clock1130:', 'aliases' => '', 'keywords' => 'clock face eleven-thirty clock time'], + 'clock1230' => ['unicode' => '1F567', 'shortname' => ':clock1230:', 'aliases' => '', 'keywords' => 'clock face twelve-thirty clock time'] + ]; + } + + /** + * Get the emoji list to include in chat + * @return array + */ + public static function getEmojisToInclude() + { + return [ + ':bowtie:', + ':smile:'| + ':laughing:', + ':blush:', + ':smiley:', + ':relaxed:', + ':smirk:', + ':heart_eyes:', + ':kissing_heart:', + ':kissing_closed_eyes:', + ':flushed:', + ':relieved:', + ':satisfied:', + ':grin:', + ':wink:', + ':stuck_out_tongue_winking_eye:', + ':stuck_out_tongue_closed_eyes:', + ':grinning:', + ':kissing:', + ':kissing_smiling_eyes:', + ':stuck_out_tongue:', + ':sleeping:', + ':worried:', + ':frowning:', + ':anguished:', + ':open_mouth:', + ':grimacing:', + ':confused:', + ':hushed:', + ':expressionless:', + ':unamused:', + ':sweat_smile:', + ':sweat:', + ':disappointed_relieved:', + ':weary:', + ':pensive:', + ':disappointed:', + ':confounded:', + ':fearful:', + ':cold_sweat:', + ':persevere:', + ':cry:', + ':sob:', + ':joy:', + ':astonished:', + ':scream:', + ':neckbeard:', + ':tired_face:', + ':angry:', + ':rage:', + ':triumph:', + ':sleepy:', + ':yum:', + ':mask:', + ':sunglasses:', + ':dizzy_face:', + ':imp:', + ':smiling_imp:', + ':neutral_face:', + ':no_mouth:', + ':innocent:', + ':alien:' + ]; + } + + /** + * Get the chat history file name + * @param bool $absolute Optional. Whether get the base or the absolute file path + * @param int $friendId Optional. + * @return string + */ + public function getFileName($absolute = false, $friendId = 0) + { + $date = date('Y-m-d'); + + $base = 'messages-' . $date . '.log.html'; + + if ($this->groupId && !$friendId) { + $base = 'messages-' . $date . '_gid-' . $this->groupId . '.log.html'; + } elseif ($this->sessionId && !$friendId) { + $base = 'messages-' . $date . '_sid-' . $this->sessionId . '.log.html'; + } elseif ($friendId) { + if ($this->userId < $friendId) { + $base = 'messages-' . $date . '_uid-' . $this->userId . '-' . $friendId . '.log.html'; + } else { + $base = 'messages-' . $date . '_uid-' . $friendId . '-' . $this->userId . '.log.html'; + } + } + + if (!$absolute) { + return $base; + } + + $courseInfo = api_get_course_info_by_id($this->courseId); + $document_path = api_get_path(SYS_COURSE_PATH) . $courseInfo['path'] . '/document'; + + $chatPath = $document_path . '/chat_files/'; + + if ($this->groupId) { + $group_info = GroupManager::get_group_properties($this->groupId); + $chatPath = $document_path . $group_info['directory'] . '/chat_files/'; + } + + return $chatPath . $base; + } + + /** + * Get the chat history + * @param bool $reset + * @param int $friendId Optional. + * @return string + */ + public function readMessages($reset = false, $friendId = 0) + { + $courseInfo = api_get_course_info_by_id($this->courseId); + $date_now = date('Y-m-d'); + $isMaster = (bool)api_is_course_admin(); + $basepath_chat = '/chat_files'; + $document_path = api_get_path(SYS_COURSE_PATH) . $courseInfo['path'] . '/document'; + + if ($this->groupId) { + $group_info = GroupManager:: get_group_properties($this->groupId); + $basepath_chat = $group_info['directory'] . '/chat_files'; + } + + $chat_path = $document_path . $basepath_chat . '/'; + + if (!is_dir($chat_path)) { + if (is_file($chat_path)) { + @unlink($chat_path); + } + + if (!api_is_anonymous()) { + @mkdir($chat_path, api_get_permissions_for_new_directories()); + // Save chat files document for group into item property + if ($this->groupId) { + $doc_id = add_document($courseInfo, $basepath_chat, 'folder', 0, 'chat_files'); + api_item_property_update( + $courseInfo, + TOOL_DOCUMENT, + $doc_id, + 'FolderCreated', + null, + $this->groupId, + null, + null, + null + ); + } + } + } + + $filename_chat = 'messages-' . $date_now . '.log.html'; + + if ($this->groupId && !$friendId) { + $filename_chat = 'messages-' . $date_now . '_gid-' . $this->groupId . '.log.html'; + } else if ($this->sessionId && !$friendId) { + $filename_chat = 'messages-' . $date_now . '_sid-' . $this->sessionId . '.log.html'; + } elseif ($friendId) { + if ($this->userId < $friendId) { + $filename_chat = 'messages-' . $date_now . '_uid-' . $this->userId . '-' . $friendId . '.log.html'; + } else { + $filename_chat = 'messages-' . $date_now . '_uid-' . $friendId . '-' . $this->userId . '.log.html'; + } + } + + if (!file_exists($chat_path . $filename_chat)) { + @fclose(fopen($chat_path . $filename_chat, 'w')); + if (!api_is_anonymous()) { + $doc_id = add_document($courseInfo, $basepath_chat . '/' . $filename_chat, 'file', 0, $filename_chat); + api_item_property_update( + $courseInfo, + TOOL_DOCUMENT, + $doc_id, + 'DocumentAdded', + $this->userId, + $this->groupId, + null, + null, + null, + $this->sessionId + ); + api_item_property_update( + $courseInfo, + TOOL_DOCUMENT, + $doc_id, + 'invisible', + $this->userId, + $this->groupId, + null, + null, + null, + $this->sessionId + ); + item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId); + } + } + + $basename_chat = 'messages-' . $date_now; + + if ($this->groupId && !$friendId) { + $basename_chat = 'messages-' . $date_now . '_gid-' . $this->groupId; + } else if ($this->sessionId && !$friendId) { + $basename_chat = 'messages-' . $date_now . '_sid-' . $this->sessionId; + } elseif ($friendId) { + if ($this->userId < $friendId) { + $basename_chat = 'messages-' . $date_now . '_uid-' . $this->userId . '-' . $friendId; + } else { + $basename_chat = 'messages-' . $date_now . '_uid-' . $friendId . '-' . $this->userId; + } + } + + if ($reset && $isMaster) { + + $i = 1; + + while (file_exists($chat_path . $basename_chat . '-' . $i . '.log.html')) { + $i++; + } + + @rename($chat_path . $basename_chat . '.log.html', $chat_path . $basename_chat . '-' . $i . '.log.html'); + @fclose(fopen($chat_path . $basename_chat . '.log.html', 'w')); + + $doc_id = add_document( + $courseInfo, + $basepath_chat . '/' . $basename_chat . '-' . $i . '.log.html', + 'file', + filesize($chat_path . $basename_chat . '-' . $i . '.log.html'), + $basename_chat . '-' . $i . '.log.html' + ); + + api_item_property_update( + $courseInfo, + TOOL_DOCUMENT, + $doc_id, + 'DocumentAdded', + $this->userId, + $this->groupId, + null, + null, + null, + $this->sessionId + ); + api_item_property_update( + $courseInfo, + TOOL_DOCUMENT, + $doc_id, + 'invisible', + $this->userId, + $this->groupId, + null, + null, + null, + $this->sessionId + ); + item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId); + + $doc_id = DocumentManager::get_document_id( + $courseInfo, + $basepath_chat . '/' . $basename_chat . '.log.html' + ); + + update_existing_document($courseInfo, $doc_id, 0); + } + + $remove = 0; + $content = array(); + + if (file_exists($chat_path . $basename_chat . '.log.html')) { + $content = file($chat_path . $basename_chat . '.log.html'); + $nbr_lines = sizeof($content); + $remove = $nbr_lines - 100; + } + + if ($remove < 0) { + $remove = 0; + } + + array_splice($content, 0, $remove); + + if (isset($_GET['origin']) && $_GET['origin'] == 'whoisonline') { + //the caller + $content[0] = get_lang('CallSent') . '
' . $content[0]; + } + if (isset($_GET['origin']) && $_GET['origin'] == 'whoisonlinejoin') { + //the joiner (we have to delete the chat request to him when he joins the chat) + Database::getManager() + ->createQuery(' + UPDATE ChamiloUserBundle:User u + SET u.chatcallUserId = NULL, u.chatcallDate = NULL, u.chatcallText = NULL + WHERE u.id = :user + ') + ->execute(['user' => $this->userId]); + } + + $history = '
'; + + foreach ($content as $this_line) { + $history .= $this_line; + } + + $history .= '
'; + + if ($isMaster || $GLOBALS['is_courseCoach']) { + $history .= ' +
+ +
+ '; + } + + return $history; + } + + /** + * Get the number of users connected in chat + * @return mixed + */ + public function countUsersOnline() + { + $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC')); + $date->modify('-5 seconds'); + + $extraCondition = null; + + if ($this->groupId) { + $extraCondition = 'AND ccc.toGroupId = ' . intval($this->groupId); + } else { + $extraCondition = 'AND ccc.sessionId = ' . intval($this->sessionId); + } + + $number = Database::getManager() + ->createQuery(" + SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc + WHERE ccc.lastConnection > :date AND ccc.cId = :course $extraCondition + ") + ->setParameters([ + 'date' => $date, + 'course' => $this->courseId + ]) + ->getSingleScalarResult(); + + return intval($number); + } + + /** + * Check if a user is connected in course chat + * @param int $userId + * @return int + */ + private function userIsConnected($userId) + { + $date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC')); + $date->modify('-5 seconds'); + + $extraCondition = null; + + if ($this->groupId) { + $extraCondition = 'AND ccc.toGroupId = ' . intval($this->groupId); + } else { + $extraCondition = 'AND ccc.sessionId = ' . intval($this->sessionId); + } + + $number = Database::getManager() + ->createQuery(" + SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc + WHERE ccc.lastConnection > :date AND ccc.cId = :course AND ccc.userId = :user $extraCondition + ") + ->setParameters([ + 'date' => $date, + 'course' => $this->courseId, + 'user' => $userId + ]) + ->getSingleScalarResult(); + + return intval($number); + } + + /** + * Get the users online data + * @return string + */ + public function listUsersOnline() + { + $subscriptions = $this->getUsersSubscriptions(); + $usersInfo = []; + + foreach ($subscriptions as $subscription) { + $user = $subscription->getUser(); + + $usersInfo[] = [ + 'id' => $user->getId(), + 'firstname' => $user->getFirstname(), + 'lastname' => $user->getLastname(), + 'status' => !$this->sessionId ? $subscription->getStatus() : $user->getStatus(), + 'image_url' => UserManager::getUserPicture($user->getId(), USER_IMAGE_SIZE_MEDIUM), + 'profile_url' => api_get_path(WEB_CODE_PATH) . 'social/profile.php?u=' . $user->getId(), + 'complete_name' => $user->getCompleteName(), + 'username' => $user->getUsername(), + 'email' => $user->getEmail(), + 'isConnected' => $this->userIsConnected($user->getId()) + ]; + } + + return $usersInfo; + } +} diff --git a/main/inc/lib/document.lib.php b/main/inc/lib/document.lib.php index 8d47bf16eb..d5d52c7a78 100755 --- a/main/inc/lib/document.lib.php +++ b/main/inc/lib/document.lib.php @@ -517,7 +517,7 @@ class DocumentManager if (!empty($sessionId)) { // Chat folder filter if ($path == '/chat_files') { - $condition .= " AND (id_session = '$sessionId') "; + $condition .= " AND (docs.session_id = '$sessionId') "; } // share_folder filter $condition .= " AND docs.path != '/shared_folder' "; diff --git a/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.js b/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.js index 47304c3e5c..770c2b9070 100644 --- a/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.js +++ b/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.js @@ -1,553 +1,1483 @@ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof module === "object" && module.exports) { + var $ = require('jquery'); + module.exports = factory($); + } else { + // Browser globals + factory(jQuery); + } +}(function (jQuery) { + /*! - * jQuery.textcomplete.js + * jQuery.textcomplete * - * Repositiory: https://github.com/yuku-t/jquery-textcomplete - * License: MIT - * Author: Yuku Takahashi + * Repository: https://github.com/yuku-t/jquery-textcomplete + * License: MIT (https://github.com/yuku-t/jquery-textcomplete/blob/master/LICENSE) + * Author: Yuku Takahashi */ -;(function ($) { - - 'use strict'; - - /** - * Exclusive execution control utility. - */ - var lock = function (func) { - var free, locked; - free = function () { locked = false; }; - return function () { - var args; - if (locked) return; - locked = true; - args = toArray(arguments); - args.unshift(free); - func.apply(this, args); - }; - }; +if (typeof jQuery === 'undefined') { + throw new Error('jQuery.textcomplete requires jQuery'); +} - /** - * Convert arguments into a real array. - */ - var toArray = function (args) { - var result; - result = Array.prototype.slice.call(args); - return result; - }; ++function ($) { + 'use strict'; - /** - * Get the styles of any element from property names. - */ - var getStyles = (function () { - var color; - color = $('
').css(['color']).color; - if (typeof color !== 'undefined') { - return function ($el, properties) { - return $el.css(properties); - }; - } else { // for jQuery 1.8 or below - return function ($el, properties) { - var styles; - styles = {}; - $.each(properties, function (i, property) { - styles[property] = $el.css(property); - }); - return styles; - }; + var warn = function (message) { + if (console.warn) { console.warn(message); } + }; + + var id = 1; + + $.fn.textcomplete = function (strategies, option) { + var args = Array.prototype.slice.call(arguments); + return this.each(function () { + var self = this; + var $this = $(this); + var completer = $this.data('textComplete'); + if (!completer) { + option || (option = {}); + option._oid = id++; // unique object id + completer = new $.fn.textcomplete.Completer(this, option); + $this.data('textComplete', completer); + } + if (typeof strategies === 'string') { + if (!completer) return; + args.shift() + completer[strategies].apply(completer, args); + if (strategies === 'destroy') { + $this.removeData('textComplete'); } - })(); - - /** - * Default template function. - */ - var identity = function (obj) { return obj; }; - - /** - * Memoize a search function. - */ - var memoize = function (func) { - var memo = {}; - return function (term, callback) { - if (memo[term]) { - callback(memo[term]); - } else { - func.call(this, term, function (data) { - memo[term] = (memo[term] || []).concat(data); - callback.apply(null, arguments); - }); + } else { + // For backward compatibility. + // TODO: Remove at v0.4 + $.each(strategies, function (obj) { + $.each(['header', 'footer', 'placement', 'maxCount'], function (name) { + if (obj[name]) { + completer.option[name] = obj[name]; + warn(name + 'as a strategy param is deprecated. Use option.'); + delete obj[name]; } - }; - }; + }); + }); + completer.register($.fn.textcomplete.Strategy.parse(strategies, { + el: self, + $el: $this + })); + } + }); + }; + +}(jQuery); + ++function ($) { + 'use strict'; + + // Exclusive execution control utility. + // + // func - The function to be locked. It is executed with a function named + // `free` as the first argument. Once it is called, additional + // execution are ignored until the free is invoked. Then the last + // ignored execution will be replayed immediately. + // + // Examples + // + // var lockedFunc = lock(function (free) { + // setTimeout(function { free(); }, 1000); // It will be free in 1 sec. + // console.log('Hello, world'); + // }); + // lockedFunc(); // => 'Hello, world' + // lockedFunc(); // none + // lockedFunc(); // none + // // 1 sec past then + // // => 'Hello, world' + // lockedFunc(); // => 'Hello, world' + // lockedFunc(); // none + // + // Returns a wrapped function. + var lock = function (func) { + var locked, queuedArgsToReplay; - /** - * Determine if the array contains a given value. - */ - var include = function (array, value) { - var i, l; - if (array.indexOf) return array.indexOf(value) != -1; - for (i = 0, l = array.length; i < l; i++) { - if (array[i] === value) return true; + return function () { + // Convert arguments into a real array. + var args = Array.prototype.slice.call(arguments); + if (locked) { + // Keep a copy of this argument list to replay later. + // OK to overwrite a previous value because we only replay + // the last one. + queuedArgsToReplay = args; + return; + } + locked = true; + var self = this; + args.unshift(function replayOrFree() { + if (queuedArgsToReplay) { + // Other request(s) arrived while we were locked. + // Now that the lock is becoming available, replay + // the latest such request, then call back here to + // unlock (or replay another request that arrived + // while this one was in flight). + var replayArgs = queuedArgsToReplay; + queuedArgsToReplay = undefined; + replayArgs.unshift(replayOrFree); + func.apply(self, replayArgs); + } else { + locked = false; } - return false; + }); + func.apply(this, args); }; + }; - /** - * Textarea manager class. - */ - var Completer = (function () { - var html, css, $baseWrapper, $baseList, _id; + var isString = function (obj) { + return Object.prototype.toString.call(obj) === '[object String]'; + }; - html = { - wrapper: '
', - list: '' - }; - css = { - wrapper: { - position: 'relative' - }, - list: { - position: 'absolute', - top: 0, - left: 0, - zIndex: '100', - display: 'none' - } - }; - $baseWrapper = $(html.wrapper).css(css.wrapper); - $baseList = $(html.list).css(css.list); - _id = 0; - - function Completer($el) { - var focus; - this.el = $el.get(0); // textarea element - focus = this.el === document.activeElement; - // Cannot wrap $el at initialize method lazily due to Firefox's behavior. - this.$el = wrapElement($el); // Focus is lost - this.id = 'textComplete' + _id++; - this.strategies = []; - if (focus) { - this.initialize(); - this.$el.focus(); - } else { - this.$el.one('focus.textComplete', $.proxy(this.initialize, this)); + var isFunction = function (obj) { + return Object.prototype.toString.call(obj) === '[object Function]'; + }; + + var uniqueId = 0; + + function Completer(element, option) { + this.$el = $(element); + this.id = 'textcomplete' + uniqueId++; + this.strategies = []; + this.views = []; + this.option = $.extend({}, Completer._getDefaults(), option); + + if (!this.$el.is('input[type=text]') && !this.$el.is('input[type=search]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') { + throw new Error('textcomplete must be called on a Textarea or a ContentEditable.'); + } + + // use ownerDocument to fix iframe / IE issues + if (element === element.ownerDocument.activeElement) { + // element has already been focused. Initialize view objects immediately. + this.initialize() + } else { + // Initialize view objects lazily. + var self = this; + this.$el.one('focus.' + this.id, function () { self.initialize(); }); + + // Special handling for CKEditor: lazy init on instance load + if ((!this.option.adapter || this.option.adapter == 'CKEditor') && typeof CKEDITOR != 'undefined' && (this.$el.is('textarea'))) { + CKEDITOR.on("instanceReady", function(event) { + event.editor.once("focus", function(event2) { + // replace the element with the Iframe element and flag it as CKEditor + self.$el = $(event.editor.editable().$); + if (!self.option.adapter) { + self.option.adapter = $.fn.textcomplete['CKEditor']; } + self.initialize(); + }); + }); + } + } + } + + Completer._getDefaults = function () { + if (!Completer.DEFAULTS) { + Completer.DEFAULTS = { + appendTo: $('body'), + className: '', // deprecated option + dropdownClassName: 'dropdown-menu textcomplete-dropdown', + maxCount: 10, + zIndex: '100' + }; + } + + return Completer.DEFAULTS; + } + + $.extend(Completer.prototype, { + // Public properties + // ----------------- + + id: null, + option: null, + strategies: null, + adapter: null, + dropdown: null, + $el: null, + $iframe: null, + + // Public methods + // -------------- + + initialize: function () { + var element = this.$el.get(0); + + // check if we are in an iframe + // we need to alter positioning logic if using an iframe + if (this.$el.prop('ownerDocument') !== document && window.frames.length) { + for (var iframeIndex = 0; iframeIndex < window.frames.length; iframeIndex++) { + if (this.$el.prop('ownerDocument') === window.frames[iframeIndex].document) { + this.$iframe = $(window.frames[iframeIndex].frameElement); + break; + } } + } + + + // Initialize view objects. + this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option); + var Adapter, viewName; + if (this.option.adapter) { + Adapter = this.option.adapter; + } else { + if (this.$el.is('textarea') || this.$el.is('input[type=text]') || this.$el.is('input[type=search]')) { + viewName = typeof element.selectionEnd === 'number' ? 'Textarea' : 'IETextarea'; + } else { + viewName = 'ContentEditable'; + } + Adapter = $.fn.textcomplete[viewName]; + } + this.adapter = new Adapter(element, this, this.option); + }, - /** - * Completer's public methods - */ - $.extend(Completer.prototype, { - - /** - * Prepare ListView and bind events. - */ - initialize: function () { - var $list, globalEvents; - $list = $baseList.clone(); - this.listView = new ListView($list, this); - this.$el - .before($list) - .on({ - 'keyup.textComplete': $.proxy(this.onKeyup, this), - 'keydown.textComplete': $.proxy(this.listView.onKeydown, - this.listView) - }); - globalEvents = {}; - globalEvents['click.' + this.id] = $.proxy(this.onClickDocument, this); - globalEvents['keyup.' + this.id] = $.proxy(this.onKeyupDocument, this); - $(document).on(globalEvents); - }, - - /** - * Register strategies to the completer. - */ - register: function (strategies) { - this.strategies = this.strategies.concat(strategies); - }, - - /** - * Show autocomplete list next to the caret. - */ - renderList: function (data) { - if (this.clearAtNext) { - this.listView.clear(); - this.clearAtNext = false; - } - if (data.length) { - if (!this.listView.shown) { - this.listView - .setPosition(this.getCaretPosition()) - .clear() - .activate(); - this.listView.strategy = this.strategy; - } - data = data.slice(0, this.strategy.maxCount); - this.listView.render(data); - } - - if (!this.listView.data.length && this.listView.shown) { - this.listView.deactivate(); - } - }, - - searchCallbackFactory: function (free) { - var self = this; - return function (data, keep) { - self.renderList(data); - if (!keep) { - // This is the last callback for this search. - free(); - self.clearAtNext = true; - } - }; - }, - - /** - * Keyup event handler. - */ - onKeyup: function (e) { - var searchQuery, term; - if (this.skipSearch(e)) { return; } - - searchQuery = this.extractSearchQuery(this.getTextFromHeadToCaret()); - if (searchQuery.length) { - term = searchQuery[1]; - if (this.term === term) return; // Ignore shift-key or something. - this.term = term; - this.search(searchQuery); - } else { - this.term = null; - this.listView.deactivate(); - } - }, - - /** - * Suppress searching if it returns true. - */ - skipSearch: function (e) { - if (this.skipNextKeyup) { - this.skipNextKeyup = false; - return true; - } - switch (e.keyCode) { - case 40: - case 38: - return true; - } - }, - - onSelect: function (value) { - var pre, post, newSubStr; - pre = this.getTextFromHeadToCaret(); - post = this.el.value.substring(this.el.selectionEnd); - - newSubStr = this.strategy.replace(value); - if ($.isArray(newSubStr)) { - post = newSubStr[1] + post; - newSubStr = newSubStr[0]; - } - pre = pre.replace(this.strategy.match, newSubStr); - this.$el.val(pre + post) - .trigger('change') - .trigger('textComplete:select', value); - this.el.focus(); - this.el.selectionStart = this.el.selectionEnd = pre.length; - this.skipNextKeyup = true; - }, - - /** - * Global click event handler. - */ - onClickDocument: function (e) { - if (e.originalEvent && !e.originalEvent.keepTextCompleteDropdown) { - this.listView.deactivate(); - } - }, - - /** - * Global keyup event handler. - */ - onKeyupDocument: function (e) { - if (this.listView.shown && e.keyCode === 27) { // ESC - this.listView.deactivate(); - this.$el.focus(); - } - }, - - /** - * Remove all event handlers and the wrapper element. - */ - destroy: function () { - var $wrapper; - this.$el.off('.textComplete'); - $(document).off('.' + this.id); - if (this.listView) { this.listView.destroy(); } - $wrapper = this.$el.parent(); - $wrapper.after(this.$el).remove(); - this.$el.data('textComplete', void 0); - this.$el = null; - }, - - // Helper methods - // ============== - - /** - * Returns caret's relative coordinates from textarea's left top corner. - */ - getCaretPosition: function () { - // Browser native API does not provide the way to know the position of - // caret in pixels, so that here we use a kind of hack to accomplish - // the aim. First of all it puts a div element and completely copies - // the textarea's style to the element, then it inserts the text and a - // span element into the textarea. - // Consequently, the span element's position is the thing what we want. - - if (this.el.selectionEnd === 0) return; - var properties, css, $div, $span, position, dir; - - dir = this.$el.attr('dir') || this.$el.css('direction'); - properties = ['border-width', 'font-family', 'font-size', 'font-style', - 'font-variant', 'font-weight', 'height', 'letter-spacing', - 'word-spacing', 'line-height', 'text-decoration', 'text-align', - 'width', 'padding-top', 'padding-right', 'padding-bottom', - 'padding-left', 'margin-top', 'margin-right', 'margin-bottom', - 'margin-left' - ]; - css = $.extend({ - position: 'absolute', - overflow: 'auto', - 'white-space': 'pre-wrap', - top: 0, - left: -9999, - direction: dir - }, getStyles(this.$el, properties)); - - $div = $('
').css(css).text(this.getTextFromHeadToCaret()); - $span = $('').text('.').appendTo($div); - this.$el.before($div); - position = $span.position(); - position.top += $span.height() - this.$el.scrollTop(); - if (dir === 'rtl') { position.left -= this.listView.$el.width(); } - $div.remove(); - return position; - }, - - getTextFromHeadToCaret: function () { - var text, selectionEnd, range; - selectionEnd = this.el.selectionEnd; - if (typeof selectionEnd === 'number') { - text = this.el.value.substring(0, selectionEnd); - } else if (document.selection) { - range = this.el.createTextRange(); - range.moveStart('character', 0); - range.moveEnd('textedit'); - text = range.text; - } - return text; - }, - - /** - * Parse the value of textarea and extract search query. - */ - extractSearchQuery: function (text) { - // If a search query found, it returns used strategy and the query - // term. If the caret is currently in a code block or search query does - // not found, it returns an empty array. - - var i, l, strategy, match; - for (i = 0, l = this.strategies.length; i < l; i++) { - strategy = this.strategies[i]; - match = text.match(strategy.match); - if (match) { return [strategy, match[strategy.index]]; } - } - return []; - }, - - search: lock(function (free, searchQuery) { - var term; - this.strategy = searchQuery[0]; - term = searchQuery[1]; - this.strategy.search(term, this.searchCallbackFactory(free)); - }) - }); + destroy: function () { + this.$el.off('.' + this.id); + if (this.adapter) { + this.adapter.destroy(); + } + if (this.dropdown) { + this.dropdown.destroy(); + } + this.$el = this.adapter = this.dropdown = null; + }, - /** - * Completer's private functions - */ - var wrapElement = function ($el) { - return $el.wrap($baseWrapper.clone().css('display', $el.css('display'))); - }; + deactivate: function () { + if (this.dropdown) { + this.dropdown.deactivate(); + } + }, + + // Invoke textcomplete. + trigger: function (text, skipUnchangedTerm) { + if (!this.dropdown) { this.initialize(); } + text != null || (text = this.adapter.getTextFromHeadToCaret()); + var searchQuery = this._extractSearchQuery(text); + if (searchQuery.length) { + var term = searchQuery[1]; + // Ignore shift-key, ctrl-key and so on. + if (skipUnchangedTerm && this._term === term && term !== "") { return; } + this._term = term; + this._search.apply(this, searchQuery); + } else { + this._term = null; + this.dropdown.deactivate(); + } + }, + + fire: function (eventName) { + var args = Array.prototype.slice.call(arguments, 1); + this.$el.trigger(eventName, args); + return this; + }, - return Completer; - })(); + register: function (strategies) { + Array.prototype.push.apply(this.strategies, strategies); + }, - /** - * Dropdown menu manager class. - */ - var ListView = (function () { + // Insert the value into adapter view. It is called when the dropdown is clicked + // or selected. + // + // value - The selected element of the array callbacked from search func. + // strategy - The Strategy object. + // e - Click or keydown event object. + select: function (value, strategy, e) { + this._term = null; + this.adapter.select(value, strategy, e); + this.fire('change').fire('textComplete:select', value, strategy); + this.adapter.focus(); + }, - function ListView($el, completer) { - this.data = []; - this.$el = $el; - this.index = 0; - this.completer = completer; + // Private properties + // ------------------ - this.$el.on('click.textComplete', 'li.textcomplete-item', - $.proxy(this.onClick, this)); + _clearAtNext: true, + _term: null, + + // Private methods + // --------------- + + // Parse the given text and extract the first matching strategy. + // + // Returns an array including the strategy, the query term and the match + // object if the text matches an strategy; otherwise returns an empty array. + _extractSearchQuery: function (text) { + for (var i = 0; i < this.strategies.length; i++) { + var strategy = this.strategies[i]; + var context = strategy.context(text); + if (context || context === '') { + var matchRegexp = isFunction(strategy.match) ? strategy.match(text) : strategy.match; + if (isString(context)) { text = context; } + var match = text.match(matchRegexp); + if (match) { return [strategy, match[strategy.index], match]; } } + } + return [] + }, - $.extend(ListView.prototype, { - shown: false, - - render: function (data) { - var html, i, l, index, val; - - html = ''; - for (i = 0, l = data.length; i < l; i++) { - val = data[i]; - if (include(this.data, val)) continue; - index = this.data.length; - this.data.push(val); - html += '
  • '; - html += this.strategy.template(val); - html += '
  • '; - if (this.data.length === this.strategy.maxCount) break; - } - this.$el.append(html); - if (!this.data.length) { - this.deactivate(); - } else { - this.activateIndexedItem(); - } - }, - - clear: function () { - this.data = []; - this.$el.html(''); - this.index = 0; - return this; - }, - - activateIndexedItem: function () { - this.$el.find('.active').removeClass('active'); - this.getActiveItem().addClass('active'); - }, - - getActiveItem: function () { - return $(this.$el.children().get(this.index)); - }, - - activate: function () { - if (!this.shown) { - this.$el.show(); - this.completer.$el.trigger('textComplete:show'); - this.shown = true; - } - return this; - }, - - deactivate: function () { - if (this.shown) { - this.$el.hide(); - this.completer.$el.trigger('textComplete:hide'); - this.shown = false; - this.data = this.index = null; - } - return this; - }, - - setPosition: function (position) { - this.$el.css(position); - return this; - }, - - select: function (index) { - var self = this; - this.completer.onSelect(this.data[index]); - // Deactive at next tick to allow other event handlers to know whether - // the dropdown has been shown or not. - setTimeout(function () { self.deactivate(); }, 0); - }, - - onKeydown: function (e) { - if (!this.shown) return; - if (e.keyCode === 38) { // UP - e.preventDefault(); - if (this.index === 0) { - this.index = this.data.length-1; - } else { - this.index -= 1; - } - this.activateIndexedItem(); - } else if (e.keyCode === 40) { // DOWN - e.preventDefault(); - if (this.index === this.data.length - 1) { - this.index = 0; - } else { - this.index += 1; - } - this.activateIndexedItem(); - } else if (e.keyCode === 13 || e.keyCode === 9) { // ENTER or TAB - e.preventDefault(); - this.select(parseInt(this.getActiveItem().data('index'), 10)); - } - }, - - onClick: function (e) { - var $e = $(e.target); - e.originalEvent.keepTextCompleteDropdown = true; - if (!$e.hasClass('textcomplete-item')) { - $e = $e.parents('li.textcomplete-item'); - } - this.select(parseInt($e.data('index'), 10)); - }, - - destroy: function () { - this.deactivate(); - this.$el.off('click.textComplete').remove(); - this.$el = null; - } - }); + // Call the search method of selected strategy.. + _search: lock(function (free, strategy, term, match) { + var self = this; + strategy.search(term, function (data, stillSearching) { + if (!self.dropdown.shown) { + self.dropdown.activate(); + } + if (self._clearAtNext) { + // The first callback in the current lock. + self.dropdown.clear(); + self._clearAtNext = false; + } + self.dropdown.setPosition(self.adapter.getCaretPosition()); + self.dropdown.render(self._zip(data, strategy, term)); + if (!stillSearching) { + // The last callback in the current lock. + free(); + self._clearAtNext = true; // Call dropdown.clear at the next time. + } + }, match); + }), - return ListView; - })(); + // Build a parameter for Dropdown#render. + // + // Examples + // + // this._zip(['a', 'b'], 's'); + // //=> [{ value: 'a', strategy: 's' }, { value: 'b', strategy: 's' }] + _zip: function (data, strategy, term) { + return $.map(data, function (value) { + return { value: value, strategy: strategy, term: term }; + }); + } + }); - $.fn.textcomplete = function (strategies) { - var i, l, strategy, dataKey; + $.fn.textcomplete.Completer = Completer; +}(jQuery); - dataKey = 'textComplete'; ++function ($) { + 'use strict'; - if (strategies === 'destroy') { - return this.each(function () { - var completer = $(this).data(dataKey); - if (completer) { completer.destroy(); } - }); + var $window = $(window); + + var include = function (zippedData, datum) { + var i, elem; + var idProperty = datum.strategy.idProperty + for (i = 0; i < zippedData.length; i++) { + elem = zippedData[i]; + if (elem.strategy !== datum.strategy) continue; + if (idProperty) { + if (elem.value[idProperty] === datum.value[idProperty]) return true; + } else { + if (elem.value === datum.value) return true; + } + } + return false; + }; + + var dropdownViews = {}; + $(document).on('click', function (e) { + var id = e.originalEvent && e.originalEvent.keepTextCompleteDropdown; + $.each(dropdownViews, function (key, view) { + if (key !== id) { view.deactivate(); } + }); + }); + + var commands = { + SKIP_DEFAULT: 0, + KEY_UP: 1, + KEY_DOWN: 2, + KEY_ENTER: 3, + KEY_PAGEUP: 4, + KEY_PAGEDOWN: 5, + KEY_ESCAPE: 6 + }; + + // Dropdown view + // ============= + + // Construct Dropdown object. + // + // element - Textarea or contenteditable element. + function Dropdown(element, completer, option) { + this.$el = Dropdown.createElement(option); + this.completer = completer; + this.id = completer.id + 'dropdown'; + this._data = []; // zipped data. + this.$inputEl = $(element); + this.option = option; + + // Override setPosition method. + if (option.listPosition) { this.setPosition = option.listPosition; } + if (option.height) { this.$el.height(option.height); } + var self = this; + $.each(['maxCount', 'placement', 'footer', 'header', 'noResultsMessage', 'className'], function (_i, name) { + if (option[name] != null) { self[name] = option[name]; } + }); + this._bindEvents(element); + dropdownViews[this.id] = this; + } + + $.extend(Dropdown, { + // Class methods + // ------------- + + createElement: function (option) { + var $parent = option.appendTo; + if (!($parent instanceof $)) { $parent = $($parent); } + var $el = $('') + .addClass(option.dropdownClassName) + .attr('id', 'textcomplete-dropdown-' + option._oid) + .css({ + display: 'none', + left: 0, + position: 'absolute', + zIndex: option.zIndex + }) + .appendTo($parent); + return $el; + } + }); + + $.extend(Dropdown.prototype, { + // Public properties + // ----------------- + + $el: null, // jQuery object of ul.dropdown-menu element. + $inputEl: null, // jQuery object of target textarea. + completer: null, + footer: null, + header: null, + id: null, + maxCount: null, + placement: '', + shown: false, + data: [], // Shown zipped data. + className: '', + + // Public methods + // -------------- + + destroy: function () { + // Don't remove $el because it may be shared by several textcompletes. + this.deactivate(); + + this.$el.off('.' + this.id); + this.$inputEl.off('.' + this.id); + this.clear(); + this.$el.remove(); + this.$el = this.$inputEl = this.completer = null; + delete dropdownViews[this.id] + }, + + render: function (zippedData) { + var contentsHtml = this._buildContents(zippedData); + var unzippedData = $.map(this.data, function (d) { return d.value; }); + if (this.data.length) { + var strategy = zippedData[0].strategy; + if (strategy.id) { + this.$el.attr('data-strategy', strategy.id); + } else { + this.$el.removeAttr('data-strategy'); } + this._renderHeader(unzippedData); + this._renderFooter(unzippedData); + if (contentsHtml) { + this._renderContents(contentsHtml); + this._fitToBottom(); + this._fitToRight(); + this._activateIndexedItem(); + } + this._setScroll(); + } else if (this.noResultsMessage) { + this._renderNoResultsMessage(unzippedData); + } else if (this.shown) { + this.deactivate(); + } + }, - for (i = 0, l = strategies.length; i < l; i++) { - strategy = strategies[i]; - if (!strategy.template) { - strategy.template = identity; - } - if (strategy.index == null) { - strategy.index = 2; - } - if (strategy.cache) { - strategy.search = memoize(strategy.search); - } - strategy.maxCount || (strategy.maxCount = 10); + setPosition: function (pos) { + // Make the dropdown fixed if the input is also fixed + // This can't be done during init, as textcomplete may be used on multiple elements on the same page + // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed + var position = 'absolute'; + // Check if input or one of its parents has positioning we need to care about + this.$inputEl.add(this.$inputEl.parents()).each(function() { + if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK + return false; + if($(this).css('position') === 'fixed') { + pos.top -= $window.scrollTop(); + pos.left -= $window.scrollLeft(); + position = 'fixed'; + return false; } + }); + this.$el.css(this._applyPlacement(pos)); + this.$el.css({ position: position }); // Update positioning - return this.each(function () { - var $this, completer; - $this = $(this); - completer = $this.data(dataKey); - if (!completer) { - completer = new Completer($this); - $this.data(dataKey, completer); - } - completer.register(strategies); + return this; + }, + + clear: function () { + this.$el.html(''); + this.data = []; + this._index = 0; + this._$header = this._$footer = this._$noResultsMessage = null; + }, + + activate: function () { + if (!this.shown) { + this.clear(); + this.$el.show(); + if (this.className) { this.$el.addClass(this.className); } + this.completer.fire('textComplete:show'); + this.shown = true; + } + return this; + }, + + deactivate: function () { + if (this.shown) { + this.$el.hide(); + if (this.className) { this.$el.removeClass(this.className); } + this.completer.fire('textComplete:hide'); + this.shown = false; + } + return this; + }, + + isUp: function (e) { + return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80); // UP, Ctrl-P + }, + + isDown: function (e) { + return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78); // DOWN, Ctrl-N + }, + + isEnter: function (e) { + var modifiers = e.ctrlKey || e.altKey || e.metaKey || e.shiftKey; + return !modifiers && (e.keyCode === 13 || e.keyCode === 9 || (this.option.completeOnSpace === true && e.keyCode === 32)) // ENTER, TAB + }, + + isPageup: function (e) { + return e.keyCode === 33; // PAGEUP + }, + + isPagedown: function (e) { + return e.keyCode === 34; // PAGEDOWN + }, + + isEscape: function (e) { + return e.keyCode === 27; // ESCAPE + }, + + // Private properties + // ------------------ + + _data: null, // Currently shown zipped data. + _index: null, + _$header: null, + _$noResultsMessage: null, + _$footer: null, + + // Private methods + // --------------- + + _bindEvents: function () { + this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)); + this.$el.on('touchstart.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)); + this.$el.on('mouseover.' + this.id, '.textcomplete-item', $.proxy(this._onMouseover, this)); + this.$inputEl.on('keydown.' + this.id, $.proxy(this._onKeydown, this)); + }, + + _onClick: function (e) { + var $el = $(e.target); + e.preventDefault(); + e.originalEvent.keepTextCompleteDropdown = this.id; + if (!$el.hasClass('textcomplete-item')) { + $el = $el.closest('.textcomplete-item'); + } + var datum = this.data[parseInt($el.data('index'), 10)]; + this.completer.select(datum.value, datum.strategy, e); + var self = this; + // Deactive at next tick to allow other event handlers to know whether + // the dropdown has been shown or not. + setTimeout(function () { + self.deactivate(); + if (e.type === 'touchstart') { + self.$inputEl.focus(); + } + }, 0); + }, + + // Activate hovered item. + _onMouseover: function (e) { + var $el = $(e.target); + e.preventDefault(); + if (!$el.hasClass('textcomplete-item')) { + $el = $el.closest('.textcomplete-item'); + } + this._index = parseInt($el.data('index'), 10); + this._activateIndexedItem(); + }, + + _onKeydown: function (e) { + if (!this.shown) { return; } + + var command; + + if ($.isFunction(this.option.onKeydown)) { + command = this.option.onKeydown(e, commands); + } + + if (command == null) { + command = this._defaultKeydown(e); + } + + switch (command) { + case commands.KEY_UP: + e.preventDefault(); + this._up(); + break; + case commands.KEY_DOWN: + e.preventDefault(); + this._down(); + break; + case commands.KEY_ENTER: + e.preventDefault(); + this._enter(e); + break; + case commands.KEY_PAGEUP: + e.preventDefault(); + this._pageup(); + break; + case commands.KEY_PAGEDOWN: + e.preventDefault(); + this._pagedown(); + break; + case commands.KEY_ESCAPE: + e.preventDefault(); + this.deactivate(); + break; + } + }, + + _defaultKeydown: function (e) { + if (this.isUp(e)) { + return commands.KEY_UP; + } else if (this.isDown(e)) { + return commands.KEY_DOWN; + } else if (this.isEnter(e)) { + return commands.KEY_ENTER; + } else if (this.isPageup(e)) { + return commands.KEY_PAGEUP; + } else if (this.isPagedown(e)) { + return commands.KEY_PAGEDOWN; + } else if (this.isEscape(e)) { + return commands.KEY_ESCAPE; + } + }, + + _up: function () { + if (this._index === 0) { + this._index = this.data.length - 1; + } else { + this._index -= 1; + } + this._activateIndexedItem(); + this._setScroll(); + }, + + _down: function () { + if (this._index === this.data.length - 1) { + this._index = 0; + } else { + this._index += 1; + } + this._activateIndexedItem(); + this._setScroll(); + }, + + _enter: function (e) { + var datum = this.data[parseInt(this._getActiveElement().data('index'), 10)]; + this.completer.select(datum.value, datum.strategy, e); + this.deactivate(); + }, + + _pageup: function () { + var target = 0; + var threshold = this._getActiveElement().position().top - this.$el.innerHeight(); + this.$el.children().each(function (i) { + if ($(this).position().top + $(this).outerHeight() > threshold) { + target = i; + return false; + } + }); + this._index = target; + this._activateIndexedItem(); + this._setScroll(); + }, + + _pagedown: function () { + var target = this.data.length - 1; + var threshold = this._getActiveElement().position().top + this.$el.innerHeight(); + this.$el.children().each(function (i) { + if ($(this).position().top > threshold) { + target = i; + return false + } + }); + this._index = target; + this._activateIndexedItem(); + this._setScroll(); + }, + + _activateIndexedItem: function () { + this.$el.find('.textcomplete-item.active').removeClass('active'); + this._getActiveElement().addClass('active'); + }, + + _getActiveElement: function () { + return this.$el.children('.textcomplete-item:nth(' + this._index + ')'); + }, + + _setScroll: function () { + var $activeEl = this._getActiveElement(); + var itemTop = $activeEl.position().top; + var itemHeight = $activeEl.outerHeight(); + var visibleHeight = this.$el.innerHeight(); + var visibleTop = this.$el.scrollTop(); + if (this._index === 0 || this._index == this.data.length - 1 || itemTop < 0) { + this.$el.scrollTop(itemTop + visibleTop); + } else if (itemTop + itemHeight > visibleHeight) { + this.$el.scrollTop(itemTop + itemHeight + visibleTop - visibleHeight); + } + }, + + _buildContents: function (zippedData) { + var datum, i, index; + var html = ''; + for (i = 0; i < zippedData.length; i++) { + if (this.data.length === this.maxCount) break; + datum = zippedData[i]; + if (include(this.data, datum)) { continue; } + index = this.data.length; + this.data.push(datum); + html += '
  • '; + html += datum.strategy.template(datum.value, datum.term); + html += '
  • '; + } + return html; + }, + + _renderHeader: function (unzippedData) { + if (this.header) { + if (!this._$header) { + this._$header = $('
  • ').prependTo(this.$el); + } + var html = $.isFunction(this.header) ? this.header(unzippedData) : this.header; + this._$header.html(html); + } + }, + + _renderFooter: function (unzippedData) { + if (this.footer) { + if (!this._$footer) { + this._$footer = $('').appendTo(this.$el); + } + var html = $.isFunction(this.footer) ? this.footer(unzippedData) : this.footer; + this._$footer.html(html); + } + }, + + _renderNoResultsMessage: function (unzippedData) { + if (this.noResultsMessage) { + if (!this._$noResultsMessage) { + this._$noResultsMessage = $('
  • ').appendTo(this.$el); + } + var html = $.isFunction(this.noResultsMessage) ? this.noResultsMessage(unzippedData) : this.noResultsMessage; + this._$noResultsMessage.html(html); + } + }, + + _renderContents: function (html) { + if (this._$footer) { + this._$footer.before(html); + } else { + this.$el.append(html); + } + }, + + _fitToBottom: function() { + var windowScrollBottom = $window.scrollTop() + $window.height(); + var height = this.$el.height(); + if ((this.$el.position().top + height) > windowScrollBottom) { + // only do this if we are not in an iframe + if (!this.completer.$iframe) { + this.$el.offset({top: windowScrollBottom - height}); + } + } + }, + + _fitToRight: function() { + // We don't know how wide our content is until the browser positions us, and at that point it clips us + // to the document width so we don't know if we would have overrun it. As a heuristic to avoid that clipping + // (which makes our elements wrap onto the next line and corrupt the next item), if we're close to the right + // edge, move left. We don't know how far to move left, so just keep nudging a bit. + var tolerance = 30; // pixels. Make wider than vertical scrollbar because we might not be able to use that space. + var lastOffset = this.$el.offset().left, offset; + var width = this.$el.width(); + var maxLeft = $window.width() - tolerance; + while (lastOffset + width > maxLeft) { + this.$el.offset({left: lastOffset - tolerance}); + offset = this.$el.offset().left; + if (offset >= lastOffset) { break; } + lastOffset = offset; + } + }, + + _applyPlacement: function (position) { + // If the 'placement' option set to 'top', move the position above the element. + if (this.placement.indexOf('top') !== -1) { + // Overwrite the position object to set the 'bottom' property instead of the top. + position = { + top: 'auto', + bottom: this.$el.parent().height() - position.top + position.lineHeight, + left: position.left + }; + } else { + position.bottom = 'auto'; + delete position.lineHeight; + } + if (this.placement.indexOf('absleft') !== -1) { + position.left = 0; + } else if (this.placement.indexOf('absright') !== -1) { + position.right = 0; + position.left = 'auto'; + } + return position; + } + }); + + $.fn.textcomplete.Dropdown = Dropdown; + $.extend($.fn.textcomplete, commands); +}(jQuery); + ++function ($) { + 'use strict'; + + // Memoize a search function. + var memoize = function (func) { + var memo = {}; + return function (term, callback) { + if (memo[term]) { + callback(memo[term]); + } else { + func.call(this, term, function (data) { + memo[term] = (memo[term] || []).concat(data); + callback.apply(null, arguments); }); + } }; + }; + + function Strategy(options) { + $.extend(this, options); + if (this.cache) { this.search = memoize(this.search); } + } + + Strategy.parse = function (strategiesArray, params) { + return $.map(strategiesArray, function (strategy) { + var strategyObj = new Strategy(strategy); + strategyObj.el = params.el; + strategyObj.$el = params.$el; + return strategyObj; + }); + }; + + $.extend(Strategy.prototype, { + // Public properties + // ----------------- + + // Required + match: null, + replace: null, + search: null, + + // Optional + id: null, + cache: false, + context: function () { return true; }, + index: 2, + template: function (obj) { return obj; }, + idProperty: null + }); + + $.fn.textcomplete.Strategy = Strategy; + +}(jQuery); + ++function ($) { + 'use strict'; + + var now = Date.now || function () { return new Date().getTime(); }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // `wait` msec. + // + // This utility function was originally implemented at Underscore.js. + var debounce = function (func, wait) { + var timeout, args, context, timestamp, result; + var later = function () { + var last = now() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + result = func.apply(context, args); + context = args = null; + } + }; + + return function () { + context = this; + args = arguments; + timestamp = now(); + if (!timeout) { + timeout = setTimeout(later, wait); + } + return result; + }; + }; + + function Adapter () {} + + $.extend(Adapter.prototype, { + // Public properties + // ----------------- + + id: null, // Identity. + completer: null, // Completer object which creates it. + el: null, // Textarea element. + $el: null, // jQuery object of the textarea. + option: null, + + // Public methods + // -------------- + + initialize: function (element, completer, option) { + this.el = element; + this.$el = $(element); + this.id = completer.id + this.constructor.name; + this.completer = completer; + this.option = option; + + if (this.option.debounce) { + this._onKeyup = debounce(this._onKeyup, this.option.debounce); + } + + this._bindEvents(); + }, + + destroy: function () { + this.$el.off('.' + this.id); // Remove all event handlers. + this.$el = this.el = this.completer = null; + }, + + // Update the element with the given value and strategy. + // + // value - The selected object. It is one of the item of the array + // which was callbacked from the search function. + // strategy - The Strategy associated with the selected value. + select: function (/* value, strategy */) { + throw new Error('Not implemented'); + }, + + // Returns the caret's relative coordinates from body's left top corner. + getCaretPosition: function () { + var position = this._getCaretRelativePosition(); + var offset = this.$el.offset(); + + // Calculate the left top corner of `this.option.appendTo` element. + var $parent = this.option.appendTo; + if ($parent) { + if (!($parent instanceof $)) { $parent = $($parent); } + var parentOffset = $parent.offsetParent().offset(); + offset.top -= parentOffset.top; + offset.left -= parentOffset.left; + } + + position.top += offset.top; + position.left += offset.left; + return position; + }, + + // Focus on the element. + focus: function () { + this.$el.focus(); + }, + + // Private methods + // --------------- + + _bindEvents: function () { + this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this)); + }, + + _onKeyup: function (e) { + if (this._skipSearch(e)) { return; } + this.completer.trigger(this.getTextFromHeadToCaret(), true); + }, + + // Suppress searching if it returns true. + _skipSearch: function (clickEvent) { + switch (clickEvent.keyCode) { + case 9: // TAB + case 13: // ENTER + case 40: // DOWN + case 38: // UP + return true; + } + if (clickEvent.ctrlKey) switch (clickEvent.keyCode) { + case 78: // Ctrl-N + case 80: // Ctrl-P + return true; + } + } + }); + + $.fn.textcomplete.Adapter = Adapter; +}(jQuery); + ++function ($) { + 'use strict'; + + // Textarea adapter + // ================ + // + // Managing a textarea. It doesn't know a Dropdown. + function Textarea(element, completer, option) { + this.initialize(element, completer, option); + } + + $.extend(Textarea.prototype, $.fn.textcomplete.Adapter.prototype, { + // Public methods + // -------------- + + // Update the textarea with the given value and strategy. + select: function (value, strategy, e) { + var pre = this.getTextFromHeadToCaret(); + var post = this.el.value.substring(this.el.selectionEnd); + var newSubstr = strategy.replace(value, e); + if (typeof newSubstr !== 'undefined') { + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + this.$el.val(pre + post); + this.el.selectionStart = this.el.selectionEnd = pre.length; + } + }, + + getTextFromHeadToCaret: function () { + return this.el.value.substring(0, this.el.selectionEnd); + }, + + // Private methods + // --------------- + + _getCaretRelativePosition: function () { + var p = $.fn.textcomplete.getCaretCoordinates(this.el, this.el.selectionStart); + return { + top: p.top + this._calculateLineHeight() - this.$el.scrollTop(), + left: p.left - this.$el.scrollLeft() + }; + }, + + _calculateLineHeight: function () { + var lineHeight = parseInt(this.$el.css('line-height'), 10); + if (isNaN(lineHeight)) { + // http://stackoverflow.com/a/4515470/1297336 + var parentNode = this.el.parentNode; + var temp = document.createElement(this.el.nodeName); + var style = this.el.style; + temp.setAttribute( + 'style', + 'margin:0px;padding:0px;font-family:' + style.fontFamily + ';font-size:' + style.fontSize + ); + temp.innerHTML = 'test'; + parentNode.appendChild(temp); + lineHeight = temp.clientHeight; + parentNode.removeChild(temp); + } + return lineHeight; + } + }); + + $.fn.textcomplete.Textarea = Textarea; +}(jQuery); + ++function ($) { + 'use strict'; + + var sentinelChar = '吶'; + + function IETextarea(element, completer, option) { + this.initialize(element, completer, option); + $('' + sentinelChar + '').css({ + position: 'absolute', + top: -9999, + left: -9999 + }).insertBefore(element); + } + + $.extend(IETextarea.prototype, $.fn.textcomplete.Textarea.prototype, { + // Public methods + // -------------- + + select: function (value, strategy, e) { + var pre = this.getTextFromHeadToCaret(); + var post = this.el.value.substring(pre.length); + var newSubstr = strategy.replace(value, e); + if (typeof newSubstr !== 'undefined') { + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr); + this.$el.val(pre + post); + this.el.focus(); + var range = this.el.createTextRange(); + range.collapse(true); + range.moveEnd('character', pre.length); + range.moveStart('character', pre.length); + range.select(); + } + }, + + getTextFromHeadToCaret: function () { + this.el.focus(); + var range = document.selection.createRange(); + range.moveStart('character', -this.el.value.length); + var arr = range.text.split(sentinelChar) + return arr.length === 1 ? arr[0] : arr[1]; + } + }); + + $.fn.textcomplete.IETextarea = IETextarea; +}(jQuery); + +// NOTE: TextComplete plugin has contenteditable support but it does not work +// fine especially on old IEs. +// Any pull requests are REALLY welcome. + ++function ($) { + 'use strict'; + + // ContentEditable adapter + // ======================= + // + // Adapter for contenteditable elements. + function ContentEditable (element, completer, option) { + this.initialize(element, completer, option); + } + + $.extend(ContentEditable.prototype, $.fn.textcomplete.Adapter.prototype, { + // Public methods + // -------------- + + // Update the content with the given value and strategy. + // When an dropdown item is selected, it is executed. + select: function (value, strategy, e) { + var pre = this.getTextFromHeadToCaret(); + // use ownerDocument instead of window to support iframes + var sel = this.el.ownerDocument.getSelection(); + + var range = sel.getRangeAt(0); + var selection = range.cloneRange(); + selection.selectNodeContents(range.startContainer); + var content = selection.toString(); + var post = content.substring(range.startOffset); + var newSubstr = strategy.replace(value, e); + if (typeof newSubstr !== 'undefined') { + if ($.isArray(newSubstr)) { + post = newSubstr[1] + post; + newSubstr = newSubstr[0]; + } + pre = pre.replace(strategy.match, newSubstr) + .replace(/ $/, " "); //   necessary at least for CKeditor to not eat spaces + range.selectNodeContents(range.startContainer); + range.deleteContents(); + + // create temporary elements + var preWrapper = this.el.ownerDocument.createElement("div"); + preWrapper.innerHTML = pre; + var postWrapper = this.el.ownerDocument.createElement("div"); + postWrapper.innerHTML = post; + + // create the fragment thats inserted + var fragment = this.el.ownerDocument.createDocumentFragment(); + var childNode; + var lastOfPre; + while (childNode = preWrapper.firstChild) { + lastOfPre = fragment.appendChild(childNode); + } + while (childNode = postWrapper.firstChild) { + fragment.appendChild(childNode); + } + + // insert the fragment & jump behind the last node in "pre" + range.insertNode(fragment); + range.setStartAfter(lastOfPre); + + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); + } + }, + + // Private methods + // --------------- + + // Returns the caret's relative position from the contenteditable's + // left top corner. + // + // Examples + // + // this._getCaretRelativePosition() + // //=> { top: 18, left: 200, lineHeight: 16 } + // + // Dropdown's position will be decided using the result. + _getCaretRelativePosition: function () { + var range = this.el.ownerDocument.getSelection().getRangeAt(0).cloneRange(); + var node = this.el.ownerDocument.createElement('span'); + range.insertNode(node); + range.selectNodeContents(node); + range.deleteContents(); + var $node = $(node); + var position = $node.offset(); + position.left -= this.$el.offset().left; + position.top += $node.height() - this.$el.offset().top; + position.lineHeight = $node.height(); + + // special positioning logic for iframes + // this is typically used for contenteditables such as tinymce or ckeditor + if (this.completer.$iframe) { + var iframePosition = this.completer.$iframe.offset(); + position.top += iframePosition.top; + position.left += iframePosition.left; + //subtract scrollTop from element in iframe + position.top -= this.$el.scrollTop(); + } + + $node.remove(); + return position; + }, + + // Returns the string between the first character and the caret. + // Completer will be triggered with the result for start autocompleting. + // + // Example + // + // // Suppose the html is 'hello wor|ld' and | is the caret. + // this.getTextFromHeadToCaret() + // // => ' wor' // not 'hello wor' + getTextFromHeadToCaret: function () { + var range = this.el.ownerDocument.getSelection().getRangeAt(0); + var selection = range.cloneRange(); + selection.selectNodeContents(range.startContainer); + return selection.toString().substring(0, range.startOffset); + } + }); + + $.fn.textcomplete.ContentEditable = ContentEditable; +}(jQuery); + +// NOTE: TextComplete plugin has contenteditable support but it does not work +// fine especially on old IEs. +// Any pull requests are REALLY welcome. + ++function ($) { + 'use strict'; + + // CKEditor adapter + // ======================= + // + // Adapter for CKEditor, based on contenteditable elements. + function CKEditor (element, completer, option) { + this.initialize(element, completer, option); + } + + $.extend(CKEditor.prototype, $.fn.textcomplete.ContentEditable.prototype, { + _bindEvents: function () { + var $this = this; + CKEDITOR.instances["issue_notes"].on('key', function(event) { + var domEvent = event.data; + $this._onKeyup(domEvent); + if ($this.completer.dropdown.shown && $this._skipSearch(domEvent)) { + return false; + } + }, null, null, 1); // 1 = Priority = Important! + // we actually also need the native event, as the CKEditor one is happening to late + this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this)); + }, +}); + + $.fn.textcomplete.CKEditor = CKEditor; +}(jQuery); + +// The MIT License (MIT) +// +// Copyright (c) 2015 Jonathan Ong me@jongleberry.com +// +// 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. +// +// https://github.com/component/textarea-caret-position + +(function ($) { + +// The properties that we copy into a mirrored div. +// Note that some browsers, such as Firefox, +// do not concatenate properties, i.e. padding-top, bottom etc. -> padding, +// so we have to do every single property specifically. +var properties = [ + 'direction', // RTL support + 'boxSizing', + 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does + 'height', + 'overflowX', + 'overflowY', // copy the scrollbar for IE + + 'borderTopWidth', + 'borderRightWidth', + 'borderBottomWidth', + 'borderLeftWidth', + 'borderStyle', + + 'paddingTop', + 'paddingRight', + 'paddingBottom', + 'paddingLeft', + + // https://developer.mozilla.org/en-US/docs/Web/CSS/font + 'fontStyle', + 'fontVariant', + 'fontWeight', + 'fontStretch', + 'fontSize', + 'fontSizeAdjust', + 'lineHeight', + 'fontFamily', + + 'textAlign', + 'textTransform', + 'textIndent', + 'textDecoration', // might not make a difference, but better be safe + + 'letterSpacing', + 'wordSpacing', + + 'tabSize', + 'MozTabSize' + +]; + +var isBrowser = (typeof window !== 'undefined'); +var isFirefox = (isBrowser && window.mozInnerScreenX != null); + +function getCaretCoordinates(element, position, options) { + if(!isBrowser) { + throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser'); + } + + var debug = options && options.debug || false; + if (debug) { + var el = document.querySelector('#input-textarea-caret-position-mirror-div'); + if ( el ) { el.parentNode.removeChild(el); } + } + + // mirrored div + var div = document.createElement('div'); + div.id = 'input-textarea-caret-position-mirror-div'; + document.body.appendChild(div); + + var style = div.style; + var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 + + // default textarea styles + style.whiteSpace = 'pre-wrap'; + if (element.nodeName !== 'INPUT') + style.wordWrap = 'break-word'; // only for textarea-s + + // position off-screen + style.position = 'absolute'; // required to return coordinates properly + if (!debug) + style.visibility = 'hidden'; // not 'display: none' because we want rendering + + // transfer the element's properties to the div + properties.forEach(function (prop) { + style[prop] = computed[prop]; + }); + + if (isFirefox) { + // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 + if (element.scrollHeight > parseInt(computed.height)) + style.overflowY = 'scroll'; + } else { + style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' + } + + div.textContent = element.value.substring(0, position); + // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 + if (element.nodeName === 'INPUT') + div.textContent = div.textContent.replace(/\s/g, '\u00a0'); + + var span = document.createElement('span'); + // Wrapping must be replicated *exactly*, including when a long word gets + // onto the next line, with whitespace at the end of the line before (#7). + // The *only* reliable way to do that is to copy the *entire* rest of the + // textarea's content into the created at the caret position. + // for inputs, just '.' would be enough, but why bother? + span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all + div.appendChild(span); + + var coordinates = { + top: span.offsetTop + parseInt(computed['borderTopWidth']), + left: span.offsetLeft + parseInt(computed['borderLeftWidth']) + }; + + if (debug) { + span.style.backgroundColor = '#aaa'; + } else { + document.body.removeChild(div); + } + + return coordinates; +} + +$.fn.textcomplete.getCaretCoordinates = getCaretCoordinates; + +}(jQuery)); -})(window.jQuery || window.Zepto); +return jQuery; +})); diff --git a/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.js b/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.js index 3a3fbd47bd..3768d6539c 100644 --- a/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.js +++ b/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.js @@ -1,4 +1,3 @@ -/*! jquery-textcomplete - v0.3.3 - 2014-09-25 */if("undefined"==typeof jQuery)throw new Error("jQuery.textcomplete requires jQuery");+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)};a.fn.textcomplete=function(c,d){var e=Array.prototype.slice.call(arguments);return this.each(function(){var f=a(this),g=f.data("textComplete");if(g||(g=new a.fn.textcomplete.Completer(this,d||{}),f.data("textComplete",g)),"string"==typeof c){if(!g)return;e.shift(),g[c].apply(g,e)}else a.each(c,function(c){a.each(["header","footer","placement","maxCount"],function(a){c[a]&&(g.option[a]=c[a],b(a+"as a strategy param is deplicated. Use option."),delete c[a])})}),g.register(a.fn.textcomplete.Strategy.parse(c))})}}(jQuery),+function(a){"use strict";function b(c,d){if(this.$el=a(c),this.id="textcomplete"+e++,this.strategies=[],this.views=[],this.option=a.extend({},b.DEFAULTS,d),!this.$el.is("textarea")&&!c.isContentEditable)throw new Error("textcomplete must be called to a Textarea or a ContentEditable.");if(c===document.activeElement)this.initialize();else{var f=this;this.$el.one("focus."+this.id,function(){f.initialize()})}}var c=function(a){var b,c;return function(){var d=Array.prototype.slice.call(arguments);if(b)return c=d,void 0;b=!0;var e=this;d.unshift(function f(){if(c){var d=c;c=void 0,d.unshift(f),a.apply(e,d)}else b=!1}),a.apply(this,d)}},d=function(a){return"[object String]"===Object.prototype.toString.call(a)},e=0;b.DEFAULTS={appendTo:a("body"),zIndex:"100"},a.extend(b.prototype,{id:null,option:null,strategies:null,adapter:null,dropdown:null,$el:null,initialize:function(){var b=this.$el.get(0);this.dropdown=new a.fn.textcomplete.Dropdown(b,this,this.option);var c,d;this.option.adapter?c=this.option.adapter:(d=this.$el.is("textarea")?"number"==typeof b.selectionEnd?"Textarea":"IETextarea":"ContentEditable",c=a.fn.textcomplete[d]),this.adapter=new c(b,this,this.option)},destroy:function(){this.$el.off("."+this.id),this.adapter.destroy(),this.dropdown.destroy(),this.$el=this.adapter=this.dropdown=null},trigger:function(a,b){this.dropdown||this.initialize(),null!=a||(a=this.adapter.getTextFromHeadToCaret());var c=this._extractSearchQuery(a);if(c.length){var d=c[1];if(b&&this._term===d)return;this._term=d,this._search.apply(this,c)}else this._term=null,this.dropdown.deactivate()},fire:function(a){return this.$el.trigger(a),this},register:function(a){Array.prototype.push.apply(this.strategies,a)},select:function(a,b){this.adapter.select(a,b),this.fire("change").fire("textComplete:select",a,b),this.adapter.focus()},_clearAtNext:!0,_term:null,_extractSearchQuery:function(a){for(var b=0;b').css({display:"none",left:0,position:"absolute",zIndex:b.zIndex}).appendTo(c)),d}}),a.extend(b.prototype,{$el:null,$inputEl:null,completer:null,footer:null,header:null,id:null,maxCount:10,placement:"",shown:!1,data:[],className:"",destroy:function(){this.deactivate(),this.$el.off("."+this.id),this.$inputEl.off("."+this.id),this.clear(),this.$el=this.$inputEl=this.completer=null,delete d[this.id]},render:function(b){var c=this._buildContents(b),d=a.map(this.data,function(a){return a.value});this.data.length?(this._renderHeader(d),this._renderFooter(d),c&&(this._renderContents(c),this._activateIndexedItem()),this._setScroll()):this.shown&&this.deactivate()},setPosition:function(a){return this.$el.css(this._applyPlacement(a)),this},clear:function(){this.$el.html(""),this.data=[],this._index=0,this._$header=this._$footer=null},activate:function(){return this.shown||(this.clear(),this.$el.show(),this.className&&this.$el.addClass(this.className),this.completer.fire("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.className&&this.$el.removeClass(this.className),this.completer.fire("textComplete:hide"),this.shown=!1),this},isUp:function(a){return 38===a.keyCode||a.ctrlKey&&80===a.keyCode},isDown:function(a){return 40===a.keyCode||a.ctrlKey&&78===a.keyCode},isEnter:function(a){var b=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey;return!b&&(13===a.keyCode||9===a.keyCode)},isPageup:function(a){return 33===a.keyCode},isPagedown:function(a){return 34===a.keyCode},_data:null,_index:null,_$header:null,_$footer:null,_bindEvents:function(){this.$el.on("mousedown."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("mouseover."+this.id,".textcomplete-item",a.proxy(this._onMouseover,this)),this.$inputEl.on("keydown."+this.id,a.proxy(this._onKeydown,this))},_onClick:function(b){var c=a(b.target);b.preventDefault(),b.originalEvent.keepTextCompleteDropdown=this.id,c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item"));var d=this.data[parseInt(c.data("index"),10)];this.completer.select(d.value,d.strategy);var e=this;setTimeout(function(){e.deactivate()},0)},_onMouseover:function(b){var c=a(b.target);b.preventDefault(),c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item")),this._index=parseInt(c.data("index"),10),this._activateIndexedItem()},_onKeydown:function(a){this.shown&&(this.isUp(a)?(a.preventDefault(),this._up()):this.isDown(a)?(a.preventDefault(),this._down()):this.isEnter(a)?(a.preventDefault(),this._enter()):this.isPageup(a)?(a.preventDefault(),this._pageup()):this.isPagedown(a)&&(a.preventDefault(),this._pagedown()))},_up:function(){0===this._index?this._index=this.data.length-1:this._index-=1,this._activateIndexedItem(),this._setScroll()},_down:function(){this._index===this.data.length-1?this._index=0:this._index+=1,this._activateIndexedItem(),this._setScroll()},_enter:function(){var a=this.data[parseInt(this._getActiveElement().data("index"),10)];this.completer.select(a.value,a.strategy),this._setScroll()},_pageup:function(){var b=0,c=this._getActiveElement().position().top-this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top+a(this).outerHeight()>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_pagedown:function(){var b=this.data.length-1,c=this._getActiveElement().position().top+this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_activateIndexedItem:function(){this.$el.find(".textcomplete-item.active").removeClass("active"),this._getActiveElement().addClass("active")},_getActiveElement:function(){return this.$el.children(".textcomplete-item:nth("+this._index+")")},_setScroll:function(){var a=this._getActiveElement(),b=a.position().top,c=a.outerHeight(),d=this.$el.innerHeight(),e=this.$el.scrollTop();0===this._index||this._index==this.data.length-1||0>b?this.$el.scrollTop(b+e):b+c>d&&this.$el.scrollTop(b+c+e-d)},_buildContents:function(a){var b,d,e,f="";for(d=0;d',f+=b.strategy.template(b.value),f+="");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('
  • ').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b}(jQuery),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c){return a.map(c,function(a){return new b(a)})},a.extend(b.prototype,{match:null,replace:null,search:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(jQuery),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var a=this._getCaretRelativePosition(),b=this.$el.offset();return a.top+=b.top,a.left+=b.left,a},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(jQuery),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}b.DIV_PROPERTIES={left:-9999,position:"absolute",top:0,whiteSpace:"pre-wrap"},b.COPY_PROPERTIES=["border-width","font-family","font-size","font-style","font-variant","font-weight","height","letter-spacing","word-spacing","line-height","text-decoration","text-align","width","padding-top","padding-right","padding-bottom","padding-left","margin-top","margin-right","margin-bottom","margin-left","border-style","box-sizing","tab-size"],a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=this.el.value.substring(this.el.selectionEnd),f=c.replace(b);a.isArray(f)&&(e=f[1]+e,f=f[0]),d=d.replace(c.match,f),this.$el.val(d+e),this.el.selectionStart=this.el.selectionEnd=d.length},_getCaretRelativePosition:function(){var b=a("
    ").css(this._copyCss()).text(this.getTextFromHeadToCaret()),c=a("").text(".").appendTo(b);this.$el.before(b);var d=c.position();return d.top+=c.height()-this.$el.scrollTop(),d.lineHeight=c.height(),b.remove(),d},_copyCss:function(){return a.extend({overflow:this.el.scrollHeight>this.el.offsetHeight?"scroll":"auto"},b.DIV_PROPERTIES,this._getStyles())},_getStyles:function(a){var c=a("
    ").css(["color"]).color;return"undefined"!=typeof c?function(){return this.$el.css(b.COPY_PROPERTIES)}:function(){var c=this.$el,d={};return a.each(b.COPY_PROPERTIES,function(a,b){d[b]=c.css(b)}),d}}(a),getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)}}),a.fn.textcomplete.Textarea=b}(jQuery),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a(""+c+"").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c){var d=this._getTextFromHeadToCaret(),e=this.el.value.substring(d.length),f=c.replace(b);a.isArray(f)&&(e=f[1]+e,f=f[0]),d=d.replace(c.match,f),this.$el.val(d+e),this.el.focus();var g=this.el.createTextRange();g.collapse(!0),g.moveEnd("character",d.length),g.moveStart("character",d.length),g.select()},_getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(jQuery),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=window.getSelection(),f=e.getRangeAt(0),g=f.cloneRange();g.selectNodeContents(f.startContainer);var h=g.toString(),i=h.substring(f.startOffset),j=c.replace(b);a.isArray(j)&&(i=j[1]+i,j=j[0]),d=d.replace(c.match,j),f.selectNodeContents(f.startContainer),f.deleteContents();var k=document.createTextNode(d+i);f.insertNode(k),f.setStart(k,d.length),f.collapse(!0),e.removeAllRanges(),e.addRange(f)},_getCaretRelativePosition:function(){var b=window.getSelection().getRangeAt(0).cloneRange(),c=document.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height();var f=this.$el.attr("dir")||this.$el.css("direction");return"rtl"===f&&(e.left-=this.listView.$el.width()),e},getTextFromHeadToCaret:function(){var a=window.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(jQuery); -/* -//@ sourceMappingURL=dist/jquery.textcomplete.min.map -*/ \ No newline at end of file +/*! jquery-textcomplete - v1.5.0 - 2016-06-07 */ +!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"==typeof module&&module.exports){var b=require("jquery");module.exports=a(b)}else a(jQuery)}(function(a){if("undefined"==typeof a)throw new Error("jQuery.textcomplete requires jQuery");return+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)},c=1;a.fn.textcomplete=function(d,e){var f=Array.prototype.slice.call(arguments);return this.each(function(){var g=this,h=a(this),i=h.data("textComplete");if(i||(e||(e={}),e._oid=c++,i=new a.fn.textcomplete.Completer(this,e),h.data("textComplete",i)),"string"==typeof d){if(!i)return;f.shift(),i[d].apply(i,f),"destroy"===d&&h.removeData("textComplete")}else a.each(d,function(c){a.each(["header","footer","placement","maxCount"],function(a){c[a]&&(i.option[a]=c[a],b(a+"as a strategy param is deprecated. Use option."),delete c[a])})}),i.register(a.fn.textcomplete.Strategy.parse(d,{el:g,$el:h}))})}}(a),+function(a){"use strict";function b(c,d){if(this.$el=a(c),this.id="textcomplete"+f++,this.strategies=[],this.views=[],this.option=a.extend({},b._getDefaults(),d),!(this.$el.is("input[type=text]")||this.$el.is("input[type=search]")||this.$el.is("textarea")||c.isContentEditable||"true"==c.contentEditable))throw new Error("textcomplete must be called on a Textarea or a ContentEditable.");if(c===c.ownerDocument.activeElement)this.initialize();else{var e=this;this.$el.one("focus."+this.id,function(){e.initialize()}),this.option.adapter&&"CKEditor"!=this.option.adapter||"undefined"==typeof CKEDITOR||!this.$el.is("textarea")||CKEDITOR.on("instanceReady",function(b){b.editor.once("focus",function(c){e.$el=a(b.editor.editable().$),e.option.adapter||(e.option.adapter=a.fn.textcomplete.CKEditor),e.initialize()})})}}var c=function(a){var b,c;return function(){var d=Array.prototype.slice.call(arguments);if(b)return void(c=d);b=!0;var e=this;d.unshift(function f(){if(c){var d=c;c=void 0,d.unshift(f),a.apply(e,d)}else b=!1}),a.apply(this,d)}},d=function(a){return"[object String]"===Object.prototype.toString.call(a)},e=function(a){return"[object Function]"===Object.prototype.toString.call(a)},f=0;b._getDefaults=function(){return b.DEFAULTS||(b.DEFAULTS={appendTo:a("body"),className:"",dropdownClassName:"dropdown-menu textcomplete-dropdown",maxCount:10,zIndex:"100"}),b.DEFAULTS},a.extend(b.prototype,{id:null,option:null,strategies:null,adapter:null,dropdown:null,$el:null,$iframe:null,initialize:function(){var b=this.$el.get(0);if(this.$el.prop("ownerDocument")!==document&&window.frames.length)for(var c=0;c").addClass(b.dropdownClassName).attr("id","textcomplete-dropdown-"+b._oid).css({display:"none",left:0,position:"absolute",zIndex:b.zIndex}).appendTo(c);return d}}),a.extend(b.prototype,{$el:null,$inputEl:null,completer:null,footer:null,header:null,id:null,maxCount:null,placement:"",shown:!1,data:[],className:"",destroy:function(){this.deactivate(),this.$el.off("."+this.id),this.$inputEl.off("."+this.id),this.clear(),this.$el.remove(),this.$el=this.$inputEl=this.completer=null,delete e[this.id]},render:function(b){var c=this._buildContents(b),d=a.map(this.data,function(a){return a.value});if(this.data.length){var e=b[0].strategy;e.id?this.$el.attr("data-strategy",e.id):this.$el.removeAttr("data-strategy"),this._renderHeader(d),this._renderFooter(d),c&&(this._renderContents(c),this._fitToBottom(),this._fitToRight(),this._activateIndexedItem()),this._setScroll()}else this.noResultsMessage?this._renderNoResultsMessage(d):this.shown&&this.deactivate()},setPosition:function(b){var d="absolute";return this.$inputEl.add(this.$inputEl.parents()).each(function(){return"absolute"===a(this).css("position")?!1:"fixed"===a(this).css("position")?(b.top-=c.scrollTop(),b.left-=c.scrollLeft(),d="fixed",!1):void 0}),this.$el.css(this._applyPlacement(b)),this.$el.css({position:d}),this},clear:function(){this.$el.html(""),this.data=[],this._index=0,this._$header=this._$footer=this._$noResultsMessage=null},activate:function(){return this.shown||(this.clear(),this.$el.show(),this.className&&this.$el.addClass(this.className),this.completer.fire("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.className&&this.$el.removeClass(this.className),this.completer.fire("textComplete:hide"),this.shown=!1),this},isUp:function(a){return 38===a.keyCode||a.ctrlKey&&80===a.keyCode},isDown:function(a){return 40===a.keyCode||a.ctrlKey&&78===a.keyCode},isEnter:function(a){var b=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey;return!b&&(13===a.keyCode||9===a.keyCode||this.option.completeOnSpace===!0&&32===a.keyCode)},isPageup:function(a){return 33===a.keyCode},isPagedown:function(a){return 34===a.keyCode},isEscape:function(a){return 27===a.keyCode},_data:null,_index:null,_$header:null,_$noResultsMessage:null,_$footer:null,_bindEvents:function(){this.$el.on("mousedown."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("touchstart."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("mouseover."+this.id,".textcomplete-item",a.proxy(this._onMouseover,this)),this.$inputEl.on("keydown."+this.id,a.proxy(this._onKeydown,this))},_onClick:function(b){var c=a(b.target);b.preventDefault(),b.originalEvent.keepTextCompleteDropdown=this.id,c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item"));var d=this.data[parseInt(c.data("index"),10)];this.completer.select(d.value,d.strategy,b);var e=this;setTimeout(function(){e.deactivate(),"touchstart"===b.type&&e.$inputEl.focus()},0)},_onMouseover:function(b){var c=a(b.target);b.preventDefault(),c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item")),this._index=parseInt(c.data("index"),10),this._activateIndexedItem()},_onKeydown:function(b){if(this.shown){var c;switch(a.isFunction(this.option.onKeydown)&&(c=this.option.onKeydown(b,f)),null==c&&(c=this._defaultKeydown(b)),c){case f.KEY_UP:b.preventDefault(),this._up();break;case f.KEY_DOWN:b.preventDefault(),this._down();break;case f.KEY_ENTER:b.preventDefault(),this._enter(b);break;case f.KEY_PAGEUP:b.preventDefault(),this._pageup();break;case f.KEY_PAGEDOWN:b.preventDefault(),this._pagedown();break;case f.KEY_ESCAPE:b.preventDefault(),this.deactivate()}}},_defaultKeydown:function(a){return this.isUp(a)?f.KEY_UP:this.isDown(a)?f.KEY_DOWN:this.isEnter(a)?f.KEY_ENTER:this.isPageup(a)?f.KEY_PAGEUP:this.isPagedown(a)?f.KEY_PAGEDOWN:this.isEscape(a)?f.KEY_ESCAPE:void 0},_up:function(){0===this._index?this._index=this.data.length-1:this._index-=1,this._activateIndexedItem(),this._setScroll()},_down:function(){this._index===this.data.length-1?this._index=0:this._index+=1,this._activateIndexedItem(),this._setScroll()},_enter:function(a){var b=this.data[parseInt(this._getActiveElement().data("index"),10)];this.completer.select(b.value,b.strategy,a),this.deactivate()},_pageup:function(){var b=0,c=this._getActiveElement().position().top-this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top+a(this).outerHeight()>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_pagedown:function(){var b=this.data.length-1,c=this._getActiveElement().position().top+this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_activateIndexedItem:function(){this.$el.find(".textcomplete-item.active").removeClass("active"),this._getActiveElement().addClass("active")},_getActiveElement:function(){return this.$el.children(".textcomplete-item:nth("+this._index+")")},_setScroll:function(){var a=this._getActiveElement(),b=a.position().top,c=a.outerHeight(),d=this.$el.innerHeight(),e=this.$el.scrollTop();0===this._index||this._index==this.data.length-1||0>b?this.$el.scrollTop(b+e):b+c>d&&this.$el.scrollTop(b+c+e-d)},_buildContents:function(a){var b,c,e,f="";for(c=0;c',f+=b.strategy.template(b.value,b.term),f+="");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('
  • ').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderNoResultsMessage:function(b){if(this.noResultsMessage){this._$noResultsMessage||(this._$noResultsMessage=a('
  • ').appendTo(this.$el));var c=a.isFunction(this.noResultsMessage)?this.noResultsMessage(b):this.noResultsMessage;this._$noResultsMessage.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_fitToBottom:function(){var a=c.scrollTop()+c.height(),b=this.$el.height();this.$el.position().top+b>a&&(this.completer.$iframe||this.$el.offset({top:a-b}))},_fitToRight:function(){for(var a,b=30,d=this.$el.offset().left,e=this.$el.width(),f=c.width()-b;d+e>f&&(this.$el.offset({left:d-b}),a=this.$el.offset().left,!(a>=d));)d=a},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b,a.extend(a.fn.textcomplete,f)}(a),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c,d){return a.map(c,function(a){var c=new b(a);return c.el=d.el,c.$el=d.$el,c})},a.extend(b.prototype,{match:null,replace:null,search:null,id:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(a),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var b=this._getCaretRelativePosition(),c=this.$el.offset(),d=this.option.appendTo;if(d){d instanceof a||(d=a(d));var e=d.offsetParent().offset();c.top-=e.top,c.left-=e.left}return b.top+=c.top,b.left+=c.left,b},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 9:case 13:case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(this.el.selectionEnd),g=c.replace(b,d);"undefined"!=typeof g&&(a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.selectionStart=this.el.selectionEnd=e.length)},getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)},_getCaretRelativePosition:function(){var b=a.fn.textcomplete.getCaretCoordinates(this.el,this.el.selectionStart);return{top:b.top+this._calculateLineHeight()-this.$el.scrollTop(),left:b.left-this.$el.scrollLeft()}},_calculateLineHeight:function(){var a=parseInt(this.$el.css("line-height"),10);if(isNaN(a)){var b=this.el.parentNode,c=document.createElement(this.el.nodeName),d=this.el.style;c.setAttribute("style","margin:0px;padding:0px;font-family:"+d.fontFamily+";font-size:"+d.fontSize),c.innerHTML="test",b.appendChild(c),a=c.clientHeight,b.removeChild(c)}return a}}),a.fn.textcomplete.Textarea=b}(a),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a(""+c+"").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.value.substring(e.length),g=c.replace(b,d);if("undefined"!=typeof g){a.isArray(g)&&(f=g[1]+f,g=g[0]),e=e.replace(c.match,g),this.$el.val(e+f),this.el.focus();var h=this.el.createTextRange();h.collapse(!0),h.moveEnd("character",e.length),h.moveStart("character",e.length),h.select()}},getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c,d){var e=this.getTextFromHeadToCaret(),f=this.el.ownerDocument.getSelection(),g=f.getRangeAt(0),h=g.cloneRange();h.selectNodeContents(g.startContainer);var i=h.toString(),j=i.substring(g.startOffset),k=c.replace(b,d);if("undefined"!=typeof k){a.isArray(k)&&(j=k[1]+j,k=k[0]),e=e.replace(c.match,k).replace(/ $/," "),g.selectNodeContents(g.startContainer),g.deleteContents();var l=this.el.ownerDocument.createElement("div");l.innerHTML=e;var m=this.el.ownerDocument.createElement("div");m.innerHTML=j;for(var n,o,p=this.el.ownerDocument.createDocumentFragment();n=l.firstChild;)o=p.appendChild(n);for(;n=m.firstChild;)p.appendChild(n);g.insertNode(p),g.setStartAfter(o),g.collapse(!0),f.removeAllRanges(),f.addRange(g)}},_getCaretRelativePosition:function(){var b=this.el.ownerDocument.getSelection().getRangeAt(0).cloneRange(),c=this.el.ownerDocument.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();if(e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height(),this.completer.$iframe){var f=this.completer.$iframe.offset();e.top+=f.top,e.left+=f.left,e.top-=this.$el.scrollTop()}return d.remove(),e},getTextFromHeadToCaret:function(){var a=this.el.ownerDocument.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(a),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.ContentEditable.prototype,{_bindEvents:function(){var b=this;CKEDITOR.instances.issue_notes.on("key",function(a){var c=a.data;return b._onKeyup(c),b.completer.dropdown.shown&&b._skipSearch(c)?!1:void 0},null,null,1),this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))}}),a.fn.textcomplete.CKEditor=b}(a),function(a){function b(a,b,f){if(!d)throw new Error("textarea-caret-position#getCaretCoordinates should only be called in a browser");var g=f&&f.debug||!1;if(g){var h=document.querySelector("#input-textarea-caret-position-mirror-div");h&&h.parentNode.removeChild(h)}var i=document.createElement("div");i.id="input-textarea-caret-position-mirror-div",document.body.appendChild(i);var j=i.style,k=window.getComputedStyle?getComputedStyle(a):a.currentStyle;j.whiteSpace="pre-wrap","INPUT"!==a.nodeName&&(j.wordWrap="break-word"),j.position="absolute",g||(j.visibility="hidden"),c.forEach(function(a){j[a]=k[a]}),e?a.scrollHeight>parseInt(k.height)&&(j.overflowY="scroll"):j.overflow="hidden",i.textContent=a.value.substring(0,b),"INPUT"===a.nodeName&&(i.textContent=i.textContent.replace(/\s/g," "));var l=document.createElement("span");l.textContent=a.value.substring(b)||".",i.appendChild(l);var m={top:l.offsetTop+parseInt(k.borderTopWidth),left:l.offsetLeft+parseInt(k.borderLeftWidth)};return g?l.style.backgroundColor="#aaa":document.body.removeChild(i),m}var c=["direction","boxSizing","width","height","overflowX","overflowY","borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth","borderStyle","paddingTop","paddingRight","paddingBottom","paddingLeft","fontStyle","fontVariant","fontWeight","fontStretch","fontSize","fontSizeAdjust","lineHeight","fontFamily","textAlign","textTransform","textIndent","textDecoration","letterSpacing","wordSpacing","tabSize","MozTabSize"],d="undefined"!=typeof window,e=d&&null!=window.mozInnerScreenX;a.fn.textcomplete.getCaretCoordinates=b}(a),a}); +//# sourceMappingURL=dist/jquery.textcomplete.min.map \ No newline at end of file diff --git a/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.map b/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.map index 3fbebd5213..47283a1bce 100644 --- a/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.map +++ b/main/inc/lib/javascript/jquery-textcomplete/jquery.textcomplete.min.map @@ -1 +1 @@ -{"version":3,"file":"dist/jquery.textcomplete.min.js","sources":["dist/jquery.textcomplete.js"],"names":["jQuery","Error","$","warn","message","console","fn","textcomplete","strategies","option","args","Array","prototype","slice","call","arguments","this","each","$this","completer","data","Completer","shift","apply","obj","name","register","Strategy","parse","element","$el","id","uniqueId","views","extend","DEFAULTS","is","isContentEditable","document","activeElement","initialize","self","one","lock","func","locked","queuedArgsToReplay","unshift","replayOrFree","replayArgs","undefined","isString","Object","toString","appendTo","zIndex","adapter","dropdown","get","Dropdown","Adapter","viewName","selectionEnd","destroy","off","trigger","text","skipUnchangedTerm","getTextFromHeadToCaret","searchQuery","_extractSearchQuery","length","term","_term","_search","deactivate","fire","eventName","push","select","value","strategy","focus","_clearAtNext","i","context","match","index","free","search","stillSearching","shown","activate","setPosition","getCaretPosition","clear","render","_zip","map","findOrCreateElement","_data","$inputEl","listPosition","height","_i","_bindEvents","dropdownViews","include","zippedData","datum","elem","idProperty","on","e","originalEvent","keepTextCompleteDropdown","key","view","$parent","children","css","display","left","position","footer","header","maxCount","placement","className","contentsHtml","_buildContents","unzippedData","d","_renderHeader","_renderFooter","_renderContents","_activateIndexedItem","_setScroll","_applyPlacement","html","_index","_$header","_$footer","show","addClass","hide","removeClass","isUp","keyCode","ctrlKey","isDown","isEnter","modifiers","altKey","metaKey","shiftKey","isPageup","isPagedown","proxy","_onClick","_onMouseover","_onKeydown","target","preventDefault","hasClass","closest","parseInt","setTimeout","_up","_down","_enter","_pageup","_pagedown","_getActiveElement","threshold","top","innerHeight","outerHeight","find","$activeEl","itemTop","itemHeight","visibleHeight","visibleTop","scrollTop","template","prependTo","isFunction","before","append","indexOf","bottom","parent","lineHeight","right","options","cache","memoize","memo","callback","concat","optionsArray","replace","now","Date","getTime","debounce","wait","timeout","timestamp","result","later","last","el","constructor","_onKeyup","_getCaretRelativePosition","offset","_skipSearch","clickEvent","Textarea","DIV_PROPERTIES","whiteSpace","COPY_PROPERTIES","pre","post","substring","newSubstr","isArray","val","selectionStart","dummyDiv","_copyCss","span","remove","overflow","scrollHeight","offsetHeight","_getStyles","color","styles","property","IETextarea","sentinelChar","insertBefore","_getTextFromHeadToCaret","range","createTextRange","collapse","moveEnd","moveStart","selection","createRange","arr","split","ContentEditable","sel","window","getSelection","getRangeAt","cloneRange","selectNodeContents","startContainer","content","startOffset","deleteContents","node","createTextNode","insertNode","setStart","removeAllRanges","addRange","createElement","$node","dir","attr","listView","width"],"mappings":"AAQA,GAAsB,mBAAXA,QACT,KAAM,IAAIC,OAAM,wCAGjB,SAAUC,GACT,YAEA,IAAIC,GAAO,SAAUC,GACfC,QAAQF,MAAQE,QAAQF,KAAKC,GAGnCF,GAAEI,GAAGC,aAAe,SAAUC,EAAYC,GACxC,GAAIC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,OAAOC,MAAKC,KAAK,WACf,GAAIC,GAAQhB,EAAEc,MACVG,EAAYD,EAAME,KAAK,eAK3B,IAJKD,IACHA,EAAY,GAAIjB,GAAEI,GAAGC,aAAac,UAAUL,KAAMP,OAClDS,EAAME,KAAK,eAAgBD,IAEH,gBAAfX,GAAyB,CAClC,IAAKW,EAAW,MAChBT,GAAKY,QACLH,EAAUX,GAAYe,MAAMJ,EAAWT,OAIvCR,GAAEe,KAAKT,EAAY,SAAUgB,GAC3BtB,EAAEe,MAAM,SAAU,SAAU,YAAa,YAAa,SAAUQ,GAC1DD,EAAIC,KACNN,EAAUV,OAAOgB,GAAQD,EAAIC,GAC7BtB,EAAKsB,EAAO,wDACLD,GAAIC,QAIjBN,EAAUO,SAASxB,EAAEI,GAAGC,aAAaoB,SAASC,MAAMpB,QAK1DR,SAED,SAAUE,GACT,YAgEA,SAASmB,GAAUQ,EAASpB,GAO1B,GANAO,KAAKc,IAAa5B,EAAE2B,GACpBb,KAAKe,GAAa,eAAiBC,IACnChB,KAAKR,cACLQ,KAAKiB,SACLjB,KAAKP,OAAaP,EAAEgC,UAAWb,EAAUc,SAAU1B,IAE9CO,KAAKc,IAAIM,GAAG,cAAgBP,EAAQQ,kBACvC,KAAM,IAAIpC,OAAM,kEAGlB,IAAI4B,IAAYS,SAASC,cAEvBvB,KAAKwB,iBACA,CAEL,GAAIC,GAAOzB,IACXA,MAAKc,IAAIY,IAAI,SAAW1B,KAAKe,GAAI,WAAcU,EAAKD,gBAzDxD,GAAIG,GAAO,SAAUC,GACnB,GAAIC,GAAQC,CAEZ,OAAO,YAEL,GAAIpC,GAAOC,MAAMC,UAAUC,MAAMC,KAAKC,UACtC,IAAI8B,EAKF,MADAC,GAAqBpC,EACrB,MAEFmC,IAAS,CACT,IAAIJ,GAAOzB,IACXN,GAAKqC,QAAQ,QAASC,KACpB,GAAIF,EAAoB,CAMtB,GAAIG,GAAaH,CACjBA,GAAqBI,OACrBD,EAAWF,QAAQC,GACnBJ,EAAKrB,MAAMkB,EAAMQ,OAEjBJ,IAAS,IAGbD,EAAKrB,MAAMP,KAAMN,KAIjByC,EAAW,SAAU3B,GACvB,MAA+C,oBAAxC4B,OAAOxC,UAAUyC,SAASvC,KAAKU,IAGpCQ,EAAW,CAuBfX,GAAUc,UACRmB,SAAUpD,EAAE,QACZqD,OAAQ,OAGVrD,EAAEgC,OAAOb,EAAUT,WAIjBmB,GAAY,KACZtB,OAAY,KACZD,WAAY,KACZgD,QAAY,KACZC,SAAY,KACZ3B,IAAY,KAKZU,WAAY,WACV,GAAIX,GAAUb,KAAKc,IAAI4B,IAAI,EAE3B1C,MAAKyC,SAAW,GAAIvD,GAAEI,GAAGC,aAAaoD,SAAS9B,EAASb,KAAMA,KAAKP,OACnE,IAAImD,GAASC,CACT7C,MAAKP,OAAO+C,QACdI,EAAU5C,KAAKP,OAAO+C,SAGpBK,EADE7C,KAAKc,IAAIM,GAAG,YAC6B,gBAAzBP,GAAQiC,aAA4B,WAAa,aAExD,kBAEbF,EAAU1D,EAAEI,GAAGC,aAAasD,IAE9B7C,KAAKwC,QAAU,GAAII,GAAQ/B,EAASb,KAAMA,KAAKP,SAGjDsD,QAAS,WACP/C,KAAKc,IAAIkC,IAAI,IAAMhD,KAAKe,IACxBf,KAAKwC,QAAQO,UACb/C,KAAKyC,SAASM,UACd/C,KAAKc,IAAMd,KAAKwC,QAAUxC,KAAKyC,SAAW,MAI5CQ,QAAS,SAAUC,EAAMC,GAClBnD,KAAKyC,UAAYzC,KAAKwB,aACnB,MAAR0B,IAAiBA,EAAOlD,KAAKwC,QAAQY,yBACrC,IAAIC,GAAcrD,KAAKsD,oBAAoBJ,EAC3C,IAAIG,EAAYE,OAAQ,CACtB,GAAIC,GAAOH,EAAY,EAEvB,IAAIF,GAAqBnD,KAAKyD,QAAUD,EAAQ,MAChDxD,MAAKyD,MAAQD,EACbxD,KAAK0D,QAAQnD,MAAMP,KAAMqD,OAEzBrD,MAAKyD,MAAQ,KACbzD,KAAKyC,SAASkB,cAIlBC,KAAM,SAAUC,GAEd,MADA7D,MAAKc,IAAImC,QAAQY,GACV7D,MAGTU,SAAU,SAAUlB,GAClBG,MAAMC,UAAUkE,KAAKvD,MAAMP,KAAKR,WAAYA,IAQ9CuE,OAAQ,SAAUC,EAAOC,GACvBjE,KAAKwC,QAAQuB,OAAOC,EAAOC,GAC3BjE,KAAK4D,KAAK,UAAUA,KAAK,sBAAuBI,EAAOC,GACvDjE,KAAKwC,QAAQ0B,SAMfC,cAAc,EACdV,MAAc,KASdH,oBAAqB,SAAUJ,GAC7B,IAAK,GAAIkB,GAAI,EAAGA,EAAIpE,KAAKR,WAAW+D,OAAQa,IAAK,CAC/C,GAAIH,GAAWjE,KAAKR,WAAW4E,GAC3BC,EAAUJ,EAASI,QAAQnB,EAC/B,IAAImB,GAAuB,KAAZA,EAAgB,CACzBlC,EAASkC,KAAYnB,EAAOmB,EAChC,IAAIC,GAAQpB,EAAKoB,MAAML,EAASK,MAChC,IAAIA,EAAS,OAAQL,EAAUK,EAAML,EAASM,OAAQD,IAG1D,UAIFZ,QAAS/B,EAAK,SAAU6C,EAAMP,EAAUT,EAAMc,GAC5C,GAAI7C,GAAOzB,IACXiE,GAASQ,OAAOjB,EAAM,SAAUpD,EAAMsE,GAC/BjD,EAAKgB,SAASkC,QACjBlD,EAAKgB,SAASmC,WACdnD,EAAKgB,SAASoC,YAAYpD,EAAKe,QAAQsC,qBAErCrD,EAAK0C,eAEP1C,EAAKgB,SAASsC,QACdtD,EAAK0C,cAAe,GAEtB1C,EAAKgB,SAASuC,OAAOvD,EAAKwD,KAAK7E,EAAM6D,IAChCS,IAEHF,IACA/C,EAAK0C,cAAe,IAErBG,KASLW,KAAM,SAAU7E,EAAM6D,GACpB,MAAO/E,GAAEgG,IAAI9E,EAAM,SAAU4D,GAC3B,OAASA,MAAOA,EAAOC,SAAUA,QAKvC/E,EAAEI,GAAGC,aAAac,UAAYA,GAC9BrB,SAED,SAAUE,GACT,YA+BA,SAASyD,GAAS9B,EAASV,EAAWV,GACpCO,KAAKc,IAAY6B,EAASwC,oBAAoB1F,GAC9CO,KAAKG,UAAYA,EACjBH,KAAKe,GAAYZ,EAAUY,GAAK,WAChCf,KAAKoF,SACLpF,KAAKqF,SAAYnG,EAAE2B,GAGfpB,EAAO6F,eAAgBtF,KAAK6E,YAAcpF,EAAO6F,cACjD7F,EAAO8F,QAAUvF,KAAKc,IAAIyE,OAAO9F,EAAO8F,OAC5C,IAAI9D,GAAOzB,IACXd,GAAEe,MAAM,WAAY,YAAa,SAAU,SAAU,aAAc,SAAUuF,EAAI/E,GAC3D,MAAhBhB,EAAOgB,KAAiBgB,EAAKhB,GAAQhB,EAAOgB,MAElDT,KAAKyF,YAAY5E,GACjB6E,EAAc1F,KAAKe,IAAMf,KA5C3B,GAAI2F,GAAU,SAAUC,EAAYC,GAClC,GAAIzB,GAAG0B,EACHC,EAAaF,EAAM5B,SAAS8B,UAChC,KAAK3B,EAAI,EAAGA,EAAIwB,EAAWrC,OAAQa,IAEjC,GADA0B,EAAOF,EAAWxB,GACd0B,EAAK7B,WAAa4B,EAAM5B,SAC5B,GAAI8B,GACF,GAAID,EAAK9B,MAAM+B,KAAgBF,EAAM7B,MAAM+B,GAAa,OAAO,MAE/D,IAAID,EAAK9B,QAAU6B,EAAM7B,MAAO,OAAO,CAG3C,QAAO,GAGL0B,IACJxG,GAAEoC,UAAU0E,GAAG,QAAS,SAAUC,GAChC,GAAIlF,GAAKkF,EAAEC,eAAiBD,EAAEC,cAAcC,wBAC5CjH,GAAEe,KAAKyF,EAAe,SAAUU,EAAKC,GAC/BD,IAAQrF,GAAMsF,EAAK1C,iBA4B3BzE,EAAEgC,OAAOyB,GAIPwC,oBAAqB,SAAU1F,GAC7B,GAAI6G,GAAU7G,EAAO6C,QACfgE,aAAmBpH,KAAMoH,EAAUpH,EAAEoH,GAC3C,IAAIxF,GAAMwF,EAAQC,SAAS,iBAS3B,OARKzF,GAAIyC,SACPzC,EAAM5B,EAAE,mCAAmCsH,KACzCC,QAAS,OACTC,KAAM,EACNC,SAAU,WACVpE,OAAQ9C,EAAO8C,SACdD,SAASgE,IAEPxF,KAIX5B,EAAEgC,OAAOyB,EAAS/C,WAIhBkB,IAAW,KACXuE,SAAW,KACXlF,UAAW,KACXyG,OAAW,KACXC,OAAW,KACX9F,GAAW,KACX+F,SAAW,GACXC,UAAW,GACXpC,OAAW,EACXvE,QACA4G,UAAW,GAKXjE,QAAS,WAEP/C,KAAK2D,aAEL3D,KAAKc,IAAIkC,IAAI,IAAMhD,KAAKe,IACxBf,KAAKqF,SAASrC,IAAI,IAAMhD,KAAKe,IAC7Bf,KAAK+E,QACL/E,KAAKc,IAAMd,KAAKqF,SAAWrF,KAAKG,UAAY,WACrCuF,GAAc1F,KAAKe,KAG5BiE,OAAQ,SAAUY,GAChB,GAAIqB,GAAejH,KAAKkH,eAAetB,GACnCuB,EAAejI,EAAEgG,IAAIlF,KAAKI,KAAM,SAAUgH,GAAK,MAAOA,GAAEpD,OACxDhE,MAAKI,KAAKmD,QACZvD,KAAKqH,cAAcF,GACnBnH,KAAKsH,cAAcH,GACfF,IACFjH,KAAKuH,gBAAgBN,GACrBjH,KAAKwH,wBAEPxH,KAAKyH,cACIzH,KAAK2E,OACd3E,KAAK2D,cAITkB,YAAa,SAAU8B,GAErB,MADA3G,MAAKc,IAAI0F,IAAIxG,KAAK0H,gBAAgBf,IAC3B3G,MAGT+E,MAAO,WACL/E,KAAKc,IAAI6G,KAAK,IACd3H,KAAKI,QACLJ,KAAK4H,OAAS,EACd5H,KAAK6H,SAAW7H,KAAK8H,SAAW,MAGlClD,SAAU,WAQR,MAPK5E,MAAK2E,QACR3E,KAAK+E,QACL/E,KAAKc,IAAIiH,OACL/H,KAAKgH,WAAahH,KAAKc,IAAIkH,SAAShI,KAAKgH,WAC7ChH,KAAKG,UAAUyD,KAAK,qBACpB5D,KAAK2E,OAAQ,GAER3E,MAGT2D,WAAY,WAOV,MANI3D,MAAK2E,QACP3E,KAAKc,IAAImH,OACLjI,KAAKgH,WAAahH,KAAKc,IAAIoH,YAAYlI,KAAKgH,WAChDhH,KAAKG,UAAUyD,KAAK,qBACpB5D,KAAK2E,OAAQ,GAER3E,MAGTmI,KAAM,SAAUlC,GACd,MAAqB,MAAdA,EAAEmC,SAAmBnC,EAAEoC,SAAyB,KAAdpC,EAAEmC,SAG7CE,OAAQ,SAAUrC,GAChB,MAAqB,MAAdA,EAAEmC,SAAmBnC,EAAEoC,SAAyB,KAAdpC,EAAEmC,SAG7CG,QAAS,SAAUtC,GACjB,GAAIuC,GAAYvC,EAAEoC,SAAWpC,EAAEwC,QAAUxC,EAAEyC,SAAWzC,EAAE0C,QACxD,QAAQH,IAA4B,KAAdvC,EAAEmC,SAAgC,IAAdnC,EAAEmC,UAG9CQ,SAAU,SAAU3C,GAClB,MAAqB,MAAdA,EAAEmC,SAGXS,WAAY,SAAU5C,GACpB,MAAqB,MAAdA,EAAEmC,SAMXhD,MAAU,KACVwC,OAAU,KACVC,SAAU,KACVC,SAAU,KAKVrC,YAAa,WACXzF,KAAKc,IAAIkF,GAAG,aAAehG,KAAKe,GAAI,qBAAsB7B,EAAE4J,MAAM9I,KAAK+I,SAAU/I,OACjFA,KAAKc,IAAIkF,GAAG,aAAehG,KAAKe,GAAI,qBAAsB7B,EAAE4J,MAAM9I,KAAKgJ,aAAchJ,OACrFA,KAAKqF,SAASW,GAAG,WAAahG,KAAKe,GAAI7B,EAAE4J,MAAM9I,KAAKiJ,WAAYjJ,QAGlE+I,SAAU,SAAU9C,GAClB,GAAInF,GAAM5B,EAAE+G,EAAEiD,OACdjD,GAAEkD,iBACFlD,EAAEC,cAAcC,yBAA2BnG,KAAKe,GAC3CD,EAAIsI,SAAS,uBAChBtI,EAAMA,EAAIuI,QAAQ,sBAEpB,IAAIxD,GAAQ7F,KAAKI,KAAKkJ,SAASxI,EAAIV,KAAK,SAAU,IAClDJ,MAAKG,UAAU4D,OAAO8B,EAAM7B,MAAO6B,EAAM5B,SACzC,IAAIxC,GAAOzB,IAGXuJ,YAAW,WAAc9H,EAAKkC,cAAiB,IAIjDqF,aAAc,SAAU/C,GACtB,GAAInF,GAAM5B,EAAE+G,EAAEiD,OACdjD,GAAEkD,iBACGrI,EAAIsI,SAAS,uBAChBtI,EAAMA,EAAIuI,QAAQ,uBAEpBrJ,KAAK4H,OAAS0B,SAASxI,EAAIV,KAAK,SAAU,IAC1CJ,KAAKwH,wBAGPyB,WAAY,SAAUhD,GACfjG,KAAK2E,QACN3E,KAAKmI,KAAKlC,IACZA,EAAEkD,iBACFnJ,KAAKwJ,OACIxJ,KAAKsI,OAAOrC,IACrBA,EAAEkD,iBACFnJ,KAAKyJ,SACIzJ,KAAKuI,QAAQtC,IACtBA,EAAEkD,iBACFnJ,KAAK0J,UACI1J,KAAK4I,SAAS3C,IACvBA,EAAEkD,iBACFnJ,KAAK2J,WACI3J,KAAK6I,WAAW5C,KACzBA,EAAEkD,iBACFnJ,KAAK4J,eAITJ,IAAK,WACiB,IAAhBxJ,KAAK4H,OACP5H,KAAK4H,OAAS5H,KAAKI,KAAKmD,OAAS,EAEjCvD,KAAK4H,QAAU,EAEjB5H,KAAKwH,uBACLxH,KAAKyH,cAGPgC,MAAO,WACDzJ,KAAK4H,SAAW5H,KAAKI,KAAKmD,OAAS,EACrCvD,KAAK4H,OAAS,EAEd5H,KAAK4H,QAAU,EAEjB5H,KAAKwH,uBACLxH,KAAKyH,cAGPiC,OAAQ,WACN,GAAI7D,GAAQ7F,KAAKI,KAAKkJ,SAAStJ,KAAK6J,oBAAoBzJ,KAAK,SAAU,IACvEJ,MAAKG,UAAU4D,OAAO8B,EAAM7B,MAAO6B,EAAM5B,UACzCjE,KAAKyH,cAGPkC,QAAS,WACP,GAAIT,GAAS,EACTY,EAAY9J,KAAK6J,oBAAoBlD,WAAWoD,IAAM/J,KAAKc,IAAIkJ,aACnEhK,MAAKc,IAAIyF,WAAWtG,KAAK,SAAUmE,GACjC,MAAIlF,GAAEc,MAAM2G,WAAWoD,IAAM7K,EAAEc,MAAMiK,cAAgBH,GACnDZ,EAAS9E,GACF,GAFT,SAKFpE,KAAK4H,OAASsB,EACdlJ,KAAKwH,uBACLxH,KAAKyH,cAGPmC,UAAW,WACT,GAAIV,GAASlJ,KAAKI,KAAKmD,OAAS,EAC5BuG,EAAY9J,KAAK6J,oBAAoBlD,WAAWoD,IAAM/J,KAAKc,IAAIkJ,aACnEhK,MAAKc,IAAIyF,WAAWtG,KAAK,SAAUmE,GACjC,MAAIlF,GAAEc,MAAM2G,WAAWoD,IAAMD,GAC3BZ,EAAS9E,GACF,GAFT,SAKFpE,KAAK4H,OAASsB,EACdlJ,KAAKwH,uBACLxH,KAAKyH,cAGPD,qBAAsB,WACpBxH,KAAKc,IAAIoJ,KAAK,6BAA6BhC,YAAY,UACvDlI,KAAK6J,oBAAoB7B,SAAS,WAGpC6B,kBAAmB,WACjB,MAAO7J,MAAKc,IAAIyF,SAAS,0BAA4BvG,KAAK4H,OAAS,MAGrEH,WAAY,WACV,GAAI0C,GAAYnK,KAAK6J,oBACjBO,EAAUD,EAAUxD,WAAWoD,IAC/BM,EAAaF,EAAUF,cACvBK,EAAgBtK,KAAKc,IAAIkJ,cACzBO,EAAavK,KAAKc,IAAI0J,WACN,KAAhBxK,KAAK4H,QAAgB5H,KAAK4H,QAAU5H,KAAKI,KAAKmD,OAAS,GAAe,EAAV6G,EAC9DpK,KAAKc,IAAI0J,UAAUJ,EAAUG,GACpBH,EAAUC,EAAaC,GAChCtK,KAAKc,IAAI0J,UAAUJ,EAAUC,EAAaE,EAAaD,IAI3DpD,eAAgB,SAAUtB,GACxB,GAAIC,GAAOzB,EAAGG,EACVoD,EAAO,EACX,KAAKvD,EAAI,EAAGA,EAAIwB,EAAWrC,QACrBvD,KAAKI,KAAKmD,SAAWvD,KAAK8G,SADG1C,IAEjCyB,EAAQD,EAAWxB,GACfuB,EAAQ3F,KAAKI,KAAMyF,KACvBtB,EAAQvE,KAAKI,KAAKmD,OAClBvD,KAAKI,KAAK0D,KAAK+B,GACf8B,GAAQ,6CAA+CpD,EAAQ,QAC/DoD,GAAU9B,EAAM5B,SAASwG,SAAS5E,EAAM7B,OACxC2D,GAAQ,YAEV,OAAOA,IAGTN,cAAe,SAAUF,GACvB,GAAInH,KAAK6G,OAAQ,CACV7G,KAAK6H,WACR7H,KAAK6H,SAAW3I,EAAE,yCAAyCwL,UAAU1K,KAAKc,KAE5E,IAAI6G,GAAOzI,EAAEyL,WAAW3K,KAAK6G,QAAU7G,KAAK6G,OAAOM,GAAgBnH,KAAK6G,MACxE7G,MAAK6H,SAASF,KAAKA,KAIvBL,cAAe,SAAUH,GACvB,GAAInH,KAAK4G,OAAQ,CACV5G,KAAK8H,WACR9H,KAAK8H,SAAW5I,EAAE,yCAAyCoD,SAAStC,KAAKc,KAE3E,IAAI6G,GAAOzI,EAAEyL,WAAW3K,KAAK4G,QAAU5G,KAAK4G,OAAOO,GAAgBnH,KAAK4G,MACxE5G,MAAK8H,SAASH,KAAKA,KAIvBJ,gBAAiB,SAAUI,GACrB3H,KAAK8H,SACP9H,KAAK8H,SAAS8C,OAAOjD,GAErB3H,KAAKc,IAAI+J,OAAOlD,IAIpBD,gBAAiB,SAAUf,GAmBzB,MAjBsC,KAAlC3G,KAAK+G,UAAU+D,QAAQ,OAEzBnE,GACEoD,IAAK,OACLgB,OAAQ/K,KAAKc,IAAIkK,SAASzF,SAAWoB,EAASoD,IAAMpD,EAASsE,WAC7DvE,KAAMC,EAASD,OAGjBC,EAASoE,OAAS,aACXpE,GAASsE,YAEwB,KAAtCjL,KAAK+G,UAAU+D,QAAQ,WACzBnE,EAASD,KAAO,EACgC,KAAvC1G,KAAK+G,UAAU+D,QAAQ,cAChCnE,EAASuE,MAAQ,EACjBvE,EAASD,KAAO,QAEXC,KAIXzH,EAAEI,GAAGC,aAAaoD,SAAWA,GAC7B3D,SAED,SAAUE,GACT,YAiBA,SAASyB,GAASwK,GAChBjM,EAAEgC,OAAOlB,KAAMmL,GACXnL,KAAKoL,QAASpL,KAAKyE,OAAS4G,EAAQrL,KAAKyE,SAhB/C,GAAI4G,GAAU,SAAUzJ,GACtB,GAAI0J,KACJ,OAAO,UAAU9H,EAAM+H,GACjBD,EAAK9H,GACP+H,EAASD,EAAK9H,IAEd5B,EAAK9B,KAAKE,KAAMwD,EAAM,SAAUpD,GAC9BkL,EAAK9H,IAAS8H,EAAK9H,QAAagI,OAAOpL,GACvCmL,EAAShL,MAAM,KAAMR,cAW7BY,GAASC,MAAQ,SAAU6K,GACzB,MAAOvM,GAAEgG,IAAIuG,EAAc,SAAUN,GACnC,MAAO,IAAIxK,GAASwK,MAIxBjM,EAAEgC,OAAOP,EAASf,WAKhB0E,MAAY,KACZoH,QAAY,KACZjH,OAAY,KAGZ2G,OAAY,EACZ/G,QAAY,WAAc,OAAO,GACjCE,MAAY,EACZkG,SAAY,SAAUjK,GAAO,MAAOA,IACpCuF,WAAY,OAGd7G,EAAEI,GAAGC,aAAaoB,SAAWA,GAE7B3B,SAED,SAAUE,GACT,YAiCA,SAAS0D,MA/BT,GAAI+I,GAAMC,KAAKD,KAAO,WAAc,OAAO,GAAIC,OAAOC,WAOlDC,EAAW,SAAUlK,EAAMmK,GAC7B,GAAIC,GAAStM,EAAM2E,EAAS4H,EAAWC,EACnCC,EAAQ,WACV,GAAIC,GAAOT,IAAQM,CACRF,GAAPK,EACFJ,EAAUzC,WAAW4C,EAAOJ,EAAOK,IAEnCJ,EAAU,KACVE,EAAStK,EAAKrB,MAAM8D,EAAS3E,GAC7B2E,EAAU3E,EAAO,MAIrB,OAAO,YAOL,MANA2E,GAAUrE,KACVN,EAAOK,UACPkM,EAAYN,IACPK,IACHA,EAAUzC,WAAW4C,EAAOJ,IAEvBG,GAMXhN,GAAEgC,OAAO0B,EAAQhD,WAIfmB,GAAW,KACXZ,UAAW,KACXkM,GAAW,KACXvL,IAAW,KACXrB,OAAW,KAKX+B,WAAY,SAAUX,EAASV,EAAWV,GACxCO,KAAKqM,GAAYxL,EACjBb,KAAKc,IAAY5B,EAAE2B,GACnBb,KAAKe,GAAYZ,EAAUY,GAAKf,KAAKsM,YAAY7L,KACjDT,KAAKG,UAAYA,EACjBH,KAAKP,OAAYA,EAEbO,KAAKP,OAAOqM,WACd9L,KAAKuM,SAAWT,EAAS9L,KAAKuM,SAAUvM,KAAKP,OAAOqM,WAGtD9L,KAAKyF,eAGP1C,QAAS,WACP/C,KAAKc,IAAIkC,IAAI,IAAMhD,KAAKe,IACxBf,KAAKc,IAAMd,KAAKqM,GAAKrM,KAAKG,UAAY,MAQxC4D,OAAQ,WACN,KAAM,IAAI9E,OAAM,oBAMlB6F,iBAAkB,WAChB,GAAI6B,GAAW3G,KAAKwM,4BAChBC,EAASzM,KAAKc,IAAI2L,QAGtB,OAFA9F,GAASoD,KAAO0C,EAAO1C,IACvBpD,EAASD,MAAQ+F,EAAO/F,KACjBC,GAITzC,MAAO,WACLlE,KAAKc,IAAIoD,SAMXuB,YAAa,WACXzF,KAAKc,IAAIkF,GAAG,SAAWhG,KAAKe,GAAI7B,EAAE4J,MAAM9I,KAAKuM,SAAUvM,QAGzDuM,SAAU,SAAUtG,GACdjG,KAAK0M,YAAYzG,IACrBjG,KAAKG,UAAU8C,QAAQjD,KAAKoD,0BAA0B,IAIxDsJ,YAAa,SAAUC,GACrB,OAAQA,EAAWvE,SACjB,IAAK,IACL,IAAK,IACH,OAAO,EAEX,GAAIuE,EAAWtE,QAAS,OAAQsE,EAAWvE,SACzC,IAAK,IACL,IAAK,IACH,OAAO,MAKflJ,EAAEI,GAAGC,aAAaqD,QAAUA,GAC5B5D,SAED,SAAUE,GACT,YAMA,SAAS0N,GAAS/L,EAASV,EAAWV,GACpCO,KAAKwB,WAAWX,EAASV,EAAWV,GAGtCmN,EAASC,gBACPnG,KAAM,MACNC,SAAU,WACVoD,IAAK,EACL+C,WAAY,YAGdF,EAASG,iBACP,eAAgB,cAAe,YAAa,aAAc,eAC1D,cAAe,SAAU,iBAAkB,eAAgB,cAC3D,kBAAmB,aAAc,QAAS,cAAe,gBACzD,iBAAkB,eAAgB,aAAc,eAChD,gBAAiB,cAAe,eAAgB,aAAc,YAGhE7N,EAAEgC,OAAO0L,EAAShN,UAAWV,EAAEI,GAAGC,aAAaqD,QAAQhD,WAKrDmE,OAAQ,SAAUC,EAAOC,GACvB,GAAI+I,GAAMhN,KAAKoD,yBACX6J,EAAOjN,KAAKqM,GAAGrI,MAAMkJ,UAAUlN,KAAKqM,GAAGvJ,cACvCqK,EAAYlJ,EAASyH,QAAQ1H,EAC7B9E,GAAEkO,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAItB,QAAQzH,EAASK,MAAO6I,GAClCnN,KAAKc,IAAIuM,IAAIL,EAAMC,GACnBjN,KAAKqM,GAAGiB,eAAiBtN,KAAKqM,GAAGvJ,aAAekK,EAAIzJ,QActDiJ,0BAA2B,WACzB,GAAIe,GAAWrO,EAAE,eAAesH,IAAIxG,KAAKwN,YACtCtK,KAAKlD,KAAKoD,0BACTqK,EAAOvO,EAAE,iBAAiBgE,KAAK,KAAKZ,SAASiL,EACjDvN,MAAKc,IAAI8J,OAAO2C,EAChB,IAAI5G,GAAW8G,EAAK9G,UAIpB,OAHAA,GAASoD,KAAO0D,EAAKlI,SAAWvF,KAAKc,IAAI0J,YACzC7D,EAASsE,WAAawC,EAAKlI,SAC3BgI,EAASG,SACF/G,GAGT6G,SAAU,WACR,MAAOtO,GAAEgC,QAEPyM,SAAU3N,KAAKqM,GAAGuB,aAAe5N,KAAKqM,GAAGwB,aAAe,SAAW,QAClEjB,EAASC,eAAgB7M,KAAK8N,eAGnCA,WAAY,SAAW5O,GACrB,GAAI6O,GAAQ7O,EAAE,eAAesH,KAAK,UAAUuH,KAC5C,OAAqB,mBAAVA,GACF,WACL,MAAO/N,MAAKc,IAAI0F,IAAIoG,EAASG,kBAGxB,WACL,GAAIjM,GAAMd,KAAKc,IACXkN,IAIJ,OAHA9O,GAAEe,KAAK2M,EAASG,gBAAiB,SAAU3I,EAAG6J,GAC5CD,EAAOC,GAAYnN,EAAI0F,IAAIyH,KAEtBD,IAGV9O,GAEHkE,uBAAwB,WACtB,MAAOpD,MAAKqM,GAAGrI,MAAMkJ,UAAU,EAAGlN,KAAKqM,GAAGvJ,iBAI9C5D,EAAEI,GAAGC,aAAaqN,SAAWA,GAC7B5N,SAED,SAAUE,GACT,YAIA,SAASgP,GAAWrN,EAASV,EAAWV,GACtCO,KAAKwB,WAAWX,EAASV,EAAWV,GACpCP,EAAE,SAAWiP,EAAe,WAAW3H,KACrCG,SAAU,WACVoD,IAAK,MACLrD,KAAM,QACL0H,aAAavN,GARlB,GAAIsN,GAAe,GAWnBjP,GAAEgC,OAAOgN,EAAWtO,UAAWV,EAAEI,GAAGC,aAAaqN,SAAShN,WAIxDmE,OAAQ,SAAUC,EAAOC,GACvB,GAAI+I,GAAMhN,KAAKqO,0BACXpB,EAAOjN,KAAKqM,GAAGrI,MAAMkJ,UAAUF,EAAIzJ,QACnC4J,EAAYlJ,EAASyH,QAAQ1H,EAC7B9E,GAAEkO,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAItB,QAAQzH,EAASK,MAAO6I,GAClCnN,KAAKc,IAAIuM,IAAIL,EAAMC,GACnBjN,KAAKqM,GAAGnI,OACR,IAAIoK,GAAQtO,KAAKqM,GAAGkC,iBACpBD,GAAME,UAAS,GACfF,EAAMG,QAAQ,YAAazB,EAAIzJ,QAC/B+K,EAAMI,UAAU,YAAa1B,EAAIzJ,QACjC+K,EAAMvK,UAGRsK,wBAAyB,WACvBrO,KAAKqM,GAAGnI,OACR,IAAIoK,GAAQhN,SAASqN,UAAUC,aAC/BN,GAAMI,UAAU,aAAc1O,KAAKqM,GAAGrI,MAAMT,OAC5C,IAAIsL,GAAMP,EAAMpL,KAAK4L,MAAMX,EAC3B,OAAsB,KAAfU,EAAItL,OAAesL,EAAI,GAAKA,EAAI,MAI3C3P,EAAEI,GAAGC,aAAa2O,WAAaA,GAC/BlP,SAMD,SAAUE,GACT,YAMA,SAAS6P,GAAiBlO,EAASV,EAAWV,GAC5CO,KAAKwB,WAAWX,EAASV,EAAWV,GAGtCP,EAAEgC,OAAO6N,EAAgBnP,UAAWV,EAAEI,GAAGC,aAAaqD,QAAQhD,WAM5DmE,OAAQ,SAAUC,EAAOC,GACvB,GAAI+I,GAAMhN,KAAKoD,yBACX4L,EAAMC,OAAOC,eACbZ,EAAQU,EAAIG,WAAW,GACvBR,EAAYL,EAAMc,YACtBT,GAAUU,mBAAmBf,EAAMgB,eACnC,IAAIC,GAAUZ,EAAUtM,WACpB4K,EAAOsC,EAAQrC,UAAUoB,EAAMkB,aAC/BrC,EAAYlJ,EAASyH,QAAQ1H,EAC7B9E,GAAEkO,QAAQD,KACZF,EAAOE,EAAU,GAAKF,EACtBE,EAAYA,EAAU,IAExBH,EAAMA,EAAItB,QAAQzH,EAASK,MAAO6I,GAClCmB,EAAMe,mBAAmBf,EAAMgB,gBAC/BhB,EAAMmB,gBACN,IAAIC,GAAOpO,SAASqO,eAAe3C,EAAMC,EACzCqB,GAAMsB,WAAWF,GACjBpB,EAAMuB,SAASH,EAAM1C,EAAIzJ,QACzB+K,EAAME,UAAS,GACfQ,EAAIc,kBACJd,EAAIe,SAASzB,IAef9B,0BAA2B,WACzB,GAAI8B,GAAQW,OAAOC,eAAeC,WAAW,GAAGC,aAC5CM,EAAOpO,SAAS0O,cAAc,OAClC1B,GAAMsB,WAAWF,GACjBpB,EAAMe,mBAAmBK,GACzBpB,EAAMmB,gBACN,IAAIQ,GAAQ/Q,EAAEwQ,GACV/I,EAAWsJ,EAAMxD,QACrB9F,GAASD,MAAQ1G,KAAKc,IAAI2L,SAAS/F,KACnCC,EAASoD,KAAOkG,EAAM1K,SAAWvF,KAAKc,IAAI2L,SAAS1C,IACnDpD,EAASsE,WAAagF,EAAM1K,QAC5B,IAAI2K,GAAMlQ,KAAKc,IAAIqP,KAAK,QAAUnQ,KAAKc,IAAI0F,IAAI,YAE/C,OADY,QAAR0J,IAAiBvJ,EAASD,MAAQ1G,KAAKoQ,SAAStP,IAAIuP,SACjD1J,GAWTvD,uBAAwB,WACtB,GAAIkL,GAAQW,OAAOC,eAAeC,WAAW,GACzCR,EAAYL,EAAMc,YAEtB,OADAT,GAAUU,mBAAmBf,EAAMgB,gBAC5BX,EAAUtM,WAAW6K,UAAU,EAAGoB,EAAMkB,gBAInDtQ,EAAEI,GAAGC,aAAawP,gBAAkBA,GACpC/P"} \ No newline at end of file +{"version":3,"file":"dist/jquery.textcomplete.min.js","sources":["dist/jquery.textcomplete.js"],"names":["factory","define","amd","module","exports","$","require","jQuery","Error","warn","message","console","id","fn","textcomplete","strategies","option","args","Array","prototype","slice","call","arguments","this","each","self","$this","completer","data","_oid","Completer","shift","apply","removeData","obj","name","register","Strategy","parse","el","$el","element","uniqueId","views","extend","_getDefaults","is","isContentEditable","contentEditable","ownerDocument","activeElement","initialize","one","adapter","CKEDITOR","on","event","editor","once","event2","editable","lock","func","locked","queuedArgsToReplay","unshift","replayOrFree","replayArgs","undefined","isString","Object","toString","isFunction","DEFAULTS","appendTo","className","dropdownClassName","maxCount","zIndex","dropdown","$iframe","get","prop","document","window","frames","length","iframeIndex","frameElement","Dropdown","Adapter","viewName","selectionEnd","destroy","off","deactivate","trigger","text","skipUnchangedTerm","getTextFromHeadToCaret","searchQuery","_extractSearchQuery","term","_term","_search","fire","eventName","push","select","value","strategy","e","focus","_clearAtNext","i","context","matchRegexp","match","index","free","search","stillSearching","shown","activate","clear","setPosition","getCaretPosition","render","_zip","map","createElement","_data","$inputEl","listPosition","height","_i","_bindEvents","dropdownViews","$window","include","zippedData","datum","elem","idProperty","originalEvent","keepTextCompleteDropdown","key","view","commands","SKIP_DEFAULT","KEY_UP","KEY_DOWN","KEY_ENTER","KEY_PAGEUP","KEY_PAGEDOWN","KEY_ESCAPE","$parent","addClass","attr","css","display","left","position","footer","header","placement","remove","contentsHtml","_buildContents","unzippedData","d","removeAttr","_renderHeader","_renderFooter","_renderContents","_fitToBottom","_fitToRight","_activateIndexedItem","_setScroll","noResultsMessage","_renderNoResultsMessage","pos","add","parents","top","scrollTop","scrollLeft","_applyPlacement","html","_index","_$header","_$footer","_$noResultsMessage","show","hide","removeClass","isUp","keyCode","ctrlKey","isDown","isEnter","modifiers","altKey","metaKey","shiftKey","completeOnSpace","isPageup","isPagedown","isEscape","proxy","_onClick","_onMouseover","_onKeydown","target","preventDefault","hasClass","closest","parseInt","setTimeout","type","command","onKeydown","_defaultKeydown","_up","_down","_enter","_pageup","_pagedown","_getActiveElement","threshold","innerHeight","children","outerHeight","find","$activeEl","itemTop","itemHeight","visibleHeight","visibleTop","template","prependTo","before","append","windowScrollBottom","offset","tolerance","lastOffset","width","maxLeft","indexOf","bottom","parent","lineHeight","right","options","cache","memoize","memo","callback","concat","strategiesArray","params","strategyObj","replace","now","Date","getTime","debounce","wait","timeout","timestamp","result","later","last","constructor","_onKeyup","_getCaretRelativePosition","parentOffset","offsetParent","_skipSearch","clickEvent","Textarea","pre","post","substring","newSubstr","isArray","val","selectionStart","p","getCaretCoordinates","_calculateLineHeight","isNaN","parentNode","temp","nodeName","style","setAttribute","fontFamily","fontSize","innerHTML","appendChild","clientHeight","removeChild","IETextarea","sentinelChar","insertBefore","range","createTextRange","collapse","moveEnd","moveStart","selection","createRange","arr","split","ContentEditable","sel","getSelection","getRangeAt","cloneRange","selectNodeContents","startContainer","content","startOffset","deleteContents","preWrapper","postWrapper","childNode","lastOfPre","fragment","createDocumentFragment","firstChild","insertNode","setStartAfter","removeAllRanges","addRange","node","$node","iframePosition","CKEditor","instances","domEvent","isBrowser","debug","querySelector","div","body","computed","getComputedStyle","currentStyle","whiteSpace","wordWrap","visibility","properties","forEach","isFirefox","scrollHeight","overflowY","overflow","textContent","span","coordinates","offsetTop","offsetLeft","backgroundColor","mozInnerScreenX"],"mappings":"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"} \ No newline at end of file diff --git a/main/inc/lib/online.inc.php b/main/inc/lib/online.inc.php index c233c0792c..9562abebc4 100755 --- a/main/inc/lib/online.inc.php +++ b/main/inc/lib/online.inc.php @@ -149,8 +149,7 @@ function online_logout($user_id = null, $logout_redirect = false) } } - require_once api_get_path(SYS_PATH) . 'main/chat/chat_functions.lib.php'; - exit_of_chat($user_id); + CourseChatUtils::exitChat($user_id); session_regenerate_id(); Session::destroy(); if ($logout_redirect) { diff --git a/main/template/default/chat/chat.tpl b/main/template/default/chat/chat.tpl new file mode 100644 index 0000000000..beeb5dac7e --- /dev/null +++ b/main/template/default/chat/chat.tpl @@ -0,0 +1,342 @@ +
    +
    +
    +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      + {{ 'Message'|get_lang }} + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/tests/main/chat/chat_functions.lib.test.php b/tests/main/chat/chat_functions.lib.test.php index f4da104e12..c10a8dfb5c 100755 --- a/tests/main/chat/chat_functions.lib.test.php +++ b/tests/main/chat/chat_functions.lib.test.php @@ -1,5 +1,4 @@ tcourse = null; } - - - function testuser_connected_in_chat () { - $course_code = 'COURSETEST'; - $user_id = 1; - $res = user_connected_in_chat($user_id); - $this->assertTrue(is_bool($res)); - } - - - function testUsersListInChat () { - $course_code = 'COURSETEST'; - $course_info = api_get_course_info($course_code); - $database_name = $course_info['dbName']; - $res = users_list_in_chat($database_name); - $this->assertTrue(is_array($res)); - //var_dump($res); - } - function CreateChatConnection($database_name) { $session_id = 1; $tbl_chat_connected = Database::get_main_table(TABLE_MAIN_CHAT); @@ -51,29 +31,5 @@ class TestChatFunctions extends UnitTestCase { } Database::query($query); } - - - function testExitOfChat () { - $course_code = 'COURSETEST'; - $course_info = api_get_course_info($course_code); - $database_name = $course_info['dbName']; - $this->CreateChatConnection($database_name); - $user_id = 1; - $res = exit_of_chat($user_id); - //$resu = $this->tcourse->delete_course($course_code); - $this->assertTrue(is_null($res)); - //var_dump($res); - } - - - function testDisconnectUserOfChat() { - $_SESSION['is_courseAdmin'] = 1; - $course_code = 'COURSETEST'; - $course_info = api_get_course_info($course_code); - $database_name = $course_info['dbName']; - $this->CreateChatConnection($database_name); - $res = disconnect_user_of_chat($database_name); - $this->assertTrue(is_null($res)); - } } ?>