luca 0.9.4 → 0.9.6

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.
Files changed (144) hide show
  1. data/CHANGELOG +41 -1
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +2 -0
  4. data/README.md +5 -0
  5. data/Rakefile +4 -0
  6. data/assets/javascripts/dependencies/underscore-min.js +5 -31
  7. data/assets/javascripts/luca-templates.js +1 -0
  8. data/assets/javascripts/luca-ui-base.coffee +1 -1
  9. data/assets/javascripts/luca-ui-development-tools.coffee +1 -1
  10. data/assets/javascripts/luca-ui-full.js +1 -1
  11. data/assets/javascripts/luca-ui-spec.coffee +1 -1
  12. data/assets/javascripts/luca-ui.js +3 -0
  13. data/assets/javascripts/luca/index.coffee +1 -0
  14. data/lib/generators/luca/application/application_generator.rb +71 -0
  15. data/lib/generators/luca/application/templates/controller.rb +6 -0
  16. data/lib/generators/luca/application/templates/index.html.erb +7 -0
  17. data/lib/generators/luca/application/templates/index.html.haml +6 -0
  18. data/lib/generators/luca/application/templates/javascripts/application.js +28 -0
  19. data/lib/generators/luca/application/templates/javascripts/application.js.coffee +20 -0
  20. data/lib/generators/luca/application/templates/javascripts/config.js +15 -0
  21. data/lib/generators/luca/application/templates/javascripts/config.js.coffee +9 -0
  22. data/lib/generators/luca/application/templates/javascripts/dependencies.js +5 -0
  23. data/lib/generators/luca/application/templates/javascripts/dependencies.js.coffee +5 -0
  24. data/lib/generators/luca/application/templates/javascripts/index.js +9 -0
  25. data/lib/generators/luca/application/templates/javascripts/index.js.coffee +9 -0
  26. data/lib/generators/luca/application/templates/javascripts/main.js +8 -0
  27. data/lib/generators/luca/application/templates/javascripts/main.js.coffee +3 -0
  28. data/lib/generators/luca/application/templates/javascripts/main.jst.ejs +1 -0
  29. data/lib/generators/luca/application/templates/javascripts/router.js +12 -0
  30. data/lib/generators/luca/application/templates/javascripts/router.js.coffee +7 -0
  31. data/lib/luca/rails/version.rb +1 -1
  32. data/lib/luca/template.rb +1 -1
  33. data/spec/components/collection_view_spec.coffee +37 -0
  34. data/spec/components/multi_collection_view_spec.coffee +5 -0
  35. data/spec/components/table_view_spec.coffee +17 -0
  36. data/spec/core/container_spec.coffee +112 -5
  37. data/spec/core/model_spec.coffee +21 -3
  38. data/spec/define_spec.coffee +19 -0
  39. data/spec/mixin_spec.coffee +49 -0
  40. data/src/components/application.coffee +33 -19
  41. data/src/components/collection_view.coffee +109 -38
  42. data/src/components/fields/checkbox_field.coffee +2 -2
  43. data/src/components/fields/file_upload_field.coffee +0 -3
  44. data/src/components/fields/hidden_field.coffee +0 -3
  45. data/src/components/fields/label_field.coffee +1 -4
  46. data/src/components/fields/select_field.coffee +6 -6
  47. data/src/components/fields/text_area_field.coffee +1 -0
  48. data/src/components/fields/text_field.coffee +4 -0
  49. data/src/components/fields/type_ahead_field.coffee +5 -9
  50. data/src/components/form_view.coffee +2 -0
  51. data/src/components/index.coffee +1 -0
  52. data/src/components/multi_collection_view.coffee +94 -0
  53. data/src/components/pagination_control.coffee +100 -0
  54. data/src/components/table_view.coffee +62 -0
  55. data/src/containers/card_view.coffee +44 -11
  56. data/src/containers/panel_toolbar.coffee +88 -82
  57. data/src/containers/tab_view.coffee +3 -3
  58. data/src/containers/viewport.coffee +10 -4
  59. data/src/core/collection.coffee +11 -4
  60. data/src/core/container.coffee +189 -113
  61. data/src/core/field.coffee +13 -10
  62. data/src/core/model.coffee +23 -27
  63. data/src/core/registry.coffee +48 -35
  64. data/src/core/view.coffee +60 -140
  65. data/src/define.coffee +91 -19
  66. data/src/framework.coffee +10 -8
  67. data/src/index.coffee +23 -0
  68. data/src/managers/collection_manager.coffee +24 -8
  69. data/src/modules/application_event_bindings.coffee +19 -0
  70. data/src/modules/collection_event_bindings.coffee +26 -0
  71. data/src/modules/deferrable.coffee +3 -1
  72. data/src/modules/dom_helpers.coffee +49 -0
  73. data/src/modules/enhanced_properties.coffee +23 -0
  74. data/src/modules/filterable.coffee +60 -0
  75. data/src/modules/grid_layout.coffee +15 -0
  76. data/src/modules/{load_mask.coffee → loadmaskable.coffee} +10 -4
  77. data/src/modules/modal_view.coffee +38 -0
  78. data/src/modules/paginatable.coffee +79 -0
  79. data/src/modules/state_model.coffee +16 -0
  80. data/src/modules/templating.coffee +8 -0
  81. data/src/plugins/events.coffee +30 -2
  82. data/src/templates/components/bootstrap_form_controls.jst.ejs +10 -0
  83. data/src/templates/components/collection_loader_view.jst.ejs +6 -0
  84. data/src/templates/components/form_alert.jst.ejs +4 -0
  85. data/src/templates/components/grid_view.jst.ejs +11 -0
  86. data/src/templates/components/grid_view_empty_text.jst.ejs +3 -0
  87. data/src/templates/components/load_mask.jst.ejs +5 -0
  88. data/src/templates/components/nav_bar.jst.ejs +4 -0
  89. data/src/templates/components/pagination.jst.ejs +10 -0
  90. data/src/templates/containers/basic.jst.ejs +1 -0
  91. data/src/templates/containers/tab_selector_container.jst.ejs +12 -0
  92. data/src/templates/containers/tab_view.jst.ejs +2 -0
  93. data/src/templates/containers/toolbar_wrapper.jst.ejs +1 -0
  94. data/src/templates/fields/button_field.jst.ejs +2 -0
  95. data/src/templates/fields/button_field_link.jst.ejs +6 -0
  96. data/src/templates/fields/checkbox_array.jst.ejs +4 -0
  97. data/src/templates/fields/checkbox_array_item.jst.ejs +3 -0
  98. data/src/templates/fields/checkbox_field.jst.ejs +10 -0
  99. data/src/templates/fields/file_upload_field.jst.ejs +10 -0
  100. data/src/templates/fields/hidden_field.jst.ejs +1 -0
  101. data/src/templates/fields/select_field.jst.ejs +11 -0
  102. data/src/templates/fields/text_area_field.jst.ejs +11 -0
  103. data/src/templates/fields/text_field.jst.ejs +16 -0
  104. data/src/templates/table_view.jst.ejs +4 -0
  105. data/src/tools/console.coffee +51 -21
  106. data/src/util.coffee +17 -4
  107. data/vendor/assets/javascripts/luca-ui-base.js +3288 -613
  108. data/vendor/assets/javascripts/luca-ui-development-tools.js +49 -21
  109. data/vendor/assets/javascripts/luca-ui-development-tools.min.js +1 -1
  110. data/vendor/assets/javascripts/luca-ui-full.js +1704 -554
  111. data/vendor/assets/javascripts/luca-ui-full.min.js +7 -6
  112. data/vendor/assets/javascripts/luca-ui-spec.js +1783 -830
  113. data/vendor/assets/javascripts/luca-ui-templates.js +92 -0
  114. data/vendor/assets/javascripts/luca-ui.js +1694 -523
  115. data/vendor/assets/javascripts/luca-ui.min.js +4 -4
  116. metadata +69 -31
  117. data/assets/javascripts/luca-ui.coffee +0 -3
  118. data/src/luca.coffee +0 -22
  119. data/src/templates/components/bootstrap_form_controls.luca +0 -7
  120. data/src/templates/components/collection_loader_view.luca +0 -5
  121. data/src/templates/components/form_alert +0 -0
  122. data/src/templates/components/form_alert.luca +0 -3
  123. data/src/templates/components/grid_view.luca +0 -7
  124. data/src/templates/components/grid_view_empty_text.luca +0 -3
  125. data/src/templates/components/load_mask.luca +0 -3
  126. data/src/templates/components/nav_bar.luca +0 -2
  127. data/src/templates/containers/basic.luca +0 -1
  128. data/src/templates/containers/tab_selector_container.luca +0 -8
  129. data/src/templates/containers/tab_view.luca +0 -2
  130. data/src/templates/containers/toolbar_wrapper.luca +0 -1
  131. data/src/templates/fields/button_field.luca +0 -2
  132. data/src/templates/fields/button_field_link.luca +0 -5
  133. data/src/templates/fields/checkbox_array.luca +0 -4
  134. data/src/templates/fields/checkbox_array_item.luca +0 -4
  135. data/src/templates/fields/checkbox_field.luca +0 -9
  136. data/src/templates/fields/file_upload_field.luca +0 -8
  137. data/src/templates/fields/hidden_field.luca +0 -1
  138. data/src/templates/fields/select_field.luca +0 -8
  139. data/src/templates/fields/text_area_field.luca +0 -8
  140. data/src/templates/fields/text_field.luca +0 -17
  141. data/src/templates/sample/contents.luca +0 -1
  142. data/src/templates/sample/welcome.luca +0 -1
  143. data/vendor/assets/javascripts/luca-spec-dependencies.js +0 -6135
  144. data/vendor/assets/javascripts/luca-ui-development-dependencies.js +0 -12845
@@ -1,26 +1,95 @@
1
+ panelToolbar = Luca.register "Luca.components.PanelToolbar"
2
+ # The Panel Toolbar is a collection of buttons and / or dropdowns
3
+ # which are automatically created by BasicPanel classes, or can be
4
+ # added to any other view component.
5
+ panelToolbar.extends "Luca.View"
6
+
7
+
8
+ panelToolbar.defines
9
+ # @buttons is an array of button config objects
10
+ # button config accepts the following paramters:
11
+ #
12
+ # label what should the button say
13
+ # eventId what event should the button trigger
14
+ # dropdown an array of arrays: [eventId, label]
15
+ # group an array of button configs
16
+ # wrapper a css class, in addition to btn-group
17
+ # icon which icon do you want to use on this button?
18
+ # white true or false: is it a white colored text?
19
+ # color options are primary, info, success, warning, danger, inverse
20
+ buttons:[]
21
+
22
+ className: "luca-ui-toolbar btn-toolbar"
23
+
24
+ well: true
25
+
26
+ orientation: 'top'
27
+
28
+ autoBindEventHandlers: true
29
+
30
+ events:
31
+ "click a.btn, click .dropdown-menu li" : "clickHandler"
32
+
33
+ initialize: (@options={})->
34
+ @_super("initialize", @, arguments)
35
+
36
+ # if the toolbar consists of a single button group
37
+ # don't make the developer specify buttons = {buttons:[group:true, buttons:[...]]}
38
+ if @group is true and @buttons?.length >= 0
39
+ @buttons = [
40
+ group: true
41
+ buttons: @buttons
42
+ ]
43
+
44
+ # The Toolbar behaves by triggering events on the components which they
45
+ # belong to. Combined with Luca.View::setupHooks it is a clean way
46
+ # to organize actions
47
+ clickHandler: (e)->
48
+ me = my = $( e.target )
49
+ me = my = $( e.target ).parent() if me.is('i')
50
+
51
+ if @selectable is true
52
+ my.siblings().removeClass("is-selected")
53
+ me.addClass('is-selected')
54
+
55
+ return unless eventId = my.data('eventid')
56
+
57
+ hook = Luca.util.hook( eventId )
58
+
59
+ source = @parent || @
60
+ if _.isFunction( source[hook] )
61
+ source[ hook ].call(@, me, e)
62
+ else
63
+ source.trigger(eventId, me, e)
64
+
65
+ beforeRender:()->
66
+ @_super("beforeRender", @, arguments)
67
+
68
+ if @well is true
69
+ @$el.addClass 'well'
70
+
71
+ @$el.addClass 'btn-selectable' if @selectable is true
72
+ @$el.addClass "toolbar-#{ @orientation }"
73
+ @$el.addClass "pull-right" if @align is "right"
74
+ @$el.addClass "pull-left" if @align is "left"
75
+
76
+ render: ()->
77
+ @$el.empty()
78
+ @$el.append( element ) for element in prepareButtons(@buttons)
79
+ @
1
80
 
2
- # button config accepts the following paramters:
3
- #
4
- # label what should the button say
5
- # eventId what event should the button trigger
6
- # dropdown an array of arrays: [eventId, label]
7
- # group an array of button configs
8
- # wrapper a css class, in addition to btn-group
9
- # icon which icon do you want to use on this button?
10
- # white true or false: is it a white colored text?
11
- # color options are primary, info, success, warning, danger, inverse
12
81
 
13
82
  make = Backbone.View::make
14
83
 
15
84
  buildButton = (config, wrap=true)->
16
- if config.ctype?
85
+ if config.ctype? or config.type?
17
86
  config.className ||= ""
18
87
  config.className += 'toolbar-component'
19
88
 
20
89
  object = Luca(config).render()
90
+
21
91
  if Luca.isBackboneView(object)
22
- console.log "Adding toolbar component", object
23
- return object.el
92
+ return object.$el
24
93
 
25
94
  if config.spacer
26
95
  return make "div", class: "spacer #{ config.spacer }"
@@ -29,8 +98,9 @@ buildButton = (config, wrap=true)->
29
98
  return make "div", {class: "toolbar-text"}, config.text
30
99
 
31
100
  wrapper = 'btn-group'
32
- wrapper += " #{ config.wrapper }" if config.wrapper?
33
- wrapper += " align-#{ config.align }" if config.align?
101
+ wrapper += "#{ config.wrapper }" if config.wrapper?
102
+ wrapper += "pull-#{ config.align } align-#{ config.align }" if config.align?
103
+ wrapper += 'btn-selectable' if config.selectable is true
34
104
 
35
105
  # if we're passed a group, then we need to just
36
106
  # wrap the contents of the buttons property in that group
@@ -57,6 +127,7 @@ buildButton = (config, wrap=true)->
57
127
  title: config.title || config.description
58
128
 
59
129
  buttonAttributes["class"] += " btn-#{ config.color }" if config.color?
130
+ buttonAttributes["class"] += " is-selected" if config.selected?
60
131
 
61
132
  if config.dropdown
62
133
  label = "#{ label } <span class='caret'></span>"
@@ -82,70 +153,5 @@ buildButton = (config, wrap=true)->
82
153
  # for buttons which are already part f a group
83
154
  buttonEl
84
155
 
85
- prepareButtons = (buttons, wrap=true)->
86
- _( buttons ).map (button)->
87
- buildButton(button, wrap)
88
-
89
-
90
- #### Panel Toolbar Component
91
- #
92
- # The Panel Toolbar is a collection of buttons and / or dropdowns
93
- # which are automatically created by BasicPanel classes, or can be
94
- # added to any other view component.
95
- _.def("Luca.containers.PanelToolbar").extends("Luca.View").with
96
-
97
- className: "luca-ui-toolbar btn-toolbar"
98
-
99
- # @buttons is an array of button config objects
100
-
101
-
102
- buttons:[]
103
-
104
- well: true
105
-
106
- orientation: 'top'
107
-
108
- autoBindEventHandlers: true
109
-
110
- events:
111
- "click a.btn, click .dropdown-menu li" : "clickHandler"
112
-
113
- #autoBindEventHandlers: true
114
-
115
- # The Toolbar behaves by triggering events on the components which they
116
- # belong to. Combined with Luca.View::setupHooks it is a clean way
117
- # to organize actions
118
- clickHandler: (e)->
119
- me = my = $( e.target )
120
-
121
- if me.is('i')
122
- me = my = $( e.target ).parent()
123
-
124
- eventId = my.data('eventid')
125
-
126
- return unless eventId?
127
-
128
- hook = Luca.util.hook( eventId )
129
-
130
- source = @parent || @
131
- if _.isFunction( source[hook] )
132
- source[ hook ].call(@, me, e)
133
- else
134
- source.trigger(eventId, me, e)
135
-
136
- beforeRender:()->
137
- @_super("beforeRender", @, arguments)
138
-
139
- if @well is true
140
- @$el.addClass 'well'
141
-
142
- @$el.addClass "toolbar-#{ @orientation }"
143
-
144
- @applyStyles( @styles ) if @styles?
145
-
146
- render: ()->
147
- @$el.empty()
148
-
149
- elements = prepareButtons(@buttons)
150
- _( elements ).each (element)=>
151
- @$el.append( element )
156
+ prepareButtons = (buttons=[], wrap=true)->
157
+ buildButton(button, wrap) for button in buttons
@@ -42,17 +42,17 @@ _.def('Luca.containers.TabView').extends('Luca.containers.CardView').with
42
42
 
43
43
  afterRender: ()->
44
44
  Luca.containers.CardView::afterRender?.apply @, arguments
45
- @registerEvent("click ##{ @cid }-tabs-selector li a", "select")
45
+ tabContainerId = @tabContainer().attr("id")
46
+ @registerEvent("click ##{ tabContainerId } li a", "select")
46
47
 
47
48
  if Luca.enableBootstrap and (@tab_position is "left" or @tab_position is "right")
48
49
  @tabContainerWrapper().addClass("span2")
49
50
  @tabContentWrapper().addClass("span9")
50
51
 
51
-
52
52
  createTabSelectors: ()->
53
53
  tabView = @
54
54
  @each (component,index)->
55
- icon = "<i class='icon-#{ component.tabIcon }" if component.tabIcon
55
+ icon = "<i class='icon-#{ component.tabIcon }'></i>" if component.tabIcon
56
56
  link = "<a href='#'>#{ icon || ''} #{ component.title }</a>"
57
57
  selector = tabView.make("li",{class:"tab-selector","data-target":index}, link)
58
58
  tabView.tabContainer().append(selector)
@@ -19,11 +19,17 @@ _.def('Luca.containers.Viewport').extend('Luca.containers.CardView').with
19
19
 
20
20
  @enableFullscreen() if @fullscreen is true
21
21
 
22
- enableFluid: ()->
23
- @$el.parent().addClass( @wrapperClass )
22
+ enableFluid: ()-> @enableWrapper()
24
23
 
25
- disableFluid: ()->
26
- @$el.parent().removeClass( @wrapperClass )
24
+ disableFluid: ()-> @disableWrapper()
25
+
26
+ enableWrapper: ()->
27
+ if @wrapperClass?
28
+ @$el.parent().addClass( @wrapperClass )
29
+
30
+ disableWrapper: ()->
31
+ if @wrapperClass?
32
+ @$el.parent().removeClass( @wrapperClass )
27
33
 
28
34
  enableFullscreen: ()->
29
35
  $('html,body').addClass('luca-ui-fullscreen')
@@ -1,8 +1,14 @@
1
- source = 'Backbone.Collection'
2
- source = 'Backbone.QueryCollection' if Backbone.QueryCollection?
1
+ collection = Luca.define 'Luca.Collection'
3
2
 
4
- _.def("Luca.Collection").extends( source ).with
5
- include: ['Luca.Events']
3
+ if Backbone.QueryCollection?
4
+ collection.extends 'Backbone.QueryCollection'
5
+ else
6
+ collection.extends 'Backbone.Collection'
7
+
8
+ collection.includes 'Luca.Events'
9
+
10
+ collection.defines
11
+ model: Luca.Model
6
12
  # cachedMethods refers to a list of methods on the collection
7
13
  # whose value gets cached once it is ran. the collection then
8
14
  # binds to change, add, remove, and reset events and then expires
@@ -325,6 +331,7 @@ _.def("Luca.Collection").extends( source ).with
325
331
  collection.bind "change:#{dependency}", ()->
326
332
  collection.clearMethodCache(method: method)
327
333
 
334
+
328
335
  # make sure the querying interface from backbone.query is present
329
336
  # in the case backbone-query isn't loaded. without it, it will
330
337
  # just return the models
@@ -1,90 +1,53 @@
1
- # The Component Container
2
- #
3
- # The Component Container is a nestable component
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
1
+ container = Luca.register "Luca.core.Container"
49
2
 
3
+ container.extends "Luca.components.Panel"
50
4
 
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
5
+ container.triggers "before:components",
6
+ "before:render:components",
7
+ "before:layout",
8
+ "after:components",
9
+ "after:layout",
10
+ "first:activation"
56
11
 
12
+ container.defines
57
13
  className: 'luca-ui-container'
58
14
 
59
15
  componentTag: 'div'
16
+
60
17
  componentClass: 'luca-ui-panel'
61
18
 
62
19
  isContainer: true
63
20
 
64
- hooks:[
65
- "before:components"
66
- "before:render:components"
67
- "before:layout"
68
- "after:components"
69
- "after:layout"
70
- "first:activation"
71
- ]
72
-
73
21
  rendered: false
74
22
 
75
23
  components: []
76
24
 
25
+ # @componentEvents provides declarative syntax for responding to events on
26
+ # the components in this container. the format of the syntax is very similar
27
+ # to the other event binding helpers:
28
+ #
29
+ # component_accessor component:trigger
30
+ #
31
+ # where component_accessor is either the name of the role, or a method on the container
32
+ # which will find the component in question.
33
+ #
34
+ # myContainer = new Luca.core.Container
35
+ # componentEvents:
36
+ # "name component:trigger" : "handler"
37
+ # "role component:trigger" : "handler"
38
+ # "getter component:trigger" : "handler"
39
+ #
40
+ componentEvents: {}
41
+
77
42
  initialize: (@options={})->
78
43
  _.extend @, @options
79
44
 
80
- @setupHooks [
81
- "before:components"
82
- "before:render:components"
83
- "before:layout"
84
- "after:components"
85
- "after:layout"
86
- "first:activation"
87
- ]
45
+ @setupHooks( Luca.core.Container::hooks )
46
+
47
+ # aliases for the components property
48
+ @components ||= @fields ||= @pages ||= @cards ||= @views
49
+
50
+ validateContainerConfiguration(@)
88
51
 
89
52
  Luca.View::initialize.apply @, arguments
90
53
 
@@ -183,6 +146,9 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
183
146
  map = @componentIndex =
184
147
  name_index: {}
185
148
  cid_index: {}
149
+ role_index: {}
150
+
151
+ container = @
186
152
 
187
153
  @components = _( @components ).map (object, index)=>
188
154
 
@@ -202,13 +168,12 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
202
168
  else
203
169
  object.type = object.ctype = Luca.defaultComponentType
204
170
 
205
- Luca.util.lazyComponent( object )
171
+ # if the container defines a @defaults property
172
+ # then we should make sure our child components inherit
173
+ # these values unless specifically defined
174
+ object = _.defaults(object, (container.defaults || {}))
206
175
 
207
- # if you define a @getter property as a string on your component
208
- # we will create a function with that name on this container that
209
- # allows you to access this component
210
- if _.isString( component.getter )
211
- @[ component.getter ] = (()-> component)
176
+ created = Luca.util.lazyComponent( object )
212
177
 
213
178
  # if we're using base backbone views, then they don't extend themselves
214
179
  # with their passed options, so this is a workaround to get them to
@@ -216,18 +181,12 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
216
181
  if !component.container and component.options.container
217
182
  component.container = component.options.container
218
183
 
219
- if map and component.cid?
220
- map.cid_index[ component.cid ] = index
221
-
222
- if map and component.name?
223
- map.name_index[ component.name ] = index
184
+ indexComponent( component ).at(index).in( @componentIndex )
224
185
 
225
186
  component
226
187
 
227
188
  @componentsCreated = true
228
189
 
229
- @registerComponentEvents() unless _.isEmpty(@componentEvents)
230
-
231
190
  map
232
191
 
233
192
  # Trigger the Rendering Pipeline process on all of the nested
@@ -271,32 +230,74 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
271
230
  component.previously_activated = true
272
231
 
273
232
  #### Underscore Methods For Working with Components
233
+ _: ()-> _( @components )
234
+
274
235
  pluck: (attribute)->
275
- _( @components ).pluck attribute
236
+ @_().pluck(attribute)
276
237
 
277
238
  invoke: (method)->
278
- _( @components ).invoke method
239
+ @_().invoke(method)
279
240
 
280
- map: (fn)->
281
- _( @components ).map(fn)
282
-
283
- # event binding sugar for nested components
284
- #
285
- # you can define events like:
241
+ select: (fn)->
242
+ @_().select(fn)
286
243
 
287
- # _.def("MyContainer").extends("Luca.View").with
288
- # componentEvents:
289
- # "component_name before:load" : "mySpecialHandler"
290
- componentEvents: {}
244
+ detect: (fn)->
245
+ @_().detect(attribute)
246
+
247
+ reject: (fn)->
248
+ @_().reject(fn)
249
+
250
+ map: (fn)->
251
+ @_().map(fn)
291
252
 
292
253
  registerComponentEvents: ()->
293
- for listener, handler of @componentEvents
294
- [componentName,trigger] = listener.split(' ')
295
- component = @findComponentByName(componentName)
296
- component?.bind trigger, @[handler]
254
+ container = @
255
+
256
+ for listener, handler of (@componentEvents||{})
257
+ [componentNameOrRole,eventId] = listener.split(' ')
258
+
259
+ unless _.isFunction( @[handler] )
260
+ console.log "Error registering component event", listener, componentNameOrRole, eventId
261
+ throw "Invalid component event definition #{ listener }. Specified handler is not a method on the container"
262
+
263
+ if componentNameOrRole is "*"
264
+ @eachComponent (component)=> component.on(eventId, @[handler], container)
265
+ else
266
+ component = @findComponentForEventBinding( componentNameOrRole )
267
+
268
+ unless component? and Luca.isComponent(component)
269
+ console.log "Error registering component event", listener, componentNameOrRole, eventId
270
+ throw "Invalid component event definition: #{ componentNameOrRole }"
271
+
272
+ component?.bind eventId, @[handler], container
273
+
274
+
275
+ subContainers: ()->
276
+ @select (component)->
277
+ component.isContainer is true
278
+
279
+ roles: ()->
280
+ _( @allChildren() ).pluck('role')
281
+
282
+ allChildren: ()->
283
+ children = @components
284
+ grandchildren = _( @subContainers() ).invoke('allChildren')
285
+ @_allChildren ||= _([children,grandchildren]).chain().compact().flatten().uniq().value()
286
+
287
+ findComponentForEventBinding: (nameRoleOrGetter, deep=false)->
288
+ @findComponentByName(nameRoleOrGetter, deep) || @findComponentByGetter( nameRoleOrGetter, deep ) || @findComponentByRole( nameRoleOrGetter, deep )
289
+
290
+ findComponentByGetter: (getter, deep=false)->
291
+ _( @allChildren() ).detect (component)->
292
+ component.getter is getter
293
+
294
+ findComponentByRole: (role,deep=false)->
295
+ _( @allChildren() ).detect (component)->
296
+ component.role is role
297
297
 
298
298
  findComponentByName: (name, deep=false)->
299
- @findComponent(name, "name_index", deep)
299
+ _( @allChildren() ).detect (component)->
300
+ component.name is name
300
301
 
301
302
  findComponentById: (id, deep=false)->
302
303
  @findComponent(id, "cid_index", deep)
@@ -305,12 +306,14 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
305
306
  @createComponents() unless @componentsCreated is true
306
307
 
307
308
  position = @componentIndex?[ haystack ][ needle ]
308
- component = @components?[ position ]
309
+ component = @components[ position ]
309
310
 
310
311
  return component if component
311
312
 
312
313
  if deep is true
313
- sub_container = _( @components ).detect (component)-> component?.findComponent?(needle, haystack, true)
314
+ sub_container = _( @components ).detect (component)->
315
+ component?.findComponent?(needle, haystack, true)
316
+
314
317
  sub_container?.findComponent?(needle, haystack, true)
315
318
 
316
319
  each: (fn)->
@@ -333,27 +336,24 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
333
336
  return @components[ @activeItem ]
334
337
 
335
338
  componentElements: ()->
336
- @$(">.#{ @componentClass }", @$bodyEl())
339
+ @$("[data-luca-parent='#{ @name || @cid }']")
337
340
 
338
341
  getComponent: (needle)->
339
342
  @components[ needle ]
340
343
 
341
- rootComponent: ()->
342
- console.log "Calling rootComponent will be deprecated. use isRootComponent instead"
343
- !@getParent?
344
-
345
344
  isRootComponent:()->
346
345
  !@getParent?
347
346
 
348
347
  getRootComponent: ()->
349
- if @rootComponent() then @ else @getParent().getRootComponent()
348
+ if @isRootComponent() then @ else @getParent().getRootComponent()
350
349
 
351
- selectByAttribute: (attribute, value, deep=false)->
350
+
351
+ selectByAttribute: (attribute, value=undefined, deep=false)->
352
352
  components = _( @components ).map (component)->
353
353
  matches = []
354
354
  test = component[ attribute ]
355
355
 
356
- matches.push( component ) if test is value
356
+ matches.push( component ) if test is value or (not value? and test?)
357
357
 
358
358
  # recursively traverse our components
359
359
  matches.push component.selectByAttribute?(attribute, value, true) if deep is true
@@ -362,9 +362,6 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
362
362
 
363
363
  _.flatten( components )
364
364
 
365
- select: (attribute, value, deep=false)->
366
- console.log "Container.select will be replaced by selectByAttribute in 1.0"
367
- Luca.core.Container::selectByAttribute.apply(@, arguments)
368
365
 
369
366
  # This is the method by which a container injects the rendered child views
370
367
  # into the DOM. It will get passed the container object, and the component
@@ -372,3 +369,82 @@ _.def('Luca.core.Container').extends('Luca.components.Panel').with
372
369
  Luca.core.Container.componentRenderer = (container, component)->
373
370
  attachMethod = $( component.container )[ component.attachWith || "append" ]
374
371
  attachMethod( component.render().el )
372
+
373
+
374
+ #### Private Helpers
375
+
376
+ doLayout = ()->
377
+ @trigger "before:layout", @
378
+ @prepareLayout()
379
+ @trigger "after:layout", @
380
+
381
+ applyDOMConfig = (panel, panelIndex)->
382
+ style_declarations = []
383
+
384
+ style_declarations.push "height: #{ (if _.isNumber(panel.height) then panel.height + 'px' else panel.height ) }" if panel.height?
385
+ style_declarations.push "width: #{ (if _.isNumber(panel.width) then panel.width + 'px' else panel.width ) }" if panel.width?
386
+ style_declarations.push "float: #{ panel.float }" if panel.float
387
+
388
+ config =
389
+ class: panel?.classes || @componentClass
390
+ id: "#{ @cid }-#{ panelIndex }"
391
+ style: style_declarations.join(';')
392
+ "data-luca-parent" : @name || @cid
393
+
394
+ if @customizeContainerEl?
395
+ config = @customizeContainerEl( config, panel, panelIndex )
396
+
397
+ config
398
+
399
+
400
+
401
+ createGetterMethods = ()->
402
+ container = @
403
+
404
+ childrenWithGetter = _( @allChildren() ).select (component)->
405
+ component.getter?
406
+
407
+ _( childrenWithGetter ).each (component)->
408
+ container[ component.getter ] ||= ()->
409
+ console.log "getter is being deprecated in favor of role"
410
+ console.log component.getter, component, container
411
+ component
412
+
413
+ createMethodsToGetComponentsByRole = ()->
414
+ container = @
415
+
416
+ childrenWithRole = _( @allChildren() ).select (component)->
417
+ component.role?
418
+
419
+ _( childrenWithRole ).each (component)->
420
+ getter = _.str.camelize( "get_" + component.role )
421
+ container[ getter ] ||= ()->
422
+ component
423
+
424
+ doComponents = ()->
425
+ @trigger "before:components", @, @components
426
+ @prepareComponents()
427
+ @createComponents()
428
+ @trigger "before:render:components", @, @components
429
+ @renderComponents()
430
+ @trigger "after:components", @, @components
431
+ createGetterMethods.call(@)
432
+ createMethodsToGetComponentsByRole.call(@)
433
+ @registerComponentEvents()
434
+
435
+ validateContainerConfiguration = ()->
436
+ true
437
+
438
+
439
+ # Private Helpers
440
+ #
441
+ # indexComponent( component ).at( index ).in( componentsInternalIndexMap )
442
+ indexComponent = (component)->
443
+ at: (index)->
444
+ in: (map)->
445
+ if component.cid?
446
+ map.cid_index[ component.cid ] = index
447
+ if component.role?
448
+ map.role_index[ component.role ] = index
449
+ if component.name?
450
+ map.name_index[ component.name ] = index