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); | 
 Swift
 Swift