diff options
Diffstat (limited to 'SwifTools')
104 files changed, 3388 insertions, 2615 deletions
diff --git a/SwifTools/Application/ApplicationPathProvider.cpp b/SwifTools/Application/ApplicationPathProvider.cpp index aa7e609..77457ef 100644 --- a/SwifTools/Application/ApplicationPathProvider.cpp +++ b/SwifTools/Application/ApplicationPathProvider.cpp @@ -1,14 +1,14 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ +#include <SwifTools/Application/ApplicationPathProvider.h> + #include <boost/filesystem.hpp> -#include <iostream> -#include <SwifTools/Application/ApplicationPathProvider.h> -#include <Swiften/Base/foreach.h> +#include <Swiften/Base/Log.h> #include <Swiften/Base/Paths.h> namespace Swift { @@ -20,29 +20,29 @@ ApplicationPathProvider::~ApplicationPathProvider() { } boost::filesystem::path ApplicationPathProvider::getProfileDir(const std::string& profile) const { - boost::filesystem::path result(getHomeDir() / profile); - try { - boost::filesystem::create_directory(result); - } - catch (const boost::filesystem::filesystem_error& e) { - std::cerr << "ERROR: " << e.what() << std::endl; - } - return result; + boost::filesystem::path result(getHomeDir() / profile); + try { + boost::filesystem::create_directory(result); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << e.what(); + } + return result; } boost::filesystem::path ApplicationPathProvider::getResourcePath(const std::string& resource) const { - std::vector<boost::filesystem::path> resourcePaths = getResourceDirs(); - foreach(const boost::filesystem::path& resourcePath, resourcePaths) { - boost::filesystem::path r(resourcePath / resource); - if (boost::filesystem::exists(r)) { - return r; - } - } - return boost::filesystem::path(); + std::vector<boost::filesystem::path> resourcePaths = getResourceDirs(); + for (const auto& resourcePath : resourcePaths) { + boost::filesystem::path r(resourcePath / resource); + if (boost::filesystem::exists(r)) { + return r; + } + } + return boost::filesystem::path(); } boost::filesystem::path ApplicationPathProvider::getExecutableDir() const { - return Paths::getExecutablePath(); + return Paths::getExecutablePath(); } } diff --git a/SwifTools/Application/ApplicationPathProvider.h b/SwifTools/Application/ApplicationPathProvider.h index d5beb8d..399ac5d 100644 --- a/SwifTools/Application/ApplicationPathProvider.h +++ b/SwifTools/Application/ApplicationPathProvider.h @@ -1,34 +1,35 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/filesystem.hpp> -#include <vector> #include <string> +#include <vector> + +#include <boost/filesystem.hpp> namespace Swift { - class ApplicationPathProvider { - public: - ApplicationPathProvider(const std::string& applicationName); - virtual ~ApplicationPathProvider(); + class ApplicationPathProvider { + public: + ApplicationPathProvider(const std::string& applicationName); + virtual ~ApplicationPathProvider(); - virtual boost::filesystem::path getHomeDir() const = 0; - virtual boost::filesystem::path getDataDir() const = 0; - virtual boost::filesystem::path getExecutableDir() const; - boost::filesystem::path getProfileDir(const std::string& profile) const; - boost::filesystem::path getResourcePath(const std::string& resource) const; + virtual boost::filesystem::path getHomeDir() const = 0; + virtual boost::filesystem::path getDataDir() const = 0; + boost::filesystem::path getExecutableDir() const; + boost::filesystem::path getProfileDir(const std::string& profile) const; + boost::filesystem::path getResourcePath(const std::string& resource) const; - protected: - virtual std::vector<boost::filesystem::path> getResourceDirs() const = 0; - const std::string& getApplicationName() const { - return applicationName; - } + protected: + virtual std::vector<boost::filesystem::path> getResourceDirs() const = 0; + const std::string& getApplicationName() const { + return applicationName; + } - private: - std::string applicationName; - }; + private: + std::string applicationName; + }; } diff --git a/SwifTools/Application/CocoaApplication.h b/SwifTools/Application/CocoaApplication.h index 3b19d28..4b366c6 100644 --- a/SwifTools/Application/CocoaApplication.h +++ b/SwifTools/Application/CocoaApplication.h @@ -1,19 +1,21 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <memory> + namespace Swift { - class CocoaApplication { - public: - CocoaApplication(); - ~CocoaApplication(); + class CocoaApplication { + public: + CocoaApplication(); + ~CocoaApplication(); - private: - class Private; - Private* d; - }; + private: + class Private; + const std::unique_ptr<Private> d; + }; } diff --git a/SwifTools/Application/CocoaApplication.mm b/SwifTools/Application/CocoaApplication.mm index cbb5f2e..f058ed4 100644 --- a/SwifTools/Application/CocoaApplication.mm +++ b/SwifTools/Application/CocoaApplication.mm @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <SwifTools/Application/CocoaApplication.h> #include <AppKit/AppKit.h> @@ -6,19 +12,17 @@ namespace Swift { class CocoaApplication::Private { - public: - NSAutoreleasePool* autoReleasePool_; + public: + NSAutoreleasePool* autoReleasePool_; }; -CocoaApplication::CocoaApplication() { - d = new CocoaApplication::Private(); - NSApplicationLoad(); - d->autoReleasePool_ = [[NSAutoreleasePool alloc] init]; +CocoaApplication::CocoaApplication() : d(new Private()) { + NSApplicationLoad(); + d->autoReleasePool_ = [[NSAutoreleasePool alloc] init]; } CocoaApplication::~CocoaApplication() { - [d->autoReleasePool_ release]; - delete d; + [d->autoReleasePool_ release]; } } diff --git a/SwifTools/Application/MacOSXApplicationPathProvider.cpp b/SwifTools/Application/MacOSXApplicationPathProvider.cpp index 41b0e4c..d7b99b9 100644 --- a/SwifTools/Application/MacOSXApplicationPathProvider.cpp +++ b/SwifTools/Application/MacOSXApplicationPathProvider.cpp @@ -1,34 +1,35 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Application/MacOSXApplicationPathProvider.h> -#include <iostream> #include <mach-o/dyld.h> +#include <Swiften/Base/Log.h> + namespace Swift { MacOSXApplicationPathProvider::MacOSXApplicationPathProvider(const std::string& name) : ApplicationPathProvider(name) { - resourceDirs.push_back(getExecutableDir() / "../Resources"); - resourceDirs.push_back(getExecutableDir() / "../resources"); // Development + resourceDirs.push_back(getExecutableDir() / "../Resources"); + resourceDirs.push_back(getExecutableDir() / "../resources"); // Development } boost::filesystem::path MacOSXApplicationPathProvider::getDataDir() const { - boost::filesystem::path result(getHomeDir() / "Library/Application Support" / getApplicationName()); - try { - boost::filesystem::create_directory(result); - } - catch (const boost::filesystem::filesystem_error& e) { - std::cerr << "ERROR: " << e.what() << std::endl; - } - return result; + boost::filesystem::path result(getHomeDir() / "Library/Application Support" / getApplicationName()); + try { + boost::filesystem::create_directory(result); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << e.what(); + } + return result; } boost::filesystem::path MacOSXApplicationPathProvider::getHomeDir() const { - return boost::filesystem::path(getenv("HOME")); + return boost::filesystem::path(getenv("HOME")); } } diff --git a/SwifTools/Application/MacOSXApplicationPathProvider.h b/SwifTools/Application/MacOSXApplicationPathProvider.h index 760ea9b..9d8f619 100644 --- a/SwifTools/Application/MacOSXApplicationPathProvider.h +++ b/SwifTools/Application/MacOSXApplicationPathProvider.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,18 +9,18 @@ #include <SwifTools/Application/ApplicationPathProvider.h> namespace Swift { - class MacOSXApplicationPathProvider : public ApplicationPathProvider { - public: - MacOSXApplicationPathProvider(const std::string& name); + class MacOSXApplicationPathProvider : public ApplicationPathProvider { + public: + MacOSXApplicationPathProvider(const std::string& name); - virtual boost::filesystem::path getHomeDir() const; - boost::filesystem::path getDataDir() const; + virtual boost::filesystem::path getHomeDir() const; + boost::filesystem::path getDataDir() const; - virtual std::vector<boost::filesystem::path> getResourceDirs() const { - return resourceDirs; - } + virtual std::vector<boost::filesystem::path> getResourceDirs() const { + return resourceDirs; + } - private: - std::vector<boost::filesystem::path> resourceDirs; - }; + private: + std::vector<boost::filesystem::path> resourceDirs; + }; } diff --git a/SwifTools/Application/PlatformApplicationPathProvider.h b/SwifTools/Application/PlatformApplicationPathProvider.h index 7ea4b35..5de91fe 100644 --- a/SwifTools/Application/PlatformApplicationPathProvider.h +++ b/SwifTools/Application/PlatformApplicationPathProvider.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -11,16 +11,16 @@ #if defined(SWIFTEN_PLATFORM_MACOSX) #include <SwifTools/Application/MacOSXApplicationPathProvider.h> namespace Swift { - typedef MacOSXApplicationPathProvider PlatformApplicationPathProvider; + typedef MacOSXApplicationPathProvider PlatformApplicationPathProvider; } #elif defined(SWIFTEN_PLATFORM_WIN32) #include <SwifTools/Application/WindowsApplicationPathProvider.h> namespace Swift { - typedef WindowsApplicationPathProvider PlatformApplicationPathProvider; + typedef WindowsApplicationPathProvider PlatformApplicationPathProvider; } #else #include <SwifTools/Application/UnixApplicationPathProvider.h> namespace Swift { - typedef UnixApplicationPathProvider PlatformApplicationPathProvider; + typedef UnixApplicationPathProvider PlatformApplicationPathProvider; } #endif diff --git a/SwifTools/Application/SConscript b/SwifTools/Application/SConscript index 32924fc..29bcceb 100644 --- a/SwifTools/Application/SConscript +++ b/SwifTools/Application/SConscript @@ -1,27 +1,27 @@ Import("swiftools_env", "env") sources = [ - "ApplicationPathProvider.cpp", - ] + "ApplicationPathProvider.cpp", + ] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : - sources += [ - "CocoaApplication.mm", - "MacOSXApplicationPathProvider.cpp", - ] + sources += [ + "CocoaApplication.mm", + "MacOSXApplicationPathProvider.cpp", + ] elif swiftools_env["PLATFORM"] == "win32" : - sources += [ - "WindowsApplicationPathProvider.cpp" - ] + sources += [ + "WindowsApplicationPathProvider.cpp" + ] else : - sources += [ - "UnixApplicationPathProvider.cpp" - ] + sources += [ + "UnixApplicationPathProvider.cpp" + ] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) if swiftools_env["PLATFORM"] != "darwin" or swiftools_env["target"] == "native" : - env.Append(UNITTEST_SOURCES = [ - File("UnitTest/ApplicationPathProviderTest.cpp") - ]) + env.Append(UNITTEST_SOURCES = [ + File("UnitTest/ApplicationPathProviderTest.cpp") + ]) diff --git a/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp b/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp index e7a47a7..433b379 100644 --- a/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp +++ b/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp @@ -1,51 +1,54 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ -#include <cppunit/extensions/HelperMacros.h> -#include <cppunit/extensions/TestFactoryRegistry.h> #include <string> + #include <boost/algorithm/string.hpp> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + #include <Swiften/Base/Path.h> + #include <SwifTools/Application/PlatformApplicationPathProvider.h> using namespace Swift; class ApplicationPathProviderTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(ApplicationPathProviderTest); - CPPUNIT_TEST(testGetDataDir); - CPPUNIT_TEST(testGetExecutableDir); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - testling_ = new PlatformApplicationPathProvider("SwiftTest"); - } - - void tearDown() { - delete testling_; - } - - void testGetDataDir() { - boost::filesystem::path dir = testling_->getDataDir(); - - CPPUNIT_ASSERT(boost::filesystem::exists(dir)); - CPPUNIT_ASSERT(boost::filesystem::is_directory(dir)); - - boost::filesystem::remove(dir); - } - - void testGetExecutableDir() { - boost::filesystem::path dir = testling_->getExecutableDir(); - CPPUNIT_ASSERT(boost::filesystem::is_directory(dir)); - CPPUNIT_ASSERT(boost::ends_with(pathToString(dir), "UnitTest")); - } - - private: - ApplicationPathProvider* testling_; + CPPUNIT_TEST_SUITE(ApplicationPathProviderTest); + CPPUNIT_TEST(testGetDataDir); + CPPUNIT_TEST(testGetExecutableDir); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + testling_ = new PlatformApplicationPathProvider("SwiftTest"); + } + + void tearDown() { + delete testling_; + } + + void testGetDataDir() { + boost::filesystem::path dir = testling_->getDataDir(); + + CPPUNIT_ASSERT(boost::filesystem::exists(dir)); + CPPUNIT_ASSERT(boost::filesystem::is_directory(dir)); + + boost::filesystem::remove(dir); + } + + void testGetExecutableDir() { + boost::filesystem::path dir = testling_->getExecutableDir(); + CPPUNIT_ASSERT(boost::filesystem::is_directory(dir)); + CPPUNIT_ASSERT(boost::ends_with(pathToString(dir), "UnitTest")); + } + + private: + ApplicationPathProvider* testling_; }; CPPUNIT_TEST_SUITE_REGISTRATION(ApplicationPathProviderTest); diff --git a/SwifTools/Application/UnixApplicationPathProvider.cpp b/SwifTools/Application/UnixApplicationPathProvider.cpp index 784256f..a345766 100644 --- a/SwifTools/Application/UnixApplicationPathProvider.cpp +++ b/SwifTools/Application/UnixApplicationPathProvider.cpp @@ -1,60 +1,62 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Application/UnixApplicationPathProvider.h> -#include <unistd.h> +#include <stdlib.h> + #include <boost/algorithm/string.hpp> -#include <iostream> +#include <unistd.h> + +#include <Swiften/Base/Log.h> #include <Swiften/Base/String.h> -#include <Swiften/Base/foreach.h> namespace Swift { UnixApplicationPathProvider::UnixApplicationPathProvider(const std::string& name) : ApplicationPathProvider(name) { - resourceDirs.push_back(getExecutableDir() / "../resources"); // Development - resourceDirs.push_back(getExecutableDir() / ".." / "share" / boost::to_lower_copy(getApplicationName())); // Local install - char* xdgDataDirs = getenv("XDG_DATA_DIRS"); - if (xdgDataDirs) { - std::vector<std::string> dataDirs = String::split(xdgDataDirs, ':'); - if (!dataDirs.empty()) { - foreach(const std::string& dir, dataDirs) { - resourceDirs.push_back(boost::filesystem::path(dir) / "swift"); - } - return; - } - } - resourceDirs.push_back("/usr/local/share/" + boost::to_lower_copy(getApplicationName())); - resourceDirs.push_back("/usr/share/" + boost::to_lower_copy(getApplicationName())); + resourceDirs.push_back(getExecutableDir() / "../resources"); // Development + resourceDirs.push_back(getExecutableDir() / ".." / "share" / boost::to_lower_copy(getApplicationName())); // Local install + char* xdgDataDirs = getenv("XDG_DATA_DIRS"); + if (xdgDataDirs) { + std::vector<std::string> dataDirs = String::split(xdgDataDirs, ':'); + if (!dataDirs.empty()) { + for (const auto& dir : dataDirs) { + resourceDirs.push_back(boost::filesystem::path(dir) / "swift"); + } + return; + } + } + resourceDirs.push_back("/usr/local/share/" + boost::to_lower_copy(getApplicationName())); + resourceDirs.push_back("/usr/share/" + boost::to_lower_copy(getApplicationName())); } boost::filesystem::path UnixApplicationPathProvider::getHomeDir() const { - char* home = getenv("HOME"); - return home ? boost::filesystem::path(home) : boost::filesystem::path(); + char* home = getenv("HOME"); + return home ? boost::filesystem::path(home) : boost::filesystem::path(); } boost::filesystem::path UnixApplicationPathProvider::getDataDir() const { - char* xdgDataHome = getenv("XDG_DATA_HOME"); - std::string dataDir; - if (xdgDataHome) { - dataDir = std::string(xdgDataHome); - } - - boost::filesystem::path dataPath = (dataDir.empty() ? - getHomeDir() / ".local" / "share" - : boost::filesystem::path(dataDir)) / boost::to_lower_copy(getApplicationName()); - - try { - boost::filesystem::create_directories(dataPath); - } - catch (const boost::filesystem::filesystem_error& e) { - std::cerr << "ERROR: " << e.what() << std::endl; - } - return dataPath; + char* xdgDataHome = getenv("XDG_DATA_HOME"); + std::string dataDir; + if (xdgDataHome) { + dataDir = std::string(xdgDataHome); + } + + boost::filesystem::path dataPath = (dataDir.empty() ? + getHomeDir() / ".local" / "share" + : boost::filesystem::path(dataDir)) / boost::to_lower_copy(getApplicationName()); + + try { + boost::filesystem::create_directories(dataPath); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << "file system error: " << e.what(); + } + return dataPath; } } diff --git a/SwifTools/Application/UnixApplicationPathProvider.h b/SwifTools/Application/UnixApplicationPathProvider.h index 198e4ee..9e27a93 100644 --- a/SwifTools/Application/UnixApplicationPathProvider.h +++ b/SwifTools/Application/UnixApplicationPathProvider.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,19 +9,19 @@ #include <SwifTools/Application/ApplicationPathProvider.h> namespace Swift { - class UnixApplicationPathProvider : public ApplicationPathProvider { - public: - UnixApplicationPathProvider(const std::string& name); + class UnixApplicationPathProvider : public ApplicationPathProvider { + public: + UnixApplicationPathProvider(const std::string& name); - virtual boost::filesystem::path getHomeDir() const; - boost::filesystem::path getDataDir() const; + virtual boost::filesystem::path getHomeDir() const; + boost::filesystem::path getDataDir() const; - virtual std::vector<boost::filesystem::path> getResourceDirs() const { - return resourceDirs; - } + virtual std::vector<boost::filesystem::path> getResourceDirs() const { + return resourceDirs; + } - private: - std::vector<boost::filesystem::path> resourceDirs; - }; + private: + std::vector<boost::filesystem::path> resourceDirs; + }; } diff --git a/SwifTools/Application/WindowsApplicationPathProvider.cpp b/SwifTools/Application/WindowsApplicationPathProvider.cpp index 2c61208..e90214c 100644 --- a/SwifTools/Application/WindowsApplicationPathProvider.cpp +++ b/SwifTools/Application/WindowsApplicationPathProvider.cpp @@ -1,36 +1,38 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Application/WindowsApplicationPathProvider.h> -#include <windows.h> #include <cassert> + +#include <windows.h> + #include <Swiften/Base/String.h> namespace Swift { WindowsApplicationPathProvider::WindowsApplicationPathProvider(const std::string& name) : ApplicationPathProvider(name) { - resourceDirs.push_back(getExecutableDir()); - resourceDirs.push_back(getExecutableDir() / "../resources"); // Development + resourceDirs.push_back(getExecutableDir()); + resourceDirs.push_back(getExecutableDir() / "../resources"); // Development } boost::filesystem::path WindowsApplicationPathProvider::getDataDir() const { - wchar_t* appDirRaw = _wgetenv(L"APPDATA"); - assert(appDirRaw); - boost::filesystem::path result( - boost::filesystem::path(appDirRaw) / getApplicationName()); - boost::filesystem::create_directory(result); - return result; + wchar_t* appDirRaw = _wgetenv(L"APPDATA"); + assert(appDirRaw); + boost::filesystem::path result( + boost::filesystem::path(appDirRaw) / getApplicationName()); + boost::filesystem::create_directory(result); + return result; } boost::filesystem::path WindowsApplicationPathProvider::getHomeDir() const { - //FIXME: This should be My Documents - wchar_t* homeDirRaw = _wgetenv(L"USERPROFILE"); - assert(homeDirRaw); - return boost::filesystem::path(homeDirRaw); + //FIXME: This should be My Documents + wchar_t* homeDirRaw = _wgetenv(L"USERPROFILE"); + assert(homeDirRaw); + return boost::filesystem::path(homeDirRaw); } diff --git a/SwifTools/Application/WindowsApplicationPathProvider.h b/SwifTools/Application/WindowsApplicationPathProvider.h index a4e8668..bf8dada 100644 --- a/SwifTools/Application/WindowsApplicationPathProvider.h +++ b/SwifTools/Application/WindowsApplicationPathProvider.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,18 +9,18 @@ #include <SwifTools/Application/ApplicationPathProvider.h> namespace Swift { - class WindowsApplicationPathProvider : public ApplicationPathProvider { - public: - WindowsApplicationPathProvider(const std::string& name); + class WindowsApplicationPathProvider : public ApplicationPathProvider { + public: + WindowsApplicationPathProvider(const std::string& name); - boost::filesystem::path getDataDir() const; - boost::filesystem::path getHomeDir() const; + boost::filesystem::path getDataDir() const; + boost::filesystem::path getHomeDir() const; - virtual std::vector<boost::filesystem::path> getResourceDirs() const { - return resourceDirs; - } + virtual std::vector<boost::filesystem::path> getResourceDirs() const { + return resourceDirs; + } - private: - std::vector<boost::filesystem::path> resourceDirs; - }; + private: + std::vector<boost::filesystem::path> resourceDirs; + }; } diff --git a/SwifTools/AutoUpdater/AutoUpdater.cpp b/SwifTools/AutoUpdater/AutoUpdater.cpp index e424f3b..342b1d9 100644 --- a/SwifTools/AutoUpdater/AutoUpdater.cpp +++ b/SwifTools/AutoUpdater/AutoUpdater.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/AutoUpdater/AutoUpdater.h> diff --git a/SwifTools/AutoUpdater/AutoUpdater.h b/SwifTools/AutoUpdater/AutoUpdater.h index 77e0045..fdd3a91 100644 --- a/SwifTools/AutoUpdater/AutoUpdater.h +++ b/SwifTools/AutoUpdater/AutoUpdater.h @@ -1,16 +1,41 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <string> + +#include <boost/signals2.hpp> + namespace Swift { - class AutoUpdater { - public: - virtual ~AutoUpdater(); + class AutoUpdater { + public: + enum class State { + NotCheckedForUpdatesYet, + NoUpdateAvailable, + CheckingForUpdate, + ErrorCheckingForUpdate, + DownloadingUpdate, + RestartToInstallUpdate + }; + + public: + virtual ~AutoUpdater(); + + virtual void setAppcastFeed(const std::string& appcastFeed) = 0; + virtual void checkForUpdates() = 0; + virtual State getCurrentState() = 0; + virtual bool applicationInstallationLocationWritable() = 0; - virtual void checkForUpdates() = 0; - }; + public: + /** + * Emit this signal if a new version of the software has been downloaded + * and the user needs to be notified so they can quit the app and start + * the newer version. + */ + boost::signals2::signal<void(State)> onUpdateStateChanged; + }; } diff --git a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp index d2e89ac..9ae8c09 100644 --- a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp +++ b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h> @@ -16,18 +16,18 @@ namespace Swift { bool PlatformAutoUpdaterFactory::isSupported() const { #ifdef HAVE_SPARKLE - return true; + return true; #else - return false; + return false; #endif } AutoUpdater* PlatformAutoUpdaterFactory::createAutoUpdater(const std::string& appcastURL) { #ifdef HAVE_SPARKLE - return new SparkleAutoUpdater(appcastURL); + return new SparkleAutoUpdater(appcastURL); #else - (void) appcastURL; - return NULL; + (void) appcastURL; + return nullptr; #endif } diff --git a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h index 59df238..9942d6a 100644 --- a/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h +++ b/SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h @@ -1,18 +1,18 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <string> namespace Swift { - class AutoUpdater; + class AutoUpdater; - class PlatformAutoUpdaterFactory { - public: - bool isSupported() const; + class PlatformAutoUpdaterFactory { + public: + bool isSupported() const; - AutoUpdater* createAutoUpdater(const std::string& appcastURL); - }; + AutoUpdater* createAutoUpdater(const std::string& appcastURL); + }; } diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.h b/SwifTools/AutoUpdater/SparkleAutoUpdater.h index f367945..26e08da 100644 --- a/SwifTools/AutoUpdater/SparkleAutoUpdater.h +++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.h @@ -1,24 +1,38 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <memory> #include <string> + #include <SwifTools/AutoUpdater/AutoUpdater.h> namespace Swift { - class SparkleAutoUpdater : public AutoUpdater { - public: - SparkleAutoUpdater(const std::string& url); - ~SparkleAutoUpdater(); + /** + * @brief The SparkleAutoUpdater class provides integration with Sparkle. + * This enables automatic silent background updates. If using this in Qt you + * need to emit a NSApplicationWillTerminateNotification before you quit + * the application. + */ + class SparkleAutoUpdater : public AutoUpdater { + public: + SparkleAutoUpdater(const std::string& appcastFeed); + ~SparkleAutoUpdater() override; + + void setAppcastFeed(const std::string& appcastFeed) override; + void checkForUpdates() override; + State getCurrentState() override; + bool applicationInstallationLocationWritable() override; + + private: + void setCurrentState(State updatedState); - void checkForUpdates(); - - private: - class Private; - Private* d; - }; + private: + class Private; + const std::unique_ptr<Private> d; + }; } diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm index c35abc8..274ab3c 100644 --- a/SwifTools/AutoUpdater/SparkleAutoUpdater.mm +++ b/SwifTools/AutoUpdater/SparkleAutoUpdater.mm @@ -1,34 +1,158 @@ +/* + * Copyright (c) 2016-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <SwifTools/AutoUpdater/SparkleAutoUpdater.h> + +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> + #include <Cocoa/Cocoa.h> #include <Sparkle/Sparkle.h> +#include <Swiften/Base/Log.h> + +#include <SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h> +#include <SwifTools/Cocoa/CocoaUtil.h> + namespace Swift { class SparkleAutoUpdater::Private { - public: - SUUpdater* updater; + public: + SUUpdater* updater; + boost::intrusive_ptr<SparkleAutoUpdaterDelegate> delegate; + State currentState = State::NotCheckedForUpdatesYet; }; -SparkleAutoUpdater::SparkleAutoUpdater(const std::string& url) { - d = new Private; +SparkleAutoUpdater::SparkleAutoUpdater(const std::string& appcastFeed) : d(new Private()) { + d->updater = [SUUpdater sharedUpdater]; + [d->updater retain]; - d->updater = [SUUpdater sharedUpdater]; - [d->updater retain]; - [d->updater setAutomaticallyChecksForUpdates: true]; + d->delegate = boost::intrusive_ptr<SparkleAutoUpdaterDelegate>([[SparkleAutoUpdaterDelegate alloc] init], false); + [d->delegate.get() setOnNewUpdateState: [&](AutoUpdater::State updatedState){ + setCurrentState(updatedState); + }]; + [d->updater setDelegate: d->delegate.get()]; - NSURL* nsurl = [NSURL URLWithString: - [NSString stringWithUTF8String: url.c_str()]]; - [d->updater setFeedURL: nsurl]; + [d->updater setAutomaticallyChecksForUpdates: true]; + // Automatically check for an update after a day. + [d->updater setUpdateCheckInterval: 86400]; + + auto canDoSilentUpdates = applicationInstallationLocationWritable(); + [d->updater setAutomaticallyDownloadsUpdates: canDoSilentUpdates]; + + SWIFT_LOG(debug) << (canDoSilentUpdates ? + "The current running user has enough permissions to do a silent update." : + "The current running user has insufficient permissions to do a silent update."); + + setAppcastFeed(appcastFeed); } SparkleAutoUpdater::~SparkleAutoUpdater() { - [d->updater release]; - delete d; + [d->updater release]; +} + +void SparkleAutoUpdater::setAppcastFeed(const std::string& appcastFeed) { + NSURL* nsurl = [NSURL URLWithString: std2NSString(appcastFeed)]; + [d->updater setFeedURL: nsurl]; } void SparkleAutoUpdater::checkForUpdates() { - [d->updater checkForUpdatesInBackground]; + if (!(getCurrentState() == State::CheckingForUpdate || + getCurrentState() == State::DownloadingUpdate || + getCurrentState() == State::RestartToInstallUpdate)) { + setCurrentState(State::CheckingForUpdate); + [d->updater resetUpdateCycle]; + [d->updater checkForUpdatesInBackground]; + } +} + +void SparkleAutoUpdater::setCurrentState(AutoUpdater::State updatedState) { + d->currentState = updatedState; + onUpdateStateChanged(d->currentState); +} + +AutoUpdater::State SparkleAutoUpdater::getCurrentState() { + return d->currentState; +} + +bool Swift::SparkleAutoUpdater::applicationInstallationLocationWritable() { + auto bundlePath = ns2StdString([[NSBundle mainBundle] bundlePath]); + + auto createTemporaryFile = [](const boost::filesystem::path& parentFolder) { + boost::optional<boost::filesystem::path> tempFilePath; + boost::system::error_code error; + + if (!boost::filesystem::is_directory(parentFolder, error) || error) { + return tempFilePath; + } + auto uniquePath = boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%", error); + if (error) { + return tempFilePath; + } + auto testFilePath = parentFolder / uniquePath; + + boost::filesystem::ofstream testFile(testFilePath, std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc); + if (testFile) { + testFile.close(); + tempFilePath = testFilePath; + } + + return tempFilePath; + }; + + auto isDirectoryWritable = [&](const boost::filesystem::path& path) { + auto bundleTestFilePath = createTemporaryFile(path); + if (!bundleTestFilePath) { + return false; + } + + boost::system::error_code error; + if (!boost::filesystem::remove(bundleTestFilePath.get(), error) || error) { + // File did not exist in the first place or error while removing it. + return false; + } + + return true; + }; + + auto applyMatchingPermissions = [](const boost::filesystem::path& permissionsFrom, const boost::filesystem::path& applyTo) { + auto permissions = boost::filesystem::status(permissionsFrom).permissions(); + + boost::system::error_code error; + boost::filesystem::permissions(applyTo, permissions, error); + + return !error; + }; + + auto canChangePermissionsOnTemporaryFile = [&](const boost::filesystem::path& pathToCreateTemporaryFileUnder, const boost::filesystem::path& pathToTakePermissionsFrom) { + auto temporaryFilePath = createTemporaryFile(pathToCreateTemporaryFileUnder); + if (!temporaryFilePath) { + return false; + } + + boost::system::error_code error; + auto fileExists = boost::filesystem::exists(temporaryFilePath.get(), error); + if (!fileExists || error) { + return false; + } + + auto successfullPermissionCopy = applyMatchingPermissions(pathToTakePermissionsFrom, temporaryFilePath.get()); + + boost::filesystem::remove(temporaryFilePath.get(), error); + + return successfullPermissionCopy; + }; + + auto bundleBoostPath = boost::filesystem::path(bundlePath); + if (!isDirectoryWritable(bundleBoostPath.parent_path()) || !isDirectoryWritable(bundleBoostPath)) { + return false; + } + + return canChangePermissionsOnTemporaryFile(bundleBoostPath.parent_path(), bundleBoostPath); } } diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h new file mode 100644 index 0000000..4aa236b --- /dev/null +++ b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <functional> + +#import <Cocoa/Cocoa.h> + +#import <Sparkle/Sparkle.h> + +#include <SwifTools/AutoUpdater/AutoUpdater.h> + +@interface SparkleAutoUpdaterDelegate : NSObject<SUUpdaterDelegate> +@property (atomic) std::function< void (Swift::AutoUpdater::State)> onNewUpdateState; + +- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + +- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update; + +- (id <SUVersionComparison>)versionComparatorForUpdater:(SUUpdater *)updater; + +- (void)updaterDidNotFindUpdate:(SUUpdater *)update; + +- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; + +- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; + +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update; + +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation; + +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; +@end diff --git a/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm new file mode 100644 index 0000000..b9294d9 --- /dev/null +++ b/SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.mm @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#import <SwifTools/AutoUpdater/SparkleAutoUpdaterDelegate.h> + +#include <string> + +#include <Swiften/Base/Log.h> + +#include <SwifTools/Cocoa/CocoaUtil.h> + +using namespace Swift; + +@implementation SparkleAutoUpdaterDelegate + +@synthesize onNewUpdateState; + +- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast { + (void)updater; + (void)appcast; + onNewUpdateState(AutoUpdater::State::DownloadingUpdate); +} + +- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update { + (void)updater; + (void)update; +} + +- (id <SUVersionComparison>)versionComparatorForUpdater:(SUUpdater *)updater { + (void)updater; + return nil; +} + +- (void)updaterDidNotFindUpdate:(SUUpdater *)updater { + (void)updater; + onNewUpdateState(AutoUpdater::State::NoUpdateAvailable); +} + +- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request { + (void)updater; + (void)item; + (void)request; + onNewUpdateState(AutoUpdater::State::DownloadingUpdate); +} + +- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error { + (void)updater; + (void)item; + SWIFT_LOG(error) << ns2StdString([error localizedDescription]); + onNewUpdateState(AutoUpdater::State::ErrorCheckingForUpdate); +} + +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update { + (void)updater; + (void)update; +} + +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation { + (void)updater; + (void)item; + (void)invocation; + onNewUpdateState(AutoUpdater::State::RestartToInstallUpdate); +} + +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error { + (void)updater; + if ([error code] == SUNoUpdateError) { + onNewUpdateState(AutoUpdater::State::NoUpdateAvailable); + } + else { + SWIFT_LOG(error) << ns2StdString([error localizedDescription]); + onNewUpdateState(AutoUpdater::State::ErrorCheckingForUpdate); + } +} + +@end diff --git a/SwifTools/Cocoa/CocoaAction.h b/SwifTools/Cocoa/CocoaAction.h index 0ef993e..8807f08 100644 --- a/SwifTools/Cocoa/CocoaAction.h +++ b/SwifTools/Cocoa/CocoaAction.h @@ -1,14 +1,15 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <Cocoa/Cocoa.h> #include <boost/function.hpp> +#include <Cocoa/Cocoa.h> + @interface CocoaAction : NSObject /** diff --git a/SwifTools/Cocoa/CocoaAction.mm b/SwifTools/Cocoa/CocoaAction.mm index 7162cd2..341da2c 100644 --- a/SwifTools/Cocoa/CocoaAction.mm +++ b/SwifTools/Cocoa/CocoaAction.mm @@ -1,30 +1,30 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2013 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Cocoa/CocoaAction.h> @implementation CocoaAction { - boost::function<void ()>* function; + boost::function<void ()>* function; } - (id) initWithFunction: (boost::function<void()>*) f { - if ((self = [super init])) { - function = f; - } - return self; + if ((self = [super init])) { + function = f; + } + return self; } - (void) dealloc { - delete function; - [super dealloc]; + delete function; + [super dealloc]; } - (void) doAction: (id) sender { - (void) sender; - (*function)(); + (void) sender; + (*function)(); } @end diff --git a/SwifTools/Cocoa/CocoaUtil.h b/SwifTools/Cocoa/CocoaUtil.h index 5bb3612..8c4dd64 100644 --- a/SwifTools/Cocoa/CocoaUtil.h +++ b/SwifTools/Cocoa/CocoaUtil.h @@ -1,27 +1,47 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once // Conversion utilities -#define NS2STDSTRING(a) (a == nil ? std::string() : std::string([a cStringUsingEncoding:NSUTF8StringEncoding])) -#define STD2NSSTRING(a) [NSString stringWithCString:a.c_str() encoding:NSUTF8StringEncoding] +namespace { + +inline std::string ns2StdString(NSString* _Nullable nsString); +inline std::string ns2StdString(NSString* _Nullable nsString) { + std::string stdString; + if (nsString != nil) { + stdString = std::string([nsString cStringUsingEncoding:NSUTF8StringEncoding]); + } + return stdString; +} + +inline NSString* _Nonnull std2NSString(const std::string& stdString); +inline NSString* _Nonnull std2NSString(const std::string& stdString) { + NSString* _Nullable nsString = [NSString stringWithUTF8String:stdString.c_str()]; + if (nsString == nil) { + nsString = @""; + } + // At this point nsString is guaranteed to be not null/nil. + return static_cast<NSString* _Nonnull>(nsString); +} + +} // Intrusive pointer for NSObjects -namespace boost { - inline void intrusive_ptr_add_ref(NSObject* object) { - [object retain]; - } - - inline void intrusive_ptr_release(NSObject* object) { - [object release]; - } +namespace boost { + inline void intrusive_ptr_add_ref(NSObject* _Nonnull object) { + [object retain]; + } + + inline void intrusive_ptr_release(NSObject* _Nonnull object) { + [object release]; + } } -// Including intrusive_ptr after ref/release methods to avoid compilation +// Including intrusive_ptr after ref/release methods to avoid compilation // errors with CLang #include <boost/intrusive_ptr.hpp> diff --git a/SwifTools/Cocoa/SConscript b/SwifTools/Cocoa/SConscript index 4ae4a07..f4bd286 100644 --- a/SwifTools/Cocoa/SConscript +++ b/SwifTools/Cocoa/SConscript @@ -2,7 +2,7 @@ Import("swiftools_env", "env") sources = [] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : - sources += ["CocoaAction.mm"] + sources += ["CocoaAction.mm"] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) diff --git a/SwifTools/CrashReporter.cpp b/SwifTools/CrashReporter.cpp index f47ab33..b02e73b 100644 --- a/SwifTools/CrashReporter.cpp +++ b/SwifTools/CrashReporter.cpp @@ -1,19 +1,20 @@ /* - * Copyright (c) 2012-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2012-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ - -#include <SwifTools/CrashReporter.h> +#include <Swiften/Base/Log.h> #include <Swiften/Base/Platform.h> #include <Swiften/Base/Path.h> +#include <SwifTools/CrashReporter.h> + #if defined(HAVE_BREAKPAD) #pragma GCC diagnostic ignored "-Wold-style-cast" -#include <boost/smart_ptr/make_shared.hpp> +#include <memory> #ifdef SWIFTEN_PLATFORM_MACOSX #include "client/mac/handler/exception_handler.h" @@ -24,46 +25,47 @@ #if defined(SWIFTEN_PLATFORM_WINDOWS) static bool handleDump(const wchar_t* /* dir */, const wchar_t* /* id*/, void* /* context */, EXCEPTION_POINTERS*, MDRawAssertionInfo*, bool /* succeeded */) { - return false; + return false; } #else static bool handleDump(const char* /* dir */, const char* /* id*/, void* /* context */, bool /* succeeded */) { - return false; + return false; } #endif namespace Swift { struct CrashReporter::Private { - boost::shared_ptr<google_breakpad::ExceptionHandler> handler; + std::shared_ptr<google_breakpad::ExceptionHandler> handler; }; -CrashReporter::CrashReporter(const boost::filesystem::path& path) { - // Create the path that will contain the crash dumps - if (!boost::filesystem::exists(path)) { - try { - boost::filesystem::create_directories(path); - } - catch (const boost::filesystem::filesystem_error& e) { - std::cerr << "ERROR: " << e.what() << std::endl; - } - } +CrashReporter::CrashReporter(const boost::filesystem::path& path, const std::string& dumpPrefix) { + // Create the path that will contain the crash dumps + if (!boost::filesystem::exists(path)) { + try { + boost::filesystem::create_directories(path); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << "ERROR: " << e.what(); + } + } - p = boost::make_shared<Private>(); + p = std::make_shared<Private>(); #if defined(SWIFTEN_PLATFORM_WINDOWS) - // FIXME: Need UTF8 conversion from string to wstring - std::string pathString = pathToString(path); - p->handler = boost::shared_ptr<google_breakpad::ExceptionHandler>( - // Not using make_shared, because 'handleDump' seems to have problems with VC2010 - new google_breakpad::ExceptionHandler( - std::wstring(pathString.begin(), pathString.end()), - (google_breakpad::ExceptionHandler::FilterCallback) 0, - handleDump, - (void*) 0, - google_breakpad::ExceptionHandler::HANDLER_ALL)); + // FIXME: Need UTF8 conversion from string to wstring + std::string pathString = pathToString(path); + p->handler = std::shared_ptr<google_breakpad::ExceptionHandler>( + // Not using make_shared, because 'handleDump' seems to have problems with VC2010 + new google_breakpad::ExceptionHandler( + std::wstring(pathString.begin(), pathString.end()), + (google_breakpad::ExceptionHandler::FilterCallback) 0, + handleDump, + (void*) 0, + google_breakpad::ExceptionHandler::HANDLER_ALL)); + p->handler->set_dump_filename_prefix(std::wstring(dumpPrefix.begin(), dumpPrefix.end())); // Turning it off for Mac, because it doesn't really help us //#elif defined(SWIFTEN_PLATFORM_MACOSX) -// p->handler = boost::make_shared<google_breakpad::ExceptionHandler>(pathToString(path), (google_breakpad::ExceptionHandler::FilterCallback) 0, handleDump, (void*) 0, true, (const char*) 0); +// p->handler = std::make_shared<google_breakpad::ExceptionHandler>(pathToString(path), (google_breakpad::ExceptionHandler::FilterCallback) 0, handleDump, (void*) 0, true, (const char*) 0); #endif } @@ -73,7 +75,7 @@ CrashReporter::CrashReporter(const boost::filesystem::path& path) { // Dummy implementation namespace Swift { - CrashReporter::CrashReporter(const boost::filesystem::path&) {} + CrashReporter::CrashReporter(const boost::filesystem::path&, const std::string&) {} } #endif diff --git a/SwifTools/CrashReporter.h b/SwifTools/CrashReporter.h index 834e8af..1efa801 100644 --- a/SwifTools/CrashReporter.h +++ b/SwifTools/CrashReporter.h @@ -1,22 +1,23 @@ /* - * Copyright (c) 2012 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2012-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/shared_ptr.hpp> -#include <boost/filesystem.hpp> +#include <memory> #include <string> +#include <boost/filesystem.hpp> + namespace Swift { - class CrashReporter { - public: - CrashReporter(const boost::filesystem::path& path); + class CrashReporter { + public: + CrashReporter(const boost::filesystem::path& path, const std::string& dumpPrefix); - private: - struct Private; - boost::shared_ptr<Private> p; - }; + private: + struct Private; + std::shared_ptr<Private> p; + }; } diff --git a/SwifTools/Dock/Dock.cpp b/SwifTools/Dock/Dock.cpp index e159b8d..91e2ca3 100644 --- a/SwifTools/Dock/Dock.cpp +++ b/SwifTools/Dock/Dock.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Dock/Dock.h> diff --git a/SwifTools/Dock/Dock.h b/SwifTools/Dock/Dock.h index 1bd96fb..362aabc 100644 --- a/SwifTools/Dock/Dock.h +++ b/SwifTools/Dock/Dock.h @@ -1,18 +1,20 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <cstddef> + namespace Swift { - - class Dock { - public: - virtual ~Dock(); - virtual void setNumberOfPendingMessages(int i) = 0; - }; + class Dock { + public: + virtual ~Dock(); + + virtual void setNumberOfPendingMessages(size_t i) = 0; + }; } diff --git a/SwifTools/Dock/MacOSXDock.h b/SwifTools/Dock/MacOSXDock.h index df2686f..ef85a88 100644 --- a/SwifTools/Dock/MacOSXDock.h +++ b/SwifTools/Dock/MacOSXDock.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,13 +9,13 @@ #include <SwifTools/Dock/Dock.h> namespace Swift { - - class CocoaApplication; - class MacOSXDock : public Dock { - public: - MacOSXDock(CocoaApplication* application); + class CocoaApplication; - virtual void setNumberOfPendingMessages(int i); - }; + class MacOSXDock : public Dock { + public: + MacOSXDock(CocoaApplication* application); + + virtual void setNumberOfPendingMessages(size_t i); + }; } diff --git a/SwifTools/Dock/MacOSXDock.mm b/SwifTools/Dock/MacOSXDock.mm index 3164998..3935ac0 100644 --- a/SwifTools/Dock/MacOSXDock.mm +++ b/SwifTools/Dock/MacOSXDock.mm @@ -1,11 +1,15 @@ -// Fix Boost-Cocoa conflict -#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 +/* + * Copyright (c) 2015-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ #include <SwifTools/Dock/MacOSXDock.h> +#include <boost/lexical_cast.hpp> + #include <AppKit/AppKit.h> #include <Cocoa/Cocoa.h> -#include <boost/lexical_cast.hpp> #include <Swiften/Base/String.h> @@ -14,12 +18,12 @@ namespace Swift { MacOSXDock::MacOSXDock(CocoaApplication*) { } -void MacOSXDock::setNumberOfPendingMessages(int i) { - std::string label(i > 0 ? boost::lexical_cast<std::string>(i) : ""); - NSString *labelString = [[NSString alloc] initWithUTF8String: label.c_str()]; - [[NSApp dockTile] setBadgeLabel: labelString]; - [labelString release]; - [NSApp requestUserAttention: NSInformationalRequest]; +void MacOSXDock::setNumberOfPendingMessages(size_t i) { + std::string label(i > 0 ? boost::lexical_cast<std::string>(i) : ""); + NSString *labelString = [[NSString alloc] initWithUTF8String: label.c_str()]; + [[NSApp dockTile] setBadgeLabel: labelString]; + [labelString release]; + [NSApp requestUserAttention: NSInformationalRequest]; } } diff --git a/SwifTools/Dock/NullDock.h b/SwifTools/Dock/NullDock.h index b015770..137ba03 100644 --- a/SwifTools/Dock/NullDock.h +++ b/SwifTools/Dock/NullDock.h @@ -1,19 +1,21 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <cstddef> + #include <SwifTools/Dock/Dock.h> namespace Swift { - class NullDock : public Dock { - public: - NullDock() {} + class NullDock : public Dock { + public: + NullDock() {} - virtual void setNumberOfPendingMessages(int) { - } - }; + virtual void setNumberOfPendingMessages(size_t) { + } + }; } diff --git a/SwifTools/Dock/SConscript b/SwifTools/Dock/SConscript index 94797cb..3dbcf60 100644 --- a/SwifTools/Dock/SConscript +++ b/SwifTools/Dock/SConscript @@ -1,13 +1,13 @@ Import("swiftools_env") sources = [ - "Dock.cpp", - ] + "Dock.cpp", + ] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : - sources += [ - "MacOSXDock.mm", - ] + sources += [ + "MacOSXDock.mm", + ] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = objects) diff --git a/SwifTools/Dock/WindowsDock.h b/SwifTools/Dock/WindowsDock.h index 0254617..f9a9dae 100644 --- a/SwifTools/Dock/WindowsDock.h +++ b/SwifTools/Dock/WindowsDock.h @@ -1,42 +1,43 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <QSystemTrayIcon> #include <boost/lexical_cast.hpp> +#include <QSystemTrayIcon> + #include <SwifTools/Dock/Dock.h> #include <SwifTools/Notifier/Notifier.h> namespace Swift { - class WindowsDock : public Dock { - public: - WindowsDock(QSystemTrayIcon* tray, Notifier* notifier) : tray(tray), notifier(notifier) {} - - virtual void setNumberOfPendingMessages(int i) { - if (notifier->isAvailable()) { - return; - } - - if (i > 0) { - std::string message = boost::lexical_cast<std::string>(i) + " new message"; - if (i > 1) { - message += "s"; - } - message += " received."; - tray->showMessage("New messages", message.c_str(), QSystemTrayIcon::NoIcon); - } - else { - tray->showMessage("", "", QSystemTrayIcon::NoIcon, 0); - } - } - - private: - QSystemTrayIcon* tray; - Notifier* notifier; - }; + class WindowsDock : public Dock { + public: + WindowsDock(QSystemTrayIcon* tray, Notifier* notifier) : tray(tray), notifier(notifier) {} + + virtual void setNumberOfPendingMessages(size_t i) { + if (notifier->isAvailable()) { + return; + } + + if (i > 0) { + std::string message = boost::lexical_cast<std::string>(i) + " new message"; + if (i > 1) { + message += "s"; + } + message += " received."; + tray->showMessage("New messages", message.c_str(), QSystemTrayIcon::NoIcon); + } + else { + tray->showMessage("", "", QSystemTrayIcon::NoIcon, 0); + } + } + + private: + QSystemTrayIcon* tray; + Notifier* notifier; + }; } diff --git a/SwifTools/EmojiMapper.cpp b/SwifTools/EmojiMapper.cpp new file mode 100644 index 0000000..4381cf0 --- /dev/null +++ b/SwifTools/EmojiMapper.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016-2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <SwifTools/EmojiMapper.h> + +#include <algorithm> +#include <string> +#include <unordered_map> + +namespace Swift { + + //AUTO-GENERATED CONTENT + const std::unordered_map<std::string, std::string> EmojiMapper::shortnameUnicode = std::unordered_map<std::string, std::string>{{":100:", "\xf0\x9f\x92\xaf"}, {":1234:", "\xf0\x9f\x94\xa2"}, {":grinning:", "\xf0\x9f\x98\x80"}, {":grin:", "\xf0\x9f\x98\x81"}, {":joy:", "\xf0\x9f\x98\x82"}, {":rofl:", "\xf0\x9f\xa4\xa3"}, {":smiley:", "\xf0\x9f\x98\x83"}, {":smile:", "\xf0\x9f\x98\x84"}, {":sweat_smile:", "\xf0\x9f\x98\x85"}, {":laughing:", "\xf0\x9f\x98\x86"}, {":wink:", "\xf0\x9f\x98\x89"}, {":blush:", "\xf0\x9f\x98\x8a"}, {":yum:", "\xf0\x9f\x98\x8b"}, {":sunglasses:", "\xf0\x9f\x98\x8e"}, {":heart_eyes:", "\xf0\x9f\x98\x8d"}, {":kissing_heart:", "\xf0\x9f\x98\x98"}, {":kissing:", "\xf0\x9f\x98\x97"}, {":kissing_smiling_eyes:", "\xf0\x9f\x98\x99"}, {":kissing_closed_eyes:", "\xf0\x9f\x98\x9a"}, {":relaxed:", "\xe2\x98\xba"}, {":slight_smile:", "\xf0\x9f\x99\x82"}, {":hugging:", "\xf0\x9f\xa4\x97"}, {":thinking:", "\xf0\x9f\xa4\x94"}, {":neutral_face:", "\xf0\x9f\x98\x90"}, {":expressionless:", "\xf0\x9f\x98\x91"}, {":no_mouth:", "\xf0\x9f\x98\xb6"}, {":rolling_eyes:", "\xf0\x9f\x99\x84"}, {":smirk:", "\xf0\x9f\x98\x8f"}, {":persevere:", "\xf0\x9f\x98\xa3"}, {":disappointed_relieved:", "\xf0\x9f\x98\xa5"}, {":open_mouth:", "\xf0\x9f\x98\xae"}, {":zipper_mouth:", "\xf0\x9f\xa4\x90"}, {":hushed:", "\xf0\x9f\x98\xaf"}, {":sleepy:", "\xf0\x9f\x98\xaa"}, {":tired_face:", "\xf0\x9f\x98\xab"}, {":sleeping:", "\xf0\x9f\x98\xb4"}, {":relieved:", "\xf0\x9f\x98\x8c"}, {":nerd:", "\xf0\x9f\xa4\x93"}, {":stuck_out_tongue:", "\xf0\x9f\x98\x9b"}, {":stuck_out_tongue_winking_eye:", "\xf0\x9f\x98\x9c"}, {":stuck_out_tongue_closed_eyes:", "\xf0\x9f\x98\x9d"}, {":drooling_face:", "\xf0\x9f\xa4\xa4"}, {":unamused:", "\xf0\x9f\x98\x92"}, {":sweat:", "\xf0\x9f\x98\x93"}, {":pensive:", "\xf0\x9f\x98\x94"}, {":confused:", "\xf0\x9f\x98\x95"}, {":upside_down:", "\xf0\x9f\x99\x83"}, {":money_mouth:", "\xf0\x9f\xa4\x91"}, {":astonished:", "\xf0\x9f\x98\xb2"}, {":frowning2:", "\xe2\x98\xb9"}, {":slight_frown:", "\xf0\x9f\x99\x81"}, {":confounded:", "\xf0\x9f\x98\x96"}, {":disappointed:", "\xf0\x9f\x98\x9e"}, {":worried:", "\xf0\x9f\x98\x9f"}, {":triumph:", "\xf0\x9f\x98\xa4"}, {":cry:", "\xf0\x9f\x98\xa2"}, {":sob:", "\xf0\x9f\x98\xad"}, {":frowning:", "\xf0\x9f\x98\xa6"}, {":anguished:", "\xf0\x9f\x98\xa7"}, {":fearful:", "\xf0\x9f\x98\xa8"}, {":weary:", "\xf0\x9f\x98\xa9"}, {":grimacing:", "\xf0\x9f\x98\xac"}, {":cold_sweat:", "\xf0\x9f\x98\xb0"}, {":scream:", "\xf0\x9f\x98\xb1"}, {":flushed:", "\xf0\x9f\x98\xb3"}, {":dizzy_face:", "\xf0\x9f\x98\xb5"}, {":rage:", "\xf0\x9f\x98\xa1"}, {":angry:", "\xf0\x9f\x98\xa0"}, {":innocent:", "\xf0\x9f\x98\x87"}, {":cowboy:", "\xf0\x9f\xa4\xa0"}, {":clown:", "\xf0\x9f\xa4\xa1"}, {":lying_face:", "\xf0\x9f\xa4\xa5"}, {":mask:", "\xf0\x9f\x98\xb7"}, {":thermometer_face:", "\xf0\x9f\xa4\x92"}, {":head_bandage:", "\xf0\x9f\xa4\x95"}, {":nauseated_face:", "\xf0\x9f\xa4\xa2"}, {":sneezing_face:", "\xf0\x9f\xa4\xa7"}, {":smiling_imp:", "\xf0\x9f\x98\x88"}, {":imp:", "\xf0\x9f\x91\xbf"}, {":japanese_ogre:", "\xf0\x9f\x91\xb9"}, {":japanese_goblin:", "\xf0\x9f\x91\xba"}, {":skull:", "\xf0\x9f\x92\x80"}, {":skull_crossbones:", "\xe2\x98\xa0"}, {":ghost:", "\xf0\x9f\x91\xbb"}, {":alien:", "\xf0\x9f\x91\xbd"}, {":space_invader:", "\xf0\x9f\x91\xbe"}, {":robot:", "\xf0\x9f\xa4\x96"}, {":poop:", "\xf0\x9f\x92\xa9"}, {":smiley_cat:", "\xf0\x9f\x98\xba"}, {":smile_cat:", "\xf0\x9f\x98\xb8"}, {":joy_cat:", "\xf0\x9f\x98\xb9"}, {":heart_eyes_cat:", "\xf0\x9f\x98\xbb"}, {":smirk_cat:", "\xf0\x9f\x98\xbc"}, {":kissing_cat:", "\xf0\x9f\x98\xbd"}, {":scream_cat:", "\xf0\x9f\x99\x80"}, {":crying_cat_face:", "\xf0\x9f\x98\xbf"}, {":pouting_cat:", "\xf0\x9f\x98\xbe"}, {":see_no_evil:", "\xf0\x9f\x99\x88"}, {":hear_no_evil:", "\xf0\x9f\x99\x89"}, {":speak_no_evil:", "\xf0\x9f\x99\x8a"}, {":boy:", "\xf0\x9f\x91\xa6"}, {":boy_tone1:", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbb"}, {":boy_tone2:", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbc"}, {":boy_tone3:", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbd"}, {":boy_tone4:", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbe"}, {":boy_tone5:", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbf"}, {":girl:", "\xf0\x9f\x91\xa7"}, {":girl_tone1:", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbb"}, {":girl_tone2:", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbc"}, {":girl_tone3:", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbd"}, {":girl_tone4:", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbe"}, {":girl_tone5:", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbf"}, {":man:", "\xf0\x9f\x91\xa8"}, {":man_tone1:", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbb"}, {":man_tone2:", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbc"}, {":man_tone3:", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbd"}, {":man_tone4:", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbe"}, {":man_tone5:", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbf"}, {":woman:", "\xf0\x9f\x91\xa9"}, {":woman_tone1:", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbb"}, {":woman_tone2:", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbc"}, {":woman_tone3:", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbd"}, {":woman_tone4:", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbe"}, {":woman_tone5:", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbf"}, {":older_man:", "\xf0\x9f\x91\xb4"}, {":older_man_tone1:", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbb"}, {":older_man_tone2:", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbc"}, {":older_man_tone3:", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbd"}, {":older_man_tone4:", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbe"}, {":older_man_tone5:", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbf"}, {":older_woman:", "\xf0\x9f\x91\xb5"}, {":older_woman_tone1:", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbb"}, {":older_woman_tone2:", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbc"}, {":older_woman_tone3:", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbd"}, {":older_woman_tone4:", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbe"}, {":older_woman_tone5:", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbf"}, {":baby:", "\xf0\x9f\x91\xb6"}, {":baby_tone1:", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbb"}, {":baby_tone2:", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbc"}, {":baby_tone3:", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbd"}, {":baby_tone4:", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbe"}, {":baby_tone5:", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbf"}, {":angel:", "\xf0\x9f\x91\xbc"}, {":angel_tone1:", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbb"}, {":angel_tone2:", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbc"}, {":angel_tone3:", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbd"}, {":angel_tone4:", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbe"}, {":angel_tone5:", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbf"}, {":cop:", "\xf0\x9f\x91\xae"}, {":cop_tone1:", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbb"}, {":cop_tone2:", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbc"}, {":cop_tone3:", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbd"}, {":cop_tone4:", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbe"}, {":cop_tone5:", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbf"}, {":spy:", "\xf0\x9f\x95\xb5"}, {":spy_tone1:", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbb"}, {":spy_tone2:", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbc"}, {":spy_tone3:", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbd"}, {":spy_tone4:", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbe"}, {":spy_tone5:", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbf"}, {":guardsman:", "\xf0\x9f\x92\x82"}, {":guardsman_tone1:", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbb"}, {":guardsman_tone2:", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbc"}, {":guardsman_tone3:", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbd"}, {":guardsman_tone4:", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbe"}, {":guardsman_tone5:", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbf"}, {":construction_worker:", "\xf0\x9f\x91\xb7"}, {":construction_worker_tone1:", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbb"}, {":construction_worker_tone2:", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbc"}, {":construction_worker_tone3:", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbd"}, {":construction_worker_tone4:", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbe"}, {":construction_worker_tone5:", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbf"}, {":man_with_turban:", "\xf0\x9f\x91\xb3"}, {":man_with_turban_tone1:", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbb"}, {":man_with_turban_tone2:", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbc"}, {":man_with_turban_tone3:", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbd"}, {":man_with_turban_tone4:", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbe"}, {":man_with_turban_tone5:", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbf"}, {":person_with_blond_hair:", "\xf0\x9f\x91\xb1"}, {":person_with_blond_hair_tone1:", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbb"}, {":person_with_blond_hair_tone2:", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbc"}, {":person_with_blond_hair_tone3:", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbd"}, {":person_with_blond_hair_tone4:", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbe"}, {":person_with_blond_hair_tone5:", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbf"}, {":santa:", "\xf0\x9f\x8e\x85"}, {":santa_tone1:", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbb"}, {":santa_tone2:", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbc"}, {":santa_tone3:", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbd"}, {":santa_tone4:", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbe"}, {":santa_tone5:", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbf"}, {":mrs_claus:", "\xf0\x9f\xa4\xb6"}, {":mrs_claus_tone1:", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbb"}, {":mrs_claus_tone2:", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbc"}, {":mrs_claus_tone3:", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbd"}, {":mrs_claus_tone4:", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbe"}, {":mrs_claus_tone5:", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbf"}, {":princess:", "\xf0\x9f\x91\xb8"}, {":princess_tone1:", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbb"}, {":princess_tone2:", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbc"}, {":princess_tone3:", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbd"}, {":princess_tone4:", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbe"}, {":princess_tone5:", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbf"}, {":prince:", "\xf0\x9f\xa4\xb4"}, {":prince_tone1:", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbb"}, {":prince_tone2:", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbc"}, {":prince_tone3:", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbd"}, {":prince_tone4:", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbe"}, {":prince_tone5:", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbf"}, {":bride_with_veil:", "\xf0\x9f\x91\xb0"}, {":bride_with_veil_tone1:", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbb"}, {":bride_with_veil_tone2:", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbc"}, {":bride_with_veil_tone3:", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbd"}, {":bride_with_veil_tone4:", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbe"}, {":bride_with_veil_tone5:", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbf"}, {":man_in_tuxedo:", "\xf0\x9f\xa4\xb5"}, {":man_in_tuxedo_tone1:", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbb"}, {":man_in_tuxedo_tone2:", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbc"}, {":man_in_tuxedo_tone3:", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbd"}, {":man_in_tuxedo_tone4:", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbe"}, {":man_in_tuxedo_tone5:", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbf"}, {":pregnant_woman:", "\xf0\x9f\xa4\xb0"}, {":pregnant_woman_tone1:", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbb"}, {":pregnant_woman_tone2:", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbc"}, {":pregnant_woman_tone3:", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbd"}, {":pregnant_woman_tone4:", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbe"}, {":pregnant_woman_tone5:", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbf"}, {":man_with_gua_pi_mao:", "\xf0\x9f\x91\xb2"}, {":man_with_gua_pi_mao_tone1:", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbb"}, {":man_with_gua_pi_mao_tone2:", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbc"}, {":man_with_gua_pi_mao_tone3:", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbd"}, {":man_with_gua_pi_mao_tone4:", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbe"}, {":man_with_gua_pi_mao_tone5:", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbf"}, {":person_frowning:", "\xf0\x9f\x99\x8d"}, {":person_frowning_tone1:", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbb"}, {":person_frowning_tone2:", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbc"}, {":person_frowning_tone3:", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbd"}, {":person_frowning_tone4:", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbe"}, {":person_frowning_tone5:", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbf"}, {":person_with_pouting_face:", "\xf0\x9f\x99\x8e"}, {":person_with_pouting_face_tone1:", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbb"}, {":person_with_pouting_face_tone2:", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbc"}, {":person_with_pouting_face_tone3:", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbd"}, {":person_with_pouting_face_tone4:", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbe"}, {":person_with_pouting_face_tone5:", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbf"}, {":no_good:", "\xf0\x9f\x99\x85"}, {":no_good_tone1:", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbb"}, {":no_good_tone2:", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbc"}, {":no_good_tone3:", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbd"}, {":no_good_tone4:", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbe"}, {":no_good_tone5:", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbf"}, {":ok_woman:", "\xf0\x9f\x99\x86"}, {":ok_woman_tone1:", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbb"}, {":ok_woman_tone2:", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbc"}, {":ok_woman_tone3:", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbd"}, {":ok_woman_tone4:", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbe"}, {":ok_woman_tone5:", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbf"}, {":information_desk_person:", "\xf0\x9f\x92\x81"}, {":information_desk_person_tone1:", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbb"}, {":information_desk_person_tone2:", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbc"}, {":information_desk_person_tone3:", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbd"}, {":information_desk_person_tone4:", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbe"}, {":information_desk_person_tone5:", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbf"}, {":raising_hand:", "\xf0\x9f\x99\x8b"}, {":raising_hand_tone1:", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbb"}, {":raising_hand_tone2:", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbc"}, {":raising_hand_tone3:", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbd"}, {":raising_hand_tone4:", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbe"}, {":raising_hand_tone5:", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbf"}, {":bow:", "\xf0\x9f\x99\x87"}, {":bow_tone1:", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbb"}, {":bow_tone2:", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbc"}, {":bow_tone3:", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbd"}, {":bow_tone4:", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbe"}, {":bow_tone5:", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbf"}, {":face_palm:", "\xf0\x9f\xa4\xa6"}, {":face_palm_tone1:", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbb"}, {":face_palm_tone2:", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbc"}, {":face_palm_tone3:", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbd"}, {":face_palm_tone4:", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbe"}, {":face_palm_tone5:", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbf"}, {":shrug:", "\xf0\x9f\xa4\xb7"}, {":shrug_tone1:", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbb"}, {":shrug_tone2:", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbc"}, {":shrug_tone3:", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbd"}, {":shrug_tone4:", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbe"}, {":shrug_tone5:", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbf"}, {":massage:", "\xf0\x9f\x92\x86"}, {":massage_tone1:", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbb"}, {":massage_tone2:", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbc"}, {":massage_tone3:", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbd"}, {":massage_tone4:", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbe"}, {":massage_tone5:", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbf"}, {":haircut:", "\xf0\x9f\x92\x87"}, {":haircut_tone1:", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbb"}, {":haircut_tone2:", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbc"}, {":haircut_tone3:", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbd"}, {":haircut_tone4:", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbe"}, {":haircut_tone5:", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbf"}, {":walking:", "\xf0\x9f\x9a\xb6"}, {":walking_tone1:", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbb"}, {":walking_tone2:", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbc"}, {":walking_tone3:", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbd"}, {":walking_tone4:", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbe"}, {":walking_tone5:", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbf"}, {":runner:", "\xf0\x9f\x8f\x83"}, {":runner_tone1:", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbb"}, {":runner_tone2:", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbc"}, {":runner_tone3:", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbd"}, {":runner_tone4:", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbe"}, {":runner_tone5:", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbf"}, {":dancer:", "\xf0\x9f\x92\x83"}, {":dancer_tone1:", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbb"}, {":dancer_tone2:", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbc"}, {":dancer_tone3:", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbd"}, {":dancer_tone4:", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbe"}, {":dancer_tone5:", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbf"}, {":man_dancing:", "\xf0\x9f\x95\xba"}, {":man_dancing_tone1:", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbb"}, {":man_dancing_tone2:", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbc"}, {":man_dancing_tone3:", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbd"}, {":man_dancing_tone4:", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbe"}, {":man_dancing_tone5:", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbf"}, {":dancers:", "\xf0\x9f\x91\xaf"}, {":levitate:", "\xf0\x9f\x95\xb4"}, {":speaking_head:", "\xf0\x9f\x97\xa3"}, {":bust_in_silhouette:", "\xf0\x9f\x91\xa4"}, {":busts_in_silhouette:", "\xf0\x9f\x91\xa5"}, {":fencer:", "\xf0\x9f\xa4\xba"}, {":horse_racing:", "\xf0\x9f\x8f\x87"}, {":horse_racing_tone1:", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbb"}, {":horse_racing_tone2:", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbc"}, {":horse_racing_tone3:", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbd"}, {":horse_racing_tone4:", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbe"}, {":horse_racing_tone5:", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbf"}, {":skier:", "\xe2\x9b\xb7"}, {":snowboarder:", "\xf0\x9f\x8f\x82"}, {":golfer:", "\xf0\x9f\x8f\x8c"}, {":surfer:", "\xf0\x9f\x8f\x84"}, {":surfer_tone1:", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbb"}, {":surfer_tone2:", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbc"}, {":surfer_tone3:", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbd"}, {":surfer_tone4:", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbe"}, {":surfer_tone5:", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbf"}, {":rowboat:", "\xf0\x9f\x9a\xa3"}, {":rowboat_tone1:", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbb"}, {":rowboat_tone2:", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbc"}, {":rowboat_tone3:", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbd"}, {":rowboat_tone4:", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbe"}, {":rowboat_tone5:", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbf"}, {":swimmer:", "\xf0\x9f\x8f\x8a"}, {":swimmer_tone1:", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbb"}, {":swimmer_tone2:", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbc"}, {":swimmer_tone3:", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbd"}, {":swimmer_tone4:", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbe"}, {":swimmer_tone5:", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbf"}, {":basketball_player:", "\xe2\x9b\xb9"}, {":basketball_player_tone1:", "\xe2\x9b\xb9\xf0\x9f\x8f\xbb"}, {":basketball_player_tone2:", "\xe2\x9b\xb9\xf0\x9f\x8f\xbc"}, {":basketball_player_tone3:", "\xe2\x9b\xb9\xf0\x9f\x8f\xbd"}, {":basketball_player_tone4:", "\xe2\x9b\xb9\xf0\x9f\x8f\xbe"}, {":basketball_player_tone5:", "\xe2\x9b\xb9\xf0\x9f\x8f\xbf"}, {":lifter:", "\xf0\x9f\x8f\x8b"}, {":lifter_tone1:", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbb"}, {":lifter_tone2:", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbc"}, {":lifter_tone3:", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbd"}, {":lifter_tone4:", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbe"}, {":lifter_tone5:", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbf"}, {":bicyclist:", "\xf0\x9f\x9a\xb4"}, {":bicyclist_tone1:", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbb"}, {":bicyclist_tone2:", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbc"}, {":bicyclist_tone3:", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbd"}, {":bicyclist_tone4:", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbe"}, {":bicyclist_tone5:", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbf"}, {":mountain_bicyclist:", "\xf0\x9f\x9a\xb5"}, {":mountain_bicyclist_tone1:", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbb"}, {":mountain_bicyclist_tone2:", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbc"}, {":mountain_bicyclist_tone3:", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbd"}, {":mountain_bicyclist_tone4:", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbe"}, {":mountain_bicyclist_tone5:", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbf"}, {":race_car:", "\xf0\x9f\x8f\x8e"}, {":motorcycle:", "\xf0\x9f\x8f\x8d"}, {":cartwheel:", "\xf0\x9f\xa4\xb8"}, {":cartwheel_tone1:", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbb"}, {":cartwheel_tone2:", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbc"}, {":cartwheel_tone3:", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbd"}, {":cartwheel_tone4:", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbe"}, {":cartwheel_tone5:", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbf"}, {":wrestlers:", "\xf0\x9f\xa4\xbc"}, {":wrestlers_tone1:", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbb"}, {":wrestlers_tone2:", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbc"}, {":wrestlers_tone3:", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbd"}, {":wrestlers_tone4:", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbe"}, {":wrestlers_tone5:", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbf"}, {":water_polo:", "\xf0\x9f\xa4\xbd"}, {":water_polo_tone1:", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbb"}, {":water_polo_tone2:", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbc"}, {":water_polo_tone3:", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbd"}, {":water_polo_tone4:", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbe"}, {":water_polo_tone5:", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbf"}, {":handball:", "\xf0\x9f\xa4\xbe"}, {":handball_tone1:", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbb"}, {":handball_tone2:", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbc"}, {":handball_tone3:", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbd"}, {":handball_tone4:", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbe"}, {":handball_tone5:", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbf"}, {":juggling:", "\xf0\x9f\xa4\xb9"}, {":juggling_tone1:", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbb"}, {":juggling_tone2:", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbc"}, {":juggling_tone3:", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbd"}, {":juggling_tone4:", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbe"}, {":juggling_tone5:", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbf"}, {":couple:", "\xf0\x9f\x91\xab"}, {":two_men_holding_hands:", "\xf0\x9f\x91\xac"}, {":two_women_holding_hands:", "\xf0\x9f\x91\xad"}, {":couplekiss:", "\xf0\x9f\x92\x8f"}, {":couple_with_heart:", "\xf0\x9f\x92\x91"}, {":family:", "\xf0\x9f\x91\xaa"}, {":tone1:", "\xf0\x9f\x8f\xbb"}, {":tone2:", "\xf0\x9f\x8f\xbc"}, {":tone3:", "\xf0\x9f\x8f\xbd"}, {":tone4:", "\xf0\x9f\x8f\xbe"}, {":tone5:", "\xf0\x9f\x8f\xbf"}, {":muscle:", "\xf0\x9f\x92\xaa"}, {":muscle_tone1:", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbb"}, {":muscle_tone2:", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbc"}, {":muscle_tone3:", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbd"}, {":muscle_tone4:", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbe"}, {":muscle_tone5:", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbf"}, {":selfie:", "\xf0\x9f\xa4\xb3"}, {":selfie_tone1:", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbb"}, {":selfie_tone2:", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbc"}, {":selfie_tone3:", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbd"}, {":selfie_tone4:", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbe"}, {":selfie_tone5:", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbf"}, {":point_left:", "\xf0\x9f\x91\x88"}, {":point_left_tone1:", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbb"}, {":point_left_tone2:", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbc"}, {":point_left_tone3:", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbd"}, {":point_left_tone4:", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbe"}, {":point_left_tone5:", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbf"}, {":point_right:", "\xf0\x9f\x91\x89"}, {":point_right_tone1:", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbb"}, {":point_right_tone2:", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbc"}, {":point_right_tone3:", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbd"}, {":point_right_tone4:", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbe"}, {":point_right_tone5:", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbf"}, {":point_up:", "\xe2\x98\x9d"}, {":point_up_tone1:", "\xe2\x98\x9d\xf0\x9f\x8f\xbb"}, {":point_up_tone2:", "\xe2\x98\x9d\xf0\x9f\x8f\xbc"}, {":point_up_tone3:", "\xe2\x98\x9d\xf0\x9f\x8f\xbd"}, {":point_up_tone4:", "\xe2\x98\x9d\xf0\x9f\x8f\xbe"}, {":point_up_tone5:", "\xe2\x98\x9d\xf0\x9f\x8f\xbf"}, {":point_up_2:", "\xf0\x9f\x91\x86"}, {":point_up_2_tone1:", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbb"}, {":point_up_2_tone2:", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbc"}, {":point_up_2_tone3:", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbd"}, {":point_up_2_tone4:", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbe"}, {":point_up_2_tone5:", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbf"}, {":middle_finger:", "\xf0\x9f\x96\x95"}, {":middle_finger_tone1:", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbb"}, {":middle_finger_tone2:", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbc"}, {":middle_finger_tone3:", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbd"}, {":middle_finger_tone4:", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbe"}, {":middle_finger_tone5:", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbf"}, {":point_down:", "\xf0\x9f\x91\x87"}, {":point_down_tone1:", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbb"}, {":point_down_tone2:", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbc"}, {":point_down_tone3:", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbd"}, {":point_down_tone4:", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbe"}, {":point_down_tone5:", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbf"}, {":v:", "\xe2\x9c\x8c"}, {":v_tone1:", "\xe2\x9c\x8c\xf0\x9f\x8f\xbb"}, {":v_tone2:", "\xe2\x9c\x8c\xf0\x9f\x8f\xbc"}, {":v_tone3:", "\xe2\x9c\x8c\xf0\x9f\x8f\xbd"}, {":v_tone4:", "\xe2\x9c\x8c\xf0\x9f\x8f\xbe"}, {":v_tone5:", "\xe2\x9c\x8c\xf0\x9f\x8f\xbf"}, {":fingers_crossed:", "\xf0\x9f\xa4\x9e"}, {":fingers_crossed_tone1:", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbb"}, {":fingers_crossed_tone2:", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbc"}, {":fingers_crossed_tone3:", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbd"}, {":fingers_crossed_tone4:", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbe"}, {":fingers_crossed_tone5:", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbf"}, {":vulcan:", "\xf0\x9f\x96\x96"}, {":vulcan_tone1:", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbb"}, {":vulcan_tone2:", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbc"}, {":vulcan_tone3:", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbd"}, {":vulcan_tone4:", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbe"}, {":vulcan_tone5:", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbf"}, {":metal:", "\xf0\x9f\xa4\x98"}, {":metal_tone1:", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbb"}, {":metal_tone2:", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbc"}, {":metal_tone3:", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbd"}, {":metal_tone4:", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbe"}, {":metal_tone5:", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbf"}, {":call_me:", "\xf0\x9f\xa4\x99"}, {":call_me_tone1:", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbb"}, {":call_me_tone2:", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbc"}, {":call_me_tone3:", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbd"}, {":call_me_tone4:", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbe"}, {":call_me_tone5:", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbf"}, {":hand_splayed:", "\xf0\x9f\x96\x90"}, {":hand_splayed_tone1:", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbb"}, {":hand_splayed_tone2:", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbc"}, {":hand_splayed_tone3:", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbd"}, {":hand_splayed_tone4:", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbe"}, {":hand_splayed_tone5:", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbf"}, {":raised_hand:", "\xe2\x9c\x8b"}, {":raised_hand_tone1:", "\xe2\x9c\x8b\xf0\x9f\x8f\xbb"}, {":raised_hand_tone2:", "\xe2\x9c\x8b\xf0\x9f\x8f\xbc"}, {":raised_hand_tone3:", "\xe2\x9c\x8b\xf0\x9f\x8f\xbd"}, {":raised_hand_tone4:", "\xe2\x9c\x8b\xf0\x9f\x8f\xbe"}, {":raised_hand_tone5:", "\xe2\x9c\x8b\xf0\x9f\x8f\xbf"}, {":ok_hand:", "\xf0\x9f\x91\x8c"}, {":ok_hand_tone1:", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbb"}, {":ok_hand_tone2:", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbc"}, {":ok_hand_tone3:", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbd"}, {":ok_hand_tone4:", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbe"}, {":ok_hand_tone5:", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbf"}, {":thumbsup:", "\xf0\x9f\x91\x8d"}, {":thumbsup_tone1:", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbb"}, {":thumbsup_tone2:", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbc"}, {":thumbsup_tone3:", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbd"}, {":thumbsup_tone4:", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbe"}, {":thumbsup_tone5:", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbf"}, {":thumbsdown:", "\xf0\x9f\x91\x8e"}, {":thumbsdown_tone1:", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbb"}, {":thumbsdown_tone2:", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbc"}, {":thumbsdown_tone3:", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbd"}, {":thumbsdown_tone4:", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbe"}, {":thumbsdown_tone5:", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbf"}, {":fist:", "\xe2\x9c\x8a"}, {":fist_tone1:", "\xe2\x9c\x8a\xf0\x9f\x8f\xbb"}, {":fist_tone2:", "\xe2\x9c\x8a\xf0\x9f\x8f\xbc"}, {":fist_tone3:", "\xe2\x9c\x8a\xf0\x9f\x8f\xbd"}, {":fist_tone4:", "\xe2\x9c\x8a\xf0\x9f\x8f\xbe"}, {":fist_tone5:", "\xe2\x9c\x8a\xf0\x9f\x8f\xbf"}, {":punch:", "\xf0\x9f\x91\x8a"}, {":punch_tone1:", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbb"}, {":punch_tone2:", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbc"}, {":punch_tone3:", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbd"}, {":punch_tone4:", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbe"}, {":punch_tone5:", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbf"}, {":left_facing_fist:", "\xf0\x9f\xa4\x9b"}, {":left_facing_fist_tone1:", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbb"}, {":left_facing_fist_tone2:", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbc"}, {":left_facing_fist_tone3:", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbd"}, {":left_facing_fist_tone4:", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbe"}, {":left_facing_fist_tone5:", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbf"}, {":right_facing_fist:", "\xf0\x9f\xa4\x9c"}, {":right_facing_fist_tone1:", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbb"}, {":right_facing_fist_tone2:", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbc"}, {":right_facing_fist_tone3:", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbd"}, {":right_facing_fist_tone4:", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbe"}, {":right_facing_fist_tone5:", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbf"}, {":raised_back_of_hand:", "\xf0\x9f\xa4\x9a"}, {":raised_back_of_hand_tone1:", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbb"}, {":raised_back_of_hand_tone2:", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbc"}, {":raised_back_of_hand_tone3:", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbd"}, {":raised_back_of_hand_tone4:", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbe"}, {":raised_back_of_hand_tone5:", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbf"}, {":wave:", "\xf0\x9f\x91\x8b"}, {":wave_tone1:", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbb"}, {":wave_tone2:", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbc"}, {":wave_tone3:", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbd"}, {":wave_tone4:", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbe"}, {":wave_tone5:", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbf"}, {":clap:", "\xf0\x9f\x91\x8f"}, {":clap_tone1:", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbb"}, {":clap_tone2:", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbc"}, {":clap_tone3:", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbd"}, {":clap_tone4:", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbe"}, {":clap_tone5:", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbf"}, {":writing_hand:", "\xe2\x9c\x8d"}, {":writing_hand_tone1:", "\xe2\x9c\x8d\xf0\x9f\x8f\xbb"}, {":writing_hand_tone2:", "\xe2\x9c\x8d\xf0\x9f\x8f\xbc"}, {":writing_hand_tone3:", "\xe2\x9c\x8d\xf0\x9f\x8f\xbd"}, {":writing_hand_tone4:", "\xe2\x9c\x8d\xf0\x9f\x8f\xbe"}, {":writing_hand_tone5:", "\xe2\x9c\x8d\xf0\x9f\x8f\xbf"}, {":open_hands:", "\xf0\x9f\x91\x90"}, {":open_hands_tone1:", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbb"}, {":open_hands_tone2:", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbc"}, {":open_hands_tone3:", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbd"}, {":open_hands_tone4:", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbe"}, {":open_hands_tone5:", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbf"}, {":raised_hands:", "\xf0\x9f\x99\x8c"}, {":raised_hands_tone1:", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbb"}, {":raised_hands_tone2:", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbc"}, {":raised_hands_tone3:", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbd"}, {":raised_hands_tone4:", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbe"}, {":raised_hands_tone5:", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbf"}, {":pray:", "\xf0\x9f\x99\x8f"}, {":pray_tone1:", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbb"}, {":pray_tone2:", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbc"}, {":pray_tone3:", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbd"}, {":pray_tone4:", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbe"}, {":pray_tone5:", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbf"}, {":handshake:", "\xf0\x9f\xa4\x9d"}, {":handshake_tone1:", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbb"}, {":handshake_tone2:", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbc"}, {":handshake_tone3:", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbd"}, {":handshake_tone4:", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbe"}, {":handshake_tone5:", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbf"}, {":nail_care:", "\xf0\x9f\x92\x85"}, {":nail_care_tone1:", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbb"}, {":nail_care_tone2:", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbc"}, {":nail_care_tone3:", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbd"}, {":nail_care_tone4:", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbe"}, {":nail_care_tone5:", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbf"}, {":ear:", "\xf0\x9f\x91\x82"}, {":ear_tone1:", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbb"}, {":ear_tone2:", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbc"}, {":ear_tone3:", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbd"}, {":ear_tone4:", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbe"}, {":ear_tone5:", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbf"}, {":nose:", "\xf0\x9f\x91\x83"}, {":nose_tone1:", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbb"}, {":nose_tone2:", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbc"}, {":nose_tone3:", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbd"}, {":nose_tone4:", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbe"}, {":nose_tone5:", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbf"}, {":footprints:", "\xf0\x9f\x91\xa3"}, {":eyes:", "\xf0\x9f\x91\x80"}, {":eye:", "\xf0\x9f\x91\x81"}, {":eye_in_speech_bubble:", "\xf0\x9f\x91\x81\xf0\x9f\x97\xa8"}, {":tongue:", "\xf0\x9f\x91\x85"}, {":lips:", "\xf0\x9f\x91\x84"}, {":kiss:", "\xf0\x9f\x92\x8b"}, {":cupid:", "\xf0\x9f\x92\x98"}, {":heart:", "\xe2\x9d\xa4"}, {":heartbeat:", "\xf0\x9f\x92\x93"}, {":broken_heart:", "\xf0\x9f\x92\x94"}, {":two_hearts:", "\xf0\x9f\x92\x95"}, {":sparkling_heart:", "\xf0\x9f\x92\x96"}, {":heartpulse:", "\xf0\x9f\x92\x97"}, {":blue_heart:", "\xf0\x9f\x92\x99"}, {":green_heart:", "\xf0\x9f\x92\x9a"}, {":yellow_heart:", "\xf0\x9f\x92\x9b"}, {":purple_heart:", "\xf0\x9f\x92\x9c"}, {":black_heart:", "\xf0\x9f\x96\xa4"}, {":gift_heart:", "\xf0\x9f\x92\x9d"}, {":revolving_hearts:", "\xf0\x9f\x92\x9e"}, {":heart_decoration:", "\xf0\x9f\x92\x9f"}, {":heart_exclamation:", "\xe2\x9d\xa3"}, {":love_letter:", "\xf0\x9f\x92\x8c"}, {":zzz:", "\xf0\x9f\x92\xa4"}, {":anger:", "\xf0\x9f\x92\xa2"}, {":bomb:", "\xf0\x9f\x92\xa3"}, {":boom:", "\xf0\x9f\x92\xa5"}, {":sweat_drops:", "\xf0\x9f\x92\xa6"}, {":dash:", "\xf0\x9f\x92\xa8"}, {":dizzy:", "\xf0\x9f\x92\xab"}, {":speech_balloon:", "\xf0\x9f\x92\xac"}, {":speech_left:", "\xf0\x9f\x97\xa8"}, {":anger_right:", "\xf0\x9f\x97\xaf"}, {":thought_balloon:", "\xf0\x9f\x92\xad"}, {":hole:", "\xf0\x9f\x95\xb3"}, {":eyeglasses:", "\xf0\x9f\x91\x93"}, {":dark_sunglasses:", "\xf0\x9f\x95\xb6"}, {":necktie:", "\xf0\x9f\x91\x94"}, {":shirt:", "\xf0\x9f\x91\x95"}, {":jeans:", "\xf0\x9f\x91\x96"}, {":dress:", "\xf0\x9f\x91\x97"}, {":kimono:", "\xf0\x9f\x91\x98"}, {":bikini:", "\xf0\x9f\x91\x99"}, {":womans_clothes:", "\xf0\x9f\x91\x9a"}, {":purse:", "\xf0\x9f\x91\x9b"}, {":handbag:", "\xf0\x9f\x91\x9c"}, {":pouch:", "\xf0\x9f\x91\x9d"}, {":shopping_bags:", "\xf0\x9f\x9b\x8d"}, {":school_satchel:", "\xf0\x9f\x8e\x92"}, {":mans_shoe:", "\xf0\x9f\x91\x9e"}, {":athletic_shoe:", "\xf0\x9f\x91\x9f"}, {":high_heel:", "\xf0\x9f\x91\xa0"}, {":sandal:", "\xf0\x9f\x91\xa1"}, {":boot:", "\xf0\x9f\x91\xa2"}, {":crown:", "\xf0\x9f\x91\x91"}, {":womans_hat:", "\xf0\x9f\x91\x92"}, {":tophat:", "\xf0\x9f\x8e\xa9"}, {":mortar_board:", "\xf0\x9f\x8e\x93"}, {":helmet_with_cross:", "\xe2\x9b\x91"}, {":prayer_beads:", "\xf0\x9f\x93\xbf"}, {":lipstick:", "\xf0\x9f\x92\x84"}, {":ring:", "\xf0\x9f\x92\x8d"}, {":gem:", "\xf0\x9f\x92\x8e"}, {":monkey_face:", "\xf0\x9f\x90\xb5"}, {":monkey:", "\xf0\x9f\x90\x92"}, {":gorilla:", "\xf0\x9f\xa6\x8d"}, {":dog:", "\xf0\x9f\x90\xb6"}, {":dog2:", "\xf0\x9f\x90\x95"}, {":poodle:", "\xf0\x9f\x90\xa9"}, {":wolf:", "\xf0\x9f\x90\xba"}, {":fox:", "\xf0\x9f\xa6\x8a"}, {":cat:", "\xf0\x9f\x90\xb1"}, {":cat2:", "\xf0\x9f\x90\x88"}, {":lion_face:", "\xf0\x9f\xa6\x81"}, {":tiger:", "\xf0\x9f\x90\xaf"}, {":tiger2:", "\xf0\x9f\x90\x85"}, {":leopard:", "\xf0\x9f\x90\x86"}, {":horse:", "\xf0\x9f\x90\xb4"}, {":racehorse:", "\xf0\x9f\x90\x8e"}, {":deer:", "\xf0\x9f\xa6\x8c"}, {":unicorn:", "\xf0\x9f\xa6\x84"}, {":cow:", "\xf0\x9f\x90\xae"}, {":ox:", "\xf0\x9f\x90\x82"}, {":water_buffalo:", "\xf0\x9f\x90\x83"}, {":cow2:", "\xf0\x9f\x90\x84"}, {":pig:", "\xf0\x9f\x90\xb7"}, {":pig2:", "\xf0\x9f\x90\x96"}, {":boar:", "\xf0\x9f\x90\x97"}, {":pig_nose:", "\xf0\x9f\x90\xbd"}, {":ram:", "\xf0\x9f\x90\x8f"}, {":sheep:", "\xf0\x9f\x90\x91"}, {":goat:", "\xf0\x9f\x90\x90"}, {":dromedary_camel:", "\xf0\x9f\x90\xaa"}, {":camel:", "\xf0\x9f\x90\xab"}, {":elephant:", "\xf0\x9f\x90\x98"}, {":rhino:", "\xf0\x9f\xa6\x8f"}, {":mouse:", "\xf0\x9f\x90\xad"}, {":mouse2:", "\xf0\x9f\x90\x81"}, {":rat:", "\xf0\x9f\x90\x80"}, {":hamster:", "\xf0\x9f\x90\xb9"}, {":rabbit:", "\xf0\x9f\x90\xb0"}, {":rabbit2:", "\xf0\x9f\x90\x87"}, {":chipmunk:", "\xf0\x9f\x90\xbf"}, {":bat:", "\xf0\x9f\xa6\x87"}, {":bear:", "\xf0\x9f\x90\xbb"}, {":koala:", "\xf0\x9f\x90\xa8"}, {":panda_face:", "\xf0\x9f\x90\xbc"}, {":feet:", "\xf0\x9f\x90\xbe"}, {":turkey:", "\xf0\x9f\xa6\x83"}, {":chicken:", "\xf0\x9f\x90\x94"}, {":rooster:", "\xf0\x9f\x90\x93"}, {":hatching_chick:", "\xf0\x9f\x90\xa3"}, {":baby_chick:", "\xf0\x9f\x90\xa4"}, {":hatched_chick:", "\xf0\x9f\x90\xa5"}, {":bird:", "\xf0\x9f\x90\xa6"}, {":penguin:", "\xf0\x9f\x90\xa7"}, {":dove:", "\xf0\x9f\x95\x8a"}, {":eagle:", "\xf0\x9f\xa6\x85"}, {":duck:", "\xf0\x9f\xa6\x86"}, {":owl:", "\xf0\x9f\xa6\x89"}, {":frog:", "\xf0\x9f\x90\xb8"}, {":crocodile:", "\xf0\x9f\x90\x8a"}, {":turtle:", "\xf0\x9f\x90\xa2"}, {":lizard:", "\xf0\x9f\xa6\x8e"}, {":snake:", "\xf0\x9f\x90\x8d"}, {":dragon_face:", "\xf0\x9f\x90\xb2"}, {":dragon:", "\xf0\x9f\x90\x89"}, {":whale:", "\xf0\x9f\x90\xb3"}, {":whale2:", "\xf0\x9f\x90\x8b"}, {":dolphin:", "\xf0\x9f\x90\xac"}, {":fish:", "\xf0\x9f\x90\x9f"}, {":tropical_fish:", "\xf0\x9f\x90\xa0"}, {":blowfish:", "\xf0\x9f\x90\xa1"}, {":shark:", "\xf0\x9f\xa6\x88"}, {":octopus:", "\xf0\x9f\x90\x99"}, {":shell:", "\xf0\x9f\x90\x9a"}, {":crab:", "\xf0\x9f\xa6\x80"}, {":shrimp:", "\xf0\x9f\xa6\x90"}, {":squid:", "\xf0\x9f\xa6\x91"}, {":butterfly:", "\xf0\x9f\xa6\x8b"}, {":snail:", "\xf0\x9f\x90\x8c"}, {":bug:", "\xf0\x9f\x90\x9b"}, {":ant:", "\xf0\x9f\x90\x9c"}, {":bee:", "\xf0\x9f\x90\x9d"}, {":beetle:", "\xf0\x9f\x90\x9e"}, {":spider:", "\xf0\x9f\x95\xb7"}, {":spider_web:", "\xf0\x9f\x95\xb8"}, {":scorpion:", "\xf0\x9f\xa6\x82"}, {":bouquet:", "\xf0\x9f\x92\x90"}, {":cherry_blossom:", "\xf0\x9f\x8c\xb8"}, {":white_flower:", "\xf0\x9f\x92\xae"}, {":rosette:", "\xf0\x9f\x8f\xb5"}, {":rose:", "\xf0\x9f\x8c\xb9"}, {":wilted_rose:", "\xf0\x9f\xa5\x80"}, {":hibiscus:", "\xf0\x9f\x8c\xba"}, {":sunflower:", "\xf0\x9f\x8c\xbb"}, {":blossom:", "\xf0\x9f\x8c\xbc"}, {":tulip:", "\xf0\x9f\x8c\xb7"}, {":seedling:", "\xf0\x9f\x8c\xb1"}, {":evergreen_tree:", "\xf0\x9f\x8c\xb2"}, {":deciduous_tree:", "\xf0\x9f\x8c\xb3"}, {":palm_tree:", "\xf0\x9f\x8c\xb4"}, {":cactus:", "\xf0\x9f\x8c\xb5"}, {":ear_of_rice:", "\xf0\x9f\x8c\xbe"}, {":herb:", "\xf0\x9f\x8c\xbf"}, {":shamrock:", "\xe2\x98\x98"}, {":four_leaf_clover:", "\xf0\x9f\x8d\x80"}, {":maple_leaf:", "\xf0\x9f\x8d\x81"}, {":fallen_leaf:", "\xf0\x9f\x8d\x82"}, {":leaves:", "\xf0\x9f\x8d\x83"}, {":grapes:", "\xf0\x9f\x8d\x87"}, {":melon:", "\xf0\x9f\x8d\x88"}, {":watermelon:", "\xf0\x9f\x8d\x89"}, {":tangerine:", "\xf0\x9f\x8d\x8a"}, {":lemon:", "\xf0\x9f\x8d\x8b"}, {":banana:", "\xf0\x9f\x8d\x8c"}, {":pineapple:", "\xf0\x9f\x8d\x8d"}, {":apple:", "\xf0\x9f\x8d\x8e"}, {":green_apple:", "\xf0\x9f\x8d\x8f"}, {":pear:", "\xf0\x9f\x8d\x90"}, {":peach:", "\xf0\x9f\x8d\x91"}, {":cherries:", "\xf0\x9f\x8d\x92"}, {":strawberry:", "\xf0\x9f\x8d\x93"}, {":kiwi:", "\xf0\x9f\xa5\x9d"}, {":tomato:", "\xf0\x9f\x8d\x85"}, {":avocado:", "\xf0\x9f\xa5\x91"}, {":eggplant:", "\xf0\x9f\x8d\x86"}, {":potato:", "\xf0\x9f\xa5\x94"}, {":carrot:", "\xf0\x9f\xa5\x95"}, {":corn:", "\xf0\x9f\x8c\xbd"}, {":hot_pepper:", "\xf0\x9f\x8c\xb6"}, {":cucumber:", "\xf0\x9f\xa5\x92"}, {":mushroom:", "\xf0\x9f\x8d\x84"}, {":peanuts:", "\xf0\x9f\xa5\x9c"}, {":chestnut:", "\xf0\x9f\x8c\xb0"}, {":bread:", "\xf0\x9f\x8d\x9e"}, {":croissant:", "\xf0\x9f\xa5\x90"}, {":french_bread:", "\xf0\x9f\xa5\x96"}, {":pancakes:", "\xf0\x9f\xa5\x9e"}, {":cheese:", "\xf0\x9f\xa7\x80"}, {":meat_on_bone:", "\xf0\x9f\x8d\x96"}, {":poultry_leg:", "\xf0\x9f\x8d\x97"}, {":bacon:", "\xf0\x9f\xa5\x93"}, {":hamburger:", "\xf0\x9f\x8d\x94"}, {":fries:", "\xf0\x9f\x8d\x9f"}, {":pizza:", "\xf0\x9f\x8d\x95"}, {":hotdog:", "\xf0\x9f\x8c\xad"}, {":taco:", "\xf0\x9f\x8c\xae"}, {":burrito:", "\xf0\x9f\x8c\xaf"}, {":stuffed_flatbread:", "\xf0\x9f\xa5\x99"}, {":egg:", "\xf0\x9f\xa5\x9a"}, {":cooking:", "\xf0\x9f\x8d\xb3"}, {":shallow_pan_of_food:", "\xf0\x9f\xa5\x98"}, {":stew:", "\xf0\x9f\x8d\xb2"}, {":salad:", "\xf0\x9f\xa5\x97"}, {":popcorn:", "\xf0\x9f\x8d\xbf"}, {":bento:", "\xf0\x9f\x8d\xb1"}, {":rice_cracker:", "\xf0\x9f\x8d\x98"}, {":rice_ball:", "\xf0\x9f\x8d\x99"}, {":rice:", "\xf0\x9f\x8d\x9a"}, {":curry:", "\xf0\x9f\x8d\x9b"}, {":ramen:", "\xf0\x9f\x8d\x9c"}, {":spaghetti:", "\xf0\x9f\x8d\x9d"}, {":sweet_potato:", "\xf0\x9f\x8d\xa0"}, {":oden:", "\xf0\x9f\x8d\xa2"}, {":sushi:", "\xf0\x9f\x8d\xa3"}, {":fried_shrimp:", "\xf0\x9f\x8d\xa4"}, {":fish_cake:", "\xf0\x9f\x8d\xa5"}, {":dango:", "\xf0\x9f\x8d\xa1"}, {":icecream:", "\xf0\x9f\x8d\xa6"}, {":shaved_ice:", "\xf0\x9f\x8d\xa7"}, {":ice_cream:", "\xf0\x9f\x8d\xa8"}, {":doughnut:", "\xf0\x9f\x8d\xa9"}, {":cookie:", "\xf0\x9f\x8d\xaa"}, {":birthday:", "\xf0\x9f\x8e\x82"}, {":cake:", "\xf0\x9f\x8d\xb0"}, {":chocolate_bar:", "\xf0\x9f\x8d\xab"}, {":candy:", "\xf0\x9f\x8d\xac"}, {":lollipop:", "\xf0\x9f\x8d\xad"}, {":custard:", "\xf0\x9f\x8d\xae"}, {":honey_pot:", "\xf0\x9f\x8d\xaf"}, {":baby_bottle:", "\xf0\x9f\x8d\xbc"}, {":milk:", "\xf0\x9f\xa5\x9b"}, {":coffee:", "\xe2\x98\x95"}, {":tea:", "\xf0\x9f\x8d\xb5"}, {":sake:", "\xf0\x9f\x8d\xb6"}, {":champagne:", "\xf0\x9f\x8d\xbe"}, {":wine_glass:", "\xf0\x9f\x8d\xb7"}, {":cocktail:", "\xf0\x9f\x8d\xb8"}, {":tropical_drink:", "\xf0\x9f\x8d\xb9"}, {":beer:", "\xf0\x9f\x8d\xba"}, {":beers:", "\xf0\x9f\x8d\xbb"}, {":champagne_glass:", "\xf0\x9f\xa5\x82"}, {":tumbler_glass:", "\xf0\x9f\xa5\x83"}, {":fork_knife_plate:", "\xf0\x9f\x8d\xbd"}, {":fork_and_knife:", "\xf0\x9f\x8d\xb4"}, {":spoon:", "\xf0\x9f\xa5\x84"}, {":knife:", "\xf0\x9f\x94\xaa"}, {":amphora:", "\xf0\x9f\x8f\xba"}, {":earth_africa:", "\xf0\x9f\x8c\x8d"}, {":earth_americas:", "\xf0\x9f\x8c\x8e"}, {":earth_asia:", "\xf0\x9f\x8c\x8f"}, {":globe_with_meridians:", "\xf0\x9f\x8c\x90"}, {":map:", "\xf0\x9f\x97\xba"}, {":japan:", "\xf0\x9f\x97\xbe"}, {":mountain_snow:", "\xf0\x9f\x8f\x94"}, {":mountain:", "\xe2\x9b\xb0"}, {":volcano:", "\xf0\x9f\x8c\x8b"}, {":mount_fuji:", "\xf0\x9f\x97\xbb"}, {":camping:", "\xf0\x9f\x8f\x95"}, {":beach:", "\xf0\x9f\x8f\x96"}, {":desert:", "\xf0\x9f\x8f\x9c"}, {":island:", "\xf0\x9f\x8f\x9d"}, {":park:", "\xf0\x9f\x8f\x9e"}, {":stadium:", "\xf0\x9f\x8f\x9f"}, {":classical_building:", "\xf0\x9f\x8f\x9b"}, {":construction_site:", "\xf0\x9f\x8f\x97"}, {":homes:", "\xf0\x9f\x8f\x98"}, {":cityscape:", "\xf0\x9f\x8f\x99"}, {":house_abandoned:", "\xf0\x9f\x8f\x9a"}, {":house:", "\xf0\x9f\x8f\xa0"}, {":house_with_garden:", "\xf0\x9f\x8f\xa1"}, {":office:", "\xf0\x9f\x8f\xa2"}, {":post_office:", "\xf0\x9f\x8f\xa3"}, {":european_post_office:", "\xf0\x9f\x8f\xa4"}, {":hospital:", "\xf0\x9f\x8f\xa5"}, {":bank:", "\xf0\x9f\x8f\xa6"}, {":hotel:", "\xf0\x9f\x8f\xa8"}, {":love_hotel:", "\xf0\x9f\x8f\xa9"}, {":convenience_store:", "\xf0\x9f\x8f\xaa"}, {":school:", "\xf0\x9f\x8f\xab"}, {":department_store:", "\xf0\x9f\x8f\xac"}, {":factory:", "\xf0\x9f\x8f\xad"}, {":japanese_castle:", "\xf0\x9f\x8f\xaf"}, {":european_castle:", "\xf0\x9f\x8f\xb0"}, {":wedding:", "\xf0\x9f\x92\x92"}, {":tokyo_tower:", "\xf0\x9f\x97\xbc"}, {":statue_of_liberty:", "\xf0\x9f\x97\xbd"}, {":church:", "\xe2\x9b\xaa"}, {":mosque:", "\xf0\x9f\x95\x8c"}, {":synagogue:", "\xf0\x9f\x95\x8d"}, {":shinto_shrine:", "\xe2\x9b\xa9"}, {":kaaba:", "\xf0\x9f\x95\x8b"}, {":fountain:", "\xe2\x9b\xb2"}, {":tent:", "\xe2\x9b\xba"}, {":foggy:", "\xf0\x9f\x8c\x81"}, {":night_with_stars:", "\xf0\x9f\x8c\x83"}, {":sunrise_over_mountains:", "\xf0\x9f\x8c\x84"}, {":sunrise:", "\xf0\x9f\x8c\x85"}, {":city_dusk:", "\xf0\x9f\x8c\x86"}, {":city_sunset:", "\xf0\x9f\x8c\x87"}, {":bridge_at_night:", "\xf0\x9f\x8c\x89"}, {":hotsprings:", "\xe2\x99\xa8"}, {":milky_way:", "\xf0\x9f\x8c\x8c"}, {":carousel_horse:", "\xf0\x9f\x8e\xa0"}, {":ferris_wheel:", "\xf0\x9f\x8e\xa1"}, {":roller_coaster:", "\xf0\x9f\x8e\xa2"}, {":barber:", "\xf0\x9f\x92\x88"}, {":circus_tent:", "\xf0\x9f\x8e\xaa"}, {":performing_arts:", "\xf0\x9f\x8e\xad"}, {":frame_photo:", "\xf0\x9f\x96\xbc"}, {":art:", "\xf0\x9f\x8e\xa8"}, {":slot_machine:", "\xf0\x9f\x8e\xb0"}, {":steam_locomotive:", "\xf0\x9f\x9a\x82"}, {":railway_car:", "\xf0\x9f\x9a\x83"}, {":bullettrain_side:", "\xf0\x9f\x9a\x84"}, {":bullettrain_front:", "\xf0\x9f\x9a\x85"}, {":train2:", "\xf0\x9f\x9a\x86"}, {":metro:", "\xf0\x9f\x9a\x87"}, {":light_rail:", "\xf0\x9f\x9a\x88"}, {":station:", "\xf0\x9f\x9a\x89"}, {":tram:", "\xf0\x9f\x9a\x8a"}, {":monorail:", "\xf0\x9f\x9a\x9d"}, {":mountain_railway:", "\xf0\x9f\x9a\x9e"}, {":train:", "\xf0\x9f\x9a\x8b"}, {":bus:", "\xf0\x9f\x9a\x8c"}, {":oncoming_bus:", "\xf0\x9f\x9a\x8d"}, {":trolleybus:", "\xf0\x9f\x9a\x8e"}, {":minibus:", "\xf0\x9f\x9a\x90"}, {":ambulance:", "\xf0\x9f\x9a\x91"}, {":fire_engine:", "\xf0\x9f\x9a\x92"}, {":police_car:", "\xf0\x9f\x9a\x93"}, {":oncoming_police_car:", "\xf0\x9f\x9a\x94"}, {":taxi:", "\xf0\x9f\x9a\x95"}, {":oncoming_taxi:", "\xf0\x9f\x9a\x96"}, {":red_car:", "\xf0\x9f\x9a\x97"}, {":oncoming_automobile:", "\xf0\x9f\x9a\x98"}, {":blue_car:", "\xf0\x9f\x9a\x99"}, {":truck:", "\xf0\x9f\x9a\x9a"}, {":articulated_lorry:", "\xf0\x9f\x9a\x9b"}, {":tractor:", "\xf0\x9f\x9a\x9c"}, {":bike:", "\xf0\x9f\x9a\xb2"}, {":scooter:", "\xf0\x9f\x9b\xb4"}, {":motor_scooter:", "\xf0\x9f\x9b\xb5"}, {":busstop:", "\xf0\x9f\x9a\x8f"}, {":motorway:", "\xf0\x9f\x9b\xa3"}, {":railway_track:", "\xf0\x9f\x9b\xa4"}, {":fuelpump:", "\xe2\x9b\xbd"}, {":rotating_light:", "\xf0\x9f\x9a\xa8"}, {":traffic_light:", "\xf0\x9f\x9a\xa5"}, {":vertical_traffic_light:", "\xf0\x9f\x9a\xa6"}, {":construction:", "\xf0\x9f\x9a\xa7"}, {":octagonal_sign:", "\xf0\x9f\x9b\x91"}, {":anchor:", "\xe2\x9a\x93"}, {":sailboat:", "\xe2\x9b\xb5"}, {":canoe:", "\xf0\x9f\x9b\xb6"}, {":speedboat:", "\xf0\x9f\x9a\xa4"}, {":cruise_ship:", "\xf0\x9f\x9b\xb3"}, {":ferry:", "\xe2\x9b\xb4"}, {":motorboat:", "\xf0\x9f\x9b\xa5"}, {":ship:", "\xf0\x9f\x9a\xa2"}, {":airplane:", "\xe2\x9c\x88"}, {":airplane_small:", "\xf0\x9f\x9b\xa9"}, {":airplane_departure:", "\xf0\x9f\x9b\xab"}, {":airplane_arriving:", "\xf0\x9f\x9b\xac"}, {":seat:", "\xf0\x9f\x92\xba"}, {":helicopter:", "\xf0\x9f\x9a\x81"}, {":suspension_railway:", "\xf0\x9f\x9a\x9f"}, {":mountain_cableway:", "\xf0\x9f\x9a\xa0"}, {":aerial_tramway:", "\xf0\x9f\x9a\xa1"}, {":rocket:", "\xf0\x9f\x9a\x80"}, {":satellite_orbital:", "\xf0\x9f\x9b\xb0"}, {":bellhop:", "\xf0\x9f\x9b\x8e"}, {":door:", "\xf0\x9f\x9a\xaa"}, {":sleeping_accommodation:", "\xf0\x9f\x9b\x8c"}, {":bed:", "\xf0\x9f\x9b\x8f"}, {":couch:", "\xf0\x9f\x9b\x8b"}, {":toilet:", "\xf0\x9f\x9a\xbd"}, {":shower:", "\xf0\x9f\x9a\xbf"}, {":bath:", "\xf0\x9f\x9b\x80"}, {":bath_tone1:", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbb"}, {":bath_tone2:", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbc"}, {":bath_tone3:", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbd"}, {":bath_tone4:", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbe"}, {":bath_tone5:", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbf"}, {":bathtub:", "\xf0\x9f\x9b\x81"}, {":hourglass:", "\xe2\x8c\x9b"}, {":hourglass_flowing_sand:", "\xe2\x8f\xb3"}, {":watch:", "\xe2\x8c\x9a"}, {":alarm_clock:", "\xe2\x8f\xb0"}, {":stopwatch:", "\xe2\x8f\xb1"}, {":timer:", "\xe2\x8f\xb2"}, {":clock:", "\xf0\x9f\x95\xb0"}, {":clock12:", "\xf0\x9f\x95\x9b"}, {":clock1230:", "\xf0\x9f\x95\xa7"}, {":clock1:", "\xf0\x9f\x95\x90"}, {":clock130:", "\xf0\x9f\x95\x9c"}, {":clock2:", "\xf0\x9f\x95\x91"}, {":clock230:", "\xf0\x9f\x95\x9d"}, {":clock3:", "\xf0\x9f\x95\x92"}, {":clock330:", "\xf0\x9f\x95\x9e"}, {":clock4:", "\xf0\x9f\x95\x93"}, {":clock430:", "\xf0\x9f\x95\x9f"}, {":clock5:", "\xf0\x9f\x95\x94"}, {":clock530:", "\xf0\x9f\x95\xa0"}, {":clock6:", "\xf0\x9f\x95\x95"}, {":clock630:", "\xf0\x9f\x95\xa1"}, {":clock7:", "\xf0\x9f\x95\x96"}, {":clock730:", "\xf0\x9f\x95\xa2"}, {":clock8:", "\xf0\x9f\x95\x97"}, {":clock830:", "\xf0\x9f\x95\xa3"}, {":clock9:", "\xf0\x9f\x95\x98"}, {":clock930:", "\xf0\x9f\x95\xa4"}, {":clock10:", "\xf0\x9f\x95\x99"}, {":clock1030:", "\xf0\x9f\x95\xa5"}, {":clock11:", "\xf0\x9f\x95\x9a"}, {":clock1130:", "\xf0\x9f\x95\xa6"}, {":new_moon:", "\xf0\x9f\x8c\x91"}, {":waxing_crescent_moon:", "\xf0\x9f\x8c\x92"}, {":first_quarter_moon:", "\xf0\x9f\x8c\x93"}, {":waxing_gibbous_moon:", "\xf0\x9f\x8c\x94"}, {":full_moon:", "\xf0\x9f\x8c\x95"}, {":waning_gibbous_moon:", "\xf0\x9f\x8c\x96"}, {":last_quarter_moon:", "\xf0\x9f\x8c\x97"}, {":waning_crescent_moon:", "\xf0\x9f\x8c\x98"}, {":crescent_moon:", "\xf0\x9f\x8c\x99"}, {":new_moon_with_face:", "\xf0\x9f\x8c\x9a"}, {":first_quarter_moon_with_face:", "\xf0\x9f\x8c\x9b"}, {":last_quarter_moon_with_face:", "\xf0\x9f\x8c\x9c"}, {":thermometer:", "\xf0\x9f\x8c\xa1"}, {":sunny:", "\xe2\x98\x80"}, {":full_moon_with_face:", "\xf0\x9f\x8c\x9d"}, {":sun_with_face:", "\xf0\x9f\x8c\x9e"}, {":star:", "\xe2\xad\x90"}, {":star2:", "\xf0\x9f\x8c\x9f"}, {":stars:", "\xf0\x9f\x8c\xa0"}, {":cloud:", "\xe2\x98\x81"}, {":partly_sunny:", "\xe2\x9b\x85"}, {":thunder_cloud_rain:", "\xe2\x9b\x88"}, {":white_sun_small_cloud:", "\xf0\x9f\x8c\xa4"}, {":white_sun_cloud:", "\xf0\x9f\x8c\xa5"}, {":white_sun_rain_cloud:", "\xf0\x9f\x8c\xa6"}, {":cloud_rain:", "\xf0\x9f\x8c\xa7"}, {":cloud_snow:", "\xf0\x9f\x8c\xa8"}, {":cloud_lightning:", "\xf0\x9f\x8c\xa9"}, {":cloud_tornado:", "\xf0\x9f\x8c\xaa"}, {":fog:", "\xf0\x9f\x8c\xab"}, {":wind_blowing_face:", "\xf0\x9f\x8c\xac"}, {":cyclone:", "\xf0\x9f\x8c\x80"}, {":rainbow:", "\xf0\x9f\x8c\x88"}, {":closed_umbrella:", "\xf0\x9f\x8c\x82"}, {":umbrella2:", "\xe2\x98\x82"}, {":umbrella:", "\xe2\x98\x94"}, {":beach_umbrella:", "\xe2\x9b\xb1"}, {":zap:", "\xe2\x9a\xa1"}, {":snowflake:", "\xe2\x9d\x84"}, {":snowman2:", "\xe2\x98\x83"}, {":snowman:", "\xe2\x9b\x84"}, {":comet:", "\xe2\x98\x84"}, {":fire:", "\xf0\x9f\x94\xa5"}, {":droplet:", "\xf0\x9f\x92\xa7"}, {":ocean:", "\xf0\x9f\x8c\x8a"}, {":jack_o_lantern:", "\xf0\x9f\x8e\x83"}, {":christmas_tree:", "\xf0\x9f\x8e\x84"}, {":fireworks:", "\xf0\x9f\x8e\x86"}, {":sparkler:", "\xf0\x9f\x8e\x87"}, {":sparkles:", "\xe2\x9c\xa8"}, {":balloon:", "\xf0\x9f\x8e\x88"}, {":tada:", "\xf0\x9f\x8e\x89"}, {":confetti_ball:", "\xf0\x9f\x8e\x8a"}, {":tanabata_tree:", "\xf0\x9f\x8e\x8b"}, {":bamboo:", "\xf0\x9f\x8e\x8d"}, {":dolls:", "\xf0\x9f\x8e\x8e"}, {":flags:", "\xf0\x9f\x8e\x8f"}, {":wind_chime:", "\xf0\x9f\x8e\x90"}, {":rice_scene:", "\xf0\x9f\x8e\x91"}, {":ribbon:", "\xf0\x9f\x8e\x80"}, {":gift:", "\xf0\x9f\x8e\x81"}, {":reminder_ribbon:", "\xf0\x9f\x8e\x97"}, {":tickets:", "\xf0\x9f\x8e\x9f"}, {":ticket:", "\xf0\x9f\x8e\xab"}, {":military_medal:", "\xf0\x9f\x8e\x96"}, {":trophy:", "\xf0\x9f\x8f\x86"}, {":medal:", "\xf0\x9f\x8f\x85"}, {":first_place:", "\xf0\x9f\xa5\x87"}, {":second_place:", "\xf0\x9f\xa5\x88"}, {":third_place:", "\xf0\x9f\xa5\x89"}, {":soccer:", "\xe2\x9a\xbd"}, {":baseball:", "\xe2\x9a\xbe"}, {":basketball:", "\xf0\x9f\x8f\x80"}, {":volleyball:", "\xf0\x9f\x8f\x90"}, {":football:", "\xf0\x9f\x8f\x88"}, {":rugby_football:", "\xf0\x9f\x8f\x89"}, {":tennis:", "\xf0\x9f\x8e\xbe"}, {":8ball:", "\xf0\x9f\x8e\xb1"}, {":bowling:", "\xf0\x9f\x8e\xb3"}, {":cricket:", "\xf0\x9f\x8f\x8f"}, {":field_hockey:", "\xf0\x9f\x8f\x91"}, {":hockey:", "\xf0\x9f\x8f\x92"}, {":ping_pong:", "\xf0\x9f\x8f\x93"}, {":badminton:", "\xf0\x9f\x8f\xb8"}, {":boxing_glove:", "\xf0\x9f\xa5\x8a"}, {":martial_arts_uniform:", "\xf0\x9f\xa5\x8b"}, {":goal:", "\xf0\x9f\xa5\x85"}, {":dart:", "\xf0\x9f\x8e\xaf"}, {":golf:", "\xe2\x9b\xb3"}, {":ice_skate:", "\xe2\x9b\xb8"}, {":fishing_pole_and_fish:", "\xf0\x9f\x8e\xa3"}, {":running_shirt_with_sash:", "\xf0\x9f\x8e\xbd"}, {":ski:", "\xf0\x9f\x8e\xbf"}, {":video_game:", "\xf0\x9f\x8e\xae"}, {":joystick:", "\xf0\x9f\x95\xb9"}, {":game_die:", "\xf0\x9f\x8e\xb2"}, {":spades:", "\xe2\x99\xa0"}, {":hearts:", "\xe2\x99\xa5"}, {":diamonds:", "\xe2\x99\xa6"}, {":clubs:", "\xe2\x99\xa3"}, {":black_joker:", "\xf0\x9f\x83\x8f"}, {":mahjong:", "\xf0\x9f\x80\x84"}, {":flower_playing_cards:", "\xf0\x9f\x8e\xb4"}, {":mute:", "\xf0\x9f\x94\x87"}, {":speaker:", "\xf0\x9f\x94\x88"}, {":sound:", "\xf0\x9f\x94\x89"}, {":loud_sound:", "\xf0\x9f\x94\x8a"}, {":loudspeaker:", "\xf0\x9f\x93\xa2"}, {":mega:", "\xf0\x9f\x93\xa3"}, {":postal_horn:", "\xf0\x9f\x93\xaf"}, {":bell:", "\xf0\x9f\x94\x94"}, {":no_bell:", "\xf0\x9f\x94\x95"}, {":musical_score:", "\xf0\x9f\x8e\xbc"}, {":musical_note:", "\xf0\x9f\x8e\xb5"}, {":notes:", "\xf0\x9f\x8e\xb6"}, {":microphone2:", "\xf0\x9f\x8e\x99"}, {":level_slider:", "\xf0\x9f\x8e\x9a"}, {":control_knobs:", "\xf0\x9f\x8e\x9b"}, {":microphone:", "\xf0\x9f\x8e\xa4"}, {":headphones:", "\xf0\x9f\x8e\xa7"}, {":radio:", "\xf0\x9f\x93\xbb"}, {":saxophone:", "\xf0\x9f\x8e\xb7"}, {":guitar:", "\xf0\x9f\x8e\xb8"}, {":musical_keyboard:", "\xf0\x9f\x8e\xb9"}, {":trumpet:", "\xf0\x9f\x8e\xba"}, {":violin:", "\xf0\x9f\x8e\xbb"}, {":drum:", "\xf0\x9f\xa5\x81"}, {":iphone:", "\xf0\x9f\x93\xb1"}, {":calling:", "\xf0\x9f\x93\xb2"}, {":telephone:", "\xe2\x98\x8e"}, {":telephone_receiver:", "\xf0\x9f\x93\x9e"}, {":pager:", "\xf0\x9f\x93\x9f"}, {":fax:", "\xf0\x9f\x93\xa0"}, {":battery:", "\xf0\x9f\x94\x8b"}, {":electric_plug:", "\xf0\x9f\x94\x8c"}, {":computer:", "\xf0\x9f\x92\xbb"}, {":desktop:", "\xf0\x9f\x96\xa5"}, {":printer:", "\xf0\x9f\x96\xa8"}, {":keyboard:", "\xe2\x8c\xa8"}, {":mouse_three_button:", "\xf0\x9f\x96\xb1"}, {":trackball:", "\xf0\x9f\x96\xb2"}, {":minidisc:", "\xf0\x9f\x92\xbd"}, {":floppy_disk:", "\xf0\x9f\x92\xbe"}, {":cd:", "\xf0\x9f\x92\xbf"}, {":dvd:", "\xf0\x9f\x93\x80"}, {":movie_camera:", "\xf0\x9f\x8e\xa5"}, {":film_frames:", "\xf0\x9f\x8e\x9e"}, {":projector:", "\xf0\x9f\x93\xbd"}, {":clapper:", "\xf0\x9f\x8e\xac"}, {":tv:", "\xf0\x9f\x93\xba"}, {":camera:", "\xf0\x9f\x93\xb7"}, {":camera_with_flash:", "\xf0\x9f\x93\xb8"}, {":video_camera:", "\xf0\x9f\x93\xb9"}, {":vhs:", "\xf0\x9f\x93\xbc"}, {":mag:", "\xf0\x9f\x94\x8d"}, {":mag_right:", "\xf0\x9f\x94\x8e"}, {":microscope:", "\xf0\x9f\x94\xac"}, {":telescope:", "\xf0\x9f\x94\xad"}, {":satellite:", "\xf0\x9f\x93\xa1"}, {":candle:", "\xf0\x9f\x95\xaf"}, {":bulb:", "\xf0\x9f\x92\xa1"}, {":flashlight:", "\xf0\x9f\x94\xa6"}, {":izakaya_lantern:", "\xf0\x9f\x8f\xae"}, {":notebook_with_decorative_cover:", "\xf0\x9f\x93\x94"}, {":closed_book:", "\xf0\x9f\x93\x95"}, {":book:", "\xf0\x9f\x93\x96"}, {":green_book:", "\xf0\x9f\x93\x97"}, {":blue_book:", "\xf0\x9f\x93\x98"}, {":orange_book:", "\xf0\x9f\x93\x99"}, {":books:", "\xf0\x9f\x93\x9a"}, {":notebook:", "\xf0\x9f\x93\x93"}, {":ledger:", "\xf0\x9f\x93\x92"}, {":page_with_curl:", "\xf0\x9f\x93\x83"}, {":scroll:", "\xf0\x9f\x93\x9c"}, {":page_facing_up:", "\xf0\x9f\x93\x84"}, {":newspaper:", "\xf0\x9f\x93\xb0"}, {":newspaper2:", "\xf0\x9f\x97\x9e"}, {":bookmark_tabs:", "\xf0\x9f\x93\x91"}, {":bookmark:", "\xf0\x9f\x94\x96"}, {":label:", "\xf0\x9f\x8f\xb7"}, {":moneybag:", "\xf0\x9f\x92\xb0"}, {":yen:", "\xf0\x9f\x92\xb4"}, {":dollar:", "\xf0\x9f\x92\xb5"}, {":euro:", "\xf0\x9f\x92\xb6"}, {":pound:", "\xf0\x9f\x92\xb7"}, {":money_with_wings:", "\xf0\x9f\x92\xb8"}, {":credit_card:", "\xf0\x9f\x92\xb3"}, {":chart:", "\xf0\x9f\x92\xb9"}, {":currency_exchange:", "\xf0\x9f\x92\xb1"}, {":heavy_dollar_sign:", "\xf0\x9f\x92\xb2"}, {":envelope:", "\xe2\x9c\x89"}, {":e-mail:", "\xf0\x9f\x93\xa7"}, {":incoming_envelope:", "\xf0\x9f\x93\xa8"}, {":envelope_with_arrow:", "\xf0\x9f\x93\xa9"}, {":outbox_tray:", "\xf0\x9f\x93\xa4"}, {":inbox_tray:", "\xf0\x9f\x93\xa5"}, {":package:", "\xf0\x9f\x93\xa6"}, {":mailbox:", "\xf0\x9f\x93\xab"}, {":mailbox_closed:", "\xf0\x9f\x93\xaa"}, {":mailbox_with_mail:", "\xf0\x9f\x93\xac"}, {":mailbox_with_no_mail:", "\xf0\x9f\x93\xad"}, {":postbox:", "\xf0\x9f\x93\xae"}, {":ballot_box:", "\xf0\x9f\x97\xb3"}, {":pencil2:", "\xe2\x9c\x8f"}, {":black_nib:", "\xe2\x9c\x92"}, {":pen_fountain:", "\xf0\x9f\x96\x8b"}, {":pen_ballpoint:", "\xf0\x9f\x96\x8a"}, {":paintbrush:", "\xf0\x9f\x96\x8c"}, {":crayon:", "\xf0\x9f\x96\x8d"}, {":pencil:", "\xf0\x9f\x93\x9d"}, {":briefcase:", "\xf0\x9f\x92\xbc"}, {":file_folder:", "\xf0\x9f\x93\x81"}, {":open_file_folder:", "\xf0\x9f\x93\x82"}, {":dividers:", "\xf0\x9f\x97\x82"}, {":date:", "\xf0\x9f\x93\x85"}, {":calendar:", "\xf0\x9f\x93\x86"}, {":notepad_spiral:", "\xf0\x9f\x97\x92"}, {":calendar_spiral:", "\xf0\x9f\x97\x93"}, {":card_index:", "\xf0\x9f\x93\x87"}, {":chart_with_upwards_trend:", "\xf0\x9f\x93\x88"}, {":chart_with_downwards_trend:", "\xf0\x9f\x93\x89"}, {":bar_chart:", "\xf0\x9f\x93\x8a"}, {":clipboard:", "\xf0\x9f\x93\x8b"}, {":pushpin:", "\xf0\x9f\x93\x8c"}, {":round_pushpin:", "\xf0\x9f\x93\x8d"}, {":paperclip:", "\xf0\x9f\x93\x8e"}, {":paperclips:", "\xf0\x9f\x96\x87"}, {":straight_ruler:", "\xf0\x9f\x93\x8f"}, {":triangular_ruler:", "\xf0\x9f\x93\x90"}, {":scissors:", "\xe2\x9c\x82"}, {":card_box:", "\xf0\x9f\x97\x83"}, {":file_cabinet:", "\xf0\x9f\x97\x84"}, {":wastebasket:", "\xf0\x9f\x97\x91"}, {":lock:", "\xf0\x9f\x94\x92"}, {":unlock:", "\xf0\x9f\x94\x93"}, {":lock_with_ink_pen:", "\xf0\x9f\x94\x8f"}, {":closed_lock_with_key:", "\xf0\x9f\x94\x90"}, {":key:", "\xf0\x9f\x94\x91"}, {":key2:", "\xf0\x9f\x97\x9d"}, {":hammer:", "\xf0\x9f\x94\xa8"}, {":pick:", "\xe2\x9b\x8f"}, {":hammer_pick:", "\xe2\x9a\x92"}, {":tools:", "\xf0\x9f\x9b\xa0"}, {":dagger:", "\xf0\x9f\x97\xa1"}, {":crossed_swords:", "\xe2\x9a\x94"}, {":gun:", "\xf0\x9f\x94\xab"}, {":bow_and_arrow:", "\xf0\x9f\x8f\xb9"}, {":shield:", "\xf0\x9f\x9b\xa1"}, {":wrench:", "\xf0\x9f\x94\xa7"}, {":nut_and_bolt:", "\xf0\x9f\x94\xa9"}, {":gear:", "\xe2\x9a\x99"}, {":compression:", "\xf0\x9f\x97\x9c"}, {":alembic:", "\xe2\x9a\x97"}, {":scales:", "\xe2\x9a\x96"}, {":link:", "\xf0\x9f\x94\x97"}, {":chains:", "\xe2\x9b\x93"}, {":syringe:", "\xf0\x9f\x92\x89"}, {":pill:", "\xf0\x9f\x92\x8a"}, {":smoking:", "\xf0\x9f\x9a\xac"}, {":coffin:", "\xe2\x9a\xb0"}, {":urn:", "\xe2\x9a\xb1"}, {":moyai:", "\xf0\x9f\x97\xbf"}, {":oil:", "\xf0\x9f\x9b\xa2"}, {":crystal_ball:", "\xf0\x9f\x94\xae"}, {":shopping_cart:", "\xf0\x9f\x9b\x92"}, {":atm:", "\xf0\x9f\x8f\xa7"}, {":put_litter_in_its_place:", "\xf0\x9f\x9a\xae"}, {":potable_water:", "\xf0\x9f\x9a\xb0"}, {":wheelchair:", "\xe2\x99\xbf"}, {":mens:", "\xf0\x9f\x9a\xb9"}, {":womens:", "\xf0\x9f\x9a\xba"}, {":restroom:", "\xf0\x9f\x9a\xbb"}, {":baby_symbol:", "\xf0\x9f\x9a\xbc"}, {":wc:", "\xf0\x9f\x9a\xbe"}, {":passport_control:", "\xf0\x9f\x9b\x82"}, {":customs:", "\xf0\x9f\x9b\x83"}, {":baggage_claim:", "\xf0\x9f\x9b\x84"}, {":left_luggage:", "\xf0\x9f\x9b\x85"}, {":warning:", "\xe2\x9a\xa0"}, {":children_crossing:", "\xf0\x9f\x9a\xb8"}, {":no_entry:", "\xe2\x9b\x94"}, {":no_entry_sign:", "\xf0\x9f\x9a\xab"}, {":no_bicycles:", "\xf0\x9f\x9a\xb3"}, {":no_smoking:", "\xf0\x9f\x9a\xad"}, {":do_not_litter:", "\xf0\x9f\x9a\xaf"}, {":non-potable_water:", "\xf0\x9f\x9a\xb1"}, {":no_pedestrians:", "\xf0\x9f\x9a\xb7"}, {":no_mobile_phones:", "\xf0\x9f\x93\xb5"}, {":underage:", "\xf0\x9f\x94\x9e"}, {":radioactive:", "\xe2\x98\xa2"}, {":biohazard:", "\xe2\x98\xa3"}, {":arrow_up:", "\xe2\xac\x86"}, {":arrow_upper_right:", "\xe2\x86\x97"}, {":arrow_right:", "\xe2\x9e\xa1"}, {":arrow_lower_right:", "\xe2\x86\x98"}, {":arrow_down:", "\xe2\xac\x87"}, {":arrow_lower_left:", "\xe2\x86\x99"}, {":arrow_left:", "\xe2\xac\x85"}, {":arrow_upper_left:", "\xe2\x86\x96"}, {":arrow_up_down:", "\xe2\x86\x95"}, {":left_right_arrow:", "\xe2\x86\x94"}, {":leftwards_arrow_with_hook:", "\xe2\x86\xa9"}, {":arrow_right_hook:", "\xe2\x86\xaa"}, {":arrow_heading_up:", "\xe2\xa4\xb4"}, {":arrow_heading_down:", "\xe2\xa4\xb5"}, {":arrows_clockwise:", "\xf0\x9f\x94\x83"}, {":arrows_counterclockwise:", "\xf0\x9f\x94\x84"}, {":back:", "\xf0\x9f\x94\x99"}, {":end:", "\xf0\x9f\x94\x9a"}, {":on:", "\xf0\x9f\x94\x9b"}, {":soon:", "\xf0\x9f\x94\x9c"}, {":top:", "\xf0\x9f\x94\x9d"}, {":place_of_worship:", "\xf0\x9f\x9b\x90"}, {":atom:", "\xe2\x9a\x9b"}, {":om_symbol:", "\xf0\x9f\x95\x89"}, {":star_of_david:", "\xe2\x9c\xa1"}, {":wheel_of_dharma:", "\xe2\x98\xb8"}, {":yin_yang:", "\xe2\x98\xaf"}, {":cross:", "\xe2\x9c\x9d"}, {":orthodox_cross:", "\xe2\x98\xa6"}, {":star_and_crescent:", "\xe2\x98\xaa"}, {":peace:", "\xe2\x98\xae"}, {":menorah:", "\xf0\x9f\x95\x8e"}, {":six_pointed_star:", "\xf0\x9f\x94\xaf"}, {":aries:", "\xe2\x99\x88"}, {":taurus:", "\xe2\x99\x89"}, {":gemini:", "\xe2\x99\x8a"}, {":cancer:", "\xe2\x99\x8b"}, {":leo:", "\xe2\x99\x8c"}, {":virgo:", "\xe2\x99\x8d"}, {":libra:", "\xe2\x99\x8e"}, {":scorpius:", "\xe2\x99\x8f"}, {":sagittarius:", "\xe2\x99\x90"}, {":capricorn:", "\xe2\x99\x91"}, {":aquarius:", "\xe2\x99\x92"}, {":pisces:", "\xe2\x99\x93"}, {":ophiuchus:", "\xe2\x9b\x8e"}, {":twisted_rightwards_arrows:", "\xf0\x9f\x94\x80"}, {":repeat:", "\xf0\x9f\x94\x81"}, {":repeat_one:", "\xf0\x9f\x94\x82"}, {":arrow_forward:", "\xe2\x96\xb6"}, {":fast_forward:", "\xe2\x8f\xa9"}, {":track_next:", "\xe2\x8f\xad"}, {":play_pause:", "\xe2\x8f\xaf"}, {":arrow_backward:", "\xe2\x97\x80"}, {":rewind:", "\xe2\x8f\xaa"}, {":track_previous:", "\xe2\x8f\xae"}, {":arrow_up_small:", "\xf0\x9f\x94\xbc"}, {":arrow_double_up:", "\xe2\x8f\xab"}, {":arrow_down_small:", "\xf0\x9f\x94\xbd"}, {":arrow_double_down:", "\xe2\x8f\xac"}, {":pause_button:", "\xe2\x8f\xb8"}, {":stop_button:", "\xe2\x8f\xb9"}, {":record_button:", "\xe2\x8f\xba"}, {":eject:", "\xe2\x8f\x8f"}, {":cinema:", "\xf0\x9f\x8e\xa6"}, {":low_brightness:", "\xf0\x9f\x94\x85"}, {":high_brightness:", "\xf0\x9f\x94\x86"}, {":signal_strength:", "\xf0\x9f\x93\xb6"}, {":vibration_mode:", "\xf0\x9f\x93\xb3"}, {":mobile_phone_off:", "\xf0\x9f\x93\xb4"}, {":recycle:", "\xe2\x99\xbb"}, {":name_badge:", "\xf0\x9f\x93\x9b"}, {":fleur-de-lis:", "\xe2\x9a\x9c"}, {":beginner:", "\xf0\x9f\x94\xb0"}, {":trident:", "\xf0\x9f\x94\xb1"}, {":o:", "\xe2\xad\x95"}, {":white_check_mark:", "\xe2\x9c\x85"}, {":ballot_box_with_check:", "\xe2\x98\x91"}, {":heavy_check_mark:", "\xe2\x9c\x94"}, {":heavy_multiplication_x:", "\xe2\x9c\x96"}, {":x:", "\xe2\x9d\x8c"}, {":negative_squared_cross_mark:", "\xe2\x9d\x8e"}, {":heavy_plus_sign:", "\xe2\x9e\x95"}, {":heavy_minus_sign:", "\xe2\x9e\x96"}, {":heavy_division_sign:", "\xe2\x9e\x97"}, {":curly_loop:", "\xe2\x9e\xb0"}, {":loop:", "\xe2\x9e\xbf"}, {":part_alternation_mark:", "\xe3\x80\xbd"}, {":eight_spoked_asterisk:", "\xe2\x9c\xb3"}, {":eight_pointed_black_star:", "\xe2\x9c\xb4"}, {":sparkle:", "\xe2\x9d\x87"}, {":bangbang:", "\xe2\x80\xbc"}, {":interrobang:", "\xe2\x81\x89"}, {":question:", "\xe2\x9d\x93"}, {":grey_question:", "\xe2\x9d\x94"}, {":grey_exclamation:", "\xe2\x9d\x95"}, {":exclamation:", "\xe2\x9d\x97"}, {":wavy_dash:", "\xe3\x80\xb0"}, {":copyright:", "\xc2\xa9"}, {":registered:", "\xc2\xae"}, {":tm:", "\xe2\x84\xa2"}, {":hash:", "\x23\xe2\x83\xa3"}, {":asterisk:", "\x2a\xe2\x83\xa3"}, {":zero:", "\x30\xe2\x83\xa3"}, {":one:", "\x31\xe2\x83\xa3"}, {":two:", "\x32\xe2\x83\xa3"}, {":three:", "\x33\xe2\x83\xa3"}, {":four:", "\x34\xe2\x83\xa3"}, {":five:", "\x35\xe2\x83\xa3"}, {":six:", "\x36\xe2\x83\xa3"}, {":seven:", "\x37\xe2\x83\xa3"}, {":eight:", "\x38\xe2\x83\xa3"}, {":nine:", "\x39\xe2\x83\xa3"}, {":keycap_ten:", "\xf0\x9f\x94\x9f"}, {":capital_abcd:", "\xf0\x9f\x94\xa0"}, {":abcd:", "\xf0\x9f\x94\xa1"}, {":symbols:", "\xf0\x9f\x94\xa3"}, {":abc:", "\xf0\x9f\x94\xa4"}, {":a:", "\xf0\x9f\x85\xb0"}, {":ab:", "\xf0\x9f\x86\x8e"}, {":b:", "\xf0\x9f\x85\xb1"}, {":cl:", "\xf0\x9f\x86\x91"}, {":cool:", "\xf0\x9f\x86\x92"}, {":free:", "\xf0\x9f\x86\x93"}, {":information_source:", "\xe2\x84\xb9"}, {":id:", "\xf0\x9f\x86\x94"}, {":m:", "\xe2\x93\x82"}, {":new:", "\xf0\x9f\x86\x95"}, {":ng:", "\xf0\x9f\x86\x96"}, {":o2:", "\xf0\x9f\x85\xbe"}, {":ok:", "\xf0\x9f\x86\x97"}, {":parking:", "\xf0\x9f\x85\xbf"}, {":sos:", "\xf0\x9f\x86\x98"}, {":up:", "\xf0\x9f\x86\x99"}, {":vs:", "\xf0\x9f\x86\x9a"}, {":koko:", "\xf0\x9f\x88\x81"}, {":sa:", "\xf0\x9f\x88\x82"}, {":u6708:", "\xf0\x9f\x88\xb7"}, {":u6709:", "\xf0\x9f\x88\xb6"}, {":u6307:", "\xf0\x9f\x88\xaf"}, {":ideograph_advantage:", "\xf0\x9f\x89\x90"}, {":u5272:", "\xf0\x9f\x88\xb9"}, {":u7121:", "\xf0\x9f\x88\x9a"}, {":u7981:", "\xf0\x9f\x88\xb2"}, {":accept:", "\xf0\x9f\x89\x91"}, {":u7533:", "\xf0\x9f\x88\xb8"}, {":u5408:", "\xf0\x9f\x88\xb4"}, {":u7a7a:", "\xf0\x9f\x88\xb3"}, {":congratulations:", "\xe3\x8a\x97"}, {":secret:", "\xe3\x8a\x99"}, {":u55b6:", "\xf0\x9f\x88\xba"}, {":u6e80:", "\xf0\x9f\x88\xb5"}, {":black_small_square:", "\xe2\x96\xaa"}, {":white_small_square:", "\xe2\x96\xab"}, {":white_medium_square:", "\xe2\x97\xbb"}, {":black_medium_square:", "\xe2\x97\xbc"}, {":white_medium_small_square:", "\xe2\x97\xbd"}, {":black_medium_small_square:", "\xe2\x97\xbe"}, {":black_large_square:", "\xe2\xac\x9b"}, {":white_large_square:", "\xe2\xac\x9c"}, {":large_orange_diamond:", "\xf0\x9f\x94\xb6"}, {":large_blue_diamond:", "\xf0\x9f\x94\xb7"}, {":small_orange_diamond:", "\xf0\x9f\x94\xb8"}, {":small_blue_diamond:", "\xf0\x9f\x94\xb9"}, {":small_red_triangle:", "\xf0\x9f\x94\xba"}, {":small_red_triangle_down:", "\xf0\x9f\x94\xbb"}, {":diamond_shape_with_a_dot_inside:", "\xf0\x9f\x92\xa0"}, {":radio_button:", "\xf0\x9f\x94\x98"}, {":black_square_button:", "\xf0\x9f\x94\xb2"}, {":white_square_button:", "\xf0\x9f\x94\xb3"}, {":white_circle:", "\xe2\x9a\xaa"}, {":black_circle:", "\xe2\x9a\xab"}, {":red_circle:", "\xf0\x9f\x94\xb4"}, {":blue_circle:", "\xf0\x9f\x94\xb5"}, {":checkered_flag:", "\xf0\x9f\x8f\x81"}, {":triangular_flag_on_post:", "\xf0\x9f\x9a\xa9"}, {":crossed_flags:", "\xf0\x9f\x8e\x8c"}, {":flag_black:", "\xf0\x9f\x8f\xb4"}, {":flag_white:", "\xf0\x9f\x8f\xb3"}, {":rainbow_flag:", "\xf0\x9f\x8f\xb3\xf0\x9f\x8c\x88"}, {":flag_ac:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xa8"}, {":flag_ad:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xa9"}, {":flag_ae:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xaa"}, {":flag_af:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xab"}, {":flag_ag:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xac"}, {":flag_ai:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xae"}, {":flag_al:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb1"}, {":flag_am:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2"}, {":flag_ao:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb4"}, {":flag_aq:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb6"}, {":flag_ar:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb7"}, {":flag_as:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb8"}, {":flag_at:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb9"}, {":flag_au:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xba"}, {":flag_aw:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xbc"}, {":flag_ax:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xbd"}, {":flag_az:", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xbf"}, {":flag_ba:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xa6"}, {":flag_bb:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xa7"}, {":flag_bd:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xa9"}, {":flag_be:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xaa"}, {":flag_bf:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xab"}, {":flag_bg:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xac"}, {":flag_bh:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xad"}, {":flag_bi:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xae"}, {":flag_bj:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xaf"}, {":flag_bl:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb1"}, {":flag_bm:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb2"}, {":flag_bn:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb3"}, {":flag_bo:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb4"}, {":flag_bq:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb6"}, {":flag_br:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb7"}, {":flag_bs:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb8"}, {":flag_bt:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb9"}, {":flag_bv:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbb"}, {":flag_bw:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbc"}, {":flag_by:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbe"}, {":flag_bz:", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbf"}, {":flag_ca:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xa6"}, {":flag_cc:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xa8"}, {":flag_cd:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xa9"}, {":flag_cf:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xab"}, {":flag_cg:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xac"}, {":flag_ch:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xad"}, {":flag_ci:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xae"}, {":flag_ck:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb0"}, {":flag_cl:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb1"}, {":flag_cm:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb2"}, {":flag_cn:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb3"}, {":flag_co:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb4"}, {":flag_cp:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb5"}, {":flag_cr:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb7"}, {":flag_cu:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xba"}, {":flag_cv:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbb"}, {":flag_cw:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbc"}, {":flag_cx:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbd"}, {":flag_cy:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbe"}, {":flag_cz:", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbf"}, {":flag_de:", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xaa"}, {":flag_dg:", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xac"}, {":flag_dj:", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xaf"}, {":flag_dk:", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xb0"}, {":flag_dm:", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xb2"}, {":flag_do:", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xb4"}, {":flag_dz:", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xbf"}, {":flag_ea:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xa6"}, {":flag_ec:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xa8"}, {":flag_ee:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xaa"}, {":flag_eg:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xac"}, {":flag_eh:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xad"}, {":flag_er:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xb7"}, {":flag_es:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xb8"}, {":flag_et:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xb9"}, {":flag_eu:", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xba"}, {":flag_fi:", "\xf0\x9f\x87\xab\xf0\x9f\x87\xae"}, {":flag_fj:", "\xf0\x9f\x87\xab\xf0\x9f\x87\xaf"}, {":flag_fk:", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb0"}, {":flag_fm:", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb2"}, {":flag_fo:", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb4"}, {":flag_fr:", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb7"}, {":flag_ga:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xa6"}, {":flag_gb:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xa7"}, {":flag_gd:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xa9"}, {":flag_ge:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xaa"}, {":flag_gf:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xab"}, {":flag_gg:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xac"}, {":flag_gh:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xad"}, {":flag_gi:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xae"}, {":flag_gl:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb1"}, {":flag_gm:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb2"}, {":flag_gn:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb3"}, {":flag_gp:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb5"}, {":flag_gq:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb6"}, {":flag_gr:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb7"}, {":flag_gs:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb8"}, {":flag_gt:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb9"}, {":flag_gu:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xba"}, {":flag_gw:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xbc"}, {":flag_gy:", "\xf0\x9f\x87\xac\xf0\x9f\x87\xbe"}, {":flag_hk:", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb0"}, {":flag_hm:", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb2"}, {":flag_hn:", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb3"}, {":flag_hr:", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb7"}, {":flag_ht:", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb9"}, {":flag_hu:", "\xf0\x9f\x87\xad\xf0\x9f\x87\xba"}, {":flag_ic:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xa8"}, {":flag_id:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xa9"}, {":flag_ie:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xaa"}, {":flag_il:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb1"}, {":flag_im:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb2"}, {":flag_in:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb3"}, {":flag_io:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb4"}, {":flag_iq:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb6"}, {":flag_ir:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb7"}, {":flag_is:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb8"}, {":flag_it:", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb9"}, {":flag_je:", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xaa"}, {":flag_jm:", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xb2"}, {":flag_jo:", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xb4"}, {":flag_jp:", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5"}, {":flag_ke:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xaa"}, {":flag_kg:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xac"}, {":flag_kh:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xad"}, {":flag_ki:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xae"}, {":flag_km:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb2"}, {":flag_kn:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb3"}, {":flag_kp:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb5"}, {":flag_kr:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb7"}, {":flag_kw:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xbc"}, {":flag_ky:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xbe"}, {":flag_kz:", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xbf"}, {":flag_la:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xa6"}, {":flag_lb:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xa7"}, {":flag_lc:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xa8"}, {":flag_li:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xae"}, {":flag_lk:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb0"}, {":flag_lr:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb7"}, {":flag_ls:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb8"}, {":flag_lt:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb9"}, {":flag_lu:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xba"}, {":flag_lv:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xbb"}, {":flag_ly:", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xbe"}, {":flag_ma:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xa6"}, {":flag_mc:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xa8"}, {":flag_md:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xa9"}, {":flag_me:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xaa"}, {":flag_mf:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xab"}, {":flag_mg:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xac"}, {":flag_mh:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xad"}, {":flag_mk:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb0"}, {":flag_ml:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb1"}, {":flag_mm:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb2"}, {":flag_mn:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb3"}, {":flag_mo:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb4"}, {":flag_mp:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb5"}, {":flag_mq:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb6"}, {":flag_mr:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb7"}, {":flag_ms:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8"}, {":flag_mt:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb9"}, {":flag_mu:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xba"}, {":flag_mv:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbb"}, {":flag_mw:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbc"}, {":flag_mx:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbd"}, {":flag_my:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbe"}, {":flag_mz:", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbf"}, {":flag_na:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xa6"}, {":flag_nc:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xa8"}, {":flag_ne:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xaa"}, {":flag_nf:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xab"}, {":flag_ng:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xac"}, {":flag_ni:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xae"}, {":flag_nl:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb1"}, {":flag_no:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb4"}, {":flag_np:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb5"}, {":flag_nr:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb7"}, {":flag_nu:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xba"}, {":flag_nz:", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xbf"}, {":flag_om:", "\xf0\x9f\x87\xb4\xf0\x9f\x87\xb2"}, {":flag_pa:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xa6"}, {":flag_pe:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xaa"}, {":flag_pf:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xab"}, {":flag_pg:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xac"}, {":flag_ph:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xad"}, {":flag_pk:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb0"}, {":flag_pl:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb1"}, {":flag_pm:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb2"}, {":flag_pn:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb3"}, {":flag_pr:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb7"}, {":flag_ps:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb8"}, {":flag_pt:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb9"}, {":flag_pw:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xbc"}, {":flag_py:", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xbe"}, {":flag_qa:", "\xf0\x9f\x87\xb6\xf0\x9f\x87\xa6"}, {":flag_re:", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xaa"}, {":flag_ro:", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xb4"}, {":flag_rs:", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xb8"}, {":flag_ru:", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xba"}, {":flag_rw:", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xbc"}, {":flag_sa:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6"}, {":flag_sb:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa7"}, {":flag_sc:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa8"}, {":flag_sd:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa9"}, {":flag_se:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xaa"}, {":flag_sg:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xac"}, {":flag_sh:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xad"}, {":flag_si:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xae"}, {":flag_sj:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xaf"}, {":flag_sk:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb0"}, {":flag_sl:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb1"}, {":flag_sm:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb2"}, {":flag_sn:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb3"}, {":flag_so:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb4"}, {":flag_sr:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7"}, {":flag_ss:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb8"}, {":flag_st:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb9"}, {":flag_sv:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbb"}, {":flag_sx:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbd"}, {":flag_sy:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbe"}, {":flag_sz:", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbf"}, {":flag_ta:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xa6"}, {":flag_tc:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xa8"}, {":flag_td:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xa9"}, {":flag_tf:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xab"}, {":flag_tg:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xac"}, {":flag_th:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xad"}, {":flag_tj:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xaf"}, {":flag_tk:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb0"}, {":flag_tl:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb1"}, {":flag_tm:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb2"}, {":flag_tn:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb3"}, {":flag_to:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb4"}, {":flag_tr:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb7"}, {":flag_tt:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb9"}, {":flag_tv:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xbb"}, {":flag_tw:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xbc"}, {":flag_tz:", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xbf"}, {":flag_ua:", "\xf0\x9f\x87\xba\xf0\x9f\x87\xa6"}, {":flag_ug:", "\xf0\x9f\x87\xba\xf0\x9f\x87\xac"}, {":flag_um:", "\xf0\x9f\x87\xba\xf0\x9f\x87\xb2"}, {":flag_us:", "\xf0\x9f\x87\xba\xf0\x9f\x87\xb8"}, {":flag_uy:", "\xf0\x9f\x87\xba\xf0\x9f\x87\xbe"}, {":flag_uz:", "\xf0\x9f\x87\xba\xf0\x9f\x87\xbf"}, {":flag_va:", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xa6"}, {":flag_vc:", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xa8"}, {":flag_ve:", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xaa"}, {":flag_vg:", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xac"}, {":flag_vi:", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xae"}, {":flag_vn:", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xb3"}, {":flag_vu:", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xba"}, {":flag_wf:", "\xf0\x9f\x87\xbc\xf0\x9f\x87\xab"}, {":flag_ws:", "\xf0\x9f\x87\xbc\xf0\x9f\x87\xb8"}, {":flag_xk:", "\xf0\x9f\x87\xbd\xf0\x9f\x87\xb0"}, {":flag_ye:", "\xf0\x9f\x87\xbe\xf0\x9f\x87\xaa"}, {":flag_yt:", "\xf0\x9f\x87\xbe\xf0\x9f\x87\xb9"}, {":flag_za:", "\xf0\x9f\x87\xbf\xf0\x9f\x87\xa6"}, {":flag_zm:", "\xf0\x9f\x87\xbf\xf0\x9f\x87\xb2"}, {":flag_zw:", "\xf0\x9f\x87\xbf\xf0\x9f\x87\xbc"}}; + + const std::unordered_map<std::string, std::string> EmojiMapper::unicodeShortname = [](){ + std::unordered_map<std::string, std::string> unicodeSequenceToShortname; + const auto& shortnameToUnicodeMap = EmojiMapper::shortnameUnicode; + for (const auto& shortnameToUnicode : shortnameToUnicodeMap) { + unicodeSequenceToShortname[shortnameToUnicode.second] = shortnameToUnicode.first; + } + return unicodeSequenceToShortname; + }(); + + const std::unordered_map<std::string, std::vector<std::string>> EmojiMapper::emojisInCategory = std::unordered_map<std::string, std::vector<std::string>>{{"symbols", {"\xf0\x9f\x92\xaf", "\xf0\x9f\x94\xa2", "\xf0\x9f\x91\x81\xf0\x9f\x97\xa8", "\xf0\x9f\x92\x98", "\xe2\x9d\xa4", "\xf0\x9f\x92\x93", "\xf0\x9f\x92\x94", "\xf0\x9f\x92\x95", "\xf0\x9f\x92\x96", "\xf0\x9f\x92\x97", "\xf0\x9f\x92\x99", "\xf0\x9f\x92\x9a", "\xf0\x9f\x92\x9b", "\xf0\x9f\x92\x9c", "\xf0\x9f\x96\xa4", "\xf0\x9f\x92\x9d", "\xf0\x9f\x92\x9e", "\xf0\x9f\x92\x9f", "\xe2\x9d\xa3", "\xf0\x9f\x92\xa2", "\xf0\x9f\x92\xa5", "\xf0\x9f\x92\xab", "\xf0\x9f\x92\xac", "\xf0\x9f\x97\xa8", "\xf0\x9f\x97\xaf", "\xf0\x9f\x92\xad", "\xf0\x9f\x92\xae", "\xf0\x9f\x8c\x90", "\xe2\x99\xa8", "\xf0\x9f\x9b\x91", "\xf0\x9f\x95\x9b", "\xf0\x9f\x95\xa7", "\xf0\x9f\x95\x90", "\xf0\x9f\x95\x9c", "\xf0\x9f\x95\x91", "\xf0\x9f\x95\x9d", "\xf0\x9f\x95\x92", "\xf0\x9f\x95\x9e", "\xf0\x9f\x95\x93", "\xf0\x9f\x95\x9f", "\xf0\x9f\x95\x94", "\xf0\x9f\x95\xa0", "\xf0\x9f\x95\x95", "\xf0\x9f\x95\xa1", "\xf0\x9f\x95\x96", "\xf0\x9f\x95\xa2", "\xf0\x9f\x95\x97", "\xf0\x9f\x95\xa3", "\xf0\x9f\x95\x98", "\xf0\x9f\x95\xa4", "\xf0\x9f\x95\x99", "\xf0\x9f\x95\xa5", "\xf0\x9f\x95\x9a", "\xf0\x9f\x95\xa6", "\xf0\x9f\x8c\x80", "\xe2\x99\xa0", "\xe2\x99\xa5", "\xe2\x99\xa6", "\xe2\x99\xa3", "\xf0\x9f\x83\x8f", "\xf0\x9f\x80\x84", "\xf0\x9f\x8e\xb4", "\xf0\x9f\x94\x87", "\xf0\x9f\x94\x88", "\xf0\x9f\x94\x89", "\xf0\x9f\x94\x8a", "\xf0\x9f\x93\xa2", "\xf0\x9f\x93\xa3", "\xf0\x9f\x94\x94", "\xf0\x9f\x94\x95", "\xf0\x9f\x8e\xb5", "\xf0\x9f\x8e\xb6", "\xf0\x9f\x92\xb9", "\xf0\x9f\x92\xb1", "\xf0\x9f\x92\xb2", "\xf0\x9f\x8f\xa7", "\xf0\x9f\x9a\xae", "\xf0\x9f\x9a\xb0", "\xe2\x99\xbf", "\xf0\x9f\x9a\xb9", "\xf0\x9f\x9a\xba", "\xf0\x9f\x9a\xbb", "\xf0\x9f\x9a\xbc", "\xf0\x9f\x9a\xbe", "\xf0\x9f\x9b\x82", "\xf0\x9f\x9b\x83", "\xf0\x9f\x9b\x84", "\xf0\x9f\x9b\x85", "\xe2\x9a\xa0", "\xf0\x9f\x9a\xb8", "\xe2\x9b\x94", "\xf0\x9f\x9a\xab", "\xf0\x9f\x9a\xb3", "\xf0\x9f\x9a\xad", "\xf0\x9f\x9a\xaf", "\xf0\x9f\x9a\xb1", "\xf0\x9f\x9a\xb7", "\xf0\x9f\x93\xb5", "\xf0\x9f\x94\x9e", "\xe2\x98\xa2", "\xe2\x98\xa3", "\xe2\xac\x86", "\xe2\x86\x97", "\xe2\x9e\xa1", "\xe2\x86\x98", "\xe2\xac\x87", "\xe2\x86\x99", "\xe2\xac\x85", "\xe2\x86\x96", "\xe2\x86\x95", "\xe2\x86\x94", "\xe2\x86\xa9", "\xe2\x86\xaa", "\xe2\xa4\xb4", "\xe2\xa4\xb5", "\xf0\x9f\x94\x83", "\xf0\x9f\x94\x84", "\xf0\x9f\x94\x99", "\xf0\x9f\x94\x9a", "\xf0\x9f\x94\x9b", "\xf0\x9f\x94\x9c", "\xf0\x9f\x94\x9d", "\xf0\x9f\x9b\x90", "\xe2\x9a\x9b", "\xf0\x9f\x95\x89", "\xe2\x9c\xa1", "\xe2\x98\xb8", "\xe2\x98\xaf", "\xe2\x9c\x9d", "\xe2\x98\xa6", "\xe2\x98\xaa", "\xe2\x98\xae", "\xf0\x9f\x95\x8e", "\xf0\x9f\x94\xaf", "\xe2\x99\x88", "\xe2\x99\x89", "\xe2\x99\x8a", "\xe2\x99\x8b", "\xe2\x99\x8c", "\xe2\x99\x8d", "\xe2\x99\x8e", "\xe2\x99\x8f", "\xe2\x99\x90", "\xe2\x99\x91", "\xe2\x99\x92", "\xe2\x99\x93", "\xe2\x9b\x8e", "\xf0\x9f\x94\x80", "\xf0\x9f\x94\x81", "\xf0\x9f\x94\x82", "\xe2\x96\xb6", "\xe2\x8f\xa9", "\xe2\x8f\xad", "\xe2\x8f\xaf", "\xe2\x97\x80", "\xe2\x8f\xaa", "\xe2\x8f\xae", "\xf0\x9f\x94\xbc", "\xe2\x8f\xab", "\xf0\x9f\x94\xbd", "\xe2\x8f\xac", "\xe2\x8f\xb8", "\xe2\x8f\xb9", "\xe2\x8f\xba", "\xe2\x8f\x8f", "\xf0\x9f\x8e\xa6", "\xf0\x9f\x94\x85", "\xf0\x9f\x94\x86", "\xf0\x9f\x93\xb6", "\xf0\x9f\x93\xb3", "\xf0\x9f\x93\xb4", "\xe2\x99\xbb", "\xf0\x9f\x93\x9b", "\xe2\x9a\x9c", "\xf0\x9f\x94\xb0", "\xf0\x9f\x94\xb1", "\xe2\xad\x95", "\xe2\x9c\x85", "\xe2\x98\x91", "\xe2\x9c\x94", "\xe2\x9c\x96", "\xe2\x9d\x8c", "\xe2\x9d\x8e", "\xe2\x9e\x95", "\xe2\x9e\x96", "\xe2\x9e\x97", "\xe2\x9e\xb0", "\xe2\x9e\xbf", "\xe3\x80\xbd", "\xe2\x9c\xb3", "\xe2\x9c\xb4", "\xe2\x9d\x87", "\xe2\x80\xbc", "\xe2\x81\x89", "\xe2\x9d\x93", "\xe2\x9d\x94", "\xe2\x9d\x95", "\xe2\x9d\x97", "\xe3\x80\xb0", "\xc2\xa9", "\xc2\xae", "\xe2\x84\xa2", "\x23\xe2\x83\xa3", "\x2a\xe2\x83\xa3", "\x30\xe2\x83\xa3", "\x31\xe2\x83\xa3", "\x32\xe2\x83\xa3", "\x33\xe2\x83\xa3", "\x34\xe2\x83\xa3", "\x35\xe2\x83\xa3", "\x36\xe2\x83\xa3", "\x37\xe2\x83\xa3", "\x38\xe2\x83\xa3", "\x39\xe2\x83\xa3", "\xf0\x9f\x94\x9f", "\xf0\x9f\x94\xa0", "\xf0\x9f\x94\xa1", "\xf0\x9f\x94\xa3", "\xf0\x9f\x94\xa4", "\xf0\x9f\x85\xb0", "\xf0\x9f\x86\x8e", "\xf0\x9f\x85\xb1", "\xf0\x9f\x86\x91", "\xf0\x9f\x86\x92", "\xf0\x9f\x86\x93", "\xe2\x84\xb9", "\xf0\x9f\x86\x94", "\xe2\x93\x82", "\xf0\x9f\x86\x95", "\xf0\x9f\x86\x96", "\xf0\x9f\x85\xbe", "\xf0\x9f\x86\x97", "\xf0\x9f\x85\xbf", "\xf0\x9f\x86\x98", "\xf0\x9f\x86\x99", "\xf0\x9f\x86\x9a", "\xf0\x9f\x88\x81", "\xf0\x9f\x88\x82", "\xf0\x9f\x88\xb7", "\xf0\x9f\x88\xb6", "\xf0\x9f\x88\xaf", "\xf0\x9f\x89\x90", "\xf0\x9f\x88\xb9", "\xf0\x9f\x88\x9a", "\xf0\x9f\x88\xb2", "\xf0\x9f\x89\x91", "\xf0\x9f\x88\xb8", "\xf0\x9f\x88\xb4", "\xf0\x9f\x88\xb3", "\xe3\x8a\x97", "\xe3\x8a\x99", "\xf0\x9f\x88\xba", "\xf0\x9f\x88\xb5", "\xe2\x96\xaa", "\xe2\x96\xab", "\xe2\x97\xbb", "\xe2\x97\xbc", "\xe2\x97\xbd", "\xe2\x97\xbe", "\xe2\xac\x9b", "\xe2\xac\x9c", "\xf0\x9f\x94\xb6", "\xf0\x9f\x94\xb7", "\xf0\x9f\x94\xb8", "\xf0\x9f\x94\xb9", "\xf0\x9f\x94\xba", "\xf0\x9f\x94\xbb", "\xf0\x9f\x92\xa0", "\xf0\x9f\x94\x98", "\xf0\x9f\x94\xb2", "\xf0\x9f\x94\xb3", "\xe2\x9a\xaa", "\xe2\x9a\xab", "\xf0\x9f\x94\xb4", "\xf0\x9f\x94\xb5"}}, {"people", {"\xf0\x9f\x98\x80", "\xf0\x9f\x98\x81", "\xf0\x9f\x98\x82", "\xf0\x9f\xa4\xa3", "\xf0\x9f\x98\x83", "\xf0\x9f\x98\x84", "\xf0\x9f\x98\x85", "\xf0\x9f\x98\x86", "\xf0\x9f\x98\x89", "\xf0\x9f\x98\x8a", "\xf0\x9f\x98\x8b", "\xf0\x9f\x98\x8e", "\xf0\x9f\x98\x8d", "\xf0\x9f\x98\x98", "\xf0\x9f\x98\x97", "\xf0\x9f\x98\x99", "\xf0\x9f\x98\x9a", "\xe2\x98\xba", "\xf0\x9f\x99\x82", "\xf0\x9f\xa4\x97", "\xf0\x9f\xa4\x94", "\xf0\x9f\x98\x90", "\xf0\x9f\x98\x91", "\xf0\x9f\x98\xb6", "\xf0\x9f\x99\x84", "\xf0\x9f\x98\x8f", "\xf0\x9f\x98\xa3", "\xf0\x9f\x98\xa5", "\xf0\x9f\x98\xae", "\xf0\x9f\xa4\x90", "\xf0\x9f\x98\xaf", "\xf0\x9f\x98\xaa", "\xf0\x9f\x98\xab", "\xf0\x9f\x98\xb4", "\xf0\x9f\x98\x8c", "\xf0\x9f\xa4\x93", "\xf0\x9f\x98\x9b", "\xf0\x9f\x98\x9c", "\xf0\x9f\x98\x9d", "\xf0\x9f\xa4\xa4", "\xf0\x9f\x98\x92", "\xf0\x9f\x98\x93", "\xf0\x9f\x98\x94", "\xf0\x9f\x98\x95", "\xf0\x9f\x99\x83", "\xf0\x9f\xa4\x91", "\xf0\x9f\x98\xb2", "\xe2\x98\xb9", "\xf0\x9f\x99\x81", "\xf0\x9f\x98\x96", "\xf0\x9f\x98\x9e", "\xf0\x9f\x98\x9f", "\xf0\x9f\x98\xa4", "\xf0\x9f\x98\xa2", "\xf0\x9f\x98\xad", "\xf0\x9f\x98\xa6", "\xf0\x9f\x98\xa7", "\xf0\x9f\x98\xa8", "\xf0\x9f\x98\xa9", "\xf0\x9f\x98\xac", "\xf0\x9f\x98\xb0", "\xf0\x9f\x98\xb1", "\xf0\x9f\x98\xb3", "\xf0\x9f\x98\xb5", "\xf0\x9f\x98\xa1", "\xf0\x9f\x98\xa0", "\xf0\x9f\x98\x87", "\xf0\x9f\xa4\xa0", "\xf0\x9f\xa4\xa1", "\xf0\x9f\xa4\xa5", "\xf0\x9f\x98\xb7", "\xf0\x9f\xa4\x92", "\xf0\x9f\xa4\x95", "\xf0\x9f\xa4\xa2", "\xf0\x9f\xa4\xa7", "\xf0\x9f\x98\x88", "\xf0\x9f\x91\xbf", "\xf0\x9f\x91\xb9", "\xf0\x9f\x91\xba", "\xf0\x9f\x92\x80", "\xf0\x9f\x91\xbb", "\xf0\x9f\x91\xbd", "\xf0\x9f\xa4\x96", "\xf0\x9f\x92\xa9", "\xf0\x9f\x98\xba", "\xf0\x9f\x98\xb8", "\xf0\x9f\x98\xb9", "\xf0\x9f\x98\xbb", "\xf0\x9f\x98\xbc", "\xf0\x9f\x98\xbd", "\xf0\x9f\x99\x80", "\xf0\x9f\x98\xbf", "\xf0\x9f\x98\xbe", "\xf0\x9f\x91\xa6", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xa6\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xa7", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xa7\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xa8", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xa9", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb4", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb4\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb5", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb5\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb6", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb6\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xbc", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xbc\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xae", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xae\xf0\x9f\x8f\xbf", "\xf0\x9f\x95\xb5", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbb", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbc", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbd", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbe", "\xf0\x9f\x95\xb5\xf0\x9f\x8f\xbf", "\xf0\x9f\x92\x82", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbb", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbc", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbd", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbe", "\xf0\x9f\x92\x82\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb7", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb7\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb3", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb3\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb1", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb1\xf0\x9f\x8f\xbf", "\xf0\x9f\x8e\x85", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbb", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbc", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbd", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbe", "\xf0\x9f\x8e\x85\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb6", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb6\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb8", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb8\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb4", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb4\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb0", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb0\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb5", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb5\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb0", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb0\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xb2", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\xb2\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x8d", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x8d\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x8e", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x8e\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x85", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x85\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x86", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x86\xf0\x9f\x8f\xbf", "\xf0\x9f\x92\x81", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbb", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbc", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbd", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbe", "\xf0\x9f\x92\x81\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x8b", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x8b\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x87", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x87\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xa6", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xa6\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb7", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb7\xf0\x9f\x8f\xbf", "\xf0\x9f\x92\x86", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbb", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbc", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbd", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbe", "\xf0\x9f\x92\x86\xf0\x9f\x8f\xbf", "\xf0\x9f\x92\x87", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbb", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbc", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbd", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbe", "\xf0\x9f\x92\x87\xf0\x9f\x8f\xbf", "\xf0\x9f\x9a\xb6", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbb", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbc", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbd", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbe", "\xf0\x9f\x9a\xb6\xf0\x9f\x8f\xbf", "\xf0\x9f\x8f\x83", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbb", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbc", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbd", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbe", "\xf0\x9f\x8f\x83\xf0\x9f\x8f\xbf", "\xf0\x9f\x92\x83", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbb", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbc", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbd", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbe", "\xf0\x9f\x92\x83\xf0\x9f\x8f\xbf", "\xf0\x9f\x95\xba", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbb", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbc", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbd", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbe", "\xf0\x9f\x95\xba\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xaf", "\xf0\x9f\x97\xa3", "\xf0\x9f\x91\xa4", "\xf0\x9f\x91\xa5", "\xf0\x9f\x91\xab", "\xf0\x9f\x91\xac", "\xf0\x9f\x91\xad", "\xf0\x9f\x92\x8f", "\xf0\x9f\x92\x91", "\xf0\x9f\x91\xaa", "\xf0\x9f\x92\xaa", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbb", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbc", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbd", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbe", "\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb3", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb3\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x88", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x88\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x89", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x89\xf0\x9f\x8f\xbf", "\xe2\x98\x9d", "\xe2\x98\x9d\xf0\x9f\x8f\xbb", "\xe2\x98\x9d\xf0\x9f\x8f\xbc", "\xe2\x98\x9d\xf0\x9f\x8f\xbd", "\xe2\x98\x9d\xf0\x9f\x8f\xbe", "\xe2\x98\x9d\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x86", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x86\xf0\x9f\x8f\xbf", "\xf0\x9f\x96\x95", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbb", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbc", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbd", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbe", "\xf0\x9f\x96\x95\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x87", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x87\xf0\x9f\x8f\xbf", "\xe2\x9c\x8c", "\xe2\x9c\x8c\xf0\x9f\x8f\xbb", "\xe2\x9c\x8c\xf0\x9f\x8f\xbc", "\xe2\x9c\x8c\xf0\x9f\x8f\xbd", "\xe2\x9c\x8c\xf0\x9f\x8f\xbe", "\xe2\x9c\x8c\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\x9e", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\x9e\xf0\x9f\x8f\xbf", "\xf0\x9f\x96\x96", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbb", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbc", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbd", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbe", "\xf0\x9f\x96\x96\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\x98", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\x98\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\x99", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\x99\xf0\x9f\x8f\xbf", "\xf0\x9f\x96\x90", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbb", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbc", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbd", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbe", "\xf0\x9f\x96\x90\xf0\x9f\x8f\xbf", "\xe2\x9c\x8b", "\xe2\x9c\x8b\xf0\x9f\x8f\xbb", "\xe2\x9c\x8b\xf0\x9f\x8f\xbc", "\xe2\x9c\x8b\xf0\x9f\x8f\xbd", "\xe2\x9c\x8b\xf0\x9f\x8f\xbe", "\xe2\x9c\x8b\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x8c", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x8c\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x8d", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x8d\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x8e", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x8e\xf0\x9f\x8f\xbf", "\xe2\x9c\x8a", "\xe2\x9c\x8a\xf0\x9f\x8f\xbb", "\xe2\x9c\x8a\xf0\x9f\x8f\xbc", "\xe2\x9c\x8a\xf0\x9f\x8f\xbd", "\xe2\x9c\x8a\xf0\x9f\x8f\xbe", "\xe2\x9c\x8a\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x8a", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x8a\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\x9b", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\x9b\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\x9c", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\x9c\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\x9a", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\x9a\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x8b", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x8b\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x8f", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbf", "\xe2\x9c\x8d", "\xe2\x9c\x8d\xf0\x9f\x8f\xbb", "\xe2\x9c\x8d\xf0\x9f\x8f\xbc", "\xe2\x9c\x8d\xf0\x9f\x8f\xbd", "\xe2\x9c\x8d\xf0\x9f\x8f\xbe", "\xe2\x9c\x8d\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x90", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x90\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x8c", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbf", "\xf0\x9f\x99\x8f", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbb", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbc", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbd", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbe", "\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\x9d", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\x9d\xf0\x9f\x8f\xbf", "\xf0\x9f\x92\x85", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbb", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbc", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbd", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbe", "\xf0\x9f\x92\x85\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x82", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x82\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\x83", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbb", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbc", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbd", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbe", "\xf0\x9f\x91\x83\xf0\x9f\x8f\xbf", "\xf0\x9f\x91\xa3", "\xf0\x9f\x91\x80", "\xf0\x9f\x91\x81", "\xf0\x9f\x91\x85", "\xf0\x9f\x91\x84", "\xf0\x9f\x92\x8b", "\xf0\x9f\x92\xa4", "\xf0\x9f\x91\x93", "\xf0\x9f\x95\xb6", "\xf0\x9f\x91\x94", "\xf0\x9f\x91\x95", "\xf0\x9f\x91\x96", "\xf0\x9f\x91\x97", "\xf0\x9f\x91\x98", "\xf0\x9f\x91\x99", "\xf0\x9f\x91\x9a", "\xf0\x9f\x91\x9b", "\xf0\x9f\x91\x9c", "\xf0\x9f\x91\x9d", "\xf0\x9f\x8e\x92", "\xf0\x9f\x91\x9e", "\xf0\x9f\x91\x9f", "\xf0\x9f\x91\xa0", "\xf0\x9f\x91\xa1", "\xf0\x9f\x91\xa2", "\xf0\x9f\x91\x91", "\xf0\x9f\x91\x92", "\xf0\x9f\x8e\xa9", "\xf0\x9f\x8e\x93", "\xe2\x9b\x91", "\xf0\x9f\x92\x84", "\xf0\x9f\x92\x8d", "\xf0\x9f\x8c\x82", "\xf0\x9f\x92\xbc"}}, {"objects", {"\xe2\x98\xa0", "\xf0\x9f\x92\x8c", "\xf0\x9f\x92\xa3", "\xf0\x9f\x95\xb3", "\xf0\x9f\x9b\x8d", "\xf0\x9f\x93\xbf", "\xf0\x9f\x92\x8e", "\xf0\x9f\x94\xaa", "\xf0\x9f\x8f\xba", "\xf0\x9f\x97\xba", "\xf0\x9f\x92\x88", "\xf0\x9f\x96\xbc", "\xf0\x9f\x9b\x8e", "\xf0\x9f\x9a\xaa", "\xf0\x9f\x9b\x8c", "\xf0\x9f\x9b\x8f", "\xf0\x9f\x9b\x8b", "\xf0\x9f\x9a\xbd", "\xf0\x9f\x9a\xbf", "\xf0\x9f\x9b\x81", "\xe2\x8c\x9b", "\xe2\x8f\xb3", "\xe2\x8c\x9a", "\xe2\x8f\xb0", "\xe2\x8f\xb1", "\xe2\x8f\xb2", "\xf0\x9f\x95\xb0", "\xf0\x9f\x8c\xa1", "\xe2\x9b\xb1", "\xf0\x9f\x8e\x88", "\xf0\x9f\x8e\x89", "\xf0\x9f\x8e\x8a", "\xf0\x9f\x8e\x8e", "\xf0\x9f\x8e\x8f", "\xf0\x9f\x8e\x90", "\xf0\x9f\x8e\x80", "\xf0\x9f\x8e\x81", "\xf0\x9f\x95\xb9", "\xf0\x9f\x93\xaf", "\xf0\x9f\x8e\x99", "\xf0\x9f\x8e\x9a", "\xf0\x9f\x8e\x9b", "\xf0\x9f\x93\xbb", "\xf0\x9f\x93\xb1", "\xf0\x9f\x93\xb2", "\xe2\x98\x8e", "\xf0\x9f\x93\x9e", "\xf0\x9f\x93\x9f", "\xf0\x9f\x93\xa0", "\xf0\x9f\x94\x8b", "\xf0\x9f\x94\x8c", "\xf0\x9f\x92\xbb", "\xf0\x9f\x96\xa5", "\xf0\x9f\x96\xa8", "\xe2\x8c\xa8", "\xf0\x9f\x96\xb1", "\xf0\x9f\x96\xb2", "\xf0\x9f\x92\xbd", "\xf0\x9f\x92\xbe", "\xf0\x9f\x92\xbf", "\xf0\x9f\x93\x80", "\xf0\x9f\x8e\xa5", "\xf0\x9f\x8e\x9e", "\xf0\x9f\x93\xbd", "\xf0\x9f\x93\xba", "\xf0\x9f\x93\xb7", "\xf0\x9f\x93\xb8", "\xf0\x9f\x93\xb9", "\xf0\x9f\x93\xbc", "\xf0\x9f\x94\x8d", "\xf0\x9f\x94\x8e", "\xf0\x9f\x94\xac", "\xf0\x9f\x94\xad", "\xf0\x9f\x93\xa1", "\xf0\x9f\x95\xaf", "\xf0\x9f\x92\xa1", "\xf0\x9f\x94\xa6", "\xf0\x9f\x8f\xae", "\xf0\x9f\x93\x94", "\xf0\x9f\x93\x95", "\xf0\x9f\x93\x96", "\xf0\x9f\x93\x97", "\xf0\x9f\x93\x98", "\xf0\x9f\x93\x99", "\xf0\x9f\x93\x9a", "\xf0\x9f\x93\x93", "\xf0\x9f\x93\x92", "\xf0\x9f\x93\x83", "\xf0\x9f\x93\x9c", "\xf0\x9f\x93\x84", "\xf0\x9f\x93\xb0", "\xf0\x9f\x97\x9e", "\xf0\x9f\x93\x91", "\xf0\x9f\x94\x96", "\xf0\x9f\x8f\xb7", "\xf0\x9f\x92\xb0", "\xf0\x9f\x92\xb4", "\xf0\x9f\x92\xb5", "\xf0\x9f\x92\xb6", "\xf0\x9f\x92\xb7", "\xf0\x9f\x92\xb8", "\xf0\x9f\x92\xb3", "\xe2\x9c\x89", "\xf0\x9f\x93\xa7", "\xf0\x9f\x93\xa8", "\xf0\x9f\x93\xa9", "\xf0\x9f\x93\xa4", "\xf0\x9f\x93\xa5", "\xf0\x9f\x93\xa6", "\xf0\x9f\x93\xab", "\xf0\x9f\x93\xaa", "\xf0\x9f\x93\xac", "\xf0\x9f\x93\xad", "\xf0\x9f\x93\xae", "\xf0\x9f\x97\xb3", "\xe2\x9c\x8f", "\xe2\x9c\x92", "\xf0\x9f\x96\x8b", "\xf0\x9f\x96\x8a", "\xf0\x9f\x96\x8c", "\xf0\x9f\x96\x8d", "\xf0\x9f\x93\x9d", "\xf0\x9f\x93\x81", "\xf0\x9f\x93\x82", "\xf0\x9f\x97\x82", "\xf0\x9f\x93\x85", "\xf0\x9f\x93\x86", "\xf0\x9f\x97\x92", "\xf0\x9f\x97\x93", "\xf0\x9f\x93\x87", "\xf0\x9f\x93\x88", "\xf0\x9f\x93\x89", "\xf0\x9f\x93\x8a", "\xf0\x9f\x93\x8b", "\xf0\x9f\x93\x8c", "\xf0\x9f\x93\x8d", "\xf0\x9f\x93\x8e", "\xf0\x9f\x96\x87", "\xf0\x9f\x93\x8f", "\xf0\x9f\x93\x90", "\xe2\x9c\x82", "\xf0\x9f\x97\x83", "\xf0\x9f\x97\x84", "\xf0\x9f\x97\x91", "\xf0\x9f\x94\x92", "\xf0\x9f\x94\x93", "\xf0\x9f\x94\x8f", "\xf0\x9f\x94\x90", "\xf0\x9f\x94\x91", "\xf0\x9f\x97\x9d", "\xf0\x9f\x94\xa8", "\xe2\x9b\x8f", "\xe2\x9a\x92", "\xf0\x9f\x9b\xa0", "\xf0\x9f\x97\xa1", "\xe2\x9a\x94", "\xf0\x9f\x94\xab", "\xf0\x9f\x9b\xa1", "\xf0\x9f\x94\xa7", "\xf0\x9f\x94\xa9", "\xe2\x9a\x99", "\xf0\x9f\x97\x9c", "\xe2\x9a\x97", "\xe2\x9a\x96", "\xf0\x9f\x94\x97", "\xe2\x9b\x93", "\xf0\x9f\x92\x89", "\xf0\x9f\x92\x8a", "\xf0\x9f\x9a\xac", "\xe2\x9a\xb0", "\xe2\x9a\xb1", "\xf0\x9f\x97\xbf", "\xf0\x9f\x9b\xa2", "\xf0\x9f\x94\xae", "\xf0\x9f\x9b\x92", "\xf0\x9f\x9a\xa9", "\xf0\x9f\x8e\x8c", "\xf0\x9f\x8f\xb4", "\xf0\x9f\x8f\xb3", "\xf0\x9f\x8f\xb3\xf0\x9f\x8c\x88"}}, {"activity", {"\xf0\x9f\x91\xbe", "\xf0\x9f\x95\xb4", "\xf0\x9f\xa4\xba", "\xf0\x9f\x8f\x87", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbb", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbc", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbd", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbe", "\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbf", "\xe2\x9b\xb7", "\xf0\x9f\x8f\x82", "\xf0\x9f\x8f\x8c", "\xf0\x9f\x8f\x84", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbb", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbc", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbd", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbe", "\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbf", "\xf0\x9f\x9a\xa3", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbb", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbc", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbd", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbe", "\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbf", "\xf0\x9f\x8f\x8a", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbb", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbc", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbd", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbe", "\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbf", "\xe2\x9b\xb9", "\xe2\x9b\xb9\xf0\x9f\x8f\xbb", "\xe2\x9b\xb9\xf0\x9f\x8f\xbc", "\xe2\x9b\xb9\xf0\x9f\x8f\xbd", "\xe2\x9b\xb9\xf0\x9f\x8f\xbe", "\xe2\x9b\xb9\xf0\x9f\x8f\xbf", "\xf0\x9f\x8f\x8b", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbb", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbc", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbd", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbe", "\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbf", "\xf0\x9f\x9a\xb4", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbb", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbc", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbd", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbe", "\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbf", "\xf0\x9f\x9a\xb5", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbb", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbc", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbd", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbe", "\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb8", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xbc", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xbd", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xbe", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbf", "\xf0\x9f\xa4\xb9", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbb", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbc", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbd", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbe", "\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbf", "\xf0\x9f\x8e\xaa", "\xf0\x9f\x8e\xad", "\xf0\x9f\x8e\xa8", "\xf0\x9f\x8e\xb0", "\xf0\x9f\x9b\x80", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbb", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbc", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbd", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbe", "\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbf", "\xf0\x9f\x8e\x97", "\xf0\x9f\x8e\x9f", "\xf0\x9f\x8e\xab", "\xf0\x9f\x8e\x96", "\xf0\x9f\x8f\x86", "\xf0\x9f\x8f\x85", "\xf0\x9f\xa5\x87", "\xf0\x9f\xa5\x88", "\xf0\x9f\xa5\x89", "\xe2\x9a\xbd", "\xe2\x9a\xbe", "\xf0\x9f\x8f\x80", "\xf0\x9f\x8f\x90", "\xf0\x9f\x8f\x88", "\xf0\x9f\x8f\x89", "\xf0\x9f\x8e\xbe", "\xf0\x9f\x8e\xb1", "\xf0\x9f\x8e\xb3", "\xf0\x9f\x8f\x8f", "\xf0\x9f\x8f\x91", "\xf0\x9f\x8f\x92", "\xf0\x9f\x8f\x93", "\xf0\x9f\x8f\xb8", "\xf0\x9f\xa5\x8a", "\xf0\x9f\xa5\x8b", "\xf0\x9f\xa5\x85", "\xf0\x9f\x8e\xaf", "\xe2\x9b\xb3", "\xe2\x9b\xb8", "\xf0\x9f\x8e\xa3", "\xf0\x9f\x8e\xbd", "\xf0\x9f\x8e\xbf", "\xf0\x9f\x8e\xae", "\xf0\x9f\x8e\xb2", "\xf0\x9f\x8e\xbc", "\xf0\x9f\x8e\xa4", "\xf0\x9f\x8e\xa7", "\xf0\x9f\x8e\xb7", "\xf0\x9f\x8e\xb8", "\xf0\x9f\x8e\xb9", "\xf0\x9f\x8e\xba", "\xf0\x9f\x8e\xbb", "\xf0\x9f\xa5\x81", "\xf0\x9f\x8e\xac", "\xf0\x9f\x8f\xb9"}}, {"nature", {"\xf0\x9f\x99\x88", "\xf0\x9f\x99\x89", "\xf0\x9f\x99\x8a", "\xf0\x9f\x92\xa6", "\xf0\x9f\x92\xa8", "\xf0\x9f\x90\xb5", "\xf0\x9f\x90\x92", "\xf0\x9f\xa6\x8d", "\xf0\x9f\x90\xb6", "\xf0\x9f\x90\x95", "\xf0\x9f\x90\xa9", "\xf0\x9f\x90\xba", "\xf0\x9f\xa6\x8a", "\xf0\x9f\x90\xb1", "\xf0\x9f\x90\x88", "\xf0\x9f\xa6\x81", "\xf0\x9f\x90\xaf", "\xf0\x9f\x90\x85", "\xf0\x9f\x90\x86", "\xf0\x9f\x90\xb4", "\xf0\x9f\x90\x8e", "\xf0\x9f\xa6\x8c", "\xf0\x9f\xa6\x84", "\xf0\x9f\x90\xae", "\xf0\x9f\x90\x82", "\xf0\x9f\x90\x83", "\xf0\x9f\x90\x84", "\xf0\x9f\x90\xb7", "\xf0\x9f\x90\x96", "\xf0\x9f\x90\x97", "\xf0\x9f\x90\xbd", "\xf0\x9f\x90\x8f", "\xf0\x9f\x90\x91", "\xf0\x9f\x90\x90", "\xf0\x9f\x90\xaa", "\xf0\x9f\x90\xab", "\xf0\x9f\x90\x98", "\xf0\x9f\xa6\x8f", "\xf0\x9f\x90\xad", "\xf0\x9f\x90\x81", "\xf0\x9f\x90\x80", "\xf0\x9f\x90\xb9", "\xf0\x9f\x90\xb0", "\xf0\x9f\x90\x87", "\xf0\x9f\x90\xbf", "\xf0\x9f\xa6\x87", "\xf0\x9f\x90\xbb", "\xf0\x9f\x90\xa8", "\xf0\x9f\x90\xbc", "\xf0\x9f\x90\xbe", "\xf0\x9f\xa6\x83", "\xf0\x9f\x90\x94", "\xf0\x9f\x90\x93", "\xf0\x9f\x90\xa3", "\xf0\x9f\x90\xa4", "\xf0\x9f\x90\xa5", "\xf0\x9f\x90\xa6", "\xf0\x9f\x90\xa7", "\xf0\x9f\x95\x8a", "\xf0\x9f\xa6\x85", "\xf0\x9f\xa6\x86", "\xf0\x9f\xa6\x89", "\xf0\x9f\x90\xb8", "\xf0\x9f\x90\x8a", "\xf0\x9f\x90\xa2", "\xf0\x9f\xa6\x8e", "\xf0\x9f\x90\x8d", "\xf0\x9f\x90\xb2", "\xf0\x9f\x90\x89", "\xf0\x9f\x90\xb3", "\xf0\x9f\x90\x8b", "\xf0\x9f\x90\xac", "\xf0\x9f\x90\x9f", "\xf0\x9f\x90\xa0", "\xf0\x9f\x90\xa1", "\xf0\x9f\xa6\x88", "\xf0\x9f\x90\x99", "\xf0\x9f\x90\x9a", "\xf0\x9f\xa6\x80", "\xf0\x9f\xa6\x90", "\xf0\x9f\xa6\x91", "\xf0\x9f\xa6\x8b", "\xf0\x9f\x90\x8c", "\xf0\x9f\x90\x9b", "\xf0\x9f\x90\x9c", "\xf0\x9f\x90\x9d", "\xf0\x9f\x90\x9e", "\xf0\x9f\x95\xb7", "\xf0\x9f\x95\xb8", "\xf0\x9f\xa6\x82", "\xf0\x9f\x92\x90", "\xf0\x9f\x8c\xb8", "\xf0\x9f\x8f\xb5", "\xf0\x9f\x8c\xb9", "\xf0\x9f\xa5\x80", "\xf0\x9f\x8c\xba", "\xf0\x9f\x8c\xbb", "\xf0\x9f\x8c\xbc", "\xf0\x9f\x8c\xb7", "\xf0\x9f\x8c\xb1", "\xf0\x9f\x8c\xb2", "\xf0\x9f\x8c\xb3", "\xf0\x9f\x8c\xb4", "\xf0\x9f\x8c\xb5", "\xf0\x9f\x8c\xbe", "\xf0\x9f\x8c\xbf", "\xe2\x98\x98", "\xf0\x9f\x8d\x80", "\xf0\x9f\x8d\x81", "\xf0\x9f\x8d\x82", "\xf0\x9f\x8d\x83", "\xf0\x9f\x8d\x84", "\xf0\x9f\x8c\xb0", "\xf0\x9f\x8c\x8d", "\xf0\x9f\x8c\x8e", "\xf0\x9f\x8c\x8f", "\xf0\x9f\x8c\x91", "\xf0\x9f\x8c\x92", "\xf0\x9f\x8c\x93", "\xf0\x9f\x8c\x94", "\xf0\x9f\x8c\x95", "\xf0\x9f\x8c\x96", "\xf0\x9f\x8c\x97", "\xf0\x9f\x8c\x98", "\xf0\x9f\x8c\x99", "\xf0\x9f\x8c\x9a", "\xf0\x9f\x8c\x9b", "\xf0\x9f\x8c\x9c", "\xe2\x98\x80", "\xf0\x9f\x8c\x9d", "\xf0\x9f\x8c\x9e", "\xe2\xad\x90", "\xf0\x9f\x8c\x9f", "\xe2\x98\x81", "\xe2\x9b\x85", "\xe2\x9b\x88", "\xf0\x9f\x8c\xa4", "\xf0\x9f\x8c\xa5", "\xf0\x9f\x8c\xa6", "\xf0\x9f\x8c\xa7", "\xf0\x9f\x8c\xa8", "\xf0\x9f\x8c\xa9", "\xf0\x9f\x8c\xaa", "\xf0\x9f\x8c\xab", "\xf0\x9f\x8c\xac", "\xe2\x98\x82", "\xe2\x98\x94", "\xe2\x9a\xa1", "\xe2\x9d\x84", "\xe2\x98\x83", "\xe2\x9b\x84", "\xe2\x98\x84", "\xf0\x9f\x94\xa5", "\xf0\x9f\x92\xa7", "\xf0\x9f\x8c\x8a", "\xf0\x9f\x8e\x83", "\xf0\x9f\x8e\x84", "\xe2\x9c\xa8", "\xf0\x9f\x8e\x8b", "\xf0\x9f\x8e\x8d"}}, {"travel", {"\xf0\x9f\x8f\x8e", "\xf0\x9f\x8f\x8d", "\xf0\x9f\x97\xbe", "\xf0\x9f\x8f\x94", "\xe2\x9b\xb0", "\xf0\x9f\x8c\x8b", "\xf0\x9f\x97\xbb", "\xf0\x9f\x8f\x95", "\xf0\x9f\x8f\x96", "\xf0\x9f\x8f\x9c", "\xf0\x9f\x8f\x9d", "\xf0\x9f\x8f\x9e", "\xf0\x9f\x8f\x9f", "\xf0\x9f\x8f\x9b", "\xf0\x9f\x8f\x97", "\xf0\x9f\x8f\x98", "\xf0\x9f\x8f\x99", "\xf0\x9f\x8f\x9a", "\xf0\x9f\x8f\xa0", "\xf0\x9f\x8f\xa1", "\xf0\x9f\x8f\xa2", "\xf0\x9f\x8f\xa3", "\xf0\x9f\x8f\xa4", "\xf0\x9f\x8f\xa5", "\xf0\x9f\x8f\xa6", "\xf0\x9f\x8f\xa8", "\xf0\x9f\x8f\xa9", "\xf0\x9f\x8f\xaa", "\xf0\x9f\x8f\xab", "\xf0\x9f\x8f\xac", "\xf0\x9f\x8f\xad", "\xf0\x9f\x8f\xaf", "\xf0\x9f\x8f\xb0", "\xf0\x9f\x92\x92", "\xf0\x9f\x97\xbc", "\xf0\x9f\x97\xbd", "\xe2\x9b\xaa", "\xf0\x9f\x95\x8c", "\xf0\x9f\x95\x8d", "\xe2\x9b\xa9", "\xf0\x9f\x95\x8b", "\xe2\x9b\xb2", "\xe2\x9b\xba", "\xf0\x9f\x8c\x81", "\xf0\x9f\x8c\x83", "\xf0\x9f\x8c\x84", "\xf0\x9f\x8c\x85", "\xf0\x9f\x8c\x86", "\xf0\x9f\x8c\x87", "\xf0\x9f\x8c\x89", "\xf0\x9f\x8c\x8c", "\xf0\x9f\x8e\xa0", "\xf0\x9f\x8e\xa1", "\xf0\x9f\x8e\xa2", "\xf0\x9f\x9a\x82", "\xf0\x9f\x9a\x83", "\xf0\x9f\x9a\x84", "\xf0\x9f\x9a\x85", "\xf0\x9f\x9a\x86", "\xf0\x9f\x9a\x87", "\xf0\x9f\x9a\x88", "\xf0\x9f\x9a\x89", "\xf0\x9f\x9a\x8a", "\xf0\x9f\x9a\x9d", "\xf0\x9f\x9a\x9e", "\xf0\x9f\x9a\x8b", "\xf0\x9f\x9a\x8c", "\xf0\x9f\x9a\x8d", "\xf0\x9f\x9a\x8e", "\xf0\x9f\x9a\x90", "\xf0\x9f\x9a\x91", "\xf0\x9f\x9a\x92", "\xf0\x9f\x9a\x93", "\xf0\x9f\x9a\x94", "\xf0\x9f\x9a\x95", "\xf0\x9f\x9a\x96", "\xf0\x9f\x9a\x97", "\xf0\x9f\x9a\x98", "\xf0\x9f\x9a\x99", "\xf0\x9f\x9a\x9a", "\xf0\x9f\x9a\x9b", "\xf0\x9f\x9a\x9c", "\xf0\x9f\x9a\xb2", "\xf0\x9f\x9b\xb4", "\xf0\x9f\x9b\xb5", "\xf0\x9f\x9a\x8f", "\xf0\x9f\x9b\xa3", "\xf0\x9f\x9b\xa4", "\xe2\x9b\xbd", "\xf0\x9f\x9a\xa8", "\xf0\x9f\x9a\xa5", "\xf0\x9f\x9a\xa6", "\xf0\x9f\x9a\xa7", "\xe2\x9a\x93", "\xe2\x9b\xb5", "\xf0\x9f\x9b\xb6", "\xf0\x9f\x9a\xa4", "\xf0\x9f\x9b\xb3", "\xe2\x9b\xb4", "\xf0\x9f\x9b\xa5", "\xf0\x9f\x9a\xa2", "\xe2\x9c\x88", "\xf0\x9f\x9b\xa9", "\xf0\x9f\x9b\xab", "\xf0\x9f\x9b\xac", "\xf0\x9f\x92\xba", "\xf0\x9f\x9a\x81", "\xf0\x9f\x9a\x9f", "\xf0\x9f\x9a\xa0", "\xf0\x9f\x9a\xa1", "\xf0\x9f\x9a\x80", "\xf0\x9f\x9b\xb0", "\xf0\x9f\x8c\xa0", "\xf0\x9f\x8c\x88", "\xf0\x9f\x8e\x86", "\xf0\x9f\x8e\x87", "\xf0\x9f\x8e\x91", "\xf0\x9f\x8f\x81"}}, {"modifier", {"\xf0\x9f\x8f\xbb", "\xf0\x9f\x8f\xbc", "\xf0\x9f\x8f\xbd", "\xf0\x9f\x8f\xbe", "\xf0\x9f\x8f\xbf"}}, {"food", {"\xf0\x9f\x8d\x87", "\xf0\x9f\x8d\x88", "\xf0\x9f\x8d\x89", "\xf0\x9f\x8d\x8a", "\xf0\x9f\x8d\x8b", "\xf0\x9f\x8d\x8c", "\xf0\x9f\x8d\x8d", "\xf0\x9f\x8d\x8e", "\xf0\x9f\x8d\x8f", "\xf0\x9f\x8d\x90", "\xf0\x9f\x8d\x91", "\xf0\x9f\x8d\x92", "\xf0\x9f\x8d\x93", "\xf0\x9f\xa5\x9d", "\xf0\x9f\x8d\x85", "\xf0\x9f\xa5\x91", "\xf0\x9f\x8d\x86", "\xf0\x9f\xa5\x94", "\xf0\x9f\xa5\x95", "\xf0\x9f\x8c\xbd", "\xf0\x9f\x8c\xb6", "\xf0\x9f\xa5\x92", "\xf0\x9f\xa5\x9c", "\xf0\x9f\x8d\x9e", "\xf0\x9f\xa5\x90", "\xf0\x9f\xa5\x96", "\xf0\x9f\xa5\x9e", "\xf0\x9f\xa7\x80", "\xf0\x9f\x8d\x96", "\xf0\x9f\x8d\x97", "\xf0\x9f\xa5\x93", "\xf0\x9f\x8d\x94", "\xf0\x9f\x8d\x9f", "\xf0\x9f\x8d\x95", "\xf0\x9f\x8c\xad", "\xf0\x9f\x8c\xae", "\xf0\x9f\x8c\xaf", "\xf0\x9f\xa5\x99", "\xf0\x9f\xa5\x9a", "\xf0\x9f\x8d\xb3", "\xf0\x9f\xa5\x98", "\xf0\x9f\x8d\xb2", "\xf0\x9f\xa5\x97", "\xf0\x9f\x8d\xbf", "\xf0\x9f\x8d\xb1", "\xf0\x9f\x8d\x98", "\xf0\x9f\x8d\x99", "\xf0\x9f\x8d\x9a", "\xf0\x9f\x8d\x9b", "\xf0\x9f\x8d\x9c", "\xf0\x9f\x8d\x9d", "\xf0\x9f\x8d\xa0", "\xf0\x9f\x8d\xa2", "\xf0\x9f\x8d\xa3", "\xf0\x9f\x8d\xa4", "\xf0\x9f\x8d\xa5", "\xf0\x9f\x8d\xa1", "\xf0\x9f\x8d\xa6", "\xf0\x9f\x8d\xa7", "\xf0\x9f\x8d\xa8", "\xf0\x9f\x8d\xa9", "\xf0\x9f\x8d\xaa", "\xf0\x9f\x8e\x82", "\xf0\x9f\x8d\xb0", "\xf0\x9f\x8d\xab", "\xf0\x9f\x8d\xac", "\xf0\x9f\x8d\xad", "\xf0\x9f\x8d\xae", "\xf0\x9f\x8d\xaf", "\xf0\x9f\x8d\xbc", "\xf0\x9f\xa5\x9b", "\xe2\x98\x95", "\xf0\x9f\x8d\xb5", "\xf0\x9f\x8d\xb6", "\xf0\x9f\x8d\xbe", "\xf0\x9f\x8d\xb7", "\xf0\x9f\x8d\xb8", "\xf0\x9f\x8d\xb9", "\xf0\x9f\x8d\xba", "\xf0\x9f\x8d\xbb", "\xf0\x9f\xa5\x82", "\xf0\x9f\xa5\x83", "\xf0\x9f\x8d\xbd", "\xf0\x9f\x8d\xb4", "\xf0\x9f\xa5\x84"}}, {"flags", {"\xf0\x9f\x87\xa6\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xab", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xac", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xae", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb6", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xba", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xbd", "\xf0\x9f\x87\xa6\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xa7", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xab", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xac", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xad", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xae", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xaf", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb6", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbb", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xa7\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xab", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xac", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xad", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xae", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb5", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xba", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbb", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbd", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xa8\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xac", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xaf", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xa9\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xac", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xad", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xaa\xf0\x9f\x87\xba", "\xf0\x9f\x87\xab\xf0\x9f\x87\xae", "\xf0\x9f\x87\xab\xf0\x9f\x87\xaf", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xab\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xac\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xac\xf0\x9f\x87\xa7", "\xf0\x9f\x87\xac\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xac\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xac\xf0\x9f\x87\xab", "\xf0\x9f\x87\xac\xf0\x9f\x87\xac", "\xf0\x9f\x87\xac\xf0\x9f\x87\xad", "\xf0\x9f\x87\xac\xf0\x9f\x87\xae", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb5", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb6", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xac\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xac\xf0\x9f\x87\xba", "\xf0\x9f\x87\xac\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xac\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xad\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xad\xf0\x9f\x87\xba", "\xf0\x9f\x87\xae\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xae\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xae\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb6", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xae\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xac", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xad", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xae", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb5", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xb0\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xa7", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xae", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xba", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xbb", "\xf0\x9f\x87\xb1\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xab", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xac", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xad", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb5", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb6", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xba", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbb", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbd", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xb2\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xab", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xac", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xae", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb5", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xba", "\xf0\x9f\x87\xb3\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xb4\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xab", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xac", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xad", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xb5\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xb6\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xba", "\xf0\x9f\x87\xb7\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa7", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xac", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xad", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xae", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xaf", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbb", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbd", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xb8\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xa9", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xab", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xac", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xad", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xaf", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb1", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb4", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb7", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xbb", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xbc", "\xf0\x9f\x87\xb9\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xba\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xba\xf0\x9f\x87\xac", "\xf0\x9f\x87\xba\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xba\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xba\xf0\x9f\x87\xbe", "\xf0\x9f\x87\xba\xf0\x9f\x87\xbf", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xa8", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xac", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xae", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xb3", "\xf0\x9f\x87\xbb\xf0\x9f\x87\xba", "\xf0\x9f\x87\xbc\xf0\x9f\x87\xab", "\xf0\x9f\x87\xbc\xf0\x9f\x87\xb8", "\xf0\x9f\x87\xbd\xf0\x9f\x87\xb0", "\xf0\x9f\x87\xbe\xf0\x9f\x87\xaa", "\xf0\x9f\x87\xbe\xf0\x9f\x87\xb9", "\xf0\x9f\x87\xbf\xf0\x9f\x87\xa6", "\xf0\x9f\x87\xbf\xf0\x9f\x87\xb2", "\xf0\x9f\x87\xbf\xf0\x9f\x87\xbc"}}}; + + + std::vector<std::string> EmojiMapper::getCategories() { + std::vector<std::string> categories = { + "people", + "nature", + "food", + "activity", + "travel", + "objects", + "symbols", + "flags" + }; + + for (const auto& keyValuePair : emojisInCategory) { + if (std::find(std::begin(categories), std::end(categories), keyValuePair.first) == std::end(categories)) { + categories.push_back(keyValuePair.first); + } + } + return categories; + } + + std::string EmojiMapper::shortnameToUnicode(const std::string& shortname) { + auto unicodeSequenceIterator = shortnameUnicode.find(shortname); + if (unicodeSequenceIterator != shortnameUnicode.end()) { + return unicodeSequenceIterator->second; + } + else { + return std::string(); + } + } + + std::string EmojiMapper::unicodeToShortname(const std::string& unicode) { + auto shortnameIterator = unicodeShortname.find(unicode); + if (shortnameIterator != unicodeShortname.end()) { + return shortnameIterator->second; + } + else { + return std::string(); + } + } + + std::vector<std::string> EmojiMapper::categoryNameToEmojis(const std::string& categoryName) { + auto emojiIterator = emojisInCategory.find(categoryName); + if (emojiIterator != emojisInCategory.end()) { + return emojiIterator->second; + } + else { + return std::vector<std::string>(); + } + } + + std::string EmojiMapper::categoryToFlagshipUnicodeEmoji(const std::string& category) { + if (category == "recent") { + return shortnameToUnicode(":clock3:"); + } else if (category == "people") { + return shortnameToUnicode(":smiley:"); + } else if (category == "nature" ) { + return shortnameToUnicode(":dog:"); + } else if (category == "food") { + return shortnameToUnicode(":apple:"); + } else if (category == "activity") { + return shortnameToUnicode(":soccer:"); + } else if (category == "travel") { + return shortnameToUnicode(":red_car:"); + } else if (category == "objects") { + return shortnameToUnicode(":bulb:"); + } else if (category == "symbols") { + return shortnameToUnicode(":heavy_division_sign:"); + } else if (category == "flags") { + return shortnameToUnicode(":flag_white:"); + } + return std::string(); + } +} diff --git a/SwifTools/EmojiMapper.h b/SwifTools/EmojiMapper.h new file mode 100644 index 0000000..e2c599c --- /dev/null +++ b/SwifTools/EmojiMapper.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016-2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <string> +#include <unordered_map> +#include <vector> + +namespace Swift { + class EmojiMapper { + public: + static std::string shortnameToUnicode(const std::string& shortname); + static std::string unicodeToShortname(const std::string& unicode); + static std::vector<std::string> categoryNameToEmojis(const std::string& category); + static std::string categoryToFlagshipUnicodeEmoji(const std::string& category); + static std::vector<std::string> getCategories(); + + public: + // \ref shortnameUnicode maps ASCII shortnames (e.g. joy, grin, smiley, …) to a emoji UTF-8 encoded unicode sequence. + static const std::unordered_map<std::string, std::string> shortnameUnicode; + // \ref unicodeShortname is the reverse mapping of \ref shortnameUnicode. + static const std::unordered_map<std::string, std::string> unicodeShortname; + // \ref emojisInCategory maps an emoji category name to a \ref std::vector of UTF-8 encoded unicode sequence strings. + static const std::unordered_map<std::string, std::vector<std::string>> emojisInCategory; + }; +} diff --git a/SwifTools/HunspellChecker.cpp b/SwifTools/HunspellChecker.cpp index ecd352e..019a4dc 100644 --- a/SwifTools/HunspellChecker.cpp +++ b/SwifTools/HunspellChecker.cpp @@ -4,52 +4,165 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <SwifTools/HunspellChecker.h> #include <algorithm> -#include <hunspell/hunspell.hxx> + #include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <boost/regex.hpp> + +#include <hunspell/hunspell.hxx> +#include <Swiften/Base/Log.h> namespace Swift { -HunspellChecker::HunspellChecker(const char* affix_path, const char* dictionary_path) { - speller_ = new Hunspell(affix_path, dictionary_path); +static std::vector<std::string> recursiveFileSearch(const std::vector<std::string>& paths, const boost::regex& regex) { + std::vector<std::string> matches; + + for (auto& path : paths) { + if (boost::filesystem::exists(path)) { + boost::cmatch what; + for (auto filename : boost::make_iterator_range(boost::filesystem::directory_iterator(path), boost::filesystem::directory_iterator())) { + if (boost::filesystem::is_regular_file(filename) && boost::regex_match(filename.path().c_str(), what, regex)) { + matches.push_back(filename.path().string()); + } + } + } + } + return matches; +} + +HunspellChecker::HunspellChecker() { } HunspellChecker::~HunspellChecker() { - delete speller_; +} + +std::vector<std::string> HunspellChecker::hunspellDictionaryPaths() const { + // The following list of paths comes from the source of the Hunspell command line tool. + std::vector<std::string> paths = { + "/usr/share/hunspell", + "/usr/share/myspell", + "/usr/share/myspell/dicts", + "/Library/Spelling", + "/opt/openoffice.org/basis3.0/share/dict/ooo", + "/usr/lib/openoffice.org/basis3.0/share/dict/ooo", + "/opt/openoffice.org2.4/share/dict/ooo", + "/usr/lib/openoffice.org2.4/share/dict/ooo", + "/opt/openoffice.org2.3/share/dict/ooo", + "/usr/lib/openoffice.org2.3/share/dict/ooo", + "/opt/openoffice.org2.2/share/dict/ooo", + "/usr/lib/openoffice.org2.2/share/dict/ooo", + "/opt/openoffice.org2.1/share/dict/ooo", + "/usr/lib/openoffice.org2.1/share/dict/ooo", + "/opt/openoffice.org2.0/share/dict/ooo", + "/usr/lib/openoffice.org2.0/share/dict/ooo" + }; + + if (std::getenv("DICPATH")) { + std::string dicpathEnvironment(std::getenv("DICPATH")); + std::vector<std::string> dicpaths; + boost::split(dicpaths,dicpathEnvironment,boost::is_any_of(":")); + paths.insert(paths.begin(), dicpaths.begin(), dicpaths.end()); + } + + return paths; +} + +bool HunspellChecker::isAutomaticallyDetectingLanguage() { + return false; +} + +void HunspellChecker::setActiveLanguage(const std::string& language) { + auto dictionaries = detectedDictionaries(); + if (dictionaries.find(language) != dictionaries.end()) { + SWIFT_LOG(debug) << "Initialized Hunspell with dic,aff files " << dictionaries[language].dicPath << " , " << dictionaries[language].affPath; + speller_ = std::unique_ptr<Hunspell>(new Hunspell(dictionaries[language].affPath.c_str(), dictionaries[language].dicPath.c_str())); + activeLangauge_ = language; + } + else { + SWIFT_LOG(warning) << "Unsupported language '" << language << "'"; + } +} + +std::string HunspellChecker::activeLanguage() const { + return activeLangauge_.get_value_or(""); +} + +std::vector<std::string> HunspellChecker::supportedLanguages() const { + std::vector<std::string> languages; + + for (const auto& n : detectedDictionaries()) { + languages.push_back(n.first); + } + + return languages; +} + +std::unordered_map<std::string, HunspellChecker::Dictionary> HunspellChecker::detectedDictionaries() const { + std::unordered_map<std::string, HunspellChecker::Dictionary> dictionaries; + + auto dictionaryFiles = recursiveFileSearch(hunspellDictionaryPaths(), boost::regex(".*\\.dic$")); + for (const auto& dictionary : dictionaryFiles) { + std::string correspondingAffixPath = dictionary; + boost::replace_last(correspondingAffixPath, ".dic", ".aff"); + if (boost::filesystem::is_regular_file(correspondingAffixPath)) { + auto filenameWithoutExtension = boost::filesystem::basename(dictionary); + dictionaries[filenameWithoutExtension] = {dictionary, correspondingAffixPath}; + } + } + + return dictionaries; } bool HunspellChecker::isCorrect(const std::string& word) { - return speller_->spell(word.c_str()); + if (speller_) { + return speller_->spell(word.c_str()); + } + else { + return true; + } } void HunspellChecker::getSuggestions(const std::string& word, std::vector<std::string>& list) { - char **suggestList; - int words_returned; - if (!word.empty()) { - words_returned = speller_->suggest(&suggestList, word.c_str()); - } - for (int i = 0; i < words_returned; ++i) { - list.push_back(suggestList[i]); - free(suggestList[i]); - } - free(suggestList); + if (speller_) { + char **suggestList = NULL; + int words_returned = 0; + if (!word.empty()) { + words_returned = speller_->suggest(&suggestList, word.c_str()); + if (suggestList != NULL) { + for (int i = 0; i < words_returned; ++i) { + list.push_back(suggestList[i]); + free(suggestList[i]); + } + free(suggestList); + } + } + } } void HunspellChecker::checkFragment(const std::string& fragment, PositionPairList& misspelledPositions) { - if (!fragment.empty()) { - parser_->check(fragment, misspelledPositions); - for (PositionPairList::iterator it = misspelledPositions.begin(); it != misspelledPositions.end();) { - if (isCorrect(fragment.substr(boost::get<0>(*it), boost::get<1>(*it) - boost::get<0>(*it)))) { - it = misspelledPositions.erase(it); - } - else { - ++it; - } - } - } + if (speller_) { + if (!fragment.empty()) { + parser_.check(fragment, misspelledPositions); + for (PositionPairList::iterator it = misspelledPositions.begin(); it != misspelledPositions.end();) { + if (isCorrect(fragment.substr(boost::get<0>(*it), boost::get<1>(*it) - boost::get<0>(*it)))) { + it = misspelledPositions.erase(it); + } + else { + ++it; + } + } + } + } } } diff --git a/SwifTools/HunspellChecker.h b/SwifTools/HunspellChecker.h index 12c0485..2d4831e 100644 --- a/SwifTools/HunspellChecker.h +++ b/SwifTools/HunspellChecker.h @@ -4,24 +4,54 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <memory> +#include <string> +#include <unordered_map> #include <vector> + #include <boost/algorithm/string.hpp> -#include <boost/tuple/tuple.hpp> -#include <SwifTools/SpellChecker.h> +#include <boost/optional.hpp> -#pragma once +#include <SwifTools/SpellChecker.h> class Hunspell; namespace Swift { - class HunspellChecker : public SpellChecker { - public: - HunspellChecker(const char* affix_path, const char* dict_path); - virtual ~HunspellChecker(); - virtual bool isCorrect(const std::string& word); - virtual void getSuggestions(const std::string& word, std::vector<std::string>& list); - virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions); - private: - Hunspell* speller_; - }; + class HunspellChecker : public SpellChecker { + public: + HunspellChecker(); + virtual ~HunspellChecker(); + + virtual bool isAutomaticallyDetectingLanguage(); + + virtual void setActiveLanguage(const std::string& language); + virtual std::string activeLanguage() const; + virtual std::vector<std::string> supportedLanguages() const; + + virtual bool isCorrect(const std::string& word); + virtual void getSuggestions(const std::string& word, std::vector<std::string>& list); + virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions); + + private: + struct Dictionary { + std::string dicPath; + std::string affPath; + }; + + std::unordered_map<std::string, Dictionary> detectedDictionaries() const; + std::vector<std::string> hunspellDictionaryPaths() const; + + private: + std::unique_ptr<Hunspell> speller_; + boost::optional<std::string> activeLangauge_; + + }; } diff --git a/SwifTools/Idle/ActualIdleDetector.cpp b/SwifTools/Idle/ActualIdleDetector.cpp index 2a99e1a..2a16fca 100644 --- a/SwifTools/Idle/ActualIdleDetector.cpp +++ b/SwifTools/Idle/ActualIdleDetector.cpp @@ -1,34 +1,35 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Idle/ActualIdleDetector.h> #include <boost/bind.hpp> -#include <iostream> -#include <SwifTools/Idle/IdleQuerier.h> #include <Swiften/Network/Timer.h> #include <Swiften/Network/TimerFactory.h> +#include <SwifTools/Idle/IdleQuerier.h> + namespace Swift { ActualIdleDetector::ActualIdleDetector(IdleQuerier* querier, TimerFactory* timerFactory, int refreshRateMilliseconds) : querier(querier) { - timer = timerFactory->createTimer(refreshRateMilliseconds); - timer->onTick.connect(boost::bind(&ActualIdleDetector::handleTimerTick, this)); - timer->start(); + timer = timerFactory->createTimer(refreshRateMilliseconds); + timer->onTick.connect(boost::bind(&ActualIdleDetector::handleTimerTick, this)); + timer->start(); } ActualIdleDetector::~ActualIdleDetector() { - timer->stop(); + timer->onTick.disconnect(boost::bind(&ActualIdleDetector::handleTimerTick, this)); + timer->stop(); } void ActualIdleDetector::handleTimerTick() { - timer->stop(); - setIdle(querier->getIdleTimeSeconds() >= getIdleTimeSeconds()); - timer->start(); + timer->stop(); + setIdle(querier->getIdleTimeSeconds() >= getIdleTimeSeconds()); + timer->start(); } } diff --git a/SwifTools/Idle/ActualIdleDetector.h b/SwifTools/Idle/ActualIdleDetector.h index 7845398..176eb11 100644 --- a/SwifTools/Idle/ActualIdleDetector.h +++ b/SwifTools/Idle/ActualIdleDetector.h @@ -1,30 +1,30 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/shared_ptr.hpp> +#include <memory> #include <SwifTools/Idle/IdleDetector.h> namespace Swift { - class IdleQuerier; - class TimerFactory; - class Timer; + class IdleQuerier; + class TimerFactory; + class Timer; - class ActualIdleDetector : public IdleDetector, public boost::bsignals::trackable { - public: - ActualIdleDetector(IdleQuerier*, TimerFactory*, int refreshRateMilliseconds); - ~ActualIdleDetector(); + class ActualIdleDetector : public IdleDetector, public boost::signals2::trackable { + public: + ActualIdleDetector(IdleQuerier*, TimerFactory*, int refreshRateMilliseconds); + ~ActualIdleDetector(); - private: - void handleTimerTick(); + private: + void handleTimerTick(); - private: - IdleQuerier* querier; - boost::shared_ptr<Timer> timer; - }; + private: + IdleQuerier* querier; + std::shared_ptr<Timer> timer; + }; } diff --git a/SwifTools/Idle/DummyIdleQuerier.h b/SwifTools/Idle/DummyIdleQuerier.h index 162d8f6..d3f5177 100644 --- a/SwifTools/Idle/DummyIdleQuerier.h +++ b/SwifTools/Idle/DummyIdleQuerier.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,12 +9,12 @@ #include <SwifTools/Idle/IdleQuerier.h> namespace Swift { - class DummyIdleQuerier : public IdleQuerier { - public: - DummyIdleQuerier() {} + class DummyIdleQuerier : public IdleQuerier { + public: + DummyIdleQuerier() {} - virtual int getIdleTimeSeconds() { - return 0; - } - }; + virtual int getIdleTimeSeconds() { + return 0; + } + }; } diff --git a/SwifTools/Idle/IdleDetector.cpp b/SwifTools/Idle/IdleDetector.cpp index 66ec4a5..f364e25 100644 --- a/SwifTools/Idle/IdleDetector.cpp +++ b/SwifTools/Idle/IdleDetector.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Idle/IdleDetector.h> diff --git a/SwifTools/Idle/IdleDetector.h b/SwifTools/Idle/IdleDetector.h index 9e379f7..3e9df66 100644 --- a/SwifTools/Idle/IdleDetector.h +++ b/SwifTools/Idle/IdleDetector.h @@ -1,43 +1,44 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <Swiften/Base/boost_bsignals.h> -#include <boost/shared_ptr.hpp> +#include <memory> + +#include <boost/signals2.hpp> namespace Swift { - class IdleDetector { - public: - IdleDetector() : idle(false), idleTimeSeconds(300) {} - virtual ~IdleDetector(); - - void setIdleTimeSeconds(int time) { - idleTimeSeconds = time; - } - - int getIdleTimeSeconds() const { - return idleTimeSeconds; - } - - virtual bool isIdle() const { - return idle; - } - - boost::signal<void (bool /* isIdle */)> onIdleChanged; - - void setIdle(bool b) { - if (b != idle) { - idle = b; - onIdleChanged(b); - } - } - - private: - bool idle; - int idleTimeSeconds; - }; + class IdleDetector { + public: + IdleDetector() : idle(false), idleTimeSeconds(300) {} + virtual ~IdleDetector(); + + void setIdleTimeSeconds(int time) { + idleTimeSeconds = time; + } + + int getIdleTimeSeconds() const { + return idleTimeSeconds; + } + + virtual bool isIdle() const { + return idle; + } + + boost::signals2::signal<void (bool /* isIdle */)> onIdleChanged; + + void setIdle(bool b) { + if (b != idle) { + idle = b; + onIdleChanged(b); + } + } + + private: + bool idle; + int idleTimeSeconds; + }; } diff --git a/SwifTools/Idle/IdleQuerier.cpp b/SwifTools/Idle/IdleQuerier.cpp index f553252..002088c 100644 --- a/SwifTools/Idle/IdleQuerier.cpp +++ b/SwifTools/Idle/IdleQuerier.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Idle/IdleQuerier.h> diff --git a/SwifTools/Idle/IdleQuerier.h b/SwifTools/Idle/IdleQuerier.h index 6ccc67a..e0de8be 100644 --- a/SwifTools/Idle/IdleQuerier.h +++ b/SwifTools/Idle/IdleQuerier.h @@ -1,16 +1,16 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once namespace Swift { - class IdleQuerier { - public: - virtual ~IdleQuerier(); + class IdleQuerier { + public: + virtual ~IdleQuerier(); - virtual int getIdleTimeSeconds() = 0; - }; + virtual int getIdleTimeSeconds() = 0; + }; } diff --git a/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp b/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp index 41238a0..a0b78e6 100644 --- a/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp +++ b/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp @@ -1,23 +1,24 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ -#include <iostream> #include <cassert> +#include <iostream> -#include <SwifTools/Idle/PlatformIdleQuerier.h> #include <Swiften/Base/sleep.h> +#include <SwifTools/Idle/PlatformIdleQuerier.h> + using namespace Swift; int main() { - PlatformIdleQuerier querier; - while (true) { - std::cout << "Idle time: " << querier.getIdleTimeSeconds() << std::endl; - Swift::sleep(1000); - } - assert(false); - return 0; + PlatformIdleQuerier querier; + while (true) { + std::cout << "Idle time: " << querier.getIdleTimeSeconds() << std::endl; + Swift::sleep(1000); + } + assert(false); + return 0; } diff --git a/SwifTools/Idle/IdleQuerierTest/SConscript b/SwifTools/Idle/IdleQuerierTest/SConscript index 5878a46..89bb3f9 100644 --- a/SwifTools/Idle/IdleQuerierTest/SConscript +++ b/SwifTools/Idle/IdleQuerierTest/SConscript @@ -1,12 +1,12 @@ Import("env") if env["TEST"] : - myenv = env.Clone() - myenv.MergeFlags(myenv["SWIFTOOLS_FLAGS"]) - myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) - myenv.MergeFlags(myenv["BOOST_FLAGS"]) - if myenv["HAVE_XSS"] : - myenv.MergeFlags(myenv.get("XSS_FLAGS", {})) - myenv.Append(LIBS = ["X11"]) - myenv.MergeFlags(myenv["PLATFORM_FLAGS"]) - tester = myenv.Program("IdleQuerierTest", ["IdleQuerierTest.cpp"]) + myenv = env.Clone() + myenv.MergeFlags(myenv["SWIFTOOLS_FLAGS"]) + myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) + myenv.MergeFlags(myenv["BOOST_FLAGS"]) + if myenv["HAVE_XSS"] : + myenv.MergeFlags(myenv.get("XSS_FLAGS", {})) + myenv.Append(LIBS = ["X11"]) + myenv.MergeFlags(myenv["PLATFORM_FLAGS"]) + tester = myenv.Program("IdleQuerierTest", ["IdleQuerierTest.cpp"]) diff --git a/SwifTools/Idle/MacOSXIdleQuerier.cpp b/SwifTools/Idle/MacOSXIdleQuerier.cpp index 8eaece6..89fa050 100644 --- a/SwifTools/Idle/MacOSXIdleQuerier.cpp +++ b/SwifTools/Idle/MacOSXIdleQuerier.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Idle/MacOSXIdleQuerier.h> @@ -10,26 +10,33 @@ #include <cassert> #include <iostream> +#include <limits> + #include <boost/numeric/conversion/cast.hpp> #include <CoreFoundation/CoreFoundation.h> namespace Swift { MacOSXIdleQuerier::MacOSXIdleQuerier() : ioService(0) { - mach_port_t masterPort; - IOMasterPort(MACH_PORT_NULL, &masterPort); - ioService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOHIDSystem")); - assert(ioService); + mach_port_t masterPort; + IOMasterPort(MACH_PORT_NULL, &masterPort); + ioService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOHIDSystem")); + assert(ioService); } int MacOSXIdleQuerier::getIdleTimeSeconds() { - CFTypeRef property = IORegistryEntryCreateCFProperty(ioService, CFSTR("HIDIdleTime"), kCFAllocatorDefault, 0); - uint64_t idle = 0; - bool result = CFNumberGetValue((CFNumberRef)property, kCFNumberSInt64Type, &idle); - assert(result); - (void) result; - CFRelease(property); - return boost::numeric_cast<int>(idle / 1000000000); + CFTypeRef property = IORegistryEntryCreateCFProperty(ioService, CFSTR("HIDIdleTime"), kCFAllocatorDefault, 0); + uint64_t idle = 0; + bool result = CFNumberGetValue((CFNumberRef)property, kCFNumberSInt64Type, &idle); + assert(result); + (void) result; + CFRelease(property); + try { + return boost::numeric_cast<int>(idle / 1000000000); + } + catch (const boost::numeric::bad_numeric_cast&) { + return std::numeric_limits<int>::max(); + } } } diff --git a/SwifTools/Idle/MacOSXIdleQuerier.h b/SwifTools/Idle/MacOSXIdleQuerier.h index fbbd448..8ff747c 100644 --- a/SwifTools/Idle/MacOSXIdleQuerier.h +++ b/SwifTools/Idle/MacOSXIdleQuerier.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -11,13 +11,13 @@ #include <SwifTools/Idle/IdleQuerier.h> namespace Swift { - class MacOSXIdleQuerier : public IdleQuerier { - public: - MacOSXIdleQuerier(); + class MacOSXIdleQuerier : public IdleQuerier { + public: + MacOSXIdleQuerier(); - virtual int getIdleTimeSeconds(); + virtual int getIdleTimeSeconds(); - private: - io_service_t ioService; - }; + private: + io_service_t ioService; + }; } diff --git a/SwifTools/Idle/PlatformIdleQuerier.cpp b/SwifTools/Idle/PlatformIdleQuerier.cpp index 34bcedd..8b62df8 100644 --- a/SwifTools/Idle/PlatformIdleQuerier.cpp +++ b/SwifTools/Idle/PlatformIdleQuerier.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Idle/PlatformIdleQuerier.h> @@ -21,24 +21,24 @@ namespace Swift { -PlatformIdleQuerier::PlatformIdleQuerier() : querier(NULL) { +PlatformIdleQuerier::PlatformIdleQuerier() : querier(nullptr) { #if defined(SWIFTEN_PLATFORM_MACOSX) #if defined(HAVE_IOKIT) && !defined(SWIFTEN_PLATFORM_IPHONE) - querier = new MacOSXIdleQuerier(); + querier = new MacOSXIdleQuerier(); #else - querier = new DummyIdleQuerier(); + querier = new DummyIdleQuerier(); #endif #elif defined(SWIFTEN_PLATFORM_WINDOWS) - querier = new WindowsIdleQuerier(); + querier = new WindowsIdleQuerier(); #elif defined(HAVE_XSS) - querier = new XSSIdleQuerier(); + querier = new XSSIdleQuerier(); #else - querier = new DummyIdleQuerier(); + querier = new DummyIdleQuerier(); #endif } PlatformIdleQuerier::~PlatformIdleQuerier() { - delete querier; + delete querier; } } diff --git a/SwifTools/Idle/PlatformIdleQuerier.h b/SwifTools/Idle/PlatformIdleQuerier.h index 2679e39..1221ada 100644 --- a/SwifTools/Idle/PlatformIdleQuerier.h +++ b/SwifTools/Idle/PlatformIdleQuerier.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,16 +9,16 @@ #include <SwifTools/Idle/IdleQuerier.h> namespace Swift { - class PlatformIdleQuerier : public IdleQuerier { - public: - PlatformIdleQuerier(); - ~PlatformIdleQuerier(); + class PlatformIdleQuerier : public IdleQuerier { + public: + PlatformIdleQuerier(); + ~PlatformIdleQuerier(); - virtual int getIdleTimeSeconds() { - return querier->getIdleTimeSeconds(); - } + virtual int getIdleTimeSeconds() { + return querier->getIdleTimeSeconds(); + } - private: - IdleQuerier* querier; - }; + private: + IdleQuerier* querier; + }; } diff --git a/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp b/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp index 686b7a0..ecd72b1 100644 --- a/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp +++ b/SwifTools/Idle/UnitTest/ActualIdleDetectorTest.cpp @@ -1,170 +1,168 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ +#include <memory> + +#include <boost/bind.hpp> + #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/bind.hpp> + +#include <Swiften/Network/Timer.h> +#include <Swiften/Network/TimerFactory.h> #include <SwifTools/Idle/ActualIdleDetector.h> #include <SwifTools/Idle/IdleQuerier.h> -#include <Swiften/Base/foreach.h> -#include <Swiften/Network/TimerFactory.h> -#include <Swiften/Network/Timer.h> using namespace Swift; class ActualIdleDetectorTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(ActualIdleDetectorTest); - CPPUNIT_TEST(testDestructor); - CPPUNIT_TEST(testHandleTick_Idle); - CPPUNIT_TEST(testHandleTick_Idle_AlreadyIdle); - CPPUNIT_TEST(testHandleTick_NotIdle); - CPPUNIT_TEST(testHandleTick_NotIdle_AlreadyNotIdle); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - querier = new MockIdleQuerier(); - timerFactory = new MockTimerFactory(); - idleEvents.clear(); - } - - void tearDown() { - delete timerFactory; - delete querier; - } - - void testDestructor() { - ActualIdleDetector* testling = createDetector(); - testling->setIdleTimeSeconds(15); - delete testling; - - querier->idleTime = 15; - timerFactory->updateTime(15000); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(idleEvents.size())); - } - - void testHandleTick_Idle() { - std::auto_ptr<ActualIdleDetector> testling(createDetector()); - testling->setIdleTimeSeconds(15); - querier->idleTime = 15; - - timerFactory->updateTime(15000); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(idleEvents.size())); - CPPUNIT_ASSERT(idleEvents[0]); - } - - void testHandleTick_Idle_AlreadyIdle() { - std::auto_ptr<ActualIdleDetector> testling(createDetector()); - testling->setIdleTimeSeconds(15); - querier->idleTime = 15; - timerFactory->updateTime(15000); - - querier->idleTime = 30; - timerFactory->updateTime(30000); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(idleEvents.size())); - CPPUNIT_ASSERT(idleEvents[0]); - } - - void testHandleTick_NotIdle() { - std::auto_ptr<ActualIdleDetector> testling(createDetector()); - testling->setIdleTimeSeconds(15); - querier->idleTime = 15; - timerFactory->updateTime(15000); - - querier->idleTime = 5; - timerFactory->updateTime(30000); - - CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(idleEvents.size())); - CPPUNIT_ASSERT(idleEvents[0]); - CPPUNIT_ASSERT(!idleEvents[1]); - } - - void testHandleTick_NotIdle_AlreadyNotIdle() { - std::auto_ptr<ActualIdleDetector> testling(createDetector()); - testling->setIdleTimeSeconds(15); - querier->idleTime = 5; - - timerFactory->updateTime(15000); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(idleEvents.size())); - } - - private: - ActualIdleDetector* createDetector() { - ActualIdleDetector* detector = new ActualIdleDetector(querier, timerFactory, 10); - detector->onIdleChanged.connect(boost::bind(&ActualIdleDetectorTest::handleIdle, this, _1)); - return detector; - } - - void handleIdle(bool b) { - idleEvents.push_back(b); - } - - private: - struct MockIdleQuerier : public IdleQuerier { - MockIdleQuerier() : idleTime(0) {} - virtual int getIdleTimeSeconds() { return idleTime; } - int idleTime; - }; - - struct MockTimer : public Timer { - MockTimer(int interval) : interval(interval), running(false), lastTime(0) {} - - virtual void start() { - running = true; - } - - virtual void stop() { - running = false; - } - - virtual void updateTime(int currentTime) { - if (lastTime == currentTime) { - return; - } - if (running) { - int time = lastTime; - while (time <= currentTime) { - onTick(); - time += interval; - } - } - lastTime = currentTime; - } - - int interval; - bool running; - int lastTime; - }; - - struct MockTimerFactory : public TimerFactory { - MockTimerFactory() {} - - void updateTime(int milliseconds) { - foreach(boost::shared_ptr<MockTimer> timer, timers) { - timer->updateTime(milliseconds); - } - } - - boost::shared_ptr<Timer> createTimer(int milliseconds) { - boost::shared_ptr<MockTimer> timer(new MockTimer(milliseconds)); - timers.push_back(timer); - return timer; - } - - std::vector<boost::shared_ptr<MockTimer> > timers; - }; - - MockIdleQuerier* querier; - MockTimerFactory* timerFactory; - std::vector<bool> idleEvents; + CPPUNIT_TEST_SUITE(ActualIdleDetectorTest); + CPPUNIT_TEST(testDestructor); + CPPUNIT_TEST(testHandleTick_Idle); + CPPUNIT_TEST(testHandleTick_Idle_AlreadyIdle); + CPPUNIT_TEST(testHandleTick_NotIdle); + CPPUNIT_TEST(testHandleTick_NotIdle_AlreadyNotIdle); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + querier = std::unique_ptr<MockIdleQuerier>(new MockIdleQuerier()); + timerFactory = std::unique_ptr<MockTimerFactory>(new MockTimerFactory()); + idleEvents.clear(); + } + + void testDestructor() { + ActualIdleDetector* testling = createDetector(); + testling->setIdleTimeSeconds(15); + delete testling; + + querier->idleTime = 15; + timerFactory->updateTime(15000); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(idleEvents.size())); + } + + void testHandleTick_Idle() { + std::unique_ptr<ActualIdleDetector> testling(createDetector()); + testling->setIdleTimeSeconds(15); + querier->idleTime = 15; + + timerFactory->updateTime(15000); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(idleEvents.size())); + CPPUNIT_ASSERT(idleEvents[0]); + } + + void testHandleTick_Idle_AlreadyIdle() { + std::unique_ptr<ActualIdleDetector> testling(createDetector()); + testling->setIdleTimeSeconds(15); + querier->idleTime = 15; + timerFactory->updateTime(15000); + + querier->idleTime = 30; + timerFactory->updateTime(30000); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(idleEvents.size())); + CPPUNIT_ASSERT(idleEvents[0]); + } + + void testHandleTick_NotIdle() { + std::unique_ptr<ActualIdleDetector> testling(createDetector()); + testling->setIdleTimeSeconds(15); + querier->idleTime = 15; + timerFactory->updateTime(15000); + + querier->idleTime = 5; + timerFactory->updateTime(30000); + + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(idleEvents.size())); + CPPUNIT_ASSERT(idleEvents[0]); + CPPUNIT_ASSERT(!idleEvents[1]); + } + + void testHandleTick_NotIdle_AlreadyNotIdle() { + std::unique_ptr<ActualIdleDetector> testling(createDetector()); + testling->setIdleTimeSeconds(15); + querier->idleTime = 5; + + timerFactory->updateTime(15000); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(idleEvents.size())); + } + + private: + ActualIdleDetector* createDetector() { + ActualIdleDetector* detector = new ActualIdleDetector(querier.get(), timerFactory.get(), 10); + detector->onIdleChanged.connect(boost::bind(&ActualIdleDetectorTest::handleIdle, this, _1)); + return detector; + } + + void handleIdle(bool b) { + idleEvents.push_back(b); + } + + private: + struct MockIdleQuerier : public IdleQuerier { + MockIdleQuerier() : idleTime(0) {} + virtual int getIdleTimeSeconds() { return idleTime; } + int idleTime; + }; + + struct MockTimer : public Timer { + MockTimer(int interval) : interval(interval), running(false), lastTime(0) {} + + virtual void start() { + running = true; + } + + virtual void stop() { + running = false; + } + + virtual void updateTime(int currentTime) { + if (lastTime == currentTime) { + return; + } + if (running) { + int time = lastTime; + while (time <= currentTime) { + onTick(); + time += interval; + } + } + lastTime = currentTime; + } + + int interval; + bool running; + int lastTime; + }; + + struct MockTimerFactory : public TimerFactory { + MockTimerFactory() {} + + void updateTime(int milliseconds) { + for (std::shared_ptr<MockTimer> timer : timers) { + timer->updateTime(milliseconds); + } + } + + std::shared_ptr<Timer> createTimer(int milliseconds) { + std::shared_ptr<MockTimer> timer(new MockTimer(milliseconds)); + timers.push_back(timer); + return timer; + } + + std::vector<std::shared_ptr<MockTimer> > timers; + }; + + std::unique_ptr<MockIdleQuerier> querier; + std::unique_ptr<MockTimerFactory> timerFactory; + std::vector<bool> idleEvents; }; CPPUNIT_TEST_SUITE_REGISTRATION(ActualIdleDetectorTest); diff --git a/SwifTools/Idle/UnitTest/SConscript b/SwifTools/Idle/UnitTest/SConscript index f193349..26a9190 100644 --- a/SwifTools/Idle/UnitTest/SConscript +++ b/SwifTools/Idle/UnitTest/SConscript @@ -1,5 +1,5 @@ Import("env") env.Append(UNITTEST_SOURCES = [ - File("ActualIdleDetectorTest.cpp") - ]) + File("ActualIdleDetectorTest.cpp") + ]) diff --git a/SwifTools/Idle/WindowsIdleQuerier.cpp b/SwifTools/Idle/WindowsIdleQuerier.cpp index 4b9a5a1..f766436 100644 --- a/SwifTools/Idle/WindowsIdleQuerier.cpp +++ b/SwifTools/Idle/WindowsIdleQuerier.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Idle/WindowsIdleQuerier.h> @@ -14,14 +14,14 @@ WindowsIdleQuerier::WindowsIdleQuerier() { } int WindowsIdleQuerier::getIdleTimeSeconds() { - LASTINPUTINFO info; - info.cbSize = sizeof(info); - if (GetLastInputInfo(&info)) { - return (GetTickCount() - info.dwTime) / 1000; - } - else { - return 0; - } + LASTINPUTINFO info; + info.cbSize = sizeof(info); + if (GetLastInputInfo(&info)) { + return (GetTickCount() - info.dwTime) / 1000; + } + else { + return 0; + } } } diff --git a/SwifTools/Idle/WindowsIdleQuerier.h b/SwifTools/Idle/WindowsIdleQuerier.h index 4a219e5..198c6e9 100644 --- a/SwifTools/Idle/WindowsIdleQuerier.h +++ b/SwifTools/Idle/WindowsIdleQuerier.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,10 +9,10 @@ #include <SwifTools/Idle/IdleQuerier.h> namespace Swift { - class WindowsIdleQuerier : public IdleQuerier { - public: - WindowsIdleQuerier(); + class WindowsIdleQuerier : public IdleQuerier { + public: + WindowsIdleQuerier(); - virtual int getIdleTimeSeconds(); - }; + virtual int getIdleTimeSeconds(); + }; } diff --git a/SwifTools/Idle/XSSIdleQuerier.cpp b/SwifTools/Idle/XSSIdleQuerier.cpp index 74e1eda..03c5330 100644 --- a/SwifTools/Idle/XSSIdleQuerier.cpp +++ b/SwifTools/Idle/XSSIdleQuerier.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Idle/XSSIdleQuerier.h> @@ -14,31 +14,31 @@ namespace Swift { XSSIdleQuerier::XSSIdleQuerier() : display(NULL), info(NULL) { - display = XOpenDisplay(NULL); - assert(display); - rootWindow = DefaultRootWindow(display); - int event, error; - available = XScreenSaverQueryExtension(display, &event, &error); - if (available) { - info = XScreenSaverAllocInfo(); - } - else { - std::cerr << "Warning: XScreenSaver extension not found. Idle time detection will not work." << std::endl; - } + display = XOpenDisplay(NULL); + assert(display); + rootWindow = DefaultRootWindow(display); + int event, error; + available = XScreenSaverQueryExtension(display, &event, &error); + if (available) { + info = XScreenSaverAllocInfo(); + } + else { + std::cerr << "Warning: XScreenSaver extension not found. Idle time detection will not work." << std::endl; + } } XSSIdleQuerier::~XSSIdleQuerier() { - XFree(info); + XFree(info); } int XSSIdleQuerier::getIdleTimeSeconds() { - if (available) { - XScreenSaverQueryInfo(display, rootWindow, info); - return info->idle / 1000; - } - else { - return 0; - } + if (available) { + XScreenSaverQueryInfo(display, rootWindow, info); + return info->idle / 1000; + } + else { + return 0; + } } } diff --git a/SwifTools/Idle/XSSIdleQuerier.h b/SwifTools/Idle/XSSIdleQuerier.h index 29569a3..225f781 100644 --- a/SwifTools/Idle/XSSIdleQuerier.h +++ b/SwifTools/Idle/XSSIdleQuerier.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -12,17 +12,17 @@ #include <SwifTools/Idle/IdleQuerier.h> namespace Swift { - class XSSIdleQuerier : public IdleQuerier { - public: - XSSIdleQuerier(); - ~XSSIdleQuerier(); + class XSSIdleQuerier : public IdleQuerier { + public: + XSSIdleQuerier(); + ~XSSIdleQuerier(); - virtual int getIdleTimeSeconds(); + virtual int getIdleTimeSeconds(); - private: - Display* display; - Window rootWindow; - bool available; - XScreenSaverInfo* info; - }; + private: + Display* display; + Window rootWindow; + bool available; + XScreenSaverInfo* info; + }; } diff --git a/SwifTools/LastLineTracker.cpp b/SwifTools/LastLineTracker.cpp index a7360a8..b9f1aa3 100644 --- a/SwifTools/LastLineTracker.cpp +++ b/SwifTools/LastLineTracker.cpp @@ -4,27 +4,33 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include "LastLineTracker.h" +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <SwifTools/LastLineTracker.h> using namespace Swift; LastLineTracker::LastLineTracker() { - lastFocus = true; - shouldMove = false; + lastFocus = true; + shouldMove = false; } void LastLineTracker::setHasFocus(bool focus) { - if (!focus && lastFocus) { - shouldMove = true; - lastFocus = focus; - return; - } - shouldMove = false; - lastFocus = focus; + if (!focus && lastFocus) { + shouldMove = true; + lastFocus = focus; + return; + } + shouldMove = false; + lastFocus = focus; } bool LastLineTracker::getShouldMoveLastLine() { - bool ret = shouldMove; - shouldMove = false; - return ret; + bool ret = shouldMove; + shouldMove = false; + return ret; } diff --git a/SwifTools/LastLineTracker.h b/SwifTools/LastLineTracker.h index b7c9a3b..7156ec3 100644 --- a/SwifTools/LastLineTracker.h +++ b/SwifTools/LastLineTracker.h @@ -7,13 +7,13 @@ #pragma once namespace Swift { - class LastLineTracker { - public: - LastLineTracker(); - void setHasFocus(bool focus); - bool getShouldMoveLastLine(); - private: - bool lastFocus; - bool shouldMove; - }; + class LastLineTracker { + public: + LastLineTracker(); + void setHasFocus(bool focus); + bool getShouldMoveLastLine(); + private: + bool lastFocus; + bool shouldMove; + }; } diff --git a/SwifTools/Linkify.cpp b/SwifTools/Linkify.cpp index 8ecbb09..b1557e5 100644 --- a/SwifTools/Linkify.cpp +++ b/SwifTools/Linkify.cpp @@ -1,103 +1,104 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Linkify.h> -#include <boost/regex.hpp> -#include <sstream> #include <iostream> +#include <sstream> + +#include <boost/regex.hpp> namespace Swift { static boost::regex linkifyRegexp("^(https?://|xmpp:).*"); std::string Linkify::linkify(const std::string& input) { - std::ostringstream result; - std::vector<char> currentURL; - bool inURL = false; - for (size_t i = 0; i < input.size(); ++i) { - char c = input[i]; - if (inURL) { - if (c != ' ' && c != '\t' && c != '\n' && !(c == '*' && i == input.size() - 1 && input[0] == '*')) { - currentURL.push_back(c); - } - else { - std::string url(¤tURL[0], currentURL.size()); - result << "<a href=\"" << url << "\">" << url << "</a>"; - currentURL.clear(); - inURL = false; - result << c; - } - } - else { - if (boost::regex_match(input.substr(i, 8), linkifyRegexp)) { - currentURL.push_back(c); - inURL = true; - } - else { - result << c; - } - } - } - if (!currentURL.empty()) { - std::string url(¤tURL[0], currentURL.size()); - result << "<a href=\"" << url << "\">" << url << "</a>"; - } - return std::string(result.str()); + std::ostringstream result; + std::vector<char> currentURL; + bool inURL = false; + for (size_t i = 0; i < input.size(); ++i) { + char c = input[i]; + if (inURL) { + if (c != ' ' && c != '\t' && c != '\n' && !(c == '*' && i == input.size() - 1 && input[0] == '*')) { + currentURL.push_back(c); + } + else { + std::string url(¤tURL[0], currentURL.size()); + result << "<a href=\"" << url << "\">" << url << "</a>"; + currentURL.clear(); + inURL = false; + result << c; + } + } + else { + if (boost::regex_match(input.substr(i, 8), linkifyRegexp)) { + currentURL.push_back(c); + inURL = true; + } + else { + result << c; + } + } + } + if (!currentURL.empty()) { + std::string url(¤tURL[0], currentURL.size()); + result << "<a href=\"" << url << "\">" << url << "</a>"; + } + return std::string(result.str()); } std::pair<std::vector<std::string>, size_t> Linkify::splitLink(const std::string& input) { - std::vector<std::string> result; - std::pair<std::vector<std::string>, size_t> pair; - std::vector<char> currentURL; - bool inURL = false; - size_t urlStartsAt = 0; - for (size_t i = 0; i < input.size(); ++i) { - char c = input[i]; - if (inURL) { - if (c != ' ' && c != '\t' && c != '\n' && !(c == '*' && i == input.size() - 1 && input[0] == '*')) { - // Keep parsing - } - else { - std::string url(input.substr(urlStartsAt, i - urlStartsAt)); - result.push_back(url); - inURL = false; - size_t remaining = input.size() - i; - if (remaining > 0) { - result.push_back(input.substr(i, remaining)); - } - pair.first = result; - pair.second = urlStartsAt == 0 ? 0 : 1; - return pair; - } - } - else { - if (boost::regex_match(input.substr(i, 8), linkifyRegexp)) { - urlStartsAt = i; - inURL = true; - if (i > 0) { - result.push_back(input.substr(0, i)); - } - } - else { - // Just keep swimming - } - } - } - if (urlStartsAt > 0 || inURL) { - std::string url(input.substr(urlStartsAt, input.size() - urlStartsAt)); - result.push_back(url); - pair.first = result; - pair.second = urlStartsAt == 0 ? 0 : 1; - } - else { - pair.first.push_back(input); - pair.second = 1; - } - return pair; + std::vector<std::string> result; + std::pair<std::vector<std::string>, size_t> pair; + std::vector<char> currentURL; + bool inURL = false; + size_t urlStartsAt = 0; + for (size_t i = 0; i < input.size(); ++i) { + char c = input[i]; + if (inURL) { + if (c != ' ' && c != '\t' && c != '\n' && !(c == '*' && i == input.size() - 1 && input[0] == '*')) { + // Keep parsing + } + else { + std::string url(input.substr(urlStartsAt, i - urlStartsAt)); + result.push_back(url); + inURL = false; + size_t remaining = input.size() - i; + if (remaining > 0) { + result.push_back(input.substr(i, remaining)); + } + pair.first = result; + pair.second = urlStartsAt == 0 ? 0 : 1; + return pair; + } + } + else { + if (boost::regex_match(input.substr(i, 8), linkifyRegexp)) { + urlStartsAt = i; + inURL = true; + if (i > 0) { + result.push_back(input.substr(0, i)); + } + } + else { + // Just keep swimming + } + } + } + if (urlStartsAt > 0 || inURL) { + std::string url(input.substr(urlStartsAt, input.size() - urlStartsAt)); + result.push_back(url); + pair.first = result; + pair.second = urlStartsAt == 0 ? 0 : 1; + } + else { + pair.first.push_back(input); + pair.second = 1; + } + return pair; } } diff --git a/SwifTools/Linkify.h b/SwifTools/Linkify.h index 0a9c132..64c92dc 100644 --- a/SwifTools/Linkify.h +++ b/SwifTools/Linkify.h @@ -1,27 +1,27 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <vector> #include <string> +#include <vector> namespace Swift { - namespace Linkify { - std::string linkify(const std::string&); - /** - * Parse the string for a URI. The string will be split by the URI, and the segments plus index of the URI returned. - * If no URI is found the index will be result.size() (i.e. an invalid index) - * - * Examples: - * "not a URI" -> <<"not a URI">, -1> - * "http://swift.im" -> <<"http://swift.im">, 0 - * " See http://swift.im" -> <<" See ", "http://swift.im">, 1> - * "Right, http://swift.im it is" -> <<"Right, ", "http://swift.im", " it is">, 1> - */ - std::pair<std::vector<std::string>, size_t> splitLink(const std::string& text); - } + namespace Linkify { + std::string linkify(const std::string&); + /** + * Parse the string for a URI. The string will be split by the URI, and the segments plus index of the URI returned. + * If no URI is found the index will be result.size() (i.e. an invalid index) + * + * Examples: + * "not a URI" -> <<"not a URI">, -1> + * "http://swift.im" -> <<"http://swift.im">, 0 + * " See http://swift.im" -> <<" See ", "http://swift.im">, 1> + * "Right, http://swift.im it is" -> <<"Right, ", "http://swift.im", " it is">, 1> + */ + std::pair<std::vector<std::string>, size_t> splitLink(const std::string& text); + } } diff --git a/SwifTools/MacOSXChecker.h b/SwifTools/MacOSXChecker.h index f2f8ebc..7587c99 100644 --- a/SwifTools/MacOSXChecker.h +++ b/SwifTools/MacOSXChecker.h @@ -4,19 +4,34 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #pragma once #include <vector> + #include <boost/tuple/tuple.hpp> + #include <SwifTools/SpellChecker.h> namespace Swift { - class MacOSXChecker : public SpellChecker { - public: - MacOSXChecker(); - virtual ~MacOSXChecker(); - virtual bool isCorrect(const std::string& word); - virtual void getSuggestions(const std::string& word, std::vector<std::string>& list); - virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions); - }; + class MacOSXChecker : public SpellChecker { + public: + MacOSXChecker(); + virtual ~MacOSXChecker(); + + virtual bool isAutomaticallyDetectingLanguage(); + + virtual void setActiveLanguage(const std::string& language); + virtual std::string activeLanguage() const; + virtual std::vector<std::string> supportedLanguages() const; + + virtual bool isCorrect(const std::string& word); + virtual void getSuggestions(const std::string& word, std::vector<std::string>& list); + virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions); + }; } diff --git a/SwifTools/MacOSXChecker.mm b/SwifTools/MacOSXChecker.mm index 3e687d1..afaa87c 100644 --- a/SwifTools/MacOSXChecker.mm +++ b/SwifTools/MacOSXChecker.mm @@ -4,51 +4,78 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2015-2017 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <SwifTools/MacOSXChecker.h> #include <algorithm> +#include <cassert> + #include <boost/algorithm/string.hpp> #include <AppKit/AppKit.h> +#include <SwifTools/Cocoa/CocoaUtil.h> + namespace Swift { MacOSXChecker::MacOSXChecker() { + NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker]; + [spellChecker setAutomaticallyIdentifiesLanguages:YES]; } MacOSXChecker::~MacOSXChecker() { } bool MacOSXChecker::isCorrect(const std::string& /*word*/) { - // No content since it doesn't seem to be used anywhere. - return false; + // No content since it doesn't seem to be used anywhere. + return false; +} + +bool MacOSXChecker::isAutomaticallyDetectingLanguage() { + return true; +} + +void MacOSXChecker::setActiveLanguage(const std::string& /*language*/) { + assert(false); +} + +std::string MacOSXChecker::activeLanguage() const { + assert(false); +} +std::vector<std::string> MacOSXChecker::supportedLanguages() const { + assert(false); } void MacOSXChecker::getSuggestions(const std::string& word, std::vector<std::string>& list) { - NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker]; - NSString* wordString = [[NSString alloc] initWithUTF8String: word.c_str()]; - NSArray* suggestions = [spellChecker guessesForWord: wordString]; - for(unsigned int i = 0; i < [suggestions count]; ++i) { - list.push_back(std::string([[suggestions objectAtIndex:i] UTF8String])); - } - [wordString release]; + NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker]; + NSString* wordString = [[NSString alloc] initWithUTF8String: word.c_str()]; + NSArray* suggestions = [spellChecker guessesForWordRange:NSMakeRange(0, [wordString length]) inString:wordString language:nil inSpellDocumentWithTag:0]; + for(unsigned int i = 0; i < [suggestions count]; ++i) { + list.push_back(ns2StdString([suggestions objectAtIndex:i])); + } + [wordString release]; } void MacOSXChecker::checkFragment(const std::string& fragment, PositionPairList& misspelledPositions) { - NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker]; - size_t nextLocation = 0; - NSRange range; - NSString *fragmentString = [[NSString alloc] initWithUTF8String: fragment.c_str()]; - do { - range = [spellChecker checkSpellingOfString:fragmentString startingAt:static_cast<long>(nextLocation)]; - if (range.location != NSNotFound) { - if (range.location < nextLocation) - break; - misspelledPositions.push_back(PositionPair(static_cast<int>(range.location), static_cast<int>(range.location + range.length))); - nextLocation = range.location + range.length + 1; - } - } while (range.location != NSNotFound); - [fragmentString release]; + NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker]; + size_t nextLocation = 0; + NSRange range; + NSString *fragmentString = [[NSString alloc] initWithUTF8String: fragment.c_str()]; + do { + range = [spellChecker checkSpellingOfString:fragmentString startingAt:static_cast<long>(nextLocation)]; + if (range.location != NSNotFound) { + if (range.location < nextLocation) + break; + misspelledPositions.push_back(PositionPair(range.location, range.location + range.length)); + nextLocation = range.location + range.length + 1; + } + } while (range.location != NSNotFound); + [fragmentString release]; } } diff --git a/SwifTools/Notifier/GNTPNotifier.cpp b/SwifTools/Notifier/GNTPNotifier.cpp index 757594f..89025af 100644 --- a/SwifTools/Notifier/GNTPNotifier.cpp +++ b/SwifTools/Notifier/GNTPNotifier.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ // FIXME: This notifier needs finishing (handling callbacks etc.) @@ -10,77 +10,77 @@ #include <cassert> #include <iostream> -#include <boost/bind.hpp> #include <sstream> -#include <Swiften/Base/foreach.h> +#include <boost/bind.hpp> + #include <Swiften/Base/Path.h> #include <Swiften/Network/ConnectionFactory.h> namespace Swift { GNTPNotifier::GNTPNotifier(const std::string& name, const boost::filesystem::path& icon, ConnectionFactory* connectionFactory) : name(name), icon(icon), connectionFactory(connectionFactory), initialized(false), registered(false) { - // Registration message - std::ostringstream message; - message << "GNTP/1.0 REGISTER NONE\r\n"; - message << "Application-Name: " << name << "\r\n"; - message << "Application-Icon: file://" << pathToString(icon) << "\r\n"; - message << "Notifications-Count: " << getAllTypes().size() << "\r\n"; - std::vector<Notifier::Type> defaultTypes = getDefaultTypes(); - std::vector<Notifier::Type> allTypes = getAllTypes(); - foreach(Notifier::Type type, allTypes) { - message << "\r\n"; - message << "Notification-Name: " << typeToString(type) << "\r\n"; - message << "Notification-Enabled: " << (std::find(defaultTypes.begin(), defaultTypes.end(), type) == defaultTypes.end() ? "false" : "true") << "\r\n"; - } - message << "\r\n"; + // Registration message + std::ostringstream message; + message << "GNTP/1.0 REGISTER NONE\r\n"; + message << "Application-Name: " << name << "\r\n"; + message << "Application-Icon: file://" << pathToString(icon) << "\r\n"; + message << "Notifications-Count: " << getAllTypes().size() << "\r\n"; + std::vector<Notifier::Type> defaultTypes = getDefaultTypes(); + std::vector<Notifier::Type> allTypes = getAllTypes(); + for (const auto& type : allTypes) { + message << "\r\n"; + message << "Notification-Name: " << typeToString(type) << "\r\n"; + message << "Notification-Enabled: " << (std::find(defaultTypes.begin(), defaultTypes.end(), type) == defaultTypes.end() ? "false" : "true") << "\r\n"; + } + message << "\r\n"; - send(message.str()); + send(message.str()); } GNTPNotifier::~GNTPNotifier() { } void GNTPNotifier::send(const std::string& message) { - if (currentConnection) { - return; - } - currentMessage = message; - currentConnection = connectionFactory->createConnection(); - currentConnection->onConnectFinished.connect(boost::bind(&GNTPNotifier::handleConnectFinished, this, _1)); - currentConnection->onDataRead.connect(boost::bind(&GNTPNotifier::handleDataRead, this, _1)); - currentConnection->connect(HostAddressPort(HostAddress("127.0.0.1"), 23053)); + if (currentConnection) { + return; + } + currentMessage = message; + currentConnection = connectionFactory->createConnection(); + currentConnection->onConnectFinished.connect(boost::bind(&GNTPNotifier::handleConnectFinished, this, _1)); + currentConnection->onDataRead.connect(boost::bind(&GNTPNotifier::handleDataRead, this, _1)); + currentConnection->connect(HostAddressPort(HostAddress("127.0.0.1"), 23053)); } void GNTPNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()>) { - if (registered) { - std::ostringstream message; - message << "GNTP/1.0 NOTIFY NONE\r\n"; - message << "Application-Name: " << name << "\r\n"; - message << "Notification-Name: " << typeToString(type) << "\r\n"; - message << "Notification-Title: " << subject << "\r\n"; - message << "Notification-Text: " << description << "\r\n"; - message << "Notification-Icon: " << pathToString(picture) << "\r\n"; - message << "\r\n"; - send(message.str()); - } + if (registered) { + std::ostringstream message; + message << "GNTP/1.0 NOTIFY NONE\r\n"; + message << "Application-Name: " << name << "\r\n"; + message << "Notification-Name: " << typeToString(type) << "\r\n"; + message << "Notification-Title: " << subject << "\r\n"; + message << "Notification-Text: " << description << "\r\n"; + message << "Notification-Icon: " << pathToString(picture) << "\r\n"; + message << "\r\n"; + send(message.str()); + } } void GNTPNotifier::handleConnectFinished(bool error) { - if (!initialized) { - initialized = true; - registered = !error; - } + if (!initialized) { + initialized = true; + registered = !error; + } - if (!error) { - currentConnection->write(currentMessage.c_str()); - } + if (!error) { + currentConnection->write(currentMessage.c_str()); + } } void GNTPNotifier::handleDataRead(const ByteArray&) { - currentConnection->onDataRead.disconnect(boost::bind(&GNTPNotifier::handleDataRead, this, _1)); - currentConnection->onConnectFinished.disconnect(boost::bind(&GNTPNotifier::handleConnectFinished, this, _1)); - currentConnection.reset(); + currentConnection->onDataRead.disconnect(boost::bind(&GNTPNotifier::handleDataRead, this, _1)); + currentConnection->onConnectFinished.disconnect(boost::bind(&GNTPNotifier::handleConnectFinished, this, _1)); + currentConnection.reset(); } } diff --git a/SwifTools/Notifier/GNTPNotifier.h b/SwifTools/Notifier/GNTPNotifier.h index 2cba044..44811e7 100644 --- a/SwifTools/Notifier/GNTPNotifier.h +++ b/SwifTools/Notifier/GNTPNotifier.h @@ -1,38 +1,39 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once #include <boost/filesystem.hpp> -#include <SwifTools/Notifier/Notifier.h> #include <Swiften/Network/Connection.h> +#include <SwifTools/Notifier/Notifier.h> + namespace Swift { - class ConnectionFactory; - - class GNTPNotifier : public Notifier { - public: - GNTPNotifier(const std::string& name, const boost::filesystem::path& icon, ConnectionFactory* connectionFactory); - ~GNTPNotifier(); - - virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback); - - private: - void handleConnectFinished(bool error); - void handleDataRead(const ByteArray& data); - void send(const std::string& message); - - private: - std::string name; - boost::filesystem::path icon; - ConnectionFactory* connectionFactory; - bool initialized; - bool registered; - std::string currentMessage; - Connection::ref currentConnection; - }; + class ConnectionFactory; + + class GNTPNotifier : public Notifier { + public: + GNTPNotifier(const std::string& name, const boost::filesystem::path& icon, ConnectionFactory* connectionFactory); + ~GNTPNotifier(); + + virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback); + + private: + void handleConnectFinished(bool error); + void handleDataRead(const ByteArray& data); + void send(const std::string& message); + + private: + std::string name; + boost::filesystem::path icon; + ConnectionFactory* connectionFactory; + bool initialized; + bool registered; + std::string currentMessage; + Connection::ref currentConnection; + }; } diff --git a/SwifTools/Notifier/GrowlNotifier.h b/SwifTools/Notifier/GrowlNotifier.h index 73f1008..1b5f191 100644 --- a/SwifTools/Notifier/GrowlNotifier.h +++ b/SwifTools/Notifier/GrowlNotifier.h @@ -1,41 +1,43 @@ /* - * Copyright (c) 2010-2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <memory> + #include <boost/filesystem/fstream.hpp> #include <SwifTools/Notifier/Notifier.h> namespace Swift { - /** - * Preconditions for using growlnotifier: - * - Must be part a bundle. - * - The Carbon/Cocoa application loop must be running (e.g. through QApplication) - * such that notifications are coming through. - */ - class GrowlNotifier : public Notifier { - public: - GrowlNotifier(const std::string& name); - ~GrowlNotifier(); - - virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback); - virtual bool isExternallyConfigured() const; - - // Called by the delegate. Don't call. - void handleNotificationClicked(void* data); - void handleNotificationTimedOut(void* data); - - virtual void purgeCallbacks(); - - private: - void clearPendingNotifications(); - - private: - class Private; - boost::shared_ptr<Private> p; - }; + /** + * Preconditions for using growlnotifier: + * - Must be part a bundle. + * - The Carbon/Cocoa application loop must be running (e.g. through QApplication) + * such that notifications are coming through. + */ + class GrowlNotifier : public Notifier { + public: + GrowlNotifier(const std::string& name); + ~GrowlNotifier(); + + virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback); + virtual bool isExternallyConfigured() const; + + // Called by the delegate. Don't call. + void handleNotificationClicked(void* data); + void handleNotificationTimedOut(void* data); + + virtual void purgeCallbacks(); + + private: + void clearPendingNotifications(); + + private: + class Private; + const std::unique_ptr<Private> p; + }; } diff --git a/SwifTools/Notifier/GrowlNotifier.mm b/SwifTools/Notifier/GrowlNotifier.mm index 2ababf4..4ca53f7 100644 --- a/SwifTools/Notifier/GrowlNotifier.mm +++ b/SwifTools/Notifier/GrowlNotifier.mm @@ -1,12 +1,12 @@ /* - * Copyright (c) 2010-2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Notifier/GrowlNotifier.h> -#include <boost/smart_ptr/make_shared.hpp> +#include <memory> #include <set> #include <SwifTools/Notifier/GrowlNotifierDelegate.h> @@ -17,101 +17,100 @@ #pragma GCC diagnostic ignored "-Wold-style-cast" namespace { - struct Context { - Context(const boost::function<void()>& callback) : callback(new boost::function<void()>(callback)) {} + struct Context { + Context(const boost::function<void()>& callback) : callback(new boost::function<void()>(callback)) {} - boost::function<void()>* callback; - }; + boost::function<void()>* callback; + }; } namespace Swift { class GrowlNotifier::Private { - public: - std::set<Context*> pendingNotifications; - boost::intrusive_ptr<GrowlNotifierDelegate> delegate; + public: + std::set<Context*> pendingNotifications; + boost::intrusive_ptr<GrowlNotifierDelegate> delegate; }; -GrowlNotifier::GrowlNotifier(const std::string& name) { - p = boost::make_shared<Private>(); - p->delegate = boost::intrusive_ptr<GrowlNotifierDelegate>([[GrowlNotifierDelegate alloc] init], false); - p->delegate.get().notifier = this; - p->delegate.get().name = STD2NSSTRING(name); - - NSMutableArray* allNotifications = [[NSMutableArray alloc] init]; - foreach(Type type, getAllTypes()) { - [allNotifications addObject: STD2NSSTRING(typeToString(type))]; - } - - NSMutableArray* defaultNotifications = [[NSMutableArray alloc] init]; - foreach(Type type, getDefaultTypes()) { - [defaultNotifications addObject: STD2NSSTRING(typeToString(type))]; - } - - p->delegate.get().registrationDictionary = [[[NSDictionary alloc] - initWithObjects: [NSArray arrayWithObjects: allNotifications, defaultNotifications, nil] - forKeys: [NSArray arrayWithObjects: GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT, nil]] autorelease]; - - [allNotifications release]; - [defaultNotifications release]; - - [GrowlApplicationBridge setGrowlDelegate: p->delegate.get()]; +GrowlNotifier::GrowlNotifier(const std::string& name) : p(new Private()){ + p->delegate = boost::intrusive_ptr<GrowlNotifierDelegate>([[GrowlNotifierDelegate alloc] init], false); + p->delegate.get().notifier = this; + p->delegate.get().name = std2NSString(name); + + NSMutableArray* allNotifications = [[NSMutableArray alloc] init]; + foreach(Type type, getAllTypes()) { + [allNotifications addObject: std2NSString(typeToString(type))]; + } + + NSMutableArray* defaultNotifications = [[NSMutableArray alloc] init]; + foreach(Type type, getDefaultTypes()) { + [defaultNotifications addObject: std2NSString(typeToString(type))]; + } + + p->delegate.get().registrationDictionary = [[[NSDictionary alloc] + initWithObjects: [NSArray arrayWithObjects: allNotifications, defaultNotifications, nil] + forKeys: [NSArray arrayWithObjects: GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT, nil]] autorelease]; + + [allNotifications release]; + [defaultNotifications release]; + + [GrowlApplicationBridge setGrowlDelegate: p->delegate.get()]; } GrowlNotifier::~GrowlNotifier() { - [GrowlApplicationBridge setGrowlDelegate: nil]; - clearPendingNotifications(); + [GrowlApplicationBridge setGrowlDelegate: nil]; + clearPendingNotifications(); } void GrowlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picturePath, boost::function<void()> callback) { - ByteArray picture; - readByteArrayFromFile(picture, picturePath); - - Context* context = new Context(callback); - // Growl sometimes sends timeout notifications twice for the same message. We therefore need - // to keep track of which ones have already been processed. - p->pendingNotifications.insert(context); - - [GrowlApplicationBridge - notifyWithTitle: STD2NSSTRING(subject) - description: STD2NSSTRING(description) - notificationName: STD2NSSTRING(typeToString(type)) - iconData: [NSData dataWithBytes: vecptr(picture) length: picture.size()] - priority: 0 - isSticky: NO - clickContext: [NSData dataWithBytes: &context length: sizeof(context)]]; + ByteArray picture; + readByteArrayFromFile(picture, picturePath); + + Context* context = new Context(callback); + // Growl sometimes sends timeout notifications twice for the same message. We therefore need + // to keep track of which ones have already been processed. + p->pendingNotifications.insert(context); + + [GrowlApplicationBridge + notifyWithTitle: std2NSString(subject) + description: std2NSString(description) + notificationName: std2NSString(typeToString(type)) + iconData: [NSData dataWithBytes: vecptr(picture) length: picture.size()] + priority: 0 + isSticky: NO + clickContext: [NSData dataWithBytes: &context length: sizeof(context)]]; } void GrowlNotifier::handleNotificationClicked(void* rawData) { - Context* context = *(Context**) [((NSData*) rawData) bytes]; - if (p->pendingNotifications.erase(context) > 0) { - if (!context->callback->empty()) { - (*context->callback)(); - } - delete context; - } + Context* context = *(Context**) [((NSData*) rawData) bytes]; + if (p->pendingNotifications.erase(context) > 0) { + if (!context->callback->empty()) { + (*context->callback)(); + } + delete context; + } } void GrowlNotifier::handleNotificationTimedOut(void* rawData) { - Context* context = *(Context**) [((NSData*) rawData) bytes]; - if (p->pendingNotifications.erase(context) > 0) { - delete context; - } + Context* context = *(Context**) [((NSData*) rawData) bytes]; + if (p->pendingNotifications.erase(context) > 0) { + delete context; + } } bool GrowlNotifier::isExternallyConfigured() const { - return ![GrowlApplicationBridge isMistEnabled]; + return ![GrowlApplicationBridge isMistEnabled]; } void GrowlNotifier::purgeCallbacks() { - clearPendingNotifications(); + clearPendingNotifications(); } void GrowlNotifier::clearPendingNotifications() { - foreach (Context* context, p->pendingNotifications) { - delete context; - } - p->pendingNotifications.clear(); + foreach (Context* context, p->pendingNotifications) { + delete context; + } + p->pendingNotifications.clear(); } } diff --git a/SwifTools/Notifier/GrowlNotifierDelegate.h b/SwifTools/Notifier/GrowlNotifierDelegate.h index b7f0968..f4ce132 100644 --- a/SwifTools/Notifier/GrowlNotifierDelegate.h +++ b/SwifTools/Notifier/GrowlNotifierDelegate.h @@ -1,19 +1,19 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #import <Growl/Growl.h> namespace Swift { - class GrowlNotifier; + class GrowlNotifier; } @interface GrowlNotifierDelegate : NSObject<GrowlApplicationBridgeDelegate> { - Swift::GrowlNotifier* notifier; - NSString* name; - NSDictionary* registrationDictionary; + Swift::GrowlNotifier* notifier; + NSString* name; + NSDictionary* registrationDictionary; } @property (nonatomic, retain) NSDictionary* registrationDictionary; diff --git a/SwifTools/Notifier/GrowlNotifierDelegate.mm b/SwifTools/Notifier/GrowlNotifierDelegate.mm index e184da6..77df3ab 100644 --- a/SwifTools/Notifier/GrowlNotifierDelegate.mm +++ b/SwifTools/Notifier/GrowlNotifierDelegate.mm @@ -1,7 +1,7 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #import "GrowlNotifierDelegate.h" @@ -17,19 +17,19 @@ using namespace Swift; - (NSString *) applicationNameForGrowl { - return name; + return name; } - (NSDictionary*) registrationDictionaryForGrowl { - return registrationDictionary; + return registrationDictionary; } - (void) growlNotificationWasClicked: (id) clickContext { - notifier->handleNotificationClicked(clickContext); + notifier->handleNotificationClicked(clickContext); } - (void) growlNotificationTimedOut: (id) clickContext { - notifier->handleNotificationTimedOut(clickContext); + notifier->handleNotificationTimedOut(clickContext); } @end diff --git a/SwifTools/Notifier/LoggingNotifier.h b/SwifTools/Notifier/LoggingNotifier.h index 3d62593..e12500b 100644 --- a/SwifTools/Notifier/LoggingNotifier.h +++ b/SwifTools/Notifier/LoggingNotifier.h @@ -1,32 +1,33 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <SwifTools/Notifier/Notifier.h> #include <Swiften/Base/ByteArray.h> +#include <SwifTools/Notifier/Notifier.h> + namespace Swift { - class LoggingNotifier : public Notifier { - public: - virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback) { - notifications.push_back(Notification(type, subject, description, picture, callback)); - } + class LoggingNotifier : public Notifier { + public: + virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback) { + notifications.push_back(Notification(type, subject, description, picture, callback)); + } - struct Notification { - Notification(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback) : type(type), subject(subject), description(description), picture(picture), callback(callback) {} - Type type; - std::string subject; - std::string description; - boost::filesystem::path picture; - boost::function<void()> callback; - }; + struct Notification { + Notification(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback) : type(type), subject(subject), description(description), picture(picture), callback(callback) {} + Type type; + std::string subject; + std::string description; + boost::filesystem::path picture; + boost::function<void()> callback; + }; - virtual void purgeCallbacks() {} + virtual void purgeCallbacks() {} - std::vector<Notification> notifications; - }; + std::vector<Notification> notifications; + }; } diff --git a/SwifTools/Notifier/NotificationCenterNotifier.h b/SwifTools/Notifier/NotificationCenterNotifier.h new file mode 100644 index 0000000..838971c --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifier.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <memory> +#include <string> + +#include <SwifTools/Notifier/Notifier.h> + +namespace Swift { + +/** + * @brief The NotificationCenterNotifier class implmenents the notification interface for the + * OS X Notification Center API. + */ +class NotificationCenterNotifier : public Notifier { +public: + NotificationCenterNotifier(); + virtual ~NotificationCenterNotifier(); + + virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void ()> callback); + virtual void purgeCallbacks(); + + /** + * @brief The handleUserNotificationActivated is called by the delegate, when a user activates/clicks on a notification. + * @param identifier The std::string UUID identifiying the notification. + */ + void handleUserNotificationActivated(const std::string& identifier); + +private: + class Private; + const std::unique_ptr<Private> p; +}; + +} diff --git a/SwifTools/Notifier/NotificationCenterNotifier.mm b/SwifTools/Notifier/NotificationCenterNotifier.mm new file mode 100644 index 0000000..e6fdfb6 --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifier.mm @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <SwifTools/Notifier/NotificationCenterNotifier.h> + +#include <map> +#include <string> + +#include <memory> + +#include <Swiften/Base/Log.h> + +#import <Cocoa/Cocoa.h> + +#include <SwifTools/Notifier/NotificationCenterNotifierDelegate.h> +#include <SwifTools/Cocoa/CocoaUtil.h> + +namespace { + struct Context { + Context(const boost::function<void()>& callback) : callback(new boost::function<void()>(callback)) { + } + + ~Context() { + delete callback; + } + + boost::function<void()>* callback; + }; +} + +namespace Swift { + +class NotificationCenterNotifier::Private { + public: + std::map<std::string, std::shared_ptr<Context> > callbacksForNotifications; + boost::intrusive_ptr<NotificationCenterNotifierDelegate> delegate; +}; + +NotificationCenterNotifier::NotificationCenterNotifier() : p(new Private()) { + p->delegate = boost::intrusive_ptr<NotificationCenterNotifierDelegate>([[NotificationCenterNotifierDelegate alloc] init], false); + [p->delegate.get() setNotifier: this]; + + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: p->delegate.get()]; +} + +NotificationCenterNotifier::~NotificationCenterNotifier() { + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: nil]; + p->callbacksForNotifications.clear(); +} + +void NotificationCenterNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void ()> callback) { + std::vector<Notifier::Type> defaultTypes = getDefaultTypes(); + if (std::find(defaultTypes.begin(), defaultTypes.end(), type) == defaultTypes.end()) { + return; + } + NSImage* image = [[NSImage alloc] initWithContentsOfFile: std2NSString(picture.string())]; + NSUserNotification* notification = [[NSUserNotification alloc] init]; + [notification setTitle:std2NSString(typeToString(type))]; + [notification setSubtitle:std2NSString(subject)]; + [notification setInformativeText:std2NSString(description)]; + [notification setContentImage: image]; + [image release]; + + // The OS X Notification Center API does not allow to attach custom data, like a pointer to a callback function, + // to the NSUserNotification object. Therefore we maintain a mapping from a NSUserNotification instance's identification + // to their respective callbacks. + [notification setIdentifier:[[NSUUID UUID] UUIDString]]; + + /// \todo Currently the elements are only removed on application exit. Ideally the notifications not required anymore + /// are removed from the map; e.g. when visiting a chat view, all notifications from that view can be removed from + /// the map and the NSUserNotificationCenter. + p->callbacksForNotifications[ns2StdString(notification.identifier)] = std::make_shared<Context>(callback); + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + [notification release]; +} + +void NotificationCenterNotifier::purgeCallbacks() { + p->callbacksForNotifications.clear(); +} + +void NotificationCenterNotifier::handleUserNotificationActivated(const std::string& identifier) { + if (p->callbacksForNotifications.find(identifier) != p->callbacksForNotifications.end()) { + if (!(*p->callbacksForNotifications[identifier]->callback).empty()) { + (*p->callbacksForNotifications[identifier]->callback)(); + } + } + else { + SWIFT_LOG(warning) << "Missing callback entry for activated notification. The activate notification may come from another instance."; + } +} + +} diff --git a/SwifTools/Notifier/NotificationCenterNotifierDelegate.h b/SwifTools/Notifier/NotificationCenterNotifierDelegate.h new file mode 100644 index 0000000..f09c09f --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifierDelegate.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#import <Cocoa/Cocoa.h> + +namespace Swift { + class NotificationCenterNotifier; +} + +@interface NotificationCenterNotifierDelegate : NSObject<NSUserNotificationCenterDelegate> { +} + +@property (nonatomic) Swift::NotificationCenterNotifier* notifier; + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification; + +@end diff --git a/SwifTools/Notifier/NotificationCenterNotifierDelegate.mm b/SwifTools/Notifier/NotificationCenterNotifierDelegate.mm new file mode 100644 index 0000000..84ec943 --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifierDelegate.mm @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#import "SwifTools/Notifier/NotificationCenterNotifierDelegate.h" + +#include <string> + +#include <SwifTools/Cocoa/CocoaUtil.h> +#include <SwifTools/Notifier/NotificationCenterNotifier.h> + +@implementation NotificationCenterNotifierDelegate + +using namespace Swift; + +@synthesize notifier; + +- (void)userNotificationCenter:(NSUserNotificationCenter *) center didActivateNotification:(NSUserNotification *)notification { + (void)center; + std::string identifier = ns2StdString(notification.identifier); + notifier->handleUserNotificationActivated(identifier); +} + +@end diff --git a/SwifTools/Notifier/Notifier.cpp b/SwifTools/Notifier/Notifier.cpp index 5cd9c50..314d39c 100644 --- a/SwifTools/Notifier/Notifier.cpp +++ b/SwifTools/Notifier/Notifier.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/Notifier/Notifier.h> @@ -15,32 +15,32 @@ Notifier::~Notifier() { } std::string Notifier::typeToString(Type type) { - switch (type) { - case ContactAvailable: return "Contact Becomes Available"; - case ContactUnavailable: return "Contact Becomes Unavailable"; - case ContactStatusChange: return "Contact Changes Status"; - case IncomingMessage: return "Incoming Message"; - case SystemMessage: return "System Message"; - } - assert(false); - return ""; + switch (type) { + case ContactAvailable: return "Contact Becomes Available"; + case ContactUnavailable: return "Contact Becomes Unavailable"; + case ContactStatusChange: return "Contact Changes Status"; + case IncomingMessage: return "Incoming Message"; + case SystemMessage: return "System Message"; + } + assert(false); + return ""; } std::vector<Notifier::Type> Notifier::getAllTypes() { - std::vector<Type> result; - result.push_back(ContactAvailable); - result.push_back(ContactUnavailable); - result.push_back(ContactStatusChange); - result.push_back(IncomingMessage); - result.push_back(SystemMessage); - return result; + std::vector<Type> result; + result.push_back(ContactAvailable); + result.push_back(ContactUnavailable); + result.push_back(ContactStatusChange); + result.push_back(IncomingMessage); + result.push_back(SystemMessage); + return result; } std::vector<Notifier::Type> Notifier::getDefaultTypes() { - std::vector<Type> result; - result.push_back(IncomingMessage); - result.push_back(SystemMessage); - return result; + std::vector<Type> result; + result.push_back(IncomingMessage); + result.push_back(SystemMessage); + return result; } } diff --git a/SwifTools/Notifier/Notifier.h b/SwifTools/Notifier/Notifier.h index 9537ec1..afd596b 100644 --- a/SwifTools/Notifier/Notifier.h +++ b/SwifTools/Notifier/Notifier.h @@ -1,50 +1,51 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/function.hpp> -#include <boost/filesystem/path.hpp> #include <string> #include <vector> +#include <boost/filesystem/path.hpp> +#include <boost/function.hpp> + namespace Swift { - class Notifier { - public: - virtual ~Notifier(); - - enum Type { ContactAvailable, ContactUnavailable, ContactStatusChange, IncomingMessage, SystemMessage }; - - /** - * Picture is a PNG image. - */ - virtual void showMessage( - Type type, - const std::string& subject, - const std::string& description, - const boost::filesystem::path& picture, - boost::function<void()> callback) = 0; - - virtual bool isAvailable() const { - return true; - } - - virtual bool isExternallyConfigured() const { - return false; - } - - /** Remove any pending callbacks. */ - virtual void purgeCallbacks() = 0; - - protected: - std::string typeToString(Type type); - static std::vector<Type> getAllTypes(); - static std::vector<Type> getDefaultTypes(); - - static const int DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; - static const int DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS; - }; + class Notifier { + public: + virtual ~Notifier(); + + enum Type { ContactAvailable, ContactUnavailable, ContactStatusChange, IncomingMessage, SystemMessage }; + + /** + * Picture is a PNG image. + */ + virtual void showMessage( + Type type, + const std::string& subject, + const std::string& description, + const boost::filesystem::path& picture, + boost::function<void()> callback) = 0; + + virtual bool isAvailable() const { + return true; + } + + virtual bool isExternallyConfigured() const { + return false; + } + + /** Remove any pending callbacks. */ + virtual void purgeCallbacks() = 0; + + protected: + std::string typeToString(Type type); + static std::vector<Type> getAllTypes(); + static std::vector<Type> getDefaultTypes(); + + static const int DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; + static const int DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS; + }; } diff --git a/SwifTools/Notifier/NullNotifier.h b/SwifTools/Notifier/NullNotifier.h index 7d3cc3d..8945a53 100644 --- a/SwifTools/Notifier/NullNotifier.h +++ b/SwifTools/Notifier/NullNotifier.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,11 +9,11 @@ #include <SwifTools/Notifier/Notifier.h> namespace Swift { - class NullNotifier : public Notifier { - public: - virtual void showMessage(Type, const std::string&, const std::string&, const boost::filesystem::path&, boost::function<void()>) { - } - virtual void purgeCallbacks() { - } - }; + class NullNotifier : public Notifier { + public: + virtual void showMessage(Type, const std::string&, const std::string&, const boost::filesystem::path&, boost::function<void()>) { + } + virtual void purgeCallbacks() { + } + }; } diff --git a/SwifTools/Notifier/SConscript b/SwifTools/Notifier/SConscript index 98b5400..b3c8115 100644 --- a/SwifTools/Notifier/SConscript +++ b/SwifTools/Notifier/SConscript @@ -3,19 +3,19 @@ Import("swiftools_env") myenv = swiftools_env.Clone() sources = [ - "Notifier.cpp", - ] + "Notifier.cpp", + ] if swiftools_env.get("HAVE_GROWL", False) : - sources += [ - "GrowlNotifier.mm", - "GrowlNotifierDelegate.mm", - ] -if swiftools_env.get("HAVE_SNARL", False) : - myenv.MergeFlags(myenv["SNARL_FLAGS"]) - sources += [ - "SnarlNotifier.cpp", - ] - + sources += [ + "GrowlNotifier.mm", + "GrowlNotifierDelegate.mm", + ] +elif myenv["PLATFORM"] == "darwin" and myenv["target"] == "native" : + sources += [ + "NotificationCenterNotifier.mm", + "NotificationCenterNotifierDelegate.mm", + ] + objects = myenv.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = objects) diff --git a/SwifTools/Notifier/SnarlNotifier.cpp b/SwifTools/Notifier/SnarlNotifier.cpp deleted file mode 100644 index 19d3622..0000000 --- a/SwifTools/Notifier/SnarlNotifier.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include <SwifTools/Notifier/SnarlNotifier.h> - -#include <cassert> -#include <iostream> -#include <boost/bind.hpp> - -#include <Swiften/Base/foreach.h> -#include <SwifTools/Notifier/Win32NotifierWindow.h> - -#define SWIFT_SNARLNOTIFIER_MESSAGE_ID 0x4567 // Sounds sick to pick a number, but this is windows - -namespace Swift { - -SnarlNotifier::SnarlNotifier(const std::string& name, Win32NotifierWindow* window, const boost::filesystem::path& icon) : window(window), available(false) { - window->onMessageReceived.connect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); - available = snarl.RegisterApp(name.c_str(), name.c_str(), icon.string().c_str(), window->getID(), SWIFT_SNARLNOTIFIER_MESSAGE_ID); - foreach(Notifier::Type type, getAllTypes()) { - snarl.AddClass(typeToString(type).c_str(), typeToString(type).c_str()); - } -} - -SnarlNotifier::~SnarlNotifier() { - snarl.UnregisterApp(); - window->onMessageReceived.disconnect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); - if (!notifications.empty()) { - std::cerr << "Warning: " << notifications.size() << " Snarl notifications pending" << std::endl; - } -} - -bool SnarlNotifier::isAvailable() const { - return available; -} - - -void SnarlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback) { - int timeout = (type == IncomingMessage || type == SystemMessage) ? DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS : DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; - int notificationID = snarl.EZNotify( - typeToString(type).c_str(), - subject.c_str(), - description.c_str(), - timeout, - picture.string().c_str()); - if (notificationID > 0) { - notifications.insert(std::make_pair(notificationID, callback)); - } -} - -void SnarlNotifier::handleMessageReceived(MSG* message) { - if (message->message == SWIFT_SNARLNOTIFIER_MESSAGE_ID) { - int action = message->wParam; - if (action == Snarl::V41::SnarlEnums::NotificationTimedOut || action == Snarl::V41::SnarlEnums::NotificationAck || action == Snarl::V41::SnarlEnums::NotificationClosed) { - int notificationID = message->lParam; - NotificationsMap::iterator i = notifications.find(notificationID); - if (i != notifications.end()) { - if (action == Snarl::V41::SnarlEnums::NotificationAck && !i->second.empty()) { - i->second(); - } - notifications.erase(i); - } - else { - std::cerr << "Warning: Orphaned Snarl notification received"; - } - } - } -} - -} diff --git a/SwifTools/Notifier/SnarlNotifier.h b/SwifTools/Notifier/SnarlNotifier.h deleted file mode 100644 index eb0eb5a..0000000 --- a/SwifTools/Notifier/SnarlNotifier.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include <map> - -#include <SwifTools/Notifier/Notifier.h> -#include <SnarlInterface.h> - -namespace Swift { - class Win32NotifierWindow; - - class SnarlNotifier : public Notifier { - public: - SnarlNotifier(const std::string& name, Win32NotifierWindow* window, const boost::filesystem::path& icon); - ~SnarlNotifier(); - - virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback); - virtual bool isAvailable() const; - - virtual void purgeCallbacks() { - notifications.clear(); - } - - private: - void handleMessageReceived(MSG* message); - - private: - Snarl::V41::SnarlInterface snarl; - Win32NotifierWindow* window; - bool available; - typedef std::map<int, boost::function<void()> > NotificationsMap; - NotificationsMap notifications; - }; -} diff --git a/SwifTools/Notifier/TogglableNotifier.h b/SwifTools/Notifier/TogglableNotifier.h index a4f0bb6..c537a6f 100644 --- a/SwifTools/Notifier/TogglableNotifier.h +++ b/SwifTools/Notifier/TogglableNotifier.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,54 +9,54 @@ #include <SwifTools/Notifier/Notifier.h> namespace Swift { - class TogglableNotifier : public Notifier { - public: - TogglableNotifier(Notifier* notifier) : notifier(notifier), persistentEnabled(true), temporarilyDisabled(false) { - } - - /** - * Set a long-term (usually user-set) enabled. - * This may be temporarily overriden by the application, e.g. if the - * user is marked DND. - */ - void setPersistentEnabled(bool b) { - persistentEnabled = b; - } - - /** - * Set a temporary override to stop notifications without changing the - * long-term state. e.g. if the user goes DND, but the persistent - * enabled shouldn't be lost when they become available again. - */ - void setTemporarilyDisabled(bool b) { - temporarilyDisabled = b; - } - - /** - * Get the result of applying the temporary override to the persistent - * enabledness. - */ - bool getCurrentlyEnabled() const { - return persistentEnabled && !temporarilyDisabled; - } - - virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback) { - if (getCurrentlyEnabled() || notifier->isExternallyConfigured()) { - notifier->showMessage(type, subject, description, picture, callback); - } - } - - virtual bool isExternallyConfigured() const { - return notifier->isExternallyConfigured(); - } - - virtual void purgeCallbacks() { - notifier->purgeCallbacks(); - } - - private: - Notifier* notifier; - bool persistentEnabled; - bool temporarilyDisabled; - }; + class TogglableNotifier : public Notifier { + public: + TogglableNotifier(Notifier* notifier) : notifier(notifier), persistentEnabled(true), temporarilyDisabled(false) { + } + + /** + * Set a long-term (usually user-set) enabled. + * This may be temporarily overriden by the application, e.g. if the + * user is marked DND. + */ + void setPersistentEnabled(bool b) { + persistentEnabled = b; + } + + /** + * Set a temporary override to stop notifications without changing the + * long-term state. e.g. if the user goes DND, but the persistent + * enabled shouldn't be lost when they become available again. + */ + void setTemporarilyDisabled(bool b) { + temporarilyDisabled = b; + } + + /** + * Get the result of applying the temporary override to the persistent + * enabledness. + */ + bool getCurrentlyEnabled() const { + return persistentEnabled && !temporarilyDisabled; + } + + virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback) { + if (getCurrentlyEnabled() || notifier->isExternallyConfigured()) { + notifier->showMessage(type, subject, description, picture, callback); + } + } + + virtual bool isExternallyConfigured() const { + return notifier->isExternallyConfigured(); + } + + virtual void purgeCallbacks() { + notifier->purgeCallbacks(); + } + + private: + Notifier* notifier; + bool persistentEnabled; + bool temporarilyDisabled; + }; } diff --git a/SwifTools/Notifier/Win32NotifierWindow.h b/SwifTools/Notifier/Win32NotifierWindow.h index 2279b0b..cff80ec 100644 --- a/SwifTools/Notifier/Win32NotifierWindow.h +++ b/SwifTools/Notifier/Win32NotifierWindow.h @@ -1,22 +1,22 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once //#include <windows.h> -#include <Swiften/Base/boost_bsignals.h> +#include <boost/signals2.hpp> namespace Swift { - class Win32NotifierWindow { - public: - virtual ~Win32NotifierWindow() {} + class Win32NotifierWindow { + public: + virtual ~Win32NotifierWindow() {} - virtual HWND getID() const = 0; + virtual HWND getID() const = 0; - boost::signal<void (MSG*)> onMessageReceived; - }; + boost::signals2::signal<void (MSG*)> onMessageReceived; + }; } diff --git a/SwifTools/SConscript b/SwifTools/SConscript index b9822e0..cf14b29 100644 --- a/SwifTools/SConscript +++ b/SwifTools/SConscript @@ -5,79 +5,80 @@ Import("env") ################################################################################ if env["SCONS_STAGE"] == "flags" : - env["SWIFTOOLS_FLAGS"] = { - "LIBPATH": [Dir(".")], - "LIBS": ["SwifTools"] - } + env["SWIFTOOLS_FLAGS"] = { + "LIBPATH": [Dir(".")], + "LIBS": ["SwifTools"] + } ################################################################################ # Build ################################################################################ if env["SCONS_STAGE"] == "build" : - swiftools_env = env.Clone() - swiftools_env.UseFlags(swiftools_env["SWIFTEN_FLAGS"]) - swiftools_env.UseFlags(swiftools_env["BOOST_FLAGS"]) + swiftools_env = env.Clone() + swiftools_env.UseFlags(swiftools_env["SWIFTEN_FLAGS"]) + swiftools_env.UseFlags(swiftools_env["BOOST_FLAGS"]) - sources = [ - "Idle/IdleDetector.cpp", - "Idle/ActualIdleDetector.cpp", - "Idle/IdleQuerier.cpp", - "Idle/PlatformIdleQuerier.cpp", - "AutoUpdater/AutoUpdater.cpp", - "AutoUpdater/PlatformAutoUpdaterFactory.cpp", - "Linkify.cpp", - "TabComplete.cpp", - "LastLineTracker.cpp", - ] - - if swiftools_env["HAVE_HUNSPELL"] : - swiftools_env.UseFlags(swiftools_env["HUNSPELL_FLAGS"]) - swiftools_env.Append(CPPDEFINES = ["HAVE_HUNSPELL"]) - sources += [ - "SpellCheckerFactory.cpp", - "HunspellChecker.cpp", - "SpellParser.cpp", - ] - elif swiftools_env["PLATFORM"] == "darwin" and env["target"] == "native" : - sources += [ - "SpellCheckerFactory.cpp", - "MacOSXChecker.mm", - "SpellParser.cpp", - ] + sources = [ + "EmojiMapper.cpp", + "Idle/IdleDetector.cpp", + "Idle/ActualIdleDetector.cpp", + "Idle/IdleQuerier.cpp", + "Idle/PlatformIdleQuerier.cpp", + "AutoUpdater/AutoUpdater.cpp", + "AutoUpdater/PlatformAutoUpdaterFactory.cpp", + "Linkify.cpp", + "TabComplete.cpp", + "LastLineTracker.cpp", + ] + if swiftools_env["HAVE_HUNSPELL"] : + swiftools_env.UseFlags(swiftools_env["HUNSPELL_FLAGS"]) + swiftools_env.Append(CPPDEFINES = ["HAVE_HUNSPELL"]) + sources += [ + "SpellCheckerFactory.cpp", + "HunspellChecker.cpp", + "SpellParser.cpp", + ] + elif swiftools_env["PLATFORM"] == "darwin" and env["target"] == "native" : + sources += [ + "SpellCheckerFactory.cpp", + "MacOSXChecker.mm", + "SpellParser.cpp", + ] - if swiftools_env.get("HAVE_SPARKLE", 0) : - swiftools_env.UseFlags(swiftools_env["SPARKLE_FLAGS"]) - swiftools_env.Append(CPPDEFINES = ["HAVE_SPARKLE"]) - sources += ["AutoUpdater/SparkleAutoUpdater.mm"] - if swiftools_env["PLATFORM"] == "win32" : - sources += ["Idle/WindowsIdleQuerier.cpp"] - elif swiftools_env["PLATFORM"] == "darwin" and swiftools_env.get("HAVE_IOKIT", False) : - swiftools_env.Append(CPPDEFINES = ["HAVE_IOKIT"]) - sources += ["Idle/MacOSXIdleQuerier.cpp"] - elif swiftools_env["HAVE_XSS"] : - swiftools_env.Append(CPPDEFINES = ["HAVE_XSS"]) - sources += ["Idle/XSSIdleQuerier.cpp"] + if swiftools_env.get("HAVE_SPARKLE", 0) : + swiftools_env.UseFlags(swiftools_env["SPARKLE_FLAGS"]) + swiftools_env.Append(CPPDEFINES = ["HAVE_SPARKLE"]) + sources += ["AutoUpdater/SparkleAutoUpdater.mm", "AutoUpdater/SparkleAutoUpdaterDelegate.mm"] - if env.get("HAVE_BREAKPAD", False) : - swiftools_env.UseFlags(swiftools_env["BREAKPAD_FLAGS"]) - swiftools_env.Append(CPPDEFINES = ["HAVE_BREAKPAD"]) - sources += ["CrashReporter.cpp"] - - swiftools_env["SWIFTOOLS_OBJECTS"] = [] - Export("swiftools_env") - - SConscript(dirs = [ - "Application", - "Dock", - "Notifier", - "URIHandler", - "Idle/IdleQuerierTest", - "Idle/UnitTest", - "Cocoa", - "UnitTest" - ]) + if swiftools_env["PLATFORM"] == "win32" : + sources += ["Idle/WindowsIdleQuerier.cpp"] + elif swiftools_env["PLATFORM"] == "darwin" and swiftools_env.get("HAVE_IOKIT", False) : + swiftools_env.Append(CPPDEFINES = ["HAVE_IOKIT"]) + sources += ["Idle/MacOSXIdleQuerier.cpp"] + elif swiftools_env["HAVE_XSS"] : + swiftools_env.Append(CPPDEFINES = ["HAVE_XSS"]) + sources += ["Idle/XSSIdleQuerier.cpp"] - swiftools_env.StaticLibrary("SwifTools", sources + swiftools_env["SWIFTOOLS_OBJECTS"]) + if env.get("HAVE_BREAKPAD", False) : + swiftools_env.UseFlags(swiftools_env["BREAKPAD_FLAGS"]) + swiftools_env.Append(CPPDEFINES = ["HAVE_BREAKPAD"]) + sources += ["CrashReporter.cpp"] + + swiftools_env["SWIFTOOLS_OBJECTS"] = [] + Export("swiftools_env") + + SConscript(dirs = [ + "Application", + "Dock", + "Notifier", + "URIHandler", + "Idle/IdleQuerierTest", + "Idle/UnitTest", + "Cocoa", + "UnitTest" + ]) + + swiftools_env.StaticLibrary("SwifTools", sources + swiftools_env["SWIFTOOLS_OBJECTS"]) diff --git a/SwifTools/SpellChecker.h b/SwifTools/SpellChecker.h index fd38418..664fc63 100644 --- a/SwifTools/SpellChecker.h +++ b/SwifTools/SpellChecker.h @@ -4,27 +4,38 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include <SwifTools/SpellParser.h> +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once -#include <boost/algorithm/string.hpp> -#include <boost/tuple/tuple.hpp> #include <vector> -#pragma once +#include <SwifTools/SpellParser.h> namespace Swift { - class SpellChecker { - public: - SpellChecker() { - parser_ = new SpellParser(); - } - virtual ~SpellChecker() { - delete parser_; - } - virtual bool isCorrect(const std::string& word) = 0; - virtual void getSuggestions(const std::string& word, std::vector<std::string>& list) = 0; - virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions) = 0; - protected: - SpellParser *parser_; - }; + class SpellChecker { + public: + SpellChecker() { + } + + virtual ~SpellChecker() { + } + + virtual bool isAutomaticallyDetectingLanguage() = 0; + + virtual void setActiveLanguage(const std::string& language) = 0; + virtual std::string activeLanguage() const = 0; + virtual std::vector<std::string> supportedLanguages() const = 0; + + virtual bool isCorrect(const std::string& word) = 0; + virtual void getSuggestions(const std::string& word, std::vector<std::string>& list) = 0; + virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions) = 0; + + protected: + SpellParser parser_; + }; } diff --git a/SwifTools/SpellCheckerFactory.cpp b/SwifTools/SpellCheckerFactory.cpp index 428e1a5..bfd3d4a 100644 --- a/SwifTools/SpellCheckerFactory.cpp +++ b/SwifTools/SpellCheckerFactory.cpp @@ -4,6 +4,12 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <boost/filesystem/operations.hpp> #include <SwifTools/SpellChecker.h> @@ -23,18 +29,12 @@ SpellCheckerFactory::SpellCheckerFactory() { } #ifdef HAVE_HUNSPELL -SpellChecker* SpellCheckerFactory::createSpellChecker(const std::string& dictFile) { - std::string affixFile(dictFile); - boost::replace_all(affixFile, ".dic", ".aff"); - if ((boost::filesystem::exists(dictFile)) && (boost::filesystem::exists(affixFile))) { - return new HunspellChecker(affixFile.c_str(), dictFile.c_str()); - } - // If dictionaries don't exist disable the checker - return NULL; +SpellChecker* SpellCheckerFactory::createSpellChecker() { + return new HunspellChecker(); } #elif defined(SWIFTEN_PLATFORM_MACOSX) -SpellChecker* SpellCheckerFactory::createSpellChecker(const std::string& /*dictFile*/) { - return new MacOSXChecker(); +SpellChecker* SpellCheckerFactory::createSpellChecker() { + return new MacOSXChecker(); } #endif diff --git a/SwifTools/SpellCheckerFactory.h b/SwifTools/SpellCheckerFactory.h index 91118f9..eb2ade6 100644 --- a/SwifTools/SpellCheckerFactory.h +++ b/SwifTools/SpellCheckerFactory.h @@ -4,8 +4,16 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #pragma once +#include <string> + #include <Swiften/Base/Platform.h> #ifdef HAVE_HUNSPELL @@ -15,10 +23,11 @@ #endif namespace Swift { - class SpellChecker; - class SpellCheckerFactory { - public: - SpellCheckerFactory(); - SpellChecker* createSpellChecker(const std::string& dictFile); - }; + class SpellChecker; + + class SpellCheckerFactory { + public: + SpellCheckerFactory(); + SpellChecker* createSpellChecker(); + }; } diff --git a/SwifTools/SpellParser.cpp b/SwifTools/SpellParser.cpp index 8cb42e4..e449b45 100644 --- a/SwifTools/SpellParser.cpp +++ b/SwifTools/SpellParser.cpp @@ -4,14 +4,19 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <SwifTools/SpellParser.h> -#include <boost/spirit/include/lex_lexertl.hpp> +#include <string> + #include <boost/bind.hpp> #include <boost/ref.hpp> -#include <boost/numeric/conversion/cast.hpp> - -#include <string> +#include <boost/spirit/include/lex_lexertl.hpp> namespace lex = boost::spirit::lex; @@ -20,51 +25,51 @@ namespace Swift { template <typename Lexer> struct word_count_tokens : lex::lexer<Lexer> { - word_count_tokens() - { - // define tokens (regular expresions) to match strings - // order is important - this->self.add - ("w{3}.[^ ]+", ID_WWW) - ("http:\\/\\/[^ ]+", ID_HTTP) - ("\\w{1,}['?|\\-?]?\\w{1,}", ID_WORD) - (".", ID_CHAR); - } + word_count_tokens() + { + // define tokens (regular expresions) to match strings + // order is important + this->self.add + ("w{3}.[^ ]+", ID_WWW) + ("http:\\/\\/[^ ]+", ID_HTTP) + ("\\w{1,}['?|\\-?]?\\w{1,}", ID_WORD) + (".", ID_CHAR); + } }; struct counter { - typedef bool result_type; - // the function operator gets called for each of the matched tokens - template <typename Token> - bool operator()(Token const& t, PositionPairList& wordPositions, std::size_t& position) const - { - switch (t.id()) { - case ID_WWW: - position += boost::numeric_cast<size_t>(t.value().size()); - break; - case ID_HTTP: - position += boost::numeric_cast<size_t>(t.value().size()); - break; - case ID_WORD: // matched a word - wordPositions.push_back(boost::tuples::make_tuple(position, position + boost::numeric_cast<size_t>(t.value().size()))); - position += boost::numeric_cast<size_t>(t.value().size()); - break; - case ID_CHAR: // match a simple char - ++position; - break; - } - return true; // always continue to tokenize - } + typedef bool result_type; + // the function operator gets called for each of the matched tokens + template <typename Token> + bool operator()(Token const& t, PositionPairList& wordPositions, std::size_t& position) const + { + switch (t.id()) { + case ID_WWW: + position += static_cast<std::size_t>(t.value().size()); + break; + case ID_HTTP: + position += static_cast<std::size_t>(t.value().size()); + break; + case ID_WORD: // matched a word + wordPositions.push_back(boost::tuples::make_tuple(position, position + static_cast<std::size_t>(t.value().size()))); + position += static_cast<std::size_t>(t.value().size()); + break; + case ID_CHAR: // match a simple char + ++position; + break; + } + return true; // always continue to tokenize + } }; void SpellParser::check(const std::string& fragment, PositionPairList& wordPositions) { - std::size_t position = 0; - // create the token definition instance needed to invoke the lexical analyzer - word_count_tokens<lex::lexertl::lexer<> > word_count_functor; - char const* first = fragment.c_str(); - char const* last = &first[fragment.size()]; - lex::tokenize(first, last, word_count_functor, boost::bind(counter(), _1, boost::ref(wordPositions), boost::ref(position))); + std::size_t position = 0; + // create the token definition instance needed to invoke the lexical analyzer + word_count_tokens<lex::lexertl::lexer<> > word_count_functor; + char const* first = fragment.c_str(); + char const* last = &first[fragment.size()]; + lex::tokenize(first, last, word_count_functor, boost::bind(counter(), _1, boost::ref(wordPositions), boost::ref(position))); } } diff --git a/SwifTools/SpellParser.h b/SwifTools/SpellParser.h index b64565c..3c1cf8b 100644 --- a/SwifTools/SpellParser.h +++ b/SwifTools/SpellParser.h @@ -4,28 +4,33 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #pragma once +#include <vector> + #include <boost/algorithm/string.hpp> #include <boost/tuple/tuple.hpp> -#include <boost/algorithm/string.hpp> - -#include <vector> namespace Swift { - enum token_ids - { - ID_WWW = 1, - ID_HTTP = 2, - ID_WORD = 3, - ID_CHAR = 4 - }; + enum token_ids + { + ID_WWW = 1, + ID_HTTP = 2, + ID_WORD = 3, + ID_CHAR = 4 + }; - typedef boost::tuple<int, int> PositionPair; - typedef std::vector<PositionPair > PositionPairList; + typedef boost::tuple<size_t, size_t> PositionPair; + typedef std::vector<PositionPair > PositionPairList; - class SpellParser{ - public: - void check(const std::string& fragment, PositionPairList& wordPositions); - }; + class SpellParser{ + public: + void check(const std::string& fragment, PositionPairList& wordPositions); + }; } diff --git a/SwifTools/TabComplete.cpp b/SwifTools/TabComplete.cpp index f4ef837..39a70cc 100644 --- a/SwifTools/TabComplete.cpp +++ b/SwifTools/TabComplete.cpp @@ -1,55 +1,54 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/TabComplete.h> #include <algorithm> -#include <boost/algorithm/string.hpp> -#include <Swiften/Base/foreach.h> +#include <boost/algorithm/string.hpp> namespace Swift { void TabComplete::addWord(const std::string& word) { - words_.erase(std::remove(words_.begin(), words_.end(), word), words_.end()); - words_.insert(words_.begin(), word); - if (boost::starts_with(boost::to_lower_copy(word), lastShort_)) { - lastCompletionCandidates_.insert(lastCompletionCandidates_.begin(), word); - } + words_.erase(std::remove(words_.begin(), words_.end(), word), words_.end()); + words_.insert(words_.begin(), word); + if (boost::starts_with(boost::to_lower_copy(word), lastShort_)) { + lastCompletionCandidates_.insert(lastCompletionCandidates_.begin(), word); + } } void TabComplete::removeWord(const std::string& word) { - words_.erase(std::remove(words_.begin(), words_.end(), word), words_.end()); - lastCompletionCandidates_.erase(std::remove(lastCompletionCandidates_.begin(), lastCompletionCandidates_.end(), word), lastCompletionCandidates_.end()); + words_.erase(std::remove(words_.begin(), words_.end(), word), words_.end()); + lastCompletionCandidates_.erase(std::remove(lastCompletionCandidates_.begin(), lastCompletionCandidates_.end(), word), lastCompletionCandidates_.end()); } std::string TabComplete::completeWord(const std::string& word) { - if (word == lastCompletion_) { - if (!lastCompletionCandidates_.empty()) { - size_t match = 0; - for (match = 0; match < lastCompletionCandidates_.size(); match++) { - if (lastCompletionCandidates_[match] == lastCompletion_) { - break; - } - } - size_t nextIndex = match + 1; - nextIndex = nextIndex >= lastCompletionCandidates_.size() ? 0 : nextIndex; - lastCompletion_ = lastCompletionCandidates_[nextIndex]; - } - } else { - lastShort_ = boost::to_lower_copy(word); - lastCompletionCandidates_.clear(); - foreach (std::string candidate, words_) { - if (boost::starts_with(boost::to_lower_copy(candidate), boost::to_lower_copy(word))) { - lastCompletionCandidates_.push_back(candidate); - } - } - lastCompletion_ = !lastCompletionCandidates_.empty() ? lastCompletionCandidates_[0] : word; - } - return lastCompletion_; + if (word == lastCompletion_) { + if (!lastCompletionCandidates_.empty()) { + size_t match = 0; + for (match = 0; match < lastCompletionCandidates_.size(); match++) { + if (lastCompletionCandidates_[match] == lastCompletion_) { + break; + } + } + size_t nextIndex = match + 1; + nextIndex = nextIndex >= lastCompletionCandidates_.size() ? 0 : nextIndex; + lastCompletion_ = lastCompletionCandidates_[nextIndex]; + } + } else { + lastShort_ = boost::to_lower_copy(word); + lastCompletionCandidates_.clear(); + for (auto&& candidate : words_) { + if (boost::starts_with(boost::to_lower_copy(candidate), boost::to_lower_copy(word))) { + lastCompletionCandidates_.push_back(candidate); + } + } + lastCompletion_ = !lastCompletionCandidates_.empty() ? lastCompletionCandidates_[0] : word; + } + return lastCompletion_; } } diff --git a/SwifTools/TabComplete.h b/SwifTools/TabComplete.h index d01174f..ac1e07e 100644 --- a/SwifTools/TabComplete.h +++ b/SwifTools/TabComplete.h @@ -1,25 +1,24 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <vector> - #include <string> +#include <vector> namespace Swift { - class TabComplete { - public: - void addWord(const std::string& word); - void removeWord(const std::string& word); - std::string completeWord(const std::string& word); - private: - std::vector<std::string> words_; - std::string lastCompletion_; - std::string lastShort_; - std::vector<std::string> lastCompletionCandidates_; - }; + class TabComplete { + public: + void addWord(const std::string& word); + void removeWord(const std::string& word); + std::string completeWord(const std::string& word); + private: + std::vector<std::string> words_; + std::string lastCompletion_; + std::string lastShort_; + std::vector<std::string> lastCompletionCandidates_; + }; } diff --git a/SwifTools/URIHandler/MacOSXURIHandler.h b/SwifTools/URIHandler/MacOSXURIHandler.h index f803420..8136fa7 100644 --- a/SwifTools/URIHandler/MacOSXURIHandler.h +++ b/SwifTools/URIHandler/MacOSXURIHandler.h @@ -1,24 +1,26 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <memory> + #include <SwifTools/URIHandler/URIHandler.h> namespace Swift { - class MacOSXURIHandler : public URIHandler { - public: - MacOSXURIHandler(); - virtual ~MacOSXURIHandler(); + class MacOSXURIHandler : public URIHandler { + public: + MacOSXURIHandler(); + virtual ~MacOSXURIHandler(); - virtual void start(); - virtual void stop(); + virtual void start(); + virtual void stop(); - private: - class Private; - Private* p; - }; + private: + class Private; + const std::unique_ptr<Private> p; + }; } diff --git a/SwifTools/URIHandler/MacOSXURIHandler.mm b/SwifTools/URIHandler/MacOSXURIHandler.mm index cdfba33..ae7dc44 100644 --- a/SwifTools/URIHandler/MacOSXURIHandler.mm +++ b/SwifTools/URIHandler/MacOSXURIHandler.mm @@ -1,14 +1,15 @@ /* - * Copyright (c) 2011-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/URIHandler/MacOSXURIHandler.h> -#include <Cocoa/Cocoa.h> #include <iostream> +#include <Cocoa/Cocoa.h> + using namespace Swift; @interface MacOSXURIEventHandler : NSObject { @@ -20,47 +21,45 @@ using namespace Swift; @end @implementation MacOSXURIEventHandler - { - URIHandler* handler; - } - - - (id) initWithHandler: (URIHandler*) h { - if ((self = [super init])) { - handler = h; - } - return self; - } - - - (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent { - (void) replyEvent; - NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; - handler->onURI(std::string([url UTF8String])); - } + { + URIHandler* handler; + } + + - (id) initWithHandler: (URIHandler*) h { + if ((self = [super init])) { + handler = h; + } + return self; + } + + - (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent { + (void) replyEvent; + NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + handler->onURI(std::string([url UTF8String])); + } @end class MacOSXURIHandler::Private { - public: - MacOSXURIEventHandler* eventHandler; + public: + MacOSXURIEventHandler* eventHandler; }; -MacOSXURIHandler::MacOSXURIHandler() { - p = new Private(); - p->eventHandler = [[MacOSXURIEventHandler alloc] initWithHandler: this]; +MacOSXURIHandler::MacOSXURIHandler() : p(new Private()) { + p->eventHandler = [[MacOSXURIEventHandler alloc] initWithHandler: this]; } MacOSXURIHandler::~MacOSXURIHandler() { - [p->eventHandler release]; - delete p; + [p->eventHandler release]; } void MacOSXURIHandler::start() { - [[NSAppleEventManager sharedAppleEventManager] setEventHandler:p->eventHandler andSelector:@selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; + [[NSAppleEventManager sharedAppleEventManager] setEventHandler:p->eventHandler andSelector:@selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; - // Register ourselves as default URI handler - //NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier]; - //LSSetDefaultHandlerForURLScheme((CFStringRef)@"xmpp", (CFStringRef)bundleID); + // Register ourselves as default URI handler + //NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier]; + //LSSetDefaultHandlerForURLScheme((CFStringRef)@"xmpp", (CFStringRef)bundleID); } void MacOSXURIHandler::stop() { - [[NSAppleEventManager sharedAppleEventManager] removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; + [[NSAppleEventManager sharedAppleEventManager] removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; } diff --git a/SwifTools/URIHandler/NullURIHandler.h b/SwifTools/URIHandler/NullURIHandler.h index 28c35bb..99c01e8 100644 --- a/SwifTools/URIHandler/NullURIHandler.h +++ b/SwifTools/URIHandler/NullURIHandler.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,12 +9,12 @@ #include <SwifTools/URIHandler/URIHandler.h> namespace Swift { - class NullURIHandler : public URIHandler { - public: - virtual void start() { - } + class NullURIHandler : public URIHandler { + public: + virtual void start() { + } - virtual void stop() { - } - }; + virtual void stop() { + } + }; } diff --git a/SwifTools/URIHandler/SConscript b/SwifTools/URIHandler/SConscript index 90ec00a..8ebbdaf 100644 --- a/SwifTools/URIHandler/SConscript +++ b/SwifTools/URIHandler/SConscript @@ -1,22 +1,22 @@ Import("swiftools_env", "env") sources = [ - "XMPPURI.cpp", - "URIHandler.cpp", - ] + "XMPPURI.cpp", + "URIHandler.cpp", + ] if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : - sources += [ - "MacOSXURIHandler.mm" - ] + sources += [ + "MacOSXURIHandler.mm" + ] elif swiftools_env["PLATFORM"] == "win32" : - sources += [] + sources += [] else : - sources += [] + sources += [] objects = swiftools_env.StaticObject(sources) swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) env.Append(UNITTEST_SOURCES = [ - File("UnitTest/XMPPURITest.cpp"), - ]) + File("UnitTest/XMPPURITest.cpp"), + ]) diff --git a/SwifTools/URIHandler/URIHandler.cpp b/SwifTools/URIHandler/URIHandler.cpp index 91e54e5..5dc3f29 100644 --- a/SwifTools/URIHandler/URIHandler.cpp +++ b/SwifTools/URIHandler/URIHandler.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/URIHandler/URIHandler.h> diff --git a/SwifTools/URIHandler/URIHandler.h b/SwifTools/URIHandler/URIHandler.h index 9dd13a8..8aeb11a 100644 --- a/SwifTools/URIHandler/URIHandler.h +++ b/SwifTools/URIHandler/URIHandler.h @@ -1,20 +1,21 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once #include <string> -#include <Swiften/Base/boost_bsignals.h> + +#include <boost/signals2.hpp> namespace Swift { - class URIHandler { - public: - URIHandler(); - virtual ~URIHandler(); + class URIHandler { + public: + URIHandler(); + virtual ~URIHandler(); - boost::signal<void (const std::string&)> onURI; - }; + boost::signals2::signal<void (const std::string&)> onURI; + }; } diff --git a/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp index 35020da..aa0570c 100644 --- a/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp +++ b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <cppunit/extensions/HelperMacros.h> @@ -12,180 +12,180 @@ using namespace Swift; class XMPPURITest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(XMPPURITest); - CPPUNIT_TEST(testFromString_Authority); - CPPUNIT_TEST(testFromString_AuthorityWithPath); - CPPUNIT_TEST(testFromString_AuthorityWithFragment); - CPPUNIT_TEST(testFromString_AuthorityWithPathAndFragment); - CPPUNIT_TEST(testFromString_AuthorityWithIntlChars); - CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParameters); - CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParameters); - CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParametersWithFragment); - CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParametersWithFragment); - CPPUNIT_TEST(testFromString_Path); - CPPUNIT_TEST(testFromString_PathWithFragment); - CPPUNIT_TEST(testFromString_PathWithIntlChars); - CPPUNIT_TEST(testFromString_PathWithInvalidEscapedChar); - CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar); - CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar2); - CPPUNIT_TEST(testFromString_PathWithQueryWithoutParameters); - CPPUNIT_TEST(testFromString_PathWithQueryWithParameters); - CPPUNIT_TEST(testFromString_PathWithQueryWithoutParametersWithFragment); - CPPUNIT_TEST(testFromString_PathWithQueryWithParametersWithFragment); - CPPUNIT_TEST(testFromString_NoPrefix); - CPPUNIT_TEST_SUITE_END(); - - public: - void testFromString_Authority() { - XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com"); - - CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); - } - - void testFromString_AuthorityWithPath() { - XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com"); - - CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); - CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); - } - - void testFromString_AuthorityWithFragment() { - XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com#myfragment"); - - CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); - CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); - } - - void testFromString_AuthorityWithPathAndFragment() { - XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com#myfragment"); - - CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); - CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); - CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); - } - - void testFromString_AuthorityWithIntlChars() { - XMPPURI testling = XMPPURI::fromString("xmpp://nasty!%23$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com"); - - CPPUNIT_ASSERT_EQUAL(JID("nasty!#$%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getAuthority()); - } - - void testFromString_AuthorityWithQueryWithoutParameters() { - XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message"); - - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - } - - void testFromString_AuthorityWithQueryWithParameters() { - XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); - - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); - CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); - } - - void testFromString_AuthorityWithQueryWithoutParametersWithFragment() { - XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message#myfragment"); - - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); - } - - void testFromString_AuthorityWithQueryWithParametersWithFragment() { - XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); - - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); - CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); - CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); - } - - void testFromString_Path() { - XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com"); - - CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); - } - - void testFromString_PathWithFragment() { - XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com#myfragment"); - - CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); - CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); - } - - void testFromString_PathWithIntlChars() { - XMPPURI testling = XMPPURI::fromString("xmpp:nasty!%23$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com"); + CPPUNIT_TEST_SUITE(XMPPURITest); + CPPUNIT_TEST(testFromString_Authority); + CPPUNIT_TEST(testFromString_AuthorityWithPath); + CPPUNIT_TEST(testFromString_AuthorityWithFragment); + CPPUNIT_TEST(testFromString_AuthorityWithPathAndFragment); + CPPUNIT_TEST(testFromString_AuthorityWithIntlChars); + CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParameters); + CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParameters); + CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParametersWithFragment); + CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParametersWithFragment); + CPPUNIT_TEST(testFromString_Path); + CPPUNIT_TEST(testFromString_PathWithFragment); + CPPUNIT_TEST(testFromString_PathWithIntlChars); + CPPUNIT_TEST(testFromString_PathWithInvalidEscapedChar); + CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar); + CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar2); + CPPUNIT_TEST(testFromString_PathWithQueryWithoutParameters); + CPPUNIT_TEST(testFromString_PathWithQueryWithParameters); + CPPUNIT_TEST(testFromString_PathWithQueryWithoutParametersWithFragment); + CPPUNIT_TEST(testFromString_PathWithQueryWithParametersWithFragment); + CPPUNIT_TEST(testFromString_NoPrefix); + CPPUNIT_TEST_SUITE_END(); + + public: + void testFromString_Authority() { + XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com"); + + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); + } + + void testFromString_AuthorityWithPath() { + XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com"); + + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); + } + + void testFromString_AuthorityWithFragment() { + XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com#myfragment"); + + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); + } + + void testFromString_AuthorityWithPathAndFragment() { + XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com#myfragment"); + + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); + } + + void testFromString_AuthorityWithIntlChars() { + XMPPURI testling = XMPPURI::fromString("xmpp://nasty!%23$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com"); + + CPPUNIT_ASSERT_EQUAL(JID("nasty!#$%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getAuthority()); + } + + void testFromString_AuthorityWithQueryWithoutParameters() { + XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message"); + + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + } + + void testFromString_AuthorityWithQueryWithParameters() { + XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); + + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); + CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); + } + + void testFromString_AuthorityWithQueryWithoutParametersWithFragment() { + XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message#myfragment"); + + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); + } + + void testFromString_AuthorityWithQueryWithParametersWithFragment() { + XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); + + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); + CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); + CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); + } + + void testFromString_Path() { + XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com"); + + CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); + } + + void testFromString_PathWithFragment() { + XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com#myfragment"); + + CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); + } + + void testFromString_PathWithIntlChars() { + XMPPURI testling = XMPPURI::fromString("xmpp:nasty!%23$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com"); - CPPUNIT_ASSERT_EQUAL(JID("nasty!#$%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getPath()); - } + CPPUNIT_ASSERT_EQUAL(JID("nasty!#$%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getPath()); + } - void testFromString_PathWithInvalidEscapedChar() { - XMPPURI testling = XMPPURI::fromString("xmpp:test%%@example.com"); + void testFromString_PathWithInvalidEscapedChar() { + XMPPURI testling = XMPPURI::fromString("xmpp:test%%@example.com"); - CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); - } + CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); + } - void testFromString_PathWithIncompleteEscapedChar() { - XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%"); + void testFromString_PathWithIncompleteEscapedChar() { + XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%"); - CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); - } + CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); + } - void testFromString_PathWithIncompleteEscapedChar2() { - XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%1"); + void testFromString_PathWithIncompleteEscapedChar2() { + XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%1"); - CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); - } + CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); + } - void testFromString_PathWithQueryWithoutParameters() { - XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message"); + void testFromString_PathWithQueryWithoutParameters() { + XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message"); - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - } + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + } - void testFromString_PathWithQueryWithParameters() { - XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); + void testFromString_PathWithQueryWithParameters() { + XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); - CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); - } + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); + CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); + } - void testFromString_PathWithQueryWithoutParametersWithFragment() { - XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message#myfragment"); + void testFromString_PathWithQueryWithoutParametersWithFragment() { + XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message#myfragment"); - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); - } + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); + } - void testFromString_PathWithQueryWithParametersWithFragment() { - XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); + void testFromString_PathWithQueryWithParametersWithFragment() { + XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); - CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); - CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); - CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); - CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); - CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); - } + CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); + CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); + CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); + CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); + CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); + } - void testFromString_NoPrefix() { - XMPPURI testling = XMPPURI::fromString("baz@example.com"); + void testFromString_NoPrefix() { + XMPPURI testling = XMPPURI::fromString("baz@example.com"); - CPPUNIT_ASSERT(testling.isNull()); - } + CPPUNIT_ASSERT(testling.isNull()); + } - private: - std::string get(const std::map<std::string, std::string>& m, const std::string& k) { - std::map<std::string, std::string>::const_iterator i = m.find(k); - return i == m.end() ? "" : i->second; - } + private: + std::string get(const std::map<std::string, std::string>& m, const std::string& k) { + std::map<std::string, std::string>::const_iterator i = m.find(k); + return i == m.end() ? "" : i->second; + } }; CPPUNIT_TEST_SUITE_REGISTRATION(XMPPURITest); diff --git a/SwifTools/URIHandler/XMPPURI.cpp b/SwifTools/URIHandler/XMPPURI.cpp index cf99ae6..8b8b81c 100644 --- a/SwifTools/URIHandler/XMPPURI.cpp +++ b/SwifTools/URIHandler/XMPPURI.cpp @@ -1,22 +1,24 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <SwifTools/URIHandler/XMPPURI.h> -#include <Swiften/Base/URL.h> -#include <boost/algorithm/string/predicate.hpp> -#include <boost/algorithm/string/find_format.hpp> -#include <boost/algorithm/string/formatter.hpp> -#include <boost/algorithm/string/find_iterator.hpp> -#include <boost/algorithm/string/split.hpp> -#include <boost/algorithm/string/classification.hpp> #include <sstream> #include <stdexcept> #include <vector> +#include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/find_format.hpp> +#include <boost/algorithm/string/find_iterator.hpp> +#include <boost/algorithm/string/formatter.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/split.hpp> + +#include <Swiften/Base/URL.h> + using namespace Swift; @@ -24,77 +26,77 @@ XMPPURI::XMPPURI() { } XMPPURI XMPPURI::fromString(const std::string& s) { - XMPPURI result; - if (boost::starts_with(s, "xmpp:")) { - std::string uri = s.substr(5, s.npos); - bool parsePath = true; - bool parseQuery = true; - bool parseFragment = true; + XMPPURI result; + if (boost::starts_with(s, "xmpp:")) { + std::string uri = s.substr(5, s.npos); + bool parsePath = true; + bool parseQuery = true; + bool parseFragment = true; - // Parse authority - if (boost::starts_with(uri, "//")) { - size_t i = uri.find_first_of("/#?", 2); - result.setAuthority(JID(URL::unescape(uri.substr(2, i - 2)))); - if (i == uri.npos) { - uri = ""; - parsePath = parseQuery = parseFragment = false; - } - else { - if (uri[i] == '?') { - parsePath = false; - } - else if (uri[i] == '#') { - parseQuery = parsePath = false; - } - uri = uri.substr(i + 1, uri.npos); - } - } + // Parse authority + if (boost::starts_with(uri, "//")) { + size_t i = uri.find_first_of("/#?", 2); + result.setAuthority(JID(URL::unescape(uri.substr(2, i - 2)))); + if (i == uri.npos) { + uri = ""; + parsePath = parseQuery = parseFragment = false; + } + else { + if (uri[i] == '?') { + parsePath = false; + } + else if (uri[i] == '#') { + parseQuery = parsePath = false; + } + uri = uri.substr(i + 1, uri.npos); + } + } - // Parse path - if (parsePath) { - size_t i = uri.find_first_of("#?"); - result.setPath(JID(URL::unescape(uri.substr(0, i)))); - if (i == uri.npos) { - uri = ""; - parseQuery = parseFragment = false; - } - else { - if (uri[i] == '#') { - parseQuery = false; - } - uri = uri.substr(i + 1, uri.npos); - } - } + // Parse path + if (parsePath) { + size_t i = uri.find_first_of("#?"); + result.setPath(JID(URL::unescape(uri.substr(0, i)))); + if (i == uri.npos) { + uri = ""; + parseQuery = parseFragment = false; + } + else { + if (uri[i] == '#') { + parseQuery = false; + } + uri = uri.substr(i + 1, uri.npos); + } + } - // Parse query - if (parseQuery) { - size_t end = uri.find_first_of("#"); - std::string query = uri.substr(0, end); - bool haveType = false; - typedef boost::split_iterator<std::string::iterator> split_iterator; - for (split_iterator it = boost::make_split_iterator(query, boost::first_finder(";")); it != split_iterator(); ++it) { - if (haveType) { - std::vector<std::string> keyValue; - boost::split(keyValue, *it, boost::is_any_of("=")); - if (keyValue.size() == 1) { - result.addQueryParameter(URL::unescape(keyValue[0]), ""); - } - else if (keyValue.size() >= 2) { - result.addQueryParameter(URL::unescape(keyValue[0]), URL::unescape(keyValue[1])); - } - } - else { - result.setQueryType(URL::unescape(boost::copy_range<std::string>(*it))); - haveType = true; - } - } - uri = (end == uri.npos ? "" : uri.substr(end + 1, uri.npos)); - } + // Parse query + if (parseQuery) { + size_t end = uri.find_first_of("#"); + std::string query = uri.substr(0, end); + bool haveType = false; + typedef boost::split_iterator<std::string::iterator> split_iterator; + for (split_iterator it = boost::make_split_iterator(query, boost::first_finder(";")); it != split_iterator(); ++it) { + if (haveType) { + std::vector<std::string> keyValue; + boost::split(keyValue, *it, boost::is_any_of("=")); + if (keyValue.size() == 1) { + result.addQueryParameter(URL::unescape(keyValue[0]), ""); + } + else if (keyValue.size() >= 2) { + result.addQueryParameter(URL::unescape(keyValue[0]), URL::unescape(keyValue[1])); + } + } + else { + result.setQueryType(URL::unescape(boost::copy_range<std::string>(*it))); + haveType = true; + } + } + uri = (end == uri.npos ? "" : uri.substr(end + 1, uri.npos)); + } - // Parse fragment - if (parseFragment) { - result.setFragment(URL::unescape(uri)); - } - } - return result; + // Parse fragment + if (parseFragment) { + result.setFragment(URL::unescape(uri)); + } + } + return result; } diff --git a/SwifTools/URIHandler/XMPPURI.h b/SwifTools/URIHandler/XMPPURI.h index 36bfc41..a8c9f95 100644 --- a/SwifTools/URIHandler/XMPPURI.h +++ b/SwifTools/URIHandler/XMPPURI.h @@ -1,73 +1,73 @@ /* - * Copyright (c) 2011 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2011-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <string> #include <map> +#include <string> #include <Swiften/JID/JID.h> namespace Swift { - // TODO: Implement using Base/URI - class XMPPURI { - public: - XMPPURI(); - - const JID& getAuthority() const { - return authority; - } - - void setAuthority(const JID& j) { - authority = j; - } - - const JID& getPath() const { - return path; - } - - void setPath(const JID& j) { - path = j; - } - - const std::string& getQueryType() const { - return queryType; - } - - void setQueryType(const std::string& q) { - queryType = q; - } - - const std::map<std::string, std::string>& getQueryParameters() const { - return queryParameters; - } - - void addQueryParameter(const std::string& key, const std::string& path) { - queryParameters[key] = path; - } - - const std::string& getFragment() const { - return fragment; - } - - void setFragment(const std::string& f) { - fragment = f; - } - - bool isNull() const { - return !authority.isValid() && !path.isValid(); - } - - static XMPPURI fromString(const std::string&); - - private: - JID authority; - JID path; - std::string fragment; - std::string queryType; - std::map<std::string, std::string> queryParameters; - }; + // TODO: Implement using Base/URI + class XMPPURI { + public: + XMPPURI(); + + const JID& getAuthority() const { + return authority; + } + + void setAuthority(const JID& j) { + authority = j; + } + + const JID& getPath() const { + return path; + } + + void setPath(const JID& j) { + path = j; + } + + const std::string& getQueryType() const { + return queryType; + } + + void setQueryType(const std::string& q) { + queryType = q; + } + + const std::map<std::string, std::string>& getQueryParameters() const { + return queryParameters; + } + + void addQueryParameter(const std::string& key, const std::string& path) { + queryParameters[key] = path; + } + + const std::string& getFragment() const { + return fragment; + } + + void setFragment(const std::string& f) { + fragment = f; + } + + bool isNull() const { + return !authority.isValid() && !path.isValid(); + } + + static XMPPURI fromString(const std::string&); + + private: + JID authority; + JID path; + std::string fragment; + std::string queryType; + std::map<std::string, std::string> queryParameters; + }; } diff --git a/SwifTools/UnitTest/LastLineTrackerTest.cpp b/SwifTools/UnitTest/LastLineTrackerTest.cpp index 97790e5..0fc77b6 100644 --- a/SwifTools/UnitTest/LastLineTrackerTest.cpp +++ b/SwifTools/UnitTest/LastLineTrackerTest.cpp @@ -12,53 +12,53 @@ using namespace Swift; class LastLineTrackerTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(LastLineTrackerTest); - CPPUNIT_TEST(testFocusNormal); - CPPUNIT_TEST(testFocusOut); - CPPUNIT_TEST(testFocusOtherTab); - CPPUNIT_TEST(testRepeatedFocusOut); - CPPUNIT_TEST(testRepeatedFocusIn); - CPPUNIT_TEST_SUITE_END(); - public: - LastLineTrackerTest () { - } - void testFocusNormal() { - LastLineTracker testling; - testling.setHasFocus(true); - CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); - } - void testFocusOut() { - LastLineTracker testling; - testling.setHasFocus(false); - CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); - CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); - CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); - } - void testFocusOtherTab() { - LastLineTracker testling; - testling.setHasFocus(true); - testling.setHasFocus(false); - CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); - CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); - } + CPPUNIT_TEST_SUITE(LastLineTrackerTest); + CPPUNIT_TEST(testFocusNormal); + CPPUNIT_TEST(testFocusOut); + CPPUNIT_TEST(testFocusOtherTab); + CPPUNIT_TEST(testRepeatedFocusOut); + CPPUNIT_TEST(testRepeatedFocusIn); + CPPUNIT_TEST_SUITE_END(); + public: + LastLineTrackerTest () { + } + void testFocusNormal() { + LastLineTracker testling; + testling.setHasFocus(true); + CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); + } + void testFocusOut() { + LastLineTracker testling; + testling.setHasFocus(false); + CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); + CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); + CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); + } + void testFocusOtherTab() { + LastLineTracker testling; + testling.setHasFocus(true); + testling.setHasFocus(false); + CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); + CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); + } - void testRepeatedFocusOut() { - LastLineTracker testling; - testling.setHasFocus(true); - CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); - testling.setHasFocus(false); - CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); - testling.setHasFocus(false); - CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); - } - void testRepeatedFocusIn() { - LastLineTracker testling; - testling.setHasFocus(false); - CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); - testling.setHasFocus(true); - testling.setHasFocus(false); - CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); - } + void testRepeatedFocusOut() { + LastLineTracker testling; + testling.setHasFocus(true); + CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); + testling.setHasFocus(false); + CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); + testling.setHasFocus(false); + CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); + } + void testRepeatedFocusIn() { + LastLineTracker testling; + testling.setHasFocus(false); + CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); + testling.setHasFocus(true); + testling.setHasFocus(false); + CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(LastLineTrackerTest); diff --git a/SwifTools/UnitTest/LinkifyTest.cpp b/SwifTools/UnitTest/LinkifyTest.cpp index c464b50..69a0e23 100644 --- a/SwifTools/UnitTest/LinkifyTest.cpp +++ b/SwifTools/UnitTest/LinkifyTest.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2013 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <cppunit/extensions/HelperMacros.h> @@ -12,230 +12,230 @@ using namespace Swift; class LinkifyTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(LinkifyTest); - CPPUNIT_TEST(testLinkify_URLWithResource); - CPPUNIT_TEST(testLinkify_HTTPSURLWithResource); - CPPUNIT_TEST(testLinkify_URLWithEmptyResource); - CPPUNIT_TEST(testLinkify_BareURL); - CPPUNIT_TEST(testLinkify_URLSurroundedByWhitespace); - CPPUNIT_TEST(testLinkify_MultipleURLs); - CPPUNIT_TEST(testLinkify_CamelCase); - CPPUNIT_TEST(testLinkify_HierarchicalResource); - CPPUNIT_TEST(testLinkify_Anchor); - CPPUNIT_TEST(testLinkify_Plus); - CPPUNIT_TEST(testLinkify_Tilde); - CPPUNIT_TEST(testLinkify_Equal); - CPPUNIT_TEST(testLinkify_Authentication); - CPPUNIT_TEST(testLinkify_At); - CPPUNIT_TEST(testLinkify_Amps); - CPPUNIT_TEST(testLinkify_UnicodeCharacter); - CPPUNIT_TEST(testLinkify_NewLine); - CPPUNIT_TEST(testLinkify_Tab); - CPPUNIT_TEST(testLinkify_Action); - - CPPUNIT_TEST(testLinkify_SplitNone); - CPPUNIT_TEST(testLinkify_SplitAll); - CPPUNIT_TEST(testLinkify_SplitFirst); - CPPUNIT_TEST(testLinkify_SplitSecond); - CPPUNIT_TEST(testLinkify_SplitMiddle); - CPPUNIT_TEST_SUITE_END(); - - public: - void testLinkify_URLWithResource() { - std::string result = Linkify::linkify("http://swift.im/blog"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://swift.im/blog\">http://swift.im/blog</a>"), - result); - } - - void testLinkify_HTTPSURLWithResource() { - std::string result = Linkify::linkify("https://swift.im/blog"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"https://swift.im/blog\">https://swift.im/blog</a>"), - result); - } - - void testLinkify_URLWithEmptyResource() { - std::string result = Linkify::linkify("http://swift.im/"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://swift.im/\">http://swift.im/</a>"), - result); - } - - - void testLinkify_BareURL() { - std::string result = Linkify::linkify("http://swift.im"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://swift.im\">http://swift.im</a>"), - result); - } - - void testLinkify_URLSurroundedByWhitespace() { - std::string result = Linkify::linkify("Foo http://swift.im/blog Bar"); - - CPPUNIT_ASSERT_EQUAL( - std::string("Foo <a href=\"http://swift.im/blog\">http://swift.im/blog</a> Bar"), - result); - } - - void testLinkify_MultipleURLs() { - std::string result = Linkify::linkify("Foo http://swift.im/blog Bar http://el-tramo.be/about Baz"); - - CPPUNIT_ASSERT_EQUAL( - std::string("Foo <a href=\"http://swift.im/blog\">http://swift.im/blog</a> Bar <a href=\"http://el-tramo.be/about\">http://el-tramo.be/about</a> Baz"), - result); - } - - void testLinkify_CamelCase() { - std::string result = Linkify::linkify("http://fOo.cOm/bAz"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://fOo.cOm/bAz\">http://fOo.cOm/bAz</a>"), - result); - } - - void testLinkify_HierarchicalResource() { - std::string result = Linkify::linkify("http://foo.com/bar/baz/"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://foo.com/bar/baz/\">http://foo.com/bar/baz/</a>"), - result); - } - - void testLinkify_Anchor() { - std::string result = Linkify::linkify("http://foo.com/bar#baz"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://foo.com/bar#baz\">http://foo.com/bar#baz</a>"), - result); - } - - void testLinkify_Plus() { - std::string result = Linkify::linkify("http://foo.com/bar+baz"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://foo.com/bar+baz\">http://foo.com/bar+baz</a>"), - result); - } - - void testLinkify_Tilde() { - std::string result = Linkify::linkify("http://foo.com/~kev/"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://foo.com/~kev/\">http://foo.com/~kev/</a>"), - result); - } - - void testLinkify_Equal() { - std::string result = Linkify::linkify("http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0\">http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0</a>"), - result); - } - - void testLinkify_Authentication() { - std::string result = Linkify::linkify("http://bob:bla@swift.im/foo/bar"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://bob:bla@swift.im/foo/bar\">http://bob:bla@swift.im/foo/bar</a>"), - result); - } - - void testLinkify_At() { - std::string result = Linkify::linkify("http://swift.im/foo@bar"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://swift.im/foo@bar\">http://swift.im/foo@bar</a>"), - result); - } - - void testLinkify_Amps() { - std::string result = Linkify::linkify("http://swift.im/foo&bar&baz"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://swift.im/foo&bar&baz\">http://swift.im/foo&bar&baz</a>"), - result); - } - - void testLinkify_UnicodeCharacter() { - std::string result = Linkify::linkify("http://\xe2\x98\x83.net"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://\xe2\x98\x83.net\">http://\xe2\x98\x83.net</a>"), - result); - } - - void testLinkify_NewLine() { - std::string result = Linkify::linkify("http://swift.im\nfoo"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://swift.im\">http://swift.im</a>\nfoo"), - result); - } - - void testLinkify_Tab() { - std::string result = Linkify::linkify("http://swift.im\tfoo"); - - CPPUNIT_ASSERT_EQUAL( - std::string("<a href=\"http://swift.im\">http://swift.im</a>\tfoo"), - result); - } - - void testLinkify_Action() { - std::string result = Linkify::linkify("*http://swift.im*"); - - CPPUNIT_ASSERT_EQUAL( - std::string("*<a href=\"http://swift.im\">http://swift.im</a>*"), - result); - } - - void checkResult(const std::string& testling, size_t expectedIndex, std::string expectedSplit[]) { - std::pair<std::vector<std::string>, size_t> result = Linkify::splitLink(testling); - CPPUNIT_ASSERT_EQUAL(expectedIndex, result.second); - for (size_t i = 0; i < result.first.size(); i++) { - CPPUNIT_ASSERT_EQUAL(expectedSplit[i], result.first[i]); - } - } - - void testLinkify_SplitNone() { - std::string testling = "http this ain't"; - size_t expectedIndex = 1; - std::string expectedSplit[] = {"http this ain't"}; - checkResult(testling, expectedIndex, expectedSplit); - } - - void testLinkify_SplitAll() { - std::string testling = "http://swift.im"; - size_t expectedIndex = 0; - std::string expectedSplit[] = {"http://swift.im"}; - checkResult(testling, expectedIndex, expectedSplit); - } - - void testLinkify_SplitFirst() { - std::string testling = "http://swift.im is a link"; - size_t expectedIndex = 0; - std::string expectedSplit[] = {"http://swift.im", " is a link"}; - checkResult(testling, expectedIndex, expectedSplit); - } - - void testLinkify_SplitSecond() { - std::string testling = "this is a link: http://swift.im"; - size_t expectedIndex = 1; - std::string expectedSplit[] = {"this is a link: ", "http://swift.im"}; - checkResult(testling, expectedIndex, expectedSplit); - } - - void testLinkify_SplitMiddle() { - std::string testling = "Shove a link like http://swift.im in the middle"; - size_t expectedIndex = 1; - std::string expectedSplit[] = {"Shove a link like ","http://swift.im", " in the middle"}; - checkResult(testling, expectedIndex, expectedSplit); - } + CPPUNIT_TEST_SUITE(LinkifyTest); + CPPUNIT_TEST(testLinkify_URLWithResource); + CPPUNIT_TEST(testLinkify_HTTPSURLWithResource); + CPPUNIT_TEST(testLinkify_URLWithEmptyResource); + CPPUNIT_TEST(testLinkify_BareURL); + CPPUNIT_TEST(testLinkify_URLSurroundedByWhitespace); + CPPUNIT_TEST(testLinkify_MultipleURLs); + CPPUNIT_TEST(testLinkify_CamelCase); + CPPUNIT_TEST(testLinkify_HierarchicalResource); + CPPUNIT_TEST(testLinkify_Anchor); + CPPUNIT_TEST(testLinkify_Plus); + CPPUNIT_TEST(testLinkify_Tilde); + CPPUNIT_TEST(testLinkify_Equal); + CPPUNIT_TEST(testLinkify_Authentication); + CPPUNIT_TEST(testLinkify_At); + CPPUNIT_TEST(testLinkify_Amps); + CPPUNIT_TEST(testLinkify_UnicodeCharacter); + CPPUNIT_TEST(testLinkify_NewLine); + CPPUNIT_TEST(testLinkify_Tab); + CPPUNIT_TEST(testLinkify_Action); + + CPPUNIT_TEST(testLinkify_SplitNone); + CPPUNIT_TEST(testLinkify_SplitAll); + CPPUNIT_TEST(testLinkify_SplitFirst); + CPPUNIT_TEST(testLinkify_SplitSecond); + CPPUNIT_TEST(testLinkify_SplitMiddle); + CPPUNIT_TEST_SUITE_END(); + + public: + void testLinkify_URLWithResource() { + std::string result = Linkify::linkify("http://swift.im/blog"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://swift.im/blog\">http://swift.im/blog</a>"), + result); + } + + void testLinkify_HTTPSURLWithResource() { + std::string result = Linkify::linkify("https://swift.im/blog"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"https://swift.im/blog\">https://swift.im/blog</a>"), + result); + } + + void testLinkify_URLWithEmptyResource() { + std::string result = Linkify::linkify("http://swift.im/"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://swift.im/\">http://swift.im/</a>"), + result); + } + + + void testLinkify_BareURL() { + std::string result = Linkify::linkify("http://swift.im"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://swift.im\">http://swift.im</a>"), + result); + } + + void testLinkify_URLSurroundedByWhitespace() { + std::string result = Linkify::linkify("Foo http://swift.im/blog Bar"); + + CPPUNIT_ASSERT_EQUAL( + std::string("Foo <a href=\"http://swift.im/blog\">http://swift.im/blog</a> Bar"), + result); + } + + void testLinkify_MultipleURLs() { + std::string result = Linkify::linkify("Foo http://swift.im/blog Bar http://el-tramo.be/about Baz"); + + CPPUNIT_ASSERT_EQUAL( + std::string("Foo <a href=\"http://swift.im/blog\">http://swift.im/blog</a> Bar <a href=\"http://el-tramo.be/about\">http://el-tramo.be/about</a> Baz"), + result); + } + + void testLinkify_CamelCase() { + std::string result = Linkify::linkify("http://fOo.cOm/bAz"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://fOo.cOm/bAz\">http://fOo.cOm/bAz</a>"), + result); + } + + void testLinkify_HierarchicalResource() { + std::string result = Linkify::linkify("http://foo.com/bar/baz/"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://foo.com/bar/baz/\">http://foo.com/bar/baz/</a>"), + result); + } + + void testLinkify_Anchor() { + std::string result = Linkify::linkify("http://foo.com/bar#baz"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://foo.com/bar#baz\">http://foo.com/bar#baz</a>"), + result); + } + + void testLinkify_Plus() { + std::string result = Linkify::linkify("http://foo.com/bar+baz"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://foo.com/bar+baz\">http://foo.com/bar+baz</a>"), + result); + } + + void testLinkify_Tilde() { + std::string result = Linkify::linkify("http://foo.com/~kev/"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://foo.com/~kev/\">http://foo.com/~kev/</a>"), + result); + } + + void testLinkify_Equal() { + std::string result = Linkify::linkify("http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0\">http://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=xmpp+definitive+guide&x=0&y=0</a>"), + result); + } + + void testLinkify_Authentication() { + std::string result = Linkify::linkify("http://bob:bla@swift.im/foo/bar"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://bob:bla@swift.im/foo/bar\">http://bob:bla@swift.im/foo/bar</a>"), + result); + } + + void testLinkify_At() { + std::string result = Linkify::linkify("http://swift.im/foo@bar"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://swift.im/foo@bar\">http://swift.im/foo@bar</a>"), + result); + } + + void testLinkify_Amps() { + std::string result = Linkify::linkify("http://swift.im/foo&bar&baz"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://swift.im/foo&bar&baz\">http://swift.im/foo&bar&baz</a>"), + result); + } + + void testLinkify_UnicodeCharacter() { + std::string result = Linkify::linkify("http://\xe2\x98\x83.net"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://\xe2\x98\x83.net\">http://\xe2\x98\x83.net</a>"), + result); + } + + void testLinkify_NewLine() { + std::string result = Linkify::linkify("http://swift.im\nfoo"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://swift.im\">http://swift.im</a>\nfoo"), + result); + } + + void testLinkify_Tab() { + std::string result = Linkify::linkify("http://swift.im\tfoo"); + + CPPUNIT_ASSERT_EQUAL( + std::string("<a href=\"http://swift.im\">http://swift.im</a>\tfoo"), + result); + } + + void testLinkify_Action() { + std::string result = Linkify::linkify("*http://swift.im*"); + + CPPUNIT_ASSERT_EQUAL( + std::string("*<a href=\"http://swift.im\">http://swift.im</a>*"), + result); + } + + void checkResult(const std::string& testling, size_t expectedIndex, std::string expectedSplit[]) { + std::pair<std::vector<std::string>, size_t> result = Linkify::splitLink(testling); + CPPUNIT_ASSERT_EQUAL(expectedIndex, result.second); + for (size_t i = 0; i < result.first.size(); i++) { + CPPUNIT_ASSERT_EQUAL(expectedSplit[i], result.first[i]); + } + } + + void testLinkify_SplitNone() { + std::string testling = "http this ain't"; + size_t expectedIndex = 1; + std::string expectedSplit[] = {"http this ain't"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitAll() { + std::string testling = "http://swift.im"; + size_t expectedIndex = 0; + std::string expectedSplit[] = {"http://swift.im"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitFirst() { + std::string testling = "http://swift.im is a link"; + size_t expectedIndex = 0; + std::string expectedSplit[] = {"http://swift.im", " is a link"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitSecond() { + std::string testling = "this is a link: http://swift.im"; + size_t expectedIndex = 1; + std::string expectedSplit[] = {"this is a link: ", "http://swift.im"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitMiddle() { + std::string testling = "Shove a link like http://swift.im in the middle"; + size_t expectedIndex = 1; + std::string expectedSplit[] = {"Shove a link like ","http://swift.im", " in the middle"}; + checkResult(testling, expectedIndex, expectedSplit); + } }; diff --git a/SwifTools/UnitTest/SConscript b/SwifTools/UnitTest/SConscript index dbd1ce5..bf44899 100644 --- a/SwifTools/UnitTest/SConscript +++ b/SwifTools/UnitTest/SConscript @@ -1,12 +1,12 @@ Import("env") env.Append(UNITTEST_SOURCES = [ - File("LinkifyTest.cpp"), - File("TabCompleteTest.cpp"), - File("LastLineTrackerTest.cpp"), - ]) + File("LinkifyTest.cpp"), + File("TabCompleteTest.cpp"), + File("LastLineTrackerTest.cpp"), + ]) if env["HAVE_HUNSPELL"] : - env.Append(UNITTEST_SOURCES = [ - File("SpellParserTest.cpp"), - ]) + env.Append(UNITTEST_SOURCES = [ + File("SpellParserTest.cpp"), + ]) diff --git a/SwifTools/UnitTest/SpellParserTest.cpp b/SwifTools/UnitTest/SpellParserTest.cpp index 09e686c..bf974ec 100644 --- a/SwifTools/UnitTest/SpellParserTest.cpp +++ b/SwifTools/UnitTest/SpellParserTest.cpp @@ -4,48 +4,55 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include <cppunit/extensions/HelperMacros.h> -#include <cppunit/extensions/TestFactoryRegistry.h> +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <memory> #include <boost/algorithm/string.hpp> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + #include <SwifTools/SpellParser.h> using namespace Swift; class SpellParserTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(SpellParserTest); - CPPUNIT_TEST(testSimpleCheckFragment); - CPPUNIT_TEST(testWWWCheckFragment); - CPPUNIT_TEST_SUITE_END(); - public: - SpellParserTest() { - parser_ = new SpellParser(); - }; - void tearDown() { - position_.clear(); - } - void testSimpleCheckFragment() { - parser_->check("fragment test", position_); - int size = position_.size(); - CPPUNIT_ASSERT_EQUAL(2, size); - CPPUNIT_ASSERT_EQUAL(0, boost::get<0>(position_.front())); - CPPUNIT_ASSERT_EQUAL(8, boost::get<1>(position_.front())); - CPPUNIT_ASSERT_EQUAL(9, boost::get<0>(position_.back())); - CPPUNIT_ASSERT_EQUAL(13, boost::get<1>(position_.back())); - } - void testWWWCheckFragment() { - parser_->check("www.link.com fragment test", position_); - int size = position_.size(); - CPPUNIT_ASSERT_EQUAL(2, size); - CPPUNIT_ASSERT_EQUAL(13, boost::get<0>(position_.front())); - CPPUNIT_ASSERT_EQUAL(21, boost::get<1>(position_.front())); - CPPUNIT_ASSERT_EQUAL(22, boost::get<0>(position_.back())); - CPPUNIT_ASSERT_EQUAL(26, boost::get<1>(position_.back())); - } - private: - SpellParser *parser_; - PositionPairList position_; + CPPUNIT_TEST_SUITE(SpellParserTest); + CPPUNIT_TEST(testSimpleCheckFragment); + CPPUNIT_TEST(testWWWCheckFragment); + CPPUNIT_TEST_SUITE_END(); + public: + SpellParserTest() { + } + void tearDown() { + position_.clear(); + } + void testSimpleCheckFragment() { + parser_->check("fragment test", position_); + auto size = position_.size(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), size); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), boost::get<0>(position_.front())); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(8), boost::get<1>(position_.front())); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(9), boost::get<0>(position_.back())); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(13), boost::get<1>(position_.back())); + } + void testWWWCheckFragment() { + parser_->check("www.link.com fragment test", position_); + auto size = position_.size(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), size); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(13), boost::get<0>(position_.front())); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(21), boost::get<1>(position_.front())); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(22), boost::get<0>(position_.back())); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(26), boost::get<1>(position_.back())); + } + private: + const std::unique_ptr<SpellParser> parser_ = std::unique_ptr<SpellParser>(new SpellParser()); + PositionPairList position_; }; CPPUNIT_TEST_SUITE_REGISTRATION(SpellParserTest); diff --git a/SwifTools/UnitTest/TabCompleteTest.cpp b/SwifTools/UnitTest/TabCompleteTest.cpp index cdb0296..56c91a8 100644 --- a/SwifTools/UnitTest/TabCompleteTest.cpp +++ b/SwifTools/UnitTest/TabCompleteTest.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <cppunit/extensions/HelperMacros.h> @@ -12,254 +12,254 @@ using namespace Swift; class TabCompleteTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(TabCompleteTest); - CPPUNIT_TEST(testEmpty); - CPPUNIT_TEST(testNoMatch); - CPPUNIT_TEST(testOneMatch); - CPPUNIT_TEST(testTwoMatch); - CPPUNIT_TEST(testChangeMatch); - CPPUNIT_TEST(testRemoveDuringComplete); - CPPUNIT_TEST(testAddDuringComplete); - CPPUNIT_TEST(testSwiftRoomSample); - CPPUNIT_TEST_SUITE_END(); - + CPPUNIT_TEST_SUITE(TabCompleteTest); + CPPUNIT_TEST(testEmpty); + CPPUNIT_TEST(testNoMatch); + CPPUNIT_TEST(testOneMatch); + CPPUNIT_TEST(testTwoMatch); + CPPUNIT_TEST(testChangeMatch); + CPPUNIT_TEST(testRemoveDuringComplete); + CPPUNIT_TEST(testAddDuringComplete); + CPPUNIT_TEST(testSwiftRoomSample); + CPPUNIT_TEST_SUITE_END(); + public: - TabCompleteTest() {} - - void setUp() { - completer_ = TabComplete(); - } - - void testEmpty() { - std::string blah("Blah"); - CPPUNIT_ASSERT_EQUAL( - blah, - completer_.completeWord(blah)); - CPPUNIT_ASSERT_EQUAL( - blah, - completer_.completeWord(blah)); - } - - void testNoMatch() { - completer_.addWord("Bleh"); - std::string blah("Blah"); - CPPUNIT_ASSERT_EQUAL( - blah, - completer_.completeWord(blah)); - CPPUNIT_ASSERT_EQUAL( - blah, - completer_.completeWord(blah)); - } - - void testOneMatch() { - std::string short1("Bl"); - std::string long1("Blehling"); - completer_.addWord(long1); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(short1)); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(long1)); - } - - void testTwoMatch() { - std::string short1("Hur"); - std::string long1("Hurgle"); - std::string long2("Hurdler"); - completer_.addWord(long1); - completer_.addWord("Blah"); - completer_.addWord(long2); - completer_.addWord("Bleh"); - CPPUNIT_ASSERT_EQUAL( - long2, - completer_.completeWord(short1)); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(long2)); - CPPUNIT_ASSERT_EQUAL( - long2, - completer_.completeWord(long1)); - } - - void testChangeMatch() { - std::string short1("Hur"); - std::string short2("Rub"); - std::string long1("Hurgle"); - std::string long2("Rubbish"); - completer_.addWord(long2); - completer_.addWord("Blah"); - completer_.addWord(long1); - completer_.addWord("Bleh"); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(short1)); - CPPUNIT_ASSERT_EQUAL( - long2, - completer_.completeWord(short2)); - CPPUNIT_ASSERT_EQUAL( - long2, - completer_.completeWord(long2)); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(short1)); - } - - void testRemoveDuringComplete() { - std::string short1("Kev"); - std::string long1("Kevin"); - std::string long2("Kevlar"); - completer_.addWord(long1); - completer_.addWord("Blah"); - completer_.addWord(long2); - completer_.addWord("Bleh"); - CPPUNIT_ASSERT_EQUAL( - long2, - completer_.completeWord(short1)); - completer_.removeWord(long2); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(long2)); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(long1)); - } - - void testAddDuringComplete() { - std::string short1("Rem"); - std::string long1("Remko"); - std::string long2("Remove"); - std::string long3("Remedial"); - completer_.addWord(long1); - completer_.addWord("Blah"); - completer_.addWord(long2); - completer_.addWord("Bleh"); - CPPUNIT_ASSERT_EQUAL( - long2, - completer_.completeWord(short1)); - completer_.addWord(long3); - CPPUNIT_ASSERT_EQUAL( - long1, - completer_.completeWord(long2)); - CPPUNIT_ASSERT_EQUAL( - long3, - completer_.completeWord(long1)); - } - - void testSwiftRoomSample() { - std::string t("t"); - std::string Anpan("Anpan"); - std::string cdubouloz("cdubouloz"); - std::string Tobias("Tobias"); - std::string Zash("Zash"); - std::string lastsky("lastsky"); - std::string Steve("Steve Kille"); - std::string Flo("Flo"); - std::string Test("Test"); - std::string test("test"); - completer_.addWord(Anpan); - completer_.addWord(cdubouloz); - completer_.addWord(Tobias); - completer_.addWord(lastsky); - completer_.addWord(Steve); - completer_.addWord(Flo); - completer_.addWord(Zash); - - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(Tobias)); - - completer_.addWord(Test); - - CPPUNIT_ASSERT_EQUAL( - Test, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(Test)); - CPPUNIT_ASSERT_EQUAL( - Test, - completer_.completeWord(Tobias)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(Test)); - - completer_.addWord(Zash); - completer_.addWord(Zash); - completer_.addWord(Zash); - completer_.addWord(Zash); - - completer_.removeWord(Test); - - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(Tobias)); - - completer_.addWord(test); - - completer_.addWord(Zash); - completer_.addWord(Zash); - completer_.addWord(Zash); - completer_.addWord(Zash); - - - CPPUNIT_ASSERT_EQUAL( - test, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(test)); - CPPUNIT_ASSERT_EQUAL( - test, - completer_.completeWord(Tobias)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(test)); - - completer_.removeWord(test); - - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(Tobias)); - - completer_.removeWord(Tobias); - CPPUNIT_ASSERT_EQUAL( - t, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - t, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - t, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - t, - completer_.completeWord(t)); - - completer_.addWord(Tobias); - - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(t)); - CPPUNIT_ASSERT_EQUAL( - Tobias, - completer_.completeWord(Tobias)); - - } + TabCompleteTest() {} + + void setUp() { + completer_ = TabComplete(); + } + + void testEmpty() { + std::string blah("Blah"); + CPPUNIT_ASSERT_EQUAL( + blah, + completer_.completeWord(blah)); + CPPUNIT_ASSERT_EQUAL( + blah, + completer_.completeWord(blah)); + } + + void testNoMatch() { + completer_.addWord("Bleh"); + std::string blah("Blah"); + CPPUNIT_ASSERT_EQUAL( + blah, + completer_.completeWord(blah)); + CPPUNIT_ASSERT_EQUAL( + blah, + completer_.completeWord(blah)); + } + + void testOneMatch() { + std::string short1("Bl"); + std::string long1("Blehling"); + completer_.addWord(long1); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(short1)); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(long1)); + } + + void testTwoMatch() { + std::string short1("Hur"); + std::string long1("Hurgle"); + std::string long2("Hurdler"); + completer_.addWord(long1); + completer_.addWord("Blah"); + completer_.addWord(long2); + completer_.addWord("Bleh"); + CPPUNIT_ASSERT_EQUAL( + long2, + completer_.completeWord(short1)); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(long2)); + CPPUNIT_ASSERT_EQUAL( + long2, + completer_.completeWord(long1)); + } + + void testChangeMatch() { + std::string short1("Hur"); + std::string short2("Rub"); + std::string long1("Hurgle"); + std::string long2("Rubbish"); + completer_.addWord(long2); + completer_.addWord("Blah"); + completer_.addWord(long1); + completer_.addWord("Bleh"); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(short1)); + CPPUNIT_ASSERT_EQUAL( + long2, + completer_.completeWord(short2)); + CPPUNIT_ASSERT_EQUAL( + long2, + completer_.completeWord(long2)); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(short1)); + } + + void testRemoveDuringComplete() { + std::string short1("Kev"); + std::string long1("Kevin"); + std::string long2("Kevlar"); + completer_.addWord(long1); + completer_.addWord("Blah"); + completer_.addWord(long2); + completer_.addWord("Bleh"); + CPPUNIT_ASSERT_EQUAL( + long2, + completer_.completeWord(short1)); + completer_.removeWord(long2); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(long2)); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(long1)); + } + + void testAddDuringComplete() { + std::string short1("Rem"); + std::string long1("Remko"); + std::string long2("Remove"); + std::string long3("Remedial"); + completer_.addWord(long1); + completer_.addWord("Blah"); + completer_.addWord(long2); + completer_.addWord("Bleh"); + CPPUNIT_ASSERT_EQUAL( + long2, + completer_.completeWord(short1)); + completer_.addWord(long3); + CPPUNIT_ASSERT_EQUAL( + long1, + completer_.completeWord(long2)); + CPPUNIT_ASSERT_EQUAL( + long3, + completer_.completeWord(long1)); + } + + void testSwiftRoomSample() { + std::string t("t"); + std::string Anpan("Anpan"); + std::string cdubouloz("cdubouloz"); + std::string Tobias("Tobias"); + std::string Zash("Zash"); + std::string lastsky("lastsky"); + std::string Steve("Steve Kille"); + std::string Flo("Flo"); + std::string Test("Test"); + std::string test("test"); + completer_.addWord(Anpan); + completer_.addWord(cdubouloz); + completer_.addWord(Tobias); + completer_.addWord(lastsky); + completer_.addWord(Steve); + completer_.addWord(Flo); + completer_.addWord(Zash); + + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(Tobias)); + + completer_.addWord(Test); + + CPPUNIT_ASSERT_EQUAL( + Test, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(Test)); + CPPUNIT_ASSERT_EQUAL( + Test, + completer_.completeWord(Tobias)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(Test)); + + completer_.addWord(Zash); + completer_.addWord(Zash); + completer_.addWord(Zash); + completer_.addWord(Zash); + + completer_.removeWord(Test); + + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(Tobias)); + + completer_.addWord(test); + + completer_.addWord(Zash); + completer_.addWord(Zash); + completer_.addWord(Zash); + completer_.addWord(Zash); + + + CPPUNIT_ASSERT_EQUAL( + test, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(test)); + CPPUNIT_ASSERT_EQUAL( + test, + completer_.completeWord(Tobias)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(test)); + + completer_.removeWord(test); + + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(Tobias)); + + completer_.removeWord(Tobias); + CPPUNIT_ASSERT_EQUAL( + t, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + t, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + t, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + t, + completer_.completeWord(t)); + + completer_.addWord(Tobias); + + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(t)); + CPPUNIT_ASSERT_EQUAL( + Tobias, + completer_.completeWord(Tobias)); + + } private: - TabComplete completer_; + TabComplete completer_; }; CPPUNIT_TEST_SUITE_REGISTRATION(TabCompleteTest); |