mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-21 03:43:16 +09:00
107 lines
4.2 KiB
Swift
107 lines
4.2 KiB
Swift
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import Foundation
|
|
|
|
public extension String {
|
|
public func startsWith(_ other: String) -> Bool {
|
|
// rangeOfString returns nil if other is empty, destroying the analogy with (ordered) sets.
|
|
if other.isEmpty {
|
|
return true
|
|
}
|
|
if let range = self.range(of: other,
|
|
options: NSString.CompareOptions.anchored) {
|
|
return range.lowerBound == self.startIndex
|
|
}
|
|
return false
|
|
}
|
|
|
|
public func endsWith(_ other: String) -> Bool {
|
|
// rangeOfString returns nil if other is empty, destroying the analogy with (ordered) sets.
|
|
if other.isEmpty {
|
|
return true
|
|
}
|
|
if let range = self.range(of: other,
|
|
options: [NSString.CompareOptions.anchored, NSString.CompareOptions.backwards]) {
|
|
return range.upperBound == self.endIndex
|
|
}
|
|
return false
|
|
}
|
|
|
|
func escape() -> String? {
|
|
// We can't guaruntee that strings have a valid string encoding, as this is an entry point for tainted data,
|
|
// we should be very careful about forcefully dereferencing optional types.
|
|
// https://stackoverflow.com/questions/33558933/why-is-the-return-value-of-string-addingpercentencoding-optional#33558934
|
|
let queryItemDividers = CharacterSet(charactersIn: "?=&")
|
|
let allowedEscapes = CharacterSet.urlQueryAllowed.symmetricDifference(queryItemDividers)
|
|
return self.addingPercentEncoding(withAllowedCharacters: allowedEscapes)
|
|
}
|
|
|
|
func unescape() -> String? {
|
|
return self.removingPercentEncoding
|
|
}
|
|
|
|
/**
|
|
Ellipsizes a String only if it's longer than `maxLength`
|
|
|
|
"ABCDEF".ellipsize(4)
|
|
// "AB…EF"
|
|
|
|
:param: maxLength The maximum length of the String.
|
|
|
|
:returns: A String with `maxLength` characters or less
|
|
*/
|
|
func ellipsize(maxLength: Int) -> String {
|
|
if (maxLength >= 2) && (self.characters.count > maxLength) {
|
|
let index1 = self.characters.index(self.startIndex, offsetBy: (maxLength + 1) / 2) // `+ 1` has the same effect as an int ceil
|
|
let index2 = self.characters.index(self.endIndex, offsetBy: maxLength / -2)
|
|
|
|
return self.substring(to: index1) + "…\u{2060}" + self.substring(from: index2)
|
|
}
|
|
return self
|
|
}
|
|
|
|
private var stringWithAdditionalEscaping: String {
|
|
return self.replacingOccurrences(of: "|", with: "%7C", options: NSString.CompareOptions(), range: nil)
|
|
}
|
|
|
|
public var asURL: URL? {
|
|
// Firefox and NSURL disagree about the valid contents of a URL.
|
|
// Let's escape | for them.
|
|
// We'd love to use one of the more sophisticated CFURL* or NSString.* functions, but
|
|
// none seem to be quite suitable.
|
|
return URL(string: self) ??
|
|
URL(string: self.stringWithAdditionalEscaping)
|
|
}
|
|
|
|
/// Returns a new string made by removing the leading String characters contained
|
|
/// in a given character set.
|
|
public func stringByTrimmingLeadingCharactersInSet(_ set: CharacterSet) -> String {
|
|
var trimmed = self
|
|
while trimmed.rangeOfCharacter(from: set)?.lowerBound == trimmed.startIndex {
|
|
trimmed.remove(at: trimmed.startIndex)
|
|
}
|
|
return trimmed
|
|
}
|
|
|
|
/// Adds a newline at the closest space from the middle of a string.
|
|
/// Example turning "Mark as Read" into "Mark as\n Read"
|
|
public func stringSplitWithNewline() -> String {
|
|
let mid = self.characters.count/2
|
|
|
|
let arr: [Int] = self.characters.indices.flatMap {
|
|
if self.characters[$0] == " " {
|
|
return self.distance(from: startIndex, to: $0)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
guard let closest = arr.enumerated().min(by: { abs($0.1 - mid) < abs($1.1 - mid) }) else {
|
|
return self
|
|
}
|
|
var newString = self
|
|
newString.insert("\n", at: newString.characters.index(newString.characters.startIndex, offsetBy: closest.element))
|
|
return newString
|
|
}
|
|
}
|