X-Git-Url: https://git.mdrn.pl/wl-app.git/blobdiff_plain/53b27422d140022594fc241cca91c3183be57bca..48b2fe9f7c2dc3d9aeaaa6dbfb27c7da4f3235ff:/iOS/Pods/FolioReaderKit/Source/Models/Highlight%20Helper.swift/wl-app.git/blobdiff_plain/53b27422d140022594fc241cca91c3183be57bca..48b2fe9f7c2dc3d9aeaaa6dbfb27c7da4f3235ff:/iOS/Pods/FolioReaderKit/Source/Models/Highlight+Helper.swift diff --git a/iOS/Pods/FolioReaderKit/Source/Models/Highlight+Helper.swift b/iOS/Pods/FolioReaderKit/Source/Models/Highlight+Helper.swift new file mode 100644 index 0000000..30a4198 --- /dev/null +++ b/iOS/Pods/FolioReaderKit/Source/Models/Highlight+Helper.swift @@ -0,0 +1,313 @@ +// +// Highlight+Helper.swift +// FolioReaderKit +// +// Created by Heberti Almeida on 06/07/16. +// Copyright (c) 2015 Folio Reader. All rights reserved. +// + +import Foundation +import RealmSwift + +/** + HighlightStyle type, default is .Yellow. + */ +public enum HighlightStyle: Int { + case yellow + case green + case blue + case pink + case underline + + public init () { + // Default style is `.yellow` + self = .yellow + } + + /** + Return HighlightStyle for CSS class. + */ + public static func styleForClass(_ className: String) -> HighlightStyle { + switch className { + case "highlight-yellow": return .yellow + case "highlight-green": return .green + case "highlight-blue": return .blue + case "highlight-pink": return .pink + case "highlight-underline": return .underline + default: return .yellow + } + } + + /** + Return CSS class for HighlightStyle. + */ + public static func classForStyle(_ style: Int) -> String { + + let enumStyle = (HighlightStyle(rawValue: style) ?? HighlightStyle()) + switch enumStyle { + case .yellow: return "highlight-yellow" + case .green: return "highlight-green" + case .blue: return "highlight-blue" + case .pink: return "highlight-pink" + case .underline: return "highlight-underline" + } + } + + /// Color components for the style + /// + /// - Returns: Tuple of all color compnonents. + private func colorComponents() -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { + switch self { + case .yellow: return (red: 255, green: 235, blue: 107, alpha: 0.9) + case .green: return (red: 192, green: 237, blue: 114, alpha: 0.9) + case .blue: return (red: 173, green: 216, blue: 255, alpha: 0.9) + case .pink: return (red: 255, green: 176, blue: 202, alpha: 0.9) + case .underline: return (red: 240, green: 40, blue: 20, alpha: 0.6) + } + } + + /** + Return CSS class for HighlightStyle. + */ + public static func colorForStyle(_ style: Int, nightMode: Bool = false) -> UIColor { + let enumStyle = (HighlightStyle(rawValue: style) ?? HighlightStyle()) + let colors = enumStyle.colorComponents() + return UIColor(red: colors.red/255, green: colors.green/255, blue: colors.blue/255, alpha: (nightMode ? colors.alpha : 1)) + } +} + +/// Completion block +public typealias Completion = (_ error: NSError?) -> () + +extension Highlight { + + /// Save a Highlight with completion block + /// + /// - Parameters: + /// - readerConfig: Current folio reader configuration. + /// - completion: Completion block. + public func persist(withConfiguration readerConfig: FolioReaderConfig, completion: Completion? = nil) { + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + realm.beginWrite() + realm.add(self, update: true) + try realm.commitWrite() + completion?(nil) + } catch let error as NSError { + print("Error on persist highlight: \(error)") + completion?(error) + } + } + + /// Remove a Highlight + /// + /// - Parameter readerConfig: Current folio reader configuration. + public func remove(withConfiguration readerConfig: FolioReaderConfig) { + do { + guard let realm = try? Realm(configuration: readerConfig.realmConfiguration) else { + return + } + try realm.write { + realm.delete(self) + try realm.commitWrite() + } + } catch let error as NSError { + print("Error on remove highlight: \(error)") + } + } + + /// Remove a Highlight by ID + /// + /// - Parameters: + /// - readerConfig: Current folio reader configuration. + /// - highlightId: The ID to be removed + public static func removeById(withConfiguration readerConfig: FolioReaderConfig, highlightId: String) { + var highlight: Highlight? + let predicate = NSPredicate(format:"highlightId = %@", highlightId) + + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + highlight = realm.objects(Highlight.self).filter(predicate).toArray(Highlight.self).first + highlight?.remove(withConfiguration: readerConfig) + } catch let error as NSError { + print("Error on remove highlight by id: \(error)") + } + } + + /// Update a Highlight by ID + /// + /// - Parameters: + /// - readerConfig: Current folio reader configuration. + /// - highlightId: The ID to be removed + /// - type: The `HighlightStyle` + public static func updateById(withConfiguration readerConfig: FolioReaderConfig, highlightId: String, type: HighlightStyle) { + var highlight: Highlight? + let predicate = NSPredicate(format:"highlightId = %@", highlightId) + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + highlight = realm.objects(Highlight.self).filter(predicate).toArray(Highlight.self).first + realm.beginWrite() + + highlight?.type = type.hashValue + + try realm.commitWrite() + + } catch let error as NSError { + print("Error on updateById: \(error)") + } + + } + + /// Return a list of Highlights with a given ID + /// + /// - Parameters: + /// - readerConfig: Current folio reader configuration. + /// - bookId: Book ID + /// - page: Page number + /// - Returns: Return a list of Highlights + public static func allByBookId(withConfiguration readerConfig: FolioReaderConfig, bookId: String, andPage page: NSNumber? = nil) -> [Highlight] { + var highlights: [Highlight]? + var predicate = NSPredicate(format: "bookId = %@", bookId) + if let page = page { + predicate = NSPredicate(format: "bookId = %@ && page = %@", bookId, page) + } + + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + highlights = realm.objects(Highlight.self).filter(predicate).toArray(Highlight.self) + return (highlights ?? []) + } catch let error as NSError { + print("Error on fetch all by book Id: \(error)") + return [] + } + } + + /// Return all Highlights + /// + /// - Parameter readerConfig: - readerConfig: Current folio reader configuration. + /// - Returns: Return all Highlights + public static func all(withConfiguration readerConfig: FolioReaderConfig) -> [Highlight] { + var highlights: [Highlight]? + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + highlights = realm.objects(Highlight.self).toArray(Highlight.self) + return (highlights ?? []) + } catch let error as NSError { + print("Error on fetch all: \(error)") + return [] + } + } +} + +// MARK: - HTML Methods + +extension Highlight { + + public struct MatchingHighlight { + var text: String + var id: String + var startOffset: String + var endOffset: String + var bookId: String + var currentPage: Int + } + + /** + Match a highlight on string. + */ + public static func matchHighlight(_ matchingHighlight: MatchingHighlight) -> Highlight? { + let pattern = "((.|\\s)*?)" + let regex = try? NSRegularExpression(pattern: pattern, options: []) + let matches = regex?.matches(in: matchingHighlight.text, options: [], range: NSRange(location: 0, length: matchingHighlight.text.utf16.count)) + let str = (matchingHighlight.text as NSString) + + let mapped = matches?.map { (match) -> Highlight in + var contentPre = str.substring(with: NSRange(location: match.range.location-kHighlightRange, length: kHighlightRange)) + var contentPost = str.substring(with: NSRange(location: match.range.location + match.range.length, length: kHighlightRange)) + + // Normalize string before save + contentPre = Highlight.subString(ofContent: contentPre, fromRangeOfString: ">", withPattern: "((?=[^>]*$)(.|\\s)*$)") + contentPost = Highlight.subString(ofContent: contentPost, fromRangeOfString: "<", withPattern: "^((.|\\s)*?)(?=<)") + + let highlight = Highlight() + highlight.highlightId = matchingHighlight.id + highlight.type = HighlightStyle.styleForClass(str.substring(with: match.range(at: 1))).rawValue + highlight.date = Date() + highlight.content = Highlight.removeSentenceSpam(str.substring(with: match.range(at: 2))) + highlight.contentPre = Highlight.removeSentenceSpam(contentPre) + highlight.contentPost = Highlight.removeSentenceSpam(contentPost) + highlight.page = matchingHighlight.currentPage + highlight.bookId = matchingHighlight.bookId + highlight.startOffset = (Int(matchingHighlight.startOffset) ?? -1) + highlight.endOffset = (Int(matchingHighlight.endOffset) ?? -1) + + return highlight + } + + return mapped?.first + } + + private static func subString(ofContent content: String, fromRangeOfString rangeString: String, withPattern pattern: String) -> String { + var updatedContent = content + if updatedContent.range(of: rangeString) != nil { + let regex = try? NSRegularExpression(pattern: pattern, options: []) + let searchString = regex?.firstMatch(in: updatedContent, options: .reportProgress, range: NSRange(location: 0, length: updatedContent.count)) + + if let string = searchString, (string.range.location != NSNotFound) { + updatedContent = (updatedContent as NSString).substring(with: string.range) + } + } + + return updatedContent + } + + /// Remove a Highlight from HTML by ID + /// + /// - Parameters: + /// - page: The page containing the HTML. + /// - highlightId: The ID to be removed + /// - Returns: The removed id + @discardableResult public static func removeFromHTMLById(withinPage page: FolioReaderPage?, highlightId: String) -> String? { + guard let currentPage = page else { return nil } + + if let removedId = currentPage.webView?.js("removeHighlightById('\(highlightId)')") { + return removedId + } else { + print("Error removing Highlight from page") + return nil + } + } + + /** + Remove span tag before store the highlight, this span is added on JavaScript. + + + - parameter text: Text to analise + - returns: Striped text + */ + public static func removeSentenceSpam(_ text: String) -> String { + + // Remove from text + func removeFrom(_ text: String, withPattern pattern: String) -> String { + var locator = text + let regex = try? NSRegularExpression(pattern: pattern, options: []) + let matches = regex?.matches(in: locator, options: [], range: NSRange(location: 0, length: locator.utf16.count)) + let str = (locator as NSString) + + var newLocator = "" + matches?.forEach({ (match: NSTextCheckingResult) in + newLocator += str.substring(with: match.range(at: 1)) + }) + + if (matches?.count > 0 && newLocator.isEmpty == false) { + locator = newLocator + } + + return locator + } + + let pattern = "((.|\\s)*?)" + let cleanText = removeFrom(text, withPattern: pattern) + return cleanText + } +}