import UIKit extension RDEPUBReaderController: RDEPUBWebContentViewDelegate { func epubWebContentView(_ contentView: RDEPUBWebContentView, didUpdateLocation location: RDEPUBLocation, spineIndex: Int) { guard readerView.currentPage >= 0, activePages.indices.contains(readerView.currentPage) else { return } guard readerView.pageContentView(pageNum: readerView.currentPage) === contentView else { return } let currentPage = activePages[readerView.currentPage] guard readingSession?.pageContains(spineIndex: spineIndex, in: currentPage) == true else { return } persist(location: location) readingSession?.updateReadingContext( pageNumber: readerView.currentPage + 1, location: location, spineIndex: spineIndex, chapterIndex: currentPage.chapterIndex, bookIdentifier: currentBookIdentifier ) } func epubWebContentView(_ contentView: RDEPUBWebContentView, didChangeSelection selection: RDEPUBSelection?, spineIndex: Int) { if let selection { updateCurrentSelection(scopedSelection(selection, relativeToSpineIndex: spineIndex)) } else { updateCurrentSelection(nil) } } func epubWebContentView(_ contentView: RDEPUBWebContentView, didRequestSelectionAction action: RDEPUBAnnotationMenuAction) { handleSelectionMenuAction(action, selection: currentSelection) } func epubWebContentView(_ contentView: RDEPUBWebContentView, didActivateInternalLink location: RDEPUBLocation, fromSpineIndex: Int) { if presentNotePopupIfPossible(for: location, fromSpineIndex: fromSpineIndex) { return } guard let readingSession, let pageNumber = readingSession.queueNavigation( to: location, relativeToSpineIndex: fromSpineIndex, bookIdentifier: currentBookIdentifier ) else { return } readerView.transitionToPage(pageNum: max(pageNumber - 1, 0), animated: true) } func epubWebContentView(_ contentView: RDEPUBWebContentView, didActivateExternalLink url: URL) { delegate?.epubReader(self, didActivateExternalLink: url) openExternalURLIfAllowed(url) } func epubWebContentView(_ contentView: RDEPUBWebContentView, didLogJavaScriptError message: String) { #if DEBUG print("EPUB JS Error: \(message)") #endif } func epubWebContentView(_ contentView: RDEPUBWebContentView, didTapImageWithSource src: String, sourceRect: CGRect?) { guard let publication else { return } let baseHref = contentView.currentHref let coordinator = RDEPUBImageViewerCoordinator(presentingController: self, theme: configuration.theme) coordinator.presentImageFromWebView(src: src, baseHref: baseHref, resourceResolver: publication.resourceResolver) } func epubWebContentView(_ contentView: RDEPUBWebContentView, didTapFootnoteWithAltText altText: String, sourceRect: CGRect?) { guard !altText.isEmpty else { return } let rect = sourceRect ?? .zero let sourcePoint = rect.isNull ? CGPoint(x: contentView.bounds.midX, y: contentView.bounds.midY) : CGPoint(x: rect.midX, y: rect.midY) presentAttachmentTooltip(text: altText, sourceView: contentView, sourceRect: rect, sourcePoint: sourcePoint) } } extension RDEPUBReaderController: RDEPUBTextContentViewDelegate { func textContentView(_ contentView: RDEPUBTextContentView, didChangeSelection selection: RDEPUBSelection?) { guard let selection else { updateCurrentSelection(nil) return } updateCurrentSelection(selection) } func textContentView(_ contentView: RDEPUBTextContentView, didRequestReaderTapAt point: CGPoint) { RDEpubReaderTapDebug.log( "ReaderController.textContentTap", "delegate received reader tap from contentView=\(RDEpubReaderTapDebug.describe(contentView)) point=\(RDEpubReaderTapDebug.describe(point)) currentPage=\(readerView.currentPage)" ) readerView.handleContentTap(at: point, in: contentView) } func textContentView( _ contentView: RDEPUBTextContentView, didRequestSelectionAction action: RDEPUBAnnotationMenuAction, selection: RDEPUBSelection? ) { handleSelectionMenuAction(action, selection: selection ?? currentSelection) contentView.clearSelection() } func textContentView( _ contentView: RDEPUBTextContentView, didActivateAttachmentText text: String, sourceRect: CGRect, sourcePoint: CGPoint ) { presentAttachmentTooltip(text: text, sourceView: contentView, sourceRect: sourceRect, sourcePoint: sourcePoint) } func textContentView( _ contentView: RDEPUBTextContentView, didActivateImage image: UIImage, sourceRect: CGRect, altText: String? ) { let coordinator = RDEPUBImageViewerCoordinator(presentingController: self, theme: configuration.theme) coordinator.presentImage(image, sourceRect: sourceRect, altText: altText) } func textContentView( _ contentView: RDEPUBTextContentView, didRequestHighlightActions highlight: RDEPUBHighlight, sourceRect: CGRect ) { runtime.presentHighlightActions(for: highlight, sourceView: contentView, sourceRect: sourceRect) } private func presentNotePopupIfPossible(for location: RDEPUBLocation, fromSpineIndex: Int) -> Bool { guard let publication else { return false } let sourceHref = publication.resourceResolver.href(forSpineIndex: fromSpineIndex) ?? location.href let sourceLocation = currentVisibleLocation() let sourceCFI = sourceLocation?.cfi let resolver = RDEPUBNoteResolver(resourceResolver: publication.resourceResolver) guard let note = resolver.resolveInternalLink( sourceHref: sourceHref, sourceCFI: sourceCFI, targetLocation: location, sourceLocation: sourceLocation, relativeToSpineIndex: fromSpineIndex ) else { return false } RDEPUBNotePopupCoordinator.present( note, from: self, onReturnToSource: { [weak self] in guard let self, let sourceLocation = note.sourceLocation else { return } self.navigateToLocation(sourceLocation, relativeToSpineIndex: nil, animated: true) }, onOpenNoteLocation: { [weak self] in guard let self else { return } self.navigateToLocation(note.targetLocation, relativeToSpineIndex: nil, animated: true) } ) return true } private func navigateToLocation( _ location: RDEPUBLocation, relativeToSpineIndex spineIndex: Int?, animated: Bool ) { guard let pageNumber = readingSession?.queueNavigation( to: location, relativeToSpineIndex: spineIndex, bookIdentifier: currentBookIdentifier ) else { return } readerView.transitionToPage(pageNum: max(pageNumber - 1, 0), animated: animated) } }