luca 0.9.2 → 0.9.4

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 (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,25 +1,40 @@
1
- # Luca.Model
1
+ # Computed Properties Support
2
+ # ###
2
3
  #
3
- # Luca.Model is an extenstion of Backbone.Model which provides
4
- # few useful patterns:
4
+ # Luca.Model supports computed properties, which are
5
+ # object methods which are composed of model attributes or
6
+ # of calculations which are dependent on these attributes.
7
+
8
+ # When these model attributes change, the computed property
9
+ # needs to be re-evaluated.
5
10
  #
6
- # - computed properties support
7
- _.def('Luca.Model').extends('Backbone.Model').with
8
- initialize: ()->
9
- Backbone.Model::initialize @, arguments
11
+ # The configuration API for computed properties expects a hash
12
+ # whose keys are the name of the method, and whose value is
13
+ # an array of the attribute dependencies for that method.
14
+ setupComputedProperties = ()->
15
+ return if _.isUndefined(@computed)
16
+
17
+ @_computed = {}
10
18
 
11
- return if _.isUndefined(@computed)
19
+ for attr, dependencies of @computed
20
+ @on "change:#{attr}", ()=>
21
+ @_computed[attr] = @[attr].call @
12
22
 
13
- @_computed = {}
23
+ dependencies = dependencies.split(',') if _.isString(dependencies)
14
24
 
15
- for attr, dependencies of @computed
16
- @on "change:#{attr}", ()=>
17
- @_computed[attr] = @[attr].call @
25
+ _(dependencies).each (dep)=>
26
+ @on "change:#{dep}", ()=>
27
+ @trigger "change:#{attr}"
28
+
29
+ @trigger "change:#{attr}" if @has(dep)
18
30
 
19
- _(dependencies).each (dep)=>
20
- @on "change:#{dep}", ()=>
21
- @trigger "change:#{attr}"
22
- @trigger "change:#{attr}" if @has(dep)
31
+
32
+ _.def('Luca.Model').extends('Backbone.Model').with
33
+ include: ['Luca.Events']
34
+
35
+ initialize: ()->
36
+ Backbone.Model::initialize @, arguments
37
+ setupComputedProperties.call(@)
23
38
 
24
39
  get: (attr)->
25
40
  if @computed?.hasOwnProperty(attr)
@@ -43,55 +43,12 @@ _.def("Luca.components.Panel").extends("Luca.View").with
43
43
  loadMask: false
44
44
  loadMaskTemplate: ["components/load_mask"]
45
45
  loadMaskTimeout: 3000
46
+
47
+ mixins:["LoadMaskable"]
46
48
 
47
49
  initialize: (@options={})->
48
50
  Luca.View::initialize.apply(@, arguments)
49
-
50
- _.bindAll @, "applyLoadMask", "disableLoadMask"
51
-
52
- if @loadMask is true
53
- @defer ()=>
54
- @$el.addClass('with-mask')
55
-
56
- if @$('.load-mask').length is 0
57
- @loadMaskTarget().prepend Luca.template(@loadMaskTemplate, @)
58
- @$('.load-mask').hide()
59
- .until("after:render")
60
-
61
- @on "enable:loadmask", @applyLoadMask
62
- @on "disable:loadmask", @applyLoadMask
63
-
64
- loadMaskTarget: ()->
65
- if @loadMaskEl? then @$(@loadMaskEl) else @$bodyEl()
66
-
67
- disableLoadMask: ()->
68
- @$('.load-mask .bar').css("width","100%")
69
- @$('.load-mask').hide()
70
- clearInterval(@loadMaskInterval)
71
-
72
- enableLoadMask: ()->
73
- @$('.load-mask').show().find('.bar').css("width","0%")
74
- maxWidth = @$('.load-mask .progress').width()
75
- if maxWidth < 20 and (maxWidth = @$el.width()) < 20
76
- maxWidth = @$el.parent().width()
77
-
78
- @loadMaskInterval = setInterval ()=>
79
- currentWidth = @$('.load-mask .bar').width()
80
- newWidth = currentWidth + 12
81
- @$('.load-mask .bar').css('width', newWidth)
82
- , 200
83
-
84
- return unless @loadMaskTimeout?
85
-
86
- _.delay ()=>
87
- @disableLoadMask()
88
- , @loadMaskTimeout
89
-
90
- applyLoadMask: ()->
91
- if @$('.load-mask').is(":visible")
92
- @disableLoadMask()
93
- else
94
- @enableLoadMask()
51
+
95
52
 
96
53
  applyStyles: (styles={},body=false)->
97
54
 
@@ -137,6 +94,9 @@ _.def("Luca.components.Panel").extends("Luca.View").with
137
94
  $template: (template, variables={})->
138
95
  @$html( Luca.template(template,variables) )
139
96
 
97
+ $empty: ()->
98
+ @$bodyEl().empty()
99
+
140
100
  $html: (content)->
141
101
  @$bodyEl().html( content )
142
102
 
@@ -61,10 +61,24 @@ Luca.registry.namespaces = (resolve=true)->
61
61
  _( registry.namespaces ).map (namespace)->
62
62
  if resolve then Luca.util.resolve( namespace ) else namespace
63
63
 
64
+ Luca.registry.aliases =
65
+ grid: "grid_view"
66
+ form: "form_view"
67
+ text: "text_field"
68
+ button: "button_field"
69
+ select: "select_field"
70
+ card: "card_view"
71
+ paged: "card_view"
72
+ wizard: "card_view"
73
+ collection: "collection_view"
74
+
64
75
  # Lookup a component in the Luca component registry
65
76
  # by it's ctype identifier. If it doesn't exist,
66
77
  # check any other registered namespace
67
78
  Luca.registry.lookup = (ctype)->
79
+ if alias = Luca.registry.aliases[ ctype ]
80
+ ctype = alias
81
+
68
82
  c = registry.classes[ctype]
69
83
 
70
84
  return c if c?
@@ -76,13 +90,16 @@ Luca.registry.lookup = (ctype)->
76
90
  fullPath = _( parents ).chain().map((parent)->
77
91
  parent[className]).compact().value()?[0]
78
92
 
93
+ Luca.registry.instances = ()->
94
+ _( component_cache.cid_index ).values()
95
+
79
96
  Luca.registry.findInstancesByClassName = (className)->
80
- instances = _( component_cache.cid_index ).values()
97
+ instances = Luca.registry.instances()
81
98
  _( instances ).select (instance)->
82
99
  instance.displayName is className or instance._superClass?()?.displayName is className
83
100
 
84
101
  Luca.registry.classes = (toString=false)->
85
- _( registry.classes ).map (className, ctype)->
102
+ _(_.extend({},registry.classes,registry.model_classes,registry.collection_classes)).map (className, ctype)->
86
103
  if toString
87
104
  className
88
105
  else
@@ -0,0 +1,32 @@
1
+ class Luca.ScriptLoader
2
+ @loaded: {}
3
+
4
+ constructor: (options={})->
5
+ _.extend(@, Backbone.Events, Luca.Events)
6
+ @autoStart = options.autoStart is true
7
+ @scripts = options.scripts
8
+
9
+ ready = ()-> @trigger("ready")
10
+
11
+ @ready = _.after( @scripts.length, ready)
12
+
13
+ _.bindAll @, "load", "ready"
14
+
15
+ @defer("load").until(@, "start")
16
+
17
+ if @autoStart is true
18
+ @trigger("start")
19
+
20
+ @bind "ready", @onReady
21
+
22
+ applyPrefix: (script)->
23
+ script
24
+
25
+ onReady: ()->
26
+ console.log "All dependencies loaded"
27
+
28
+ start: ()->
29
+ @trigger("start")
30
+
31
+ load: ()->
32
+ Luca.util.loadScript( @applyPrefix(script), @ready ) for script in @scripts
data/src/core/view.coffee CHANGED
@@ -1,27 +1,8 @@
1
- #### Luca Base View
2
-
3
- # The Luca.View class adds some very commonly used patterns
4
- # and functionality to the stock Backbone.View class. Features
5
- # such as auto event binding, the facilitation of deferred rendering
6
- # against a Backbone.Model or Backbone.Collection reset event, Caching
7
- # views into a global Component Registry, and more.
8
-
9
1
  _.def("Luca.View").extends("Backbone.View").with
2
+ include: ['Luca.Events']
10
3
 
11
4
  additionalClassNames:[]
12
5
 
13
- debug: ()->
14
- return unless @debugMode or window.LucaDebugMode?
15
- console.log [(@name || @cid),message] for message in arguments
16
-
17
- trigger: ()->
18
- if Luca.enableGlobalObserver
19
- if Luca.developmentMode is true or @observeEvents is true
20
- Luca.ViewObserver ||= new Luca.Observer(type:"view")
21
- Luca.ViewObserver.relay @, arguments
22
-
23
- Backbone.View.prototype.trigger.apply @, arguments
24
-
25
6
  hooks:[
26
7
  "after:initialize"
27
8
  "before:render"
@@ -32,39 +13,64 @@ _.def("Luca.View").extends("Backbone.View").with
32
13
  ]
33
14
 
34
15
  initialize: (@options={})->
16
+ @trigger "before:initialize", @, @options
35
17
 
36
18
  _.extend @, @options
37
19
 
38
20
  @cid = _.uniqueId(@name) if @name?
39
21
 
22
+ templateVars = if @bodyTemplateVars
23
+ @bodyTemplateVars.call(@)
24
+ else
25
+ @
26
+
40
27
  if template = @bodyTemplate
41
28
  @$el.empty()
42
- Luca.View::$html.call(@, Luca.template(template, @) )
29
+ Luca.View::$html.call(@, Luca.template(template, templateVars ) )
43
30
 
44
- #### View Caching
45
- #
46
- # Luca.View(s) which get created get stored in a global cache by their
47
- # component id. This allows us to re-use views when it makes sense
48
31
  Luca.cache( @cid, @ )
49
32
 
50
- unique = _( Luca.View.prototype.hooks.concat( @hooks ) ).uniq()
51
-
52
- @setupHooks( unique )
33
+ @setupHooks _( Luca.View::hooks.concat( @hooks ) ).uniq()
53
34
 
54
- if @autoBindEventHandlers is true
55
- @bindAllEventHandlers()
35
+ bindAllEventHandlers.call(@) if @autoBindEventHandlers is true or @bindAllEvents is true
56
36
 
57
37
  if @additionalClassNames
58
38
  @additionalClassNames = @additionalClassNames.split(" ") if _.isString(@additionalClassNames)
39
+
40
+ if @gridSpan
41
+ @additionalClassNames.push "span#{ @gridSpan }"
42
+
43
+ if @gridOffset
44
+ @additionalClassNames.push "offset#{ @gridOffset }"
45
+
46
+ if @gridRowFluid
47
+ @additionalClassNames.push "row-fluid"
48
+
49
+ if @gridRow
50
+ @additionalClassNames.push "row"
51
+
52
+ if @additionalClassNames?.length > 0
59
53
  @$el.addClass( additional ) for additional in @additionalClassNames
60
54
 
61
- @trigger "after:initialize", @
55
+ @$wrap( @wrapperClass ) if @wrapperClass?
62
56
 
63
- @registerCollectionEvents()
57
+ registerCollectionEvents.call(@)
58
+ registerApplicationEvents.call( @)
64
59
 
65
60
  @delegateEvents()
66
61
 
67
- #### JQuery / DOM Selector Helpers
62
+ if @stateful is true and not @state?
63
+ @state = new Backbone.Model(@defaultState || {})
64
+ unless @set?
65
+ @set = _.bind @, @state.set
66
+ @get = _.bind @, @state.get
67
+
68
+ if @mixins?.length > 0
69
+ for module in @mixins
70
+ Luca.modules[ module ]._included.call(@, @, module)
71
+
72
+ @trigger "after:initialize", @
73
+
68
74
  $wrap: (wrapper)->
69
75
  if _.isString(wrapper) and not wrapper.match(/[<>]/)
70
76
  wrapper = @make("div",class:wrapper)
@@ -80,17 +86,12 @@ _.def("Luca.View").extends("Backbone.View").with
80
86
  $append: (content)->
81
87
  @$el.append( content )
82
88
 
83
- #### Containers
84
- #
85
- # Luca is heavily reliant on the concept of Container views. Views which
86
- # contain other views and handle inter-component communication between the
87
- # component views. The default render() operation consists of building the
88
- # view's content, and then attaching that view to its container.
89
- #
90
- # 99% of the time this would happen automatically
91
89
  $attach: ()->
92
90
  @$container().append( @el )
93
91
 
92
+ $bodyEl: ()->
93
+ @$el
94
+
94
95
  $container: ()->
95
96
  $(@container)
96
97
 
@@ -120,120 +121,38 @@ _.def("Luca.View").extends("Backbone.View").with
120
121
 
121
122
  @bind eventId, callback
122
123
 
123
-
124
- #### Luca.Collection and Luca.CollectionManager integration
125
-
126
- # under the hood, this will find your collection manager using
127
- # Luca.CollectionManager.get, which is a function that returns
128
- # the first instance of the CollectionManager class ever created.
129
- #
130
- # if you want to use more than one collection manager, over ride this
131
- # function in your views with your own logic
132
- getCollectionManager: ()->
133
- @collectionManager || Luca.CollectionManager.get?()
134
-
135
- ##### Collection Events
136
- #
137
- # By defining a hash of collectionEvents in the form of
138
- #
139
- # "books add" : "onBookAdd"
140
- #
141
- # the Luca.View will bind to the collection found in the
142
- # collectionManager with that key, and bind to that event.
143
- # a property of @booksCollection will be created on the view,
144
- # and the "add" event will trigger "onBookAdd"
145
- #
146
- # you may also specify a function directly. this
147
- #
148
- registerCollectionEvents: ()->
149
- manager = @getCollectionManager()
150
-
151
- _( @collectionEvents ).each (handler, signature)=>
152
- [key,event] = signature.split(" ")
153
-
154
- collection = @["#{ key }Collection"] = manager.getOrCreate( key )
155
-
156
- if !collection
157
- throw "Could not find collection specified by #{ key }"
158
-
159
- if _.isString(handler)
160
- handler = @[handler]
161
-
162
- unless _.isFunction(handler)
163
- throw "invalid collectionEvents configuration"
164
-
165
- try
166
- collection.bind(event, handler)
167
- catch e
168
- console.log "Error Binding To Collection in registerCollectionEvents", @
169
- throw e
170
-
171
124
  registerEvent: (selector, handler)->
172
125
  @events ||= {}
173
126
  @events[ selector ] = handler
174
127
  @delegateEvents()
175
128
 
176
- bindAllEventHandlers: ()->
177
- _( @events ).each (handler,event)=>
178
- if _.isString(handler)
179
- _.bindAll @, handler
180
-
181
129
  definitionClass: ()->
182
130
  Luca.util.resolve(@displayName, window)?.prototype
183
131
 
184
- # refreshCode happens whenever the Luca.Framework extension
185
- # system is run after there are running instances of a given component
186
-
187
- # in the context of views, what this means is that each eventHandler which
188
- # is bound to a specific object via _.bind or _.bindAll, or autoBindEventHandlers
189
- # is refreshed with the prototype method of the component that it inherits from,
190
- # and then delegateEvents is called to refresh any of the updated event handlers
191
-
192
- # in addition to this, all properties of the instance of a given view which are
193
- # also backbone views will have the same process run against them
194
- refreshCode: ()->
195
- view = @
196
-
197
- _( @eventHandlerProperties() ).each (prop)->
198
- view[ prop ] = view.definitionClass()[prop]
199
-
200
- if @autoBindEventHandlers is true
201
- @bindAllEventHandlers()
202
-
203
- @delegateEvents()
204
-
205
- eventHandlerProperties: ()->
206
- handlerIds = _( @events ).values()
207
- _( handlerIds ).select (v)->
208
- _.isString(v)
209
-
210
- eventHandlerFunctions: ()->
211
- handlerIds = _( @events ).values()
212
- _( handlerIds ).map (handlerId)=>
213
- if _.isFunction(handlerId) then handlerId else @[handlerId]
214
-
215
132
  collections: ()-> Luca.util.selectProperties( Luca.isBackboneCollection, @ )
216
133
  models: ()-> Luca.util.selectProperties( Luca.isBackboneModel, @ )
217
134
  views: ()-> Luca.util.selectProperties( Luca.isBackboneView, @ )
218
135
 
136
+ debug: ()->
137
+ return unless @debugMode or window.LucaDebugMode?
138
+ console.log [(@name || @cid),message] for message in arguments
139
+
140
+ trigger: ()->
141
+ if Luca.enableGlobalObserver
142
+ if Luca.developmentMode is true or @observeEvents is true
143
+ Luca.ViewObserver ||= new Luca.Observer(type:"view")
144
+ Luca.ViewObserver.relay @, arguments
145
+
146
+ Backbone.View.prototype.trigger.apply @, arguments
147
+
219
148
 
220
149
  originalExtend = Backbone.View.extend
221
150
 
222
151
  customizeRender = (definition)->
223
- #### Rendering
224
- #
225
- # Our base view class wraps the defined render() method
226
- # of the views which inherit from it, and does things like
227
- # trigger the before and after render events automatically.
228
- # In addition, if the view has a deferrable property on it
229
- # then it will make sure that the render method doesn't get called
230
- # until.
231
-
232
152
  _base = definition.render
233
153
 
234
154
  _base ||= Luca.View::$attach
235
155
 
236
-
237
156
  definition.render = ()->
238
157
  view = @
239
158
  # if a view has a deferrable property set
@@ -274,10 +193,64 @@ customizeRender = (definition)->
274
193
 
275
194
  definition
276
195
 
277
- # By overriding Backbone.View.extend we are able to intercept
278
- # some method definitions and add special behavior around them
279
- # mostly related to render()
196
+ bindAllEventHandlers = ()->
197
+ _( @events ).each (handler,event)=>
198
+ if _.isString(handler)
199
+ _.bindAll @, handler
200
+
201
+ registerApplicationEvents = ()->
202
+ return if _.isEmpty(@applicationEvents)
203
+
204
+ app = @app
205
+
206
+ if _.isString( app ) or _.isUndefined( app )
207
+ app = Luca.Application?.get?(app)
208
+
209
+ unless Luca.supportsEvents( app )
210
+ throw "Error binding to the application object on #{ @name || @cid }"
211
+
212
+ for eventTrigger, handler in @applicationEvents
213
+ handler = @[handler] if _.isString(handler)
214
+
215
+ unless _.isFunction(handler)
216
+ throw "Error registering application event #{ eventTrigger } on #{ @name || @cid }"
217
+
218
+ app.on(eventTrigger, handler)
219
+
220
+ registerCollectionEvents = ()->
221
+ return if _.isEmpty( @collectionEvents )
222
+
223
+ manager = @collectionManager
224
+
225
+ if _.isString( manager ) or _.isUndefined( manager )
226
+ manager = Luca.CollectionManager.get( manager )
227
+
228
+ for signature, handler of @collectionEvents
229
+ console.log "Sig", signature, "Handler", handler
230
+ [key,eventTrigger] = signature.split(" ")
231
+
232
+ collection = manager.getOrCreate( key )
233
+
234
+ if !collection
235
+ throw "Could not find collection specified by #{ key }"
236
+
237
+ if _.isString(handler)
238
+ handler = @[handler]
239
+
240
+ unless _.isFunction(handler)
241
+ throw "invalid collectionEvents configuration"
242
+
243
+ try
244
+ collection.bind(eventTrigger, handler)
245
+ catch e
246
+ console.log "Error Binding To Collection in registerCollectionEvents", @
247
+ throw e
248
+
249
+
280
250
  Luca.View.extend = (definition)->
281
251
  definition = customizeRender( definition )
282
- originalExtend.call(@, definition)
283
252
 
253
+ if definition.mixins? and _.isArray( definition.mixins )
254
+ _.extend(definition, Luca.modules[module]) for module in definition.mixins
255
+
256
+ originalExtend.call(@, definition)
data/src/define.coffee ADDED
@@ -0,0 +1,110 @@
1
+ # Component Definition Helpers
2
+ #
3
+ #
4
+ # We have customized the core Backbone.extend process to use a slightly
5
+ # different syntax, which allows us to intercept the component definition at
6
+ # various points, and maintain information about classes being defined, and
7
+ # the relationships between inherited classes, etc.
8
+
9
+ # _.def, or Luca.define returns a chainable object which allows you to define
10
+ # your components with a readable syntax. For example:
11
+
12
+ # _.def("Luca.View").extends("Backbone.View").with the_good:"shit"
13
+ # _.def("MyView").extends("Luca.View").with the_custom:"shit"
14
+
15
+ Luca.define = (componentName)->
16
+ new DefineProxy(componentName)
17
+
18
+ Luca.component = Luca.define
19
+
20
+ # The define proxy chain sets up a call to Luca.extend, which is a wrapper around Luca and Backbone component class' extend function.
21
+ class DefineProxy
22
+ constructor:(componentName)->
23
+ @namespace = Luca.util.namespace()
24
+ @componentId = @componentName = componentName
25
+
26
+ if componentName.match(/\./)
27
+ @namespaced = true
28
+ parts = componentName.split('.')
29
+ @componentId = parts.pop()
30
+ @namespace = parts.join('.')
31
+
32
+ # automatically add the namespace to the namespace registry
33
+ Luca.registry.addNamespace( parts.join('.') )
34
+
35
+ # allow for specifying the namespace
36
+ in: (@namespace)-> @
37
+
38
+ # allow for multiple ways of saying the same thing for readability purposes
39
+ from: (@superClassName)-> @
40
+ extends: (@superClassName)-> @
41
+ extend: (@superClassName)-> @
42
+
43
+ # an alias for with, or a readability helper in multi-line definitions
44
+ enhance: (properties)->
45
+ return @with(properties) if properties?
46
+ @
47
+
48
+ # which properties, methods, etc will you be extending the base class with?
49
+ with: (properties)->
50
+ at = if @namespaced
51
+ Luca.util.resolve(@namespace, (window || global))
52
+ else
53
+ (window||global)
54
+
55
+ # automatically create the namespace
56
+ if @namespaced and not at?
57
+ eval("(window||global).#{ @namespace } = {}")
58
+ at = Luca.util.resolve(@namespace,(window || global))
59
+
60
+ at[@componentId] = Luca.extend(@superClassName,@componentName, properties)
61
+
62
+ if Luca.autoRegister is true
63
+ componentType = "view" if Luca.isViewPrototype( at[@componentId] )
64
+
65
+ if Luca.isCollectionPrototype( at[@componentId] )
66
+ Luca.Collection.namespaces ||= []
67
+ Luca.Collection.namespaces.push( @namespace )
68
+ componentType = "collection"
69
+
70
+ componentType = "model" if Luca.isModelPrototype( at[@componentId] )
71
+
72
+ # automatically register this with the component registry
73
+ Luca.register( _.string.underscored(@componentId), @componentName, componentType)
74
+
75
+ at[@componentId]
76
+
77
+ # The last method of the DefineProxy chain is always going to result in
78
+ # a call to Luca.extend. Luca.extend wraps the call to Luca.View.extend,
79
+ # or Backbone.Collection.extend, and accepts the names of the extending,
80
+ # and extended classes as strings. This allows us to maintain information
81
+ # and references to the classes and their prototypes, mainly for the purposes
82
+ # of introspection and development tools
83
+ Luca.extend = (superClassName, childName, properties={})->
84
+ superClass = Luca.util.resolve( superClassName, (window || global) )
85
+
86
+ unless _.isFunction(superClass?.extend)
87
+ throw "#{ superClassName } is not a valid component to extend from"
88
+
89
+ properties.displayName = childName
90
+
91
+ properties._superClass = ()->
92
+ superClass.displayName ||= superClassName
93
+ superClass
94
+
95
+ properties._super = (method, context, args)->
96
+ @_superClass().prototype[method]?.apply(context, args)
97
+
98
+ definition = superClass.extend(properties)
99
+
100
+ # _.def("MyView").extends("View").with
101
+ # include: ['Luca.Events']
102
+ if _.isArray( properties?.include )
103
+ for mixin in properties.include
104
+ mixin = Luca.util.resolve(mixin) if _.isString(mixin)
105
+ _.extend(definition::, mixin)
106
+
107
+ definition
108
+
109
+ _.mixin
110
+ def: Luca.define
data/src/framework.coffee CHANGED
@@ -19,7 +19,7 @@
19
19
  return fallback()
20
20
 
21
21
  _.extend Luca,
22
- VERSION: "0.9.2"
22
+ VERSION: "0.9.4"
23
23
  core: {}
24
24
  containers: {}
25
25
  components: {}
@@ -27,6 +27,7 @@ _.extend Luca,
27
27
  util: {}
28
28
  fields: {}
29
29
  registry:{}
30
+ options: {}
30
31
 
31
32
  # for triggering / binding to component definitions
32
33
  _.extend Luca, Backbone.Events
@@ -79,21 +80,27 @@ Luca.isComponentPrototype = (obj)->
79
80
  Luca.isViewPrototype(obj) or Luca.isModelPrototype(obj) or Luca.isCollectionPrototype(obj)
80
81
 
81
82
  Luca.isBackboneModel = (obj)->
83
+ obj = Luca.util.resolve(obj) if _.isString(obj)
82
84
  _.isFunction(obj?.set) and _.isFunction(obj?.get) and _.isObject(obj?.attributes)
83
85
 
84
86
  Luca.isBackboneView = (obj)->
87
+ obj = Luca.util.resolve(obj) if _.isString(obj)
85
88
  _.isFunction(obj?.render) and !_.isUndefined(obj?.el)
86
89
 
87
90
  Luca.isBackboneCollection = (obj)->
91
+ obj = Luca.util.resolve(obj) if _.isString(obj)
88
92
  _.isFunction(obj?.fetch) and _.isFunction(obj?.reset)
89
93
 
90
94
  Luca.isViewPrototype = (obj)->
95
+ obj = Luca.util.resolve(obj) if _.isString(obj)
91
96
  obj? and obj::? and obj::make? and obj::$? and obj::render?
92
97
 
93
98
  Luca.isModelPrototype = (obj)->
99
+ obj = Luca.util.resolve(obj) if _.isString(obj)
94
100
  obj? and obj::? obj::save? and obj::changedAttributes?
95
101
 
96
102
  Luca.isCollectionPrototype = (obj)->
103
+ obj = Luca.util.resolve(obj) if _.isString(obj)
97
104
  obj? and obj::? and !Luca.isModelPrototype(obj) and obj::reset? and obj::select? and obj::reject?
98
105
 
99
106
  Luca.inheritanceChain = (obj)->
@@ -168,7 +175,6 @@ Luca.available_templates = (filter="")->
168
175
  available
169
176
 
170
177
 
171
-
172
178
  UnderscoreExtensions =
173
179
  module: (base,module)->
174
180
  _.extend base, module