opal-ferro 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/lib/opal/ferro.rb +1 -0
- data/lib/opal-ferro/version.rb +5 -0
- data/lib/opal-ferro.rb +3 -0
- data/opal/opal-ferro/ferro_base_elements.js.rb +158 -0
- data/opal/opal-ferro/ferro_components.js.rb +92 -0
- data/opal/opal-ferro/ferro_document.js.rb +36 -0
- data/opal/opal-ferro/ferro_element.js.rb +110 -0
- data/opal/opal-ferro/ferro_elementary.js.rb +79 -0
- data/opal/opal-ferro/ferro_factory.js.rb +58 -0
- data/opal/opal-ferro/ferro_form_elements.js.rb +160 -0
- data/opal/opal-ferro/ferro_router.js.rb +119 -0
- data/opal/opal-ferro/ferro_sequence.js.rb +12 -0
- data/opal/opal-ferro/ferro_xhr.js.rb +82 -0
- data/opal/opal-ferro.rb +11 -0
- data/opal-ferro.gemspec +27 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f56567922f613ccb4b5dc4bf3dae7ea52c67f24b
|
4
|
+
data.tar.gz: 9214aa5989306aacd55e435d43161ba60ac26a30
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 66c1f60432258923680daddeef451abecfae6cf9b77d94877f088cd574f8d59385c4210cbe073614e1164b65c0e676c64de8646d6f1d9c9ce3069a4b454d54a1
|
7
|
+
data.tar.gz: d82fc2be389ff2954bf55f086ffc1f3bef427017c6dd534d0d35b63425dd702673617290a8339f6fe874367a5aa84b4142ffeb2d49f84c08707a7ae20aa6a8f2
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at info@edwhs.nl. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017-2018 Ivo Herweijer
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Opal-Ferro
|
2
|
+
Ferro is a small Ruby library on top of [Opal](http://opalrb.com/)
|
3
|
+
that enables an object-oriented programming style for creating code
|
4
|
+
that runs in the webbrowser.
|
5
|
+
No more distractions like HTML and searching for DOM elements,
|
6
|
+
just beautiful and simple Ruby code. Front-End-Ruby-ROcks!
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
``` ruby
|
12
|
+
gem 'opal-ferro'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
bundle
|
18
|
+
|
19
|
+
Or install it yourself:
|
20
|
+
|
21
|
+
gem install opal-ferro
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
Please see the [Ferro website](https://easydatawarehousing.github.io/ferro/)
|
25
|
+
for background information and examples.
|
26
|
+
|
27
|
+
## Development
|
28
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
29
|
+
To release a new version, update the version number in `version.rb`
|
30
|
+
and then run `bundle exec rake release`, which will create a git tag for
|
31
|
+
the version, push git commits and tags, and push the `.gem` file to
|
32
|
+
[rubygems.org](https://rubygems.org).
|
33
|
+
|
34
|
+
## Contributing
|
35
|
+
Bug reports and pull requests are welcome on GitHub at
|
36
|
+
https://github.com/easydatawarehousing/opal-ferro.
|
37
|
+
This project is intended to be a safe, welcoming space for collaboration
|
38
|
+
and contributors are expected to adhere to the
|
39
|
+
[Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
40
|
+
|
41
|
+
## License
|
42
|
+
The gem is available as open source under the terms of the MIT License.
|
43
|
+
See LICENSE.txt
|
data/Rakefile
ADDED
data/lib/opal/ferro.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'opal-ferro'
|
data/lib/opal-ferro.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
class FerroElementComponent < FerroElement
|
2
|
+
def component
|
3
|
+
self
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
# Semantical elements
|
8
|
+
class FerroElementHeader < FerroElementComponent
|
9
|
+
def _before_create
|
10
|
+
@domtype = :header
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class FerroElementNavigation < FerroElementComponent
|
15
|
+
def _before_create
|
16
|
+
@domtype = :nav
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class FerroElementSection < FerroElementComponent
|
21
|
+
def _before_create
|
22
|
+
@domtype = :section
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class FerroElementArticle < FerroElementComponent
|
27
|
+
def _before_create
|
28
|
+
@domtype = :article
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class FerroElementAside < FerroElementComponent
|
33
|
+
def _before_create
|
34
|
+
@domtype = :aside
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class FerroElementFooter < FerroElementComponent
|
39
|
+
def _before_create
|
40
|
+
@domtype = :footer
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
FerroElementBlock = FerroElement
|
45
|
+
|
46
|
+
# Inline elements
|
47
|
+
class FerroElementText < FerroElement
|
48
|
+
def _before_create
|
49
|
+
@size = option_replace :size, 0
|
50
|
+
@domtype = @size == 0 ? :p : "h#{@size}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class FerroElementList < FerroElement
|
55
|
+
def _before_create
|
56
|
+
@domtype = @options.has_key?(:type) ? :ol : :ul
|
57
|
+
@items = []
|
58
|
+
@id = FerroSequence.new 'list_'
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_item(element_class, options = {})
|
62
|
+
@items << add_child(@id.next, element_class, options).sym
|
63
|
+
end
|
64
|
+
|
65
|
+
def item_count
|
66
|
+
@items.length
|
67
|
+
end
|
68
|
+
|
69
|
+
def first_item
|
70
|
+
@children[@items.first]
|
71
|
+
end
|
72
|
+
|
73
|
+
def last_item
|
74
|
+
@children[@items.last]
|
75
|
+
end
|
76
|
+
|
77
|
+
def unlist_item(sym)
|
78
|
+
@items.delete_if { |item| item == sym }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class FerroElementListItem < FerroElement
|
83
|
+
def _before_create
|
84
|
+
@domtype = :li
|
85
|
+
end
|
86
|
+
|
87
|
+
def destroy
|
88
|
+
super
|
89
|
+
parent.unlist_item(@sym)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class FerroElementAnchor < FerroElement
|
94
|
+
def _before_create
|
95
|
+
@domtype = :a
|
96
|
+
@href = @options[:href]
|
97
|
+
end
|
98
|
+
|
99
|
+
def _after_create
|
100
|
+
`#{@element}.addEventListener("click",function(e){e.preventDefault();history.pushState(null,null,#{@href});#{clicked};document.activeElement.blur();})`
|
101
|
+
end
|
102
|
+
|
103
|
+
def update_href(value)
|
104
|
+
@href = value
|
105
|
+
set_attribute('href', @href)
|
106
|
+
end
|
107
|
+
|
108
|
+
def clicked
|
109
|
+
router.navigated
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class FerroElementExternalLink < FerroElement
|
114
|
+
def _before_create
|
115
|
+
@domtype = :a
|
116
|
+
@options[:target] ||= '_blank'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Miscellaneous elements
|
121
|
+
class FerroElementImage < FerroElement
|
122
|
+
|
123
|
+
# Todo use:
|
124
|
+
# <figure>
|
125
|
+
# <figcaption>
|
126
|
+
|
127
|
+
def _before_create
|
128
|
+
@domtype = :img
|
129
|
+
@options[:alt] ||= @options[:src]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class FerroElementCanvas < FerroElement
|
134
|
+
def _before_create
|
135
|
+
@domtype = :canvas
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class FerroElementScript < FerroElement
|
140
|
+
def _before_create
|
141
|
+
@domtype = :script
|
142
|
+
@run = option_replace :invoke, true
|
143
|
+
end
|
144
|
+
|
145
|
+
def _after_create
|
146
|
+
load
|
147
|
+
invoke if @run
|
148
|
+
end
|
149
|
+
|
150
|
+
def load;end
|
151
|
+
def invoke;end
|
152
|
+
end
|
153
|
+
|
154
|
+
class FerroElementVar < FerroElement
|
155
|
+
def _before_create
|
156
|
+
@domtype = option_replace :domtype, :div
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# A component with a text entry field and submit button
|
2
|
+
class FerroFormSearch < FerroForm
|
3
|
+
|
4
|
+
def _before_create
|
5
|
+
@button_text = option_replace :button_text, ' '
|
6
|
+
@placeholder = option_replace :placeholder, ' Search...'
|
7
|
+
end
|
8
|
+
|
9
|
+
def cascade
|
10
|
+
add_child :entry, FerroFormSearchInput, { placeholder: @placeholder }
|
11
|
+
add_child :submit, FerroFormSearchSubmit, { content: @button_text }
|
12
|
+
end
|
13
|
+
|
14
|
+
def do_submit
|
15
|
+
value = entry.value.strip
|
16
|
+
submitted(value) if !value.empty?
|
17
|
+
|
18
|
+
entry.toggle_state :search_input_open
|
19
|
+
submit.toggle_state :search_submit_open
|
20
|
+
|
21
|
+
entry.value = nil
|
22
|
+
entry.set_focus if entry.state_active?(:search_input_open)
|
23
|
+
end
|
24
|
+
|
25
|
+
def submitted(value);end
|
26
|
+
end
|
27
|
+
|
28
|
+
class FerroFormSearchInput < FerroFormInput
|
29
|
+
|
30
|
+
def _after_create
|
31
|
+
add_state :search_input_open
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def entered
|
36
|
+
parent.do_submit
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class FerroFormSearchSubmit < FerroFormButton
|
41
|
+
|
42
|
+
def _after_create
|
43
|
+
add_state :search_submit_open
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
def clicked
|
48
|
+
parent.do_submit
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# A simple pull-down menu
|
53
|
+
# @option[items]: a list of classes
|
54
|
+
class FerroPullDown < FerroElementBlock
|
55
|
+
|
56
|
+
def _before_create
|
57
|
+
@title_text = option_replace :title, '='
|
58
|
+
@items = option_replace :items, []
|
59
|
+
super
|
60
|
+
end
|
61
|
+
|
62
|
+
def _after_create
|
63
|
+
add_state :pull_down_open
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
67
|
+
def cascade
|
68
|
+
add_child :title, FerroPullDownTitle, { content: @title_text }
|
69
|
+
add_child :items, FerroPullDownItems, { items: @items }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class FerroPullDownTitle < FerroFormBlock
|
74
|
+
def clicked
|
75
|
+
parent.toggle_state :pull_down_open
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class FerroPullDownItems < FerroElementBlock
|
80
|
+
|
81
|
+
def _before_create
|
82
|
+
@id = FerroSequence.new 'pdi_'
|
83
|
+
@itemlist = option_replace :items, []
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def cascade
|
88
|
+
@items = @itemlist.map do |item|
|
89
|
+
add_child @id.next, item
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class FerroDocument
|
2
|
+
|
3
|
+
include FerroElementary
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@children = {}
|
7
|
+
creation
|
8
|
+
# router.navigated
|
9
|
+
end
|
10
|
+
|
11
|
+
def parent
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def element
|
16
|
+
@factory.body
|
17
|
+
end
|
18
|
+
|
19
|
+
def factory
|
20
|
+
@factory ||= FerroFactory.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def root
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def component
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def router
|
32
|
+
@router ||= FerroRouter.new(method(:page404))
|
33
|
+
end
|
34
|
+
|
35
|
+
def page404(pathname);end
|
36
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
class FerroElement
|
2
|
+
|
3
|
+
include FerroElementary
|
4
|
+
|
5
|
+
attr_reader :parent, :sym, :children, :element, :domtype
|
6
|
+
|
7
|
+
def initialize(parent, sym, options = {})
|
8
|
+
@parent = parent
|
9
|
+
@sym = sym
|
10
|
+
@children = {}
|
11
|
+
@element = nil
|
12
|
+
@domtype = :div
|
13
|
+
@states = {}
|
14
|
+
@options = options
|
15
|
+
|
16
|
+
creation
|
17
|
+
end
|
18
|
+
|
19
|
+
def factory
|
20
|
+
@parent.factory
|
21
|
+
end
|
22
|
+
|
23
|
+
def root
|
24
|
+
@parent.root
|
25
|
+
end
|
26
|
+
|
27
|
+
def component
|
28
|
+
@parent.component
|
29
|
+
end
|
30
|
+
|
31
|
+
def router
|
32
|
+
@parent.router
|
33
|
+
end
|
34
|
+
|
35
|
+
def option_replace(key, default = nil)
|
36
|
+
value = @options[key] || default
|
37
|
+
@options.delete(key) if @options.has_key?(key)
|
38
|
+
value
|
39
|
+
end
|
40
|
+
|
41
|
+
# Store element states: adds/removes css classes
|
42
|
+
def add_states(states)
|
43
|
+
states.each do |state|
|
44
|
+
add_state(state)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_state(state, value = false)
|
49
|
+
@states[state] = [factory.dasherize(state), value]
|
50
|
+
classify_state @states[state]
|
51
|
+
end
|
52
|
+
|
53
|
+
# active: true, false, nil
|
54
|
+
def update_state(state, active)
|
55
|
+
if !active.nil?
|
56
|
+
@states.each do |s, v|
|
57
|
+
v[1] = active if s == state
|
58
|
+
classify_state v
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def toggle_state(state)
|
64
|
+
@states.select { |s, _| s == state }.each do |s, v|
|
65
|
+
v[1] = !v[1]
|
66
|
+
classify_state v
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def classify_state(state)
|
71
|
+
if state[1]
|
72
|
+
`#{element}.classList.add(#{state[0]})`
|
73
|
+
else
|
74
|
+
`#{element}.classList.remove(#{state[0]})`
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def state_active?(state)
|
79
|
+
@states[state][1]
|
80
|
+
end
|
81
|
+
|
82
|
+
def value
|
83
|
+
`#{@element}.innerHTML`
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_text
|
87
|
+
`#{@element}.textContent`
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_text(value)
|
91
|
+
# https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
|
92
|
+
`#{@element}.textContent = #{value}`
|
93
|
+
end
|
94
|
+
|
95
|
+
def html(raw_html)
|
96
|
+
`#{@element}.innerHTML = #{raw_html}`
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_attribute(name, value)
|
100
|
+
if name == 'scrollTop'
|
101
|
+
`#{element}.scrollTop=#{value}`
|
102
|
+
else
|
103
|
+
`#{element}.setAttribute(#{name}, #{value})`
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def remove_attribute(name)
|
108
|
+
`#{element}.removeAttribute(#{name})`
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module FerroElementary
|
2
|
+
|
3
|
+
RESERVED_NAMES = %i[
|
4
|
+
initialize factory root router page404
|
5
|
+
creation _before_create before_create create _after_create
|
6
|
+
after_create style _stylize cascade
|
7
|
+
add_child forget_children remove_child method_missing destroy
|
8
|
+
value set_text html parent children element domtype options
|
9
|
+
add_states add_state update_state toggle_state state_active
|
10
|
+
]
|
11
|
+
|
12
|
+
def creation
|
13
|
+
_before_create
|
14
|
+
before_create
|
15
|
+
create
|
16
|
+
_after_create
|
17
|
+
after_create
|
18
|
+
_stylize
|
19
|
+
cascade
|
20
|
+
end
|
21
|
+
|
22
|
+
def _before_create;end
|
23
|
+
def before_create;end
|
24
|
+
|
25
|
+
def create
|
26
|
+
@element = factory.create_element(self, @domtype, @parent, @options) if @domtype
|
27
|
+
end
|
28
|
+
|
29
|
+
def _after_create;end
|
30
|
+
def after_create;end
|
31
|
+
|
32
|
+
def style;end
|
33
|
+
|
34
|
+
def _stylize
|
35
|
+
styles = style
|
36
|
+
|
37
|
+
if styles.class == Hash
|
38
|
+
set_attribute(
|
39
|
+
'style',
|
40
|
+
styles.map { |k, v| "#{k}:#{v};" }.join
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def cascade;end
|
46
|
+
|
47
|
+
def add_child(name, element_class, options = {})
|
48
|
+
sym = symbolize(name)
|
49
|
+
raise "Child '#{sym}' already defined" if @children.has_key?(sym)
|
50
|
+
raise "Illegal name (#{sym})" if RESERVED_NAMES.include?(sym)
|
51
|
+
@children[sym] = element_class.new(self, sym, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def symbolize(name)
|
55
|
+
name.downcase.to_sym
|
56
|
+
end
|
57
|
+
|
58
|
+
def forget_children
|
59
|
+
children = {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_child(sym)
|
63
|
+
@children.delete(sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
def destroy
|
67
|
+
`#{parent.element}.removeChild(#{element})`
|
68
|
+
parent.remove_child(@sym)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Getter for children
|
72
|
+
def method_missing(method_name, *args, &block)
|
73
|
+
if @children.has_key?(method_name)
|
74
|
+
@children[method_name]
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class FerroFactory
|
2
|
+
|
3
|
+
attr_reader :body
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@body = `document.body`
|
7
|
+
`while (document.body.firstChild) {document.body.removeChild(document.body.firstChild);}`
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_element(target, type, parent, options = {})
|
11
|
+
# Create element
|
12
|
+
element = `document.createElement(#{type})`
|
13
|
+
|
14
|
+
# Add element to DOM
|
15
|
+
if options[:prepend]
|
16
|
+
`#{parent.element}.insertBefore(#{element}, #{options[:prepend].element})`
|
17
|
+
else
|
18
|
+
`#{parent.element}.appendChild(#{element})`
|
19
|
+
end
|
20
|
+
|
21
|
+
# Add ruby class to the node
|
22
|
+
`#{element}.classList.add(#{dasherize(target.class.name)})`
|
23
|
+
|
24
|
+
# Add ruby superclass to the node to allow for more generic styling
|
25
|
+
if target.class.superclass != FerroElement
|
26
|
+
`#{element}.classList.add(#{dasherize(target.class.superclass.name)})`
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set ruby object_id as default element id
|
30
|
+
if !options.has_key?(:id)
|
31
|
+
`#{element}.id = #{target.object_id}`
|
32
|
+
end
|
33
|
+
|
34
|
+
# Set attributes
|
35
|
+
options.each do |name, value|
|
36
|
+
case name
|
37
|
+
when :prepend
|
38
|
+
nil
|
39
|
+
when :content
|
40
|
+
`#{element}.appendChild(document.createTextNode(#{value}))`
|
41
|
+
else
|
42
|
+
`#{element}.setAttribute(#{name}, #{value})`
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
element
|
47
|
+
end
|
48
|
+
|
49
|
+
def dasherize(class_name)
|
50
|
+
return class_name if class_name !~ /[A-Z_]/
|
51
|
+
(class_name[0] + class_name[1..-1].gsub(/[A-Z]/){ |c| "-#{c}" }).downcase.gsub('_', '-')
|
52
|
+
end
|
53
|
+
|
54
|
+
def camelize(class_name)
|
55
|
+
return class_name if class_name !~ /-/
|
56
|
+
class_name.gsub(/(?:-|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.strip
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
class FerroForm < FerroElement
|
2
|
+
def _before_create
|
3
|
+
# Dont use a real <form> so we don't have to prevent default actions
|
4
|
+
@domtype = :div
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# :text, :password, :reset, :radio, :checkbox, :color,
|
9
|
+
# :date, :datetime-local, :email, :month, :number,
|
10
|
+
# :range, :search, :tel, :time, :url, :week
|
11
|
+
class FerroFormInput < FerroElement
|
12
|
+
def _before_create
|
13
|
+
@domtype = :input
|
14
|
+
@options[:type] ||= :text
|
15
|
+
@options[:value] = option_replace :content
|
16
|
+
@disabled = option_replace :disabled, false
|
17
|
+
end
|
18
|
+
|
19
|
+
def value
|
20
|
+
`#{@element}.value`
|
21
|
+
end
|
22
|
+
|
23
|
+
def value=(value)
|
24
|
+
`#{@element}.value = #{value}`
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_focus
|
28
|
+
# Doesn't work ?!
|
29
|
+
`#{element}.focus()`
|
30
|
+
end
|
31
|
+
|
32
|
+
def _after_create
|
33
|
+
disable if @disabled
|
34
|
+
|
35
|
+
if [:text, :password, :number].include?(@options[:type])
|
36
|
+
`#{@element}.addEventListener("keydown",function(e){self.$keydowned(e);})`
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def keydowned(event)
|
41
|
+
entered if Native(event).keyCode == 13
|
42
|
+
end
|
43
|
+
|
44
|
+
def entered;end
|
45
|
+
|
46
|
+
def disable
|
47
|
+
set_attribute(:disabled, :disabled)
|
48
|
+
end
|
49
|
+
|
50
|
+
def enable
|
51
|
+
remove_attribute(:disabled)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class FerroFormLabel < FerroElement
|
56
|
+
def _before_create
|
57
|
+
@domtype = :label
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class FerroFormFieldset < FerroElement
|
62
|
+
def _before_create
|
63
|
+
@domtype = :fieldset
|
64
|
+
@legend = @options[:legend]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class FerroFormTextarea < FerroElement
|
69
|
+
def _before_create
|
70
|
+
@domtype = :textarea
|
71
|
+
@size = { rows: 40, cols: 5 }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class FerroFormOutput < FerroElement
|
76
|
+
def _before_create
|
77
|
+
@domtype = :output
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class FerroFormClickable < FerroFormInput
|
82
|
+
def _after_create
|
83
|
+
`#{@element}.addEventListener("click",function(e){#{clicked};document.activeElement.blur()})`
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def clicked;end
|
88
|
+
end
|
89
|
+
|
90
|
+
class FerroFormButton < FerroFormClickable
|
91
|
+
def _before_create
|
92
|
+
@options[:type] = :button
|
93
|
+
super
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class FerroFormSubmit < FerroFormClickable
|
98
|
+
def _before_create
|
99
|
+
@options[:type] = :submit
|
100
|
+
super
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class FerroFormBlock < FerroFormClickable
|
105
|
+
def _before_create
|
106
|
+
super
|
107
|
+
@domtype = :div
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# CheckBox with click callback
|
112
|
+
class FerroFormCheckBox < FerroFormClickable
|
113
|
+
def _before_create
|
114
|
+
@options[:type] = :checkbox
|
115
|
+
super
|
116
|
+
end
|
117
|
+
|
118
|
+
def checked?
|
119
|
+
`#{@element}.checked`
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Select input
|
124
|
+
class FerroFormSelect < FerroElement
|
125
|
+
def _before_create
|
126
|
+
@domtype = :select
|
127
|
+
@list = option_replace :list, {}
|
128
|
+
super
|
129
|
+
end
|
130
|
+
|
131
|
+
def after_create
|
132
|
+
@list.each do |value, content|
|
133
|
+
add_option(value, content)
|
134
|
+
end
|
135
|
+
|
136
|
+
`#{@element}.addEventListener("change",function(e){#{changed};document.activeElement.blur()})`
|
137
|
+
super
|
138
|
+
end
|
139
|
+
|
140
|
+
def changed;end
|
141
|
+
|
142
|
+
def add_option(value, content)
|
143
|
+
add_child "opt_#{value}", FerroElementVar, domtype: :option, value: value, content: content
|
144
|
+
end
|
145
|
+
|
146
|
+
def selection
|
147
|
+
option = `#{element}.options[#{element}.selectedIndex].value`
|
148
|
+
text = `#{element}.options[#{element}.selectedIndex].text`
|
149
|
+
{ option: option, text: text }
|
150
|
+
end
|
151
|
+
|
152
|
+
def select(option)
|
153
|
+
`for(var i=0; i < #{element}.options.length; i++) {
|
154
|
+
if (#{element}.options[i].value === #{option}) {
|
155
|
+
#{element}.selectedIndex = i;
|
156
|
+
break;
|
157
|
+
}
|
158
|
+
}`
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'native'
|
2
|
+
|
3
|
+
class FerroRouter
|
4
|
+
|
5
|
+
def initialize(page404)
|
6
|
+
@routes = []
|
7
|
+
@page404 = page404
|
8
|
+
setup_navigation_listener
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_route(path, callback)
|
12
|
+
@routes << { parts: path_to_parts(path), callback: callback }
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup_navigation_listener
|
16
|
+
`window.onpopstate = function(e){#{navigated}}`
|
17
|
+
end
|
18
|
+
|
19
|
+
def replace_state(url)
|
20
|
+
`history.replaceState(null,null,#{url})`
|
21
|
+
end
|
22
|
+
|
23
|
+
def push_state(url)
|
24
|
+
`history.pushState(null,null,#{url})`
|
25
|
+
end
|
26
|
+
|
27
|
+
def go_to(url)
|
28
|
+
push_state(url)
|
29
|
+
navigated
|
30
|
+
end
|
31
|
+
|
32
|
+
def go_back
|
33
|
+
`history.back()`
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_location
|
37
|
+
Native(`new URL(window.location.href)`)
|
38
|
+
end
|
39
|
+
|
40
|
+
def path_to_parts(path)
|
41
|
+
path.
|
42
|
+
downcase.
|
43
|
+
split('/').
|
44
|
+
map { |part| part.empty? ? nil : part.strip }.
|
45
|
+
compact
|
46
|
+
end
|
47
|
+
|
48
|
+
def decode(value)
|
49
|
+
`decodeURI(#{value})`
|
50
|
+
end
|
51
|
+
|
52
|
+
def navigated
|
53
|
+
url = get_location
|
54
|
+
@params = []
|
55
|
+
|
56
|
+
idx = match(path_to_parts(decode(url.pathname)), decode(url.search))
|
57
|
+
|
58
|
+
if idx
|
59
|
+
@routes[idx][:callback].call(@params)
|
60
|
+
else
|
61
|
+
@page404.call(url.pathname)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def match(path, search)
|
66
|
+
matches = get_matches(path)
|
67
|
+
|
68
|
+
if matches.length > 0
|
69
|
+
match = matches.sort { |m| m[1] }.first
|
70
|
+
|
71
|
+
@params = match[2]
|
72
|
+
add_search_to_params(search)
|
73
|
+
|
74
|
+
match[0]
|
75
|
+
else
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_matches(path)
|
81
|
+
matches = []
|
82
|
+
|
83
|
+
@routes.each_with_index do |route, i|
|
84
|
+
score, pars = score_route(route[:parts], path)
|
85
|
+
matches << [i, score, pars] if score > 0
|
86
|
+
end
|
87
|
+
|
88
|
+
matches
|
89
|
+
end
|
90
|
+
|
91
|
+
def score_route(parts, path)
|
92
|
+
score = 0
|
93
|
+
pars = {}
|
94
|
+
|
95
|
+
if parts.length == path.length
|
96
|
+
parts.each_with_index do |part, i|
|
97
|
+
if part[0] == ':'
|
98
|
+
score += 1
|
99
|
+
pars["#{part[1..-1]}"] = path[i]
|
100
|
+
elsif part == path[i].downcase
|
101
|
+
score += 2
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
return score, pars
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_search_to_params(search)
|
110
|
+
if !search.empty?
|
111
|
+
pars = search[1..-1].split('&')
|
112
|
+
|
113
|
+
pars.each do |par|
|
114
|
+
pair = par.split('=')
|
115
|
+
@params[ pair[0] ] = pair[1] if pair.length == 2
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Based on turbolinks/http_request
|
2
|
+
require 'json'
|
3
|
+
require 'native'
|
4
|
+
|
5
|
+
class FerroXhr
|
6
|
+
|
7
|
+
NETWORK_FAILURE = 'Network error'
|
8
|
+
TIMEOUT_FAILURE = 'Timeout'
|
9
|
+
|
10
|
+
def initialize(url, callback, error_callback, options = {})
|
11
|
+
@url = url
|
12
|
+
@callback = callback
|
13
|
+
@error_callback = error_callback
|
14
|
+
@referrer = options[:referrer] || 'Ferro'
|
15
|
+
@timeout = options[:timeout] || 5000
|
16
|
+
@accept = options[:accept] || 'application/json'
|
17
|
+
@xhr = nil
|
18
|
+
@sent = false
|
19
|
+
|
20
|
+
createXHR
|
21
|
+
send
|
22
|
+
end
|
23
|
+
|
24
|
+
def send
|
25
|
+
if @xhr && !@sent
|
26
|
+
@xhr.JS.send
|
27
|
+
@sent = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cancel
|
32
|
+
@xhr.JS.abort if @xhr && @sent
|
33
|
+
end
|
34
|
+
|
35
|
+
def createXHR
|
36
|
+
@xhr = `new XMLHttpRequest`
|
37
|
+
`#{@xhr}.open('GET', #{@url}, true)`
|
38
|
+
`#{@xhr}.timeout = #{@timeout}`
|
39
|
+
# `#{@xhr}.responseType = 'json'` if @accept == 'application/json'
|
40
|
+
`#{@xhr}.setRequestHeader('Accept', #{@accept})`
|
41
|
+
`#{@xhr}.setRequestHeader('Ferro-Referrer', #{@referrer})`
|
42
|
+
`#{@xhr}.addEventListener('load', function(){#{requestLoaded}})`
|
43
|
+
`#{@xhr}.addEventListener('error', function(){#{requestFailed}})`
|
44
|
+
`#{@xhr}.addEventListener('timeout', function(){#{requestTimedOut}})`
|
45
|
+
`#{@xhr}.addEventListener('abort', function(){#{requestCanceled}})`
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def destroy
|
50
|
+
`#{@xhr} = null`
|
51
|
+
end
|
52
|
+
|
53
|
+
def requestLoaded
|
54
|
+
begin
|
55
|
+
status = Native(`#{@xhr}.status`)
|
56
|
+
raise if status >= 300
|
57
|
+
json = JSON.parse(`#{@xhr}.response`)
|
58
|
+
@callback.call(json)
|
59
|
+
rescue => error
|
60
|
+
@failed = true
|
61
|
+
@error_callback.call(status, error)
|
62
|
+
end
|
63
|
+
|
64
|
+
destroy
|
65
|
+
end
|
66
|
+
|
67
|
+
def requestFailed
|
68
|
+
@failed = true
|
69
|
+
@error_callback.call(`#{@xhr}.status`, Native(`#{@xhr}.response`))
|
70
|
+
destroy
|
71
|
+
end
|
72
|
+
|
73
|
+
def requestTimedOut
|
74
|
+
@failed = true
|
75
|
+
@error_callback.call(0, TIMEOUT_FAILURE)
|
76
|
+
destroy
|
77
|
+
end
|
78
|
+
|
79
|
+
def requestCanceled
|
80
|
+
destroy
|
81
|
+
end
|
82
|
+
end
|
data/opal/opal-ferro.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'opal'
|
2
|
+
require 'opal-ferro/ferro_elementary'
|
3
|
+
require 'opal-ferro/ferro_element'
|
4
|
+
require 'opal-ferro/ferro_document'
|
5
|
+
require 'opal-ferro/ferro_factory'
|
6
|
+
require 'opal-ferro/ferro_router'
|
7
|
+
require 'opal-ferro/ferro_xhr'
|
8
|
+
require 'opal-ferro/ferro_sequence'
|
9
|
+
require 'opal-ferro/ferro_base_elements'
|
10
|
+
require 'opal-ferro/ferro_form_elements'
|
11
|
+
require 'opal-ferro/ferro_components'
|
data/opal-ferro.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'opal-ferro/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'opal-ferro'
|
8
|
+
s.version = Opal::Ferro::VERSION
|
9
|
+
s.author = ['Ivo Herweijer']
|
10
|
+
s.email = ['info@edwhs.nl']
|
11
|
+
|
12
|
+
s.summary = %q{Simplifying web-development: no more html, just beautiful and simple Ruby code.}
|
13
|
+
s.description = %q{Ferro is a small Ruby library on top of Opal that enables an object-oriented programming style for creating code that runs in the webbrowser. No more distractions like HTML and searching for DOM elements, just beautiful and simple Ruby code. Front-End-Ruby-ROcks!}
|
14
|
+
s.homepage = 'https://github.com/easydatawarehousing/opal-ferro'
|
15
|
+
s.license = 'MIT'
|
16
|
+
|
17
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
|
20
|
+
s.add_dependency 'opal', '~> 0.10'
|
21
|
+
|
22
|
+
s.add_development_dependency 'bundler', '~> 1.16'
|
23
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
24
|
+
# s.add_development_dependency 'yard', '~> 0.8.7'
|
25
|
+
s.add_development_dependency 'minitest', '~> 5.0'
|
26
|
+
# s.add_development_dependency 'selenium-webdriver', '~> 3.8'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: opal-ferro
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.10.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ivo Herweijer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: opal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
description: Ferro is a small Ruby library on top of Opal that enables an object-oriented
|
70
|
+
programming style for creating code that runs in the webbrowser. No more distractions
|
71
|
+
like HTML and searching for DOM elements, just beautiful and simple Ruby code. Front-End-Ruby-ROcks!
|
72
|
+
email:
|
73
|
+
- info@edwhs.nl
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".travis.yml"
|
80
|
+
- CODE_OF_CONDUCT.md
|
81
|
+
- Gemfile
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- lib/opal-ferro.rb
|
86
|
+
- lib/opal-ferro/version.rb
|
87
|
+
- lib/opal/ferro.rb
|
88
|
+
- opal-ferro.gemspec
|
89
|
+
- opal/opal-ferro.rb
|
90
|
+
- opal/opal-ferro/ferro_base_elements.js.rb
|
91
|
+
- opal/opal-ferro/ferro_components.js.rb
|
92
|
+
- opal/opal-ferro/ferro_document.js.rb
|
93
|
+
- opal/opal-ferro/ferro_element.js.rb
|
94
|
+
- opal/opal-ferro/ferro_elementary.js.rb
|
95
|
+
- opal/opal-ferro/ferro_factory.js.rb
|
96
|
+
- opal/opal-ferro/ferro_form_elements.js.rb
|
97
|
+
- opal/opal-ferro/ferro_router.js.rb
|
98
|
+
- opal/opal-ferro/ferro_sequence.js.rb
|
99
|
+
- opal/opal-ferro/ferro_xhr.js.rb
|
100
|
+
homepage: https://github.com/easydatawarehousing/opal-ferro
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.6.14
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: 'Simplifying web-development: no more html, just beautiful and simple Ruby
|
124
|
+
code.'
|
125
|
+
test_files: []
|