From 4c1c3f031eca96b26f2bbd2ea102821975ee7677 Mon Sep 17 00:00:00 2001 From: PanajotisMaroungas Date: Mon, 22 Aug 2016 19:26:47 +0200 Subject: [PATCH 01/10] add listenerBlock and listernerDirectory in ReaderConfig. --- Source/FolioReaderConfig.swift | 14 +++++++++++- Source/FolioReaderPage.swift | 39 +++++++++++++++++++++++++++++++++- Source/Resources/Bridge.js | 22 +++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/Source/FolioReaderConfig.swift b/Source/FolioReaderConfig.swift index 53df64905..dee61ea10 100755 --- a/Source/FolioReaderConfig.swift +++ b/Source/FolioReaderConfig.swift @@ -42,7 +42,12 @@ public enum FolioReaderScrollDirection: Int { Defines the Reader custom configuration */ public class FolioReaderConfig: NSObject { - + + /// array of disctionary with keys className and parameterName + public var listernetDictionary = [[:]] + + public var listenerBlock: ((json: String?) -> Void)? + // MARK: Colors /// Base header custom TintColor @@ -119,3 +124,10 @@ public class FolioReaderConfig: NSObject { public var localizedShareAllExcerptsFrom = NSLocalizedString("All excerpts from", comment: "") public var localizedShareBy = NSLocalizedString("by", comment: "") } + +// MARK: - Constants + +public struct Listener { + static let className = "className" + static let parameterName = "parameterName" +} diff --git a/Source/FolioReaderPage.swift b/Source/FolioReaderPage.swift index f9a093beb..ed2fe8962 100755 --- a/Source/FolioReaderPage.swift +++ b/Source/FolioReaderPage.swift @@ -136,6 +136,8 @@ class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecogni // MARK: - UIWebView Delegate func webViewDidFinishLoad(webView: UIWebView) { + + self.setupListeners() refreshPageMode() if readerConfig.enableTTS && !book.hasAudio() { @@ -223,7 +225,12 @@ class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecogni } return true - } else if url.scheme == "mailto" { + } else if url.scheme == "listener" { + + let parameter = (request.URL?.absoluteString.stringByReplacingOccurrencesOfString("listener://", withString: "").stringByRemovingPercentEncoding) + readerConfig.listenerBlock?(json: parameter) + return false + }else if url.scheme == "mailto" { print("Email") return true } else if url.absoluteString != "about:blank" && url.scheme.containsString("http") && navigationType == .LinkClicked { @@ -656,3 +663,33 @@ extension UIMenuItem { #endif } } + +// MARK: - Private helpers +extension FolioReaderPage { + + private func setupListeners() { + + let javascriptArray = self.extractListenerDictionary() + self.webView.js("setListenerDictionary(\(javascriptArray))"); + } + + private func extractListenerDictionary() -> String { + var javascriptDictionary = "[" + + for index in 0.. Date: Tue, 23 Aug 2016 09:45:31 +0200 Subject: [PATCH 02/10] Minor refactoring --- Source/FolioReaderConfig.swift | 11 ++++++++--- Source/FolioReaderPage.swift | 20 ++++++++------------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Source/FolioReaderConfig.swift b/Source/FolioReaderConfig.swift index dee61ea10..13c9f5662 100755 --- a/Source/FolioReaderConfig.swift +++ b/Source/FolioReaderConfig.swift @@ -44,9 +44,9 @@ public enum FolioReaderScrollDirection: Int { public class FolioReaderConfig: NSObject { /// array of disctionary with keys className and parameterName - public var listernetDictionary = [[:]] + public var userTapListenerConfiguration = [ListenerCofiguration]() - public var listenerBlock: ((json: String?) -> Void)? + public var userTapListenerBlock: ((json: String?) -> Void)? // MARK: Colors @@ -127,7 +127,12 @@ public class FolioReaderConfig: NSObject { // MARK: - Constants -public struct Listener { + +public struct ListenerCofiguration { + static let className = "className" static let parameterName = "parameterName" + + var classNameValue : String + var parameterNameValue : String } diff --git a/Source/FolioReaderPage.swift b/Source/FolioReaderPage.swift index ed2fe8962..382aed245 100755 --- a/Source/FolioReaderPage.swift +++ b/Source/FolioReaderPage.swift @@ -228,7 +228,7 @@ class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGestureRecogni } else if url.scheme == "listener" { let parameter = (request.URL?.absoluteString.stringByReplacingOccurrencesOfString("listener://", withString: "").stringByRemovingPercentEncoding) - readerConfig.listenerBlock?(json: parameter) + readerConfig.userTapListenerBlock?(json: parameter) return false }else if url.scheme == "mailto" { print("Email") @@ -676,17 +676,13 @@ extension FolioReaderPage { private func extractListenerDictionary() -> String { var javascriptDictionary = "[" - for index in 0.. Date: Tue, 30 Aug 2016 19:01:56 +0200 Subject: [PATCH 03/10] Merge with solved conflicts --- Example/StoryboardExample/Base.lproj/Main.storyboard | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Example/StoryboardExample/Base.lproj/Main.storyboard b/Example/StoryboardExample/Base.lproj/Main.storyboard index 15ceab661..ed6e03531 100644 --- a/Example/StoryboardExample/Base.lproj/Main.storyboard +++ b/Example/StoryboardExample/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -42,6 +42,10 @@ + + + + From 12bda49ad092baf8c0a4bc0f012729c21b362fab Mon Sep 17 00:00:00 2001 From: Hans Seiffert Date: Wed, 7 Sep 2016 17:18:49 +0200 Subject: [PATCH 04/10] Fix typo --- Source/FolioReaderConfig.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/FolioReaderConfig.swift b/Source/FolioReaderConfig.swift index d1970dc24..1f0c0485a 100755 --- a/Source/FolioReaderConfig.swift +++ b/Source/FolioReaderConfig.swift @@ -49,7 +49,7 @@ Eg. A ClassBasedOnCLickListener with the className "quote" and parameterName "id */ public struct ClassBasedOnCLickListener { - /// The name of the URL scheme which should be used. Note: Make sure that the given `String` is valid as scheme. + /// 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 HTML class name to which the listener should be added. From 4d888f1e305b148f218d7bbe83162da0f3f268a8 Mon Sep 17 00:00:00 2001 From: Hans Seiffert Date: Thu, 8 Sep 2016 12:39:43 +0200 Subject: [PATCH 05/10] Fix typo in ClassBasedOnCLickListener --- Source/FolioReaderConfig.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/FolioReaderConfig.swift b/Source/FolioReaderConfig.swift index 1f0c0485a..c969e7507 100755 --- a/Source/FolioReaderConfig.swift +++ b/Source/FolioReaderConfig.swift @@ -39,15 +39,15 @@ public enum FolioReaderScrollDirection: Int { } } -// MARK: - ClassBasedOnCLickListener +// 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. +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. +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 { +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 @@ -61,7 +61,7 @@ public struct ClassBasedOnCLickListener { /// 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. + /// Initializes a `ClassBasedOnClickListener` instance. Append it to the `classBasedOnClickListeners` property from the `FolioReaderConfig` to receive on click events. public init(schemeName: String, className: String, parameterName: String, onClickAction: ((parameterContent: String?) -> Void)) { self.schemeName = schemeName.lowercaseString self.className = className @@ -77,15 +77,15 @@ public struct ClassBasedOnCLickListener { */ public class FolioReaderConfig: NSObject { - // MARK: ClassBasedOnCLickListener + // 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. + 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. + 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]() + public var classBasedOnClickListeners = [ClassBasedOnClickListener]() // MARK: Colors From bff575e013d9f7d6beb905203f16d81a32b64af0 Mon Sep 17 00:00:00 2001 From: Hans Seiffert Date: Thu, 8 Sep 2016 12:40:29 +0200 Subject: [PATCH 06/10] Add a simple ClassBasedOnClickListener example to the StoryboardExample project --- .../ExampleFolioReaderContainer.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Example/StoryboardExample/ExampleFolioReaderContainer.swift b/Example/StoryboardExample/ExampleFolioReaderContainer.swift index bb6729a51..63824e51d 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", className: "chapter", parameterName: "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) } From 41b52cc2a92f78693489f66f71ae583c51b7bf12 Mon Sep 17 00:00:00 2001 From: Hans Seiffert Date: Thu, 8 Sep 2016 12:42:56 +0200 Subject: [PATCH 07/10] Use valid URL scheme name --- Example/StoryboardExample/ExampleFolioReaderContainer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/StoryboardExample/ExampleFolioReaderContainer.swift b/Example/StoryboardExample/ExampleFolioReaderContainer.swift index 63824e51d..52afbe2b8 100644 --- a/Example/StoryboardExample/ExampleFolioReaderContainer.swift +++ b/Example/StoryboardExample/ExampleFolioReaderContainer.swift @@ -21,7 +21,7 @@ class ExampleFolioReaderContainer: FolioReaderContainer { // 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", className: "chapter", parameterName: "id", onClickAction: { (parameterContent: String?) in + let listener = ClassBasedOnClickListener(schemeName: "chaptertapped", className: "chapter", parameterName: "id", onClickAction: { (parameterContent: String?) in print("chapter with id: " + (parameterContent ?? "-") + " clicked") }) config.classBasedOnClickListeners.append(listener) From ac27c5d1cf7333c5d880aadf1c18ded66fd14522 Mon Sep 17 00:00:00 2001 From: Hans Seiffert Date: Wed, 14 Sep 2016 10:52:59 +0200 Subject: [PATCH 08/10] Fix not working highlight click if using a class based click listener --- Source/Resources/Bridge.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Resources/Bridge.js b/Source/Resources/Bridge.js index cf44f57ce..88df53cba 100755 --- a/Source/Resources/Bridge.js +++ b/Source/Resources/Bridge.js @@ -145,7 +145,8 @@ 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; @@ -595,5 +596,6 @@ function addClassBasedOnClickListener(schemeName, className, parameterName) { } var onClassBasedListenerClick = function(schemeName, parameterContent) { + event.stopPropagation(); window.location = schemeName + "://" + encodeURIComponent(parameterContent); } \ No newline at end of file From 5467a1a7ebfd157c2166917ed4058c505f1971ba Mon Sep 17 00:00:00 2001 From: Hans Seiffert Date: Wed, 14 Sep 2016 13:14:48 +0200 Subject: [PATCH 09/10] Escape parameter content of class based onclick listeners --- Source/Resources/Bridge.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Resources/Bridge.js b/Source/Resources/Bridge.js index 88df53cba..1696f767f 100755 --- a/Source/Resources/Bridge.js +++ b/Source/Resources/Bridge.js @@ -591,11 +591,10 @@ function addClassBasedOnClickListener(schemeName, className, parameterName) { // Get the content from the given parameterName var parameterContent = element.getAttribute(parameterName); // Add the on click logic - element.setAttribute("onclick", "onClassBasedListenerClick(\"" + schemeName + "\", \"" + parameterContent + "\");"); + element.setAttribute("onclick", "onClassBasedListenerClick(\"" + schemeName + "\", \"" + encodeURIComponent(parameterContent) + "\");"); } } var onClassBasedListenerClick = function(schemeName, parameterContent) { - event.stopPropagation(); - window.location = schemeName + "://" + encodeURIComponent(parameterContent); + window.location = schemeName + "://" + parameterContent; } \ No newline at end of file From 2649840712cffb193f8ff5fd734a8d7ab1d172cb Mon Sep 17 00:00:00 2001 From: Hans Seiffert Date: Thu, 15 Sep 2016 20:13:51 +0200 Subject: [PATCH 10/10] Refactor class based on click listener to use `querySelector(All)` --- .../ExampleFolioReaderContainer.swift | 2 +- Source/FolioReaderConfig.swift | 20 +++++++------ Source/FolioReaderPage.swift | 2 +- Source/Resources/Bridge.js | 28 +++++++++++++------ 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/Example/StoryboardExample/ExampleFolioReaderContainer.swift b/Example/StoryboardExample/ExampleFolioReaderContainer.swift index 52afbe2b8..493e4ed2d 100644 --- a/Example/StoryboardExample/ExampleFolioReaderContainer.swift +++ b/Example/StoryboardExample/ExampleFolioReaderContainer.swift @@ -21,7 +21,7 @@ class ExampleFolioReaderContainer: FolioReaderContainer { // 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", className: "chapter", parameterName: "id", onClickAction: { (parameterContent: String?) in + let listener = ClassBasedOnClickListener(schemeName: "chaptertapped", querySelector: ".chapter", attributeName: "id", onClickAction: { (parameterContent: String?) in print("chapter with id: " + (parameterContent ?? "-") + " clicked") }) config.classBasedOnClickListeners.append(listener) diff --git a/Source/FolioReaderConfig.swift b/Source/FolioReaderConfig.swift index 463ca883f..ba0b74d5b 100755 --- a/Source/FolioReaderConfig.swift +++ b/Source/FolioReaderConfig.swift @@ -52,20 +52,24 @@ 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 HTML class name to which the listener should be added. - public var className : 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 parameter whose content should be passed to the `onClickAction` action - public var parameterName : 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. - public init(schemeName: String, className: String, parameterName: String, 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.className = className - self.parameterName = parameterName + self.querySelector = querySelector + self.attributeName = attributeName + self.selectAll = selectAll self.onClickAction = onClickAction } } diff --git a/Source/FolioReaderPage.swift b/Source/FolioReaderPage.swift index ea88c47fa..19c14e7af 100755 --- a/Source/FolioReaderPage.swift +++ b/Source/FolioReaderPage.swift @@ -420,7 +420,7 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture private func setupClassBasedOnClickListeners() { for listener in readerConfig.classBasedOnClickListeners { - self.webView.js("addClassBasedOnClickListener(\"\(listener.schemeName)\", \"\(listener.className)\", \"\(listener.parameterName)\")"); + self.webView.js("addClassBasedOnClickListener(\"\(listener.schemeName)\", \"\(listener.querySelector)\", \"\(listener.attributeName)\", \"\(listener.selectAll)\")"); } } } diff --git a/Source/Resources/Bridge.js b/Source/Resources/Bridge.js index 1696f767f..927e66524 100755 --- a/Source/Resources/Bridge.js +++ b/Source/Resources/Bridge.js @@ -583,18 +583,28 @@ function wrappingSentencesWithinPTags(){ // Class based onClick listener -function addClassBasedOnClickListener(schemeName, className, parameterName) { - // Get all elements with the given className - var elements = document.getElementsByClassName(className); - for (elementIndex = 0; elementIndex < elements.length; elementIndex++) { - var element = elements[elementIndex]; - // Get the content from the given parameterName - var parameterContent = element.getAttribute(parameterName); - // Add the on click logic - element.setAttribute("onclick", "onClassBasedListenerClick(\"" + schemeName + "\", \"" + encodeURIComponent(parameterContent) + "\");"); +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