Skip to content
This repository has been archived by the owner on Nov 26, 2020. It is now read-only.

Custom click listener - closes #132 #137

Merged
merged 14 commits into from
Sep 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Example/StoryboardExample/ExampleFolioReaderContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<section class="chapter" title="Chapter I" epub:type="chapter" id="id70364673704880">"
// 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)
}
Expand Down
50 changes: 49 additions & 1 deletion Source/FolioReaderConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import UIKit

// MARK: - FolioReaderScrollDirection

/**
Defines the Reader scrolling direction
*/
Expand Down Expand Up @@ -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 "<section class="quote" id="12345">" 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 "<section class="quote" id="12345">" 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
Expand Down
40 changes: 35 additions & 5 deletions Source/FolioReaderPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Down
32 changes: 30 additions & 2 deletions Source/Resources/Bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}