luca 0.9.65 → 0.9.76

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 (86) hide show
  1. data/CHANGELOG +30 -0
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +27 -0
  4. data/lib/luca/rails/version.rb +1 -1
  5. data/spec/components/controller_spec.coffee +58 -0
  6. data/spec/components/form_view_spec.coffee +4 -0
  7. data/spec/concerns/dom_helpers_spec.coffee +16 -0
  8. data/spec/{modules → concerns}/filterable_spec.coffee +0 -0
  9. data/spec/concerns/model_presenter_spec.coffee +31 -0
  10. data/spec/{modules → concerns}/paginatable_spec.coffee +0 -0
  11. data/spec/{modules → concerns}/state_model_spec.coffee +0 -0
  12. data/spec/concerns_spec.coffee +88 -0
  13. data/spec/core/container_spec.coffee +103 -6
  14. data/spec/core/field_spec.coffee +4 -0
  15. data/spec/core/model_spec.coffee +6 -1
  16. data/spec/define_spec.coffee +104 -7
  17. data/spec/framework_spec.coffee +30 -1
  18. data/spec/util_spec.coffee +24 -0
  19. data/src/components/application.coffee +62 -25
  20. data/src/components/base_toolbar.coffee +6 -4
  21. data/src/components/collection_loader_view.coffee +3 -1
  22. data/src/components/collection_view.coffee +36 -73
  23. data/src/components/controller.coffee +73 -35
  24. data/src/components/fields/button_field.coffee +20 -12
  25. data/src/components/fields/checkbox_array.coffee +8 -2
  26. data/src/components/fields/checkbox_field.coffee +18 -9
  27. data/src/components/fields/file_upload_field.coffee +5 -1
  28. data/src/components/fields/hidden_field.coffee +3 -1
  29. data/src/components/fields/label_field.coffee +4 -3
  30. data/src/components/fields/select_field.coffee +7 -8
  31. data/src/components/fields/text_area_field.coffee +3 -2
  32. data/src/components/fields/text_field.coffee +5 -4
  33. data/src/components/fields/type_ahead_field.coffee +4 -2
  34. data/src/components/form_button_toolbar.coffee +4 -1
  35. data/src/components/form_view.coffee +26 -24
  36. data/src/components/grid_view.coffee +3 -3
  37. data/src/components/multi_collection_view.coffee +6 -35
  38. data/src/components/pagination_control.coffee +1 -3
  39. data/src/components/router.coffee +2 -0
  40. data/src/components/table_view.coffee +7 -0
  41. data/src/concerns.coffee +70 -0
  42. data/src/{modules → concerns}/application_event_bindings.coffee +1 -1
  43. data/src/{modules → concerns}/collection_event_bindings.coffee +1 -1
  44. data/src/{modules → concerns}/deferrable.coffee +1 -1
  45. data/src/{modules → concerns}/dom_helpers.coffee +11 -2
  46. data/src/{modules → concerns}/enhanced_properties.coffee +1 -1
  47. data/src/{modules → concerns}/filterable.coffee +11 -11
  48. data/src/{modules → concerns}/grid_layout.coffee +1 -1
  49. data/src/{modules → concerns}/loadmaskable.coffee +1 -1
  50. data/src/{modules → concerns}/local_storage.coffee +0 -0
  51. data/src/{modules → concerns}/modal_view.coffee +1 -1
  52. data/src/concerns/model_presenter.coffee +23 -0
  53. data/src/{modules → concerns}/paginatable.coffee +9 -3
  54. data/src/concerns/query_collection_bindings.coffee +44 -0
  55. data/src/{modules → concerns}/state_model.coffee +1 -1
  56. data/src/{modules → concerns}/templating.coffee +1 -1
  57. data/src/containers/card_view.coffee +16 -9
  58. data/src/containers/tab_view.coffee +8 -11
  59. data/src/containers/viewport.coffee +3 -3
  60. data/src/core/collection.coffee +39 -28
  61. data/src/core/container.coffee +37 -15
  62. data/src/core/field.coffee +40 -39
  63. data/src/core/meta_data.coffee +93 -0
  64. data/src/core/model.coffee +18 -1
  65. data/src/core/registry.coffee +4 -3
  66. data/src/core/view.coffee +24 -30
  67. data/src/define.coffee +165 -79
  68. data/src/framework.coffee +97 -21
  69. data/src/index.coffee +4 -2
  70. data/src/managers/collection_manager.coffee +7 -3
  71. data/src/stylesheets/components/checkbox_array.scss +1 -1
  72. data/src/stylesheets/components/form_view.scss +5 -5
  73. data/src/stylesheets/components/viewport.scss +2 -1
  74. data/src/stylesheets/containers/container.scss +0 -5
  75. data/src/stylesheets/containers/tab_view.scss +5 -5
  76. data/src/templates/fields/text_area_field.jst.ejs +1 -1
  77. data/src/templates/fields/text_field.jst.ejs +1 -1
  78. data/src/util.coffee +47 -0
  79. data/vendor/assets/javascripts/luca-ui-full.js +1279 -494
  80. data/vendor/assets/javascripts/luca-ui-full.min.js +5 -5
  81. data/vendor/assets/javascripts/luca-ui-templates.js +2 -2
  82. data/vendor/assets/javascripts/luca-ui.js +1279 -494
  83. data/vendor/assets/javascripts/luca-ui.min.js +5 -4
  84. data/vendor/assets/stylesheets/luca-ui.css +15 -15
  85. metadata +27 -20
  86. data/spec/mixin_spec.coffee +0 -49
@@ -1,4 +1,4 @@
1
- Luca.modules.Templating =
1
+ Luca.concerns.Templating =
2
2
  __initializer: ()->
3
3
  templateVars = Luca.util.read.call(@, @bodyTemplateVars) || {}
4
4
 
@@ -39,9 +39,9 @@ component.defaults
39
39
  generateComponentElements: true
40
40
 
41
41
  initialize: (@options)->
42
+ @components ||= @pages ||= @cards
42
43
  Luca.core.Container::initialize.apply @,arguments
43
44
  @setupHooks(@hooks)
44
- @components ||= @pages ||= @cards
45
45
 
46
46
  prepareComponents: ()->
47
47
  Luca.core.Container::prepareComponents?.apply(@, arguments)
@@ -99,28 +99,35 @@ component.defaults
99
99
  unless current
100
100
  return
101
101
 
102
- unless silent
102
+ unless silent is true
103
103
  @trigger "before:card:switch", previous, current
104
- previous?.trigger?.apply(previous,["before:deactivation", @, previous, current])
105
- current?.trigger?.apply(previous,["before:activation", @, previous, current])
104
+ previous?.trigger "before:deactivation", @, previous, current
105
+ current?.trigger "before:activation", @, previous, current
106
106
 
107
107
  _.defer ()=>
108
108
  @$el.data( @activeAttribute || "active-card", current.name)
109
109
 
110
110
  @componentElements().hide()
111
111
 
112
- unless current.previously_activated
112
+ unless current.previously_activated is true
113
113
  current.trigger "first:activation"
114
114
  current.previously_activated = true
115
115
 
116
116
  @activeCard = index
117
117
  @activeComponentElement().show()
118
118
 
119
- unless silent
119
+ unless silent is true
120
120
  @trigger "after:card:switch", previous, current
121
- previous.trigger?.apply(previous, ["deactivation", @, previous, current])
122
- current.trigger?.apply(current, ["activation", @, previous, current])
121
+ previous?.trigger "deactivation", @, previous, current
122
+ current?.trigger "activation", @, previous, current
123
+
124
+ activationContext = @
123
125
 
126
+ if Luca.containers.CardView.activationContext is "current"
127
+ activationContext = current
124
128
 
125
129
  if _.isFunction(callback)
126
- callback.apply @, [@,previous,current]
130
+ callback.apply activationContext, [@,previous,current]
131
+
132
+
133
+ Luca.containers.CardView.activationContext = "current"
@@ -1,23 +1,20 @@
1
1
  _.def('Luca.containers.TabView').extends('Luca.containers.CardView').with
2
2
 
3
- hooks:[
4
- "before:select"
5
- "after:select"
6
- ]
7
-
8
- componentType: 'tab_view'
9
-
10
- className: 'luca-ui-tab-view tabbable'
3
+ tabView = Luca.register "Luca.containers.TabView"
4
+ tabView.triggers "before:select",
5
+ "after:select"
11
6
 
7
+ tabView.publicConfiguration
12
8
  tab_position: 'top'
13
-
14
9
  tabVerticalOffset: '50px'
15
10
 
11
+ tabView.privateConfiguration
12
+ additionalClassNames: 'tabbable'
16
13
  navClass: "nav-tabs"
17
-
18
14
  bodyTemplate: "containers/tab_view"
19
15
  bodyEl: "div.tab-content"
20
16
 
17
+ tabView.defines
21
18
  initialize: (@options={})->
22
19
  @navClass = "nav-list"if @navStyle is "list"
23
20
 
@@ -45,7 +42,7 @@ _.def('Luca.containers.TabView').extends('Luca.containers.CardView').with
45
42
  tabContainerId = @tabContainer().attr("id")
46
43
  @registerEvent("click ##{ tabContainerId } li a", "select")
47
44
 
48
- if Luca.enableBootstrap and (@tab_position is "left" or @tab_position is "right")
45
+ if Luca.config.enableBoostrap and (@tab_position is "left" or @tab_position is "right")
49
46
  @tabContainerWrapper().addClass("span2")
50
47
  @tabContentWrapper().addClass("span9")
51
48
 
@@ -12,7 +12,7 @@ _.def('Luca.containers.Viewport').extend('Luca.containers.CardView').with
12
12
  initialize: (@options={})->
13
13
  _.extend @, @options
14
14
 
15
- if Luca.enableBootstrap is true
15
+ if Luca.config.enableBoostrap is true
16
16
  @wrapperClass = if @fluid is true then Luca.containers.Viewport.fluidWrapperClass else Luca.containers.Viewport.defaultWrapperClass
17
17
 
18
18
  Luca.core.Container::initialize.apply(@, arguments)
@@ -42,7 +42,7 @@ _.def('Luca.containers.Viewport').extend('Luca.containers.CardView').with
42
42
  beforeRender: ()->
43
43
  Luca.containers.CardView::beforeRender?.apply(@, arguments)
44
44
 
45
- #if Luca.enableBootstrap and @topNav and @fullscreen
45
+ #if Luca.config.enableBoostrap and @topNav and @fullscreen
46
46
  # $('body').css('padding','40px')
47
47
 
48
48
  @renderTopNavigation() if @topNav?
@@ -57,7 +57,7 @@ _.def('Luca.containers.Viewport').extend('Luca.containers.CardView').with
57
57
  afterRender: ()->
58
58
  Luca.containers.CardView::after?.apply(@, arguments)
59
59
 
60
- if Luca.enableBootstrap is true and @containerClassName
60
+ if Luca.config.enableBoostrap is true and @containerClassName
61
61
  @$el.children().wrap('<div class="#{ containerClassName }" />')
62
62
 
63
63
  renderTopNavigation: ()->
@@ -1,12 +1,11 @@
1
1
  collection = Luca.define 'Luca.Collection'
2
-
3
- if Backbone.QueryCollection?
4
- collection.extends 'Backbone.QueryCollection'
5
- else
6
- collection.extends 'Backbone.Collection'
7
-
2
+ collection.extends 'Backbone.QueryCollection'
8
3
  collection.includes 'Luca.Events'
9
4
 
5
+ collection.triggers "after:initialize",
6
+ "before:fetch",
7
+ "after:response"
8
+
10
9
  collection.defines
11
10
  model: Luca.Model
12
11
  # cachedMethods refers to a list of methods on the collection
@@ -91,6 +90,9 @@ collection.defines
91
90
  if models
92
91
  @reset models, silent: true, parse: options?.parse
93
92
 
93
+ Luca.concern.setup.call(@)
94
+ Luca.util.setupHooks.call(@, @hooks)
95
+
94
96
  @trigger "after:initialize"
95
97
 
96
98
  # Luca.Collections will append a query string to the URL
@@ -139,11 +141,13 @@ collection.defines
139
141
  @
140
142
 
141
143
  applyFilter: (filter={}, options={})->
144
+ options = _( options ).clone()
145
+
142
146
  if options.remote? is true or @remoteFilter is true
143
147
  @applyParams(filter)
144
- @fetch _.extend(options,refresh:true)
148
+ @fetch _.extend(options,refresh:true,remote:true)
145
149
  else
146
- @reset @query filter
150
+ @reset @query(filter, options)
147
151
 
148
152
  # You can apply params to a collection, so that any upcoming requests
149
153
  # made to the REST API are made with the key values specified
@@ -211,7 +215,7 @@ collection.defines
211
215
  # fetch will try to pull from the bootstrap if it is setup to do so
212
216
  # you can actually make the roundtrip to the server anyway if you pass
213
217
  # refresh = true in the options hash
214
- return @bootstrap() if @cached_models().length and not options.refresh
218
+ return @bootstrap() if @cached_models().length and not (options.refresh is true or options.remote is true)
215
219
 
216
220
  url = if _.isFunction(@url) then @url() else @url
217
221
 
@@ -230,10 +234,11 @@ collection.defines
230
234
  # reset trigger with a function wrapped in _.once
231
235
  # so that it only gets run...ahem...once.
232
236
  #
233
- # that being said, if the collection already has models
234
237
  # it won't even bother fetching it it will just run
235
238
  # as if reset was already triggered
236
- onceLoaded: (fn, options={autoFetch:true})->
239
+ onceLoaded: (fn, options={})->
240
+ _.defaults(options, autoFetch: true)
241
+
237
242
  if @length > 0 and not @fetching
238
243
  fn.apply @, [@]
239
244
  return
@@ -262,17 +267,6 @@ collection.defines
262
267
  unless @fetching is true or !options.autoFetch or @length > 0
263
268
  @fetch()
264
269
 
265
- # parse is very close to the stock Backbone.Collection parse, which
266
- # just returns the response. However, it also triggers a callback
267
- # after:response, and automatically parses responses which contain
268
- # a JSON root like you would see in rails, if you specify the @root
269
- # property.
270
- #
271
- # it will also update the Luca.Collection.cache with the models from
272
- # the response, so that any subsequent calls to fetch() on a bootstrapped
273
- # collection, will have updated models from the server. Really only
274
- # useful if you call fetch(refresh:true) manually on any bootstrapped
275
- # collection
276
270
  parse: (response)->
277
271
  @fetching = false
278
272
  @trigger "after:response", response
@@ -350,17 +344,34 @@ _.extend Luca.Collection.prototype,
350
344
 
351
345
  Backbone.View.prototype.trigger.apply @, arguments
352
346
 
347
+ Luca.Collection._originalExtend = Backbone.Collection.extend
348
+
349
+ Luca.Collection.extend = (definition={})->
350
+ # for backward compatibility
351
+ definition.concerns ||= definition.concerns if definition.concerns?
352
+
353
+ componentClass = Luca.Collection._originalExtend.call(@, definition)
354
+
355
+ if definition.concerns? and _.isArray( definition.concerns )
356
+ for module in definition.concerns
357
+ Luca.decorate( componentClass ).with( module )
358
+
359
+ componentClass
360
+
361
+ Luca.Collection.namespace = (namespace)->
362
+ namespace = Luca.util.resolve(namespace) if _.isString(namespace)
363
+ Luca.Collection.__defaultNamespace = namespace if namespace?
364
+ Luca.Collection.__defaultNamespace ||= (window || global)
365
+ Luca.util.read( Luca.Collection.__defaultNamespace )
366
+
353
367
  # Always include these parameters in every request to your REST API.
354
368
  #
355
369
  # either specify a function which returns a hash, or just a normal hash
356
370
  Luca.Collection.baseParams = (obj)->
357
- return Luca.Collection._baseParams = obj if obj
358
-
359
- if _.isFunction( Luca.Collection._baseParams )
360
- return Luca.Collection._baseParams()
371
+ obj = Luca.util.resolve(obj) if _.isString(obj)
372
+ Luca.Collection._baseParams = obj if obj
361
373
 
362
- if _.isObject( Luca.Collection._baseParams )
363
- Luca.Collection._baseParams
374
+ Luca.util.read( Luca.Collection._baseParams )
364
375
 
365
376
  # In order to make our Backbone Apps super fast it is a good practice
366
377
  # to pre-populate your collections by what is referred to as bootstrapping
@@ -42,10 +42,18 @@ container.defines
42
42
  initialize: (@options={})->
43
43
  _.extend @, @options
44
44
 
45
- @setupHooks( Luca.core.Container::hooks )
46
-
47
45
  # aliases for the components property
48
46
  @components ||= @fields ||= @pages ||= @cards ||= @views
47
+
48
+ # accept components as an array of strings representing
49
+ # the luca component type
50
+ for component in @components when _.isString(component)
51
+ component = (type: component, role: component, name: component)
52
+
53
+ _.bindAll(@, "beforeRender")
54
+
55
+ @setupHooks( Luca.core.Container::hooks )
56
+
49
57
 
50
58
  validateContainerConfiguration(@)
51
59
 
@@ -115,10 +123,8 @@ container.defines
115
123
  applyDOMConfig.call(container, component, index)
116
124
 
117
125
  prepareComponents: ()->
118
- # accept components as an array of strings representing
119
- # the luca component type
120
- for component in @components when _.isString(component)
121
- component = (type: component)
126
+ container = @
127
+
122
128
 
123
129
  _( @components ).each (component, index)=>
124
130
  ce = componentContainerElement = @componentContainers?[index]
@@ -130,6 +136,26 @@ container.defines
130
136
  panel = @make(@componentTag, componentContainerElement, '')
131
137
  @$append( panel )
132
138
 
139
+ # if the container defines a @defaults property
140
+ # then we should make sure our child components inherit
141
+ # these values unless specifically defined
142
+ if container.defaults?
143
+ component = _.defaults(component, (container.defaults || {}))
144
+
145
+ # if the container defines an @extensions property as an array of
146
+ # configuration objects, then we will extend the component config with
147
+ # the object in the matching position of the @extensions array.
148
+ if _.isArray(container.extensions) and _.isObject(container.extensions?[ index ])
149
+ componentExtension = container.extensions[index]
150
+ component = _.extend(component, componentExtension)
151
+
152
+ # if the container defines an @extensions property as an object of nested hashes,
153
+ # then extensions is a key/value pair whose key represents the role of the component
154
+ # that we wish to extend / customize
155
+ if component.role? and _.isObject(container.extensions) and _.isObject(container.extensions[component.role])
156
+ componentExtension = container.extensions[component.role]
157
+ component = _.extend(component, componentExtension)
158
+
133
159
  unless component.container?
134
160
  component.container = "##{ componentContainerElement.id }" if @generateComponentElements
135
161
  component.container ||= @$bodyEl()
@@ -170,11 +196,7 @@ container.defines
170
196
  else
171
197
  object.type = object.ctype = Luca.defaultComponentType
172
198
 
173
- # if the container defines a @defaults property
174
- # then we should make sure our child components inherit
175
- # these values unless specifically defined
176
- object = _.defaults(object, (container.defaults || {}))
177
-
199
+ object._parentCid ||= container.cid
178
200
  created = Luca.util.lazyComponent( object )
179
201
 
180
202
  # if we're using base backbone views, then they don't extend themselves
@@ -183,6 +205,8 @@ container.defines
183
205
  if !component.container and component.options?.container
184
206
  component.container = component.options.container
185
207
 
208
+ component.getParent ||= ()-> Luca( component._parentCid )
209
+
186
210
  if not component.container?
187
211
  console.log component,index,@
188
212
  console.error "could not assign container property to component on container #{ @name || @cid }"
@@ -201,10 +225,8 @@ container.defines
201
225
  @debug "container render components"
202
226
 
203
227
  container = @
204
- _(@components).each (component)->
205
- component.getParent = ()->
206
- container
207
228
 
229
+ _(@components).each (component)->
208
230
  try
209
231
  @$( component.container ).eq(0).append( component.el )
210
232
  component.render()
@@ -348,7 +370,7 @@ container.defines
348
370
  @components[ needle ]
349
371
 
350
372
  isRootComponent:()->
351
- !@getParent?
373
+ @rootComponent is true || !@getParent?
352
374
 
353
375
  getRootComponent: ()->
354
376
  if @isRootComponent() then @ else @getParent().getRootComponent()
@@ -1,26 +1,51 @@
1
- _.def('Luca.core.Field').extends('Luca.View').with
1
+ field = Luca.register "Luca.core.Field"
2
2
 
3
- className: 'luca-ui-text-field luca-ui-field'
4
-
5
- isField: true
3
+ field.extends "Luca.View"
6
4
 
7
- template: 'fields/text_field'
5
+ field.triggers "before:validation",
6
+ "after:validation",
7
+ "on:change"
8
8
 
9
+ field.publicConfiguration
9
10
  labelAlign: 'top'
10
-
11
- hooks:[
12
- "before:validation",
13
- "after:validation",
14
- "on:change"
15
- ]
16
-
17
- # see: http://twitter.github.com/bootstrap/base-css.html
11
+ className: 'luca-ui-text-field luca-ui-field'
18
12
  statuses: [
19
13
  "warning"
20
14
  "error"
21
15
  "success"
22
16
  ]
23
17
 
18
+ field.publicInterface
19
+ disable: ()->
20
+ @getInputElement().attr('disabled', true)
21
+
22
+ enable: ()->
23
+ @getInputElement().attr('disabled', false)
24
+
25
+ getValue: ()->
26
+ raw = @getInputElement()?.attr('value')
27
+
28
+ return raw if _.str.isBlank( raw )
29
+
30
+ switch @valueType
31
+ when "integer" then parseInt(raw)
32
+ when "string" then "#{ raw }"
33
+ when "float" then parseFloat(raw)
34
+ else raw
35
+
36
+ setValue: (value)->
37
+ @getInputElement()?.attr('value', value)
38
+
39
+ updateState: (state)->
40
+ _( @statuses ).each (cls)=>
41
+ @$el.removeClass(cls)
42
+ @$el.addClass(state)
43
+
44
+ field.privateConfiguration
45
+ isField: true
46
+ template: 'fields/text_field'
47
+
48
+ field.defines
24
49
  initialize: (@options={})->
25
50
  _.extend @, @options
26
51
 
@@ -29,6 +54,7 @@ _.def('Luca.core.Field').extends('Luca.View').with
29
54
  @input_class ||= ""
30
55
  @input_type ||= ""
31
56
  @helperText ||= ""
57
+ @label = @name if not @label? or @label.length is 0
32
58
  @label ||= "*#{ @label }" if @required and not @label?.match(/^\*/)
33
59
  @inputStyles ||= ""
34
60
  @input_value ||= @value || ""
@@ -45,7 +71,7 @@ _.def('Luca.core.Field').extends('Luca.View').with
45
71
  Luca.View::initialize.apply(@, arguments)
46
72
 
47
73
  beforeRender: ()->
48
- if Luca.enableBootstrap
74
+ if Luca.config.enableBoostrap
49
75
  @$el.addClass('control-group')
50
76
 
51
77
  @$el.addClass('required') if @required
@@ -53,30 +79,5 @@ _.def('Luca.core.Field').extends('Luca.View').with
53
79
  change_handler: (e)->
54
80
  @trigger "on:change", @, e
55
81
 
56
- disable: ()->
57
- @getInputElement().attr('disabled', true)
58
-
59
- enable: ()->
60
- @getInputElement().attr('disabled', false)
61
-
62
- getValue: ()->
63
- raw = @getInputElement()?.attr('value')
64
-
65
- return raw if _.str.isBlank( raw )
66
-
67
- switch @valueType
68
- when "integer" then parseInt(raw)
69
- when "string" then "#{ raw }"
70
- when "float" then parseFloat(raw)
71
- else raw
72
-
73
- setValue: (value)->
74
- @getInputElement()?.attr('value', value)
75
-
76
82
  getInputElement: ()->
77
83
  @input ||= @$('input').eq(0)
78
-
79
- updateState: (state)->
80
- _( @statuses ).each (cls)=>
81
- @$el.removeClass(cls)
82
- @$el.addClass(state)