luca 0.9.2 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/.gitignore +1 -0
  2. data/.rvmrc +1 -1
  3. data/CHANGELOG +46 -2
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +2 -2
  6. data/Guardfile +1 -1
  7. data/README.md +64 -27
  8. data/ROADMAP +17 -2
  9. data/Rakefile +49 -1
  10. data/app.rb +38 -2
  11. data/assets/javascripts/luca-ui-base.coffee +1 -20
  12. data/assets/javascripts/luca-ui-full.js +3 -0
  13. data/assets/javascripts/luca-ui.coffee +0 -5
  14. data/assets/javascripts/sandbox/application.coffee +24 -18
  15. data/assets/javascripts/sandbox/router.coffee +16 -6
  16. data/assets/javascripts/sandbox/templates/builder/component_list.luca +1 -0
  17. data/assets/javascripts/sandbox/templates/builder.luca +2 -0
  18. data/assets/javascripts/sandbox/templates/main.luca +4 -3
  19. data/assets/javascripts/sandbox/templates/sandbox/docs_index.luca +1 -0
  20. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +6 -1
  21. data/assets/javascripts/sandbox/templates/sandbox/readme.luca +30 -0
  22. data/assets/javascripts/sandbox/views/builder/builder_canvas.coffee +3 -0
  23. data/assets/javascripts/sandbox/views/builder/builder_editor.coffee +6 -0
  24. data/assets/javascripts/sandbox/views/builder/component_list.coffee +38 -0
  25. data/assets/javascripts/sandbox/views/builder/project_browser.coffee +14 -0
  26. data/assets/javascripts/sandbox/views/builder.coffee +133 -0
  27. data/assets/javascripts/sandbox/views/docs_controller.coffee +7 -0
  28. data/assets/javascripts/sandbox/views/inspector/instance_filter.coffee +18 -0
  29. data/assets/javascripts/sandbox/{collections/sample.coffee → views/inspector/instance_list.coffee} +0 -0
  30. data/assets/javascripts/sandbox/views/inspector.coffee +11 -0
  31. data/assets/javascripts/sandbox.coffee +2 -0
  32. data/assets/stylesheets/luca-ui-full.css +3 -0
  33. data/assets/stylesheets/sandbox/builder.scss +79 -0
  34. data/assets/stylesheets/sandbox/sandbox.scss +2 -1
  35. data/docs/application.md +41 -0
  36. data/docs/collection.md +79 -0
  37. data/docs/collection_manager.md +76 -0
  38. data/docs/container_philosophy.md +122 -0
  39. data/docs/event_binding_helpers.md +164 -0
  40. data/docs/method_caching_and_computed_properties.md +77 -0
  41. data/docs/view.md +119 -0
  42. data/lib/luca/rails/version.rb +1 -1
  43. data/lib/luca/template.rb +9 -9
  44. data/site/assets/bootstrap.min.js +7 -0
  45. data/site/assets/luca-ui-bootstrap.css +19 -1
  46. data/site/assets/luca-ui-development-tools.css +10 -0
  47. data/site/assets/luca-ui-development-tools.min.js +15 -0
  48. data/site/assets/luca-ui-full.min.js +8 -0
  49. data/site/assets/luca-ui.min.js +4 -0
  50. data/site/assets/sandbox.css +52 -4
  51. data/site/assets/sandbox.js +368 -30
  52. data/site/docs/application.html +41 -0
  53. data/site/docs/caching.html +43 -0
  54. data/site/docs/collection.html +75 -0
  55. data/site/docs/collection_manager.html +71 -0
  56. data/site/docs/containers.html +118 -0
  57. data/site/docs/events.html +153 -0
  58. data/site/docs/view.html +128 -0
  59. data/site/img/glyphicons-halflings-white.png +0 -0
  60. data/site/img/glyphicons-halflings.png +0 -0
  61. data/site/source-map.js +1 -0
  62. data/spec/core/view_spec.coffee +5 -17
  63. data/spec/managers/collection_manager_spec.coffee +4 -7
  64. data/src/components/application.coffee +202 -77
  65. data/src/components/base_toolbar.coffee +1 -1
  66. data/src/components/collection_view.coffee +38 -10
  67. data/src/components/controller.coffee +24 -1
  68. data/src/components/fields/checkbox_field.coffee +9 -12
  69. data/src/components/fields/label_field.coffee +14 -0
  70. data/src/components/fields/select_field.coffee +2 -2
  71. data/src/components/fields/text_field.coffee +12 -7
  72. data/src/components/fields/type_ahead_field.coffee +1 -0
  73. data/src/components/form_view.coffee +44 -25
  74. data/src/components/page_controller.coffee +2 -0
  75. data/src/containers/card_view.coffee +4 -1
  76. data/src/containers/column_view.coffee +2 -1
  77. data/src/containers/modal_view.coffee +6 -2
  78. data/src/containers/page_view.coffee +2 -0
  79. data/src/containers/panel_toolbar.coffee +0 -5
  80. data/src/containers/viewport.coffee +28 -10
  81. data/src/core/collection.coffee +7 -1
  82. data/src/core/container.coffee +57 -30
  83. data/src/core/core.coffee +0 -186
  84. data/src/core/field.coffee +11 -3
  85. data/src/core/model.coffee +31 -16
  86. data/src/core/panel.coffee +6 -46
  87. data/src/core/registry.coffee +19 -2
  88. data/src/core/script_loader.coffee +32 -0
  89. data/src/core/view.coffee +112 -139
  90. data/src/define.coffee +110 -0
  91. data/src/framework.coffee +8 -2
  92. data/src/luca.coffee +22 -0
  93. data/src/managers/collection_manager.coffee +65 -31
  94. data/src/modules/load_mask.coffee +47 -0
  95. data/src/plugins/development_tool_helpers.coffee +21 -0
  96. data/src/plugins/events.coffee +54 -0
  97. data/src/stylesheets/components/viewport.scss +15 -0
  98. data/src/stylesheets/containers/container.scss +1 -4
  99. data/src/stylesheets/tools/component_tester.scss +18 -0
  100. data/src/templates/fields/select_field.luca +6 -5
  101. data/src/templates/fields/text_field.luca +10 -9
  102. data/src/tools/application_inspector.coffee +2 -0
  103. data/src/tools/coffee_script_editor.coffee +28 -6
  104. data/src/tools/collections/components.coffee +59 -0
  105. data/src/tools/collections/instances.coffee +15 -0
  106. data/src/tools/component_tester.coffee +12 -22
  107. data/src/tools/console.coffee +22 -4
  108. data/src/tools/models/components.coffee +16 -54
  109. data/src/tools/models/instance.coffee +2 -0
  110. data/src/{core/util.coffee → util.coffee} +10 -1
  111. data/vendor/assets/javascripts/luca-ui-base.js +132 -137
  112. data/vendor/assets/javascripts/luca-ui-development-tools.js +191 -219
  113. data/vendor/assets/javascripts/luca-ui-development-tools.min.js +2 -2
  114. data/vendor/assets/javascripts/luca-ui-full.js +4680 -0
  115. data/vendor/assets/javascripts/luca-ui-full.min.js +8 -0
  116. data/vendor/assets/javascripts/luca-ui-spec.js +291 -225
  117. data/vendor/assets/javascripts/luca-ui.js +1001 -724
  118. data/vendor/assets/javascripts/luca-ui.min.js +4 -4
  119. data/vendor/assets/stylesheets/luca-ui-bootstrap.css +19 -1
  120. data/vendor/assets/stylesheets/luca-ui-development-tools.css +10 -0
  121. data/vendor/assets/stylesheets/luca-ui-full.css +1334 -0
  122. data/vendor/assets/stylesheets/luca-ui-spec.css +19 -1
  123. data/vendor/assets/stylesheets/luca-ui.css +19 -1
  124. data/views/index.erb +2 -5
  125. metadata +58 -9
  126. data/lib/sprockets/luca_template.rb +0 -49
  127. data/src/tools/class_browser.coffee +0 -39
  128. data/src/tools/components/class_browser_detail.coffee +0 -10
  129. data/src/tools/components/class_browser_list.coffee +0 -74
@@ -1,14 +1,52 @@
1
+ # Luca.Application
2
+ #
3
+ # The Application class is the global state tracking mechanism
4
+ # for your single page application, as well as the entry point.
5
+ #
6
+ # By default it contains a main controller which is a Luca.components.Controller instance.
7
+ #
8
+ # In a typical Luca application, the router will use the applications @navigate_to() method to switch
9
+ # from page to page on the main controller, or any other controllers nested inside of it.
10
+ #
11
+ # You would control flow when the controller fires activation events on the nested view components inside of it.
12
+ #
13
+ # Decoupling application control flow from the URL Fragment from Backbone.History and preventing
14
+ # your components from directly caring about the URL Fragment, allows you to build applications as
15
+ # isolated components which can run separately or nested inside of other applications.
16
+
17
+ startHistory = ()-> Backbone.history.start()
18
+
1
19
  _.def('Luca.Application').extends('Luca.containers.Viewport').with
20
+ name: "MyApp"
21
+
22
+ # The Application uses a Backbone.Model as a state machine, which
23
+ # allows you to get / set attributes, persist them somewhere, and
24
+ # most importantly to bind to change events of certain attributes.
25
+ #
26
+ # the @defaultState property will be the default attributes
27
+ defaultState: {}
2
28
 
3
- # automatically starts the @router
4
- # if it exists, once the components
5
- # for the application have been created
6
- autoStartHistory: true
29
+ # if autoBoot is set to true, the application will
30
+ # attempt to boot on document ready.
31
+ autoBoot: false
32
+
33
+ # automatically starts the @router if it exists,
34
+ # once the components for the application have
35
+ # been created. Pass the event name you want to
36
+ # listen for on this component before you start history
37
+ autoStartHistory: "before:render"
7
38
 
8
39
  # we will create a collection manager singleton
9
- # by default unless otherwise specified
40
+ # by default unless otherwise specified.
10
41
  useCollectionManager: true
11
42
 
43
+ # to pass options to the collection manager, set the @collectionManager
44
+ # hash which will get passed once the collection manager is created
45
+ collectionManager: {}
46
+
47
+ # by default we will use the standard collection manager which ships with
48
+ # Luca. If you would like to use your own extension of the collection manager
49
+ # just pass a reference to the class you would like to use.
12
50
  collectionManagerClass: "Luca.CollectionManager"
13
51
 
14
52
  # Luca plugin apps are apps which mount onto existing
@@ -22,7 +60,30 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
22
60
  # with several 'pages' so to speak
23
61
  useController: true
24
62
 
25
- #### Nested Components
63
+ # Key Handler
64
+ #
65
+ # One responsibility of the application, since it is a viewport which monopolizes the entire screen
66
+ # is to relay keypress events from the document, to whatever views are interested in responding to them.
67
+ #
68
+ # This functionality is disabled by default.
69
+ useKeyHandler: false
70
+
71
+ # You can configure key events by specifying them by their name, as it exists in Luca.keyMap. For example:
72
+ #
73
+ keyEvents: {}
74
+
75
+ # keyEvents
76
+ # keyup: keyUpHandler
77
+ # enter: enterHandler
78
+ # meta:
79
+ # up: metaUpHandler
80
+ # control:
81
+ # forwardslash: controlSlashHandler
82
+ # keyup: controlUpHandler
83
+ # control_meta:
84
+ # keydown: metaControlKeyDownHandler
85
+ #
86
+ #
26
87
 
27
88
  # applications have one component, the controller.
28
89
  # any components defined on the application class directly
@@ -36,31 +97,36 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
36
97
  ]
37
98
 
38
99
  initialize: (@options={})->
39
- Luca.containers.Viewport::initialize.apply @, arguments
40
-
41
- if @useController is true
42
- definedComponents = @components || []
100
+ app = @
101
+ appName = @name
102
+ alreadyRunning = Luca.getApplication?()
43
103
 
44
- @components = [
45
- ctype: 'controller'
46
- name: "main_controller"
47
- components: definedComponents
48
- ]
104
+ Luca.Application.instances ||= {}
105
+ Luca.Application.instances[ appName ] = app
106
+
107
+ Luca.containers.Viewport::initialize.apply @, arguments
49
108
 
50
- if @useCollectionManager is true
51
- @collectionManagerClass = Luca.util.resolve( @collectionManagerClass ) if _.isString( @collectionManagerClass )
109
+ @state = new Luca.Model( @defaultState )
52
110
 
53
- @collectionManager ||= Luca.CollectionManager.get?()
54
- @collectionManager ||= new @collectionManagerClass( @collectionManagerOptions||={} )
111
+ # The Controller is the piece of the application that handles showing
112
+ # and hiding 'pages' of the app. The Application has a navigate_to
113
+ # method which delegates to the controller, and allows you to navigate
114
+ # to a given page, or component, by its name. The controller integrates
115
+ # with the state machine of the application
116
+ @setupMainController()
55
117
 
56
- @state = new Luca.Model( @defaultState )
118
+ # The Collection Manager is responsible
119
+ @setupCollectionManager()
57
120
 
58
121
  # we will render when all of the various components
59
122
  # which handle our data dependencies determine that
60
123
  # we are ready
61
- @defer(()=>@render()).until("ready")
124
+ @defer(()-> app.render()).until(@, "ready")
125
+
126
+ # Set up the Backbone Router
127
+ @setupRouter()
62
128
 
63
- # the keyRouter allows us to specify
129
+ # the keyHandler allows us to specify
64
130
  # keyEvents on our application with an API very similar
65
131
  # to the DOM events API for Backbone.View
66
132
  #
@@ -69,52 +135,51 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
69
135
  # keyEvents:
70
136
  # meta:
71
137
  # forwardslash: "altSlashHandler"
72
- @setupKeyRouter() if @useKeyRouter is true and @keyEvents?
138
+ if @useKeyRouter is true
139
+ console.log "The useKeyRouter property is being deprecated. switch to useKeyHandler instead"
140
+
141
+ @setupKeyHandler() if (@useKeyHandler is true or @useKeyRouter is true) and @keyEvents?
73
142
 
74
143
  # if the application is a plugin designed to modify the behavior
75
144
  # of another app, then don't claim ownership. otherwise the most common
76
145
  # use-case is that there will be one application instance
77
- unless @plugin is true
78
- Luca.getApplication = ()=> @
146
+ unless @plugin is true or alreadyRunning
147
+ Luca.getApplication = (name)=>
148
+ return app unless name?
149
+ Luca.Application.instances[ name ]
150
+
151
+ if @autoBoot
152
+ if Luca.util.resolve(@name)
153
+ throw "Attempting to override window.#{ @name } when it already exists"
79
154
 
155
+ $ ->
156
+ window[ appName ] = app
157
+ app.boot()
158
+
159
+ # @activeView() returns a reference to the instance of the view
160
+ # which is currently monopolizing the viewport.
161
+ #
162
+ # this will be whicever component is active on the controllers
163
+ # nested within the main controller, if there are any, or the view
164
+ # itself which is active on the main controller.
80
165
  activeView: ()->
81
166
  if active = @activeSubSection()
82
167
  @view( active )
83
168
  else
84
169
  @view( @activeSection() )
85
170
 
86
- activeSubSection: ()->
87
- @get("active_sub_section")
88
-
171
+ # Returns the name of the active component on the main controller
89
172
  activeSection: ()->
90
173
  @get("active_section")
91
174
 
92
- beforeRender: ()->
93
- Luca.containers.Viewport::beforeRender?.apply(@, arguments)
94
-
95
- if @router? and @autoStartHistory is true
96
- routerStartEvent = @startRouterOn || "after:render"
97
-
98
- if routerStartEvent is "before:render"
99
- Backbone.history.start()
100
- else
101
- @bind routerStartEvent, ()-> Backbone.history.start()
102
-
103
- afterComponents: ()->
104
- Luca.containers.Viewport::afterComponents?.apply @, arguments
105
-
106
- # any time the main controller card switches we should track
107
- # the active card on the global state chart
108
- @getMainController()?.bind "after:card:switch", (previous,current)=>
109
- @state.set(active_section:current.name)
110
-
111
- # any time the card switches on one of the sub controllers
112
- # then we should track the active sub section on the global state chart
113
- @getMainController()?.each (component)=>
114
- if component.ctype.match(/controller$/)
115
- component.bind "after:card:switch", (previous,current)=>
116
- @state.set(active_sub_section:current.name)
175
+ # Returns the name of the active component on the nested controllers
176
+ # on the main controller, if there is one. These get set on the
177
+ # state machine in response to card switch events on the controller component
178
+ activeSubSection: ()->
179
+ @get("active_sub_section")
117
180
 
181
+ activePages: ()->
182
+ @$('.luca-ui-controller').map (index,element)=> $(element).data('active-section')
118
183
 
119
184
  # boot should trigger the ready event, which will call the initial call
120
185
  # to render() your application, which will have a cascading effect on every
@@ -135,12 +200,8 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
135
200
  get: (attribute)->
136
201
  @state.get(attribute)
137
202
 
138
- getMainController: ()->
139
- return @components[0] if @useController is true
140
- Luca.cache('main_controller')
141
-
142
- set: (attributes)->
143
- @state.set(attributes)
203
+ set: (attribute, value, options)->
204
+ @state.set.apply(@state, arguments)
144
205
 
145
206
  view: (name)->
146
207
  Luca.cache(name)
@@ -151,23 +212,12 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
151
212
  navigate_to: (component_name, callback)->
152
213
  @getMainController().navigate_to(component_name, callback)
153
214
 
154
- setupKeyRouter: ()->
155
- return unless @keyEvents
156
-
157
- @keyEvents.control_meta ||= {}
158
-
159
- # allow for both meta_control, control_meta for the combo
160
- _.extend(@keyEvents.control_meta, @keyEvents.meta_control) if @keyEvents.meta_control
161
-
162
- router = _.bind(@keyRouter, @)
163
-
164
- $( document ).keydown( router )
215
+ getMainController: ()->
216
+ return @components[0] if @useController is true
217
+ Luca.cache('main_controller')
165
218
 
166
- #### Key Router
167
- #
168
- # TODO: Define a syntax for mapping combinations of meta, control, and keycodes
169
- # to some sort of method delegation system that the application handles.
170
- keyRouter: (e)->
219
+ #
220
+ keyHandler: (e)->
171
221
  return unless e and @keyEvents
172
222
 
173
223
  isInputEvent = $(e.target).is('input') || $(e.target).is('textarea')
@@ -187,7 +237,82 @@ _.def('Luca.Application').extends('Luca.containers.Viewport').with
187
237
  source = if meta and control then @keyEvents.meta_control else source
188
238
 
189
239
  if keyEvent = source?[keyname]
190
- if @[keyEvent]?
240
+ if @[keyEvent]? and _.isFunction(@[keyEvent])
191
241
  @[keyEvent]?.call(@)
192
242
  else
193
- @trigger(keyEvent)
243
+ @trigger(keyEvent, e, keyname)
244
+
245
+ setupControllerBindings: ()->
246
+ app = @
247
+ # any time the main controller card switches we should track
248
+ # the active card on the global state chart
249
+ @getMainController()?.bind "after:card:switch", (previous,current)=>
250
+ @state.set(active_section:current.name)
251
+ app.trigger "page:change"
252
+
253
+ # any time the card switches on one of the sub controllers
254
+ # then we should track the active sub section on the global state chart
255
+ @getMainController()?.each (component)=>
256
+ if component.ctype.match(/controller$/)
257
+ component.bind "after:card:switch", (previous,current)=>
258
+ @state.set(active_sub_section:current.name)
259
+ app.trigger "sub:page:change"
260
+
261
+ setupMainController: ()->
262
+ if @useController is true
263
+ definedComponents = @components || []
264
+
265
+ @components = [
266
+ ctype: 'controller'
267
+ name: "main_controller"
268
+ components: definedComponents
269
+ ]
270
+
271
+ @defer( @setupControllerBindings, false ).until("after:components")
272
+
273
+ setupCollectionManager: ()->
274
+ if @useCollectionManager is true
275
+ @collectionManagerClass = Luca.util.resolve( @collectionManagerClass ) if _.isString( @collectionManagerClass )
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 )
291
+
292
+ setupRouter: ()->
293
+ app = @
294
+
295
+ if _.isString( @router )
296
+ routerClass = Luca.util.resolve(@router)
297
+ @router = new routerClass({app})
298
+
299
+ # if this application has a router associated with it
300
+ # then we need to start backbone history on a certain event.
301
+ # you can control which by setting the @startHistoryOn property
302
+ if @router and @autoStartHistory
303
+ @autoStartHistory = "before:render" if @autoStartHistory is true
304
+ @defer(startHistory, false).until(@, @autoStartHistory)
305
+
306
+ setupKeyHandler: ()->
307
+ return unless @keyEvents
308
+
309
+ @keyEvents.control_meta ||= {}
310
+
311
+ # allow for both meta_control, control_meta for the combo
312
+ _.extend(@keyEvents.control_meta, @keyEvents.meta_control) if @keyEvents.meta_control
313
+
314
+ handler = _.bind(@keyHandler, @)
315
+
316
+ for keyEvent in (@keypressEvents || ["keydown"])
317
+ $( document ).on( keyEvent, handler )
318
+
@@ -1,6 +1,6 @@
1
1
  _.def('Luca.components.Toolbar').extends('Luca.core.Container').with
2
2
 
3
- className: 'luca-ui-toolbar'
3
+ className: 'luca-ui-toolbar toolbar'
4
4
 
5
5
  position: 'bottom'
6
6
 
@@ -1,4 +1,12 @@
1
- make = Luca.View::make
1
+ # Public: The CollectionView renders a collection of models into a list of items.
2
+ #
3
+ # Examples
4
+ #
5
+ # _.def('App.ListView').extends('Luca.components.CollectionView').with
6
+ # collection: new App.SampleCollection()
7
+ # itemTemplate: "list_view_item"
8
+ # loadMask: true
9
+ #
2
10
 
3
11
  _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
4
12
  tagName: "div"
@@ -17,6 +25,10 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
17
25
 
18
26
  itemClassName: 'collection-item'
19
27
 
28
+ hooks:[
29
+ "empty:results"
30
+ ]
31
+
20
32
  initialize: (@options={})->
21
33
  _.extend(@, @options)
22
34
 
@@ -31,12 +43,23 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
31
43
  Luca.components.Panel::initialize.apply(@, arguments)
32
44
 
33
45
  if _.isString(@collection) and Luca.CollectionManager.get()
34
- @collection = Luca.CollectionManager.get().get(@collection)
46
+ @collection = Luca.CollectionManager.get().getOrCreate(@collection)
35
47
 
36
48
  if Luca.isBackboneCollection(@collection)
37
- @collection.bind "reset", @refresh
49
+ @collection.on "before:fetch", ()=>
50
+ @trigger "enable:loadmask" if @loadMask is true
51
+
52
+ @collection.bind "reset", ()=>
53
+ @trigger "disable:loadmask" if @loadMask is true
54
+ @refresh()
55
+
38
56
  @collection.bind "add", @refresh
39
57
  @collection.bind "remove", @refresh
58
+ else
59
+ throw "Collection Views must have a valid backbone collection"
60
+
61
+ if @collection.length > 0
62
+ @refresh()
40
63
 
41
64
  attributesForItem: (item)->
42
65
  _.extend {}, class: @itemClassName, "data-index": item.index
@@ -46,7 +69,7 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
46
69
  content = templateFn.call(@, item)
47
70
 
48
71
  if @itemRenderer? and _.isFunction( @itemRenderer )
49
- content = @itemRenderer.call(@, item)
72
+ content = @itemRenderer.call(@, item, item.model, item.index)
50
73
 
51
74
  if @itemProperty
52
75
  content = item.model.get(@itemProperty) || item.model[ @itemProperty ]
@@ -56,7 +79,6 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
56
79
 
57
80
  makeItem: (model, index)->
58
81
  item = if @prepareItem? then @prepareItem.call(@, model, index) else (model:model, index: index)
59
-
60
82
  make(@itemTagName, @attributesForItem(item), @contentForItem(item))
61
83
 
62
84
  getModels: ()->
@@ -66,12 +88,13 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
66
88
  @collection.models
67
89
 
68
90
  refresh: ()->
69
- panel = @
70
-
71
91
  @$bodyEl().empty()
72
92
 
73
- _( @getModels() ).each (model, index)->
74
- panel.$append( panel.makeItem(model, index) )
93
+ if @getModels().length is 0
94
+ @trigger("empty:results")
95
+
96
+ _( @getModels() ).each (model, index)=>
97
+ @$append( @makeItem(model, index) )
75
98
 
76
99
  registerEvent: (domEvent, selector, handler)->
77
100
  if !handler? and _.isFunction(selector)
@@ -83,4 +106,9 @@ _.def("Luca.components.CollectionView").extends("Luca.components.Panel").with
83
106
 
84
107
  render: ()->
85
108
  @refresh()
86
- @$attach() if @$el.parent().length > 0 and @container?
109
+ @$attach() if @$el.parent().length > 0 and @container?
110
+ @
111
+
112
+ # Private Helpers
113
+
114
+ make = Luca.View::make
@@ -1,4 +1,9 @@
1
1
  _.def('Luca.components.Controller').extends('Luca.containers.CardView').with
2
+
3
+ additionalClassNames:['luca-ui-controller']
4
+
5
+ activeAttribute: "active-section"
6
+
2
7
  initialize: (@options)->
3
8
  Luca.containers.CardView::initialize.apply @, arguments
4
9
 
@@ -6,13 +11,31 @@ _.def('Luca.components.Controller').extends('Luca.containers.CardView').with
6
11
 
7
12
  throw "Controllers must specify a defaultCard property and/or the first component must have a name" unless @defaultCard
8
13
 
9
- @state = new Backbone.Model
14
+ @state = new Backbone.Model
10
15
  active_section: @defaultCard
11
16
 
12
17
  each: (fn)->
13
18
  _( @components ).each (component)=>
14
19
  fn.apply @, [component]
15
20
 
21
+ activeSection: ()->
22
+ @get("activeSection")
23
+
24
+ controllers:(deep=false)->
25
+ @select 'ctype', 'controller', deep
26
+
27
+ availableSections: ()->
28
+ base = {}
29
+ base[ @name ] = @sectionNames()
30
+
31
+ _( @controllers() ).reduce (memo,controller)=>
32
+ memo[ controller.name ] = controller.sectionNames()
33
+ memo
34
+ , base
35
+
36
+ sectionNames: (deep=false)->
37
+ @pluck('name')
38
+
16
39
  default: (callback)->
17
40
  @navigate_to(@defaultCard, callback)
18
41
 
@@ -3,23 +3,20 @@ _.def('Luca.fields.CheckboxField').extends('Luca.core.Field').with
3
3
  events:
4
4
  "change input" : "change_handler"
5
5
 
6
- change_handler: (e)->
7
- me = my = $(e.currentTarget)
6
+ className: 'luca-ui-checkbox-field luca-ui-field'
7
+ template: 'fields/checkbox_field'
8
+ hooks: ["checked","unchecked"]
9
+ send_blanks: true
8
10
 
9
- @trigger "on:change", @, e
11
+ change_handler: (e)->
12
+ me = my = $(e.target)
10
13
 
11
- if me.checked is true
14
+ if me.is(":checked")
12
15
  @trigger "checked"
13
16
  else
14
17
  @trigger "unchecked"
15
18
 
16
- className: 'luca-ui-checkbox-field luca-ui-field'
17
-
18
- template: 'fields/checkbox_field'
19
-
20
- hooks: ["checked","unchecked"]
21
-
22
- send_blanks: true
19
+ @trigger "on:change", @, e, me.is(":checked")
23
20
 
24
21
  initialize: (@options={})->
25
22
  _.extend @, @options
@@ -37,4 +34,4 @@ _.def('Luca.fields.CheckboxField').extends('Luca.core.Field').with
37
34
  @input.attr('checked', checked)
38
35
 
39
36
  getValue:()->
40
- @input.attr('checked') is true
37
+ @input.is(":checked")
@@ -0,0 +1,14 @@
1
+ _.def("Luca.components.LabelField").extends("Luca.core.Field").with
2
+ className: "luca-ui-field luca-ui-label-field"
3
+
4
+ getValue: ()->
5
+ @$('input').attr('value')
6
+
7
+ formatter: (value)->
8
+ value ||= @getValue()
9
+ _.str.titleize( value )
10
+
11
+ setValue: (value)->
12
+ @trigger("change", value, @getValue())
13
+ @$('input').attr('value', value)
14
+ @$('.value').html( @formatter(value) )
@@ -50,13 +50,13 @@ _.def('Luca.fields.SelectField').extends('Luca.core.Field').with
50
50
  parseData: ()->
51
51
  @collection.data = _( @collection.data ).map (record)=>
52
52
  return record if not _.isArray( record )
53
+
53
54
  hash = {}
54
55
  hash[ @valueField ] = record[0]
55
- hash[ @displayField ] = record[1]
56
+ hash[ @displayField ] = record[1] || record[0]
56
57
 
57
58
  hash
58
59
 
59
-
60
60
  afterRender: ()->
61
61
  @input = $('select', @el)
62
62
 
@@ -1,4 +1,3 @@
1
- change_handler = (e)-> @trigger "on:change", @, e
2
1
 
3
2
  _.def('Luca.fields.TextField').extends('Luca.core.Field').with
4
3
  events:
@@ -12,8 +11,11 @@ _.def('Luca.fields.TextField').extends('Luca.core.Field').with
12
11
 
13
12
  send_blanks: true
14
13
 
14
+ keyEventThrottle: 300
15
+
15
16
  initialize: (@options={})->
16
- Luca.core.Field::initialize.apply @, arguments
17
+ if @enableKeyEvents
18
+ @registerEvent("keyup input","keyup_handler")
17
19
 
18
20
  @input_id ||= _.uniqueId('field')
19
21
  @input_name ||= @name
@@ -28,13 +30,16 @@ _.def('Luca.fields.TextField').extends('Luca.core.Field').with
28
30
  @$el.addClass('input-append')
29
31
  @addOn = @append
30
32
 
31
- @registerEvent("keydown input","keydown_handler") if @enableKeyEvents
33
+ Luca.core.Field::initialize.apply @, arguments
34
+
35
+ keyup_handler: (e)->
36
+ @trigger "on:keyup", @, e
32
37
 
33
38
  blur_handler: (e)->
34
- me = my = $( e.currentTarget )
39
+ @trigger "on:blur", @, e
35
40
 
36
41
  focus_handler: (e)->
37
- me = my = $( e.currentTarget )
42
+ @trigger "on:focus", @, e
38
43
 
39
- change_handler: change_handler
40
- keydown_handler: _.throttle ((e)-> change_handler.apply @, arguments), 300
44
+ change_handler: (e)->
45
+ @trigger "on:change", @, e
@@ -2,6 +2,7 @@ _.def('Luca.fields.TypeAheadField').extends('Luca.fields.TextField').with
2
2
  className: 'luca-ui-field'
3
3
 
4
4
  getSource: ()->
5
+ return @source.call(@) if _.isFunction(@source)
5
6
  @source || []
6
7
 
7
8
  matcher: (item)->