diff --git a/Example/StoryboardExample/ExampleFolioReaderContainer.swift b/Example/StoryboardExample/ExampleFolioReaderContainer.swift index bb6729a51..493e4ed2d 100644 --- a/Example/StoryboardExample/ExampleFolioReaderContainer.swift +++ b/Example/StoryboardExample/ExampleFolioReaderContainer.swift @@ -16,7 +16,16 @@ class ExampleFolioReaderContainer: FolioReaderContainer { let config = FolioReaderConfig() config.scrollDirection = .horizontalWithVerticalContent - + config.shouldHideNavigationOnTap = false + + // Print the chapter ID if one was clicked + // A chapter in "The Silver Chair" looks like this "
" + // To knwo if a user tapped on a chapter we can listen to events on the class "chapter" and receive the id value + let listener = ClassBasedOnClickListener(schemeName: "chaptertapped", querySelector: ".chapter", attributeName: "id", onClickAction: { (parameterContent: String?) in + print("chapter with id: " + (parameterContent ?? "-") + " clicked") + }) + config.classBasedOnClickListeners.append(listener) + guard let bookPath = NSBundle.mainBundle().pathForResource("The Silver Chair", ofType: "epub") else { return } setupConfig(config, epubPath: bookPath) } diff --git a/Source/FolioReaderConfig.swift b/Source/FolioReaderConfig.swift index aca8643e8..ba0b74d5b 100755 --- a/Source/FolioReaderConfig.swift +++ b/Source/FolioReaderConfig.swift @@ -8,6 +8,8 @@ import UIKit +// MARK: - FolioReaderScrollDirection + /** Defines the Reader scrolling direction */ @@ -37,12 +39,58 @@ public enum FolioReaderScrollDirection: Int { } } +// MARK: - ClassBasedOnClickListener + +/** +A `ClassBasedOnClickListener` takes a closure which is performed if a given html `class` is clicked. The closure will reveice the content of the specified parameter. + +Eg. A ClassBasedOnClickListener with the className "quote" and parameterName "id" with the given epub html content "
" would call the given closure on a click on this section with the String "12345" as parameter. + +*/ +public struct ClassBasedOnClickListener { + + /// The name of the URL scheme which should be used. Note: Make sure that the given `String` is a valid as scheme name. + public var schemeName : String + + /// The query selector for the elements which the listener should be added to. See https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector for further information about query selectors. + public var querySelector : String + + /// The name of the attribute whose content should be passed to the `onClickAction` action. + public var attributeName : String + + /// Whether the listener should be added to all found elements or only to the first one. See https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll for further information. The default value is `true`. + public var selectAll : Bool + + /// The closure which will be called if the specified class was clicked. + public var onClickAction : ((parameterContent: String?) -> Void) + + /// Initializes a `ClassBasedOnClickListener` instance. Append it to the `classBasedOnClickListeners` property from the `FolioReaderConfig` to receive on click events. The default `selectAll` value is `true`. + public init(schemeName: String, querySelector: String, attributeName: String, selectAll: Bool = true, onClickAction: ((attributeContent: String?) -> Void)) { + self.schemeName = schemeName.lowercaseString + self.querySelector = querySelector + self.attributeName = attributeName + self.selectAll = selectAll + self.onClickAction = onClickAction + } +} + +// MARK: - FolioReaderConfig /** Defines the Reader custom configuration */ public class FolioReaderConfig: NSObject { - + + // MARK: ClassBasedOnClickListener + + /** + Array of `ClassBasedOnClickListener` objects. A `ClassBasedOnClickListener` takes a closure which is performed if a given html `class` is clicked. The closure will reveice the content of the specified parameter. + + Eg. A ClassBasedOnClickListener with the className "quote" and parameterName "id" with the given epub html content "
" would call the given closure on a click on this section with the String "12345" as parameter. + + */ + public var classBasedOnClickListeners = [ClassBasedOnClickListener]() + // MARK: Colors /// Base header custom TintColor diff --git a/Source/FolioReaderPage.swift b/Source/FolioReaderPage.swift index f68ae19a6..19c14e7af 100755 --- a/Source/FolioReaderPage.swift +++ b/Source/FolioReaderPage.swift @@ -130,6 +130,10 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture // MARK: - UIWebView Delegate public func webViewDidFinishLoad(webView: UIWebView) { + + // Add the custom class based onClick listener + self.setupClassBasedOnClickListeners() + refreshPageMode() if readerConfig.enableTTS && !book.hasAudio() { @@ -233,11 +237,28 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture FolioReader.sharedInstance.readerCenter.presentViewController(nav, animated: true, completion: nil) } return false - } else if UIApplication.sharedApplication().canOpenURL(url) { - UIApplication.sharedApplication().openURL(url) - return false - } - + } else { + // Check if the url is a custom class based onClick listerner + var isClassBasedOnClickListenerScheme = false + for listener in readerConfig.classBasedOnClickListeners { + if url.scheme == listener.schemeName { + let parameterContentString = (request.URL?.absoluteString.stringByReplacingOccurrencesOfString("\(url.scheme)://", withString: "").stringByRemovingPercentEncoding) + listener.onClickAction(parameterContent: parameterContentString) + isClassBasedOnClickListenerScheme = true + } + } + + if isClassBasedOnClickListenerScheme == false { + // Try to open the url with the system if it wasn't a custom class based click listener + if UIApplication.sharedApplication().canOpenURL(url) { + UIApplication.sharedApplication().openURL(url) + return false + } + } else { + return false + } + } + return true } @@ -393,6 +414,15 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture colorView.frame = CGRectZero } } + + // MARK: - Class based click listener + + private func setupClassBasedOnClickListeners() { + + for listener in readerConfig.classBasedOnClickListeners { + self.webView.js("addClassBasedOnClickListener(\"\(listener.schemeName)\", \"\(listener.querySelector)\", \"\(listener.attributeName)\", \"\(listener.selectAll)\")"); + } + } } // MARK: - WebView Highlight and share implementation diff --git a/Source/Resources/Bridge.js b/Source/Resources/Bridge.js index 5e1127b77..927e66524 100755 --- a/Source/Resources/Bridge.js +++ b/Source/Resources/Bridge.js @@ -145,14 +145,14 @@ var getRectForSelectedText = function(elm) { // Method that call that a hightlight was clicked // with URL scheme and rect informations var callHighlightURL = function(elm) { - var URLBase = "highlight://"; + event.stopPropagation(); + var URLBase = "highlight://"; var currentHighlightRect = getRectForSelectedText(elm); thisHighlight = elm; window.location = URLBase + encodeURIComponent(currentHighlightRect); } - // Reading time function getReadingTime() { var text = document.body.innerText; @@ -579,4 +579,32 @@ function wrappingSentencesWithinPTags(){ } guessSenetences(); +} + +// Class based onClick listener + +function addClassBasedOnClickListener(schemeName, querySelector, attributeName, selectAll) { + if (selectAll) { + // Get all elements with the given query selector + var elements = document.querySelectorAll(querySelector); + for (elementIndex = 0; elementIndex < elements.length; elementIndex++) { + var element = elements[elementIndex]; + addClassBasedOnClickListenerToElement(element, schemeName, attributeName); + } + } else { + // Get the first element with the given query selector + var element = document.querySelector(querySelector); + addClassBasedOnClickListenerToElement(element, schemeName, attributeName); + } +} + +function addClassBasedOnClickListenerToElement(element, schemeName, attributeName) { + // Get the content from the given attribute name + var attributeContent = element.getAttribute(attributeName); + // Add the on click logic + element.setAttribute("onclick", "onClassBasedListenerClick(\"" + schemeName + "\", \"" + encodeURIComponent(attributeContent) + "\");"); +} + +var onClassBasedListenerClick = function(schemeName, parameterContent) { + window.location = schemeName + "://" + parameterContent; } \ No newline at end of file