motion-turbo-ios 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +48 -0
- data/lib/motion-turbo-ios.rb +15 -0
- data/lib/turbo/logging.rb +25 -0
- data/lib/turbo/path_configuration/path_configuration.rb +55 -0
- data/lib/turbo/path_configuration/path_configuration_decoder.rb +25 -0
- data/lib/turbo/path_configuration/path_configuration_loader.rb +62 -0
- data/lib/turbo/path_configuration/path_rule.rb +34 -0
- data/lib/turbo/session/navigation_delegate_methods.rb +43 -0
- data/lib/turbo/session/session.rb +184 -0
- data/lib/turbo/session/session_delegate_methods.rb +30 -0
- data/lib/turbo/session/visit_delegate_methods.rb +67 -0
- data/lib/turbo/session/visitable_delegate_methods.rb +51 -0
- data/lib/turbo/session/web_view_delegate_methods.rb +46 -0
- data/lib/turbo/turbo_error.rb +30 -0
- data/lib/turbo/visit/cold_boot_visit.rb +109 -0
- data/lib/turbo/visit/javascript_visit.rb +107 -0
- data/lib/turbo/visit/visit.rb +91 -0
- data/lib/turbo/visit/visit_options.rb +36 -0
- data/lib/turbo/visit/visit_proposal.rb +13 -0
- data/lib/turbo/visit/visit_response.rb +29 -0
- data/lib/turbo/visitable/visitable.rb +58 -0
- data/lib/turbo/visitable/visitable_view.rb +20 -0
- data/lib/turbo/visitable/visitable_view_controller.rb +71 -0
- data/lib/turbo/visitable_view/activity_indicator.rb +36 -0
- data/lib/turbo/visitable_view/constraints.rb +14 -0
- data/lib/turbo/visitable_view/refresh_control.rb +61 -0
- data/lib/turbo/visitable_view/screenshots.rb +56 -0
- data/lib/turbo/visitable_view/scroll_view.rb +21 -0
- data/lib/turbo/visitable_view/web_view.rb +28 -0
- data/lib/turbo/web_view/script_message.rb +77 -0
- data/lib/turbo/web_view/script_message_handler.rb +16 -0
- data/lib/turbo/web_view/web_view_bridge.rb +154 -0
- metadata +90 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
module Turbo
|
2
|
+
class VisitableView < UIView
|
3
|
+
module RefreshControl
|
4
|
+
def refreshControl
|
5
|
+
@refreshControl ||= begin
|
6
|
+
refreshControl = UIRefreshControl.alloc.init
|
7
|
+
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEventValueChanged)
|
8
|
+
refreshControl
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def allowsPullToRefresh
|
13
|
+
return @allowsPullToRefresh if defined?(@allowsPullToRefresh)
|
14
|
+
@allowsPullToRefresh = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def allowsPullToRefresh=(allowsPullToRefresh)
|
18
|
+
@allowsPullToRefresh = allowsPullToRefresh
|
19
|
+
if allowsPullToRefresh
|
20
|
+
installRefreshControl
|
21
|
+
else
|
22
|
+
removeRefreshControl
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def isRefreshing
|
27
|
+
refreshControl.refreshing?
|
28
|
+
end
|
29
|
+
|
30
|
+
def refresh(sender)
|
31
|
+
visitable.visitableViewDidRequestRefresh if visitable
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def installRefreshControl
|
37
|
+
scrollView = webView.scrollView if webView
|
38
|
+
return unless scrollView && allowsPullToRefresh
|
39
|
+
# TODO
|
40
|
+
#if !targetEnvironment(macCatalyst)
|
41
|
+
scrollView.addSubview(refreshControl)
|
42
|
+
|
43
|
+
# Infer refresh control's default height from its frame, if given.
|
44
|
+
# Otherwise fallback to 60 (the default height).
|
45
|
+
refreshControlHeight = CGRectGetHeight(refreshControl.frame) > 0 ? CGRectGetHeight(refreshControl.frame) : 60
|
46
|
+
NSLayoutConstraint.activateConstraints([
|
47
|
+
refreshControl.centerXAnchor.constraintEqualToAnchor(centerXAnchor),
|
48
|
+
refreshControl.topAnchor.constraintEqualToAnchor(safeAreaLayoutGuide.topAnchor),
|
49
|
+
refreshControl.heightAnchor.constraintEqualToConstant(refreshControlHeight)
|
50
|
+
])
|
51
|
+
#endif
|
52
|
+
end
|
53
|
+
|
54
|
+
def removeRefreshControl
|
55
|
+
refreshControl.endRefreshing
|
56
|
+
#refreshControl.removeFromSuperview
|
57
|
+
webView.scrollView.refreshControl = nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Turbo
|
2
|
+
class VisitableView < UIView
|
3
|
+
module Screenshots
|
4
|
+
def screenshotContainerView
|
5
|
+
@screenshotContainerView ||= begin
|
6
|
+
view = UIView.alloc.initWithFrame(CGRectZero)
|
7
|
+
view.translatesAutoresizingMaskIntoConstraints = false
|
8
|
+
view.backgroundColor = backgroundColor
|
9
|
+
view
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :screenshotView
|
14
|
+
|
15
|
+
def isShowingScreenshot
|
16
|
+
screenshotContainerView.superview != nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def updateScreenshot
|
20
|
+
return unless webView
|
21
|
+
return if isShowingScreenshot
|
22
|
+
screenshot = webView.snapshotViewAfterScreenUpdates(false)
|
23
|
+
return unless screenshot
|
24
|
+
|
25
|
+
screenshotView.removeFromSuperview if screenshotView
|
26
|
+
screenshot.translatesAutoresizingMaskIntoConstraints = false
|
27
|
+
screenshotContainerView.addSubview(screenshot)
|
28
|
+
|
29
|
+
NSLayoutConstraint.activateConstraints([
|
30
|
+
screenshot.centerXAnchor.constraintEqualToAnchor(screenshotContainerView.centerXAnchor),
|
31
|
+
screenshot.topAnchor.constraintEqualToAnchor(screenshotContainerView.topAnchor),
|
32
|
+
screenshot.widthAnchor.constraintEqualToConstant(screenshot.bounds.size.width),
|
33
|
+
screenshot.heightAnchor.constraintEqualToConstant(screenshot.bounds.size.height)
|
34
|
+
])
|
35
|
+
@screenshotView = screenshot
|
36
|
+
end
|
37
|
+
|
38
|
+
def showScreenshot
|
39
|
+
if !isShowingScreenshot && !isRefreshing
|
40
|
+
addSubview(screenshotContainerView)
|
41
|
+
addFillConstraintsForSubview(screenshotContainerView)
|
42
|
+
showOrHideWebView
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def hideScreenshot
|
47
|
+
screenshotContainerView.removeFromSuperview
|
48
|
+
showOrHideWebView
|
49
|
+
end
|
50
|
+
|
51
|
+
def clearScreenshot
|
52
|
+
screenshotView.removeFromSuperview if screenshotView
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Turbo
|
2
|
+
class VisitableView < UIView
|
3
|
+
module ScrollView
|
4
|
+
private
|
5
|
+
|
6
|
+
def hiddenScrollView
|
7
|
+
@hiddenScrollView ||= begin
|
8
|
+
scrollView = UIScrollView.alloc.initWithFrame(CGRectZero)
|
9
|
+
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
10
|
+
scrollView.scrollsToTop = false
|
11
|
+
scrollView
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def installHiddenScrollView
|
16
|
+
insertSubview(hiddenScrollView, atIndex: 0)
|
17
|
+
addFillConstraintsForSubview(hiddenScrollView)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Turbo
|
2
|
+
class VisitableView < UIView
|
3
|
+
module WebView
|
4
|
+
attr_reader :webView, :visitable
|
5
|
+
|
6
|
+
def activateWebView(webView, forVisitable: visitable)
|
7
|
+
@webView = webView
|
8
|
+
@visitable = visitable
|
9
|
+
#addSubview(webView)
|
10
|
+
insertSubview(webView, atIndex: 0)
|
11
|
+
addFillConstraintsForSubview(webView)
|
12
|
+
installRefreshControl
|
13
|
+
showOrHideWebView
|
14
|
+
end
|
15
|
+
|
16
|
+
def deactivateWebView
|
17
|
+
removeRefreshControl
|
18
|
+
webView.removeFromSuperview if webView
|
19
|
+
@webView = nil
|
20
|
+
@visitable = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def showOrHideWebView
|
24
|
+
webView.hidden = isShowingScreenshot if webView
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Turbo
|
2
|
+
class ScriptMessage
|
3
|
+
NAMES = {
|
4
|
+
page_loaded: "pageLoaded",
|
5
|
+
page_load_failed: "pageLoadFailed",
|
6
|
+
error_raised: "errorRaised",
|
7
|
+
visit_proposed: "visitProposed",
|
8
|
+
visit_started: "visitStarted",
|
9
|
+
visit_request_started: "visitRequestStarted",
|
10
|
+
visit_request_completed: "visitRequestCompleted",
|
11
|
+
visit_request_failed: "visitRequestFailed",
|
12
|
+
visit_request_finished: "visitRequestFinished",
|
13
|
+
visit_rendered: "visitRendered",
|
14
|
+
visit_completed: "visitCompleted",
|
15
|
+
form_submission_started: "formSubmissionStarted",
|
16
|
+
form_submission_finished: "formSubmissionFinished",
|
17
|
+
page_invalidated: "pageInvalidated",
|
18
|
+
log: "log"
|
19
|
+
}
|
20
|
+
|
21
|
+
def self.parse(message)
|
22
|
+
body = message.body
|
23
|
+
return unless body
|
24
|
+
|
25
|
+
rawName = body["name"]
|
26
|
+
return unless rawName
|
27
|
+
|
28
|
+
name = NAMES.key(rawName)
|
29
|
+
return unless name
|
30
|
+
|
31
|
+
data = body["data"]
|
32
|
+
return unless data
|
33
|
+
|
34
|
+
return new(name, data)
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :name, :data
|
38
|
+
|
39
|
+
def initialize(name, data)
|
40
|
+
@name = name
|
41
|
+
@data = data
|
42
|
+
end
|
43
|
+
|
44
|
+
def identifier
|
45
|
+
identifier = data["identifier"]
|
46
|
+
identifier if identifier.is_a?(String)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Milliseconds since unix epoch as provided by JavaScript Date.now()
|
50
|
+
def timestamp
|
51
|
+
#data["timestamp"] as? TimeInterval ?? 0
|
52
|
+
timestamp = data["timestamp"]
|
53
|
+
timestamp.to_i || 0
|
54
|
+
end
|
55
|
+
|
56
|
+
def date
|
57
|
+
NSDate.alloc.initWithTimeIntervalSince1970(timestamp / 1000.0)
|
58
|
+
end
|
59
|
+
|
60
|
+
def restorationIdentifier
|
61
|
+
restorationIdentifier = data["restorationIdentifier"]
|
62
|
+
restorationIdentifier if restorationIdentifier.is_a?(String)
|
63
|
+
end
|
64
|
+
|
65
|
+
def location
|
66
|
+
NSURL.alloc.initWithString(data["location"]) if data["location"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def options
|
70
|
+
if options = data["options"]
|
71
|
+
VisitOptions.alloc.initFromHash(options)
|
72
|
+
else
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Turbo
|
2
|
+
# This class prevents retain cycle caused by WKUserContentController
|
3
|
+
class ScriptMessageHandler
|
4
|
+
|
5
|
+
attr_accessor :delegate
|
6
|
+
|
7
|
+
def initWithDelegate(delegate)
|
8
|
+
self.delegate = delegate
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def userContentController(userContentController, didReceiveScriptMessage: message)
|
13
|
+
delegate.scriptMessageHandlerDidReceiveMessage(message) if delegate
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Turbo
|
2
|
+
# The WebViewBridge is an internal class used for bi-directional communication
|
3
|
+
# with the web view/JavaScript
|
4
|
+
class WebViewBridge
|
5
|
+
attr_accessor :webView, :delegate, :pageLoadDelegate, :visitDelegate#, :navigationDelegate
|
6
|
+
def initWithWebView(webView)
|
7
|
+
@webView = webView
|
8
|
+
setup
|
9
|
+
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def setup
|
16
|
+
messageHandlerName = "turbo"
|
17
|
+
webView.configuration.userContentController.addUserScript(userScript)
|
18
|
+
scriptMessageHandler = ScriptMessageHandler.alloc.initWithDelegate(self)
|
19
|
+
webView.configuration.userContentController.addScriptMessageHandler(scriptMessageHandler, name: messageHandlerName)
|
20
|
+
end
|
21
|
+
|
22
|
+
def userScript
|
23
|
+
url = self.class.bundle.URLForResource("turbo", withExtension: "js")
|
24
|
+
source = NSString.stringWithContentsOfURL(url, encoding: NSUTF8StringEncoding, error: nil)
|
25
|
+
WKUserScript.alloc.initWithSource(source, injectionTime: WKUserScriptInjectionTimeAtDocumentEnd, forMainFrameOnly: true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.bundle
|
29
|
+
@bundle ||= NSBundle.bundleForClass(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
public
|
33
|
+
|
34
|
+
def visitLocation(location, withOptions: options, restorationIdentifier: restorationIdentifier)
|
35
|
+
raise unless options.is_a? Turbo::VisitOptions
|
36
|
+
callJavaScriptFunction("window.turboNative.visitLocationWithOptionsAndRestorationIdentifier",
|
37
|
+
withArguments: [
|
38
|
+
location.absoluteString,
|
39
|
+
options.encode,
|
40
|
+
restorationIdentifier]
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def clearSnapshotCache
|
45
|
+
callJavaScriptFunction("window.turboNative.clearSnapshotCache", withArguments: [])
|
46
|
+
end
|
47
|
+
|
48
|
+
def cancelVisitWithIdentifier(identifier)
|
49
|
+
callJavaScriptFunction("window.turboNative.cancelVisitWithIdentifier", withArguments: [identifier])
|
50
|
+
end
|
51
|
+
|
52
|
+
# JavaScript Evaluation
|
53
|
+
|
54
|
+
def callJavaScriptFunction(functionExpression, withArguments: arguments)
|
55
|
+
callJavaScriptFunction(functionExpression, withArguments: arguments, completionHandler: nil)
|
56
|
+
end
|
57
|
+
|
58
|
+
def callJavaScriptFunction(functionExpression, withArguments: arguments, completionHandler: completionHandler)
|
59
|
+
script = scriptForCallingJavaScriptFunction(functionExpression, withArguments: arguments)
|
60
|
+
unless script
|
61
|
+
NSLog("Error encoding arguments for JavaScript function `%@'", functionExpression)
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
debugLog("[Bridge] → #{functionExpression} #{arguments}")
|
66
|
+
|
67
|
+
webView.evaluateJavaScript(script, completionHandler: -> (result, error) {
|
68
|
+
debugLog("[Bridge] = #{functionExpression} evaluation complete")
|
69
|
+
|
70
|
+
if result
|
71
|
+
if error = result["error"]
|
72
|
+
stack = result["stack"]
|
73
|
+
NSLog("Error evaluating JavaScript function `%@': %@\n%@", functionExpression, error, stack)
|
74
|
+
else
|
75
|
+
completionHandler.call(result["value"]) if completionHandler
|
76
|
+
end
|
77
|
+
elsif error
|
78
|
+
delegate.webView(self, didFailJavaScriptEvaluationWithError: error) if delegate
|
79
|
+
end
|
80
|
+
})
|
81
|
+
end
|
82
|
+
|
83
|
+
def scriptForCallingJavaScriptFunction(functionExpression, withArguments: arguments)
|
84
|
+
encodedArguments = encodeJavaScriptArguments(arguments)
|
85
|
+
return unless encodedArguments
|
86
|
+
|
87
|
+
script = "(function(result) {\n" +
|
88
|
+
" try {\n" +
|
89
|
+
" result.value = " + functionExpression + "(" + encodedArguments + ")\n" +
|
90
|
+
" } catch (error) {\n" +
|
91
|
+
" result.error = error.toString()\n" +
|
92
|
+
" result.stack = error.stack\n" +
|
93
|
+
" }\n" +
|
94
|
+
" return result\n" +
|
95
|
+
"})({})"
|
96
|
+
return script
|
97
|
+
end
|
98
|
+
|
99
|
+
def encodeJavaScriptArguments(arguments)
|
100
|
+
arguments = arguments.map {|v| v.nil? ? NSNull.alloc.init() : v }
|
101
|
+
|
102
|
+
data = NSJSONSerialization.dataWithJSONObject(arguments, options: 0, error: nil)
|
103
|
+
if data
|
104
|
+
dataString = NSString.alloc.initWithData(data, encoding: NSUTF8StringEncoding)
|
105
|
+
return dataString[1..-2]
|
106
|
+
end
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def scriptMessageHandlerDidReceiveMessage(message)
|
111
|
+
message = ScriptMessage.parse(message)
|
112
|
+
return unless message
|
113
|
+
|
114
|
+
if message.name.to_sym != :log
|
115
|
+
debugLog("[Bridge] ← #{message.name} #{message.data}")
|
116
|
+
end
|
117
|
+
|
118
|
+
case message.name.to_sym
|
119
|
+
when :page_loaded
|
120
|
+
pageLoadDelegate.webView(self, didLoadPageWithRestorationIdentifier: message.restorationIdentifier) if pageLoadDelegate
|
121
|
+
when :page_load_failed
|
122
|
+
delegate.webView(self, didFailInitialPageLoadWithError: TurboError.pageLoadFailure) if delegate
|
123
|
+
when :form_submission_started
|
124
|
+
delegate.webView(self, didStartFormSubmissionToLocation: message.location) if delegate
|
125
|
+
when :form_submission_finished
|
126
|
+
delegate.webView(self, didFinishFormSubmissionToLocation: message.location) if delegate
|
127
|
+
when :page_invalidated
|
128
|
+
delegate.webViewDidInvalidatePage(self) if delegate
|
129
|
+
when :visit_proposed
|
130
|
+
delegate.webView(self, didProposeVisitToLocation: message.location, withOptions: message.options) if delegate
|
131
|
+
when :visit_started
|
132
|
+
visitDelegate.webView(self, didStartVisitWithIdentifier: message.identifier, hasCachedSnapshot: message.data["hasCachedSnapshot"]) if visitDelegate
|
133
|
+
when :visit_request_started
|
134
|
+
visitDelegate.webView(self, didStartRequestForVisitWithIdentifier: message.identifier, date: message.date) if visitDelegate
|
135
|
+
when :visit_request_completed
|
136
|
+
visitDelegate.webView(self, didCompleteRequestForVisitWithIdentifier: message.identifier) if visitDelegate
|
137
|
+
when :visit_request_failed
|
138
|
+
visitDelegate.webView(self, didFailRequestForVisitWithIdentifier: message.identifier, statusCode: message.data["statusCode"]) if visitDelegate
|
139
|
+
when :visit_request_finished
|
140
|
+
visitDelegate.webView(self, didFinishRequestForVisitWithIdentifier: message.identifier, date: message.date) if visitDelegate
|
141
|
+
when :visit_rendered
|
142
|
+
visitDelegate.webView(self, didRenderForVisitWithIdentifier: message.identifier) if visitDelegate
|
143
|
+
when :visit_completed
|
144
|
+
visitDelegate.webView(self, didCompleteVisitWithIdentifier: message.identifier, restorationIdentifier: message.restorationIdentifier) if visitDelegate
|
145
|
+
when :error_raised
|
146
|
+
error = message.data["error"] || "<unknown error>"
|
147
|
+
debugLog("JavaScript error: #{error}")
|
148
|
+
when :log
|
149
|
+
msg = message.data["message"]
|
150
|
+
debugLog("[Bridge] ← log: #{msg}") if msg.is_a?(String)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion-turbo-ios
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Havens
|
8
|
+
- Petrik de Heus
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2023-06-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
description: Turbo for RubyMotion apps
|
29
|
+
email:
|
30
|
+
- email@andrewhavens.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- README.md
|
36
|
+
- lib/motion-turbo-ios.rb
|
37
|
+
- lib/turbo/logging.rb
|
38
|
+
- lib/turbo/path_configuration/path_configuration.rb
|
39
|
+
- lib/turbo/path_configuration/path_configuration_decoder.rb
|
40
|
+
- lib/turbo/path_configuration/path_configuration_loader.rb
|
41
|
+
- lib/turbo/path_configuration/path_rule.rb
|
42
|
+
- lib/turbo/session/navigation_delegate_methods.rb
|
43
|
+
- lib/turbo/session/session.rb
|
44
|
+
- lib/turbo/session/session_delegate_methods.rb
|
45
|
+
- lib/turbo/session/visit_delegate_methods.rb
|
46
|
+
- lib/turbo/session/visitable_delegate_methods.rb
|
47
|
+
- lib/turbo/session/web_view_delegate_methods.rb
|
48
|
+
- lib/turbo/turbo_error.rb
|
49
|
+
- lib/turbo/visit/cold_boot_visit.rb
|
50
|
+
- lib/turbo/visit/javascript_visit.rb
|
51
|
+
- lib/turbo/visit/visit.rb
|
52
|
+
- lib/turbo/visit/visit_options.rb
|
53
|
+
- lib/turbo/visit/visit_proposal.rb
|
54
|
+
- lib/turbo/visit/visit_response.rb
|
55
|
+
- lib/turbo/visitable/visitable.rb
|
56
|
+
- lib/turbo/visitable/visitable_view.rb
|
57
|
+
- lib/turbo/visitable/visitable_view_controller.rb
|
58
|
+
- lib/turbo/visitable_view/activity_indicator.rb
|
59
|
+
- lib/turbo/visitable_view/constraints.rb
|
60
|
+
- lib/turbo/visitable_view/refresh_control.rb
|
61
|
+
- lib/turbo/visitable_view/screenshots.rb
|
62
|
+
- lib/turbo/visitable_view/scroll_view.rb
|
63
|
+
- lib/turbo/visitable_view/web_view.rb
|
64
|
+
- lib/turbo/web_view/script_message.rb
|
65
|
+
- lib/turbo/web_view/script_message_handler.rb
|
66
|
+
- lib/turbo/web_view/web_view_bridge.rb
|
67
|
+
homepage: https://github.com/p8/motion-turbo-ios
|
68
|
+
licenses:
|
69
|
+
- MIT
|
70
|
+
metadata: {}
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubygems_version: 3.1.6
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Turbo for RubyMotion apps
|
90
|
+
test_files: []
|