From b1114545a7a4f73c1709b4366253cfaa95adf50f Mon Sep 17 00:00:00 2001 From: Jonathan Jenne Date: Fri, 16 Jun 2023 09:16:49 +0200 Subject: [PATCH] Add a lot of functions to Base --- Base/Base.vbproj | 6 ++ Base/BaseUtils.vb | 7 +++ Base/DatabaseEx.vb | 16 ++++++ Base/FileEx.vb | 26 +++++++++ Base/ModuleExtensions.vb | 101 ++++++++++++++++++++++++++++++++++ Base/ObjectEx.vb | 46 ++++++++++++++++ Base/ScreenEx.vb | 41 ++++++++++++++ Base/StringEx.vb | 116 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 359 insertions(+) create mode 100644 Base/BaseUtils.vb create mode 100644 Base/DatabaseEx.vb create mode 100644 Base/FileEx.vb create mode 100644 Base/ModuleExtensions.vb create mode 100644 Base/ObjectEx.vb create mode 100644 Base/StringEx.vb diff --git a/Base/Base.vbproj b/Base/Base.vbproj index f876f98b..83178b53 100644 --- a/Base/Base.vbproj +++ b/Base/Base.vbproj @@ -77,7 +77,12 @@ + + + + + @@ -101,6 +106,7 @@ + diff --git a/Base/BaseUtils.vb b/Base/BaseUtils.vb new file mode 100644 index 00000000..ec11c3f8 --- /dev/null +++ b/Base/BaseUtils.vb @@ -0,0 +1,7 @@ +Friend Class BaseUtils + Friend Shared Function FormatHash(pChecksum) + Return BitConverter. + ToString(pChecksum). + Replace("-", String.Empty) + End Function +End Class diff --git a/Base/DatabaseEx.vb b/Base/DatabaseEx.vb new file mode 100644 index 00000000..16d6a711 --- /dev/null +++ b/Base/DatabaseEx.vb @@ -0,0 +1,16 @@ +Public Class DatabaseEx + ''' + ''' Checks a Row value for three different `null` values, + ''' Nothing, Empty String, DBNull + ''' + ''' Returns the original value if the value is not null, or `defaultValue` + ''' + ''' The type of the value + ''' The DataRow that contains the value + ''' The column name + ''' The default value + ''' The original value or the default value + Public Shared Function NotNull(Of T)(ByVal Row As DataRow, Column As String, DefaultValue As T) As T + Return ObjectEx.NotNull(Row.Item(Column), DefaultValue) + End Function +End Class diff --git a/Base/FileEx.vb b/Base/FileEx.vb new file mode 100644 index 00000000..598deaee --- /dev/null +++ b/Base/FileEx.vb @@ -0,0 +1,26 @@ +Imports System.IO +Imports System.Security.Cryptography + +Public Class FileEx + ''' + ''' Reads the file at `FilePath` and computes a SHA256 Hash from its contents + ''' + ''' + ''' + Public Function GetChecksumFromFileContents(pFilePath As String) As String + Try + Using oFileStream = IO.File.OpenRead(pFilePath) + Using oStream As New BufferedStream(oFileStream, 1200000) + Dim oChecksum() As Byte = SHA256.Create.ComputeHash(oStream) + Return BaseUtils.FormatHash(oChecksum) + End Using + End Using + Catch ex As Exception + Return Nothing + End Try + End Function + + Public Function GetHashFromFileContents(pFilePath As String) As String + Return GetChecksumFromFileContents(pFilePath) + End Function +End Class diff --git a/Base/ModuleExtensions.vb b/Base/ModuleExtensions.vb new file mode 100644 index 00000000..6070327d --- /dev/null +++ b/Base/ModuleExtensions.vb @@ -0,0 +1,101 @@ +Imports System.Runtime.CompilerServices + +Public Module ModuleExtensions + Const UnixEraStartTicks As Long = 621355968000000000 + + ' ====================================================== + ' === DATETIME + ' ====================================================== + + + Public Function GetUnixTimestamp(pDate As Date) As Long + Dim UnixEraTicks = pDate.Ticks - UnixEraStartTicks + Return UnixEraTicks \ 10000 + End Function + + + Public Function DateFromUnix(pTimestamp As Long) As Date + Return New Date(UnixEraStartTicks + pTimestamp * 10000) + End Function + + ' ====================================================== + ' === STRING + ' ====================================================== + + ''' + ''' Truncates a string to the specified length if it exceeds that length. + ''' + ''' The string + ''' The maximum string length + ''' The truncated string + + Public Function Truncate(pString As String, pLength As Integer) As String + If String.IsNullOrEmpty(pString) Then Return pString + Return pString.Substring(0, Math.Min(pLength, pString.Length)) + End Function + + ''' + ''' Replaces single quotes in text for SQL Commands. + ''' + ''' The string + ''' The escaped string. + + Public Function EscapeForSQL(pString As String) As String + Return ObjectEx.NotNull(pString, String.Empty).Replace("'", "''") + End Function + + ''' + ''' Converts a string to boolean. Accepts true and 1 as truthy values + ''' + ''' The input string + ''' True if input is true or 1, otherwise false. + + Public Function ToBoolean(pString As String) As Boolean + If String.IsNullOrEmpty(pString) Then Return False + Return (pString.Trim().ToLower() = "true") OrElse (pString.Trim() = "1") + End Function + + ' ====================================================== + ' === DATATABLE + ' ====================================================== + + + Public Function ItemEx(Of T)(pRow As DataRow, pFieldName As String, Optional pDefaultValue As T = Nothing) As T + Try + Return ObjectEx.NotNull(pRow.Item(pFieldName), pDefaultValue) + Catch ex As Exception + Return Nothing + End Try + End Function + + + Public Function ItemEx(Of T)(pRow As DataRow, pFieldIndex As Integer, Optional pDefaultValue As T = Nothing) As T + Try + Return ObjectEx.NotNull(pRow.Item(pFieldIndex), pDefaultValue) + Catch ex As Exception + Return Nothing + End Try + End Function + + + Public Function FieldOrDefault(Of T)(pRow As DataRow, pFieldName As String, Optional pDefaultValue As T = Nothing) As T + Return ItemEx(pRow, pFieldName, pDefaultValue) + End Function + + + Public Function FieldOrDefault(Of T)(pRow As DataRow, pFieldIndex As Integer, Optional pDefaultValue As T = Nothing) As T + Return ItemEx(pRow, pFieldIndex, pDefaultValue) + End Function + + + Public Function First(pTable As DataTable) As DataRow + Try + If pTable Is Nothing OrElse pTable.Rows.Count = 0 Then + Return Nothing + End If + Return pTable.Rows.Item(0) + Catch ex As Exception + Return Nothing + End Try + End Function +End Module diff --git a/Base/ObjectEx.vb b/Base/ObjectEx.vb new file mode 100644 index 00000000..fceadb7b --- /dev/null +++ b/Base/ObjectEx.vb @@ -0,0 +1,46 @@ +Public Class ObjectEx + ''' + ''' Checks a value for three different `null` values, + ''' Nothing, Empty String, DBNull + ''' + ''' Returns the original value if the value is not null, or `defaultValue` + ''' + ''' The type of the value + ''' The value + ''' The default Value + ''' The original value or the default value + Public Shared Function NotNull(Of T)(ByVal value As T, ByVal defaultValue As T) As T + If IsNothing(value) OrElse String.IsNullOrEmpty(value.ToString) OrElse IsDBNull(value) Then + Return defaultValue + Else + Return value + End If + End Function + + ''' + ''' Converts a String value to the given Enum + ''' + ''' The Enum Type + ''' The string value to convert + Public Shared Function ToEnum(Of T)(value As String) As T + Return [Enum].Parse(GetType(T), value) + End Function + + ''' + ''' Converts an Integer value to the given Enum + ''' + ''' The Enum Type + ''' The integer value to convert + Public Shared Function ToEnum(Of T)(value As Integer) As T + Return [Enum].ToObject(GetType(T), value) + End Function + + ''' + ''' Converts a Long value to the given Enum + ''' + ''' The Enum Type + ''' The long value to convert + Public Shared Function ToEnum(Of T)(value As Long) As T + Return [Enum].ToObject(GetType(T), value) + End Function +End Class diff --git a/Base/ScreenEx.vb b/Base/ScreenEx.vb index 6921e20c..d408d680 100644 --- a/Base/ScreenEx.vb +++ b/Base/ScreenEx.vb @@ -45,4 +45,45 @@ Public Class ScreenEx RestoreFormState(pForm, oFormState) End Sub + + ''' + ''' Checks if a point is Visible on any screen + ''' + Public Shared Function IsVisibleOnAnyScreen(Location As Point) As Boolean + Try + Dim oRect As New Rectangle(Location, New Size(0, 0)) + + For Each oScreen In Screen.AllScreens + If oScreen.WorkingArea.IntersectsWith(oRect) Then + Return True + End If + Next + + Return False + Catch ex As Exception + Return False + End Try + End Function + + ''' + ''' Checks if Size is not negative + ''' + Public Shared Function SizeIsVisible(Size As Size) As Boolean + If Size.Width >= 0 And Size.Height >= 0 Then + Return True + End If + + Return False + End Function + + ''' + ''' Checks if Location is not negative + ''' + Public Shared Function LocationIsVisible(Location As Point) As Boolean + If Location.X >= 0 And Location.Y >= 0 Then + Return True + End If + + Return False + End Function End Class diff --git a/Base/StringEx.vb b/Base/StringEx.vb new file mode 100644 index 00000000..3aaeb2fd --- /dev/null +++ b/Base/StringEx.vb @@ -0,0 +1,116 @@ +Imports System.IO +Imports System.Security.Cryptography +Imports System.Text +Imports System.Text.RegularExpressions + +Public Class StringEx + ''' + ''' Creates a "slug" from text that can be used as part of a valid URL. + ''' Invalid characters are converted to hyphens. Punctuation that Is + ''' perfect valid in a URL Is also converted to hyphens to keep the + ''' result mostly text. Steps are taken to prevent leading, trailing, + ''' And consecutive hyphens. + ''' + ''' The string to convert + Public Shared Function ConvertTextToSlug(ByVal s As String) As String + Dim oBuilder As New StringBuilder() + Dim oWasHyphen As Boolean = True + + For Each oChar As Char In s + + If Char.IsLetterOrDigit(oChar) Then + oBuilder.Append(Char.ToLower(oChar)) + oWasHyphen = False + ElseIf Char.IsWhiteSpace(oChar) AndAlso Not oWasHyphen Then + oBuilder.Append("-"c) + oWasHyphen = True + End If + Next + + If oWasHyphen AndAlso oBuilder.Length > 0 Then oBuilder.Length -= 1 + Return oBuilder.ToString() + End Function + + ''' + ''' Removes Invalid characters from a string, suitable for file and path names + ''' + ''' Removed characters are: + ''' + ''' * Illegal File-characters + ''' * Illegal Path-characters + ''' * Unicode characters that classify as Emoji + ''' * All characters above codepoint U+10000 + ''' + ''' See: + ''' https://stackoverflow.com/questions/46905176/detecting-all-emojis + ''' https://stackoverflow.com/questions/28023682/how-do-i-remove-emoji-characters-from-a-string + ''' + ''' + Public Shared Function RemoveInvalidCharacters(pString As String) As String + Dim oResult = pString + + Try + ' Remove all Unicode above Codepoint U+10000 + oResult = Regex.Replace(oResult, InvalidChars.UnicodeSurrogates, String.Empty) + + ' Remove all Emojis (Version 13) + oResult = Regex.Replace(oResult, InvalidChars.Emojis, String.Empty) + + ' Remove Invalid filename characters + oResult = Regex.Replace(oResult, InvalidChars.Filenames, String.Empty) + + ' Remove Invalid filename characters + oResult = Regex.Replace(oResult, InvalidChars.Paths, String.Empty) + + ' Remove Uneccessary characters + oResult = Regex.Replace(oResult, "\s{2,}", " ") + oResult = Regex.Replace(oResult, "\.{2,}", ".") + + ' Remove excess space chars + oResult = oResult.Trim() + + Return oResult + Catch ex As Exception + Return oResult + End Try + End Function + + ''' + ''' Generates a random short (8 characters) guid + ''' + ''' The generated guid as a String + Public Shared Function GetShortGUID() As String + Return Guid.NewGuid().ToString().GetHashCode().ToString("x") + End Function + + ''' + ''' Checks if string contains invalid characters + ''' + ''' + ''' + Public Shared Function TestContainsInvalidCharacters(pString As String) As Boolean + Return Not pString.Equals(RemoveInvalidCharacters(pString)) + End Function + + ''' + ''' Creates a hash using the SHA256 algorithm and returns it + ''' + ''' Any string + ''' + Public Shared Function GetChecksum(pStringToCheck As String) As String + Dim oBytes() As Byte = Encoding.UTF8.GetBytes(pStringToCheck) + Dim oChecksum() As Byte = SHA256.Create.ComputeHash(oBytes) + Return BaseUtils.FormatHash(oChecksum) + End Function + + Public Shared Function GetHash(pStringToCheck As String) As String + Return GetChecksum(pStringToCheck) + End Function + + Friend Class InvalidChars + Public Shared Filenames As String = Regex.Escape(New String(IO.Path.GetInvalidFileNameChars())) + Public Shared Paths As String = Regex.Escape(New String(IO.Path.GetInvalidPathChars())) + Public Shared UnicodeSurrogates = "\p{Cs}" + Public Shared Emojis = "\uD83D(?:\uDC68(?:\uD83C(?:[\uDFFB-\uDFFF]\u200D(?:\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83E(?:\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFF]|[\uDDAF-\uDDB3\uDDBC\uDDBD])|[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|[\uDFFB-\uDFFF])|\u200D(?:\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83D(?:(?:[\uDC68\uDC69]\u200D\uD83D)?(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|\uDC69(?:\uD83C(?:[\uDFFB-\uDFFF]\u200D(?:\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D[\uDC68\uDC69]|[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83E(?:\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFF]|[\uDDAF-\uDDB3\uDDBC\uDDBD])|[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|[\uDFFB-\uDFFF])|\u200D(?:\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D[\uDC68\uDC69]|[\uDC68\uDC69])|\uD83D(?:(?:\uDC69\u200D\uD83D)?(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|(?:\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|\uDC6F)(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6](?:\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F?)?|\u200D[\u2640\u2642]\uFE0F?)?|\uDC41(?:\uFE0F(?:\u200D\uD83D\uDDE8\uFE0F?)?|\u200D\uD83D\uDDE8\uFE0F?)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD74\uDD90]\uD83C[\uDFFB-\uDFFF]|\uDC08(?:\u200D\u2B1B)?|[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD74\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDD90\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB])|\uD83E(?:\uDDD1(?:\uD83C(?:[\uDFFB-\uDFFF]\u200D(?:\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uD83E(?:\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|[\uDDAF-\uDDB3\uDDBC\uDDBD])|[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|[\uDFFB-\uDFFF])|\u200D(?:\uD83E(?:\uDD1D\u200D\uD83E\uDDD1|[\uDDAF-\uDDB3\uDDBC\uDDBD])|[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]))?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F?)?|\u200D[\u2640\u2642]\uFE0F?)?|[\uDD3C\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0C\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD1D\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78\uDD7A-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCB\uDDD0\uDDE0-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])|\uD83C(?:\uDFF4(?:\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\u200D\u2620\uFE0F?)?|[\uDFC3\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F?)?|\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDFF3(?:\uFE0F(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|(?:[\uDFCB\uDFCC]\u200D[\u2640\u2642]|[\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7])\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCB\uDFCC\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF])|\u26F9(?:(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)(?:\u200D[\u2640\u2642]\uFE0F?)?|\u200D[\u2640\u2642]\uFE0F?)?|\u2764(?:\uFE0F(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|[\#\*0-9]\uFE0F?\u20E3|[\u261D\u270C\u270D]\uD83C[\uDFFB-\uDFFF]|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u2600-\u2604\u260E\u2611\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26B0\u26B1\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0\u26F1\u26F4\u26F7\u26F8\u2702\u2708\u2709\u270C\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u3030\u303D\u3297\u3299]\uFE0F?|[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]" + End Class +End Class