Misc util function tweaks
I was going to make wordWrap use Span, but decided against it
This commit is contained in:
parent
16fd7bfde4
commit
7e36fff0f1
@ -63,26 +63,6 @@ let htmlToPlainTextTests =
|
||||
}
|
||||
]
|
||||
|
||||
[<Tests>]
|
||||
let replaceFirstTests =
|
||||
testList "replaceFirst" [
|
||||
test "replaces the first occurrence when it is found at the beginning of the string" {
|
||||
let testString = "unit unit unit"
|
||||
Expect.equal (replaceFirst "unit" "test" testString) "test unit unit"
|
||||
"First occurrence of a substring was not replaced properly at the beginning of the string"
|
||||
}
|
||||
test "replaces the first occurrence when it is found in the center of the string" {
|
||||
let testString = "test unit test"
|
||||
Expect.equal (replaceFirst "unit" "test" testString) "test test test"
|
||||
"First occurrence of a substring was not replaced properly when it is in the center of the string"
|
||||
}
|
||||
test "returns the original string if the replacement isn't found" {
|
||||
let testString = "unit tests"
|
||||
Expect.equal (replaceFirst "tested" "testing" testString) "unit tests"
|
||||
"String which did not have the target substring was not returned properly"
|
||||
}
|
||||
]
|
||||
|
||||
[<Tests>]
|
||||
let sndAsStringTests =
|
||||
testList "sndAsString" [
|
||||
@ -91,6 +71,46 @@ let sndAsStringTests =
|
||||
}
|
||||
]
|
||||
|
||||
module StringTests =
|
||||
|
||||
open PrayerTracker.Utils.String
|
||||
|
||||
[<Tests>]
|
||||
let replaceFirstTests =
|
||||
testList "String.replaceFirst" [
|
||||
test "replaces the first occurrence when it is found at the beginning of the string" {
|
||||
let testString = "unit unit unit"
|
||||
Expect.equal (replaceFirst "unit" "test" testString) "test unit unit"
|
||||
"First occurrence of a substring was not replaced properly at the beginning of the string"
|
||||
}
|
||||
test "replaces the first occurrence when it is found in the center of the string" {
|
||||
let testString = "test unit test"
|
||||
Expect.equal (replaceFirst "unit" "test" testString) "test test test"
|
||||
"First occurrence of a substring was not replaced properly when it is in the center of the string"
|
||||
}
|
||||
test "returns the original string if the replacement isn't found" {
|
||||
let testString = "unit tests"
|
||||
Expect.equal (replaceFirst "tested" "testing" testString) "unit tests"
|
||||
"String which did not have the target substring was not returned properly"
|
||||
}
|
||||
]
|
||||
|
||||
[<Tests>]
|
||||
let replaceTests =
|
||||
testList "String.replace" [
|
||||
test "succeeds" {
|
||||
Expect.equal (replace "a" "b" "abacab") "bbbcbb" "String did not replace properly"
|
||||
}
|
||||
]
|
||||
|
||||
[<Tests>]
|
||||
let trimTests =
|
||||
testList "String.trim" [
|
||||
test "succeeds" {
|
||||
Expect.equal (trim " abc ") "abc" "Space not trimmed from string properly"
|
||||
}
|
||||
]
|
||||
|
||||
[<Tests>]
|
||||
let stripTagsTests =
|
||||
let testString = "<p class=\"testing\">Here is some text<br> <br />and some more</p>"
|
||||
@ -127,3 +147,27 @@ let wordWrapTests =
|
||||
Expect.equal (wordWrap 80 testString) testString "Blank lines were not preserved"
|
||||
}
|
||||
]
|
||||
|
||||
[<Tests>]
|
||||
let wordWrapBTests =
|
||||
testList "wordWrapB" [
|
||||
test "breaks where it is supposed to" {
|
||||
let testString = "The quick brown fox jumps over the lazy dog\nIt does!"
|
||||
Expect.equal (wordWrap 20 testString) "The quick brown fox\njumps over the lazy\ndog\nIt does!\n"
|
||||
"Line not broken correctly"
|
||||
}
|
||||
test "wraps long line without a space and a line with exact length" {
|
||||
let testString = "Asamatteroffact, the dog does too"
|
||||
Expect.equal (wordWrap 10 testString) "Asamattero\nffact, the\ndog does\ntoo\n"
|
||||
"Longer line not broken correctly"
|
||||
}
|
||||
test "wraps long line without a space and a line with non-exact length" {
|
||||
let testString = "Asamatteroffact, that dog does too"
|
||||
Expect.equal (wordWrap 10 testString) "Asamattero\nffact,\nthat dog\ndoes too\n"
|
||||
"Longer line not broken correctly"
|
||||
}
|
||||
test "preserves blank lines" {
|
||||
let testString = "Here is\n\na string with blank lines"
|
||||
Expect.equal (wordWrap 80 testString) testString "Blank lines were not preserved"
|
||||
}
|
||||
]
|
||||
|
@ -10,29 +10,39 @@ open System
|
||||
/// Hash a string with a SHA1 hash
|
||||
let sha1Hash (x : string) =
|
||||
use alg = SHA1.Create ()
|
||||
alg.ComputeHash (ASCIIEncoding().GetBytes x)
|
||||
alg.ComputeHash (Encoding.ASCII.GetBytes x)
|
||||
|> Seq.map (fun chr -> chr.ToString "x2")
|
||||
|> Seq.reduce (+)
|
||||
|> String.concat ""
|
||||
|
||||
|
||||
/// Hash a string using 1,024 rounds of PBKDF2 and a salt
|
||||
let pbkdf2Hash (salt : Guid) (x : string) =
|
||||
use alg = new Rfc2898DeriveBytes (x, Encoding.UTF8.GetBytes (salt.ToString "N"), 1024)
|
||||
Convert.ToBase64String(alg.GetBytes 64)
|
||||
(alg.GetBytes >> Convert.ToBase64String) 64
|
||||
|
||||
|
||||
/// Replace the first occurrence of a string with a second string within a given string
|
||||
let replaceFirst (needle : string) replacement (haystack : string) =
|
||||
let i = haystack.IndexOf needle
|
||||
match i with
|
||||
| -1 -> haystack
|
||||
| _ ->
|
||||
seq {
|
||||
yield haystack.Substring (0, i)
|
||||
yield replacement
|
||||
yield haystack.Substring (i + needle.Length)
|
||||
}
|
||||
|> Seq.reduce (+)
|
||||
/// String helper functions
|
||||
module String =
|
||||
|
||||
/// string.Trim()
|
||||
let trim (str: string) = str.Trim ()
|
||||
|
||||
/// string.Replace()
|
||||
let replace (find : string) repl (str : string) = str.Replace (find, repl)
|
||||
|
||||
|
||||
/// Replace the first occurrence of a string with a second string within a given string
|
||||
let replaceFirst (needle : string) replacement (haystack : string) =
|
||||
match haystack.IndexOf needle with
|
||||
| -1 -> haystack
|
||||
| idx ->
|
||||
seq {
|
||||
yield haystack.[0..idx - 1]
|
||||
yield replacement
|
||||
yield haystack.[idx + needle.Length..]
|
||||
}
|
||||
|> String.concat ""
|
||||
|
||||
|
||||
/// Strip HTML tags from the given string
|
||||
// Adapted from http://www.dijksterhuis.org/safely-cleaning-html-with-strip_tags-in-csharp/
|
||||
@ -51,21 +61,15 @@ let stripTags allowedTags input =
|
||||
|| htmlTag.IndexOf (sprintf "</%s" t) = 0) false
|
||||
match isAllowed with
|
||||
| true -> ()
|
||||
| false -> output <- replaceFirst tag.Value "" output
|
||||
| false -> output <- String.replaceFirst tag.Value "" output
|
||||
output
|
||||
|
||||
|
||||
/// Wrap a string at the specified number of characters
|
||||
let wordWrap charPerLine (input : string) =
|
||||
match input.Length with
|
||||
| len when len <= charPerLine -> input
|
||||
| _ ->
|
||||
let rec findSpace (inp : string) idx =
|
||||
match idx with
|
||||
| 0 -> 0
|
||||
| _ ->
|
||||
match inp.Substring (idx, 1) with
|
||||
| null | " " -> idx
|
||||
| _ -> findSpace inp (idx - 1)
|
||||
seq {
|
||||
for line in input.Replace("\r", "").Split '\n' do
|
||||
let mutable remaining = line
|
||||
@ -73,41 +77,52 @@ let wordWrap charPerLine (input : string) =
|
||||
| 0 -> ()
|
||||
| _ ->
|
||||
while charPerLine < remaining.Length do
|
||||
let spaceIdx = findSpace remaining charPerLine
|
||||
match spaceIdx with
|
||||
| 0 ->
|
||||
// No whitespace; just break it at [characters]
|
||||
yield remaining.Substring (0, charPerLine)
|
||||
remaining <- remaining.Substring charPerLine
|
||||
| _ ->
|
||||
yield remaining.Substring (0, spaceIdx)
|
||||
remaining <- remaining.Substring (spaceIdx + 1)
|
||||
match remaining.Length with
|
||||
| 0 -> ()
|
||||
| _ -> yield remaining
|
||||
match charPerLine + 1 < remaining.Length && remaining.[charPerLine] = ' ' with
|
||||
| true ->
|
||||
// Line length is followed by a space; return [charPerLine] as a line
|
||||
yield remaining.[0..charPerLine - 1]
|
||||
remaining <- remaining.[charPerLine + 1..]
|
||||
| false ->
|
||||
match remaining.[0..charPerLine - 1].LastIndexOf ' ' with
|
||||
| -1 ->
|
||||
// No whitespace; just break it at [characters]
|
||||
yield remaining.[0..charPerLine - 1]
|
||||
remaining <- remaining.[charPerLine..]
|
||||
| spaceIdx ->
|
||||
// Break on the last space in the line
|
||||
yield remaining.[0..spaceIdx - 1]
|
||||
remaining <- remaining.[spaceIdx + 1..]
|
||||
// Leftovers - yum!
|
||||
match remaining.Length with 0 -> () | _ -> yield remaining
|
||||
}
|
||||
|> Seq.fold (fun (acc : StringBuilder) line -> acc.AppendFormat ("{0}\n", line)) (StringBuilder ())
|
||||
|> string
|
||||
|
||||
/// Modify the text returned by CKEditor into the format we need for request and announcement text
|
||||
let ckEditorToText (text : string) =
|
||||
text
|
||||
.Replace("\n\t", "") // \r
|
||||
.Replace(" ", " ")
|
||||
.Replace(" ", "  ")
|
||||
.Replace("</p><p>", "<br><br>") // \r
|
||||
.Replace("</p>", "")
|
||||
.Replace("<p>", "")
|
||||
.Trim()
|
||||
|
||||
let trim (str : string) = str.Trim ()
|
||||
[ "\n\t", ""
|
||||
" ", " "
|
||||
" ", "  "
|
||||
"</p><p>", "<br><br>"
|
||||
"</p>", ""
|
||||
"<p>", ""
|
||||
]
|
||||
|> List.fold (fun (txt : string) (x, y) -> String.replace x y txt) text
|
||||
|> trim
|
||||
|
||||
|
||||
/// Convert an HTML piece of text to plain text
|
||||
let htmlToPlainText html =
|
||||
match html with
|
||||
| null | "" -> ""
|
||||
| _ ->
|
||||
WebUtility.HtmlDecode((html.Trim() |> stripTags [ "br" ]).Replace("<br />", "\n").Replace("<br>", "\n"))
|
||||
.Replace("\u00a0", " ")
|
||||
html.Trim ()
|
||||
|> stripTags [ "br" ]
|
||||
|> String.replace "<br />" "\n"
|
||||
|> String.replace "<br>" "\n"
|
||||
|> WebUtility.HtmlDecode
|
||||
|> String.replace "\u00a0" " "
|
||||
|
||||
/// Get the second portion of a tuple as a string
|
||||
let sndAsString x = (snd >> string) x
|
||||
|
Loading…
Reference in New Issue
Block a user