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

Provide a way to perform java script code in a readers page from external code #155

Merged
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
4 changes: 4 additions & 0 deletions FolioReaderKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
1A9590131D397BE900D56699 /* ScrollScrubber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A958F901D397BE900D56699 /* ScrollScrubber.swift */; };
1A9590151D397C1300D56699 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1A9590141D397C1300D56699 /* Images.xcassets */; };
1A9590171D397CAF00D56699 /* FolioReaderKit.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 1A9590161D397CAF00D56699 /* FolioReaderKit.podspec */; };
3AD5EEB91D9433C100E42810 /* FolioReaderWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD5EEB81D9433C100E42810 /* FolioReaderWebView.swift */; };
B0D6990F1D035FA2003B4CCD /* FolioReaderKit.h in Headers */ = {isa = PBXBuildFile; fileRef = B0D6990E1D035FA2003B4CCD /* FolioReaderKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
B0D699161D035FA2003B4CCD /* FolioReaderKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B0D6990B1D035FA2003B4CCD /* FolioReaderKit.framework */; };
B0D6991B1D035FA2003B4CCD /* FolioReaderKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D6991A1D035FA2003B4CCD /* FolioReaderKitTests.swift */; };
Expand Down Expand Up @@ -134,6 +135,7 @@
1A958F901D397BE900D56699 /* ScrollScrubber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollScrubber.swift; sourceTree = "<group>"; };
1A9590141D397C1300D56699 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
1A9590161D397CAF00D56699 /* FolioReaderKit.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = FolioReaderKit.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
3AD5EEB81D9433C100E42810 /* FolioReaderWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolioReaderWebView.swift; sourceTree = "<group>"; };
B0D6990B1D035FA2003B4CCD /* FolioReaderKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FolioReaderKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B0D6990E1D035FA2003B4CCD /* FolioReaderKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FolioReaderKit.h; sourceTree = "<group>"; };
B0D699101D035FA2003B4CCD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -194,6 +196,7 @@
1A958F1A1D397BE900D56699 /* FolioReaderPageIndicator.swift */,
1A958F1B1D397BE900D56699 /* FolioReaderPlayerMenu.swift */,
1A958F1C1D397BE900D56699 /* FolioReaderSharingProvider.swift */,
3AD5EEB81D9433C100E42810 /* FolioReaderWebView.swift */,
1A958F1D1D397BE900D56699 /* Models */,
1A958F241D397BE900D56699 /* PageViewController.swift */,
1A958F251D397BE900D56699 /* Resources */,
Expand Down Expand Up @@ -516,6 +519,7 @@
1A958FA91D397BE900D56699 /* CoreDataManager.swift in Sources */,
1A958FA81D397BE900D56699 /* FolioReaderSharingProvider.swift in Sources */,
1A958FAB1D397BE900D56699 /* Highlight+Helper.swift in Sources */,
3AD5EEB91D9433C100E42810 /* FolioReaderWebView.swift in Sources */,
1A958FA41D397BE900D56699 /* FolioReaderKit.swift in Sources */,
1A958FA21D397BE900D56699 /* FolioReaderHighlightList.swift in Sources */,
1A958FA71D397BE900D56699 /* FolioReaderPlayerMenu.swift in Sources */,
Expand Down
289 changes: 20 additions & 269 deletions Source/FolioReaderPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture
weak var delegate: FolioReaderPageDelegate?
/// The index of the current page. Note: The index start at 1!
public var pageNumber: Int!
var webView: UIWebView!
var webView: FolioReaderWebView!
private var colorView: UIView!
private var shouldShowBar = true
private var menuIsVisible = false
Expand All @@ -41,7 +41,7 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(refreshPageMode), name: "needRefreshPageMode", object: nil)

if webView == nil {
webView = UIWebView(frame: webViewFrame())
webView = FolioReaderWebView(frame: webViewFrame())
webView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
webView.dataDetectorTypes = [.None, .Link]
webView.scrollView.showsVerticalScrollIndicator = false
Expand Down Expand Up @@ -136,6 +136,9 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture
// MARK: - UIWebView Delegate

public func webViewDidFinishLoad(webView: UIWebView) {
guard let webView = webView as? FolioReaderWebView else {
return
}

// Add the custom class based onClick listener
self.setupClassBasedOnClickListeners()
Expand Down Expand Up @@ -165,7 +168,10 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture
}

public func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {

guard let webView = webView as? FolioReaderWebView else {
return true
}

guard let url = request.URL else { return false }

if url.scheme == "highlight" {
Expand Down Expand Up @@ -297,7 +303,7 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture

public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {

if gestureRecognizer.view is UIWebView {
if gestureRecognizer.view is FolioReaderWebView {
if otherGestureRecognizer is UILongPressGestureRecognizer {
if UIMenuController.sharedMenuController().menuVisible {
webView.setMenuVisible(false)
Expand Down Expand Up @@ -454,276 +460,21 @@ public class FolioReaderPage: UICollectionViewCell, UIWebViewDelegate, UIGesture
self.webView.js("addClassBasedOnClickListener(\"\(listener.schemeName)\", \"\(listener.querySelector)\", \"\(listener.attributeName)\", \"\(listener.selectAll)\")");
}
}
}

// MARK: - WebView Highlight and share implementation

private var cAssociationKey: UInt8 = 0
private var sAssociationKey: UInt8 = 0

extension UIWebView {

var isColors: Bool {
get { return objc_getAssociatedObject(self, &cAssociationKey) as? Bool ?? false }
set(newValue) {
objc_setAssociatedObject(self, &cAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}

var isShare: Bool {
get { return objc_getAssociatedObject(self, &sAssociationKey) as? Bool ?? false }
set(newValue) {
objc_setAssociatedObject(self, &sAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}

public override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {

if(readerConfig == nil){
return super.canPerformAction(action, withSender: sender)
}

// menu on existing highlight
if isShare {
if action == #selector(UIWebView.colors(_:)) || (action == #selector(UIWebView.share(_:)) && readerConfig.allowSharing) || action == #selector(UIWebView.remove(_:)) {
return true
}
return false

// menu for selecting highlight color
} else if isColors {
if action == #selector(UIWebView.setYellow(_:)) || action == #selector(UIWebView.setGreen(_:)) || action == #selector(UIWebView.setBlue(_:)) || action == #selector(UIWebView.setPink(_:)) || action == #selector(UIWebView.setUnderline(_:)) {
return true
}
return false

// default menu
} else {
var isOneWord = false
if let result = js("getSelectedText()") where result.componentsSeparatedByString(" ").count == 1 {
isOneWord = true
}

if action == #selector(UIWebView.highlight(_:))
|| (action == #selector(UIWebView.define(_:)) && isOneWord)
|| (action == #selector(UIWebView.play(_:)) && (book.hasAudio() || readerConfig.enableTTS))
|| (action == #selector(UIWebView.share(_:)) && readerConfig.allowSharing)
|| (action == #selector(NSObject.copy(_:)) && readerConfig.allowSharing) {
return true
}
return false
}
}

public override func canBecomeFirstResponder() -> Bool {
return true
}

func share(sender: UIMenuController) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)

let shareImage = UIAlertAction(title: readerConfig.localizedShareImageQuote, style: .Default, handler: { (action) -> Void in
if self.isShare {
if let textToShare = self.js("getHighlightContent()") {
FolioReader.sharedInstance.readerCenter?.presentQuoteShare(textToShare)
}
} else {
if let textToShare = self.js("getSelectedText()") {
FolioReader.sharedInstance.readerCenter?.presentQuoteShare(textToShare)
self.userInteractionEnabled = false
self.userInteractionEnabled = true
}
}
self.setMenuVisible(false)
})

let shareText = UIAlertAction(title: readerConfig.localizedShareTextQuote, style: .Default) { (action) -> Void in
if self.isShare {
if let textToShare = self.js("getHighlightContent()") {
FolioReader.sharedInstance.readerCenter?.shareHighlight(textToShare, rect: sender.menuFrame)
}
} else {
if let textToShare = self.js("getSelectedText()") {
FolioReader.sharedInstance.readerCenter?.shareHighlight(textToShare, rect: sender.menuFrame)
}
}
self.setMenuVisible(false)
}

let cancel = UIAlertAction(title: readerConfig.localizedCancel, style: .Cancel, handler: nil)

alertController.addAction(shareImage)
alertController.addAction(shareText)
alertController.addAction(cancel)

FolioReader.sharedInstance.readerCenter?.presentViewController(alertController, animated: true, completion: nil)
}

func colors(sender: UIMenuController?) {
isColors = true
createMenu(options: false)
setMenuVisible(true)
}

func remove(sender: UIMenuController?) {
if let removedId = js("removeThisHighlight()") {
Highlight.removeById(removedId)
}
setMenuVisible(false)
}

func highlight(sender: UIMenuController?) {
let highlightAndReturn = js("highlightString('\(HighlightStyle.classForStyle(FolioReader.currentHighlightStyle))')")
let jsonData = highlightAndReturn?.dataUsingEncoding(NSUTF8StringEncoding)

do {
let json = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: []) as! NSArray
let dic = json.firstObject as! [String: String]
let rect = CGRectFromString(dic["rect"]!)
let startOffset = dic["startOffset"]!
let endOffset = dic["endOffset"]!

// Force remove text selection
userInteractionEnabled = false
userInteractionEnabled = true

createMenu(options: true)
setMenuVisible(true, andRect: rect)

// Persist
let html = js("getHTML()")
if let highlight = Highlight.matchHighlight(html, andId: dic["id"]!, startOffset: startOffset, endOffset: endOffset) {
highlight.persist()
}
} catch {
print("Could not receive JSON")
}
}

func define(sender: UIMenuController?) {
let selectedText = js("getSelectedText()")

setMenuVisible(false)
userInteractionEnabled = false
userInteractionEnabled = true

let vc = UIReferenceLibraryViewController(term: selectedText! )
vc.view.tintColor = readerConfig.tintColor
FolioReader.sharedInstance.readerContainer.showViewController(vc, sender: nil)
}

func play(sender: UIMenuController?) {
FolioReader.sharedInstance.readerAudioPlayer?.play()
// MARK: - Public Java Script injection

// Force remove text selection
// @NOTE: this doesn't seem to always work
userInteractionEnabled = false
userInteractionEnabled = true
}


// MARK: - Set highlight styles

func setYellow(sender: UIMenuController?) {
changeHighlightStyle(sender, style: .Yellow)
}

func setGreen(sender: UIMenuController?) {
changeHighlightStyle(sender, style: .Green)
}

func setBlue(sender: UIMenuController?) {
changeHighlightStyle(sender, style: .Blue)
}

func setPink(sender: UIMenuController?) {
changeHighlightStyle(sender, style: .Pink)
}

func setUnderline(sender: UIMenuController?) {
changeHighlightStyle(sender, style: .Underline)
}
/**
Runs a JavaScript script and returns it result. The result of running the JavaScript script passed in the script parameter, or nil if the script fails.

func changeHighlightStyle(sender: UIMenuController?, style: HighlightStyle) {
FolioReader.currentHighlightStyle = style.rawValue

if let updateId = js("setHighlightStyle('\(HighlightStyle.classForStyle(style.rawValue))')") {
Highlight.updateById(updateId, type: style)
}
colors(sender)
}

// MARK: - Create and show menu

func createMenu(options options: Bool) {
isShare = options

let colors = UIImage(readerImageNamed: "colors-marker")
let share = UIImage(readerImageNamed: "share-marker")
let remove = UIImage(readerImageNamed: "no-marker")
let yellow = UIImage(readerImageNamed: "yellow-marker")
let green = UIImage(readerImageNamed: "green-marker")
let blue = UIImage(readerImageNamed: "blue-marker")
let pink = UIImage(readerImageNamed: "pink-marker")
let underline = UIImage(readerImageNamed: "underline-marker")

let highlightItem = UIMenuItem(title: readerConfig.localizedHighlightMenu, action: #selector(UIWebView.highlight(_:)))
let playAudioItem = UIMenuItem(title: readerConfig.localizedPlayMenu, action: #selector(UIWebView.play(_:)))
let defineItem = UIMenuItem(title: readerConfig.localizedDefineMenu, action: #selector(UIWebView.define(_:)))
let colorsItem = UIMenuItem(title: "C", image: colors!, action: #selector(UIWebView.colors(_:)))
let shareItem = UIMenuItem(title: "S", image: share!, action: #selector(UIWebView.share(_:)))
let removeItem = UIMenuItem(title: "R", image: remove!, action: #selector(UIWebView.remove(_:)))
let yellowItem = UIMenuItem(title: "Y", image: yellow!, action: #selector(UIWebView.setYellow(_:)))
let greenItem = UIMenuItem(title: "G", image: green!, action: #selector(UIWebView.setGreen(_:)))
let blueItem = UIMenuItem(title: "B", image: blue!, action: #selector(UIWebView.setBlue(_:)))
let pinkItem = UIMenuItem(title: "P", image: pink!, action: #selector(UIWebView.setPink(_:)))
let underlineItem = UIMenuItem(title: "U", image: underline!, action: #selector(UIWebView.setUnderline(_:)))

let menuItems = [playAudioItem, highlightItem, defineItem, colorsItem, removeItem, yellowItem, greenItem, blueItem, pinkItem, underlineItem, shareItem]

UIMenuController.sharedMenuController().menuItems = menuItems
}

func setMenuVisible(menuVisible: Bool, animated: Bool = true, andRect rect: CGRect = CGRectZero) {
if !menuVisible && isShare || !menuVisible && isColors {
isColors = false
isShare = false
}

if menuVisible {
if !CGRectEqualToRect(rect, CGRectZero) {
UIMenuController.sharedMenuController().setTargetRect(rect, inView: self)
}
}

UIMenuController.sharedMenuController().setMenuVisible(menuVisible, animated: animated)
}

func js(script: String) -> String? {
let callback = self.stringByEvaluatingJavaScriptFromString(script)
if callback!.isEmpty { return nil }
return callback
}

// MARK: WebView direction config

func setupScrollDirection() {
switch readerConfig.scrollDirection {
case .vertical, .horizontalWithVerticalContent:
scrollView.pagingEnabled = false
paginationMode = .Unpaginated
scrollView.bounces = true
break
case .horizontal:
scrollView.pagingEnabled = true
paginationMode = .LeftToRight
paginationBreakingMode = .Page
scrollView.bounces = false
break
}
}
- returns: The result of running the JavaScript script passed in the script parameter, or nil if the script fails.
*/
public func performJavaScript(javaScriptCode: String) -> String? {
return webView.js(javaScriptCode)
}
}

// MARK: - UIMenuItem extension

extension UIMenuItem {
convenience init(title: String, image: UIImage, action: Selector) {
#if COCOAPODS
Expand Down
Loading