under-os 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +16 -0
- data/README.md +70 -0
- data/Rakefile +24 -0
- data/app/config.rb +4 -0
- data/app/layouts/alerts.html +8 -0
- data/app/layouts/calculator.html +26 -0
- data/app/layouts/collections.html +7 -0
- data/app/layouts/home.html +14 -0
- data/app/layouts/http.html +8 -0
- data/app/layouts/inputs.html +44 -0
- data/app/layouts/lockers.html +6 -0
- data/app/layouts/navbar.html +9 -0
- data/app/layouts/scrolls.html +42 -0
- data/app/layouts/sidebars.html +17 -0
- data/app/pages/alerts_page.rb +14 -0
- data/app/pages/calculator_page.rb +36 -0
- data/app/pages/collections_page.rb +9 -0
- data/app/pages/home_page.rb +11 -0
- data/app/pages/http_page.rb +33 -0
- data/app/pages/inputs_page.rb +9 -0
- data/app/pages/lockers_page.rb +16 -0
- data/app/pages/navbar_page.rb +19 -0
- data/app/pages/scrolls_page.rb +5 -0
- data/app/pages/sidebars_page.rb +42 -0
- data/app/pages/stuff_page.rb +35 -0
- data/app/styles/application.css +25 -0
- data/app/styles/calculator.css +37 -0
- data/app/styles/collections.css +19 -0
- data/app/styles/home.css +0 -0
- data/app/styles/http.css +24 -0
- data/app/styles/inputs.css +47 -0
- data/app/styles/scrolls.css +67 -0
- data/app/styles/sidebars.css +19 -0
- data/lib/assets/fontawesome-webfont.ttf +0 -0
- data/lib/assets/under-os.css +115 -0
- data/lib/under-os.rb +9 -1
- data/lib/under_os/app.rb +33 -0
- data/lib/under_os/color.rb +176 -0
- data/lib/under_os/config.rb +28 -1
- data/lib/under_os/core/kernel.rb +16 -0
- data/lib/under_os/core/numeric.rb +23 -0
- data/lib/under_os/core/string.rb +40 -0
- data/lib/under_os/delegate.rb +3 -2
- data/lib/under_os/events.rb +85 -0
- data/lib/under_os/file.rb +110 -0
- data/lib/under_os/history.rb +50 -0
- data/lib/under_os/http/receiver.rb +44 -0
- data/lib/under_os/http/request.rb +89 -0
- data/lib/under_os/http/response.rb +24 -0
- data/lib/under_os/http/session.rb +49 -0
- data/lib/under_os/http.rb +5 -0
- data/lib/under_os/page/builder.rb +96 -0
- data/lib/under_os/page/layout.rb +43 -0
- data/lib/under_os/page/matcher.rb +83 -0
- data/lib/under_os/page/stylesheet.rb +67 -0
- data/lib/under_os/page.rb +153 -0
- data/lib/under_os/parser/css.rb +37 -0
- data/lib/under_os/parser/html.rb +97 -0
- data/lib/under_os/parser.rb +24 -0
- data/lib/under_os/point.rb +47 -0
- data/lib/under_os/screen.rb +9 -0
- data/lib/under_os/timer.rb +65 -0
- data/lib/under_os/ui/alert.rb +52 -0
- data/lib/under_os/ui/button.rb +22 -0
- data/lib/under_os/ui/collection/cell.rb +21 -0
- data/lib/under_os/ui/collection/delegate.rb +68 -0
- data/lib/under_os/ui/collection/item.rb +32 -0
- data/lib/under_os/ui/collection/layout.rb +43 -0
- data/lib/under_os/ui/collection/styles.rb +15 -0
- data/lib/under_os/ui/collection.rb +63 -0
- data/lib/under_os/ui/icon/awesome.rb +376 -0
- data/lib/under_os/ui/icon/engine.rb +10 -0
- data/lib/under_os/ui/icon.rb +42 -0
- data/lib/under_os/ui/image.rb +30 -0
- data/lib/under_os/ui/input.rb +41 -0
- data/lib/under_os/ui/label.rb +21 -0
- data/lib/under_os/ui/locker.rb +42 -0
- data/lib/under_os/ui/navbar.rb +115 -0
- data/lib/under_os/ui/progress.rb +17 -0
- data/lib/under_os/ui/scroll.rb +41 -0
- data/lib/under_os/ui/select.rb +98 -0
- data/lib/under_os/ui/sidebar.rb +45 -0
- data/lib/under_os/ui/slider.rb +49 -0
- data/lib/under_os/ui/spinner.rb +23 -0
- data/lib/under_os/ui/style/fonts.rb +60 -0
- data/lib/under_os/ui/style/margins.rb +160 -0
- data/lib/under_os/ui/style/outlining.rb +98 -0
- data/lib/under_os/ui/style/positioning.rb +169 -0
- data/lib/under_os/ui/style.rb +21 -0
- data/lib/under_os/ui/switch.rb +29 -0
- data/lib/under_os/ui/textarea.rb +14 -0
- data/lib/under_os/ui/utils/animation.rb +99 -0
- data/lib/under_os/ui/utils/commons.rb +70 -0
- data/lib/under_os/ui/utils/dimensions.rb +37 -0
- data/lib/under_os/ui/utils/editable.rb +43 -0
- data/lib/under_os/ui/utils/events.rb +75 -0
- data/lib/under_os/ui/utils/manipulation.rb +44 -0
- data/lib/under_os/ui/utils/position.rb +21 -0
- data/lib/under_os/ui/utils/size.rb +21 -0
- data/lib/under_os/ui/utils/styles.rb +89 -0
- data/lib/under_os/ui/utils/traversing.rb +44 -0
- data/lib/under_os/ui/utils/wrap.rb +77 -0
- data/lib/under_os/ui/view.rb +31 -0
- data/lib/under_os/ui.rb +3 -0
- data/lib/under_os.rb +2 -2
- data/resources/Default-568h@2x.png +0 -0
- data/resources/test.png +0 -0
- data/spec/assets/app.css +13 -0
- data/spec/assets/test.css +7 -0
- data/spec/assets/test.html +3 -0
- data/spec/assets/test_page.rb +2 -0
- data/spec/lib/core/numeric_spec.rb +37 -0
- data/spec/lib/core/string_spec.rb +87 -0
- data/spec/lib/under_os/color_spec.rb +133 -0
- data/spec/lib/under_os/events_spec.rb +71 -0
- data/spec/lib/under_os/file_spec.rb +98 -0
- data/spec/lib/under_os/page/builder_spec.rb +128 -0
- data/spec/lib/under_os/page/layout_spec.rb +18 -0
- data/spec/lib/under_os/page/matcher_spec.rb +154 -0
- data/spec/lib/under_os/page/stylesheet_spec.rb +83 -0
- data/spec/lib/under_os/page_spec.rb +5 -0
- data/spec/lib/under_os/parser/css_spec.rb +77 -0
- data/spec/lib/under_os/parser/html_spec.rb +152 -0
- data/spec/lib/under_os/parser_spec.rb +16 -0
- data/spec/lib/under_os/point_spec.rb +54 -0
- data/spec/lib/under_os/screen_spec.rb +11 -0
- data/spec/lib/under_os/timer_spec.rb +93 -0
- data/spec/lib/under_os/ui/button_spec.rb +5 -0
- data/spec/lib/under_os/ui/collection_spec.rb +19 -0
- data/spec/lib/under_os/ui/icon_spec.rb +26 -0
- data/spec/lib/under_os/ui/image_spec.rb +39 -0
- data/spec/lib/under_os/ui/input_spec.rb +77 -0
- data/spec/lib/under_os/ui/label_spec.rb +22 -0
- data/spec/lib/under_os/ui/locker_spec.rb +31 -0
- data/spec/lib/under_os/ui/progress_spec.rb +31 -0
- data/spec/lib/under_os/ui/scroll_spec.rb +40 -0
- data/spec/lib/under_os/ui/select_spec.rb +131 -0
- data/spec/lib/under_os/ui/sidebar_spec.rb +35 -0
- data/spec/lib/under_os/ui/slider_spec.rb +65 -0
- data/spec/lib/under_os/ui/spinner_spec.rb +57 -0
- data/spec/lib/under_os/ui/style/fonts_spec.rb +73 -0
- data/spec/lib/under_os/ui/style/margins_spec.rb +106 -0
- data/spec/lib/under_os/ui/style/outlining_spec.rb +69 -0
- data/spec/lib/under_os/ui/style/positioning_spec.rb +69 -0
- data/spec/lib/under_os/ui/style_spec.rb +19 -0
- data/spec/lib/under_os/ui/switch_spec.rb +56 -0
- data/spec/lib/under_os/ui/textarea_spec.rb +30 -0
- data/spec/lib/under_os/ui/utils/commons_spec.rb +81 -0
- data/spec/lib/under_os/ui/utils/manipulation_spec.rb +130 -0
- data/spec/lib/under_os/ui/utils/styles_spec.rb +140 -0
- data/spec/lib/under_os/ui/utils/traversing_spec.rb +124 -0
- data/spec/lib/under_os/ui/utils/wrap_spec.rb +69 -0
- data/spec/lib/under_os/ui/view_spec.rb +39 -0
- data/under-os.gemspec +32 -0
- metadata +225 -11
- data/lib/under_os/application.rb +0 -16
- data/lib/under_os/window.rb +0 -5
@@ -0,0 +1,110 @@
|
|
1
|
+
class UnderOs::File
|
2
|
+
attr_reader :path, :mode
|
3
|
+
|
4
|
+
def self.open(path, *args, &block)
|
5
|
+
new path, *args, &block
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.read(path, *args)
|
9
|
+
new(path, *args).read
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.write(path, content)
|
13
|
+
new(path, "w"){|f| f.write(content) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.size(path)
|
17
|
+
new(path).size
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.delete(path)
|
21
|
+
NSFileManager.defaultManager.removeItemAtPath(UnderOs::File.path(path), error:nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.path(path)
|
25
|
+
path.to_s[0] == '/' ? path : NSHomeDirectory().stringByAppendingPathComponent(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.tmp(filename, mode="w", &block)
|
29
|
+
new NSTemporaryDirectory().stringByAppendingPathComponent(filename), mode, &block
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.exists?(path, is_dir=nil)
|
33
|
+
NSFileManager.defaultManager.fileExistsAtPath(UnderOs::File.path(path), isDirectory:is_dir)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.file?(path)
|
37
|
+
exists?(path, false)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.dir?(path)
|
41
|
+
exists?(path, true)
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(path, mode="r", &block)
|
45
|
+
@path = UnderOs::File.path(path)
|
46
|
+
@mode = mode
|
47
|
+
|
48
|
+
yield(self) if block_given?
|
49
|
+
end
|
50
|
+
|
51
|
+
def url
|
52
|
+
@url ||= NSURL.fileURLWithPath(@path)
|
53
|
+
end
|
54
|
+
|
55
|
+
def read(size=nil)
|
56
|
+
open
|
57
|
+
data = if size == nil
|
58
|
+
@handle.readDataToEndOfFile
|
59
|
+
else
|
60
|
+
@handle.readDataOfLength(size)
|
61
|
+
end
|
62
|
+
close
|
63
|
+
|
64
|
+
@mode == "b" ? data : NSString.alloc.initWithData(data, encoding:NSUTF8StringEncoding)
|
65
|
+
end
|
66
|
+
|
67
|
+
def write(content)
|
68
|
+
if ! UnderOs::File.exists?(@path)
|
69
|
+
NSFileManager.defaultManager.createFileAtPath @path, contents:nil, attributes:nil
|
70
|
+
end
|
71
|
+
|
72
|
+
content = content.dataUsingEncoding(NSUTF8StringEncoding) if content.is_a?(String)
|
73
|
+
|
74
|
+
open
|
75
|
+
@handle.writeData content
|
76
|
+
close
|
77
|
+
end
|
78
|
+
|
79
|
+
def open
|
80
|
+
@handle = if @mode == "r"
|
81
|
+
NSFileHandle.fileHandleForReadingAtPath(@path)
|
82
|
+
else
|
83
|
+
NSFileHandle.fileHandleForWritingAtPath(@path)
|
84
|
+
end
|
85
|
+
|
86
|
+
@handle.seekToEndOfFile if ["w+", "a"].include?(@mode)
|
87
|
+
end
|
88
|
+
|
89
|
+
def close
|
90
|
+
@handle.closeFile
|
91
|
+
@handle = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def seek(offset)
|
95
|
+
@handle.seekToFileOffset(offset)
|
96
|
+
end
|
97
|
+
|
98
|
+
def rewind
|
99
|
+
seek 0
|
100
|
+
end
|
101
|
+
|
102
|
+
def size
|
103
|
+
NSFileManager.defaultManager.attributesOfItemAtPath(@path, error: nil).fileSize
|
104
|
+
end
|
105
|
+
|
106
|
+
def truncate(size)
|
107
|
+
@handle.truncateFileAtOffset(size)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class UnderOs::History
|
2
|
+
attr_reader :_, :navbar
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@_ = UINavigationController.alloc
|
6
|
+
@navbar = UnderOs::UI::Navbar.new(@_)
|
7
|
+
end
|
8
|
+
|
9
|
+
def root_page
|
10
|
+
@_.topViewController.wrapper
|
11
|
+
end
|
12
|
+
|
13
|
+
def root_page=(page)
|
14
|
+
@_.initWithRootViewController(page._)
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_page
|
18
|
+
@_.visibleViewController.wrapper
|
19
|
+
end
|
20
|
+
|
21
|
+
def push(page, animated=true)
|
22
|
+
if pages.include?(page)
|
23
|
+
pop_to(page)
|
24
|
+
else
|
25
|
+
@_.pushViewController(page._, animated: animated)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :<< :push
|
30
|
+
|
31
|
+
def pop(page=nil, animated=true)
|
32
|
+
if page
|
33
|
+
@_.popToViewController(page._, animated: animated)
|
34
|
+
else
|
35
|
+
@_.popViewControllerAnimated(animated)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def pages
|
40
|
+
@_.viewControllers.map{|c| c.wrapper rescue nil }.compact
|
41
|
+
end
|
42
|
+
|
43
|
+
def pop_to(page, animated=true)
|
44
|
+
pop(page, animated)
|
45
|
+
end
|
46
|
+
|
47
|
+
def pop_to_root(animated=true)
|
48
|
+
@_.popToRootViewControllerAnimated(animated)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#
|
2
|
+
# A little wrap to handle iOS HTTP requests receiving
|
3
|
+
#
|
4
|
+
class UnderOs::HTTP::Request
|
5
|
+
class Receiver
|
6
|
+
def initialize(request, stream=false)
|
7
|
+
@request = request
|
8
|
+
@stream = stream
|
9
|
+
@data = NSMutableData.dataWithCapacity(0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def connection(connection, didReceiveResponse:response)
|
13
|
+
@data.setLength(0)
|
14
|
+
@response = UnderOs::HTTP::Response.new(response, @data)
|
15
|
+
|
16
|
+
emit(:response)
|
17
|
+
end
|
18
|
+
|
19
|
+
def connection(connection, didReceiveData:data)
|
20
|
+
@data.setLength(0) if @stream
|
21
|
+
@data.appendData(data)
|
22
|
+
emit(:data)
|
23
|
+
end
|
24
|
+
|
25
|
+
# def connection(connection, willCacheResponse:cachedResponse)
|
26
|
+
# return nil # don't cache
|
27
|
+
# end
|
28
|
+
|
29
|
+
def connection(connection, didFailWithError:error)
|
30
|
+
@response = UnderOs::HTTP::Response.new(error, @data)
|
31
|
+
|
32
|
+
emit(:failure)
|
33
|
+
end
|
34
|
+
|
35
|
+
def connectionDidFinishLoading(connection)
|
36
|
+
emit(:success)
|
37
|
+
end
|
38
|
+
|
39
|
+
def emit(event)
|
40
|
+
@request.emit(event, response: @response)
|
41
|
+
@request.emit(:complete, response: @response) if event == :failure || event == :success
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
#
|
2
|
+
# Options:
|
3
|
+
#
|
4
|
+
# * :method - GET, POST, ...
|
5
|
+
# * :cookies - Hash of cookie values
|
6
|
+
# * :stream - boolean, in case you want to stream stuff
|
7
|
+
#
|
8
|
+
# Events:
|
9
|
+
#
|
10
|
+
# * :response - when response header is received
|
11
|
+
# * :data - when a chunk of data is received
|
12
|
+
# * :success - when the request is successfully finished
|
13
|
+
# * :failure - when request has failed for whatever reason
|
14
|
+
# * :complete - when it's complete (either way)
|
15
|
+
#
|
16
|
+
class UnderOs::HTTP::Request
|
17
|
+
include UnderOs::Events
|
18
|
+
|
19
|
+
attr_reader :url, :method, :params
|
20
|
+
|
21
|
+
def initialize(url, options={}, &block)
|
22
|
+
@url = url
|
23
|
+
@options = options
|
24
|
+
|
25
|
+
on :complete, &block if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
def on(*args, &block)
|
29
|
+
super *args do |event|
|
30
|
+
args = block.arity == 0 ? [] : [event.params[:response]]
|
31
|
+
block.call *args
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def send
|
36
|
+
@request = build_request
|
37
|
+
@receiver = Receiver.new(self, @options[:stream])
|
38
|
+
@connection = NSURLConnection.alloc.initWithRequest(@request, delegate:@receiver)
|
39
|
+
@connection.start
|
40
|
+
end
|
41
|
+
|
42
|
+
def cancel
|
43
|
+
@connection.cancel
|
44
|
+
end
|
45
|
+
|
46
|
+
def method
|
47
|
+
method = @options[:method] || :get
|
48
|
+
method = method.to_s.upcase
|
49
|
+
method = 'GET' unless %w[GET POST PUT DELETE PATCH HEAD].include?(method)
|
50
|
+
method
|
51
|
+
end
|
52
|
+
|
53
|
+
def headers
|
54
|
+
@options[:headers] || {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def cookies
|
58
|
+
@options[:cookies] || {}
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def build_request
|
64
|
+
url = NSURL.URLWithString(NSString.stringWithFormat(@url))
|
65
|
+
NSMutableURLRequest.requestWithURL(url).tap do |request|
|
66
|
+
request.setHTTPMethod self.method
|
67
|
+
#request.setHTTPBody self.params
|
68
|
+
|
69
|
+
cookies = self.cookies.map do |key, value|
|
70
|
+
NSHTTPCookie.cookieWithProperties({
|
71
|
+
NSHTTPCookieDomain => url.host,
|
72
|
+
NSHTTPCookiePath => "/",
|
73
|
+
NSHTTPCookieName => key.to_s,
|
74
|
+
NSHTTPCookieValue => value.to_s
|
75
|
+
})
|
76
|
+
end
|
77
|
+
|
78
|
+
request.setAllHTTPHeaderFields NSHTTPCookie.requestHeaderFieldsWithCookies(cookies) if cookies.size > 0
|
79
|
+
|
80
|
+
headers.each do |key, value|
|
81
|
+
request.addValue value, forHTTPHeaderField:key
|
82
|
+
end
|
83
|
+
|
84
|
+
# [newRequest addValue:@"application/x-www-form-urlencoded;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
|
85
|
+
# [newRequest setHTTPBody:[query dataUsingEncoding:NSUTF8StringEncoding]];
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class UnderOs::HTTP::Response
|
2
|
+
attr_reader :data
|
3
|
+
|
4
|
+
def initialize(response, data)
|
5
|
+
@response = response
|
6
|
+
@data = data # raw NSData
|
7
|
+
end
|
8
|
+
|
9
|
+
def headers
|
10
|
+
@response.allHeaderFields
|
11
|
+
end
|
12
|
+
|
13
|
+
def body
|
14
|
+
@body ||= @data.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_length
|
18
|
+
headers["Content-Length"].to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def content_type
|
22
|
+
headers["Content-Type"]
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#
|
2
|
+
# Basically keeps track of cookies between the requests
|
3
|
+
#
|
4
|
+
class Session
|
5
|
+
def initialize(default_options={})
|
6
|
+
@options = default_options
|
7
|
+
@options[:cookies] ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def cookies
|
11
|
+
@options[:cookies]
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(url, options={}, &block)
|
15
|
+
request(url, {method: 'GET'}.merge(options), &block).send
|
16
|
+
end
|
17
|
+
|
18
|
+
def post(url, options={}, &block)
|
19
|
+
request(url, {method: 'POST'}.merge(options), &block).send
|
20
|
+
end
|
21
|
+
|
22
|
+
def put(url, options={}, &block)
|
23
|
+
request(url, {method: 'PUT'}.merge(options), &block).send
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(url, options={}, &block)
|
27
|
+
request(url, {method: 'DELETE'}.merge(options), &block).send
|
28
|
+
end
|
29
|
+
|
30
|
+
def request(url, options={}, &block)
|
31
|
+
options = options.merge(@options){|k,a,b| a.merge(b) }
|
32
|
+
|
33
|
+
UnderOs::HTTP::Request.new(url, options, &block).tap do |request|
|
34
|
+
request.on :response do |response|
|
35
|
+
save_cookies response.headers["Set-Cookie"] || ''
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def save_cookies(cookies)
|
43
|
+
cookies.scan(/([a-z_]+?)=([^; ]+)/).each do |key, value|
|
44
|
+
unless ['path', 'domain', 'expires'].include?(key)
|
45
|
+
@options[:cookies][key] = value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
class UnderOs::Page::Builder
|
2
|
+
|
3
|
+
def self.views_from(source)
|
4
|
+
@inst ||= new
|
5
|
+
if source.is_a?(String)
|
6
|
+
@inst.from_html(source)
|
7
|
+
else
|
8
|
+
@inst.from_nodes(source)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_html(source)
|
13
|
+
@html ||= UnderOs::Parser::HTML.new
|
14
|
+
from_nodes @html.parse(source)
|
15
|
+
end
|
16
|
+
|
17
|
+
def from_nodes(list)
|
18
|
+
list.map do |node|
|
19
|
+
build_view_from(node)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_view_from(node)
|
24
|
+
klass = class_for(node)
|
25
|
+
options = options_from_attrs(node[:attrs] || {})
|
26
|
+
|
27
|
+
if klass.instance_methods.include?(:text) && node[:text]
|
28
|
+
options[:text] = node[:text]
|
29
|
+
end
|
30
|
+
|
31
|
+
klass.new(options).tap do |view|
|
32
|
+
if node[:children] && node[:children].any?
|
33
|
+
build_children_for(view, node[:children])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def class_for(node)
|
39
|
+
if node[:attrs] && node[:attrs][:wrapper].is_a?(String)
|
40
|
+
node[:attrs].delete(:wrapper).constantize
|
41
|
+
else
|
42
|
+
UnderOs::UI::Wrap::WRAPS_TAGS_MAP[node[:tag]] || UnderOs::UI::View
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def options_from_attrs(hash)
|
47
|
+
{}.tap do |options|
|
48
|
+
hash.each do |key, value|
|
49
|
+
options[key] = value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_children_for(view, nodes)
|
55
|
+
if view.is_a?(UnderOs::UI::Select)
|
56
|
+
view.optgroups = select_optgroups_from(nodes)
|
57
|
+
elsif view.is_a?(UnderOs::UI::Collection)
|
58
|
+
find_collection_items_for(view, nodes)
|
59
|
+
else
|
60
|
+
from_nodes(nodes).each do |child|
|
61
|
+
view.insert child
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def select_optgroups_from(nodes)
|
67
|
+
nodes = [{tag: 'optgroup', children: nodes}] if nodes[0][:tag] == 'option'
|
68
|
+
|
69
|
+
nodes.map do |group|
|
70
|
+
{}.tap do |options|
|
71
|
+
group[:children].each do |option|
|
72
|
+
if option[:tag] == 'option'
|
73
|
+
label = option[:text]
|
74
|
+
value = option[:attrs][:value] || label
|
75
|
+
|
76
|
+
options[value] = label if value && label
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_collection_items_for(collection, nodes)
|
84
|
+
nodes.each do |node|
|
85
|
+
klass = class_for(node)
|
86
|
+
|
87
|
+
if klass <= UnderOs::UI::Collection::Item
|
88
|
+
klass.build(collection) { from_nodes(node[:children]) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# header
|
92
|
+
# footer
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class UnderOs::Page::Layout
|
2
|
+
|
3
|
+
def initialize(page)
|
4
|
+
@page = page
|
5
|
+
|
6
|
+
build_layout_from_xib || build_layout_from_html || build_layout_from_nothing
|
7
|
+
|
8
|
+
@page.view = UnderOs::UI::View.new(@page._.view)
|
9
|
+
@page.view.instance_variable_set('@_tag_name', 'PAGE')
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_layout_from_xib
|
13
|
+
if filename = find_layout_with_type('xib')
|
14
|
+
@page._.view = NSBundle.mainBundle.loadNibNamed(filename.sub(/\.xib$/, ''), owner:self, options:nil)[0]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_layout_from_html
|
19
|
+
if filename = find_layout_with_type('html')
|
20
|
+
if layout = UnderOs::Parser.parse(filename)
|
21
|
+
if view = UnderOs::Page::Builder.views_from(layout)[0]
|
22
|
+
@page._.view = view._
|
23
|
+
@page.title = layout[0][:attrs][:title] || ''
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_layout_from_nothing
|
30
|
+
@page._.view = UIView.alloc.init
|
31
|
+
@page._.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_layout_with_type(ext)
|
35
|
+
filename = @page.class.layout || "#{@page.name}.#{ext}"
|
36
|
+
filename, type = filename.match(/^(.+?)\.([a-z]+)$/).to_a.slice(1,2)
|
37
|
+
|
38
|
+
if type == ext && NSBundle.mainBundle.pathForResource(filename, ofType:type)
|
39
|
+
"#{filename}.#{type}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class UnderOs::Page::StylesMatcher
|
2
|
+
|
3
|
+
def self.new(css_rule)
|
4
|
+
@cache ||= {}
|
5
|
+
@cache[css_rule] ||= super
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(css_rule)
|
9
|
+
css_rule = css_rule.strip
|
10
|
+
rules = css_rule.scan(/([\S]+)/).map(&:first)
|
11
|
+
@rule = parse(rules.pop)
|
12
|
+
@parent = rules.size == 0 ? false : self.class.new(rules.join(' '))
|
13
|
+
end
|
14
|
+
|
15
|
+
def match(view)
|
16
|
+
score_for(view) != 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def score_for(view)
|
20
|
+
id_score = id_score_for(view)
|
21
|
+
tag_score = tag_score_for(view)
|
22
|
+
class_score = class_score_for(view)
|
23
|
+
|
24
|
+
return 0 if @rule[:id] && id_score == 0
|
25
|
+
return 0 if @rule[:tag] && tag_score == 0
|
26
|
+
return 0 if @rule[:classes].size > 0 && class_score == 0
|
27
|
+
return 0 if (this_score = id_score + tag_score + class_score) == 0
|
28
|
+
|
29
|
+
parent_score = parent_score_for(view)
|
30
|
+
|
31
|
+
return 0 if @parent && parent_score == 0
|
32
|
+
|
33
|
+
parent_score + this_score
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def id_score_for(view)
|
39
|
+
@rule[:id] == view.id ? 1 : 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def tag_score_for(view)
|
43
|
+
@rule[:tag] == view.tagName ? 1 : 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def class_score_for(view)
|
47
|
+
match = @rule[:classes] & view.classNames
|
48
|
+
match.size == @rule[:classes].size ? match.size : 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def parent_score_for(view)
|
52
|
+
return 0 if ! @parent
|
53
|
+
|
54
|
+
parent = view; score = 0
|
55
|
+
|
56
|
+
while parent = parent.parent
|
57
|
+
score = @parent.score_for(parent)
|
58
|
+
break if score > 0
|
59
|
+
end
|
60
|
+
|
61
|
+
score
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse(string)
|
65
|
+
{}.tap do |rule|
|
66
|
+
if m = string.match(/^([a-z]+)/)
|
67
|
+
rule[:tag] = m[1].upcase
|
68
|
+
else
|
69
|
+
rule[:tag] = false # so it wouldn't match nil
|
70
|
+
end
|
71
|
+
|
72
|
+
if m = string.match(/#([a-z_\-0-9]+)/)
|
73
|
+
rule[:id] = m[1]
|
74
|
+
else
|
75
|
+
rule[:id] = false # so it wouldn't match nil
|
76
|
+
end
|
77
|
+
|
78
|
+
if m = string.scan(/(\.)([a-z_\-0-9]+)/)
|
79
|
+
rule[:classes] = m.map{|c| c[1]}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class UnderOs::Page::Stylesheet
|
2
|
+
attr_reader :rules
|
3
|
+
|
4
|
+
def initialize(styles={})
|
5
|
+
@rules = {}
|
6
|
+
|
7
|
+
styles.each do |rule, style|
|
8
|
+
self[rule] = style
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def styles_for(view)
|
13
|
+
{}.tap do |styles|
|
14
|
+
weighted_styles_for(view).each do |hash|
|
15
|
+
hash.each do |key, value|
|
16
|
+
styles[key] = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](rule)
|
23
|
+
@rules[rule.to_s]
|
24
|
+
end
|
25
|
+
|
26
|
+
def []=(rule, values)
|
27
|
+
@rules[rule.to_s] ||= {}
|
28
|
+
@rules[rule.to_s].merge! values
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(another_stylesheet)
|
32
|
+
another_stylesheet.rules.each do |rule, styles|
|
33
|
+
self[rule] = styles
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def +(another_stylesheet)
|
38
|
+
self.class.new.tap do |combined_sheet|
|
39
|
+
combined_sheet << self
|
40
|
+
combined_sheet << another_stylesheet
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def load(filename)
|
45
|
+
UnderOs::Parser.parse(filename).each do |rule, styles|
|
46
|
+
self[rule] = styles
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def weighted_styles_for(view)
|
53
|
+
find_styles_for(view).
|
54
|
+
sort{|a,b| a[:score] <=> b[:score]}.
|
55
|
+
map{|e| e[:style]}
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_styles_for(view)
|
59
|
+
[].tap do |styles|
|
60
|
+
@rules.each do |css, rule|
|
61
|
+
score = UnderOs::Page::StylesMatcher.new(css).score_for(view)
|
62
|
+
styles << {score: score, style: rule} if score != 0
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|