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

Commit

Permalink
Provide a way to perform java script code in a readers page from ext…
Browse files Browse the repository at this point in the history
…ernal code (#155)

* Refactor  `UIWebView` extension to `FolioReaderWebView`

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

* Replace `UIWebView` casting with `guard let` implementation

* Add new  `FolioReaderWebView` class to the `FolioReaderKit.xcodeproj`
  • Loading branch information
tschob authored and hebertialmeida committed Sep 23, 2016
1 parent 4ac4cfa commit 29d66ce
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 269 deletions.
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

0 comments on commit 29d66ce

Please sign in to comment.