lissio 0.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +53 -0
- data/README.md +184 -0
- data/Rakefile +5 -0
- data/lib/lissio.rb +5 -0
- data/lib/lissio/server.rb +148 -0
- data/lissio.gemspec +27 -0
- data/opal/lissio.rb +26 -0
- data/opal/lissio/adapter.rb +45 -0
- data/opal/lissio/adapter/rest.rb +268 -0
- data/opal/lissio/adapter/storage.rb +167 -0
- data/opal/lissio/application.rb +66 -0
- data/opal/lissio/collection.rb +70 -0
- data/opal/lissio/component.rb +177 -0
- data/opal/lissio/component/alert.rb +110 -0
- data/opal/lissio/component/container.rb +56 -0
- data/opal/lissio/component/markdown.rb +332 -0
- data/opal/lissio/component/tooltip.rb +373 -0
- data/opal/lissio/model.rb +204 -0
- data/opal/lissio/router.rb +164 -0
- data/opal/lissio/version.rb +3 -0
- data/spec/route_spec.rb +65 -0
- data/spec/router_spec.rb +109 -0
- data/spec/spec_helper.rb +33 -0
- metadata +154 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Lissio
|
5
|
+
|
6
|
+
class Application < Component
|
7
|
+
def self.inherited(klass)
|
8
|
+
super
|
9
|
+
|
10
|
+
klass.include Singleton
|
11
|
+
|
12
|
+
$document.on :load do
|
13
|
+
klass.start
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.expose(what, options = {})
|
18
|
+
if what.start_with?(?@)
|
19
|
+
name = what[1 .. -1]
|
20
|
+
|
21
|
+
define_singleton_method name do
|
22
|
+
instance.__send__ name
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader name
|
26
|
+
else
|
27
|
+
define_singleton_method what do |*args, &block|
|
28
|
+
instance.__send__ what, *args, &block
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
expose :start
|
34
|
+
expose :refresh
|
35
|
+
expose :navigate
|
36
|
+
expose :@router
|
37
|
+
|
38
|
+
extend Forwardable
|
39
|
+
def_delegators :@router, :navigate, :route
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@router = Lissio::Router.new(fragment: false)
|
43
|
+
end
|
44
|
+
|
45
|
+
def start
|
46
|
+
render
|
47
|
+
|
48
|
+
@router.update
|
49
|
+
end
|
50
|
+
|
51
|
+
def refresh
|
52
|
+
@router.update
|
53
|
+
end
|
54
|
+
|
55
|
+
element :body
|
56
|
+
|
57
|
+
on :click, 'a[href^="/"]' do |e|
|
58
|
+
unless e.alt? || e.ctrl? || e.meta? || e.shift?
|
59
|
+
e.stop!
|
60
|
+
|
61
|
+
navigate e.target[:href] || e.target.ancestors('a').first[:href]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'forwardable'
|
12
|
+
|
13
|
+
module Lissio
|
14
|
+
|
15
|
+
class Collection
|
16
|
+
def self.adapter(klass = nil, *args, &block)
|
17
|
+
if klass
|
18
|
+
@adapter.uninstall if @adapter
|
19
|
+
|
20
|
+
@adapter = klass.new(self, *args, &block)
|
21
|
+
@adapter.install
|
22
|
+
else
|
23
|
+
@adapter
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.model(klass = nil)
|
28
|
+
klass ? @model = klass : @model
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse(&block)
|
32
|
+
block ? @parse = block : @parse
|
33
|
+
end
|
34
|
+
|
35
|
+
extend Forwardable
|
36
|
+
def_delegators :class, :adapter, :model
|
37
|
+
def_delegators :@items, :empty?, :length, :[], :to_a
|
38
|
+
|
39
|
+
def initialize(data = nil, *fetched_with)
|
40
|
+
@fetched_with = fetched_with
|
41
|
+
|
42
|
+
if data
|
43
|
+
@items = data.map {|datum|
|
44
|
+
next datum if Model === datum
|
45
|
+
|
46
|
+
if block = self.class.parse
|
47
|
+
block.call(datum)
|
48
|
+
else
|
49
|
+
model.new(datum)
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
include Enumerable
|
56
|
+
|
57
|
+
def each(&block)
|
58
|
+
return enum_for :each unless block
|
59
|
+
|
60
|
+
@items.each(&block)
|
61
|
+
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
"#<#{self.class.name}: #{@items.inspect}>"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module Lissio
|
12
|
+
|
13
|
+
class Component
|
14
|
+
def self.inherited(klass)
|
15
|
+
return if self == Component
|
16
|
+
|
17
|
+
element = @element
|
18
|
+
tag = @tag
|
19
|
+
events = @events
|
20
|
+
|
21
|
+
klass.instance_eval {
|
22
|
+
@element = element if element
|
23
|
+
@tag = tag if tag
|
24
|
+
@events = events.clone if events
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.element(name = nil)
|
29
|
+
name ? @element = name : @element
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.tag(options = nil)
|
33
|
+
options ? @tag = options : @tag
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.events
|
37
|
+
@events ||= Hash.new { |h, k| h[k] = [] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.on(name, selector = nil, method = nil, &block)
|
41
|
+
if block
|
42
|
+
events[name] << [selector, block]
|
43
|
+
|
44
|
+
[name, selector, block]
|
45
|
+
elsif method
|
46
|
+
events[name] << [selector, method]
|
47
|
+
|
48
|
+
[name, selector, method]
|
49
|
+
else
|
50
|
+
events[name] << [nil, method]
|
51
|
+
|
52
|
+
[name, nil, method]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.off(id)
|
57
|
+
name, selector, block = id
|
58
|
+
|
59
|
+
events[name].delete([selector, block])
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.render(&block)
|
63
|
+
define_method :render do
|
64
|
+
instance_exec(&block)
|
65
|
+
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.html(string = nil, &block)
|
71
|
+
if block
|
72
|
+
render {
|
73
|
+
if block.arity == 1
|
74
|
+
element.inner_dom { |d|
|
75
|
+
instance_exec(d, &block)
|
76
|
+
}
|
77
|
+
else
|
78
|
+
element.inner_dom(&block)
|
79
|
+
end
|
80
|
+
}
|
81
|
+
else
|
82
|
+
render {
|
83
|
+
element.inner_html = string
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.css(content = nil, &block)
|
89
|
+
if content || block
|
90
|
+
@style.remove if @style
|
91
|
+
|
92
|
+
@style = CSS(content, &block)
|
93
|
+
@style.append_to($document.head)
|
94
|
+
else
|
95
|
+
CSS::StyleSheet.new(@style)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
attr_accessor :parent
|
100
|
+
|
101
|
+
def initialize(parent = nil)
|
102
|
+
@parent = parent
|
103
|
+
end
|
104
|
+
|
105
|
+
def tag
|
106
|
+
{ name: :div }.merge(self.class.tag || {})
|
107
|
+
end
|
108
|
+
|
109
|
+
def element
|
110
|
+
return @element if @element
|
111
|
+
|
112
|
+
scope = parent ? parent.element : $document
|
113
|
+
elem = if elem = self.class.element
|
114
|
+
scope.at(elem)
|
115
|
+
else
|
116
|
+
DOM::Element.create tag[:name]
|
117
|
+
end
|
118
|
+
|
119
|
+
unless elem
|
120
|
+
raise ArgumentError, 'element not found'
|
121
|
+
end
|
122
|
+
|
123
|
+
elem.add_class(*tag[:class]) if tag[:class]
|
124
|
+
elem[:id] = tag[:id] if tag[:id]
|
125
|
+
|
126
|
+
self.class.events.each {|name, blocks|
|
127
|
+
blocks.each {|selector, block|
|
128
|
+
if block.is_a? Symbol
|
129
|
+
elem.on(name, selector, &method(block))
|
130
|
+
else
|
131
|
+
elem.on(name, selector) {|*args|
|
132
|
+
instance_exec(*args, &block)
|
133
|
+
}
|
134
|
+
end
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
@element = elem
|
139
|
+
end
|
140
|
+
|
141
|
+
def on(name, selector = nil, method = nil, &block)
|
142
|
+
self.class.on(name, selector, method, &block)
|
143
|
+
|
144
|
+
if @element
|
145
|
+
if block
|
146
|
+
@element.on(name, selector) {|*args|
|
147
|
+
instance_exec(*args, &block)
|
148
|
+
}
|
149
|
+
elsif method
|
150
|
+
@element.on(name, selector, &method(method))
|
151
|
+
else
|
152
|
+
@element.on(name, &method(method))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# When overriding, remember to call super as last.
|
160
|
+
def render(*)
|
161
|
+
element.trigger :render, self
|
162
|
+
element
|
163
|
+
end
|
164
|
+
|
165
|
+
def remove
|
166
|
+
@element.remove if @element
|
167
|
+
end
|
168
|
+
|
169
|
+
alias destroy remove
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
Browser::DOM::Builder.for Component do |_, item|
|
174
|
+
item.render
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Lissio; class Component
|
2
|
+
|
3
|
+
class Alert < Component
|
4
|
+
def self.new!(message, options = {})
|
5
|
+
new(message, options.merge({ escape: false }))
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :message, :options
|
9
|
+
|
10
|
+
def initialize(message, options = {})
|
11
|
+
@message = message
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
if @options[:escape] == false
|
17
|
+
element.inner_html = @message
|
18
|
+
else
|
19
|
+
element << @message
|
20
|
+
end
|
21
|
+
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
tag class: :alert
|
26
|
+
|
27
|
+
css do
|
28
|
+
rule '.alert' do
|
29
|
+
border 1.px, :solid, :transparent
|
30
|
+
|
31
|
+
padding 15.px
|
32
|
+
|
33
|
+
rule 'a' do
|
34
|
+
font weight: :bold
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.customize(*args, &block)
|
40
|
+
if args.length == 1
|
41
|
+
options = args.first
|
42
|
+
else
|
43
|
+
name, options = args
|
44
|
+
end
|
45
|
+
|
46
|
+
name ||= "alert-custom-#{rand(10000)}"
|
47
|
+
options ||= {}
|
48
|
+
|
49
|
+
if self == Alert
|
50
|
+
inherited = []
|
51
|
+
else
|
52
|
+
inherited = class_names
|
53
|
+
end
|
54
|
+
|
55
|
+
Class.new(self) {
|
56
|
+
define_singleton_method :class_names do
|
57
|
+
inherited + [name]
|
58
|
+
end
|
59
|
+
|
60
|
+
tag class: [:alert, name, *inherited]
|
61
|
+
|
62
|
+
css do
|
63
|
+
rule ".alert#{".#{inherited.join('.')}" unless inherited.empty?}.#{name}" do
|
64
|
+
instance_exec(&block) if block
|
65
|
+
|
66
|
+
if value = options[:background] || options[:bg]
|
67
|
+
background color: value
|
68
|
+
end
|
69
|
+
|
70
|
+
if value = options[:foreground] || options[:fg]
|
71
|
+
color value
|
72
|
+
end
|
73
|
+
|
74
|
+
if value = options[:border]
|
75
|
+
border color: value
|
76
|
+
end
|
77
|
+
|
78
|
+
if value = options[:padding]
|
79
|
+
padding value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
Info = customize :info,
|
87
|
+
background: '#d9edf7',
|
88
|
+
foreground: '#3a87ad',
|
89
|
+
border: '#bce8f1'
|
90
|
+
|
91
|
+
Success = customize :success,
|
92
|
+
message: "The operation was successful.",
|
93
|
+
background: '#dff0d8',
|
94
|
+
foreground: '#468847',
|
95
|
+
border: '#d6e9c6'
|
96
|
+
|
97
|
+
Warning = customize :warning,
|
98
|
+
message: "Something might have gone wrong.",
|
99
|
+
background: '#fcf8e3',
|
100
|
+
foreground: '#c09853',
|
101
|
+
border: '#fbeed5'
|
102
|
+
|
103
|
+
Danger = customize :danger,
|
104
|
+
message: "An unexpected error has occurred.",
|
105
|
+
background: '#f2dede',
|
106
|
+
foreground: '#b94a48',
|
107
|
+
border: '#eed3d7'
|
108
|
+
end
|
109
|
+
|
110
|
+
end; end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Lissio; class Component
|
2
|
+
|
3
|
+
class Container < Component
|
4
|
+
class Definer
|
5
|
+
def initialize(&block)
|
6
|
+
@list = []
|
7
|
+
|
8
|
+
if block.arity == 0
|
9
|
+
instance_exec(&block)
|
10
|
+
else
|
11
|
+
block.call(self)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def render(what)
|
16
|
+
@list << what
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_a
|
20
|
+
@list
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(parent, &block)
|
25
|
+
super(parent)
|
26
|
+
|
27
|
+
if block
|
28
|
+
@content = Definer.new(&block).to_a
|
29
|
+
else
|
30
|
+
@content = []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def render(*content, &block)
|
35
|
+
content = @content.dup if content.empty?
|
36
|
+
content.compact! # FIXME: when it's fixed
|
37
|
+
|
38
|
+
element.clear
|
39
|
+
|
40
|
+
content.each {|c|
|
41
|
+
if String === c
|
42
|
+
element << c
|
43
|
+
else
|
44
|
+
element << c.render
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
if block
|
49
|
+
element << DOM(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end; end
|