added iOS source code
[wl-app.git] / iOS / Pods / FolioReaderKit / Source / FolioReaderCenter.swift
1 //
2 //  FolioReaderCenter.swift
3 //  FolioReaderKit
4 //
5 //  Created by Heberti Almeida on 08/04/15.
6 //  Copyright (c) 2015 Folio Reader. All rights reserved.
7 //
8
9 import UIKit
10 import ZFDragableModalTransition
11
12 /// Protocol which is used from `FolioReaderCenter`s.
13 @objc public protocol FolioReaderCenterDelegate: class {
14
15     /// Notifies that a page appeared. This is triggered when a page is chosen and displayed.
16     ///
17     /// - Parameter page: The appeared page
18     @objc optional func pageDidAppear(_ page: FolioReaderPage)
19
20     /// Passes and returns the HTML content as `String`. Implement this method if you want to modify the HTML content of a `FolioReaderPage`.
21     ///
22     /// - Parameters:
23     ///   - page: The `FolioReaderPage`.
24     ///   - htmlContent: The current HTML content as `String`.
25     /// - Returns: The adjusted HTML content as `String`. This is the content which will be loaded into the given `FolioReaderPage`.
26     @objc optional func htmlContentForPage(_ page: FolioReaderPage, htmlContent: String) -> String
27     
28     /// Notifies that a page changed. This is triggered when collection view cell is changed.
29     ///
30     /// - Parameter pageNumber: The appeared page item
31     @objc optional func pageItemChanged(_ pageNumber: Int)
32
33 }
34
35 /// The base reader class
36 open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
37
38     /// This delegate receives the events from the current `FolioReaderPage`s delegate.
39     open weak var delegate: FolioReaderCenterDelegate?
40
41     /// This delegate receives the events from current page
42     open weak var pageDelegate: FolioReaderPageDelegate?
43
44     /// The base reader container
45     open weak var readerContainer: FolioReaderContainer?
46
47     /// The current visible page on reader
48     open fileprivate(set) var currentPage: FolioReaderPage?
49
50     /// The collection view with pages
51     open var collectionView: UICollectionView!
52     
53     let collectionViewLayout = UICollectionViewFlowLayout()
54     var loadingView: UIActivityIndicatorView!
55     var pages: [String]!
56     var totalPages: Int = 0
57     var tempFragment: String?
58     var animator: ZFModalTransitionAnimator!
59     var pageIndicatorView: FolioReaderPageIndicator?
60     var pageIndicatorHeight: CGFloat = 20
61     var recentlyScrolled = false
62     var recentlyScrolledDelay = 2.0 // 2 second delay until we clear recentlyScrolled
63     var recentlyScrolledTimer: Timer!
64     var scrollScrubber: ScrollScrubber?
65     var activityIndicator = UIActivityIndicatorView()
66     var isScrolling = false
67     var pageScrollDirection = ScrollDirection()
68     var nextPageNumber: Int = 0
69     var previousPageNumber: Int = 0
70     var currentPageNumber: Int = 0
71     var pageWidth: CGFloat = 0.0
72     var pageHeight: CGFloat = 0.0
73     var anchor: String!
74     var lastRow: Int?
75
76     fileprivate var screenBounds: CGRect!
77     fileprivate var pointNow = CGPoint.zero
78     fileprivate var pageOffsetRate: CGFloat = 0
79     fileprivate var tempReference: FRTocReference?
80     fileprivate var isFirstLoad = true
81     fileprivate var currentWebViewScrollPositions = [Int: CGPoint]()
82     fileprivate var currentOrientation: UIInterfaceOrientation?
83     fileprivate var changingChapter = false
84
85     fileprivate var readerConfig: FolioReaderConfig {
86         guard let readerContainer = readerContainer else { return FolioReaderConfig() }
87         return readerContainer.readerConfig
88     }
89
90     fileprivate var book: FRBook {
91         guard let readerContainer = readerContainer else { return FRBook() }
92         return readerContainer.book
93     }
94
95     fileprivate var folioReader: FolioReader {
96         guard let readerContainer = readerContainer else { return FolioReader() }
97         return readerContainer.folioReader
98     }
99
100     // MARK: - Init
101
102     init(withContainer readerContainer: FolioReaderContainer) {
103         self.readerContainer = readerContainer
104         super.init(nibName: nil, bundle: Bundle.frameworkBundle())
105
106         self.initialization()
107     }
108
109     required public init?(coder aDecoder: NSCoder) {
110         fatalError("This class doesn't support NSCoding.")
111     }
112
113     /**
114      Common Initialization
115      */
116     fileprivate func initialization() {
117
118         if (self.readerConfig.hideBars == true) {
119             self.pageIndicatorHeight = 0
120         }
121         
122         self.totalPages = book.spine.spineReferences.count
123
124         // Loading indicator
125         let style: UIActivityIndicatorViewStyle = folioReader.isNight(.white, .gray)
126         loadingView = UIActivityIndicatorView(activityIndicatorStyle: style)
127         loadingView.hidesWhenStopped = true
128         loadingView.startAnimating()
129         self.view.addSubview(loadingView)
130     }
131
132     // MARK: - View life cicle
133
134     override open func viewDidLoad() {
135         super.viewDidLoad()
136
137         screenBounds = self.getScreenBounds()
138         
139         setPageSize(UIApplication.shared.statusBarOrientation)
140
141         // Layout
142         collectionViewLayout.sectionInset = UIEdgeInsets.zero
143         collectionViewLayout.minimumLineSpacing = 0
144         collectionViewLayout.minimumInteritemSpacing = 0
145         collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig)
146         
147         let background = folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white)
148         view.backgroundColor = background
149
150         // CollectionView
151         collectionView = UICollectionView(frame: screenBounds, collectionViewLayout: collectionViewLayout)
152         collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
153         collectionView.delegate = self
154         collectionView.dataSource = self
155         collectionView.isPagingEnabled = true
156         collectionView.showsVerticalScrollIndicator = false
157         collectionView.showsHorizontalScrollIndicator = false
158         collectionView.backgroundColor = background
159         collectionView.decelerationRate = UIScrollViewDecelerationRateFast
160         enableScrollBetweenChapters(scrollEnabled: true)
161         view.addSubview(collectionView)
162
163         if #available(iOS 11.0, *) {
164             collectionView.contentInsetAdjustmentBehavior = .never
165         }
166         
167         // Activity Indicator
168         self.activityIndicator.activityIndicatorViewStyle = .gray
169         self.activityIndicator.hidesWhenStopped = true
170         self.activityIndicator = UIActivityIndicatorView(frame: CGRect(x: screenBounds.size.width/2, y: screenBounds.size.height/2, width: 30, height: 30))
171         self.activityIndicator.backgroundColor = UIColor.gray
172         self.view.addSubview(self.activityIndicator)
173         self.view.bringSubview(toFront: self.activityIndicator)
174
175         if #available(iOS 10.0, *) {
176             collectionView.isPrefetchingEnabled = false
177         }
178
179         // Register cell classes
180         collectionView?.register(FolioReaderPage.self, forCellWithReuseIdentifier: kReuseCellIdentifier)
181
182         // Configure navigation bar and layout
183         automaticallyAdjustsScrollViewInsets = false
184         extendedLayoutIncludesOpaqueBars = true
185         configureNavBar()
186
187         // Page indicator view
188         if (self.readerConfig.hidePageIndicator == false) {
189             let frame = self.frameForPageIndicatorView()
190             pageIndicatorView = FolioReaderPageIndicator(frame: frame, readerConfig: readerConfig, folioReader: folioReader)
191             if let pageIndicatorView = pageIndicatorView {
192                 view.addSubview(pageIndicatorView)
193             }
194         }
195
196         guard let readerContainer = readerContainer else { return }
197         self.scrollScrubber = ScrollScrubber(frame: frameForScrollScrubber(), withReaderContainer: readerContainer)
198         self.scrollScrubber?.delegate = self
199         if let scrollScrubber = scrollScrubber {
200             view.addSubview(scrollScrubber.slider)
201         }
202     }
203
204     override open func viewWillAppear(_ animated: Bool) {
205         super.viewWillAppear(animated)
206
207         configureNavBar()
208
209         // Update pages
210         pagesForCurrentPage(currentPage)
211         pageIndicatorView?.reloadView(updateShadow: true)
212     }
213
214     override open func viewDidLayoutSubviews() {
215         super.viewDidLayoutSubviews()
216
217         screenBounds = self.getScreenBounds()
218         loadingView.center = view.center
219
220         setPageSize(UIApplication.shared.statusBarOrientation)
221         updateSubviewFrames()
222     }
223
224     // MARK: Layout
225
226     /**
227      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`.
228
229      - parameter scrollEnabled: `Bool` which enables or disables the scrolling between `FolioReaderPage`s.
230      */
231     open func enableScrollBetweenChapters(scrollEnabled: Bool) {
232         self.collectionView.isScrollEnabled = scrollEnabled
233     }
234
235     fileprivate func updateSubviewFrames() {
236         self.pageIndicatorView?.frame = self.frameForPageIndicatorView()
237         self.scrollScrubber?.frame = self.frameForScrollScrubber()
238     }
239
240     fileprivate func frameForPageIndicatorView() -> CGRect {
241         var bounds = CGRect(x: 0, y: screenBounds.size.height-pageIndicatorHeight, width: screenBounds.size.width, height: pageIndicatorHeight)
242         
243         if #available(iOS 11.0, *) {
244             bounds.size.height = bounds.size.height + view.safeAreaInsets.bottom
245         }
246         
247         return bounds
248     }
249
250     fileprivate func frameForScrollScrubber() -> CGRect {
251         let scrubberY: CGFloat = ((self.readerConfig.shouldHideNavigationOnTap == true || self.readerConfig.hideBars == true) ? 50 : 74)
252         return CGRect(x: self.pageWidth + 10, y: scrubberY, width: 40, height: (self.pageHeight - 100))
253     }
254
255     func configureNavBar() {
256         let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
257         let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor)
258         let tintColor = folioReader.isNight(greenColor, UIColor.white)// readerConfig.tintColor
259         let navText = tintColor// folioReader.isNight(UIColor.white, UIColor.black)
260         let font = UIFont(name: "Avenir-Light", size: 17)!
261         setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
262     }
263
264     func configureNavBarButtons() {
265
266         // Navbar buttons
267         let shareIcon = UIImage(readerImageNamed: "icon-navbar-share")//.ignoreSystemTint(withConfiguration: self.readerConfig)
268         let audioIcon = UIImage(readerImageNamed: "icon-navbar-tts")//?.ignoreSystemTint(withConfiguration: self.readerConfig) //man-speech-icon
269         let closeIcon = UIImage(readerImageNamed: "icon-navbar-close")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
270         let tocIcon = UIImage(readerImageNamed: "icon-navbar-toc")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
271         let fontIcon = UIImage(readerImageNamed: "icon-navbar-font")//?.ignoreSystemTint(withConfiguration: self.readerConfig)
272         let space = 70 as CGFloat
273
274         let menu = UIBarButtonItem(image: closeIcon, style: .plain, target: self, action:#selector(closeReader(_:)))
275         let toc = UIBarButtonItem(image: tocIcon, style: .plain, target: self, action:#selector(presentChapterList(_:)))
276
277         navigationItem.leftBarButtonItems = [menu, toc]
278
279         var rightBarIcons = [UIBarButtonItem]()
280
281         if (self.readerConfig.allowSharing == true) {
282             rightBarIcons.append(UIBarButtonItem(image: shareIcon, style: .plain, target: self, action:#selector(shareChapter(_:))))
283         }
284
285         if self.book.hasAudio || self.readerConfig.enableTTS {
286             rightBarIcons.append(UIBarButtonItem(image: audioIcon, style: .plain, target: self, action:#selector(presentPlayerMenu(_:))))
287         }
288
289         let font = UIBarButtonItem(image: fontIcon, style: .plain, target: self, action: #selector(presentFontsMenu))
290         font.width = space
291
292         rightBarIcons.append(contentsOf: [font])
293         navigationItem.rightBarButtonItems = rightBarIcons
294         
295         if(self.readerConfig.displayTitle){
296             navigationItem.title = book.title
297         }
298     }
299
300     func reloadData() {
301         self.loadingView.stopAnimating()
302         self.totalPages = book.spine.spineReferences.count
303
304         self.collectionView.reloadData()
305         self.configureNavBarButtons()
306         self.setCollectionViewProgressiveDirection()
307
308         if self.readerConfig.loadSavedPositionForCurrentBook {
309             guard let position = folioReader.savedPositionForCurrentBook, let pageNumber = position["pageNumber"] as? Int, pageNumber > 0 else {
310                 self.currentPageNumber = 1
311                 return
312             }
313
314             self.changePageWith(page: pageNumber)
315             self.currentPageNumber = pageNumber
316         }
317     }
318
319     // MARK: Change page progressive direction
320
321     private func transformViewForRTL(_ view: UIView?) {
322         if folioReader.needsRTLChange {
323             view?.transform = CGAffineTransform(scaleX: -1, y: 1)
324         } else {
325             view?.transform = CGAffineTransform.identity
326         }
327     }
328
329     func setCollectionViewProgressiveDirection() {
330         self.transformViewForRTL(self.collectionView)
331     }
332
333     func setPageProgressiveDirection(_ page: FolioReaderPage) {
334         self.transformViewForRTL(page)
335     }
336
337     // MARK: Change layout orientation
338
339     /// Get internal page offset before layout change
340     private func updatePageOffsetRate() {
341         guard let currentPage = self.currentPage, let webView = currentPage.webView else {
342             return
343         }
344
345         let pageScrollView = webView.scrollView
346         let contentSize = pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig)
347         let contentOffset = pageScrollView.contentOffset.forDirection(withConfiguration: self.readerConfig)
348         self.pageOffsetRate = (contentSize != 0 ? (contentOffset / contentSize) : 0)
349     }
350
351     func setScrollDirection(_ direction: FolioReaderScrollDirection) {
352         guard let currentPage = self.currentPage, let webView = currentPage.webView else {
353             return
354         }
355
356         let pageScrollView = webView.scrollView
357
358         // Get internal page offset before layout change
359         self.updatePageOffsetRate()
360         // Change layout
361         self.readerConfig.scrollDirection = direction
362         self.collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig)
363         self.currentPage?.setNeedsLayout()
364         self.collectionView.collectionViewLayout.invalidateLayout()
365         self.collectionView.setContentOffset(frameForPage(self.currentPageNumber).origin, animated: false)
366
367         // Page progressive direction
368         self.setCollectionViewProgressiveDirection()
369         delay(0.2) { self.setPageProgressiveDirection(currentPage) }
370
371
372         /**
373          *  This delay is needed because the page will not be ready yet
374          *  so the delay wait until layout finished the changes.
375          */
376         delay(0.1) {
377             var pageOffset = (pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig) * self.pageOffsetRate)
378
379             // Fix the offset for paged scroll
380             if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) {
381                 let page = round(pageOffset / self.pageWidth)
382                 pageOffset = (page * self.pageWidth)
383             }
384
385             let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset))
386             pageScrollView.setContentOffset(pageOffsetPoint, animated: true)
387         }
388     }
389
390     // MARK: Status bar and Navigation bar
391
392     func hideBars() {
393         guard self.readerConfig.shouldHideNavigationOnTap == true else {
394             return
395         }
396
397         self.updateBarsStatus(true)
398     }
399
400     func showBars() {
401         self.configureNavBar()
402         self.updateBarsStatus(false)
403     }
404
405     func toggleBars() {
406         guard self.readerConfig.shouldHideNavigationOnTap == true else {
407             return
408         }
409
410         let shouldHide = !self.navigationController!.isNavigationBarHidden
411         if shouldHide == false {
412             self.configureNavBar()
413         }
414
415         self.updateBarsStatus(shouldHide)
416     }
417
418     private func updateBarsStatus(_ shouldHide: Bool, shouldShowIndicator: Bool = false) {
419         guard let readerContainer = readerContainer else { return }
420         readerContainer.shouldHideStatusBar = shouldHide
421
422         UIView.animate(withDuration: 0.25, animations: {
423             readerContainer.setNeedsStatusBarAppearanceUpdate()
424
425             // Show minutes indicator
426             if (shouldShowIndicator == true) {
427                 self.pageIndicatorView?.minutesLabel.alpha = shouldHide ? 0 : 1
428             }
429         })
430         self.navigationController?.setNavigationBarHidden(shouldHide, animated: true)
431     }
432
433     // MARK: UICollectionViewDataSource
434
435     open func numberOfSections(in collectionView: UICollectionView) -> Int {
436         return 1
437     }
438
439     open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
440         return totalPages
441     }
442
443     open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
444         let reuseableCell = collectionView.dequeueReusableCell(withReuseIdentifier: kReuseCellIdentifier, for: indexPath) as? FolioReaderPage
445         
446         return self.configure(readerPageCell: reuseableCell, atIndexPath: indexPath)
447     }
448
449     private func configure(readerPageCell cell: FolioReaderPage?, atIndexPath indexPath: IndexPath) -> UICollectionViewCell {
450         guard let cell = cell, let readerContainer = readerContainer else {
451             return UICollectionViewCell()
452         }
453         
454         var isNextChapter = true
455         if let lastRow = lastRow {
456             isNextChapter = lastRow < indexPath.row
457         }
458         if changingChapter {
459             isNextChapter = true
460         }
461         lastRow = indexPath.row
462
463         cell.setup(withReaderContainer: readerContainer)
464         cell.pageNumber = indexPath.row+1
465         cell.webView?.scrollView.delegate = self
466         if #available(iOS 11.0, *) {
467             cell.webView?.scrollView.contentInsetAdjustmentBehavior = .never
468         }
469         cell.webView?.setupScrollDirection()
470         cell.webView?.frame = cell.webViewFrame()
471         cell.delegate = self
472         cell.backgroundColor = .clear
473
474         setPageProgressiveDirection(cell)
475
476         // Configure the cell
477         let resource = self.book.spine.spineReferences[indexPath.row].resource
478         guard var html = try? String(contentsOfFile: resource.fullHref, encoding: String.Encoding.utf8) else {
479             return cell
480         }
481
482         let mediaOverlayStyleColors = "\"\(self.readerConfig.mediaOverlayColor.hexString(false))\", \"\(self.readerConfig.mediaOverlayColor.highlightColor().hexString(false))\""
483
484         // Inject CSS
485         let jsFilePath = Bundle.frameworkBundle().path(forResource: "Bridge", ofType: "js")
486         let cssFilePath = Bundle.frameworkBundle().path(forResource: "Style", ofType: "css")
487         let cssTag = "<link rel=\"stylesheet\" type=\"text/css\" href=\"\(cssFilePath!)\">"
488         let jsTag = "<script type=\"text/javascript\" src=\"\(jsFilePath!)\"></script>" +
489         "<script type=\"text/javascript\">setMediaOverlayStyleColors(\(mediaOverlayStyleColors))</script>"
490
491         let toInject = "\n\(cssTag)\n\(jsTag)\n</head>"
492         html = html.replacingOccurrences(of: "</head>", with: toInject)
493
494         // Font class name
495         var classes = folioReader.currentFont.cssIdentifier
496         classes += " " + folioReader.currentMediaOverlayStyle.className()
497
498         // Night mode
499         if folioReader.nightMode {
500             classes += " nightMode"
501         }
502
503         // Font Size
504         classes += " \(folioReader.currentFontSize.cssIdentifier(sliderType: .font))"
505         classes += " \(folioReader.currentMarginSize.cssIdentifier(sliderType: .margin))"
506         classes += " \(folioReader.currentInterlineSize.cssIdentifier(sliderType: .interline))"
507
508         
509         
510         html = html.replacingOccurrences(of: "<html ", with: "<html class=\"\(classes)\"")
511
512         // Let the delegate adjust the html string
513         if let modifiedHtmlContent = self.delegate?.htmlContentForPage?(cell, htmlContent: html) {
514             html = modifiedHtmlContent
515         }
516
517 //        print("\n\nhtmlhtml\n\n\(html)")
518         
519         cell.loadHTMLString(html, baseURL: URL(fileURLWithPath: resource.fullHref.deletingLastPathComponent), isNextChapter: isNextChapter)
520         return cell
521     }
522
523     public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
524         var size = CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
525         
526         if #available(iOS 11.0, *) {
527             let orientation = UIDevice.current.orientation
528             
529             if orientation == .portrait || orientation == .portraitUpsideDown {
530                 if readerConfig.scrollDirection == .horizontal {
531                     size.height = size.height - view.safeAreaInsets.bottom
532                 }
533             }
534         }
535         
536         return size
537     }
538     
539     // MARK: - Device rotation
540
541     override open func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
542         guard folioReader.isReaderReady else { return }
543
544         setPageSize(toInterfaceOrientation)
545         updateCurrentPage()
546
547         if self.currentOrientation == nil || (self.currentOrientation?.isPortrait != toInterfaceOrientation.isPortrait) {
548             var pageIndicatorFrame = pageIndicatorView?.frame
549             pageIndicatorFrame?.origin.y = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - pageIndicatorHeight) : (self.collectionView.frame.width - pageIndicatorHeight))
550             pageIndicatorFrame?.origin.x = 0
551             pageIndicatorFrame?.size.width = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.width) : (self.collectionView.frame.height))
552             pageIndicatorFrame?.size.height = pageIndicatorHeight
553
554             var scrollScrubberFrame = scrollScrubber?.slider.frame;
555             scrollScrubberFrame?.origin.x = ((screenBounds.size.height < screenBounds.size.width) ? (screenBounds.size.width - 100) : (screenBounds.size.height + 10))
556             scrollScrubberFrame?.size.height = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - 100) : (self.collectionView.frame.width - 100))
557
558             self.collectionView.collectionViewLayout.invalidateLayout()
559
560             UIView.animate(withDuration: duration, animations: {
561                 // Adjust page indicator view
562                 if let pageIndicatorFrame = pageIndicatorFrame {
563                     self.pageIndicatorView?.frame = pageIndicatorFrame
564                     self.pageIndicatorView?.reloadView(updateShadow: true)
565                 }
566
567                 // Adjust scroll scrubber slider
568                 if let scrollScrubberFrame = scrollScrubberFrame {
569                     self.scrollScrubber?.slider.frame = scrollScrubberFrame
570                 }
571
572                 // Adjust collectionView
573                 self.collectionView.contentSize = self.readerConfig.isDirection(
574                     CGSize(width: self.pageWidth, height: self.pageHeight * CGFloat(self.totalPages)),
575                     CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight),
576                     CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight)
577                 )
578                 self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false)
579                 self.collectionView.collectionViewLayout.invalidateLayout()
580
581                 // Adjust internal page offset
582                 self.updatePageOffsetRate()
583             })
584         }
585
586         self.currentOrientation = toInterfaceOrientation
587     }
588
589     override open func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
590         guard folioReader.isReaderReady == true, let currentPage = currentPage else {
591             return
592         }
593
594         // Update pages
595         pagesForCurrentPage(currentPage)
596         currentPage.refreshPageMode()
597
598         scrollScrubber?.setSliderVal()
599
600         // After rotation fix internal page offset
601         var pageOffset = (currentPage.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0) * pageOffsetRate
602
603         // Fix the offset for paged scroll
604         if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) {
605             let page = round(pageOffset / self.pageWidth)
606             pageOffset = page * self.pageWidth
607         }
608
609         let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset))
610         currentPage.webView?.scrollView.setContentOffset(pageOffsetPoint, animated: true)
611     }
612
613     override open func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
614         guard folioReader.isReaderReady else {
615             return
616         }
617
618         self.collectionView.scrollToItem(at: IndexPath(row: self.currentPageNumber - 1, section: 0), at: UICollectionViewScrollPosition(), animated: false)
619         if (self.currentPageNumber + 1) >= totalPages {
620             UIView.animate(withDuration: duration, animations: {
621                 self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false)
622             })
623         }
624     }
625
626     // MARK: - Page
627
628     func setPageSize(_ orientation: UIInterfaceOrientation) {
629         guard orientation.isPortrait else {
630             if screenBounds.size.width > screenBounds.size.height {
631                 self.pageWidth = screenBounds.size.width
632                 self.pageHeight = screenBounds.size.height
633             } else {
634                 self.pageWidth = screenBounds.size.height
635                 self.pageHeight = screenBounds.size.width
636             }
637             return
638         }
639
640         if screenBounds.size.width < screenBounds.size.height {
641             self.pageWidth = screenBounds.size.width
642             self.pageHeight = screenBounds.size.height
643         } else {
644             self.pageWidth = screenBounds.size.height
645             self.pageHeight = screenBounds.size.width
646         }
647     }
648
649     func updateCurrentPage(_ page: FolioReaderPage? = nil, completion: (() -> Void)? = nil) {
650         if let page = page {
651             currentPage = page
652             self.previousPageNumber = page.pageNumber-1
653             self.currentPageNumber = page.pageNumber
654         } else {
655             let currentIndexPath = getCurrentIndexPath()
656             currentPage = collectionView.cellForItem(at: currentIndexPath) as? FolioReaderPage
657
658             self.previousPageNumber = currentIndexPath.row
659             self.currentPageNumber = currentIndexPath.row+1
660         }
661
662         self.nextPageNumber = (((self.currentPageNumber + 1) <= totalPages) ? (self.currentPageNumber + 1) : self.currentPageNumber)
663
664         // Set pages
665         guard let currentPage = currentPage else {
666             completion?()
667             return
668         }
669
670         scrollScrubber?.setSliderVal()
671
672         if let readingTime = currentPage.webView?.js("getReadingTime()") {
673             pageIndicatorView?.totalMinutes = Int(readingTime)!
674         } else {
675             pageIndicatorView?.totalMinutes = 0
676         }
677         pagesForCurrentPage(currentPage)
678
679         delegate?.pageDidAppear?(currentPage)
680         delegate?.pageItemChanged?(self.getCurrentPageItemNumber())
681
682         completion?()
683     }
684
685     func pagesForCurrentPage(_ page: FolioReaderPage?) {
686         guard let page = page, let webView = page.webView else { return }
687
688         let pageSize = self.readerConfig.isDirection(pageHeight, self.pageWidth, pageHeight)
689         let contentSize = page.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0
690         self.pageIndicatorView?.totalPages = ((pageSize != 0) ? Int(ceil(contentSize / pageSize)) : 0)
691
692         let pageOffSet = self.readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y)
693         let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize)
694
695         self.pageIndicatorView?.currentPage = webViewPage
696     }
697
698     func pageForOffset(_ offset: CGFloat, pageHeight height: CGFloat) -> Int {
699         guard (height != 0) else {
700             return 0
701         }
702
703         let page = Int(ceil(offset / height))+1
704         return page
705     }
706
707     func getCurrentIndexPath() -> IndexPath {
708         let indexPaths = collectionView.indexPathsForVisibleItems
709         var indexPath = IndexPath()
710
711         if indexPaths.count > 1 {
712             let first = indexPaths.first!
713             let last = indexPaths.last!
714
715             switch self.pageScrollDirection {
716             case .up, .left:
717                 if first.compare(last) == .orderedAscending {
718                     indexPath = last
719                 } else {
720                     indexPath = first
721                 }
722             default:
723                 if first.compare(last) == .orderedAscending {
724                     indexPath = first
725                 } else {
726                     indexPath = last
727                 }
728             }
729         } else {
730             indexPath = indexPaths.first ?? IndexPath(row: 0, section: 0)
731         }
732
733         return indexPath
734     }
735
736     func frameForPage(_ page: Int) -> CGRect {
737         return self.readerConfig.isDirection(
738             CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight),
739             CGRect(x: self.pageWidth * CGFloat(page-1), y: 0, width: self.pageWidth, height: self.pageHeight),
740             CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight)
741         )
742     }
743
744     open func changePageWith(page: Int, andFragment fragment: String, animated: Bool = false, completion: (() -> Void)? = nil) {
745         if (self.currentPageNumber == page) {
746             if let currentPage = currentPage , fragment != "" {
747                 currentPage.handleAnchor(fragment, avoidBeginningAnchors: true, animated: animated)
748             }
749             completion?()
750         } else {
751             tempFragment = fragment
752             changePageWith(page: page, animated: animated, completion: { () -> Void in
753                 self.updateCurrentPage {
754                     completion?()
755                 }
756             })
757         }
758     }
759
760     open func changePageWith(href: String, animated: Bool = false, completion: (() -> Void)? = nil) {
761         let item = findPageByHref(href)
762         let indexPath = IndexPath(row: item, section: 0)
763         changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in
764             self.updateCurrentPage {
765                 completion?()
766             }
767         })
768     }
769
770     open func changePageWith(href: String, andAudioMarkID markID: String) {
771         if recentlyScrolled { return } // if user recently scrolled, do not change pages or scroll the webview
772         guard let currentPage = currentPage else { return }
773
774         let item = findPageByHref(href)
775         let pageUpdateNeeded = item+1 != currentPage.pageNumber
776         let indexPath = IndexPath(row: item, section: 0)
777         changePageWith(indexPath: indexPath, animated: true) { () -> Void in
778             if pageUpdateNeeded {
779                 self.updateCurrentPage {
780                     currentPage.audioMarkID(markID)
781                 }
782             } else {
783                 currentPage.audioMarkID(markID)
784             }
785         }
786     }
787
788     open func changePageWith(indexPath: IndexPath, animated: Bool = false, completion: (() -> Void)? = nil) {
789         guard indexPathIsValid(indexPath) else {
790             print("ERROR: Attempt to scroll to invalid index path")
791             completion?()
792             return
793         }
794
795         UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in
796             self.collectionView.scrollToItem(at: indexPath, at: .direction(withConfiguration: self.readerConfig), animated: false)
797         }) { (finished: Bool) -> Void in
798             completion?()
799         }
800     }
801     
802     open func changePageWith(href: String, pageItem: Int, animated: Bool = false, completion: (() -> Void)? = nil) {
803         changePageWith(href: href, animated: animated) {
804             self.changePageItem(to: pageItem)
805         }
806     }
807
808     func indexPathIsValid(_ indexPath: IndexPath) -> Bool {
809         let section = indexPath.section
810         let row = indexPath.row
811         let lastSectionIndex = numberOfSections(in: collectionView) - 1
812
813         //Make sure the specified section exists
814         if section > lastSectionIndex {
815             return false
816         }
817
818         let rowCount = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) - 1
819         return row <= rowCount
820     }
821
822     open func isLastPage() -> Bool{
823         return (currentPageNumber == self.nextPageNumber)
824     }
825
826     public func changePageToNext(_ completion: (() -> Void)? = nil) {
827         changePageWith(page: self.nextPageNumber, animated: true) { () -> Void in
828             completion?()
829         }
830     }
831
832     public func changePageToPrevious(_ completion: (() -> Void)? = nil) {
833         changePageWith(page: self.previousPageNumber, animated: true) { () -> Void in
834             completion?()
835         }
836     }
837     
838     public func changePageItemToNext(_ completion: (() -> Void)? = nil) {
839         // TODO: It was implemented for horizontal orientation.
840         // Need check page orientation (v/h) and make correct calc for vertical
841         guard
842             let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
843             let contentOffset = cell.webView?.scrollView.contentOffset,
844             let contentOffsetXLimit = cell.webView?.scrollView.contentSize.width else {
845                 completion?()
846                 return
847         }
848         
849         let cellSize = cell.frame.size
850         let contentOffsetX = contentOffset.x + cellSize.width
851         
852         if contentOffsetX >= contentOffsetXLimit {
853             changePageToNext(completion)
854         } else {
855             cell.scrollPageToOffset(contentOffsetX, animated: true)
856         }
857         
858         completion?()
859     }
860
861     public func getCurrentPageItemNumber() -> Int {
862         guard let page = currentPage, let webView = page.webView else { return 0 }
863         
864         let pageSize = readerConfig.isDirection(pageHeight, pageWidth, pageHeight)
865         let pageOffSet = readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y)
866         let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize)
867         
868         return webViewPage
869     }
870
871     public func changePageItemToPrevious(_ completion: (() -> Void)? = nil) {
872         // TODO: It was implemented for horizontal orientation.
873         // Need check page orientation (v/h) and make correct calc for vertical
874         guard
875             let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
876             let contentOffset = cell.webView?.scrollView.contentOffset else {
877                 completion?()
878                 return
879         }
880         
881         let cellSize = cell.frame.size
882         let contentOffsetX = contentOffset.x - cellSize.width
883         
884         if contentOffsetX < 0 {
885             changePageToPrevious(completion)
886         } else {
887             cell.scrollPageToOffset(contentOffsetX, animated: true)
888         }
889         
890         completion?()
891     }
892
893     public func changePageItemToLast(animated: Bool = true, _ completion: (() -> Void)? = nil) {
894         // TODO: It was implemented for horizontal orientation.
895         // Need check page orientation (v/h) and make correct calc for vertical
896         guard
897             let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
898             let contentSize = cell.webView?.scrollView.contentSize else {
899                 completion?()
900                 return
901         }
902         
903         let cellSize = cell.frame.size
904         var contentOffsetX: CGFloat = 0.0
905         
906         if contentSize.width > 0 && cellSize.width > 0 {
907             contentOffsetX = (cellSize.width * (contentSize.width / cellSize.width)) - cellSize.width
908         }
909         
910         if contentOffsetX < 0 {
911             contentOffsetX = 0
912         }
913         
914         cell.scrollPageToOffset(contentOffsetX, animated: animated)
915         
916         completion?()
917     }
918
919     public func changePageItem(to: Int, animated: Bool = true, completion: (() -> Void)? = nil) {
920         // TODO: It was implemented for horizontal orientation.
921         // Need check page orientation (v/h) and make correct calc for vertical
922         guard
923             let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage,
924             let contentSize = cell.webView?.scrollView.contentSize else {
925                 delegate?.pageItemChanged?(getCurrentPageItemNumber())
926                 completion?()
927                 return
928         }
929         
930         let cellSize = cell.frame.size
931         var contentOffsetX: CGFloat = 0.0
932         
933         if contentSize.width > 0 && cellSize.width > 0 {
934             contentOffsetX = (cellSize.width * CGFloat(to)) - cellSize.width
935         }
936         
937         if contentOffsetX > contentSize.width {
938             contentOffsetX = contentSize.width - cellSize.width
939         }
940         
941         if contentOffsetX < 0 {
942             contentOffsetX = 0
943         }
944         
945         UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in
946             cell.scrollPageToOffset(contentOffsetX, animated: animated)
947         }) { (finished: Bool) -> Void in
948             self.updateCurrentPage {
949                 completion?()
950             }
951         }
952     }
953
954     /**
955      Find a page by FRTocReference.
956      */
957     public func findPageByResource(_ reference: FRTocReference) -> Int {
958         var count = 0
959         for item in self.book.spine.spineReferences {
960             if let resource = reference.resource, item.resource == resource {
961                 return count
962             }
963             count += 1
964         }
965         return count
966     }
967
968     /**
969      Find a page by href.
970      */
971     public func findPageByHref(_ href: String) -> Int {
972         var count = 0
973         for item in self.book.spine.spineReferences {
974             if item.resource.href == href {
975                 return count
976             }
977             count += 1
978         }
979         return count
980     }
981
982     /**
983      Find and return the current chapter resource.
984      */
985     public func getCurrentChapter() -> FRResource? {
986         for item in self.book.flatTableOfContents {
987             if
988                 let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)],
989                 let resource = item.resource,
990                 (resource == reference.resource) {
991                 return item.resource
992             }
993         }
994         return nil
995     }
996
997     /**
998      Return the current chapter progress based on current chapter and total of chapters.
999      */
1000     public func getCurrentChapterProgress() -> CGFloat {
1001         let total = totalPages
1002         let current = currentPageNumber
1003         
1004         if total == 0 {
1005             return 0
1006         }
1007         
1008         return CGFloat((100 * current) / total)
1009     }
1010
1011     /**
1012      Find and return the current chapter name.
1013      */
1014     public func getCurrentChapterName() -> String? {
1015         for item in self.book.flatTableOfContents {
1016             guard
1017                 let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)],
1018                 let resource = item.resource,
1019                 (resource == reference.resource),
1020                 let title = item.title else {
1021                     continue
1022             }
1023
1024             return title
1025         }
1026
1027         return nil
1028     }
1029
1030     // MARK: Public page methods
1031
1032     /**
1033      Changes the current page of the reader.
1034
1035      - parameter page: The target page index. Note: The page index starts at 1 (and not 0).
1036      - parameter animated: En-/Disables the animation of the page change.
1037      - parameter completion: A Closure which is called if the page change is completed.
1038      */
1039     public func changePageWith(page: Int, animated: Bool = false, completion: (() -> Void)? = nil) {
1040         if page > 0 && page-1 < totalPages {
1041             let indexPath = IndexPath(row: page-1, section: 0)
1042             changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in
1043                 self.updateCurrentPage {
1044                     completion?()
1045                 }
1046             })
1047         }
1048     }
1049
1050     // MARK: - Audio Playing
1051
1052     func audioMark(href: String, fragmentID: String) {
1053         changePageWith(href: href, andAudioMarkID: fragmentID)
1054     }
1055
1056     // MARK: - Sharing
1057
1058     /**
1059      Sharing chapter method.
1060      */
1061     @objc func shareChapter(_ sender: UIBarButtonItem) {
1062         guard let currentPage = currentPage else { return }
1063
1064         if let chapterText = currentPage.webView?.js("getBodyText()") {
1065             let htmlText = chapterText.replacingOccurrences(of: "[\\n\\r]+", with: "<br />", options: .regularExpression)
1066             var subject = readerConfig.localizedShareChapterSubject
1067             var html = ""
1068             var text = ""
1069             var bookTitle = ""
1070             var chapterName = ""
1071             var authorName = ""
1072             var shareItems = [AnyObject]()
1073
1074             // Get book title
1075             if let title = self.book.title {
1076                 bookTitle = title
1077                 subject += " “\(title)”"
1078             }
1079
1080             // Get chapter name
1081             if let chapter = getCurrentChapterName() {
1082                 chapterName = chapter
1083             }
1084
1085             // Get author name
1086             if let author = self.book.metadata.creators.first {
1087                 authorName = author.name
1088             }
1089
1090             // Sharing html and text
1091             html = "<html><body>"
1092             html += "<br /><hr> <p>\(htmlText)</p> <hr><br />"
1093             html += "<center><p style=\"color:gray\">"+readerConfig.localizedShareAllExcerptsFrom+"</p>"
1094             html += "<b>\(bookTitle)</b><br />"
1095             html += readerConfig.localizedShareBy+" <i>\(authorName)</i><br />"
1096
1097             if let bookShareLink = readerConfig.localizedShareWebLink {
1098                 html += "<a href=\"\(bookShareLink.absoluteString)\">\(bookShareLink.absoluteString)</a>"
1099                 shareItems.append(bookShareLink as AnyObject)
1100             }
1101
1102             html += "</center></body></html>"
1103             text = "\(chapterName)\n\n“\(chapterText)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)"
1104
1105             let act = FolioReaderSharingProvider(subject: subject, text: text, html: html)
1106             shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0)
1107
1108             let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
1109             activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo]
1110
1111             // Pop style on iPad
1112             if let actv = activityViewController.popoverPresentationController {
1113                 actv.barButtonItem = sender
1114             }
1115
1116             present(activityViewController, animated: true, completion: nil)
1117         }
1118     }
1119
1120     /**
1121      Sharing highlight method.
1122      */
1123     func shareHighlight(_ string: String, rect: CGRect) {
1124         var subject = readerConfig.localizedShareHighlightSubject
1125         var html = ""
1126         var text = ""
1127         var bookTitle = ""
1128         var chapterName = ""
1129         var authorName = ""
1130         var shareItems = [AnyObject]()
1131
1132         // Get book title
1133         if let title = self.book.title {
1134             bookTitle = title
1135             subject += " “\(title)”"
1136         }
1137
1138         // Get chapter name
1139         if let chapter = getCurrentChapterName() {
1140             chapterName = chapter
1141         }
1142
1143         // Get author name
1144         if let author = self.book.metadata.creators.first {
1145             authorName = author.name
1146         }
1147
1148         // Sharing html and text
1149         html = "<html><body>"
1150         html += "<br /><hr> <p>\(chapterName)</p>"
1151         html += "<p>\(string)</p> <hr><br />"
1152         html += "<center><p style=\"color:gray\">"+readerConfig.localizedShareAllExcerptsFrom+"</p>"
1153         html += "<b>\(bookTitle)</b><br />"
1154         html += readerConfig.localizedShareBy+" <i>\(authorName)</i><br />"
1155
1156         if let bookShareLink = readerConfig.localizedShareWebLink {
1157             html += "<a href=\"\(bookShareLink.absoluteString)\">\(bookShareLink.absoluteString)</a>"
1158             shareItems.append(bookShareLink as AnyObject)
1159         }
1160
1161         html += "</center></body></html>"
1162         text = "\(chapterName)\n\n“\(string)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)"
1163
1164         let act = FolioReaderSharingProvider(subject: subject, text: text, html: html)
1165         shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0)
1166
1167         let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
1168         activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo]
1169
1170         // Pop style on iPad
1171         if let actv = activityViewController.popoverPresentationController {
1172             actv.sourceView = currentPage
1173             actv.sourceRect = rect
1174         }
1175
1176         present(activityViewController, animated: true, completion: nil)
1177     }
1178
1179     // MARK: - ScrollView Delegate
1180
1181     open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
1182         self.isScrolling = true
1183         clearRecentlyScrolled()
1184         recentlyScrolled = true
1185         pointNow = scrollView.contentOffset
1186
1187         if let currentPage = currentPage {
1188             currentPage.webView?.createMenu(options: true)
1189             currentPage.webView?.setMenuVisible(false)
1190         }
1191
1192         scrollScrubber?.scrollViewWillBeginDragging(scrollView)
1193     }
1194
1195     open func scrollViewDidScroll(_ scrollView: UIScrollView) {
1196
1197         if (navigationController?.isNavigationBarHidden == false) {
1198             self.toggleBars()
1199         }
1200
1201         scrollScrubber?.scrollViewDidScroll(scrollView)
1202
1203         let isCollectionScrollView = (scrollView is UICollectionView)
1204         let scrollType: ScrollType = ((isCollectionScrollView == true) ? .chapter : .page)
1205
1206         // Update current reading page
1207         if (isCollectionScrollView == false), let page = currentPage, let webView = page.webView {
1208
1209             let pageSize = self.readerConfig.isDirection(self.pageHeight, self.pageWidth, self.pageHeight)
1210             let contentOffset = webView.scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig)
1211             let contentSize = webView.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig)
1212             if (contentOffset + pageSize <= contentSize) {
1213
1214                 let webViewPage = pageForOffset(contentOffset, pageHeight: pageSize)
1215
1216                 if (readerConfig.scrollDirection == .horizontalWithVerticalContent) {
1217                     let currentIndexPathRow = (page.pageNumber - 1)
1218
1219                     // if the cell reload doesn't save the top position offset
1220                     if let oldOffSet = self.currentWebViewScrollPositions[currentIndexPathRow], (abs(oldOffSet.y - scrollView.contentOffset.y) > 100) {
1221                         // Do nothing
1222                     } else {
1223                         self.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset
1224                     }
1225                 }
1226
1227                 if (pageIndicatorView?.currentPage != webViewPage) {
1228                     pageIndicatorView?.currentPage = webViewPage
1229                 }
1230                 
1231                 self.delegate?.pageItemChanged?(webViewPage)
1232             }
1233         }
1234
1235         self.updatePageScrollDirection(inScrollView: scrollView, forScrollType: scrollType)
1236     }
1237
1238     private func updatePageScrollDirection(inScrollView scrollView: UIScrollView, forScrollType scrollType: ScrollType) {
1239         
1240         let scrollViewContentOffsetForDirection = scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType)
1241         let pointNowForDirection = pointNow.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType)
1242         // The movement is either positive or negative. This happens if the page change isn't completed. Toggle to the other scroll direction then.
1243         let isCurrentlyPositive = (self.pageScrollDirection == .left || self.pageScrollDirection == .up)
1244
1245         if (scrollViewContentOffsetForDirection < pointNowForDirection) {
1246             self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType)
1247         } else if (scrollViewContentOffsetForDirection > pointNowForDirection) {
1248             self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType)
1249         } else if (isCurrentlyPositive == true) {
1250             self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType)
1251         } else {
1252             self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType)
1253         }
1254     }
1255
1256     open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
1257         self.isScrolling = false
1258
1259         // 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).
1260         delay(0.2, closure: { [weak self] in
1261             if (self?.readerConfig.scrollDirection == .horizontalWithVerticalContent),
1262                 let cell = ((scrollView.superview as? UIWebView)?.delegate as? FolioReaderPage) {
1263                 let currentIndexPathRow = cell.pageNumber - 1
1264                 self?.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset
1265             }
1266
1267             if (scrollView is UICollectionView) {
1268                 guard let instance = self else {
1269                     return
1270                 }
1271                 
1272                 if instance.totalPages > 0 {
1273                     instance.updateCurrentPage()
1274                     instance.delegate?.pageItemChanged?(instance.getCurrentPageItemNumber())
1275                 }
1276             } else {
1277                 self?.scrollScrubber?.scrollViewDidEndDecelerating(scrollView)
1278             }
1279         })
1280     }
1281
1282     open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
1283         if scrollView == collectionView{            
1284             scrollView.isUserInteractionEnabled = false
1285             delay(0.5, closure: { [weak self] in
1286                 scrollView.isUserInteractionEnabled = true
1287             })
1288         }
1289       
1290         recentlyScrolledTimer = Timer(timeInterval:recentlyScrolledDelay, target: self, selector: #selector(FolioReaderCenter.clearRecentlyScrolled), userInfo: nil, repeats: false)
1291         RunLoop.current.add(recentlyScrolledTimer, forMode: RunLoopMode.commonModes)
1292     }
1293
1294     @objc func clearRecentlyScrolled() {
1295         if(recentlyScrolledTimer != nil) {
1296             recentlyScrolledTimer.invalidate()
1297             recentlyScrolledTimer = nil
1298         }
1299         recentlyScrolled = false
1300     }
1301
1302     open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
1303         scrollScrubber?.scrollViewDidEndScrollingAnimation(scrollView)
1304     }
1305
1306     // MARK: NavigationBar Actions
1307
1308     @objc func closeReader(_ sender: UIBarButtonItem) {
1309         dismiss()
1310         folioReader.close()
1311     }
1312
1313     //PD: changed
1314     /**
1315      Present chapter list
1316      */
1317     @objc func presentChapterList(_ sender: UIBarButtonItem) {
1318         folioReader.saveReaderState()
1319
1320         let chapter = FolioReaderChapterList(folioReader: folioReader, readerConfig: readerConfig, book: book, delegate: self)
1321         chapter.title = "Spis treści"
1322 //        let highlight = FolioReaderHighlightList(folioReader: folioReader, readerConfig: readerConfig)
1323 //        let pageController = PageViewController(folioReader: folioReader, readerConfig: readerConfig)
1324
1325 //        pageController.viewControllerOne = chapter
1326 //        pageController.viewControllerTwo = highlight
1327 //        pageController.segmentedControlItems = [readerConfig.localizedContentsTitle, readerConfig.localizedHighlightsTitle]
1328
1329         let vc = FolioReaderModalViewController(folioReader: folioReader, readerConfig: readerConfig)
1330         
1331         vc.viewControllerOne = chapter
1332         let nav = UINavigationController(rootViewController:vc /*pageController*/)
1333         present(nav, animated: true, completion: nil)
1334     }
1335     
1336     /**
1337      Present fonts and settings menu
1338      */
1339     @objc func presentFontsMenu() {
1340         folioReader.saveReaderState()
1341         hideBars()
1342
1343         let menu = FolioReaderFontsMenu(folioReader: folioReader, readerConfig: readerConfig)
1344         menu.modalPresentationStyle = .custom
1345
1346         animator = ZFModalTransitionAnimator(modalViewController: menu)
1347         animator.isDragable = false
1348         animator.bounces = false
1349         animator.behindViewAlpha = 0.4
1350         animator.behindViewScale = 1
1351         animator.transitionDuration = 0.6
1352         animator.direction = ZFModalTransitonDirection.bottom
1353
1354         menu.transitioningDelegate = animator
1355         self.present(menu, animated: true, completion: nil)
1356     }
1357
1358     /**
1359      Present audio player menu
1360      */
1361     @objc func presentPlayerMenu(_ sender: UIBarButtonItem) {
1362         folioReader.saveReaderState()
1363         hideBars()
1364
1365         let menu = FolioReaderPlayerMenu(folioReader: folioReader, readerConfig: readerConfig)
1366         menu.modalPresentationStyle = .custom
1367
1368         animator = ZFModalTransitionAnimator(modalViewController: menu)
1369         animator.isDragable = true
1370         animator.bounces = false
1371         animator.behindViewAlpha = 0.4
1372         animator.behindViewScale = 1
1373         animator.transitionDuration = 0.6
1374         animator.direction = ZFModalTransitonDirection.bottom
1375
1376         menu.transitioningDelegate = animator
1377         present(menu, animated: true, completion: nil)
1378     }
1379
1380     /**
1381      Present Quote Share
1382      */
1383     func presentQuoteShare(_ string: String) {
1384         let quoteShare = FolioReaderQuoteShare(initWithText: string, readerConfig: readerConfig, folioReader: folioReader, book: book)
1385         let nav = UINavigationController(rootViewController: quoteShare)
1386
1387         if UIDevice.current.userInterfaceIdiom == .pad {
1388             nav.modalPresentationStyle = .formSheet
1389         }
1390         present(nav, animated: true, completion: nil)
1391     }
1392 }
1393
1394 // MARK: FolioPageDelegate
1395
1396 extension FolioReaderCenter: FolioReaderPageDelegate {
1397
1398     public func pageDidLoad(_ page: FolioReaderPage) {
1399         if self.readerConfig.loadSavedPositionForCurrentBook, let position = folioReader.savedPositionForCurrentBook {
1400             let pageNumber = position["pageNumber"] as? Int
1401             let offset = self.readerConfig.isDirection(position["pageOffsetY"], position["pageOffsetX"], position["pageOffsetY"]) as? CGFloat
1402             let pageOffset = offset
1403
1404             if isFirstLoad {
1405                 updateCurrentPage(page)
1406                 isFirstLoad = false
1407
1408                 if (self.currentPageNumber == pageNumber && pageOffset > 0) {
1409                     page.scrollPageToOffset(pageOffset!, animated: false)
1410                 }
1411             } else if (self.isScrolling == false && folioReader.needsRTLChange == true) {
1412                 page.scrollPageToBottom()
1413             }
1414         } else if isFirstLoad {
1415             updateCurrentPage(page)
1416             isFirstLoad = false
1417         }
1418
1419         // Go to fragment if needed
1420         if let fragmentID = tempFragment, let currentPage = currentPage , fragmentID != "" {
1421             currentPage.handleAnchor(fragmentID, avoidBeginningAnchors: true, animated: true)
1422             tempFragment = nil
1423         }
1424         
1425         if (readerConfig.scrollDirection == .horizontalWithVerticalContent),
1426             let offsetPoint = self.currentWebViewScrollPositions[page.pageNumber - 1] {
1427             page.webView?.scrollView.setContentOffset(offsetPoint, animated: false)
1428         }
1429         
1430         // Pass the event to the centers `pageDelegate`
1431         pageDelegate?.pageDidLoad?(page)
1432     }
1433     
1434     public func pageWillLoad(_ page: FolioReaderPage) {
1435         // Pass the event to the centers `pageDelegate`
1436         pageDelegate?.pageWillLoad?(page)
1437     }
1438     
1439     public func pageTap(_ recognizer: UITapGestureRecognizer) {
1440         // Pass the event to the centers `pageDelegate`
1441         pageDelegate?.pageTap?(recognizer)
1442     }
1443     
1444 }
1445
1446 // MARK: FolioReaderChapterListDelegate
1447
1448 extension FolioReaderCenter: FolioReaderChapterListDelegate {
1449     
1450     func chapterList(_ chapterList: FolioReaderChapterList, didSelectRowAtIndexPath indexPath: IndexPath, withTocReference reference: FRTocReference) {
1451         let item = findPageByResource(reference)
1452         
1453         if item < totalPages {
1454             let indexPath = IndexPath(row: item, section: 0)
1455             changingChapter = true
1456             changePageWith(indexPath: indexPath, animated: false, completion: { () -> Void in
1457                 self.updateCurrentPage()
1458                 self.changingChapter = false
1459             })
1460             tempReference = reference
1461         } else {
1462             print("Failed to load book because the requested resource is missing.")
1463         }
1464     }
1465     
1466     func chapterList(didDismissedChapterList chapterList: FolioReaderChapterList) {
1467         updateCurrentPage()
1468         
1469         // Move to #fragment
1470         if let reference = tempReference {
1471             if let fragmentID = reference.fragmentID, let currentPage = currentPage , fragmentID != "" {
1472                 currentPage.handleAnchor(reference.fragmentID!, avoidBeginningAnchors: true, animated: true)
1473             }
1474             tempReference = nil
1475         }
1476     }
1477     
1478     func getScreenBounds() -> CGRect {
1479         var bounds = view.frame
1480         
1481         if #available(iOS 11.0, *) {
1482             bounds.size.height = bounds.size.height - view.safeAreaInsets.bottom
1483         }
1484         
1485         return bounds
1486     }
1487     
1488 }
1489
1490 class FolioReaderModalViewController: UIViewController{
1491     
1492     var viewControllerOne: UIViewController!
1493     fileprivate var readerConfig: FolioReaderConfig
1494     fileprivate var folioReader: FolioReader
1495     
1496     // MARK: Init
1497     
1498     init(folioReader: FolioReader, readerConfig: FolioReaderConfig) {
1499         self.folioReader = folioReader
1500         self.readerConfig = readerConfig
1501         super.init(nibName: nil, bundle: nil)
1502         self.edgesForExtendedLayout = UIRectEdge()
1503         self.extendedLayoutIncludesOpaqueBars = true
1504     }
1505     
1506     required init?(coder: NSCoder) {
1507         fatalError("storyboards are incompatible with truth and beauty")
1508     }
1509     
1510     override func viewDidLoad() {
1511         super.viewDidLoad()
1512
1513         self.navigationItem.title = viewControllerOne.title
1514         
1515         addChildViewController(viewControllerOne)
1516         viewControllerOne.view.frame = view.bounds
1517         view.addSubview(viewControllerOne.view)
1518         viewControllerOne.view.translatesAutoresizingMaskIntoConstraints = false
1519         if #available(iOS 9.0, *) {
1520             viewControllerOne.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
1521             viewControllerOne.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
1522             viewControllerOne.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
1523             viewControllerOne.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
1524         } else {
1525             // Fallback on earlier versions
1526         }
1527         viewControllerOne.didMove(toParentViewController: self)
1528         
1529 //        self.view.backgroundColor = UIColor.white
1530 //        self.setViewControllers([viewList[index]], direction: .forward, animated: false, completion: nil)
1531         
1532         //PD: changed
1533         let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
1534         let tintColor = folioReader.isNight(greenColor, UIColor.white)
1535         
1536         let closeImage = UIImage(readerImageNamed: "icon-navbar-close")?.imageTintColor(tintColor)?.withRenderingMode(.alwaysOriginal)// ignoreSystemTint(withConfiguration: readerConfig)
1537         self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: closeImage, style: .plain, target: self, action: #selector(dismiss as () -> Void))
1538     }
1539     
1540     override func viewWillAppear(_ animated: Bool) {
1541         super.viewWillAppear(animated)
1542         configureNavBar()
1543     }
1544     
1545     func configureNavBar() {
1546         
1547         let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00)
1548         let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor)
1549         let tintColor = folioReader.isNight(greenColor, UIColor.white)
1550         let navText = tintColor
1551         let font = UIFont(name: "Avenir-Light", size: 17)!
1552         setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font)
1553     }
1554     
1555     //PD: changed
1556     override var preferredStatusBarStyle : UIStatusBarStyle {
1557         return .lightContent //self.folioReader.isNight(.lightContent, .default)
1558     }
1559 }