added iOS source code
[wl-app.git] / iOS / Pods / FolioReaderKit / Source / FolioReaderCenter.swift
diff --git a/iOS/Pods/FolioReaderKit/Source/FolioReaderCenter.swift b/iOS/Pods/FolioReaderKit/Source/FolioReaderCenter.swift
new file mode 100755 (executable)
index 0000000..06766ca
--- /dev/null
@@ -0,0 +1,1559 @@
+//
+//  FolioReaderCenter.swift
+//  FolioReaderKit
+//
+//  Created by Heberti Almeida on 08/04/15.
+//  Copyright (c) 2015 Folio Reader. All rights reserved.
+//
+
+import UIKit
+import ZFDragableModalTransition
+
+/// Protocol which is used from `FolioReaderCenter`s.
+@objc public protocol FolioReaderCenterDelegate: class {
+
+    /// Notifies that a page appeared. This is triggered when a page is chosen and displayed.
+    ///
+    /// - Parameter page: The appeared page
+    @objc optional func pageDidAppear(_ page: FolioReaderPage)
+
+    /// Passes and returns the HTML content as `String`. Implement this method if you want to modify the HTML content of a `FolioReaderPage`.
+    ///
+    /// - Parameters:
+    ///   - page: The `FolioReaderPage`.
+    ///   - htmlContent: The current HTML content as `String`.
+    /// - Returns: The adjusted HTML content as `String`. This is the content which will be loaded into the given `FolioReaderPage`.
+    @objc optional func htmlContentForPage(_ page: FolioReaderPage, htmlContent: String) -> String
+    
+    /// Notifies that a page changed. This is triggered when collection view cell is changed.
+    ///
+    /// - Parameter pageNumber: The appeared page item
+    @objc optional func pageItemChanged(_ pageNumber: Int)
+
+}
+
+/// The base reader class
+open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
+
+    /// This delegate receives the events from the current `FolioReaderPage`s delegate.
+    open weak var delegate: FolioReaderCenterDelegate?
+
+    /// This delegate receives the events from current page
+    open weak var pageDelegate: FolioReaderPageDelegate?
+
+    /// The base reader container
+    open weak var readerContainer: FolioReaderContainer?
+
+    /// The current visible page on reader
+    open fileprivate(set) var currentPage: FolioReaderPage?
+
+    /// The collection view with pages
+    open var collectionView: UICollectionView!
+    
+    let collectionViewLayout = UICollectionViewFlowLayout()
+    var loadingView: UIActivityIndicatorView!
+    var pages: [String]!
+    var totalPages: Int = 0
+    var tempFragment: String?
+    var animator: ZFModalTransitionAnimator!
+    var pageIndicatorView: FolioReaderPageIndicator?
+    var pageIndicatorHeight: CGFloat = 20
+    var recentlyScrolled = false
+    var recentlyScrolledDelay = 2.0 // 2 second delay until we clear recentlyScrolled
+    var recentlyScrolledTimer: Timer!
+    var scrollScrubber: ScrollScrubber?
+    var activityIndicator = UIActivityIndicatorView()
+    var isScrolling = false
+    var pageScrollDirection = ScrollDirection()
+    var nextPageNumber: Int = 0
+    var previousPageNumber: Int = 0
+    var currentPageNumber: Int = 0
+    var pageWidth: CGFloat = 0.0
+    var pageHeight: CGFloat = 0.0
+    var anchor: String!
+    var lastRow: Int?
+
+    fileprivate var screenBounds: CGRect!
+    fileprivate var pointNow = CGPoint.zero
+    fileprivate var pageOffsetRate: CGFloat = 0
+    fileprivate var tempReference: FRTocReference?
+    fileprivate var isFirstLoad = true
+    fileprivate var currentWebViewScrollPositions = [Int: CGPoint]()
+    fileprivate var currentOrientation: UIInterfaceOrientation?
+    fileprivate var changingChapter = false
+
+    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
+    }
+
+    // MARK: - Init
+
+    init(withContainer readerContainer: FolioReaderContainer) {
+        self.readerContainer = readerContainer
+        super.init(nibName: nil, bundle: Bundle.frameworkBundle())
+
+        self.initialization()
+    }
+
+    required public init?(coder aDecoder: NSCoder) {
+        fatalError("This class doesn't support NSCoding.")
+    }
+
+    /**
+     Common Initialization
+     */
+    fileprivate func initialization() {
+
+        if (self.readerConfig.hideBars == true) {
+            self.pageIndicatorHeight = 0
+        }
+        
+        self.totalPages = book.spine.spineReferences.count
+
+        // Loading indicator
+        let style: UIActivityIndicatorViewStyle = folioReader.isNight(.white, .gray)
+        loadingView = UIActivityIndicatorView(activityIndicatorStyle: style)
+        loadingView.hidesWhenStopped = true
+        loadingView.startAnimating()
+        self.view.addSubview(loadingView)
+    }
+
+    // MARK: - View life cicle
+
+    override open func viewDidLoad() {
+        super.viewDidLoad()
+
+        screenBounds = self.getScreenBounds()
+        
+        setPageSize(UIApplication.shared.statusBarOrientation)
+
+        // Layout
+        collectionViewLayout.sectionInset = UIEdgeInsets.zero
+        collectionViewLayout.minimumLineSpacing = 0
+        collectionViewLayout.minimumInteritemSpacing = 0
+        collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig)
+        
+        let background = folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white)
+        view.backgroundColor = background
+
+        // CollectionView
+        collectionView = UICollectionView(frame: screenBounds, collectionViewLayout: collectionViewLayout)
+        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.isPagingEnabled = true
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = background
+        collectionView.decelerationRate = UIScrollViewDecelerationRateFast
+        enableScrollBetweenChapters(scrollEnabled: true)
+        view.addSubview(collectionView)
+
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .never
+        }
+        
+        // Activity Indicator
+        self.activityIndicator.activityIndicatorViewStyle = .gray
+        self.activityIndicator.hidesWhenStopped = true
+        self.activityIndicator = UIActivityIndicatorView(frame: CGRect(x: screenBounds.size.width/2, y: screenBounds.size.height/2, width: 30, height: 30))
+        self.activityIndicator.backgroundColor = UIColor.gray
+        self.view.addSubview(self.activityIndicator)
+        self.view.bringSubview(toFront: self.activityIndicator)
+
+        if #available(iOS 10.0, *) {
+            collectionView.isPrefetchingEnabled = false
+        }
+
+        // Register cell classes
+        collectionView?.register(FolioReaderPage.self, forCellWithReuseIdentifier: kReuseCellIdentifier)
+
+        // Configure navigation bar and layout
+        automaticallyAdjustsScrollViewInsets = false
+        extendedLayoutIncludesOpaqueBars = true
+        configureNavBar()
+
+        // Page indicator view
+        if (self.readerConfig.hidePageIndicator == false) {
+            let frame = self.frameForPageIndicatorView()
+            pageIndicatorView = FolioReaderPageIndicator(frame: frame, readerConfig: readerConfig, folioReader: folioReader)
+            if let pageIndicatorView = pageIndicatorView {
+                view.addSubview(pageIndicatorView)
+            }
+        }
+
+        guard let readerContainer = readerContainer else { return }
+        self.scrollScrubber = ScrollScrubber(frame: frameForScrollScrubber(), withReaderContainer: readerContainer)
+        self.scrollScrubber?.delegate = self
+        if let scrollScrubber = scrollScrubber {
+            view.addSubview(scrollScrubber.slider)
+        }
+    }
+
+    override open func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+
+        configureNavBar()
+
+        // Update pages
+        pagesForCurrentPage(currentPage)
+        pageIndicatorView?.reloadView(updateShadow: true)
+    }
+
+    override open func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+
+        screenBounds = self.getScreenBounds()
+        loadingView.center = view.center
+
+        setPageSize(UIApplication.shared.statusBarOrientation)
+        updateSubviewFrames()
+    }
+
+    // MARK: Layout
+
+    /**
+     Enable or disable the scrolling between chapters (`FolioReaderPage`s). If this is enabled it's only possible to read the current chapter. If another chapter should be displayed is has to be triggered programmatically with `changePageWith`.
+
+     - parameter scrollEnabled: `Bool` which enables or disables the scrolling between `FolioReaderPage`s.
+     */
+    open func enableScrollBetweenChapters(scrollEnabled: Bool) {
+        self.collectionView.isScrollEnabled = scrollEnabled
+    }
+
+    fileprivate func updateSubviewFrames() {
+        self.pageIndicatorView?.frame = self.frameForPageIndicatorView()
+        self.scrollScrubber?.frame = self.frameForScrollScrubber()
+    }
+
+    fileprivate func frameForPageIndicatorView() -> CGRect {
+        var bounds = CGRect(x: 0, y: screenBounds.size.height-pageIndicatorHeight, width: screenBounds.size.width, height: pageIndicatorHeight)
+        
+        if #available(iOS 11.0, *) {
+            bounds.size.height = bounds.size.height + view.safeAreaInsets.bottom
+        }
+        
+        return bounds
+    }
+
+    fileprivate func frameForScrollScrubber() -> CGRect {
+        let scrubberY: CGFloat = ((self.readerConfig.shouldHideNavigationOnTap == true || self.readerConfig.hideBars == true) ? 50 : 74)
+        return CGRect(x: self.pageWidth + 10, y: scrubberY, width: 40, height: (self.pageHeight - 100))
+    }
+
+    func configureNavBar() {
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)// readerConfig.tintColor
+        let navText = tintColor// folioReader.isNight(UIColor.white, UIColor.black)
+        let font = UIFont(name: "Avenir-Light", size: 17)!
+        setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
+    }
+
+    func configureNavBarButtons() {
+
+        // Navbar buttons
+        let shareIcon = UIImage(readerImageNamed: "icon-navbar-share")//.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let audioIcon = UIImage(readerImageNamed: "icon-navbar-tts")//?.ignoreSystemTint(withConfiguration: self.readerConfig) //man-speech-icon
+        let closeIcon = UIImage(readerImageNamed: "icon-navbar-close")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let tocIcon = UIImage(readerImageNamed: "icon-navbar-toc")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let fontIcon = UIImage(readerImageNamed: "icon-navbar-font")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
+        let space = 70 as CGFloat
+
+        let menu = UIBarButtonItem(image: closeIcon, style: .plain, target: self, action:#selector(closeReader(_:)))
+        let toc = UIBarButtonItem(image: tocIcon, style: .plain, target: self, action:#selector(presentChapterList(_:)))
+
+        navigationItem.leftBarButtonItems = [menu, toc]
+
+        var rightBarIcons = [UIBarButtonItem]()
+
+        if (self.readerConfig.allowSharing == true) {
+            rightBarIcons.append(UIBarButtonItem(image: shareIcon, style: .plain, target: self, action:#selector(shareChapter(_:))))
+        }
+
+        if self.book.hasAudio || self.readerConfig.enableTTS {
+            rightBarIcons.append(UIBarButtonItem(image: audioIcon, style: .plain, target: self, action:#selector(presentPlayerMenu(_:))))
+        }
+
+        let font = UIBarButtonItem(image: fontIcon, style: .plain, target: self, action: #selector(presentFontsMenu))
+        font.width = space
+
+        rightBarIcons.append(contentsOf: [font])
+        navigationItem.rightBarButtonItems = rightBarIcons
+        
+        if(self.readerConfig.displayTitle){
+            navigationItem.title = book.title
+        }
+    }
+
+    func reloadData() {
+        self.loadingView.stopAnimating()
+        self.totalPages = book.spine.spineReferences.count
+
+        self.collectionView.reloadData()
+        self.configureNavBarButtons()
+        self.setCollectionViewProgressiveDirection()
+
+        if self.readerConfig.loadSavedPositionForCurrentBook {
+            guard let position = folioReader.savedPositionForCurrentBook, let pageNumber = position["pageNumber"] as? Int, pageNumber > 0 else {
+                self.currentPageNumber = 1
+                return
+            }
+
+            self.changePageWith(page: pageNumber)
+            self.currentPageNumber = pageNumber
+        }
+    }
+
+    // MARK: Change page progressive direction
+
+    private func transformViewForRTL(_ view: UIView?) {
+        if folioReader.needsRTLChange {
+            view?.transform = CGAffineTransform(scaleX: -1, y: 1)
+        } else {
+            view?.transform = CGAffineTransform.identity
+        }
+    }
+
+    func setCollectionViewProgressiveDirection() {
+        self.transformViewForRTL(self.collectionView)
+    }
+
+    func setPageProgressiveDirection(_ page: FolioReaderPage) {
+        self.transformViewForRTL(page)
+    }
+
+    // MARK: Change layout orientation
+
+    /// Get internal page offset before layout change
+    private func updatePageOffsetRate() {
+        guard let currentPage = self.currentPage, let webView = currentPage.webView else {
+            return
+        }
+
+        let pageScrollView = webView.scrollView
+        let contentSize = pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig)
+        let contentOffset = pageScrollView.contentOffset.forDirection(withConfiguration: self.readerConfig)
+        self.pageOffsetRate = (contentSize != 0 ? (contentOffset / contentSize) : 0)
+    }
+
+    func setScrollDirection(_ direction: FolioReaderScrollDirection) {
+        guard let currentPage = self.currentPage, let webView = currentPage.webView else {
+            return
+        }
+
+        let pageScrollView = webView.scrollView
+
+        // Get internal page offset before layout change
+        self.updatePageOffsetRate()
+        // Change layout
+        self.readerConfig.scrollDirection = direction
+        self.collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig)
+        self.currentPage?.setNeedsLayout()
+        self.collectionView.collectionViewLayout.invalidateLayout()
+        self.collectionView.setContentOffset(frameForPage(self.currentPageNumber).origin, animated: false)
+
+        // Page progressive direction
+        self.setCollectionViewProgressiveDirection()
+        delay(0.2) { self.setPageProgressiveDirection(currentPage) }
+
+
+        /**
+         *  This delay is needed because the page will not be ready yet
+         *  so the delay wait until layout finished the changes.
+         */
+        delay(0.1) {
+            var pageOffset = (pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig) * self.pageOffsetRate)
+
+            // Fix the offset for paged scroll
+            if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) {
+                let page = round(pageOffset / self.pageWidth)
+                pageOffset = (page * self.pageWidth)
+            }
+
+            let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset))
+            pageScrollView.setContentOffset(pageOffsetPoint, animated: true)
+        }
+    }
+
+    // MARK: Status bar and Navigation bar
+
+    func hideBars() {
+        guard self.readerConfig.shouldHideNavigationOnTap == true else {
+            return
+        }
+
+        self.updateBarsStatus(true)
+    }
+
+    func showBars() {
+        self.configureNavBar()
+        self.updateBarsStatus(false)
+    }
+
+    func toggleBars() {
+        guard self.readerConfig.shouldHideNavigationOnTap == true else {
+            return
+        }
+
+        let shouldHide = !self.navigationController!.isNavigationBarHidden
+        if shouldHide == false {
+            self.configureNavBar()
+        }
+
+        self.updateBarsStatus(shouldHide)
+    }
+
+    private func updateBarsStatus(_ shouldHide: Bool, shouldShowIndicator: Bool = false) {
+        guard let readerContainer = readerContainer else { return }
+        readerContainer.shouldHideStatusBar = shouldHide
+
+        UIView.animate(withDuration: 0.25, animations: {
+            readerContainer.setNeedsStatusBarAppearanceUpdate()
+
+            // Show minutes indicator
+            if (shouldShowIndicator == true) {
+                self.pageIndicatorView?.minutesLabel.alpha = shouldHide ? 0 : 1
+            }
+        })
+        self.navigationController?.setNavigationBarHidden(shouldHide, animated: true)
+    }
+
+    // MARK: UICollectionViewDataSource
+
+    open func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 1
+    }
+
+    open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return totalPages
+    }
+
+    open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let reuseableCell = collectionView.dequeueReusableCell(withReuseIdentifier: kReuseCellIdentifier, for: indexPath) as? FolioReaderPage
+        
+        return self.configure(readerPageCell: reuseableCell, atIndexPath: indexPath)
+    }
+
+    private func configure(readerPageCell cell: FolioReaderPage?, atIndexPath indexPath: IndexPath) -> UICollectionViewCell {
+        guard let cell = cell, let readerContainer = readerContainer else {
+            return UICollectionViewCell()
+        }
+        
+        var isNextChapter = true
+        if let lastRow = lastRow {
+            isNextChapter = lastRow < indexPath.row
+        }
+        if changingChapter {
+            isNextChapter = true
+        }
+        lastRow = indexPath.row
+
+        cell.setup(withReaderContainer: readerContainer)
+        cell.pageNumber = indexPath.row+1
+        cell.webView?.scrollView.delegate = self
+        if #available(iOS 11.0, *) {
+            cell.webView?.scrollView.contentInsetAdjustmentBehavior = .never
+        }
+        cell.webView?.setupScrollDirection()
+        cell.webView?.frame = cell.webViewFrame()
+        cell.delegate = self
+        cell.backgroundColor = .clear
+
+        setPageProgressiveDirection(cell)
+
+        // Configure the cell
+        let resource = self.book.spine.spineReferences[indexPath.row].resource
+        guard var html = try? String(contentsOfFile: resource.fullHref, encoding: String.Encoding.utf8) else {
+            return cell
+        }
+
+        let mediaOverlayStyleColors = "\"\(self.readerConfig.mediaOverlayColor.hexString(false))\", \"\(self.readerConfig.mediaOverlayColor.highlightColor().hexString(false))\""
+
+        // Inject CSS
+        let jsFilePath = Bundle.frameworkBundle().path(forResource: "Bridge", ofType: "js")
+        let cssFilePath = Bundle.frameworkBundle().path(forResource: "Style", ofType: "css")
+        let cssTag = "<link rel=\"stylesheet\" type=\"text/css\" href=\"\(cssFilePath!)\">"
+        let jsTag = "<script type=\"text/javascript\" src=\"\(jsFilePath!)\"></script>" +
+        "<script type=\"text/javascript\">setMediaOverlayStyleColors(\(mediaOverlayStyleColors))</script>"
+
+        let toInject = "\n\(cssTag)\n\(jsTag)\n</head>"
+        html = html.replacingOccurrences(of: "</head>", with: toInject)
+
+        // Font class name
+        var classes = folioReader.currentFont.cssIdentifier
+        classes += " " + folioReader.currentMediaOverlayStyle.className()
+
+        // Night mode
+        if folioReader.nightMode {
+            classes += " nightMode"
+        }
+
+        // Font Size
+        classes += " \(folioReader.currentFontSize.cssIdentifier(sliderType: .font))"
+        classes += " \(folioReader.currentMarginSize.cssIdentifier(sliderType: .margin))"
+        classes += " \(folioReader.currentInterlineSize.cssIdentifier(sliderType: .interline))"
+
+        
+        
+        html = html.replacingOccurrences(of: "<html ", with: "<html class=\"\(classes)\"")
+
+        // Let the delegate adjust the html string
+        if let modifiedHtmlContent = self.delegate?.htmlContentForPage?(cell, htmlContent: html) {
+            html = modifiedHtmlContent
+        }
+
+//        print("\n\nhtmlhtml\n\n\(html)")
+        
+        cell.loadHTMLString(html, baseURL: URL(fileURLWithPath: resource.fullHref.deletingLastPathComponent), isNextChapter: isNextChapter)
+        return cell
+    }
+
+    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        var size = CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
+        
+        if #available(iOS 11.0, *) {
+            let orientation = UIDevice.current.orientation
+            
+            if orientation == .portrait || orientation == .portraitUpsideDown {
+                if readerConfig.scrollDirection == .horizontal {
+                    size.height = size.height - view.safeAreaInsets.bottom
+                }
+            }
+        }
+        
+        return size
+    }
+    
+    // MARK: - Device rotation
+
+    override open func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
+        guard folioReader.isReaderReady else { return }
+
+        setPageSize(toInterfaceOrientation)
+        updateCurrentPage()
+
+        if self.currentOrientation == nil || (self.currentOrientation?.isPortrait != toInterfaceOrientation.isPortrait) {
+            var pageIndicatorFrame = pageIndicatorView?.frame
+            pageIndicatorFrame?.origin.y = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - pageIndicatorHeight) : (self.collectionView.frame.width - pageIndicatorHeight))
+            pageIndicatorFrame?.origin.x = 0
+            pageIndicatorFrame?.size.width = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.width) : (self.collectionView.frame.height))
+            pageIndicatorFrame?.size.height = pageIndicatorHeight
+
+            var scrollScrubberFrame = scrollScrubber?.slider.frame;
+            scrollScrubberFrame?.origin.x = ((screenBounds.size.height < screenBounds.size.width) ? (screenBounds.size.width - 100) : (screenBounds.size.height + 10))
+            scrollScrubberFrame?.size.height = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - 100) : (self.collectionView.frame.width - 100))
+
+            self.collectionView.collectionViewLayout.invalidateLayout()
+
+            UIView.animate(withDuration: duration, animations: {
+                // Adjust page indicator view
+                if let pageIndicatorFrame = pageIndicatorFrame {
+                    self.pageIndicatorView?.frame = pageIndicatorFrame
+                    self.pageIndicatorView?.reloadView(updateShadow: true)
+                }
+
+                // Adjust scroll scrubber slider
+                if let scrollScrubberFrame = scrollScrubberFrame {
+                    self.scrollScrubber?.slider.frame = scrollScrubberFrame
+                }
+
+                // Adjust collectionView
+                self.collectionView.contentSize = self.readerConfig.isDirection(
+                    CGSize(width: self.pageWidth, height: self.pageHeight * CGFloat(self.totalPages)),
+                    CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight),
+                    CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight)
+                )
+                self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false)
+                self.collectionView.collectionViewLayout.invalidateLayout()
+
+                // Adjust internal page offset
+                self.updatePageOffsetRate()
+            })
+        }
+
+        self.currentOrientation = toInterfaceOrientation
+    }
+
+    override open func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
+        guard folioReader.isReaderReady == true, let currentPage = currentPage else {
+            return
+        }
+
+        // Update pages
+        pagesForCurrentPage(currentPage)
+        currentPage.refreshPageMode()
+
+        scrollScrubber?.setSliderVal()
+
+        // After rotation fix internal page offset
+        var pageOffset = (currentPage.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0) * pageOffsetRate
+
+        // Fix the offset for paged scroll
+        if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) {
+            let page = round(pageOffset / self.pageWidth)
+            pageOffset = page * self.pageWidth
+        }
+
+        let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset))
+        currentPage.webView?.scrollView.setContentOffset(pageOffsetPoint, animated: true)
+    }
+
+    override open func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
+        guard folioReader.isReaderReady else {
+            return
+        }
+
+        self.collectionView.scrollToItem(at: IndexPath(row: self.currentPageNumber - 1, section: 0), at: UICollectionViewScrollPosition(), animated: false)
+        if (self.currentPageNumber + 1) >= totalPages {
+            UIView.animate(withDuration: duration, animations: {
+                self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false)
+            })
+        }
+    }
+
+    // MARK: - Page
+
+    func setPageSize(_ orientation: UIInterfaceOrientation) {
+        guard orientation.isPortrait else {
+            if screenBounds.size.width > screenBounds.size.height {
+                self.pageWidth = screenBounds.size.width
+                self.pageHeight = screenBounds.size.height
+            } else {
+                self.pageWidth = screenBounds.size.height
+                self.pageHeight = screenBounds.size.width
+            }
+            return
+        }
+
+        if screenBounds.size.width < screenBounds.size.height {
+            self.pageWidth = screenBounds.size.width
+            self.pageHeight = screenBounds.size.height
+        } else {
+            self.pageWidth = screenBounds.size.height
+            self.pageHeight = screenBounds.size.width
+        }
+    }
+
+    func updateCurrentPage(_ page: FolioReaderPage? = nil, completion: (() -> Void)? = nil) {
+        if let page = page {
+            currentPage = page
+            self.previousPageNumber = page.pageNumber-1
+            self.currentPageNumber = page.pageNumber
+        } else {
+            let currentIndexPath = getCurrentIndexPath()
+            currentPage = collectionView.cellForItem(at: currentIndexPath) as? FolioReaderPage
+
+            self.previousPageNumber = currentIndexPath.row
+            self.currentPageNumber = currentIndexPath.row+1
+        }
+
+        self.nextPageNumber = (((self.currentPageNumber + 1) <= totalPages) ? (self.currentPageNumber + 1) : self.currentPageNumber)
+
+        // Set pages
+        guard let currentPage = currentPage else {
+            completion?()
+            return
+        }
+
+        scrollScrubber?.setSliderVal()
+
+        if let readingTime = currentPage.webView?.js("getReadingTime()") {
+            pageIndicatorView?.totalMinutes = Int(readingTime)!
+        } else {
+            pageIndicatorView?.totalMinutes = 0
+        }
+        pagesForCurrentPage(currentPage)
+
+        delegate?.pageDidAppear?(currentPage)
+        delegate?.pageItemChanged?(self.getCurrentPageItemNumber())
+
+        completion?()
+    }
+
+    func pagesForCurrentPage(_ page: FolioReaderPage?) {
+        guard let page = page, let webView = page.webView else { return }
+
+        let pageSize = self.readerConfig.isDirection(pageHeight, self.pageWidth, pageHeight)
+        let contentSize = page.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0
+        self.pageIndicatorView?.totalPages = ((pageSize != 0) ? Int(ceil(contentSize / pageSize)) : 0)
+
+        let pageOffSet = self.readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y)
+        let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize)
+
+        self.pageIndicatorView?.currentPage = webViewPage
+    }
+
+    func pageForOffset(_ offset: CGFloat, pageHeight height: CGFloat) -> Int {
+        guard (height != 0) else {
+            return 0
+        }
+
+        let page = Int(ceil(offset / height))+1
+        return page
+    }
+
+    func getCurrentIndexPath() -> IndexPath {
+        let indexPaths = collectionView.indexPathsForVisibleItems
+        var indexPath = IndexPath()
+
+        if indexPaths.count > 1 {
+            let first = indexPaths.first!
+            let last = indexPaths.last!
+
+            switch self.pageScrollDirection {
+            case .up, .left:
+                if first.compare(last) == .orderedAscending {
+                    indexPath = last
+                } else {
+                    indexPath = first
+                }
+            default:
+                if first.compare(last) == .orderedAscending {
+                    indexPath = first
+                } else {
+                    indexPath = last
+                }
+            }
+        } else {
+            indexPath = indexPaths.first ?? IndexPath(row: 0, section: 0)
+        }
+
+        return indexPath
+    }
+
+    func frameForPage(_ page: Int) -> CGRect {
+        return self.readerConfig.isDirection(
+            CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight),
+            CGRect(x: self.pageWidth * CGFloat(page-1), y: 0, width: self.pageWidth, height: self.pageHeight),
+            CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight)
+        )
+    }
+
+    open func changePageWith(page: Int, andFragment fragment: String, animated: Bool = false, completion: (() -> Void)? = nil) {
+        if (self.currentPageNumber == page) {
+            if let currentPage = currentPage , fragment != "" {
+                currentPage.handleAnchor(fragment, avoidBeginningAnchors: true, animated: animated)
+            }
+            completion?()
+        } else {
+            tempFragment = fragment
+            changePageWith(page: page, animated: animated, completion: { () -> Void in
+                self.updateCurrentPage {
+                    completion?()
+                }
+            })
+        }
+    }
+
+    open func changePageWith(href: String, animated: Bool = false, completion: (() -> Void)? = nil) {
+        let item = findPageByHref(href)
+        let indexPath = IndexPath(row: item, section: 0)
+        changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in
+            self.updateCurrentPage {
+                completion?()
+            }
+        })
+    }
+
+    open func changePageWith(href: String, andAudioMarkID markID: String) {
+        if recentlyScrolled { return } // if user recently scrolled, do not change pages or scroll the webview
+        guard let currentPage = currentPage else { return }
+
+        let item = findPageByHref(href)
+        let pageUpdateNeeded = item+1 != currentPage.pageNumber
+        let indexPath = IndexPath(row: item, section: 0)
+        changePageWith(indexPath: indexPath, animated: true) { () -> Void in
+            if pageUpdateNeeded {
+                self.updateCurrentPage {
+                    currentPage.audioMarkID(markID)
+                }
+            } else {
+                currentPage.audioMarkID(markID)
+            }
+        }
+    }
+
+    open func changePageWith(indexPath: IndexPath, animated: Bool = false, completion: (() -> Void)? = nil) {
+        guard indexPathIsValid(indexPath) else {
+            print("ERROR: Attempt to scroll to invalid index path")
+            completion?()
+            return
+        }
+
+        UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in
+            self.collectionView.scrollToItem(at: indexPath, at: .direction(withConfiguration: self.readerConfig), animated: false)
+        }) { (finished: Bool) -> Void in
+            completion?()
+        }
+    }
+    
+    open func changePageWith(href: String, pageItem: Int, animated: Bool = false, completion: (() -> Void)? = nil) {
+        changePageWith(href: href, animated: animated) {
+            self.changePageItem(to: pageItem)
+        }
+    }
+
+    func indexPathIsValid(_ indexPath: IndexPath) -> Bool {
+        let section = indexPath.section
+        let row = indexPath.row
+        let lastSectionIndex = numberOfSections(in: collectionView) - 1
+
+        //Make sure the specified section exists
+        if section > lastSectionIndex {
+            return false
+        }
+
+        let rowCount = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) - 1
+        return row <= rowCount
+    }
+
+    open func isLastPage() -> Bool{
+        return (currentPageNumber == self.nextPageNumber)
+    }
+
+    public func changePageToNext(_ completion: (() -> Void)? = nil) {
+        changePageWith(page: self.nextPageNumber, animated: true) { () -> Void in
+            completion?()
+        }
+    }
+
+    public func changePageToPrevious(_ completion: (() -> Void)? = nil) {
+        changePageWith(page: self.previousPageNumber, animated: true) { () -> Void in
+            completion?()
+        }
+    }
+    
+    public func changePageItemToNext(_ completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentOffset = cell.webView?.scrollView.contentOffset,
+            let contentOffsetXLimit = cell.webView?.scrollView.contentSize.width else {
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        let contentOffsetX = contentOffset.x + cellSize.width
+        
+        if contentOffsetX >= contentOffsetXLimit {
+            changePageToNext(completion)
+        } else {
+            cell.scrollPageToOffset(contentOffsetX, animated: true)
+        }
+        
+        completion?()
+    }
+
+    public func getCurrentPageItemNumber() -> Int {
+        guard let page = currentPage, let webView = page.webView else { return 0 }
+        
+        let pageSize = readerConfig.isDirection(pageHeight, pageWidth, pageHeight)
+        let pageOffSet = readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y)
+        let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize)
+        
+        return webViewPage
+    }
+
+    public func changePageItemToPrevious(_ completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentOffset = cell.webView?.scrollView.contentOffset else {
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        let contentOffsetX = contentOffset.x - cellSize.width
+        
+        if contentOffsetX < 0 {
+            changePageToPrevious(completion)
+        } else {
+            cell.scrollPageToOffset(contentOffsetX, animated: true)
+        }
+        
+        completion?()
+    }
+
+    public func changePageItemToLast(animated: Bool = true, _ completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentSize = cell.webView?.scrollView.contentSize else {
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        var contentOffsetX: CGFloat = 0.0
+        
+        if contentSize.width > 0 && cellSize.width > 0 {
+            contentOffsetX = (cellSize.width * (contentSize.width / cellSize.width)) - cellSize.width
+        }
+        
+        if contentOffsetX < 0 {
+            contentOffsetX = 0
+        }
+        
+        cell.scrollPageToOffset(contentOffsetX, animated: animated)
+        
+        completion?()
+    }
+
+    public func changePageItem(to: Int, animated: Bool = true, completion: (() -> Void)? = nil) {
+        // TODO: It was implemented for horizontal orientation.
+        // Need check page orientation (v/h) and make correct calc for vertical
+        guard
+            let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
+            let contentSize = cell.webView?.scrollView.contentSize else {
+                delegate?.pageItemChanged?(getCurrentPageItemNumber())
+                completion?()
+                return
+        }
+        
+        let cellSize = cell.frame.size
+        var contentOffsetX: CGFloat = 0.0
+        
+        if contentSize.width > 0 && cellSize.width > 0 {
+            contentOffsetX = (cellSize.width * CGFloat(to)) - cellSize.width
+        }
+        
+        if contentOffsetX > contentSize.width {
+            contentOffsetX = contentSize.width - cellSize.width
+        }
+        
+        if contentOffsetX < 0 {
+            contentOffsetX = 0
+        }
+        
+        UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in
+            cell.scrollPageToOffset(contentOffsetX, animated: animated)
+        }) { (finished: Bool) -> Void in
+            self.updateCurrentPage {
+                completion?()
+            }
+        }
+    }
+
+    /**
+     Find a page by FRTocReference.
+     */
+    public func findPageByResource(_ reference: FRTocReference) -> Int {
+        var count = 0
+        for item in self.book.spine.spineReferences {
+            if let resource = reference.resource, item.resource == resource {
+                return count
+            }
+            count += 1
+        }
+        return count
+    }
+
+    /**
+     Find a page by href.
+     */
+    public func findPageByHref(_ href: String) -> Int {
+        var count = 0
+        for item in self.book.spine.spineReferences {
+            if item.resource.href == href {
+                return count
+            }
+            count += 1
+        }
+        return count
+    }
+
+    /**
+     Find and return the current chapter resource.
+     */
+    public func getCurrentChapter() -> FRResource? {
+        for item in self.book.flatTableOfContents {
+            if
+                let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)],
+                let resource = item.resource,
+                (resource == reference.resource) {
+                return item.resource
+            }
+        }
+        return nil
+    }
+
+    /**
+     Return the current chapter progress based on current chapter and total of chapters.
+     */
+    public func getCurrentChapterProgress() -> CGFloat {
+        let total = totalPages
+        let current = currentPageNumber
+        
+        if total == 0 {
+            return 0
+        }
+        
+        return CGFloat((100 * current) / total)
+    }
+
+    /**
+     Find and return the current chapter name.
+     */
+    public func getCurrentChapterName() -> String? {
+        for item in self.book.flatTableOfContents {
+            guard
+                let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)],
+                let resource = item.resource,
+                (resource == reference.resource),
+                let title = item.title else {
+                    continue
+            }
+
+            return title
+        }
+
+        return nil
+    }
+
+    // MARK: Public page methods
+
+    /**
+     Changes the current page of the reader.
+
+     - parameter page: The target page index. Note: The page index starts at 1 (and not 0).
+     - parameter animated: En-/Disables the animation of the page change.
+     - parameter completion: A Closure which is called if the page change is completed.
+     */
+    public func changePageWith(page: Int, animated: Bool = false, completion: (() -> Void)? = nil) {
+        if page > 0 && page-1 < totalPages {
+            let indexPath = IndexPath(row: page-1, section: 0)
+            changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in
+                self.updateCurrentPage {
+                    completion?()
+                }
+            })
+        }
+    }
+
+    // MARK: - Audio Playing
+
+    func audioMark(href: String, fragmentID: String) {
+        changePageWith(href: href, andAudioMarkID: fragmentID)
+    }
+
+    // MARK: - Sharing
+
+    /**
+     Sharing chapter method.
+     */
+    @objc func shareChapter(_ sender: UIBarButtonItem) {
+        guard let currentPage = currentPage else { return }
+
+        if let chapterText = currentPage.webView?.js("getBodyText()") {
+            let htmlText = chapterText.replacingOccurrences(of: "[\\n\\r]+", with: "<br />", options: .regularExpression)
+            var subject = readerConfig.localizedShareChapterSubject
+            var html = ""
+            var text = ""
+            var bookTitle = ""
+            var chapterName = ""
+            var authorName = ""
+            var shareItems = [AnyObject]()
+
+            // Get book title
+            if let title = self.book.title {
+                bookTitle = title
+                subject += " “\(title)”"
+            }
+
+            // Get chapter name
+            if let chapter = getCurrentChapterName() {
+                chapterName = chapter
+            }
+
+            // Get author name
+            if let author = self.book.metadata.creators.first {
+                authorName = author.name
+            }
+
+            // Sharing html and text
+            html = "<html><body>"
+            html += "<br /><hr> <p>\(htmlText)</p> <hr><br />"
+            html += "<center><p style=\"color:gray\">"+readerConfig.localizedShareAllExcerptsFrom+"</p>"
+            html += "<b>\(bookTitle)</b><br />"
+            html += readerConfig.localizedShareBy+" <i>\(authorName)</i><br />"
+
+            if let bookShareLink = readerConfig.localizedShareWebLink {
+                html += "<a href=\"\(bookShareLink.absoluteString)\">\(bookShareLink.absoluteString)</a>"
+                shareItems.append(bookShareLink as AnyObject)
+            }
+
+            html += "</center></body></html>"
+            text = "\(chapterName)\n\n“\(chapterText)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)"
+
+            let act = FolioReaderSharingProvider(subject: subject, text: text, html: html)
+            shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0)
+
+            let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
+            activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo]
+
+            // Pop style on iPad
+            if let actv = activityViewController.popoverPresentationController {
+                actv.barButtonItem = sender
+            }
+
+            present(activityViewController, animated: true, completion: nil)
+        }
+    }
+
+    /**
+     Sharing highlight method.
+     */
+    func shareHighlight(_ string: String, rect: CGRect) {
+        var subject = readerConfig.localizedShareHighlightSubject
+        var html = ""
+        var text = ""
+        var bookTitle = ""
+        var chapterName = ""
+        var authorName = ""
+        var shareItems = [AnyObject]()
+
+        // Get book title
+        if let title = self.book.title {
+            bookTitle = title
+            subject += " “\(title)”"
+        }
+
+        // Get chapter name
+        if let chapter = getCurrentChapterName() {
+            chapterName = chapter
+        }
+
+        // Get author name
+        if let author = self.book.metadata.creators.first {
+            authorName = author.name
+        }
+
+        // Sharing html and text
+        html = "<html><body>"
+        html += "<br /><hr> <p>\(chapterName)</p>"
+        html += "<p>\(string)</p> <hr><br />"
+        html += "<center><p style=\"color:gray\">"+readerConfig.localizedShareAllExcerptsFrom+"</p>"
+        html += "<b>\(bookTitle)</b><br />"
+        html += readerConfig.localizedShareBy+" <i>\(authorName)</i><br />"
+
+        if let bookShareLink = readerConfig.localizedShareWebLink {
+            html += "<a href=\"\(bookShareLink.absoluteString)\">\(bookShareLink.absoluteString)</a>"
+            shareItems.append(bookShareLink as AnyObject)
+        }
+
+        html += "</center></body></html>"
+        text = "\(chapterName)\n\n“\(string)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)"
+
+        let act = FolioReaderSharingProvider(subject: subject, text: text, html: html)
+        shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0)
+
+        let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
+        activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo]
+
+        // Pop style on iPad
+        if let actv = activityViewController.popoverPresentationController {
+            actv.sourceView = currentPage
+            actv.sourceRect = rect
+        }
+
+        present(activityViewController, animated: true, completion: nil)
+    }
+
+    // MARK: - ScrollView Delegate
+
+    open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+        self.isScrolling = true
+        clearRecentlyScrolled()
+        recentlyScrolled = true
+        pointNow = scrollView.contentOffset
+
+        if let currentPage = currentPage {
+            currentPage.webView?.createMenu(options: true)
+            currentPage.webView?.setMenuVisible(false)
+        }
+
+        scrollScrubber?.scrollViewWillBeginDragging(scrollView)
+    }
+
+    open func scrollViewDidScroll(_ scrollView: UIScrollView) {
+
+        if (navigationController?.isNavigationBarHidden == false) {
+            self.toggleBars()
+        }
+
+        scrollScrubber?.scrollViewDidScroll(scrollView)
+
+        let isCollectionScrollView = (scrollView is UICollectionView)
+        let scrollType: ScrollType = ((isCollectionScrollView == true) ? .chapter : .page)
+
+        // Update current reading page
+        if (isCollectionScrollView == false), let page = currentPage, let webView = page.webView {
+
+            let pageSize = self.readerConfig.isDirection(self.pageHeight, self.pageWidth, self.pageHeight)
+            let contentOffset = webView.scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig)
+            let contentSize = webView.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig)
+            if (contentOffset + pageSize <= contentSize) {
+
+                let webViewPage = pageForOffset(contentOffset, pageHeight: pageSize)
+
+                if (readerConfig.scrollDirection == .horizontalWithVerticalContent) {
+                    let currentIndexPathRow = (page.pageNumber - 1)
+
+                    // if the cell reload doesn't save the top position offset
+                    if let oldOffSet = self.currentWebViewScrollPositions[currentIndexPathRow], (abs(oldOffSet.y - scrollView.contentOffset.y) > 100) {
+                        // Do nothing
+                    } else {
+                        self.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset
+                    }
+                }
+
+                if (pageIndicatorView?.currentPage != webViewPage) {
+                    pageIndicatorView?.currentPage = webViewPage
+                }
+                
+                self.delegate?.pageItemChanged?(webViewPage)
+            }
+        }
+
+        self.updatePageScrollDirection(inScrollView: scrollView, forScrollType: scrollType)
+    }
+
+    private func updatePageScrollDirection(inScrollView scrollView: UIScrollView, forScrollType scrollType: ScrollType) {
+        
+        let scrollViewContentOffsetForDirection = scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType)
+        let pointNowForDirection = pointNow.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType)
+        // The movement is either positive or negative. This happens if the page change isn't completed. Toggle to the other scroll direction then.
+        let isCurrentlyPositive = (self.pageScrollDirection == .left || self.pageScrollDirection == .up)
+
+        if (scrollViewContentOffsetForDirection < pointNowForDirection) {
+            self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType)
+        } else if (scrollViewContentOffsetForDirection > pointNowForDirection) {
+            self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType)
+        } else if (isCurrentlyPositive == true) {
+            self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType)
+        } else {
+            self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType)
+        }
+    }
+
+    open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+        self.isScrolling = false
+
+        // Perform the page after a short delay as the collection view hasn't completed it's transition if this method is called (the index paths aren't right during fast scrolls).
+        delay(0.2, closure: { [weak self] in
+            if (self?.readerConfig.scrollDirection == .horizontalWithVerticalContent),
+                let cell = ((scrollView.superview as? UIWebView)?.delegate as? FolioReaderPage) {
+                let currentIndexPathRow = cell.pageNumber - 1
+                self?.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset
+            }
+
+            if (scrollView is UICollectionView) {
+                guard let instance = self else {
+                    return
+                }
+                
+                if instance.totalPages > 0 {
+                    instance.updateCurrentPage()
+                    instance.delegate?.pageItemChanged?(instance.getCurrentPageItemNumber())
+                }
+            } else {
+                self?.scrollScrubber?.scrollViewDidEndDecelerating(scrollView)
+            }
+        })
+    }
+
+    open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
+        if scrollView == collectionView{            
+            scrollView.isUserInteractionEnabled = false
+            delay(0.5, closure: { [weak self] in
+                scrollView.isUserInteractionEnabled = true
+            })
+        }
+      
+        recentlyScrolledTimer = Timer(timeInterval:recentlyScrolledDelay, target: self, selector: #selector(FolioReaderCenter.clearRecentlyScrolled), userInfo: nil, repeats: false)
+        RunLoop.current.add(recentlyScrolledTimer, forMode: RunLoopMode.commonModes)
+    }
+
+    @objc func clearRecentlyScrolled() {
+        if(recentlyScrolledTimer != nil) {
+            recentlyScrolledTimer.invalidate()
+            recentlyScrolledTimer = nil
+        }
+        recentlyScrolled = false
+    }
+
+    open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
+        scrollScrubber?.scrollViewDidEndScrollingAnimation(scrollView)
+    }
+
+    // MARK: NavigationBar Actions
+
+    @objc func closeReader(_ sender: UIBarButtonItem) {
+        dismiss()
+        folioReader.close()
+    }
+
+    //PD: changed
+    /**
+     Present chapter list
+     */
+    @objc func presentChapterList(_ sender: UIBarButtonItem) {
+        folioReader.saveReaderState()
+
+        let chapter = FolioReaderChapterList(folioReader: folioReader, readerConfig: readerConfig, book: book, delegate: self)
+        chapter.title = "Spis treści"
+//        let highlight = FolioReaderHighlightList(folioReader: folioReader, readerConfig: readerConfig)
+//        let pageController = PageViewController(folioReader: folioReader, readerConfig: readerConfig)
+
+//        pageController.viewControllerOne = chapter
+//        pageController.viewControllerTwo = highlight
+//        pageController.segmentedControlItems = [readerConfig.localizedContentsTitle, readerConfig.localizedHighlightsTitle]
+
+        let vc = FolioReaderModalViewController(folioReader: folioReader, readerConfig: readerConfig)
+        
+        vc.viewControllerOne = chapter
+        let nav = UINavigationController(rootViewController:vc /*pageController*/)
+        present(nav, animated: true, completion: nil)
+    }
+    
+    /**
+     Present fonts and settings menu
+     */
+    @objc func presentFontsMenu() {
+        folioReader.saveReaderState()
+        hideBars()
+
+        let menu = FolioReaderFontsMenu(folioReader: folioReader, readerConfig: readerConfig)
+        menu.modalPresentationStyle = .custom
+
+        animator = ZFModalTransitionAnimator(modalViewController: menu)
+        animator.isDragable = false
+        animator.bounces = false
+        animator.behindViewAlpha = 0.4
+        animator.behindViewScale = 1
+        animator.transitionDuration = 0.6
+        animator.direction = ZFModalTransitonDirection.bottom
+
+        menu.transitioningDelegate = animator
+        self.present(menu, animated: true, completion: nil)
+    }
+
+    /**
+     Present audio player menu
+     */
+    @objc func presentPlayerMenu(_ sender: UIBarButtonItem) {
+        folioReader.saveReaderState()
+        hideBars()
+
+        let menu = FolioReaderPlayerMenu(folioReader: folioReader, readerConfig: readerConfig)
+        menu.modalPresentationStyle = .custom
+
+        animator = ZFModalTransitionAnimator(modalViewController: menu)
+        animator.isDragable = true
+        animator.bounces = false
+        animator.behindViewAlpha = 0.4
+        animator.behindViewScale = 1
+        animator.transitionDuration = 0.6
+        animator.direction = ZFModalTransitonDirection.bottom
+
+        menu.transitioningDelegate = animator
+        present(menu, animated: true, completion: nil)
+    }
+
+    /**
+     Present Quote Share
+     */
+    func presentQuoteShare(_ string: String) {
+        let quoteShare = FolioReaderQuoteShare(initWithText: string, readerConfig: readerConfig, folioReader: folioReader, book: book)
+        let nav = UINavigationController(rootViewController: quoteShare)
+
+        if UIDevice.current.userInterfaceIdiom == .pad {
+            nav.modalPresentationStyle = .formSheet
+        }
+        present(nav, animated: true, completion: nil)
+    }
+}
+
+// MARK: FolioPageDelegate
+
+extension FolioReaderCenter: FolioReaderPageDelegate {
+
+    public func pageDidLoad(_ page: FolioReaderPage) {
+        if self.readerConfig.loadSavedPositionForCurrentBook, let position = folioReader.savedPositionForCurrentBook {
+            let pageNumber = position["pageNumber"] as? Int
+            let offset = self.readerConfig.isDirection(position["pageOffsetY"], position["pageOffsetX"], position["pageOffsetY"]) as? CGFloat
+            let pageOffset = offset
+
+            if isFirstLoad {
+                updateCurrentPage(page)
+                isFirstLoad = false
+
+                if (self.currentPageNumber == pageNumber && pageOffset > 0) {
+                    page.scrollPageToOffset(pageOffset!, animated: false)
+                }
+            } else if (self.isScrolling == false && folioReader.needsRTLChange == true) {
+                page.scrollPageToBottom()
+            }
+        } else if isFirstLoad {
+            updateCurrentPage(page)
+            isFirstLoad = false
+        }
+
+        // Go to fragment if needed
+        if let fragmentID = tempFragment, let currentPage = currentPage , fragmentID != "" {
+            currentPage.handleAnchor(fragmentID, avoidBeginningAnchors: true, animated: true)
+            tempFragment = nil
+        }
+        
+        if (readerConfig.scrollDirection == .horizontalWithVerticalContent),
+            let offsetPoint = self.currentWebViewScrollPositions[page.pageNumber - 1] {
+            page.webView?.scrollView.setContentOffset(offsetPoint, animated: false)
+        }
+        
+        // Pass the event to the centers `pageDelegate`
+        pageDelegate?.pageDidLoad?(page)
+    }
+    
+    public func pageWillLoad(_ page: FolioReaderPage) {
+        // Pass the event to the centers `pageDelegate`
+        pageDelegate?.pageWillLoad?(page)
+    }
+    
+    public func pageTap(_ recognizer: UITapGestureRecognizer) {
+        // Pass the event to the centers `pageDelegate`
+        pageDelegate?.pageTap?(recognizer)
+    }
+    
+}
+
+// MARK: FolioReaderChapterListDelegate
+
+extension FolioReaderCenter: FolioReaderChapterListDelegate {
+    
+    func chapterList(_ chapterList: FolioReaderChapterList, didSelectRowAtIndexPath indexPath: IndexPath, withTocReference reference: FRTocReference) {
+        let item = findPageByResource(reference)
+        
+        if item < totalPages {
+            let indexPath = IndexPath(row: item, section: 0)
+            changingChapter = true
+            changePageWith(indexPath: indexPath, animated: false, completion: { () -> Void in
+                self.updateCurrentPage()
+                self.changingChapter = false
+            })
+            tempReference = reference
+        } else {
+            print("Failed to load book because the requested resource is missing.")
+        }
+    }
+    
+    func chapterList(didDismissedChapterList chapterList: FolioReaderChapterList) {
+        updateCurrentPage()
+        
+        // Move to #fragment
+        if let reference = tempReference {
+            if let fragmentID = reference.fragmentID, let currentPage = currentPage , fragmentID != "" {
+                currentPage.handleAnchor(reference.fragmentID!, avoidBeginningAnchors: true, animated: true)
+            }
+            tempReference = nil
+        }
+    }
+    
+    func getScreenBounds() -> CGRect {
+        var bounds = view.frame
+        
+        if #available(iOS 11.0, *) {
+            bounds.size.height = bounds.size.height - view.safeAreaInsets.bottom
+        }
+        
+        return bounds
+    }
+    
+}
+
+class FolioReaderModalViewController: UIViewController{
+    
+    var viewControllerOne: UIViewController!
+    fileprivate var readerConfig: FolioReaderConfig
+    fileprivate var folioReader: FolioReader
+    
+    // MARK: Init
+    
+    init(folioReader: FolioReader, readerConfig: FolioReaderConfig) {
+        self.folioReader = folioReader
+        self.readerConfig = readerConfig
+        super.init(nibName: nil, bundle: nil)
+        self.edgesForExtendedLayout = UIRectEdge()
+        self.extendedLayoutIncludesOpaqueBars = true
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("storyboards are incompatible with truth and beauty")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        self.navigationItem.title = viewControllerOne.title
+        
+        addChildViewController(viewControllerOne)
+        viewControllerOne.view.frame = view.bounds
+        view.addSubview(viewControllerOne.view)
+        viewControllerOne.view.translatesAutoresizingMaskIntoConstraints = false
+        if #available(iOS 9.0, *) {
+            viewControllerOne.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
+            viewControllerOne.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
+            viewControllerOne.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
+            viewControllerOne.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
+        } else {
+            // Fallback on earlier versions
+        }
+        viewControllerOne.didMove(toParentViewController: self)
+        
+//        self.view.backgroundColor = UIColor.white
+//        self.setViewControllers([viewList[index]], direction: .forward, animated: false, completion: nil)
+        
+        //PD: changed
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)
+        
+        let closeImage = UIImage(readerImageNamed: "icon-navbar-close")?.imageTintColor(tintColor)?.withRenderingMode(.alwaysOriginal)// ignoreSystemTint(withConfiguration: readerConfig)
+        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: closeImage, style: .plain, target: self, action: #selector(dismiss as () -> Void))
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+        configureNavBar()
+    }
+    
+    func configureNavBar() {
+        
+        let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
+        let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor)
+        let tintColor = folioReader.isNight(greenColor, UIColor.white)
+        let navText = tintColor
+        let font = UIFont(name: "Avenir-Light", size: 17)!
+        setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
+    }
+    
+    //PD: changed
+    override var preferredStatusBarStyle : UIStatusBarStyle {
+        return .lightContent //self.folioReader.isNight(.lightContent, .default)
+    }
+}