luca 0.8.599 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -0
  3. data/CHANGELOG +51 -2
  4. data/README.md +10 -247
  5. data/ROADMAP +6 -2
  6. data/app.rb +16 -2
  7. data/assets/javascripts/dependencies/bootstrap.min.js +7 -1
  8. data/assets/javascripts/dependencies/codemirror-coffeescript.js +347 -0
  9. data/assets/javascripts/dependencies/codemirror-css.js +124 -0
  10. data/assets/javascripts/dependencies/codemirror-html.js +410 -0
  11. data/assets/javascripts/dependencies/codemirror-javascript.js +361 -0
  12. data/assets/javascripts/dependencies/codemirror-less.js +232 -0
  13. data/assets/javascripts/dependencies/codemirror-vim.js +500 -0
  14. data/assets/javascripts/dependencies/codemirror.js +3076 -0
  15. data/assets/javascripts/dependencies.coffee +0 -1
  16. data/assets/javascripts/luca-ui-base.coffee +10 -3
  17. data/assets/javascripts/luca-ui-bootstrap.js +1 -0
  18. data/assets/javascripts/luca-ui-development-tools.coffee +9 -0
  19. data/assets/javascripts/luca-ui.coffee +6 -1
  20. data/assets/javascripts/sandbox/application.coffee +51 -0
  21. data/assets/javascripts/sandbox/router.coffee +14 -0
  22. data/assets/javascripts/sandbox/templates/main.luca +33 -0
  23. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +1 -0
  24. data/assets/javascripts/sandbox/templates/sandbox.luca +1 -0
  25. data/assets/javascripts/sandbox/views/top_navigation.coffee +4 -0
  26. data/assets/javascripts/sandbox.coffee +2 -2
  27. data/assets/stylesheets/bootstrap.min.css +395 -297
  28. data/assets/stylesheets/codemirror-blackboard.css +25 -0
  29. data/assets/stylesheets/codemirror-monokai.css +33 -0
  30. data/assets/stylesheets/codemirror.css +126 -0
  31. data/assets/stylesheets/luca-ui-bootstrap.css +0 -1
  32. data/assets/stylesheets/luca-ui-development-tools.css +5 -0
  33. data/assets/stylesheets/sandbox/sandbox.scss +1 -3
  34. data/assets/stylesheets/themes/amelia-bootstrap.css +826 -0
  35. data/assets/stylesheets/themes/slate-bootstrap.css +797 -0
  36. data/assets/stylesheets/themes/superhero-bootstrap.css +830 -0
  37. data/lib/luca/code_browser.rb +55 -0
  38. data/lib/luca/rails/version.rb +1 -1
  39. data/lib/luca/rails.rb +1 -0
  40. data/spec/components/fields/checkbox_array_spec.coffee +46 -0
  41. data/spec/components/form_view_spec.coffee +10 -4
  42. data/spec/containers/card_view_spec.coffee +7 -0
  43. data/spec/core/collection_spec.coffee +58 -4
  44. data/spec/core/container_spec.coffee +6 -6
  45. data/spec/core/view_spec.coffee +93 -7
  46. data/spec/framework_spec.coffee +15 -12
  47. data/src/components/application.coffee +126 -18
  48. data/src/components/base_toolbar.coffee +2 -2
  49. data/src/components/collection_loader_view.coffee +1 -2
  50. data/src/components/collection_view.coffee +77 -0
  51. data/src/components/controller.coffee +1 -4
  52. data/src/components/fields/button_field.coffee +1 -1
  53. data/src/components/fields/checkbox_array.coffee +2 -2
  54. data/src/components/fields/checkbox_field.coffee +3 -1
  55. data/src/components/fields/file_upload_field.coffee +1 -1
  56. data/src/components/fields/hidden_field.coffee +1 -1
  57. data/src/components/fields/select_field.coffee +1 -1
  58. data/src/components/fields/text_area_field.coffee +1 -1
  59. data/src/components/fields/text_field.coffee +10 -6
  60. data/src/components/fields/type_ahead_field.coffee +18 -5
  61. data/src/components/form_button_toolbar.coffee +1 -2
  62. data/src/components/form_view.coffee +44 -62
  63. data/src/components/grid_view.coffee +27 -20
  64. data/src/components/load_mask.coffee +3 -0
  65. data/src/components/nav_bar.coffee +26 -0
  66. data/src/components/record_manager.coffee +1 -3
  67. data/src/components/router.coffee +1 -1
  68. data/src/components/template.coffee +3 -15
  69. data/src/components/toolbar_dialog.coffee +25 -0
  70. data/src/containers/card_view.coffee +22 -23
  71. data/src/containers/column_view.coffee +1 -6
  72. data/src/containers/modal_view.coffee +20 -71
  73. data/src/containers/panel_toolbar.coffee +156 -0
  74. data/src/containers/panel_view.coffee +1 -1
  75. data/src/containers/split_view.coffee +1 -3
  76. data/src/containers/tab_view.coffee +29 -29
  77. data/src/containers/viewport.coffee +38 -3
  78. data/src/core/collection.coffee +80 -48
  79. data/src/core/container.coffee +153 -72
  80. data/src/core/core.coffee +181 -0
  81. data/src/core/field.coffee +4 -2
  82. data/src/core/model.coffee +1 -1
  83. data/src/core/observer.coffee +3 -3
  84. data/src/core/panel.coffee +143 -0
  85. data/src/core/registry.coffee +104 -0
  86. data/src/core/util.coffee +82 -0
  87. data/src/core/view.coffee +158 -85
  88. data/src/framework.coffee +112 -178
  89. data/src/index.coffee +0 -255
  90. data/src/managers/collection_manager.coffee +1 -0
  91. data/src/samples/definition.coffee +49 -0
  92. data/src/stylesheets/base.scss +0 -78
  93. data/src/stylesheets/components/form_view.scss +8 -3
  94. data/src/stylesheets/components/grid_view.scss +3 -7
  95. data/src/stylesheets/components/load_mask.scss +14 -0
  96. data/src/stylesheets/components/toolbar.scss +0 -15
  97. data/src/stylesheets/containers/container.scss +14 -2
  98. data/src/stylesheets/containers/panels.scss +23 -0
  99. data/src/stylesheets/tools/class_browser.scss +32 -0
  100. data/src/stylesheets/tools/code_editor.scss +24 -0
  101. data/src/stylesheets/tools/component_tester.scss +8 -0
  102. data/src/stylesheets/tools/console.scss +26 -0
  103. data/src/templates/components/collection_loader_view.luca +1 -1
  104. data/src/templates/components/form_view.luca +2 -13
  105. data/src/templates/components/grid_view.luca +0 -2
  106. data/src/templates/components/load_mask.luca +3 -0
  107. data/src/templates/components/nav_bar.luca +2 -0
  108. data/src/templates/containers/tab_view.luca +1 -0
  109. data/src/templates/fields/text_field.luca +4 -1
  110. data/src/tools/class_browser.coffee +39 -0
  111. data/src/tools/code_editor.coffee +258 -0
  112. data/src/tools/code_mirror_field.coffee +57 -0
  113. data/src/tools/coffee_script_editor.coffee +60 -0
  114. data/src/tools/collection_inspector.coffee +4 -0
  115. data/src/tools/component_tester.coffee +472 -0
  116. data/src/tools/components/class_browser_detail.coffee +10 -0
  117. data/src/tools/components/class_browser_list.coffee +74 -0
  118. data/src/tools/console.coffee +147 -0
  119. data/src/tools/development_console.coffee +147 -0
  120. data/src/tools/models/components.coffee +63 -0
  121. data/src/tools/templates/component_tester/help.luca +14 -0
  122. data/vendor/assets/javascripts/luca-ui-base.js +1389 -611
  123. data/vendor/assets/javascripts/luca-ui-bootstrap.js +9 -0
  124. data/vendor/assets/javascripts/luca-ui-development-tools.js +18719 -0
  125. data/vendor/assets/javascripts/luca-ui-spec.js +2065 -878
  126. data/vendor/assets/javascripts/luca-ui.js +1759 -852
  127. data/vendor/assets/javascripts/luca-ui.min.js +3 -3
  128. data/vendor/assets/stylesheets/luca-ui-bootstrap.css +494 -440
  129. data/vendor/assets/stylesheets/luca-ui-development-tools.css +224 -0
  130. data/vendor/assets/stylesheets/luca-ui-spec.css +99 -140
  131. data/vendor/assets/stylesheets/luca-ui.css +99 -140
  132. data/views/index.erb +6 -3
  133. metadata +60 -18
  134. data/assets/javascripts/dependencies/jquery-console.js +0 -649
  135. data/assets/javascripts/development-console.coffee +0 -2
  136. data/assets/javascripts/sandbox/sandbox.coffee +0 -16
  137. data/assets/javascripts/sandbox/templates/features/collection_helpers.luca +0 -33
  138. data/assets/javascripts/sandbox/templates/features/form_demo_code.luca +0 -48
  139. data/assets/javascripts/sandbox/templates/features/grid_demo_code.luca +0 -24
  140. data/assets/javascripts/sandbox/templates/features/introduction.luca +0 -11
  141. data/assets/javascripts/sandbox/templates/features/view_helpers.luca +0 -43
  142. data/assets/javascripts/sandbox/templates/navigation.luca +0 -8
  143. data/assets/javascripts/sandbox/views/form_demo.coffee +0 -47
  144. data/assets/javascripts/sandbox/views/grid_demo.coffee +0 -23
  145. data/assets/javascripts/sandbox/views/pages/collection_events_sample.coffee +0 -1
  146. data/assets/javascripts/sandbox/views/pages/pages_controller.coffee +0 -38
  147. data/src/components/collection_inspector.coffee +0 -2
  148. data/src/components/development_console.coffee +0 -59
  149. data/src/stylesheets/components/development_console.scss +0 -47
@@ -1,39 +1,77 @@
1
- # Luca.Collection
2
- #
3
- # Luca.Collection is an extenstion of Backbone.Collection which provides
4
- # a bunch of commonly used patterns for doing things like:
5
- #
6
- # - setting base parameters used on every request to your REST API
7
- #
8
- # - bootstrapping a collection of objects which are
9
- # rendered in your markup on page load
10
- #
11
- # - filtering with query string parameters against your API
12
- #
13
- # - automatic interaction with your Luca.CollectionManager class
14
- #
15
- # - make it easier to parse Rails style responses which include the root
16
- # by specifying a @root parameter
17
- #
18
- # - use backbone-query if available
19
- #
20
- # - onceLoaded: run a callback once if there are models present, otherwise wait until
21
- # the collection fetches
22
- #
23
- # - ifLoaded: run a callback any time the model gets reset, or if there are already models
24
- #
25
- Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
1
+ source = 'Backbone.Collection'
2
+ source = 'Backbone.QueryCollection' if Backbone.QueryCollection?
3
+
4
+ _.def("Luca.Collection").extends( source ).with
5
+ # cachedMethods refers to a list of methods on the collection
6
+ # whose value gets cached once it is ran. the collection then
7
+ # binds to change, add, remove, and reset events and then expires
8
+ # the cached value once these events are fired.
9
+
10
+ # cachedMethods expects an array of strings representing the method name
11
+ # or objects containing @method and @resetEvents properties. by default
12
+ # @resetEvents are 'add','remove',reset' and 'change'.
13
+ cachedMethods: []
14
+
15
+ restoreMethodCache: ()->
16
+ for name, config of @_methodCache
17
+ if config.original?
18
+ config.args = undefined
19
+ @[ name ] = config.original
20
+
21
+ clearMethodCache: (method)->
22
+ @_methodCache[method].value = undefined
23
+
24
+ clearAllMethodsCache: ()->
25
+ for name, config of @_methodCache
26
+ @clearMethodCache(name)
27
+
28
+ setupMethodCaching: ()->
29
+ collection = @
30
+ membershipEvents = ["reset","add","remove"]
31
+ cache = @_methodCache = {}
32
+
33
+ _( @cachedMethods ).each (method)->
34
+ # store a reference to the unwrapped version of the method
35
+ # and a placeholder for the cached value
36
+ cache[ method ] =
37
+ name: method
38
+ original: collection[method]
39
+ value: undefined
40
+
41
+ # wrap the collection method with a basic memoize operation
42
+ collection[ method ] = ()->
43
+ cache[method].value ||= cache[method].original.apply collection, arguments
44
+
45
+ # bind to events on the collection, which once triggered, will
46
+ # invalidate the cached value. causing us to have to restore it
47
+ for membershipEvent in membershipEvents
48
+ collection.bind membershipEvent, ()->
49
+ collection.clearAllMethodsCache()
50
+
51
+ dependencies = method.split(':')[1]
52
+
53
+ if dependencies
54
+ for dependency in dependencies.split(",")
55
+ collection.bind "change:#{dependency}", ()->
56
+ collection.clearMethodCache(method: method)
26
57
 
27
58
  initialize: (models=[], @options)->
28
59
  _.extend @, @options
29
60
 
61
+ @setupMethodCaching()
62
+
30
63
  @_reset()
31
64
 
32
- # By specifying a @cached property or method, you can instruct
65
+ # By specifying a @cache_key property or method, you can instruct
33
66
  # Luca.Collection instances where to pull an array of model attributes
34
67
  # usually done with the bootstrap functionality provided.
68
+
69
+ # DEPRECATION NOTICE
35
70
  if @cached
36
- @bootstrap_cache_key = if _.isFunction( @cached ) then @cached() else @cached
71
+ console.log 'The @cached property of Luca.Collection is being deprecated. Please change to cache_key'
72
+
73
+ if @cache_key ||= @cached
74
+ @bootstrap_cache_key = if _.isFunction( @cache_key ) then @cache_key() else @cache_key
37
75
 
38
76
  if @registerAs or @registerWith
39
77
  console.log "This configuration API is deprecated. use @name and @manager properties instead"
@@ -42,7 +80,6 @@ Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
42
80
  @name ||= @registerAs
43
81
  @manager ||= @registerWith
44
82
 
45
-
46
83
  # if they specify a
47
84
  if @name and not @manager
48
85
  @manager = Luca.CollectionManager.get()
@@ -53,7 +90,7 @@ Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
53
90
  # to be scoped with some sort of unique id, as say some sort of belongsTo relationship
54
91
  # then you can specify @registerAs as a method()
55
92
  if @manager
56
- @name ||= @cached()
93
+ @name ||= @cache_key()
57
94
  @name = if _.isFunction( @name ) then @name() else @name
58
95
 
59
96
  unless @private or @anonymous
@@ -81,15 +118,13 @@ Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
81
118
 
82
119
  @__wrapUrl() unless @useNormalUrl is true
83
120
 
84
- Backbone.Collection.prototype.initialize.apply @, [models, @options]
121
+ Backbone.Collection::initialize.apply @, [models, @options]
85
122
 
86
123
  if models
87
124
  @reset models, silent: true, parse: options?.parse
88
125
 
89
126
  @trigger "after:initialize"
90
127
 
91
- #### Automatic Query String Generation
92
- #
93
128
  # Luca.Collections will append a query string to the URL
94
129
  # and will automatically do this for you without you having
95
130
  # to write a special url handler. If you want to use a normal
@@ -130,13 +165,16 @@ Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
130
165
  @base_params = Luca.Collection.baseParams()
131
166
  @
132
167
 
133
- # Applying a filter to a collection, will automatically apply
134
- # the filter parameters and then call fetch. passing an additional
135
- # options hash will pass these options to the call to @fetch()
136
- # setting refresh to true, forcing a remote call to the REST API
168
+ # if filtering a collection should handle via a call to a REST API
169
+ # and return the filtered results that way, then leave this true
170
+ remoteFilter: true
171
+
137
172
  applyFilter: (filter={}, options={})->
138
- @applyParams(filter)
139
- @fetch _.extend(options,refresh:true)
173
+ if options.remote? is true
174
+ @applyParams(filter)
175
+ @fetch _.extend(options,refresh:true)
176
+ else
177
+ @reset @query filter
140
178
 
141
179
  # You can apply params to a collection, so that any upcoming requests
142
180
  # made to the REST API are made with the key values specified
@@ -144,8 +182,6 @@ Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
144
182
  @base_params ||= _( Luca.Collection.baseParams() ).clone()
145
183
  _.extend @base_params, params
146
184
 
147
- # Collection Manager Registry
148
- #
149
185
  # If this collection is to be registered with some global collection
150
186
  # tracker such as new Luca.CollectionManager() then we will register
151
187
  # ourselves automatically
@@ -174,7 +210,7 @@ Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
174
210
 
175
211
  # A Luca.Collection will load models from the in memory model store
176
212
  # returned from Luca.Collection.cache, where the key returned from
177
- # the @cached attribute or method matches the key of the model cache
213
+ # the @cache_keyattribute or method matches the key of the model cache
178
214
  loadFromBootstrap: ()->
179
215
  return unless @bootstrap_cache_key
180
216
  @reset @cached_models()
@@ -186,7 +222,7 @@ Luca.Collection = (Backbone.QueryCollection || Backbone.Collection).extend
186
222
 
187
223
  # cached_models is a reference to the Luca.Collection.cache object
188
224
  # key'd on whatever this collection's bootstrap_cache_key is set to be
189
- # via the @cached() interface
225
+ # via the @cache_key() interface
190
226
  cached_models: ()->
191
227
  Luca.Collection.cache( @bootstrap_cache_key )
192
228
 
@@ -286,8 +322,6 @@ _.extend Luca.Collection.prototype,
286
322
 
287
323
  Backbone.View.prototype.trigger.apply @, arguments
288
324
 
289
- #### Base Parameters
290
- #
291
325
  # Always include these parameters in every request to your REST API.
292
326
  #
293
327
  # either specify a function which returns a hash, or just a normal hash
@@ -300,14 +334,12 @@ Luca.Collection.baseParams = (obj)->
300
334
  if _.isObject( Luca.Collection._baseParams )
301
335
  Luca.Collection._baseParams
302
336
 
303
- #### Bootstrapped Models ( stuff loaded on page load )
304
- #
305
337
  # In order to make our Backbone Apps super fast it is a good practice
306
338
  # to pre-populate your collections by what is referred to as bootstrapping
307
339
  #
308
340
  # Luca.Collections make it easier for you to do this cleanly and automatically
309
341
  #
310
- # by specifying a @cached property or method in your collection definition
342
+ # by specifying a @cache_keyproperty or method in your collection definition
311
343
  # Luca.Collections will automatically look in this space to find models
312
344
  # and avoid a roundtrip to your API unless explicitly told to.
313
345
  Luca.Collection._bootstrapped_models = {}
@@ -321,4 +353,4 @@ Luca.Collection.bootstrap = (obj)->
321
353
  # roundtrips to the API
322
354
  Luca.Collection.cache = (key, models)->
323
355
  return Luca.Collection._bootstrapped_models[ key ] = models if models
324
- Luca.Collection._bootstrapped_models[ key ] || []
356
+ Luca.Collection._bootstrapped_models[ key ] || []
@@ -1,14 +1,62 @@
1
- #### The Component Container
1
+ # The Component Container
2
2
  #
3
3
  # The Component Container is a nestable component
4
- # which is responsible for laying out the many components
5
- # it contains, assigning them to a DOM container, and
6
- # automatically instantiating and rendering the components
7
- # in their proper place.
8
- _.component('Luca.core.Container').extends('Luca.View').with
4
+ # which are responsible for handling communication between multiple
5
+ # nested views.
6
+ #
7
+ # One: Layout
8
+ #
9
+ # a container is responsible for laying out the nested views
10
+ # and rendering them in a special DOM element
11
+ doLayout = ()->
12
+ @trigger "before:layout", @
13
+ @prepareLayout()
14
+ @trigger "after:layout", @
15
+
16
+ # and displaying those elements in a way that is
17
+ # optimal for the desired user experience of that view
18
+ # ( i.e seeing only one of them at a time, seeing them side by side )
19
+ applyDOMConfig = (panel, panelIndex)->
20
+ style_declarations = []
21
+
22
+ style_declarations.push "height: #{ (if _.isNumber(panel.height) then panel.height + 'px' else panel.height ) }" if panel.height?
23
+ style_declarations.push "width: #{ (if _.isNumber(panel.width) then panel.width + 'px' else panel.width ) }" if panel.width?
24
+ style_declarations.push "float: #{ panel.float }" if panel.float
25
+
26
+ config =
27
+ class: panel?.classes || @componentClass
28
+ id: "#{ @cid }-#{ panelIndex }"
29
+ style: style_declarations.join(';')
30
+ "data-luca-owner" : @name || @cid
31
+
32
+ if @customizeContainerEl?
33
+ config = @customizeContainerEl( config, panel, panelIndex )
34
+
35
+ config
36
+
37
+ # Two: Component Creation
38
+ #
39
+ # A container is responsible for creating and storing references to the nested
40
+ # views that are required for its functioning.
41
+ doComponents = ()->
42
+
43
+ @trigger "before:components", @, @components
44
+ @prepareComponents()
45
+ @createComponents()
46
+ @trigger "before:render:components", @, @components
47
+ @renderComponents()
48
+ @trigger "after:components", @, @components
49
+
50
+
51
+ # Containers are central to Luca. They are what make it easy to structure
52
+ # your application in a logical way and to specify much of the behavior of
53
+ # complex / composite views at define time using JSON syntax combined with
54
+ # the meta data contained in the Luca component registry.
55
+ _.def('Luca.core.Container').extends('Luca.components.Panel').with
9
56
 
10
57
  className: 'luca-ui-container'
11
58
 
59
+ componentTag: 'div'
12
60
  componentClass: 'luca-ui-panel'
13
61
 
14
62
  isContainer: true
@@ -29,11 +77,18 @@ _.component('Luca.core.Container').extends('Luca.View').with
29
77
  initialize: (@options={})->
30
78
  _.extend @, @options
31
79
 
32
- @setupHooks( Luca.core.Container::hooks )
80
+ @setupHooks [
81
+ "before:components"
82
+ "before:render:components"
83
+ "before:layout"
84
+ "after:components"
85
+ "after:layout"
86
+ "first:activation"
87
+ ]
33
88
 
34
89
  Luca.View::initialize.apply @, arguments
35
90
 
36
- #### Rendering Pipeline
91
+ # Rendering Pipeline
37
92
  #
38
93
  # A container has nested components. these components
39
94
  # are automatically rendered inside their own DOM element
@@ -80,62 +135,52 @@ _.component('Luca.core.Container').extends('Luca.View').with
80
135
  # firstActivation()
81
136
  #
82
137
  beforeRender: ()->
83
- @debug "container before render"
84
- @doLayout()
85
- @doComponents()
86
-
87
- doLayout: ()->
88
- @debug "container do layout"
89
- @trigger "before:layout", @
90
- @prepareLayout()
91
- @trigger "after:layout", @
92
-
93
- doComponents: ()->
94
- @debug "container do components"
95
-
96
- @trigger "before:components", @, @components
97
- @prepareComponents()
98
- @createComponents()
99
- @trigger "before:render:components", @, @components
100
- @renderComponents()
101
- @trigger "after:components", @, @components
102
-
103
- applyPanelConfig: (panel, panelIndex)->
104
- style_declarations = []
105
-
106
- style_declarations.push "height: #{ (if _.isNumber(panel.height) then panel.height + 'px' else panel.height ) }" if panel.height
107
- style_declarations.push "width: #{ (if _.isNumber(panel.width) then panel.width + 'px' else panel.width ) }" if panel.width
108
- style_declarations.push "float: #{ panel.float }" if panel.float
109
-
110
- config =
111
- classes: panel?.classes || @componentClass
112
- id: "#{ @cid }-#{ panelIndex }"
113
- style: style_declarations.join(';')
114
-
115
- # prepare layout is where you would perform the DOM element
116
- # creation / manipulation for how your container lays out
117
- # its components. Minimally you will want to set the
118
- # container property on each component.
119
- prepareLayout: ()->
120
- @debug "container prepare layout"
121
- @componentContainers = _( @components ).map (component, index) =>
122
- @applyPanelConfig.apply @, [ component, index ]
138
+ doLayout.call(@)
139
+ doComponents.call(@)
140
+ Luca.components.Panel::beforeRender?.apply(@, arguments)
123
141
 
124
- if @appendContainers
125
- _( @componentContainers ).each (container)=>
126
- @$el.append Luca.templates["containers/basic"](container) unless container.appended?
127
- container.appended = true
142
+ # Components which inherit from Luca.core.Container can implement
143
+ # their own versions of this method, if they need to apply any sort
144
+ # of additional styling / configuration for the DOM elements that
145
+ # are created to wrap each container.
146
+ customizeContainerEl: (containerEl, panel, panelIndex)->
147
+ containerEl
128
148
 
129
- # prepare components is where you would set necessary object
130
- # attributes on the components themselves.
149
+ prepareLayout: ()->
150
+ container = @
151
+ @componentContainers = _( @components ).map (component, index)->
152
+ applyDOMConfig.call(container, component, index)
153
+
154
+ # prepare components is where each component gets assigned
155
+ # a container to be rendered into. if @appendContainers is
156
+ # set to true, then the view will automatically $append()
157
+ # elements created via Backbone.View::make() to the body element of the view
131
158
  prepareComponents: ()->
132
- @debug "container prepare components"
133
- @components = _( @components ).map (object, index) =>
134
- panel = @componentContainers[ index ]
135
- object.container = if @appendContainers then "##{ panel.id }" else @el
159
+ # accept components as an array of strings representing
160
+ # the luca component type
161
+ for component in @components when _.isString(component)
162
+ component = (type: component)
163
+
164
+ _( @components ).each (component, index)=>
165
+ container = @componentContainers?[index]
166
+
167
+ # support a variety of the bad naming conventions
168
+ container.class = container.class || container.className || container.classes
169
+
170
+ if @appendContainers
171
+ panel = @make(@componentTag, container, '')
172
+ @$append( panel )
136
173
 
137
- object
174
+ unless component.container?
175
+ component.container = "##{ container.id }" if @appendContainers
176
+ component.container ||= @$bodyEl()
138
177
 
178
+ # create components is responsible for turning the JSON syntax of the
179
+ # container's definition into live objects against a given Luca Component
180
+ # type.
181
+ #
182
+ # In addition to this, a container builds an index of the components
183
+ # which belong to it, so that they can easily be looked up by name
139
184
  createComponents: ()->
140
185
  return if @componentsCreated is true
141
186
 
@@ -144,17 +189,28 @@ _.component('Luca.core.Container').extends('Luca.View').with
144
189
  cid_index: {}
145
190
 
146
191
  @components = _( @components ).map (object, index)=>
192
+
147
193
  # you can include normal backbone views as components
148
194
  # you will want to make sure your render method handles
149
- # adding the views @$el to the appropriate @container
150
- component = if _.isObject( object ) and object.render and object.trigger
195
+ # adding the views @$el to the appropriate @container.
196
+
197
+ # you can also just pass a string representing the component_type
198
+ component = if Luca.isBackboneView( object )
151
199
  object
152
200
  else
153
- object.ctype ||= Luca.defaultComponentType || "template"
201
+ object.type ||= object.ctype
202
+
203
+ if !object.type?
204
+ if object.components?
205
+ object.type = object.ctype = 'container'
206
+ else
207
+ object.type = object.ctype = Luca.defaultComponentType
208
+
154
209
  Luca.util.lazyComponent( object )
155
210
 
156
211
  # if we're using base backbone views, then they don't extend themselves
157
- # with their passed options, so this is a workaround
212
+ # with their passed options, so this is a workaround to get them to
213
+ # pick up the container config property
158
214
  if !component.container and component.options.container
159
215
  component.container = component.options.container
160
216
 
@@ -167,6 +223,9 @@ _.component('Luca.core.Container').extends('Luca.View').with
167
223
  component
168
224
 
169
225
  @componentsCreated = true
226
+
227
+ @registerComponentEvents() unless _.isEmpty(@componentEvents)
228
+
170
229
  map
171
230
 
172
231
  # Trigger the Rendering Pipeline process on all of the nested
@@ -181,8 +240,11 @@ _.component('Luca.core.Container').extends('Luca.View').with
181
240
  component.render()
182
241
  catch e
183
242
  console.log "Error Rendering Component #{ component.name || component.cid }", component
184
- console.log e.message
185
- console.log e.stack
243
+
244
+ if _.isObject(e)
245
+ console.log e.message
246
+ console.log e.stack
247
+
186
248
  throw e unless Luca.silenceRenderErrors? is true
187
249
 
188
250
  #### Container Activation
@@ -195,13 +257,12 @@ _.component('Luca.core.Container').extends('Luca.View').with
195
257
  # will trigger first:activation on the components as they become
196
258
  # displayed.
197
259
  firstActivation: ()->
198
- _( @components ).each (component)=>
199
- activator = @
200
-
260
+ activator = @
261
+ @each (component, index)->
201
262
  # apply the first:activation trigger on the component, in the context of the component
202
263
  # passing as arguments the component itself, and the component doing the activation
203
264
  unless component?.previously_activated is true
204
- component?.trigger?.apply component, ["first:activation", [component, activator] ]
265
+ component?.trigger?.call component, "first:activation", component, activator
205
266
  component.previously_activated = true
206
267
 
207
268
  #### Component Finder Methods
@@ -219,6 +280,22 @@ _.component('Luca.core.Container').extends('Luca.View').with
219
280
 
220
281
  _.flatten( components )
221
282
 
283
+ # event binding sugar for nested components
284
+ #
285
+ # you can define events like:
286
+
287
+ # _.def("MyContainer").extends("Luca.View").with
288
+ # componentEvents:
289
+ # "component_name before:load" : "mySpecialHandler"
290
+ #
291
+ componentEvents: {}
292
+
293
+ registerComponentEvents: ()->
294
+ for listener, handler of @componentEvents
295
+ [componentName,trigger] = listener.split(' ')
296
+ component = @findComponentByName(componentName)
297
+ component?.bind trigger, @[handler]
298
+
222
299
  findComponentByName: (name, deep=false)->
223
300
  @findComponent(name, "name_index", deep)
224
301
 
@@ -238,12 +315,15 @@ _.component('Luca.core.Container').extends('Luca.View').with
238
315
  sub_container = _( @components ).detect (component)-> component?.findComponent?(needle, haystack, true)
239
316
  sub_container?.findComponent?(needle, haystack, true)
240
317
 
318
+ each: (fn)->
319
+ @eachComponent(fn, false)
320
+
241
321
  # run a function for each component in this container
242
322
  # and any nested containers in those components, recursively
243
323
  # pass false as the second argument to skip the deep recursion
244
324
  eachComponent: (fn, deep=true)->
245
- _( @components ).each (component)=>
246
- fn.apply component, [component]
325
+ _( @components ).each (component, index)=>
326
+ fn.call component, component, index
247
327
  component?.eachComponent?.apply component, [fn,deep] if deep
248
328
 
249
329
  indexOf: (name)->
@@ -254,7 +334,8 @@ _.component('Luca.core.Container').extends('Luca.View').with
254
334
  return @ unless @activeItem
255
335
  return @components[ @activeItem ]
256
336
 
257
- componentElements: ()-> $(".#{ @componentClass }", @el)
337
+ componentElements: ()->
338
+ $(">.#{ @componentClass }", @el)
258
339
 
259
340
  getComponent: (needle)->
260
341
  @components[ needle ]