added iOS source code
[wl-app.git] / iOS / Pods / FolioReaderKit / Source / FolioReaderWebView.swift
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderWebView.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderWebView.swift
new file mode 100644 (file)
index 0000000..7c1a3c3
--- /dev/null
@@ -0,0 +1,339 @@
+//
+//  FolioReaderWebView.swift
+//  FolioReaderKit
+//
+//  Created by Hans Seiffert on 21.09.16.
+//  Copyright (c) 2016 Folio Reader. All rights reserved.
+//
+
+import UIKit
+
+/// The custom WebView used in each page
+open class FolioReaderWebView: UIWebView {
+    var isColors = false
+    var isShare = false
+    var isOneWord = false
+
+    fileprivate weak var readerContainer: FolioReaderContainer?
+
+    fileprivate var readerConfig: FolioReaderConfig {
+        guard let readerContainer = readerContainer else { return FolioReaderConfig() }
+        return readerContainer.readerConfig
+    }
+
+    fileprivate var book: FRBook {
+        guard let readerContainer = readerContainer else { return FRBook() }
+        return readerContainer.book
+    }
+
+    fileprivate var folioReader: FolioReader {
+        guard let readerContainer = readerContainer else { return FolioReader() }
+        return readerContainer.folioReader
+    }
+
+    override init(frame: CGRect) {
+        fatalError("use init(frame:readerConfig:book:) instead.")
+    }
+
+    init(frame: CGRect, readerContainer: FolioReaderContainer) {
+        self.readerContainer = readerContainer
+
+        super.init(frame: frame)
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    // MARK: - UIMenuController
+
+    open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
+        guard readerConfig.useReaderMenuController else {
+            return super.canPerformAction(action, withSender: sender)
+        }
+
+        if isShare {
+            return false
+        } else if isColors {
+            return false
+        } else {
+            if action == #selector(highlight(_:))
+                || (action == #selector(define(_:)) && isOneWord)
+                || (action == #selector(play(_:)) && (book.hasAudio || readerConfig.enableTTS))
+                || (action == #selector(share(_:)) && readerConfig.allowSharing)
+                || (action == #selector(copy(_:)) && readerConfig.allowSharing) {
+                return true
+            }
+            return false
+        }
+    }
+
+    // MARK: - UIMenuController - Actions
+
+    @objc func share(_ sender: UIMenuController) {
+        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+
+        let shareImage = UIAlertAction(title: self.readerConfig.localizedShareImageQuote, style: .default, handler: { (action) -> Void in
+            if self.isShare {
+                if let textToShare = self.js("getHighlightContent()") {
+                    self.folioReader.readerCenter?.presentQuoteShare(textToShare)
+                }
+            } else {
+                if let textToShare = self.js("getSelectedText()") {
+                    self.folioReader.readerCenter?.presentQuoteShare(textToShare)
+
+                    self.clearTextSelection()
+                }
+            }
+            self.setMenuVisible(false)
+        })
+
+        let shareText = UIAlertAction(title: self.readerConfig.localizedShareTextQuote, style: .default) { (action) -> Void in
+            if self.isShare {
+                if let textToShare = self.js("getHighlightContent()") {
+                    self.folioReader.readerCenter?.shareHighlight(textToShare, rect: sender.menuFrame)
+                }
+            } else {
+                if let textToShare = self.js("getSelectedText()") {
+                    self.folioReader.readerCenter?.shareHighlight(textToShare, rect: sender.menuFrame)
+                }
+            }
+            self.setMenuVisible(false)
+        }
+
+        let cancel = UIAlertAction(title: self.readerConfig.localizedCancel, style: .cancel, handler: nil)
+
+        alertController.addAction(shareImage)
+        alertController.addAction(shareText)
+        alertController.addAction(cancel)
+
+        if let alert = alertController.popoverPresentationController {
+            alert.sourceView = self.folioReader.readerCenter?.currentPage
+            alert.sourceRect = sender.menuFrame
+        }
+
+        self.folioReader.readerCenter?.present(alertController, animated: true, completion: nil)
+    }
+
+    func colors(_ sender: UIMenuController?) {
+        isColors = true
+        createMenu(options: false)
+        setMenuVisible(true)
+    }
+
+    func remove(_ sender: UIMenuController?) {
+        if let removedId = js("removeThisHighlight()") {
+            Highlight.removeById(withConfiguration: self.readerConfig, highlightId: removedId)
+        }
+        setMenuVisible(false)
+    }
+
+    @objc func highlight(_ sender: UIMenuController?) {
+        let highlightAndReturn = js("highlightString('\(HighlightStyle.classForStyle(self.folioReader.currentHighlightStyle))')")
+        let jsonData = highlightAndReturn?.data(using: String.Encoding.utf8)
+
+        do {
+            let json = try JSONSerialization.jsonObject(with: jsonData!, options: []) as! NSArray
+            let dic = json.firstObject as! [String: String]
+            let rect = CGRectFromString(dic["rect"]!)
+            guard let startOffset = dic["startOffset"] else {
+                return
+            }
+            guard let endOffset = dic["endOffset"] else {
+                return
+            }
+
+            createMenu(options: true)
+            setMenuVisible(true, andRect: rect)
+
+            // Persist
+            guard
+                let html = js("getHTML()"),
+                let identifier = dic["id"],
+                let bookId = (self.book.name as NSString?)?.deletingPathExtension else {
+                    return
+            }
+
+            let pageNumber = folioReader.readerCenter?.currentPageNumber ?? 0
+            let match = Highlight.MatchingHighlight(text: html, id: identifier, startOffset: startOffset, endOffset: endOffset, bookId: bookId, currentPage: pageNumber)
+            let highlight = Highlight.matchHighlight(match)
+            highlight?.persist(withConfiguration: self.readerConfig)
+
+        } catch {
+            print("Could not receive JSON")
+        }
+    }
+
+    @objc func define(_ sender: UIMenuController?) {
+        guard let selectedText = js("getSelectedText()") else {
+            return
+        }
+
+        self.setMenuVisible(false)
+        self.clearTextSelection()
+
+        let vc = UIReferenceLibraryViewController(term: selectedText)
+        vc.view.tintColor = self.readerConfig.tintColor
+        guard let readerContainer = readerContainer else { return }
+        readerContainer.show(vc, sender: nil)
+    }
+
+    @objc func play(_ sender: UIMenuController?) {
+        self.folioReader.readerAudioPlayer?.play()
+
+        self.clearTextSelection()
+    }
+
+    func setYellow(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .yellow)
+    }
+
+    func setGreen(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .green)
+    }
+
+    func setBlue(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .blue)
+    }
+
+    func setPink(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .pink)
+    }
+
+    func setUnderline(_ sender: UIMenuController?) {
+        changeHighlightStyle(sender, style: .underline)
+    }
+
+    func changeHighlightStyle(_ sender: UIMenuController?, style: HighlightStyle) {
+        self.folioReader.currentHighlightStyle = style.rawValue
+
+        if let updateId = js("setHighlightStyle('\(HighlightStyle.classForStyle(style.rawValue))')") {
+            Highlight.updateById(withConfiguration: self.readerConfig, highlightId: updateId, type: style)
+        }
+    }
+
+    // MARK: - Create and show menu
+
+    func createMenu(options: Bool) {
+        guard (self.readerConfig.useReaderMenuController == true) else {
+            return
+        }
+
+        isShare = options
+
+        let colors = UIImage(readerImageNamed: "colors-marker")
+        let share = UIImage(readerImageNamed: "share-marker")
+        let remove = UIImage(readerImageNamed: "no-marker")
+        let yellow = UIImage(readerImageNamed: "yellow-marker")
+        let green = UIImage(readerImageNamed: "green-marker")
+        let blue = UIImage(readerImageNamed: "blue-marker")
+        let pink = UIImage(readerImageNamed: "pink-marker")
+        let underline = UIImage(readerImageNamed: "underline-marker")
+
+        let menuController = UIMenuController.shared
+
+        let highlightItem = UIMenuItem(title: self.readerConfig.localizedHighlightMenu, action: #selector(highlight(_:)))
+        let playAudioItem = UIMenuItem(title: self.readerConfig.localizedPlayMenu, action: #selector(play(_:)))
+        let defineItem = UIMenuItem(title: self.readerConfig.localizedDefineMenu, action: #selector(define(_:)))
+        let colorsItem = UIMenuItem(title: "C", image: colors) { [weak self] _ in
+            self?.colors(menuController)
+        }
+        let shareItem = UIMenuItem(title: "S", image: share) { [weak self] _ in
+            self?.share(menuController)
+        }
+        let removeItem = UIMenuItem(title: "R", image: remove) { [weak self] _ in
+            self?.remove(menuController)
+        }
+        let yellowItem = UIMenuItem(title: "Y", image: yellow) { [weak self] _ in
+            self?.setYellow(menuController)
+        }
+        let greenItem = UIMenuItem(title: "G", image: green) { [weak self] _ in
+            self?.setGreen(menuController)
+        }
+        let blueItem = UIMenuItem(title: "B", image: blue) { [weak self] _ in
+            self?.setBlue(menuController)
+        }
+        let pinkItem = UIMenuItem(title: "P", image: pink) { [weak self] _ in
+            self?.setPink(menuController)
+        }
+        let underlineItem = UIMenuItem(title: "U", image: underline) { [weak self] _ in
+            self?.setUnderline(menuController)
+        }
+
+        var menuItems = [shareItem]
+
+        // menu on existing highlight
+        if isShare {
+            menuItems = [colorsItem, removeItem]
+            if (self.readerConfig.allowSharing == true) {
+                menuItems.append(shareItem)
+            }
+        } else if isColors {
+            // menu for selecting highlight color
+            menuItems = [yellowItem, greenItem, blueItem, pinkItem, underlineItem]
+        } else {
+            // default menu
+            // PD: changed
+            menuItems = [highlightItem/*, defineItem*/, shareItem]
+
+            if self.book.hasAudio || self.readerConfig.enableTTS {
+                menuItems.insert(playAudioItem, at: 0)
+            }
+
+            if (self.readerConfig.allowSharing == false) {
+                menuItems.removeLast()
+            }
+        }
+        
+        menuController.menuItems = menuItems
+    }
+    
+    open func setMenuVisible(_ menuVisible: Bool, animated: Bool = true, andRect rect: CGRect = CGRect.zero) {
+        if !menuVisible && isShare || !menuVisible && isColors {
+            isColors = false
+            isShare = false
+        }
+        
+        if menuVisible  {
+            if !rect.equalTo(CGRect.zero) {
+                UIMenuController.shared.setTargetRect(rect, in: self)
+            }
+        }
+        
+        UIMenuController.shared.setMenuVisible(menuVisible, animated: animated)
+    }
+    
+    // MARK: - Java Script Bridge
+    
+    @discardableResult open func js(_ script: String) -> String? {
+        let callback = self.stringByEvaluatingJavaScript(from: script)
+        if callback!.isEmpty { return nil }
+        return callback
+    }
+    
+    // MARK: WebView
+    
+    func clearTextSelection() {
+        // Forces text selection clearing
+        // @NOTE: this doesn't seem to always work
+        
+        self.isUserInteractionEnabled = false
+        self.isUserInteractionEnabled = true
+    }
+    
+    func setupScrollDirection() {
+        switch self.readerConfig.scrollDirection {
+        case .vertical, .defaultVertical, .horizontalWithVerticalContent:
+            scrollView.isPagingEnabled = false
+            paginationMode = .unpaginated
+            scrollView.bounces = true
+            break
+        case .horizontal:
+            scrollView.isPagingEnabled = true
+            paginationMode = .leftToRight
+            paginationBreakingMode = .page
+            scrollView.bounces = false
+            break
+        }
+    }
+}