ProMotion 2.8.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +0 -3
- data/lib/ProMotion.rb +0 -59
- data/lib/ProMotion/screen/screen_module.rb +0 -5
- data/lib/ProMotion/table/table.rb +2 -5
- data/lib/ProMotion/version.rb +1 -1
- data/lib/ProMotion/web/ui_web_screen_module.rb +78 -0
- data/lib/ProMotion/web/web_screen_module.rb +7 -55
- data/lib/ProMotion/web/wk_web_screen_module.rb +87 -0
- data/spec/functional/func_table_screen_cell_spec.rb +1 -0
- data/spec/functional/func_web_screen_spec.rb +27 -17
- data/spec/unit/tables/table_view_cell_spec.rb +2 -2
- data/spec/unit/web_spec.rb +72 -50
- metadata +5 -6
- data/lib/ProMotion/repl/live_reloader.rb +0 -76
- data/lib/ProMotion/repl/repl.rb +0 -95
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8ab6f66bcfc437f402e9c8271b367626306a4d1a1cbe2e5cf7994abcf7570e4
|
4
|
+
data.tar.gz: 48619b0b60edff79951538f03a28a895c48a0b489c61d904f1f082c1d02faa6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7dfe0a34fe391efdf62bc11873c2c6bb45bfb0697eb91716df58bb60aaec40d18480adba66eedb585ed68c4f85b8214f404eca85828237c5ddac014b076603c2
|
7
|
+
data.tar.gz: 84291ed23351a205df87ef74a39e71adb2d1c885902358ec132e3395cfb436b22bc32ed14f30bffceeab09bba1f2831f5a454ab1d7626ba16d1574c2a67f2802
|
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://img.shields.io/gem/v/ProMotion.svg?style=flat)](https://rubygems.org/gems/ProMotion)
|
4
4
|
[![Build Status](https://img.shields.io/travis/infinitered/ProMotion.svg?style=flat)](https://travis-ci.org/infinitered/ProMotion)
|
5
|
-
[![Code Climate](https://img.shields.io/codeclimate/github/infinitered/ProMotion.svg?style=flat)](https://codeclimate.com/github/infinitered/ProMotion)
|
6
5
|
|
7
6
|
ProMotion was created by [Infinite Red](http://infinite.red), a web and mobile development company based in Portland, OR and San Francisco, CA. While you're welcome to use ProMotion, please note that we rely on the community to maintain it. We are happy to merge pull requests and release new versions but are no longer driving primary development.
|
8
7
|
|
@@ -76,8 +75,6 @@ end
|
|
76
75
|
|---|---|---|
|
77
76
|
|[![ProMotion SplitScreens](https://f.cloud.github.com/assets/1479215/1534507/0edb8dd4-4c96-11e3-9896-d4583d0ed161.png)](http://promotion.readthedocs.org/en/master/Reference/ProMotion%20SplitScreen/)|[![MapScreen](https://f.cloud.github.com/assets/1479215/1534628/f7dbf7e8-4c97-11e3-8817-4c2a58824771.png)](http://promotion.readthedocs.org/en/master/Reference/ProMotion%20MapScreen/)|[![ProMotion WebScreen](https://f.cloud.github.com/assets/1479215/1534631/ffe1b36a-4c97-11e3-8c8f-c7b14e26182d.png)](http://promotion.readthedocs.org/en/master/Reference/ProMotion%20WebScreen/)|
|
78
77
|
|
79
|
-
**NEW** [Live reloading!](http://promotion.readthedocs.org/en/master/Guides/Live Screen Reloading/) Use `pm_live` to enable it, and `on_live_reload` in your screen to refresh things.
|
80
|
-
|
81
78
|
#### ...and much more.
|
82
79
|
|
83
80
|
## Getting Started
|
data/lib/ProMotion.rb
CHANGED
@@ -12,63 +12,4 @@ Motion::Project::App.setup do |app|
|
|
12
12
|
app.files.insert(insert_point, file)
|
13
13
|
end
|
14
14
|
|
15
|
-
app.development do
|
16
|
-
app.info_plist["ProjectRootPath"] ||= File.absolute_path(app.project_dir)
|
17
|
-
end
|
18
|
-
|
19
|
-
# For compatibility with libraries that don't use detect_dependencies. :-(
|
20
|
-
app.files_dependencies({
|
21
|
-
"#{core_lib}/version.rb" => [ "#{core_lib}/pro_motion.rb" ],
|
22
|
-
"#{core_lib}/cocoatouch/table_view_cell.rb" => [ "#{core_lib}/table/cell/table_view_cell_module.rb" ],
|
23
|
-
"#{core_lib}/table/cell/table_view_cell_module.rb" => [ "#{core_lib}/styling/styling.rb" ],
|
24
|
-
"#{core_lib}/cocoatouch/collection_view_cell.rb" => [ "#{core_lib}/collection/cell/collection_view_cell_module.rb" ],
|
25
|
-
"#{core_lib}/collection/collection_screen.rb" => [
|
26
|
-
"#{core_lib}/cocoatouch/collection_view_controller.rb",
|
27
|
-
"#{core_lib}/screen/screen_module.rb",
|
28
|
-
"#{core_lib}/collection/collection_builder.rb",
|
29
|
-
"#{core_lib}/collection/collection.rb",
|
30
|
-
"#{core_lib}/collection/cell/collection_view_cell_module.rb",
|
31
|
-
],
|
32
|
-
"#{core_lib}/collection/cell/collection_view_cell_module.rb" => [ "#{core_lib}/styling/styling.rb" ],
|
33
|
-
"#{core_lib}/delegate/delegate.rb" => [ "#{core_lib}/delegate/delegate_parent.rb" ],
|
34
|
-
"#{core_lib}/delegate/delegate_parent.rb" => [ "#{core_lib}/delegate/delegate_module.rb" ],
|
35
|
-
"#{core_lib}/delegate/delegate_module.rb" => [
|
36
|
-
"#{core_lib}/support/support.rb",
|
37
|
-
"#{core_lib}/tabs/tabs.rb",
|
38
|
-
"#{core_lib}/ipad/split_screen.rb"
|
39
|
-
],
|
40
|
-
"#{core_lib}/screen/screen.rb" => [ "#{core_lib}/screen/screen_module.rb" ],
|
41
|
-
"#{core_lib}/screen/screen_navigation.rb" => [ "#{core_lib}/support/support.rb", ],
|
42
|
-
"#{core_lib}/screen/screen_module.rb" => [
|
43
|
-
"#{core_lib}/styling/styling.rb",
|
44
|
-
"#{core_lib}/tabs/tabs.rb",
|
45
|
-
"#{core_lib}/screen/nav_bar_module.rb",
|
46
|
-
"#{core_lib}/screen/screen_navigation.rb",
|
47
|
-
"#{core_lib}/ipad/split_screen.rb",
|
48
|
-
],
|
49
|
-
"#{core_lib}/table/data/table_data.rb" => [
|
50
|
-
"#{core_lib}/table/data/table_data_builder.rb",
|
51
|
-
"#{core_lib}/table/table.rb"
|
52
|
-
],
|
53
|
-
"#{core_lib}/collection/data/collection_data.rb" => [
|
54
|
-
"#{core_lib}/collection/data/collection_data_builder.rb",
|
55
|
-
"#{core_lib}/collection/collection.rb",
|
56
|
-
"#{core_lib}/table/table_utils.rb"
|
57
|
-
],
|
58
|
-
"#{core_lib}/table/table.rb" => [
|
59
|
-
"#{core_lib}/table/table_class_methods.rb",
|
60
|
-
"#{core_lib}/table/table_builder.rb",
|
61
|
-
"#{core_lib}/table/table_utils.rb",
|
62
|
-
"#{core_lib}/table/extensions/searchable.rb",
|
63
|
-
"#{core_lib}/table/extensions/refreshable.rb",
|
64
|
-
"#{core_lib}/table/extensions/indexable.rb",
|
65
|
-
"#{core_lib}/table/extensions/longpressable.rb"
|
66
|
-
],
|
67
|
-
"#{core_lib}/collection/collection.rb" => [
|
68
|
-
"#{core_lib}/collection/collection_class_methods.rb",
|
69
|
-
"#{core_lib}/collection/collection_builder.rb",
|
70
|
-
"#{core_lib}/table/table_utils.rb"
|
71
|
-
],
|
72
|
-
"#{core_lib}/web/web_screen.rb" => [ "#{core_lib}/web/web_screen_module.rb" ],
|
73
|
-
})
|
74
15
|
end
|
@@ -81,11 +81,6 @@ module ProMotion
|
|
81
81
|
mp "Received memory warning in #{self.inspect}. You should implement on_memory_warning in your screen.", force_color: :red
|
82
82
|
end
|
83
83
|
|
84
|
-
def on_live_reload
|
85
|
-
self.view.subviews.each(&:removeFromSuperview)
|
86
|
-
on_load
|
87
|
-
end
|
88
|
-
|
89
84
|
def should_rotate(orientation)
|
90
85
|
case orientation
|
91
86
|
when UIInterfaceOrientationPortrait
|
@@ -23,10 +23,6 @@ module ProMotion
|
|
23
23
|
set_up_row_height
|
24
24
|
end
|
25
25
|
|
26
|
-
def on_live_reload
|
27
|
-
update_table_data
|
28
|
-
end
|
29
|
-
|
30
26
|
def check_table_data
|
31
27
|
mp("Missing #table_data method in TableScreen #{self.class.to_s}.", force_color: :red) unless self.respond_to?(:table_data)
|
32
28
|
end
|
@@ -125,7 +121,8 @@ module ProMotion
|
|
125
121
|
|
126
122
|
def delete_row(index_paths, animation = nil)
|
127
123
|
deletable_index_paths = []
|
128
|
-
|
124
|
+
index_paths = [index_paths] if index_paths.kind_of?(NSIndexPath)
|
125
|
+
index_paths.each do |index_path|
|
129
126
|
delete_cell = false
|
130
127
|
|
131
128
|
delete_cell = trigger_action(:on_cell_deleted, cell_at(index_path: index_path), index_path) if respond_to?(:on_cell_deleted)
|
data/lib/ProMotion/version.rb
CHANGED
@@ -0,0 +1,78 @@
|
|
1
|
+
module ProMotion
|
2
|
+
module UIWebScreenModule
|
3
|
+
def web_view_setup
|
4
|
+
self.webview = add UIWebView.new, {
|
5
|
+
frame: CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height),
|
6
|
+
delegate: self,
|
7
|
+
data_detector_types: data_detector_types
|
8
|
+
}
|
9
|
+
|
10
|
+
self.webview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
|
11
|
+
self.webview.scalesPageToFit = self.scale_to_fit
|
12
|
+
self.webview.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate(js)
|
16
|
+
self.webview.stringByEvaluatingJavaScriptFromString(js)
|
17
|
+
end
|
18
|
+
|
19
|
+
def evaluate_async(js, &block)
|
20
|
+
Dispatch::Queue.concurrent.async do
|
21
|
+
result = evaluate(js)
|
22
|
+
Dispatch::Queue.main.async do
|
23
|
+
block.call result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def go_to_item(item)
|
29
|
+
# self.webview.goToBackForwardListItem(item)
|
30
|
+
PM.logger.warn "`go_to_item` is not implemented with the older UIWebView, which doesn't support it."
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def back_forward_list
|
35
|
+
# self.webview.backForwardList
|
36
|
+
PM.logger.warn "`back_forward_list` is not implemented with the older UIWebView, which doesn't support it."
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def progress
|
41
|
+
# self.webview.estimatedProgress
|
42
|
+
PM.logger.warn "`progress` is not implemented with the older UIWebView, which doesn't support it."
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
# CocoaTouch methods
|
47
|
+
|
48
|
+
def webView(in_web, shouldStartLoadWithRequest:in_request, navigationType:in_type)
|
49
|
+
if %w(http https).include?(in_request.URL.scheme)
|
50
|
+
if self.external_links == true && in_type == UIWebViewNavigationTypeLinkClicked
|
51
|
+
if defined?(OpenInChromeController)
|
52
|
+
open_in_chrome in_request
|
53
|
+
else
|
54
|
+
open_in_safari in_request
|
55
|
+
end
|
56
|
+
return false # don't allow the web view to load the link.
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
load_request_enable = true #return true on default for local file loading.
|
61
|
+
load_request_enable = !!on_request(in_request, in_type) if self.respond_to?(:on_request)
|
62
|
+
load_request_enable
|
63
|
+
end
|
64
|
+
|
65
|
+
def webViewDidStartLoad(webView)
|
66
|
+
load_started if self.respond_to?(:load_started)
|
67
|
+
end
|
68
|
+
|
69
|
+
def webViewDidFinishLoad(webView)
|
70
|
+
load_finished if self.respond_to?(:load_finished)
|
71
|
+
end
|
72
|
+
|
73
|
+
def webView(webView, didFailLoadWithError:error)
|
74
|
+
load_failed(error) if self.respond_to?("load_failed:")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module ProMotion
|
2
2
|
module WebScreenModule
|
3
|
+
include WKWebScreenModule if defined?(WKWebView)
|
4
|
+
include UIWebScreenModule unless defined?(WKWebView)
|
3
5
|
|
4
6
|
attr_accessor :webview, :external_links, :detector_types, :scale_to_fit
|
5
7
|
|
@@ -17,17 +19,6 @@ module ProMotion
|
|
17
19
|
# TODO: Remove in 3.0
|
18
20
|
end
|
19
21
|
|
20
|
-
def web_view_setup
|
21
|
-
self.webview ||= add UIWebView.new, {
|
22
|
-
frame: CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height),
|
23
|
-
delegate: self,
|
24
|
-
data_detector_types: data_detector_types
|
25
|
-
}
|
26
|
-
self.webview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
|
27
|
-
self.webview.scalesPageToFit = self.scale_to_fit
|
28
|
-
self.webview.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal
|
29
|
-
end
|
30
|
-
|
31
22
|
def web
|
32
23
|
self.webview
|
33
24
|
end
|
@@ -49,7 +40,6 @@ module ProMotion
|
|
49
40
|
if File.exists? content_path
|
50
41
|
content_string = File.read content_path
|
51
42
|
content_base_url = NSURL.fileURLWithPath NSBundle.mainBundle.resourcePath
|
52
|
-
|
53
43
|
self.web.loadHTMLString(convert_retina_images(content_string), baseURL:content_base_url)
|
54
44
|
else
|
55
45
|
# We assume the user wants to load an arbitrary string into the web view
|
@@ -58,10 +48,7 @@ module ProMotion
|
|
58
48
|
end
|
59
49
|
|
60
50
|
def open_url(url)
|
61
|
-
|
62
|
-
url.is_a?(NSURL) ? url : NSURL.URLWithString(url)
|
63
|
-
)
|
64
|
-
web.loadRequest request
|
51
|
+
web.loadRequest NSURLRequest.requestWithURL(url.to_url)
|
65
52
|
end
|
66
53
|
|
67
54
|
def convert_retina_images(content)
|
@@ -85,16 +72,12 @@ module ProMotion
|
|
85
72
|
mp("Missing #content method in WebScreen #{self.class.to_s}.", force_color: :red) unless self.respond_to?(:content)
|
86
73
|
end
|
87
74
|
|
88
|
-
def html
|
89
|
-
evaluate(
|
90
|
-
end
|
91
|
-
|
92
|
-
def evaluate(js)
|
93
|
-
self.webview.stringByEvaluatingJavaScriptFromString(js)
|
75
|
+
def html(&block)
|
76
|
+
evaluate('document.documentElement.outerHTML', &block)
|
94
77
|
end
|
95
78
|
|
96
|
-
def current_url
|
97
|
-
evaluate('document.URL')
|
79
|
+
def current_url(&block)
|
80
|
+
evaluate('document.URL', &block)
|
98
81
|
end
|
99
82
|
|
100
83
|
# Navigation
|
@@ -119,36 +102,6 @@ module ProMotion
|
|
119
102
|
UIApplication.sharedApplication.openURL(in_request.URL)
|
120
103
|
end
|
121
104
|
|
122
|
-
# UIWebViewDelegate Methods - Camelcase
|
123
|
-
def webView(in_web, shouldStartLoadWithRequest:in_request, navigationType:in_type)
|
124
|
-
if %w(http https).include?(in_request.URL.scheme)
|
125
|
-
if self.external_links == true && in_type == UIWebViewNavigationTypeLinkClicked
|
126
|
-
if defined?(OpenInChromeController)
|
127
|
-
open_in_chrome in_request
|
128
|
-
else
|
129
|
-
open_in_safari in_request
|
130
|
-
end
|
131
|
-
return false # don't allow the web view to load the link.
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
load_request_enable = true #return true on default for local file loading.
|
136
|
-
load_request_enable = !!on_request(in_request, in_type) if self.respond_to?(:on_request)
|
137
|
-
load_request_enable
|
138
|
-
end
|
139
|
-
|
140
|
-
def webViewDidStartLoad(webView)
|
141
|
-
load_started if self.respond_to?(:load_started)
|
142
|
-
end
|
143
|
-
|
144
|
-
def webViewDidFinishLoad(webView)
|
145
|
-
load_finished if self.respond_to?(:load_finished)
|
146
|
-
end
|
147
|
-
|
148
|
-
def webView(webView, didFailLoadWithError:error)
|
149
|
-
load_failed(error) if self.respond_to?("load_failed:")
|
150
|
-
end
|
151
|
-
|
152
105
|
protected
|
153
106
|
|
154
107
|
def data_detector_types
|
@@ -166,6 +119,5 @@ module ProMotion
|
|
166
119
|
all: UIDataDetectorTypeAll
|
167
120
|
}[symbol] || UIDataDetectorTypeNone
|
168
121
|
end
|
169
|
-
|
170
122
|
end
|
171
123
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module ProMotion
|
2
|
+
module WKWebScreenModule
|
3
|
+
def web_view_setup
|
4
|
+
configuration = WKWebViewConfiguration.alloc.init
|
5
|
+
configuration.dataDetectorTypes = data_detector_types
|
6
|
+
frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)
|
7
|
+
# FIXME: get this working: wkwebview = WKWebView.alloc.initWithFrame(frame, configuration: configuration)
|
8
|
+
wkwebview = WKWebView.alloc.initWithFrame(frame)
|
9
|
+
self.webview = add(wkwebview)
|
10
|
+
self.webview.UIDelegate = self
|
11
|
+
self.webview.navigationDelegate = self
|
12
|
+
self.webview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
|
13
|
+
self.webview.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal
|
14
|
+
end
|
15
|
+
|
16
|
+
def evaluate(js, &block)
|
17
|
+
if block
|
18
|
+
evaluate_async(js) do |result, error|
|
19
|
+
block.call(result) # ignore error
|
20
|
+
end
|
21
|
+
return
|
22
|
+
end
|
23
|
+
res = nil
|
24
|
+
semaphore = Dispatch::Semaphore.new(0)
|
25
|
+
# FIXME: this blocks and never returns
|
26
|
+
evaluate_async(js) do |result, error|
|
27
|
+
res = result
|
28
|
+
semaphore.signal
|
29
|
+
end
|
30
|
+
semaphore.wait(Dispatch::TIME_FOREVER)
|
31
|
+
return res
|
32
|
+
end
|
33
|
+
|
34
|
+
def evaluate_async(js, &block)
|
35
|
+
self.webview.evaluateJavaScript(js, completionHandler: -> (result, error) {
|
36
|
+
block.call(result, error)
|
37
|
+
})
|
38
|
+
end
|
39
|
+
|
40
|
+
def go_to_item(item)
|
41
|
+
self.webview.goToBackForwardListItem(item)
|
42
|
+
end
|
43
|
+
|
44
|
+
def back_forward_list
|
45
|
+
self.webview.backForwardList
|
46
|
+
end
|
47
|
+
|
48
|
+
def progress
|
49
|
+
self.webview.estimatedProgress
|
50
|
+
end
|
51
|
+
|
52
|
+
# CocoaTouch methods
|
53
|
+
|
54
|
+
def webView(view, decidePolicyForNavigationAction: navigationAction, decisionHandler: decisionHandler)
|
55
|
+
request = navigationAction.request
|
56
|
+
nav_type = navigationAction.navigationType
|
57
|
+
|
58
|
+
if %w(http https).include?(request.URL.scheme)
|
59
|
+
if self.external_links == true && nav_type == WKNavigationTypeLinkActivated
|
60
|
+
if defined?(OpenInChromeController)
|
61
|
+
open_in_chrome(request)
|
62
|
+
else
|
63
|
+
open_in_safari(request)
|
64
|
+
end
|
65
|
+
decisionHandler.call(WKNavigationActionPolicyCancel) # don't allow the web view to load the link
|
66
|
+
return
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
load_request_enable = true # return true by default for local file loading
|
71
|
+
load_request_enable = !!on_request(request, nav_type) if self.respond_to?(:on_request)
|
72
|
+
decisionHandler.call(load_request_enable ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel)
|
73
|
+
end
|
74
|
+
|
75
|
+
def webView(view, didCommitNavigation: navigation)
|
76
|
+
navigation_started(navigation) if self.respond_to?("navigation_started")
|
77
|
+
end
|
78
|
+
|
79
|
+
def webView(view, didFailNavigation: navigation, withError: error)
|
80
|
+
navigation_failed(navigation, error) if self.respond_to?("navigation_failed")
|
81
|
+
end
|
82
|
+
|
83
|
+
def webView(view, didFinishNavigation: navigation)
|
84
|
+
navigation_finished(navigation) if self.respond_to?("navigation_finished")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -64,6 +64,7 @@ describe "ProMotion::TableScreen functionality" do
|
|
64
64
|
table_screen.tableView.scrollToRowAtIndexPath(ip, atScrollPosition: UITableViewScrollPositionTop, animated: false)
|
65
65
|
|
66
66
|
cell = views(TestCell).first
|
67
|
+
cell.prepare_for_reuse_time.should.not.be.nil
|
67
68
|
cell.prepare_for_reuse_time.should < cell.on_reuse_time
|
68
69
|
end
|
69
70
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
describe "ProMotion::TestWebScreen functionality" do
|
2
|
-
|
2
|
+
# NOTE: webstub doesn't support stubbing WKWebView requests
|
3
|
+
# extend WebStub::SpecHelpers
|
3
4
|
|
4
5
|
before do
|
5
|
-
disable_network_access!
|
6
|
+
# disable_network_access!
|
6
7
|
UIView.setAnimationDuration 0.01
|
7
8
|
end
|
8
|
-
after
|
9
|
+
# after { enable_network_access! }
|
9
10
|
|
10
11
|
def controller
|
11
12
|
@controller ||= TestWebScreen.new(nav_bar: true)
|
@@ -18,32 +19,41 @@ describe "ProMotion::TestWebScreen functionality" do
|
|
18
19
|
it "should have the proper html content" do
|
19
20
|
file_name = "WebScreen.html"
|
20
21
|
|
21
|
-
controller.mock!(:
|
22
|
+
controller.mock!(:navigation_finished) do |nav|
|
22
23
|
loaded_file = File.read(File.join(NSBundle.mainBundle.resourcePath, file_name))
|
23
|
-
controller.html
|
24
|
-
|
24
|
+
controller.html do |html|
|
25
|
+
html.should.not.be.nil
|
26
|
+
html.delete("\n").should == loaded_file.delete("\n")
|
27
|
+
resume
|
28
|
+
end
|
25
29
|
end
|
26
30
|
controller.set_content(file_name)
|
27
31
|
wait_max 8 {}
|
28
32
|
end
|
29
33
|
|
30
34
|
it "should allow you to navigate to a website" do
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
# NOTE: webstub can't stub WKWebView requests
|
36
|
+
# stub_request(:get, "https://www.google.com/").
|
37
|
+
# to_return(body: %q{Google! <form action="/search">%}, content_type: "text/html")
|
38
|
+
|
39
|
+
controller.mock!(:navigation_finished) do |nav|
|
40
|
+
controller.html do |html|
|
41
|
+
html.should.include('<form action="/search"')
|
42
|
+
resume
|
43
|
+
end
|
37
44
|
end
|
38
|
-
controller.open_url(
|
45
|
+
controller.open_url('https://www.google.com/') # LIVE request!
|
39
46
|
wait_max 8 {}
|
40
47
|
end
|
41
48
|
|
42
49
|
it "should manipulate the webscreen contents with javascript" do
|
43
|
-
controller.mock!(:
|
44
|
-
controller.
|
45
|
-
|
46
|
-
|
50
|
+
controller.mock!(:navigation_finished) do |nav|
|
51
|
+
controller.evaluate_async('document.getElementById("cool").innerHTML = "Changed"') do |result, error|
|
52
|
+
controller.html do |html|
|
53
|
+
html.should =~ /<h1 id="cool">Changed<\/h1>/
|
54
|
+
resume
|
55
|
+
end
|
56
|
+
end
|
47
57
|
end
|
48
58
|
controller.set_content('<h1 id="cool">Something Cool</h1>')
|
49
59
|
wait_max 8 {}
|
@@ -79,11 +79,11 @@ describe "PM::TableViewCellModule" do
|
|
79
79
|
end
|
80
80
|
|
81
81
|
it "should allow attributed title" do
|
82
|
-
@attributed_subject.textLabel.attributedText.
|
82
|
+
@attributed_subject.textLabel.attributedText.string.should == "Attributed Title"
|
83
83
|
end
|
84
84
|
|
85
85
|
it "should allow attributed subtitle" do
|
86
|
-
@attributed_subject.detailTextLabel.attributedText.
|
86
|
+
@attributed_subject.detailTextLabel.attributedText.string.should == "Attributed Subtitle"
|
87
87
|
end
|
88
88
|
|
89
89
|
it "should have the right subtitle" do
|
data/spec/unit/web_spec.rb
CHANGED
@@ -1,63 +1,85 @@
|
|
1
1
|
describe "web screen properties" do
|
2
|
-
|
3
|
-
|
4
|
-
before
|
5
|
-
after
|
2
|
+
# NOTE: webstub doesn't work with WKWebView
|
3
|
+
# extend WebStub::SpecHelpers
|
4
|
+
# before { disable_network_access! }
|
5
|
+
# after { enable_network_access! }
|
6
6
|
|
7
7
|
it "should leave on_init available as a hook" do
|
8
|
-
webscreen = TestWebScreen.new
|
8
|
+
webscreen = TestWebScreen.new
|
9
9
|
webscreen.on_init_available?.should == true
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
it "can perform asynchronous javascript execution" do
|
13
|
+
webscreen = TestWebScreen.new
|
14
|
+
js = 'document.documentElement.innerHTML = "test"; document.documentElement.outerHTML'
|
15
|
+
webscreen.evaluate_async(js) do |result, error|
|
16
|
+
result.should == '<html><head></head><body>test</body></html>'
|
17
|
+
resume
|
18
|
+
end
|
19
|
+
wait_max 2 {}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can perform synchronous javascript execution" do
|
23
|
+
webscreen = TestWebScreen.new
|
24
|
+
js = 'document.documentElement.innerHTML = "test"; document.documentElement.outerHTML'
|
25
|
+
wait_max(2) do
|
26
|
+
result = webscreen.evaluate(js)
|
27
|
+
result.should == '<html><head></head><body>test</body></html>'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "opening a web page using an http request" do
|
13
32
|
before do
|
14
33
|
class TestWebScreenWithHTTPRequest < TestWebScreen
|
15
|
-
attr_accessor :
|
34
|
+
attr_accessor :on_request_called, :on_request_args
|
35
|
+
# NOTE: this hook is for deciding whether or not the request should continue
|
16
36
|
def on_request(request, type)
|
17
37
|
self.on_request_args = [request, type]
|
18
|
-
self.
|
38
|
+
self.on_request_called = true
|
39
|
+
false # cancel request since we can't stub it
|
19
40
|
end
|
20
41
|
end
|
21
|
-
|
22
|
-
|
42
|
+
|
43
|
+
# NOTE: webstub doesn't support stubbing WKWebView requests
|
44
|
+
#stub_request(:get, "https://mixi.jp/").
|
45
|
+
# to_return(body: "<html><body>[mixi]</body></html>", content_type: "text/html")
|
46
|
+
|
23
47
|
# Simulate AppDelegate setup of web screen
|
24
48
|
@webscreen = TestWebScreenWithHTTPRequest.new modal: true, nav_bar: true, external_links: false
|
25
49
|
end
|
50
|
+
|
51
|
+
it "should call on_request hook" do
|
52
|
+
@webscreen.open_url('https://mixi.jp/')
|
26
53
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
wait_for_change @webscreen, 'is_load_finished' do
|
31
|
-
@webscreen.current_url.should == 'http://mixi.jp/'
|
54
|
+
wait_for_change @webscreen, 'on_request_called' do
|
55
|
+
@webscreen.on_request_called.should == true
|
32
56
|
end
|
33
57
|
end
|
34
58
|
|
35
59
|
it "should open web page via NSMutableURLRequest" do
|
36
|
-
nsurl = NSURL.URLWithString('
|
60
|
+
nsurl = NSURL.URLWithString('https://mixi.jp/')
|
37
61
|
@webscreen.web.loadRequest NSMutableURLRequest.requestWithURL(nsurl)
|
38
62
|
|
39
|
-
wait_for_change @webscreen, '
|
40
|
-
@webscreen.
|
63
|
+
wait_for_change @webscreen, 'on_request_called' do
|
64
|
+
request = @webscreen.on_request_args.first
|
65
|
+
request.should.be.a.kind_of NSURLRequest
|
66
|
+
request.URL.absoluteString.should == 'https://mixi.jp/'
|
41
67
|
end
|
42
68
|
end
|
43
69
|
|
44
70
|
it "should open web page by url string" do
|
45
|
-
@webscreen.open_url('
|
46
|
-
wait_for_change @webscreen, 'is_load_finished' do
|
47
|
-
@webscreen.html.should =~ /mixi/
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should call on_request hook" do
|
52
|
-
@webscreen.open_url(NSURL.URLWithString('http://mixi.jp/'))
|
71
|
+
@webscreen.open_url('https://mixi.jp/')
|
53
72
|
|
54
|
-
wait_for_change @webscreen, '
|
55
|
-
@webscreen.on_request_args
|
56
|
-
|
57
|
-
|
73
|
+
wait_for_change @webscreen, 'on_request_called' do
|
74
|
+
request = @webscreen.on_request_args.first
|
75
|
+
request.should.be.a.kind_of NSURLRequest
|
76
|
+
request.URL.absoluteString.should == 'https://mixi.jp/'
|
58
77
|
end
|
59
78
|
end
|
60
79
|
|
80
|
+
# TODO: these features used to be supported prior to WKWebView migration
|
81
|
+
# it "can synchronously return the current page html"
|
82
|
+
# it "can synchronously return the current page url"
|
61
83
|
end
|
62
84
|
|
63
85
|
describe "when loading a static html file" do
|
@@ -68,16 +90,20 @@ describe "web screen properties" do
|
|
68
90
|
end
|
69
91
|
|
70
92
|
it "should get the url of content" do
|
71
|
-
@webscreen.current_url
|
93
|
+
@webscreen.current_url do |url|
|
94
|
+
url.should == 'about:blank'
|
95
|
+
resume
|
96
|
+
end
|
97
|
+
wait_max 1 {}
|
72
98
|
end
|
73
99
|
|
74
|
-
it "should have a
|
75
|
-
@webscreen.web.class.should ==
|
100
|
+
it "should have a WKWebView as the primary view" do
|
101
|
+
@webscreen.web.class.should == WKWebView
|
76
102
|
end
|
77
103
|
|
78
104
|
it "should load the about html page" do
|
79
|
-
wait_for_change @webscreen, '
|
80
|
-
@webscreen.
|
105
|
+
wait_for_change @webscreen, 'is_nav_finished', 3 do
|
106
|
+
@webscreen.is_nav_finished.should == true
|
81
107
|
end
|
82
108
|
end
|
83
109
|
end
|
@@ -85,29 +111,25 @@ describe "web screen properties" do
|
|
85
111
|
describe "when setting attributes" do
|
86
112
|
it "should have a default values" do
|
87
113
|
webscreen = TestWebScreen.new()
|
88
|
-
webscreen.web.dataDetectorTypes.should == UIDataDetectorTypeNone
|
89
|
-
webscreen.web.scalesPageToFit.should == false
|
114
|
+
webscreen.web.configuration.dataDetectorTypes.should == UIDataDetectorTypeNone
|
90
115
|
webscreen.external_links.should == false
|
91
116
|
end
|
92
117
|
|
93
|
-
|
94
|
-
|
95
|
-
webscreen.
|
96
|
-
|
118
|
+
# FIXME: these should work after getting initWithFrame:configuration: to work.
|
119
|
+
# it "should set a single data detector" do
|
120
|
+
# webscreen = TestWebScreen.new(detector_types: :phone)
|
121
|
+
# webscreen.web.configuration.dataDetectorTypes.should == UIDataDetectorTypePhoneNumber
|
122
|
+
# end
|
97
123
|
|
98
|
-
it "should set multiple data detectors" do
|
99
|
-
webscreen = TestWebScreen.new(detector_types: [:phone, :link])
|
100
|
-
webscreen.web.dataDetectorTypes.should == UIDataDetectorTypePhoneNumber | UIDataDetectorTypeLink
|
101
|
-
end
|
102
|
-
|
103
|
-
it "should set the scaling mode of the screen" do
|
104
|
-
webscreen = TestWebScreen.new(scale_to_fit: true)
|
105
|
-
webscreen.web.scalesPageToFit.should == true
|
106
|
-
end
|
124
|
+
# it "should set multiple data detectors" do
|
125
|
+
# webscreen = TestWebScreen.new(detector_types: [:phone, :link])
|
126
|
+
# webscreen.web.configuration.dataDetectorTypes.should == UIDataDetectorTypePhoneNumber | UIDataDetectorTypeLink
|
127
|
+
# end
|
107
128
|
|
108
129
|
it "should have the ability to open links externally" do
|
109
130
|
webscreen = TestWebScreen.new(external_links: true)
|
110
131
|
webscreen.external_links.should == true
|
132
|
+
# TODO: test that link click opens Safari
|
111
133
|
end
|
112
134
|
end
|
113
135
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ProMotion
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamon Holmgren
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-03-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: methadone
|
@@ -132,8 +132,6 @@ files:
|
|
132
132
|
- lib/ProMotion/ipad/split_screen.rb
|
133
133
|
- lib/ProMotion/logger/logger.rb
|
134
134
|
- lib/ProMotion/pro_motion.rb
|
135
|
-
- lib/ProMotion/repl/live_reloader.rb
|
136
|
-
- lib/ProMotion/repl/repl.rb
|
137
135
|
- lib/ProMotion/screen/nav_bar_module.rb
|
138
136
|
- lib/ProMotion/screen/screen.rb
|
139
137
|
- lib/ProMotion/screen/screen_module.rb
|
@@ -159,8 +157,10 @@ files:
|
|
159
157
|
- lib/ProMotion/table/table_utils.rb
|
160
158
|
- lib/ProMotion/tabs/tabs.rb
|
161
159
|
- lib/ProMotion/version.rb
|
160
|
+
- lib/ProMotion/web/ui_web_screen_module.rb
|
162
161
|
- lib/ProMotion/web/web_screen.rb
|
163
162
|
- lib/ProMotion/web/web_screen_module.rb
|
163
|
+
- lib/ProMotion/web/wk_web_screen_module.rb
|
164
164
|
- spec/functional/func_screen_spec.rb
|
165
165
|
- spec/functional/func_split_screen_spec.rb
|
166
166
|
- spec/functional/func_table_screen_cell_spec.rb
|
@@ -213,8 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
213
213
|
- !ruby/object:Gem::Version
|
214
214
|
version: '0'
|
215
215
|
requirements: []
|
216
|
-
|
217
|
-
rubygems_version: 2.7.6
|
216
|
+
rubygems_version: 3.0.6
|
218
217
|
signing_key:
|
219
218
|
specification_version: 4
|
220
219
|
summary: ProMotion is a fast way to get started building RubyMotion apps. Instead
|
@@ -1,76 +0,0 @@
|
|
1
|
-
class LiveReloader
|
2
|
-
attr_reader :path_query, :opts
|
3
|
-
|
4
|
-
def initialize(path, opts={})
|
5
|
-
@path_query = path
|
6
|
-
@opts = opts
|
7
|
-
end
|
8
|
-
|
9
|
-
def watch(&callback)
|
10
|
-
log path_query
|
11
|
-
log live_file_paths
|
12
|
-
|
13
|
-
@live_reload_timer = every(opts[:interval] || 0.5) do
|
14
|
-
live_files.each do |live_file, modified_date|
|
15
|
-
if File.exist?(live_file) && File.mtime(live_file) > modified_date
|
16
|
-
new_code = File.read(live_file)
|
17
|
-
eval(new_code)
|
18
|
-
callback.call *[live_file, new_code, parse_class_names(new_code)].first(callback.arity)
|
19
|
-
reload_live_files
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
log "Watching #{path_query}."
|
25
|
-
self
|
26
|
-
end
|
27
|
-
|
28
|
-
def stop
|
29
|
-
@live_reload_timer.invalidate if @live_reload_timer
|
30
|
-
@live_reload_timer = nil
|
31
|
-
log "Stopped."
|
32
|
-
self
|
33
|
-
end
|
34
|
-
|
35
|
-
def debug?
|
36
|
-
@opts[:debug]
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def every(interval, &callback)
|
42
|
-
NSTimer.scheduledTimerWithTimeInterval(interval, target: callback, selector: 'call:', userInfo: nil, repeats: true)
|
43
|
-
end
|
44
|
-
|
45
|
-
def live_files
|
46
|
-
@live_files ||= live_file_paths.inject({}) do |out, live_file_path_file|
|
47
|
-
out[live_file_path_file] = File.mtime(live_file_path_file)
|
48
|
-
out
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def reload_live_files
|
53
|
-
@live_files = nil
|
54
|
-
live_files
|
55
|
-
end
|
56
|
-
|
57
|
-
def log(s)
|
58
|
-
puts s.inspect if debug?
|
59
|
-
s
|
60
|
-
end
|
61
|
-
|
62
|
-
def project_root_path
|
63
|
-
NSBundle.mainBundle.infoDictionary["ProjectRootPath"]
|
64
|
-
end
|
65
|
-
|
66
|
-
def live_file_paths
|
67
|
-
log Dir.glob("#{project_root_path}/#{path_query}").to_a
|
68
|
-
end
|
69
|
-
|
70
|
-
def parse_class_names(code)
|
71
|
-
log code.split("\n").map do |code_line|
|
72
|
-
matched = code_line.match(/^\s*class\s+(\S+)\s+</)
|
73
|
-
matched[1] if matched
|
74
|
-
end.to_a.compact.to_a
|
75
|
-
end
|
76
|
-
end
|
data/lib/ProMotion/repl/repl.rb
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
if RUBYMOTION_ENV == "development"
|
2
|
-
puts "Type `pm_live` to enable ProMotion's live reload system."
|
3
|
-
module Kernel
|
4
|
-
|
5
|
-
@live_reloaders ||= []
|
6
|
-
|
7
|
-
def register_live_reloader watcher
|
8
|
-
@live_reloaders << watcher
|
9
|
-
end
|
10
|
-
|
11
|
-
def pm_live(opts={})
|
12
|
-
|
13
|
-
@watchers.each {|watcher| watcher.stop} if @watchers
|
14
|
-
|
15
|
-
if opts == false || opts.to_s.downcase == "off"
|
16
|
-
@watchers = nil
|
17
|
-
"Live reloading of PM screens is now off."
|
18
|
-
else
|
19
|
-
@watchers = live_reloaders.collect {|reloader| reloader.(opts)}
|
20
|
-
mp @watchers if opts[:debug]
|
21
|
-
|
22
|
-
watching = @watchers.collect {|watcher| watcher.path_query}
|
23
|
-
"Live reloading of #{watching.join(", ")} is now on."
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
alias_method :pm_live_screens, :pm_live
|
28
|
-
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def live_reloaders
|
33
|
-
Kernel.instance_variable_get(:@live_reloaders)
|
34
|
-
end
|
35
|
-
|
36
|
-
def screen_watcher
|
37
|
-
lambda do | opts |
|
38
|
-
LiveReloader.new("app/screens/**/*.rb", opts).watch do |reloaded_file, new_code, class_names|
|
39
|
-
vcs = pm_all_view_controllers(UIApplication.sharedApplication.delegate.window.rootViewController)
|
40
|
-
vcs.each do |vc|
|
41
|
-
if pm_is_in_ancestry?(vc, class_names)
|
42
|
-
puts "Sending :on_live_reload to #{vc.inspect}." if opts[:debug]
|
43
|
-
vc.send(:on_live_reload) if vc.respond_to?(:on_live_reload)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
register_live_reloader screen_watcher
|
51
|
-
|
52
|
-
def view_watcher
|
53
|
-
lambda do | opts |
|
54
|
-
LiveReloader.new("app/views/**/*.rb", opts).watch do |reloaded_file, new_code, class_names|
|
55
|
-
views = pm_all_views(UIApplication.sharedApplication.delegate.window)
|
56
|
-
views.each do |view|
|
57
|
-
if pm_is_in_ancestry?(view, class_names)
|
58
|
-
puts "Sending :on_live_reload to #{view.inspect}." if opts[:debug]
|
59
|
-
view.send(:on_live_reload) if view.respond_to?(:on_live_reload)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
register_live_reloader view_watcher
|
67
|
-
|
68
|
-
# Very permissive. Might get unnecessary reloads. That's okay.
|
69
|
-
def pm_is_in_ancestry?(vc, screen_names)
|
70
|
-
screen_names.any? do |screen_name|
|
71
|
-
vc.class.to_s.include?(screen_name) ||
|
72
|
-
vc.class.ancestors.any? do |ancestor|
|
73
|
-
screen_name.include?(ancestor.to_s)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def pm_all_view_controllers(root_view_controller)
|
79
|
-
vcs = [ root_view_controller ]
|
80
|
-
if root_view_controller.respond_to?(:viewControllers)
|
81
|
-
vcs = vcs + root_view_controller.viewControllers.map{|vc| pm_all_view_controllers(vc) }
|
82
|
-
end
|
83
|
-
if root_view_controller.respond_to?(:childViewControllers)
|
84
|
-
vcs = vcs + root_view_controller.childViewControllers.map{|vc| pm_all_view_controllers(vc) }
|
85
|
-
end
|
86
|
-
vcs.flatten.uniq
|
87
|
-
end
|
88
|
-
|
89
|
-
def pm_all_views(root_view)
|
90
|
-
views = [ root_view ]
|
91
|
-
views = views + views.map{|v| v.subviews.map{|sv| pm_all_views(sv) } }
|
92
|
-
views.flatten.uniq
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|