darwinjs-rails 1.0.1
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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +145 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/darwin/base.coffee +75 -0
- data/app/assets/javascripts/darwin/controller.coffee +125 -0
- data/app/assets/javascripts/darwin/loader.coffee +54 -0
- data/app/assets/javascripts/darwin/template.coffee +43 -0
- data/app/assets/javascripts/darwin/view.coffee +108 -0
- data/app/assets/javascripts/darwin.coffee +16 -0
- data/darwinjs-rails.gemspec +26 -0
- data/doc/base.md +147 -0
- data/doc/controller.md +266 -0
- data/doc/introduction.md +228 -0
- data/doc/loader.md +116 -0
- data/doc/view.md +282 -0
- data/lib/darwinjs/rails/engine.rb +9 -0
- data/lib/darwinjs/rails/version.rb +5 -0
- data/lib/darwinjs-rails.rb +2 -0
- data/lib/generators/darwinjs/assets/assets_generator.rb +78 -0
- data/lib/generators/darwinjs/assets/templates/controllers/controller.coffee +5 -0
- data/lib/generators/darwinjs/assets/templates/controllers/namespace.coffee +1 -0
- data/lib/generators/darwinjs/assets/templates/views/namespace.coffee +2 -0
- data/lib/generators/darwinjs/assets/templates/views/view.coffee +5 -0
- metadata +153 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Olivier El Mekki
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# Darwinjs
|
2
|
+
|
3
|
+
Darwin is a javascript framework for people that take error
|
4
|
+
handling seriously and want to achieve it through progressive
|
5
|
+
enhancement and graceful degradation.
|
6
|
+
|
7
|
+
Darwin will also let developer write clean and encapsulated
|
8
|
+
code that encourages self documentation.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'darwinjs-rails'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install darwinjs-rails
|
23
|
+
|
24
|
+
## Getting Started
|
25
|
+
|
26
|
+
First, as one time configuration, add autoloader in your
|
27
|
+
application.coffee file :
|
28
|
+
|
29
|
+
```
|
30
|
+
$(->
|
31
|
+
Darwin.Loader.run()
|
32
|
+
)
|
33
|
+
```
|
34
|
+
|
35
|
+
You can now generate a javascript module using the provided
|
36
|
+
generator :
|
37
|
+
|
38
|
+
```
|
39
|
+
$ rails generate darwin:assets users/index
|
40
|
+
|
41
|
+
create app/assets/javascripts/controllers/users.coffee
|
42
|
+
create app/assets/javascripts/views/users.coffee
|
43
|
+
create app/assets/javascripts/controllers/users/index.coffee
|
44
|
+
create app/assets/javascripts/views/users/index.coffee
|
45
|
+
```
|
46
|
+
|
47
|
+
This will create your controller and your view in the `users` namespace.
|
48
|
+
|
49
|
+
Now add a `data-module` attribute in your users index view to
|
50
|
+
autoload your module :
|
51
|
+
|
52
|
+
```erb
|
53
|
+
<div id="users" data-module="User.Index">
|
54
|
+
<ul>
|
55
|
+
<% @users.each do |user| %>
|
56
|
+
<%= render 'user', user: user %>
|
57
|
+
<% end %>
|
58
|
+
</ul>
|
59
|
+
</div>
|
60
|
+
```
|
61
|
+
|
62
|
+
This will automatically initialize your module.
|
63
|
+
|
64
|
+
A module is composed of two files :
|
65
|
+
|
66
|
+
* a controller that handles events
|
67
|
+
* a view that handles DOM manipulation
|
68
|
+
|
69
|
+
Here is a typical view :
|
70
|
+
|
71
|
+
```coffee
|
72
|
+
class App.Views.Users.Index extends Darwin.Controller
|
73
|
+
@options {
|
74
|
+
selectors:
|
75
|
+
show_users: 'a#show_users'
|
76
|
+
user_block: '#users'
|
77
|
+
user:
|
78
|
+
'sel': '.user'
|
79
|
+
more: '.more a'
|
80
|
+
delete: 'a[data-method="delete"]'
|
81
|
+
}
|
82
|
+
|
83
|
+
show_info_for( $link ) ->
|
84
|
+
$link.next( '.info' ).show()
|
85
|
+
|
86
|
+
remove_user_for( $link ) ->
|
87
|
+
$link.parent().remove()
|
88
|
+
```
|
89
|
+
|
90
|
+
And the corresponding controller :
|
91
|
+
|
92
|
+
```coffee
|
93
|
+
class App.Controllers.Users.Index extends Darwin.Controller
|
94
|
+
@options {
|
95
|
+
View: App.Views.Users.Index
|
96
|
+
|
97
|
+
events:
|
98
|
+
'Toggle user block': { el: 'show_users', type: 'click' }
|
99
|
+
'Show user info': { el: 'user_more', type: 'click' }
|
100
|
+
'Delete user on server': { el: 'user_delete', type: 'click' }
|
101
|
+
}
|
102
|
+
|
103
|
+
|
104
|
+
show_users_clicked: ->
|
105
|
+
@view.get( 'user_block' ).fadeIn()
|
106
|
+
|
107
|
+
|
108
|
+
user_more_clicked: ( $link ) ->
|
109
|
+
@view.show_info_for( $link )
|
110
|
+
|
111
|
+
|
112
|
+
user_delete_clicked: ( $link ) ->
|
113
|
+
if confirm( 'Really delete user ?' )
|
114
|
+
$.get( $link.attr( 'href' ), =>
|
115
|
+
@view.remove_user_for( $link )
|
116
|
+
)
|
117
|
+
```
|
118
|
+
|
119
|
+
As you see, a view acts as single point of configuration for selectors.
|
120
|
+
Any change needed then reflect to the whole javascript codebase.
|
121
|
+
|
122
|
+
In the same way, controller acts as a single point of configuration
|
123
|
+
for events. You can tell what a module does looking at the first
|
124
|
+
lines of the controller file.
|
125
|
+
|
126
|
+
But there is more happening under the hood, here. First, all you DOM
|
127
|
+
elements retrieved by view selectors are cached. Upon further call
|
128
|
+
they are retrieved without hitting the DOM again, which is very
|
129
|
+
costly in term of performances.
|
130
|
+
|
131
|
+
Furthermore, all event callbacks are wrapped so that they do not
|
132
|
+
execute if an error occured. In case of error, events are simply
|
133
|
+
deactivated and any link is followed, reloading the page and letting
|
134
|
+
server side handle what has to be done, so your user doesn't even
|
135
|
+
notice something got wrong.
|
136
|
+
|
137
|
+
Ready for more ? See [introduction](doc/introduction.md).
|
138
|
+
|
139
|
+
## Contributing
|
140
|
+
|
141
|
+
1. Fork it
|
142
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
143
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
144
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
145
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Documentation is in /doc/base.md
|
2
|
+
#
|
3
|
+
reserved_keywords = [ 'extended', 'included' ]
|
4
|
+
|
5
|
+
class Darwin.Base
|
6
|
+
@options: ( options ) ->
|
7
|
+
parent_options = @__super__.constructor._options
|
8
|
+
|
9
|
+
if parent_options?
|
10
|
+
@_options = $.extend true, {}, parent_options, options
|
11
|
+
else
|
12
|
+
@_options = options
|
13
|
+
|
14
|
+
|
15
|
+
@extend: ( obj ) ->
|
16
|
+
for key, value of obj when key not in reserved_keywords
|
17
|
+
@[ key ] = value
|
18
|
+
|
19
|
+
obj.extended?.apply(@)
|
20
|
+
@
|
21
|
+
|
22
|
+
|
23
|
+
@include: ( obj ) ->
|
24
|
+
for key, value of obj when key not in reserved_keywords
|
25
|
+
@::[ key ] = value
|
26
|
+
|
27
|
+
obj.included?.apply(@)
|
28
|
+
@
|
29
|
+
|
30
|
+
|
31
|
+
constructor: ( options ) ->
|
32
|
+
@_bound = {}
|
33
|
+
|
34
|
+
@options = $.extend( {}, @constructor._options, options )
|
35
|
+
|
36
|
+
if @options.dependencies?
|
37
|
+
for own name, dependency of @options.dependencies
|
38
|
+
@[ name ] = dependency
|
39
|
+
|
40
|
+
|
41
|
+
bind: ( event_name, callback ) ->
|
42
|
+
@_bound[ event_name ] ?= []
|
43
|
+
@_bound[ event_name ].push( callback )
|
44
|
+
|
45
|
+
|
46
|
+
on: ( event_name, callback ) ->
|
47
|
+
@bind( event_name, callback )
|
48
|
+
|
49
|
+
|
50
|
+
one: ( event_name, callback ) ->
|
51
|
+
callback = =>
|
52
|
+
@unbind event_name, callback
|
53
|
+
|
54
|
+
@bind event_name, callback
|
55
|
+
|
56
|
+
|
57
|
+
unbind: ( event_name, callback ) ->
|
58
|
+
if @_bound[ event_name ]
|
59
|
+
if callback
|
60
|
+
$.each @_bound[ event_name ], ( i, bound ) ->
|
61
|
+
if bound == callback
|
62
|
+
delete @_bound[ event_name ][ i ]
|
63
|
+
|
64
|
+
else
|
65
|
+
delete @_bound[ event_name ]
|
66
|
+
|
67
|
+
|
68
|
+
unbind_all: ->
|
69
|
+
@_bound = {}
|
70
|
+
|
71
|
+
|
72
|
+
trigger: ( event_name, params... ) ->
|
73
|
+
if @_bound[ event_name ]
|
74
|
+
for own callback in @_bound[ event_name ]
|
75
|
+
callback( params... )
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# Documentation is in /doc/controller.md
|
2
|
+
#
|
3
|
+
class Darwin.Controller extends Darwin.Base
|
4
|
+
@options {
|
5
|
+
events: {}
|
6
|
+
View: Darwin.View
|
7
|
+
failsafe: true
|
8
|
+
}
|
9
|
+
|
10
|
+
|
11
|
+
constructor: ( root, options ) ->
|
12
|
+
super options
|
13
|
+
|
14
|
+
@root = root.get(0)
|
15
|
+
|
16
|
+
if @root
|
17
|
+
@_dom_bound = []
|
18
|
+
@$root = $( @root )
|
19
|
+
@view = new @options.View @$root
|
20
|
+
|
21
|
+
@bind_dom_events() unless exports?
|
22
|
+
@view.run()
|
23
|
+
@run() unless exports?
|
24
|
+
else
|
25
|
+
throw new Error( 'Controller initialized without any element' )
|
26
|
+
|
27
|
+
|
28
|
+
run: ->
|
29
|
+
|
30
|
+
|
31
|
+
bind_dom_events: ->
|
32
|
+
@bind_dom_event( event, name ) for own name, event of @options.events
|
33
|
+
|
34
|
+
|
35
|
+
bind_dom_event: ( definition, name ) ->
|
36
|
+
unless definition.el?
|
37
|
+
throw new Error( "No el key for event : #{name or 'manually bound event'}" )
|
38
|
+
|
39
|
+
unless definition.type?
|
40
|
+
throw new Error( "No type key for event : #{name or 'manually bound event'}" )
|
41
|
+
|
42
|
+
|
43
|
+
wrap = ( callback, stop ) =>
|
44
|
+
( event ) =>
|
45
|
+
unless ( window.crashed and @options.failsafe is true )
|
46
|
+
event.preventDefault() if stop
|
47
|
+
$target = $( event.target )
|
48
|
+
|
49
|
+
if definition.ensure_element isnt false
|
50
|
+
el = if definition.delegate? then definition.delegate else definition.el
|
51
|
+
|
52
|
+
if el == 'root'
|
53
|
+
sel = 'root'
|
54
|
+
else
|
55
|
+
sel = ( @view.selectors[ el ] or @view._find_alternate_name( el ) ).sel
|
56
|
+
|
57
|
+
$target = $target.parents( sel ).first() unless $target.is( sel )
|
58
|
+
|
59
|
+
callback( $target, event )
|
60
|
+
|
61
|
+
definition.stop = true if definition.type == 'click' and ! definition.stop?
|
62
|
+
|
63
|
+
switch true
|
64
|
+
when !! definition.controller_method
|
65
|
+
method_name = definition.controller_method
|
66
|
+
if @[ method_name ]
|
67
|
+
method = $.proxy( @[ method_name ], this )
|
68
|
+
else
|
69
|
+
throw new Error( "Undefined method for controller : #{method_name}" )
|
70
|
+
|
71
|
+
when !! definition.view_method
|
72
|
+
method_name = definition.view_method
|
73
|
+
if @view[ method_name ]
|
74
|
+
method = $.proxy( @view[ method_name ], @view )
|
75
|
+
else
|
76
|
+
throw new Error( "Undefined method for view : #{method_name}" )
|
77
|
+
|
78
|
+
else
|
79
|
+
method_name = "#{definition.delegate or definition.el}_#{definition.type}#{if definition.type.match( /e$/ ) then 'd' else 'ed' }"
|
80
|
+
if @[ method_name ]
|
81
|
+
method = $.proxy( @[ method_name ], this )
|
82
|
+
else
|
83
|
+
throw new Error( "Undefined method for controller : #{method_name}" )
|
84
|
+
|
85
|
+
$element = @view.get( definition.el )
|
86
|
+
|
87
|
+
|
88
|
+
if definition.delegate
|
89
|
+
delegate_to = @view.selectors[ definition.delegate ] or @view._find_alternate_name( definition.delegate )
|
90
|
+
|
91
|
+
if definition.cancel_delay and definition.cancel_delay > 0
|
92
|
+
callback = ( event ) =>
|
93
|
+
window.clearTimeout @[ '_' + method_name + '_timeout' ]
|
94
|
+
wrapped = wrap( method, definition.stop )
|
95
|
+
@[ '_' + method_name + '_timeout' ] = window.setTimeout( ( -> ( wrapped event ) ), definition.cancel_delay )
|
96
|
+
else
|
97
|
+
callback = wrap method, definition.stop
|
98
|
+
|
99
|
+
throw new Error "Selector not found : #{definition.delegate}" unless delegate_to
|
100
|
+
$element.delegate delegate_to.sel, definition.type, callback
|
101
|
+
@_dom_bound.push { el: $element, delegate: delegate_to.sel, type: definition.type, callback: callback }
|
102
|
+
else
|
103
|
+
if definition.cancel_delay and definition.cancel_delay > 0
|
104
|
+
callback = ( event ) =>
|
105
|
+
window.clearTimeout @[ '_' + method_name + '_timeout' ]
|
106
|
+
wrapped = wrap( method, definition.stop )
|
107
|
+
@[ '_' + method_name + '_timeout' ] = window.setTimeout( ( -> ( wrapped event ) ), definition.cancel_delay )
|
108
|
+
else
|
109
|
+
callback = wrap method, definition.stop
|
110
|
+
|
111
|
+
$element.bind definition.type, callback
|
112
|
+
@_dom_bound.push { el: $element, type: definition.type, callback: callback }
|
113
|
+
|
114
|
+
|
115
|
+
destructor: ->
|
116
|
+
@view._destructor()
|
117
|
+
for bound in @_dom_bound
|
118
|
+
if bound.delegate
|
119
|
+
bound.el.undelegate bound.delegate, bound.type, bound.callback
|
120
|
+
else
|
121
|
+
bound.el.unbind bound.type, bound.callback
|
122
|
+
|
123
|
+
delete Darwin.Loader.controllers()[ @id ]
|
124
|
+
|
125
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Documentation is in /doc/loader.md
|
2
|
+
#
|
3
|
+
controllers = {}
|
4
|
+
errors_got = 0
|
5
|
+
|
6
|
+
loader = Darwin.Loader =
|
7
|
+
run: ->
|
8
|
+
loader.module_roots().each( ( i, $module ) =>
|
9
|
+
$module = $( $module )
|
10
|
+
module_name = loader.compute_name( $module.attr( 'data-module' ) )
|
11
|
+
path = $module.attr( 'data-module' ).split( '.' )
|
12
|
+
module = App.Controllers
|
13
|
+
module = module[ path.shift() ] while path.length
|
14
|
+
|
15
|
+
if module
|
16
|
+
controllers[ module_name ] = new module( $module )
|
17
|
+
controllers[ module_name ].id = module_name
|
18
|
+
else
|
19
|
+
throw new Error( "Can't find module #{$module.attr( 'data-module' )}" )
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
module_roots: ->
|
24
|
+
$( '*[data-module]' )
|
25
|
+
|
26
|
+
|
27
|
+
compute_name: ( module_path ) ->
|
28
|
+
name = module_path.replace( /\./g, '_' ).toLowerCase()
|
29
|
+
|
30
|
+
if controllers[ name ]
|
31
|
+
i = 1
|
32
|
+
|
33
|
+
for own controller_name, controller of controllers
|
34
|
+
i++ if controller_name.indexOf( name ) isnt -1
|
35
|
+
|
36
|
+
name = "#{name}_#{i}"
|
37
|
+
|
38
|
+
name
|
39
|
+
|
40
|
+
controllers: ->
|
41
|
+
controllers
|
42
|
+
|
43
|
+
window.onerror = ( error, url, lineno ) =>
|
44
|
+
if url && url.match( /https?:\/\/.*?assets/ )
|
45
|
+
@crashed = true
|
46
|
+
console?.log( "Error on #{url}, line #{lineno}" )
|
47
|
+
errors_got += 1
|
48
|
+
|
49
|
+
if window.js_exception_url and errors_got <= 5
|
50
|
+
$.post( window.js_exception_url, js_error: { error: error, url: url, lineno: lineno, page_url: window.location.href } )
|
51
|
+
|
52
|
+
for own controller_name, controller of controllers
|
53
|
+
controller.destructor() if controller.options.failsafe is true
|
54
|
+
error
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#= require './base'
|
2
|
+
|
3
|
+
class Darwin.Template extends Darwin.Base
|
4
|
+
@_cached: {}
|
5
|
+
|
6
|
+
@options {
|
7
|
+
dependencies:
|
8
|
+
Mustache: window.Mustache
|
9
|
+
}
|
10
|
+
|
11
|
+
constructor: ( @template ) ->
|
12
|
+
super
|
13
|
+
|
14
|
+
@retrieve_template_from_dom() unless @retrieve_template_from_memory()
|
15
|
+
|
16
|
+
|
17
|
+
retrieve_template_from_memory: ->
|
18
|
+
if Templates?[ @template ]
|
19
|
+
@template = Templates[ @template ]
|
20
|
+
true
|
21
|
+
else
|
22
|
+
false
|
23
|
+
|
24
|
+
retrieve_template_from_dom: ->
|
25
|
+
name = @template.replace( '#', '' )
|
26
|
+
|
27
|
+
if Darwin.Template._cached[ name ]
|
28
|
+
@template = Darwin.Template._cached[ name ]
|
29
|
+
else
|
30
|
+
$template = $( "script##{name}_template[type=\"text/mustache\"]" )
|
31
|
+
|
32
|
+
unless $template.length
|
33
|
+
throw new Error( "can't find template #{name}" )
|
34
|
+
|
35
|
+
@template = $template.html()
|
36
|
+
Darwin.Template._cached[ name ] = @template
|
37
|
+
|
38
|
+
|
39
|
+
render: ( data ) ->
|
40
|
+
$( @render_to_string(data ) )
|
41
|
+
|
42
|
+
render_to_string: (data) ->
|
43
|
+
Mustache.render( @template, data )
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# Documentation is in /doc/view.md
|
2
|
+
#
|
3
|
+
class Darwin.View extends Darwin.Base
|
4
|
+
_cached: {}
|
5
|
+
|
6
|
+
constructor: ( @$root, options ) ->
|
7
|
+
super options
|
8
|
+
@_flatten_selectors()
|
9
|
+
@$root.find( '.fallback-submits' ).hide()
|
10
|
+
|
11
|
+
|
12
|
+
run: ->
|
13
|
+
|
14
|
+
|
15
|
+
get: ( selector_name ) ->
|
16
|
+
if @[ "get_#{selector_name}" ]
|
17
|
+
@[ "get_#{selector_name}" ]()
|
18
|
+
else
|
19
|
+
@[ "$#{selector_name}" ] or @_find_element( selector_name )
|
20
|
+
|
21
|
+
|
22
|
+
_destructor: ->
|
23
|
+
@$root.find( '.fallback-submits' ).show()
|
24
|
+
@destructor()
|
25
|
+
|
26
|
+
|
27
|
+
destructor: ->
|
28
|
+
|
29
|
+
|
30
|
+
clear_cache: ( selector_name ) ->
|
31
|
+
if selector_name
|
32
|
+
delete @[ "$#{selector_name}" ]
|
33
|
+
else
|
34
|
+
@clear_cache( selector_name ) for own selector_name, _ of @_cached
|
35
|
+
@_cached = {}
|
36
|
+
|
37
|
+
|
38
|
+
_find_element: ( selector_name ) ->
|
39
|
+
definition = @selectors[ selector_name ] or @_find_alternate_name( selector_name )
|
40
|
+
if definition
|
41
|
+
$base = if definition.within? then @get( definition.within ) else @$root
|
42
|
+
$found = $base.find definition.sel
|
43
|
+
|
44
|
+
unless definition.cache is false
|
45
|
+
@[ "$#{selector_name}" ] = $found
|
46
|
+
@_cached[ selector_name ] = true
|
47
|
+
|
48
|
+
$found
|
49
|
+
else
|
50
|
+
$element = @$root.find( "##{selector_name}" )
|
51
|
+
if $element.get(0)
|
52
|
+
@[ "$#{selector_name}" ] = $element
|
53
|
+
@_cached[ selector_name ] = true
|
54
|
+
$element
|
55
|
+
else
|
56
|
+
throw new Error "Selector not found : #{selector_name}"
|
57
|
+
|
58
|
+
|
59
|
+
_flatten_selectors: ->
|
60
|
+
selectors = {}
|
61
|
+
walk = ( values, name, parent_names ) ->
|
62
|
+
selector_key = ''
|
63
|
+
|
64
|
+
if name
|
65
|
+
if parent_names
|
66
|
+
selector_key += "#{parent_name.short}_" for parent_name in parent_names
|
67
|
+
|
68
|
+
selector_key += name
|
69
|
+
selectors[ selector_key ] = {}
|
70
|
+
|
71
|
+
if typeof values == 'string'
|
72
|
+
selectors[ selector_key ] = { sel: values }
|
73
|
+
else
|
74
|
+
|
75
|
+
for own attr, value of values
|
76
|
+
if $.inArray( attr, [ 'cache', 'sel', 'within' ] ) isnt -1
|
77
|
+
selectors[ selector_key ][ attr ] = value
|
78
|
+
else
|
79
|
+
parents = $.merge [], ( parent_names or [] )
|
80
|
+
parents.push({ short: name, long: selector_key }) if name
|
81
|
+
walk value, attr, parents
|
82
|
+
|
83
|
+
if name and not values.sel
|
84
|
+
selectors[ selector_key ].sel = "##{name}"
|
85
|
+
|
86
|
+
if parent_names and parent_names.length
|
87
|
+
selectors[ selector_key ].within = parent_names[ parent_names.length - 1 ].long
|
88
|
+
|
89
|
+
selectors[ selector_key ].alternate_name = name if name
|
90
|
+
|
91
|
+
walk @options.selectors
|
92
|
+
@selectors = selectors
|
93
|
+
|
94
|
+
|
95
|
+
_find_alternate_name: ( name ) ->
|
96
|
+
definitions = []
|
97
|
+
|
98
|
+
for selector, definition of @selectors
|
99
|
+
definitions.push( definition ) if definition.alternate_name == name
|
100
|
+
|
101
|
+
if definitions.length
|
102
|
+
if definitions.length > 1
|
103
|
+
throw new Error "Multiple definitions for #{name}"
|
104
|
+
else
|
105
|
+
definitions[0]
|
106
|
+
else
|
107
|
+
null
|
108
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#= require_self
|
2
|
+
#= require './darwin/base'
|
3
|
+
#= require './darwin/loader'
|
4
|
+
#= require './darwin/template'
|
5
|
+
#= require './darwin/view'
|
6
|
+
#= require './darwin/controller'
|
7
|
+
#
|
8
|
+
# Documentation is in /doc/introduction.md
|
9
|
+
#
|
10
|
+
|
11
|
+
window.Darwin = {}
|
12
|
+
|
13
|
+
window.App =
|
14
|
+
Views: {}
|
15
|
+
Controllers: {}
|
16
|
+
Helpers: {}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'darwinjs/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "darwinjs-rails"
|
8
|
+
spec.version = Darwinjs::Rails::VERSION
|
9
|
+
spec.authors = ["Olivier El Mekki"]
|
10
|
+
spec.email = ["olivier@el-mekki.com"]
|
11
|
+
spec.description = %q{Javascript framework with progressive enhancement in mind.}
|
12
|
+
spec.summary = %q{Darwin lets create complex javascript interfaces that do not expect they own the application and that degrade gracefully when an error occurs}
|
13
|
+
spec.homepage = "https://github.com/oelmekki/darwinjs-rails"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'railties', '~> 3.2'
|
22
|
+
spec.add_dependency 'coffee-rails', '~> 3.2'
|
23
|
+
spec.add_dependency 'jquery-rails', '~> 2.2'
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
end
|