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
@@ -0,0 +1,37 @@
1
+ describe 'The Collection View', ->
2
+ beforeEach ->
3
+ @collection = new Luca.Collection([
4
+ id: 1, attr: "value_one"
5
+ ,
6
+ id: 2, attr: "value_two"
7
+ ],
8
+ model: Luca.Model)
9
+
10
+ @view = new Luca.components.CollectionView
11
+ itemTagName: "li"
12
+ itemClassName: "custom-class"
13
+ itemProperty: 'attr'
14
+ collection: @collection
15
+
16
+ @view.render()
17
+
18
+ it "should render the attributes in the specified list elements", ->
19
+ expect( @view.$html() ).toContain('value_one')
20
+
21
+ it "should render each of the attributes", ->
22
+ expect( @view.$('li.custom-class').length ).toEqual 2
23
+
24
+ it "should locate a dom element by luca model id", ->
25
+ expect( @view.locateItemElement(2).html() ).toContain('value_two')
26
+
27
+ it "should refresh the view when a model is added", ->
28
+ @view.collection.add(attr:"value_three",id:3)
29
+ expect( @view ).toHaveTriggered('after:refresh')
30
+
31
+ it "should refresh the view when a model is removed", ->
32
+ @view.collection.remove( @view.collection.at(0) )
33
+ expect( @view ).toHaveTriggered('after:refresh')
34
+
35
+
36
+
37
+
@@ -0,0 +1,5 @@
1
+ describe 'The Collection Multi View component', ->
2
+ it "should share a single collection among multiple collection views", ->
3
+ it "should toggle visibility of one or more views", ->
4
+ it "should share pagination state across the multiple views", ->
5
+ it "should share filter state across the multiple views", ->
@@ -0,0 +1,17 @@
1
+ describe 'The Table View', ->
2
+ beforeEach ->
3
+ @tableView = new Luca.components.TableView
4
+ collection: new Luca.Collection
5
+ columns:[
6
+ "column_one"
7
+ "column_two"
8
+ ]
9
+
10
+ $('body').append( @tableView.render() )
11
+
12
+ it 'should accept strings for column config', ->
13
+ expect( @tableView.columns[0].reader ).toEqual("column_one")
14
+
15
+ it 'should automatically determine a missing header config', ->
16
+ expect( @tableView.columns[0].header ).toBeDefined()
17
+
@@ -1,13 +1,18 @@
1
1
  describe 'The Luca Container', ->
2
2
  beforeEach ->
3
- @container = new Luca.core.Container
3
+ c = @container = new Luca.core.Container
4
+ defaults:
5
+ defaultProperty: 'it_works'
4
6
  components:[
5
7
  name: "component_one"
6
8
  ctype: "view"
9
+ defaultProperty: "oh_yeah"
7
10
  bodyTemplate: ()-> "markup for component one"
8
11
  id: "c1"
9
12
  value: 1
13
+ getter: "getOne"
10
14
  spy: sinon.spy()
15
+ role: "role_one"
11
16
  ,
12
17
  name: "component_two"
13
18
  ctype: "view"
@@ -15,6 +20,8 @@ describe 'The Luca Container', ->
15
20
  id: "c2"
16
21
  value: 0
17
22
  spy: sinon.spy()
23
+ role: "role_two"
24
+ getter: "getComponentTwo"
18
25
  ,
19
26
  name: "component_three"
20
27
  ctype: "container"
@@ -29,6 +36,23 @@ describe 'The Luca Container', ->
29
36
  ]
30
37
  ]
31
38
 
39
+ @container.render()
40
+
41
+ it "should create getter methods on the for components with roles", ->
42
+ expect( @container.getRoleTwo ).toBeDefined()
43
+
44
+ it "should create getter methods on the for components with roles", ->
45
+ expect( @container.getRoleTwo().name ).toEqual 'component_two'
46
+
47
+ it "should create a getter function on the container", ->
48
+ expect( @container.getOne().name ).toEqual 'component_one'
49
+
50
+ it "should apply default properties to components", ->
51
+ defaults = @container.selectByAttribute('defaultProperty','it_works')
52
+ custom = @container.selectByAttribute('defaultProperty','oh_yeah')
53
+ expect( defaults.length ).toEqual(2)
54
+ expect( custom.length ).toEqual(1)
55
+
32
56
  it "should trigger after initialize", ->
33
57
  expect( @container ).toHaveTriggered "after:initialize"
34
58
 
@@ -47,7 +71,7 @@ describe 'The Luca Container', ->
47
71
  expect( html ).toContain "markup for component four"
48
72
 
49
73
  it "should select all components matching a key/value combo", ->
50
- components = @container.select("value",1)
74
+ components = @container.selectByAttribute("value",1)
51
75
  expect( components.length ).toEqual 2
52
76
 
53
77
  it "should run a function on each component", ->
@@ -61,6 +85,89 @@ describe 'The Luca Container', ->
61
85
  @container.eachComponent (c)-> c.spy()
62
86
  expect( Luca.cache("component_four").spy ).toHaveBeenCalled()
63
87
 
64
- it "should be able to find a component by name", ->
65
- expect( @container.findComponentByName("component_one") ).toBeDefined()
66
- expect( @container.findComponentByName("undefined") ).not.toBeDefined()
88
+
89
+ describe 'Component Event Binding', ->
90
+ beforeEach ->
91
+ @container = new Luca.core.Container
92
+ componentEvents:
93
+ "component_alpha trigger:one" : "one"
94
+ "alpha trigger:two" : "two"
95
+ "getAlphaComponent trigger:three" : "three"
96
+ "* trigger:four" : "four"
97
+ "beta trigger:five" : "five"
98
+
99
+ one: ()->
100
+ @trigger "one"
101
+
102
+ two: ()->
103
+ @trigger "two"
104
+
105
+ three: ()->
106
+ @trigger "three"
107
+
108
+ four: ()->
109
+ @trigger "four"
110
+
111
+ five: ()->
112
+ @trigger "five"
113
+
114
+ registerComponentEvents: ()->
115
+ Luca.core.Container::registerComponentEvents.apply(@, arguments)
116
+
117
+ components:[
118
+ name: "component_alpha"
119
+ role: "alpha"
120
+ getter: "getAlphaComponent"
121
+ ,
122
+ name: "container_tester"
123
+ type: "container"
124
+ components:[
125
+ name: "beta_view"
126
+ role: "beta"
127
+ ]
128
+ ]
129
+
130
+ @container.render()
131
+
132
+ it "should give me all of the components", ->
133
+ names = _( @container.allChildren() ).pluck('name')
134
+ expect( names ).toEqual ['component_alpha','container_tester','beta_view']
135
+
136
+ it "should drill down into nested components", ->
137
+ expect( @container.getBeta ).toBeDefined()
138
+ expect( @container.getBeta().name ).toEqual 'beta_view'
139
+
140
+ it "should pick up events on nested components", ->
141
+ @container.getBeta().trigger("trigger:five")
142
+ expect( @container ).toHaveTriggered("five")
143
+
144
+ it "should define a role based getter", ->
145
+ expect( @container.getAlpha ).toBeDefined()
146
+
147
+ it "should define a getter", ->
148
+ expect( @container.getAlphaComponent ).toBeDefined()
149
+
150
+ it "should find the component by its role", ->
151
+ expect( @container.findComponentByRole("alpha") ).toBeDefined()
152
+
153
+ it "should find the component by its name", ->
154
+ expect( @container.findComponentByName('component_alpha') ).toBeDefined()
155
+
156
+ it "should find the component by its getter", ->
157
+ expect( @container.findComponentByGetter('getAlphaComponent') ).toBeDefined()
158
+
159
+ it "should accept wildcard for component", ->
160
+ @container.getAlphaComponent().trigger "trigger:four"
161
+ expect( @container ).toHaveTriggered("four")
162
+
163
+ it "should accept component events with a component name", ->
164
+ @container.getAlphaComponent().trigger "trigger:one"
165
+ expect(@container).toHaveTriggered("one")
166
+
167
+ it "should accept component events with a component role", ->
168
+ @container.getAlphaComponent().trigger "trigger:two"
169
+ expect(@container).toHaveTriggered("two")
170
+
171
+ it "should accept component events with a component getter", ->
172
+ @container.getAlphaComponent().trigger "trigger:three"
173
+ expect(@container).toHaveTriggered("three")
@@ -14,15 +14,15 @@ describe "Luca.Model with computed attribute", ->
14
14
  computed:
15
15
  fullName: ['firstName', 'lastName']
16
16
 
17
- it "should have it undefined if dependences are not set", ->
17
+ it "should be undefined if dependences are not set", ->
18
18
  model = new App.models.Sample
19
19
  expect(model.get("fullName")).toEqual(undefined)
20
20
 
21
- it "should have it undefined if callback function is not present", ->
21
+ it "should be undefined if callback function is not present", ->
22
22
  model = new App.models.SampleWithoutCallback
23
23
  expect(model.get("fullName")).toEqual(undefined)
24
24
 
25
- it "should not call it's callback if dependences are not set", ->
25
+ it "should not call the callback if dependences are not set", ->
26
26
  model = new App.models.Sample
27
27
  spy = sinon.spy(model, "fullName")
28
28
  expect( spy.called ).toEqual(false)
@@ -48,3 +48,21 @@ describe "Luca.Model with computed attribute", ->
48
48
  it "should have it set on constructor if dependencies are supplied", ->
49
49
  model = new App.models.Sample({firstName:"Nickolay", lastName: "Schwarz"})
50
50
  expect(model.get("fullName")).toEqual('Nickolay Schwarz')
51
+
52
+
53
+ describe 'The Read Method', ->
54
+
55
+ ModelClass = Luca.Model.extend
56
+ defaults:
57
+ attribute: "attribute"
58
+ reader: ()->
59
+ "reader"
60
+
61
+ it "should read an attribute", ->
62
+ model = new ModelClass()
63
+ expect( model.read('attribute') ).toEqual "attribute"
64
+
65
+ it "should read functions", ->
66
+ model = new ModelClass()
67
+ expect( model.read('attribute') ).toEqual "attribute"
68
+ expect( model.read('reader') ).toEqual 'reader'
@@ -0,0 +1,19 @@
1
+ describe 'The Component Definition System', ->
2
+ beforeEach ->
3
+ Luca.components.SampleComponentDefinition = undefined
4
+
5
+ it "should define a component", ->
6
+ Luca.register("Luca.components.SampleComponentDefinition").defines(version: 1)
7
+ expect( Luca.isComponentPrototype(Luca.components.SampleComponentDefinition) ).toEqual true
8
+
9
+ it "should default to Luca.View for the extends portion", ->
10
+ Luca.register("Luca.components.SampleComponentDefinition").defines(version: 1)
11
+ expect( Luca.parentClasses(Luca.components.SampleComponentDefinition) ).toContain 'Luca.View'
12
+
13
+ it "should track the parent classes", ->
14
+ Luca.register("Luca.components.SampleComponentDefinition").defines(version: 1)
15
+ expect( Luca.parentClasses(Luca.components.SampleComponentDefinition) ).toEqual ['Luca.View','Backbone.View']
16
+
17
+ xit "should defer component extension until dependencies are defined", ->
18
+
19
+
@@ -0,0 +1,49 @@
1
+ describe 'The Mixin System', ->
2
+
3
+ window.Luca ||= {}
4
+
5
+ Luca.mixin.namespace 'Luca.test_modules'
6
+
7
+ Luca.test_modules =
8
+ SecondMixin:
9
+ __included: ()->
10
+ window.secondMixinIncluded = true
11
+ __initializer: ()->
12
+ @trigger "second:mixin"
13
+ FirstMixin:
14
+ __initializer: ()->
15
+ @trigger "first:mixin"
16
+ __privateMethod: ()->
17
+ true
18
+ publicMethod: ()->
19
+ true
20
+
21
+ sampleView = Luca.register('Luca.components.FirstView')
22
+
23
+ sampleView.mixesIn 'FirstMixin'
24
+
25
+ sampleView.defines
26
+ sampleMethod: ()->
27
+ "sample"
28
+
29
+ secondView = Luca.register("Luca.components.SecondView")
30
+ secondView.extends 'Luca.components.FirstView'
31
+ secondView.mixesIn 'SecondMixin'
32
+ secondView.defines
33
+ version: 2
34
+
35
+ it "should omit the private methods defined on the mixin", ->
36
+ sampleView = new Luca.components.FirstView
37
+ expect( sampleView.__privateMethod ).not.toBeDefined()
38
+
39
+ it "should extend the prototype with the mixins normal methods", ->
40
+ sampleView = new Luca.components.FirstView
41
+ expect( sampleView.publicMethod ).toBeDefined()
42
+
43
+ it "should call the initializer for that module on the instance", ->
44
+ secondView = new Luca.components.SecondView
45
+ expect( secondView ).toHaveTriggered("second:mixin")
46
+
47
+ it "should call the initializers up the prototype chain", ->
48
+ secondView = new Luca.components.SecondView
49
+ expect( secondView ).toHaveTriggered("first:mixin")
@@ -113,7 +113,7 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
113
113
  # method which delegates to the controller, and allows you to navigate
114
114
  # to a given page, or component, by its name. The controller integrates
115
115
  # with the state machine of the application
116
- @setupMainController()
116
+ @setupMainController() if @useController is true
117
117
 
118
118
  # The Collection Manager is responsible
119
119
  @setupCollectionManager()
@@ -253,7 +253,8 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
253
253
  # any time the card switches on one of the sub controllers
254
254
  # then we should track the active sub section on the global state chart
255
255
  @getMainController()?.each (component)=>
256
- if component.ctype.match(/controller$/)
256
+ type = component.type || component.type
257
+ if type.match(/controller$/)
257
258
  component.bind "after:card:switch", (previous,current)=>
258
259
  @state.set(active_sub_section:current.name)
259
260
  app.trigger "sub:page:change"
@@ -263,7 +264,7 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
263
264
  definedComponents = @components || []
264
265
 
265
266
  @components = [
266
- ctype: 'controller'
267
+ type: 'controller'
267
268
  name: "main_controller"
268
269
  components: definedComponents
269
270
  ]
@@ -271,23 +272,36 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
271
272
  @defer( @setupControllerBindings, false ).until("after:components")
272
273
 
273
274
  setupCollectionManager: ()->
274
- if @useCollectionManager is true
275
- @collectionManagerClass = Luca.util.resolve( @collectionManagerClass ) if _.isString( @collectionManagerClass )
275
+ return unless @useCollectionManager is true
276
276
 
277
- collectionManagerOptions = @collectionManagerOptions
278
-
279
- if _.isObject(@collectionManager) and not _.isFunction( @collectionManager?.get )
280
- collectionManagerOptions = @collectionManager
281
- @collectionManager = undefined
282
-
283
- if _.isString(@collectionManager)
284
- collectionManagerOptions =
285
- name: @collectionManager
286
-
287
- @collectionManager = Luca.CollectionManager.get?( collectionManagerOptions.name )
288
-
289
- unless _.isFunction(@collectionManager?.get)
290
- @collectionManager = new @collectionManagerClass( collectionManagerOptions )
277
+ return if @collectionManager? and @collectionManager?.get?
278
+
279
+ if _.isString( @collectionManagerClass )
280
+ @collectionManagerClass = Luca.util.resolve( @collectionManagerClass )
281
+
282
+ collectionManagerOptions = @collectionManagerOptions || {}
283
+
284
+ # if the collectionManager property is present, and it
285
+ # isn't a reference to a collection manager instance, then
286
+ # it is being used as a configuration hash for when we do create
287
+ # the collection manager. so let's stash it.
288
+ if _.isObject(@collectionManager) and not _.isFunction( @collectionManager?.get )
289
+ collectionManagerOptions = @collectionManager
290
+ @collectionManager = undefined
291
+
292
+ # if the collection manager property is a string, then it is a
293
+ # reference to a name of a collection manager to use. so let's
294
+ # stash it
295
+ if _.isString(@collectionManager)
296
+ collectionManagerOptions =
297
+ name: @collectionManager
298
+
299
+ # let's try and get the collection manager by name if we can
300
+ @collectionManager = Luca.CollectionManager.get?( collectionManagerOptions.name )
301
+
302
+ # if we can't, then we will have to create one ourselves
303
+ unless _.isFunction(@collectionManager?.get)
304
+ @collectionManager = new @collectionManagerClass( collectionManagerOptions )
291
305
 
292
306
  setupRouter: ()->
293
307
  app = @
@@ -1,15 +1,33 @@
1
- # Public: The CollectionView renders a collection of models into a list of items.
1
+ collectionView = Luca.define "Luca.components.CollectionView"
2
+ # The CollectionView facilitates the rendering of a Collection
3
+ # of models into a group of many rendered templates
2
4
  #
3
- # Examples
5
+ # Example:
4
6
  #
5
- # _.def('App.ListView').extends('Luca.components.CollectionView').with
6
- # collection: new App.SampleCollection()
7
- # itemTemplate: "list_view_item"
8
- # loadMask: true
7
+ # new Luca.components.CollectionView
8
+ # itemTemplate: "template_name"
9
+ # collection: "collection_class_name"
10
+ # pagination:
11
+ # page: 1
12
+ # limit: 15
13
+ # filterable:
14
+ # query:
15
+ # default: 'value'
9
16
  #
17
+ collectionView.extends "Luca.components.Panel"
10
18
 
11
- _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
12
- tagName: "div"
19
+ collectionView.behavesAs "LoadMaskable",
20
+ "Filterable",
21
+ "Paginatable"
22
+
23
+ collectionView.triggers "before:refresh",
24
+ "after:refresh",
25
+ "refresh",
26
+ "empty:results"
27
+
28
+ collectionView.defaults
29
+
30
+ tagName: "ol"
13
31
 
14
32
  className: "luca-ui-collection-view"
15
33
 
@@ -25,16 +43,13 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
25
43
 
26
44
  itemClassName: 'collection-item'
27
45
 
28
- hooks:[
29
- "empty:results"
30
- ]
31
-
32
46
  initialize: (@options={})->
33
47
  _.extend(@, @options)
34
48
 
35
49
  _.bindAll @, "refresh"
36
50
 
37
51
  unless @collection? or @options.collection
52
+ console.log "Error on initialize of collection view", @
38
53
  throw "Collection Views must specify a collection"
39
54
 
40
55
  unless @itemTemplate? || @itemRenderer? || @itemProperty?
@@ -45,56 +60,111 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
45
60
  if _.isString(@collection) and Luca.CollectionManager.get()
46
61
  @collection = Luca.CollectionManager.get().getOrCreate(@collection)
47
62
 
48
- if Luca.isBackboneCollection(@collection)
63
+ unless Luca.isBackboneCollection(@collection)
64
+ throw "Collection Views must have a valid backbone collection"
65
+
49
66
  @collection.on "before:fetch", ()=>
50
- @trigger "enable:loadmask" if @loadMask is true
67
+ @trigger "enable:loadmask"
51
68
 
52
69
  @collection.bind "reset", ()=>
53
- @trigger "disable:loadmask" if @loadMask is true
54
70
  @refresh()
71
+ @trigger "disable:loadmask"
55
72
 
56
- @collection.bind "add", @refresh
57
- @collection.bind "remove", @refresh
58
- else
59
- throw "Collection Views must have a valid backbone collection"
73
+ @collection.bind "remove", ()=>
74
+ @refresh()
75
+
76
+ @collection.bind "add", ()=>
77
+ @refresh()
78
+
79
+ if @observeChanges is true
80
+ @collection.on "change", @refreshModel, @
60
81
 
61
- if @collection.length > 0
62
- @refresh()
82
+ unless @autoRefreshOnModelsPresent is false
83
+ @defer ()=>
84
+ @refresh() if @collection.length > 0
85
+ .until("after:render")
63
86
 
64
- attributesForItem: (item)->
65
- _.extend {}, class: @itemClassName, "data-index": item.index
87
+ @on "collection:change", @refresh, @
88
+
89
+ attributesForItem: (item, model)->
90
+ _.extend {}, class: @itemClassName, "data-index": item.index, "data-model-id": item.model.get('id')
66
91
 
67
92
  contentForItem: (item={})->
68
93
  if @itemTemplate? and templateFn = Luca.template(@itemTemplate)
69
- content = templateFn.call(@, item)
94
+ return content = templateFn.call(@, item)
70
95
 
71
96
  if @itemRenderer? and _.isFunction( @itemRenderer )
72
- content = @itemRenderer.call(@, item, item.model, item.index)
97
+ return content = @itemRenderer.call(@, item, item.model, item.index)
73
98
 
74
- if @itemProperty
75
- content = item.model.get(@itemProperty) || item.model[ @itemProperty ]
76
- content = content() if _.isFunction(content)
99
+ if @itemProperty and item.model?
100
+ return content = item.model.read( @itemProperty )
77
101
 
78
- content
102
+ ""
79
103
 
80
104
  makeItem: (model, index)->
81
105
  item = if @prepareItem? then @prepareItem.call(@, model, index) else (model:model, index: index)
82
- make(@itemTagName, @attributesForItem(item), @contentForItem(item))
83
-
84
- getModels: ()->
85
- if @collection?.query and (@filter || @filterOptions)
86
- @collection.query(@filter, @filterOptions)
106
+ attributes = @attributesForItem(item, model)
107
+ content = @contentForItem(item)
108
+ # TEMP
109
+ # Figure out why calls to make are failing with an unexpected string error
110
+ try
111
+ make(@itemTagName, attributes, content)
112
+ catch e
113
+ console.log "Error generating DOM element for CollectionView", @, model, index
114
+ #no op
115
+
116
+ getCollection: ()->
117
+ @collection
118
+
119
+ # Private: returns the query that is applied to the underlying collection.
120
+ # accepts the same options as Luca.Collection.query's initial query option.
121
+ getQuery: ()->
122
+ @query ||= {}
123
+
124
+ # Private: returns the query that is applied to the underlying collection.
125
+ # accepts the same options as Luca.Collection.query's initial query option.
126
+ getQueryOptions: ()->
127
+ @queryOptions ||= {}
128
+
129
+ # Private: returns the models to be rendered. If the underlying collection
130
+ # responds to @query() then it will use that interface.
131
+ getModels: (query,options)->
132
+ if @collection?.query
133
+ query ||= @getQuery()
134
+ options ||= @getQueryOptions()
135
+
136
+ @collection.query(query, options)
87
137
  else
88
138
  @collection.models
89
139
 
90
- refresh: ()->
140
+ locateItemElement: (id)->
141
+ @$(".#{ @itemClassName }[data-model-id='#{ id }']")
142
+
143
+ refreshModel: (model)->
144
+ index = @collection.indexOf( model )
145
+ @locateItemElement(model.get('id')).empty().append( @contentForItem({model,index}, model) )
146
+ @trigger("model:refreshed", index, model)
147
+
148
+ refresh: (query,options)->
149
+ query ||= @getQuery()
150
+ options ||= @getQueryOptions()
151
+
91
152
  @$bodyEl().empty()
153
+ models = @getModels(query, options)
154
+
155
+ @trigger("before:refresh", models, query, options)
92
156
 
93
- if @getModels().length is 0
157
+ if models.length is 0
94
158
  @trigger("empty:results")
95
159
 
96
- _( @getModels() ).each (model, index)=>
97
- @$append( @makeItem(model, index) )
160
+ index = 0
161
+
162
+ for model in models
163
+ @$append @makeItem(model, index++)
164
+
165
+ @trigger("after:refresh", models, query, options)
166
+
167
+ @
98
168
 
99
169
  registerEvent: (domEvent, selector, handler)->
100
170
  if !handler? and _.isFunction(selector)
@@ -111,4 +181,5 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
111
181
 
112
182
  # Private Helpers
113
183
 
184
+
114
185
  make = Luca.View::make