luca 0.9.4 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
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