luca 0.8.599 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -0
  3. data/CHANGELOG +51 -2
  4. data/README.md +10 -247
  5. data/ROADMAP +6 -2
  6. data/app.rb +16 -2
  7. data/assets/javascripts/dependencies/bootstrap.min.js +7 -1
  8. data/assets/javascripts/dependencies/codemirror-coffeescript.js +347 -0
  9. data/assets/javascripts/dependencies/codemirror-css.js +124 -0
  10. data/assets/javascripts/dependencies/codemirror-html.js +410 -0
  11. data/assets/javascripts/dependencies/codemirror-javascript.js +361 -0
  12. data/assets/javascripts/dependencies/codemirror-less.js +232 -0
  13. data/assets/javascripts/dependencies/codemirror-vim.js +500 -0
  14. data/assets/javascripts/dependencies/codemirror.js +3076 -0
  15. data/assets/javascripts/dependencies.coffee +0 -1
  16. data/assets/javascripts/luca-ui-base.coffee +10 -3
  17. data/assets/javascripts/luca-ui-bootstrap.js +1 -0
  18. data/assets/javascripts/luca-ui-development-tools.coffee +9 -0
  19. data/assets/javascripts/luca-ui.coffee +6 -1
  20. data/assets/javascripts/sandbox/application.coffee +51 -0
  21. data/assets/javascripts/sandbox/router.coffee +14 -0
  22. data/assets/javascripts/sandbox/templates/main.luca +33 -0
  23. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +1 -0
  24. data/assets/javascripts/sandbox/templates/sandbox.luca +1 -0
  25. data/assets/javascripts/sandbox/views/top_navigation.coffee +4 -0
  26. data/assets/javascripts/sandbox.coffee +2 -2
  27. data/assets/stylesheets/bootstrap.min.css +395 -297
  28. data/assets/stylesheets/codemirror-blackboard.css +25 -0
  29. data/assets/stylesheets/codemirror-monokai.css +33 -0
  30. data/assets/stylesheets/codemirror.css +126 -0
  31. data/assets/stylesheets/luca-ui-bootstrap.css +0 -1
  32. data/assets/stylesheets/luca-ui-development-tools.css +5 -0
  33. data/assets/stylesheets/sandbox/sandbox.scss +1 -3
  34. data/assets/stylesheets/themes/amelia-bootstrap.css +826 -0
  35. data/assets/stylesheets/themes/slate-bootstrap.css +797 -0
  36. data/assets/stylesheets/themes/superhero-bootstrap.css +830 -0
  37. data/lib/luca/code_browser.rb +55 -0
  38. data/lib/luca/rails/version.rb +1 -1
  39. data/lib/luca/rails.rb +1 -0
  40. data/spec/components/fields/checkbox_array_spec.coffee +46 -0
  41. data/spec/components/form_view_spec.coffee +10 -4
  42. data/spec/containers/card_view_spec.coffee +7 -0
  43. data/spec/core/collection_spec.coffee +58 -4
  44. data/spec/core/container_spec.coffee +6 -6
  45. data/spec/core/view_spec.coffee +93 -7
  46. data/spec/framework_spec.coffee +15 -12
  47. data/src/components/application.coffee +126 -18
  48. data/src/components/base_toolbar.coffee +2 -2
  49. data/src/components/collection_loader_view.coffee +1 -2
  50. data/src/components/collection_view.coffee +77 -0
  51. data/src/components/controller.coffee +1 -4
  52. data/src/components/fields/button_field.coffee +1 -1
  53. data/src/components/fields/checkbox_array.coffee +2 -2
  54. data/src/components/fields/checkbox_field.coffee +3 -1
  55. data/src/components/fields/file_upload_field.coffee +1 -1
  56. data/src/components/fields/hidden_field.coffee +1 -1
  57. data/src/components/fields/select_field.coffee +1 -1
  58. data/src/components/fields/text_area_field.coffee +1 -1
  59. data/src/components/fields/text_field.coffee +10 -6
  60. data/src/components/fields/type_ahead_field.coffee +18 -5
  61. data/src/components/form_button_toolbar.coffee +1 -2
  62. data/src/components/form_view.coffee +44 -62
  63. data/src/components/grid_view.coffee +27 -20
  64. data/src/components/load_mask.coffee +3 -0
  65. data/src/components/nav_bar.coffee +26 -0
  66. data/src/components/record_manager.coffee +1 -3
  67. data/src/components/router.coffee +1 -1
  68. data/src/components/template.coffee +3 -15
  69. data/src/components/toolbar_dialog.coffee +25 -0
  70. data/src/containers/card_view.coffee +22 -23
  71. data/src/containers/column_view.coffee +1 -6
  72. data/src/containers/modal_view.coffee +20 -71
  73. data/src/containers/panel_toolbar.coffee +156 -0
  74. data/src/containers/panel_view.coffee +1 -1
  75. data/src/containers/split_view.coffee +1 -3
  76. data/src/containers/tab_view.coffee +29 -29
  77. data/src/containers/viewport.coffee +38 -3
  78. data/src/core/collection.coffee +80 -48
  79. data/src/core/container.coffee +153 -72
  80. data/src/core/core.coffee +181 -0
  81. data/src/core/field.coffee +4 -2
  82. data/src/core/model.coffee +1 -1
  83. data/src/core/observer.coffee +3 -3
  84. data/src/core/panel.coffee +143 -0
  85. data/src/core/registry.coffee +104 -0
  86. data/src/core/util.coffee +82 -0
  87. data/src/core/view.coffee +158 -85
  88. data/src/framework.coffee +112 -178
  89. data/src/index.coffee +0 -255
  90. data/src/managers/collection_manager.coffee +1 -0
  91. data/src/samples/definition.coffee +49 -0
  92. data/src/stylesheets/base.scss +0 -78
  93. data/src/stylesheets/components/form_view.scss +8 -3
  94. data/src/stylesheets/components/grid_view.scss +3 -7
  95. data/src/stylesheets/components/load_mask.scss +14 -0
  96. data/src/stylesheets/components/toolbar.scss +0 -15
  97. data/src/stylesheets/containers/container.scss +14 -2
  98. data/src/stylesheets/containers/panels.scss +23 -0
  99. data/src/stylesheets/tools/class_browser.scss +32 -0
  100. data/src/stylesheets/tools/code_editor.scss +24 -0
  101. data/src/stylesheets/tools/component_tester.scss +8 -0
  102. data/src/stylesheets/tools/console.scss +26 -0
  103. data/src/templates/components/collection_loader_view.luca +1 -1
  104. data/src/templates/components/form_view.luca +2 -13
  105. data/src/templates/components/grid_view.luca +0 -2
  106. data/src/templates/components/load_mask.luca +3 -0
  107. data/src/templates/components/nav_bar.luca +2 -0
  108. data/src/templates/containers/tab_view.luca +1 -0
  109. data/src/templates/fields/text_field.luca +4 -1
  110. data/src/tools/class_browser.coffee +39 -0
  111. data/src/tools/code_editor.coffee +258 -0
  112. data/src/tools/code_mirror_field.coffee +57 -0
  113. data/src/tools/coffee_script_editor.coffee +60 -0
  114. data/src/tools/collection_inspector.coffee +4 -0
  115. data/src/tools/component_tester.coffee +472 -0
  116. data/src/tools/components/class_browser_detail.coffee +10 -0
  117. data/src/tools/components/class_browser_list.coffee +74 -0
  118. data/src/tools/console.coffee +147 -0
  119. data/src/tools/development_console.coffee +147 -0
  120. data/src/tools/models/components.coffee +63 -0
  121. data/src/tools/templates/component_tester/help.luca +14 -0
  122. data/vendor/assets/javascripts/luca-ui-base.js +1389 -611
  123. data/vendor/assets/javascripts/luca-ui-bootstrap.js +9 -0
  124. data/vendor/assets/javascripts/luca-ui-development-tools.js +18719 -0
  125. data/vendor/assets/javascripts/luca-ui-spec.js +2065 -878
  126. data/vendor/assets/javascripts/luca-ui.js +1759 -852
  127. data/vendor/assets/javascripts/luca-ui.min.js +3 -3
  128. data/vendor/assets/stylesheets/luca-ui-bootstrap.css +494 -440
  129. data/vendor/assets/stylesheets/luca-ui-development-tools.css +224 -0
  130. data/vendor/assets/stylesheets/luca-ui-spec.css +99 -140
  131. data/vendor/assets/stylesheets/luca-ui.css +99 -140
  132. data/views/index.erb +6 -3
  133. metadata +60 -18
  134. data/assets/javascripts/dependencies/jquery-console.js +0 -649
  135. data/assets/javascripts/development-console.coffee +0 -2
  136. data/assets/javascripts/sandbox/sandbox.coffee +0 -16
  137. data/assets/javascripts/sandbox/templates/features/collection_helpers.luca +0 -33
  138. data/assets/javascripts/sandbox/templates/features/form_demo_code.luca +0 -48
  139. data/assets/javascripts/sandbox/templates/features/grid_demo_code.luca +0 -24
  140. data/assets/javascripts/sandbox/templates/features/introduction.luca +0 -11
  141. data/assets/javascripts/sandbox/templates/features/view_helpers.luca +0 -43
  142. data/assets/javascripts/sandbox/templates/navigation.luca +0 -8
  143. data/assets/javascripts/sandbox/views/form_demo.coffee +0 -47
  144. data/assets/javascripts/sandbox/views/grid_demo.coffee +0 -23
  145. data/assets/javascripts/sandbox/views/pages/collection_events_sample.coffee +0 -1
  146. data/assets/javascripts/sandbox/views/pages/pages_controller.coffee +0 -38
  147. data/src/components/collection_inspector.coffee +0 -2
  148. data/src/components/development_console.coffee +0 -59
  149. data/src/stylesheets/components/development_console.scss +0 -47
@@ -0,0 +1,55 @@
1
+ require 'sinatra/base'
2
+
3
+ module Luca
4
+ class CodeBrowser < Sinatra::Base
5
+
6
+ class << self
7
+ attr_accessor :source_locations, :source_map
8
+ end
9
+
10
+ def self.look_for_source_in location=""
11
+ @source_locations ||= []
12
+ @source_locations << "#{ location }/**/*.coffee" if File.exists?(location)
13
+
14
+ @source_locations
15
+ end
16
+
17
+ def self.map_source
18
+ @source_map = {}
19
+
20
+ (self.source_locations || []).map do |location|
21
+ files = Dir.glob( location )
22
+ files.inject( @source_map ) do |memo, file|
23
+ definitions = IO.read(file).lines.to_a.grep /_\.def/
24
+
25
+ definitions.each do |definition|
26
+ component = definition.match(/_\.def\(['"](.+)['"]\)\./)
27
+
28
+ if component and component[1]
29
+ componentId = component[1].gsub(/['"].*$/,'')
30
+ memo[ componentId ] = file if componentId
31
+ end
32
+ end
33
+
34
+ memo
35
+ end
36
+ end
37
+
38
+ @source_map
39
+ end
40
+
41
+ def self.lookup_component component=""
42
+ map_source[ component ]
43
+ end
44
+
45
+ def self.get_component_source file=""
46
+ IO.read( file ) rescue ""
47
+ end
48
+
49
+ def self.get_source_for component=""
50
+ path_to_file = lookup_component( component )
51
+ get_component_source( path_to_file )
52
+ end
53
+
54
+ end
55
+ end
@@ -1,6 +1,6 @@
1
1
  module Luca
2
2
  module Rails
3
- VERSION = "0.8.599"
3
+ VERSION = "0.9.0"
4
4
  end
5
5
  end
6
6
 
data/lib/luca/rails.rb CHANGED
@@ -2,6 +2,7 @@ module Luca
2
2
  module Rails
3
3
  require 'luca/template'
4
4
  require 'luca/test_harness'
5
+ require 'luca/code_browser'
5
6
  require 'luca/rails/engine'
6
7
  require 'luca/rails/version'
7
8
  end
@@ -0,0 +1,46 @@
1
+ describe 'The Checkbox Array Field', ->
2
+
3
+ beforeEach ->
4
+ @model = new Backbone.Model(item_ids: ["1"])
5
+ collection = new Luca.Collection
6
+
7
+ @formView = new Luca.components.FormView
8
+ components:[
9
+ ctype: "checkbox_array"
10
+ name: 'item_ids'
11
+ collection: collection
12
+ ]
13
+
14
+ @formView.render()
15
+ @formView.loadModel(@model)
16
+
17
+ collection.reset([
18
+ id: "1", name: "Item1"
19
+ ,
20
+ id: "2", name: "Item2"
21
+ ,
22
+ id: "3", name: "Item3"
23
+ ])
24
+
25
+ @field = @formView.getFields()[0]
26
+
27
+ it "should create a checkbox array field", ->
28
+ expect(@formView.currentModel()).toEqual(@model)
29
+ expect(@field.selectedItems).toEqual(["1"])
30
+
31
+ it "should render the list of checkboxes", ->
32
+ expect(@field.$el.html()).toContain("Item1")
33
+ expect(@field.$el.html()).toContain("Item2")
34
+ expect(@field.$el.html()).toContain("Item3")
35
+
36
+ it "should check off each checkbox in the collection that is selected", ->
37
+ expect(@field.$el.find("input[value='1']")[0].checked).toBeTruthy()
38
+ expect(@field.$el.find("input[value='2']")[0].checked).toBeFalsy()
39
+ expect(@field.$el.find("input[value='3']")[0].checked).toBeFalsy()
40
+
41
+ it "should update the form model's attribute to be an array of selected items on click", ->
42
+ checkbox = $(@field.$el.find("input[value='2']")[0])
43
+ checkbox.prop("checked", true)
44
+ checkbox.click()
45
+
46
+ expect(@field.getModel().get('item_ids')).toEqual(["1", "2"])
@@ -40,6 +40,10 @@ describe 'The Form View', ->
40
40
  @form = new FormView()
41
41
  @model = new Model(field0:1,field1:"jonathan",field3:true,field4:"what up player?")
42
42
 
43
+ afterEach ->
44
+ @form = undefined
45
+ @model = undefined
46
+
43
47
  it "should create a form", ->
44
48
  expect( @form ).toBeDefined()
45
49
 
@@ -53,11 +57,13 @@ describe 'The Form View', ->
53
57
  values = @form.getValues()
54
58
  expect( values.field1 ).toEqual "jonathan"
55
59
 
56
- it "should render the components", ->
60
+ it "should render the components within the body element", ->
61
+ @form.render()
62
+ expect( @form.$bodyEl().is('.form-view-body') ).toEqual true
63
+
64
+ it "should assign the components to render inside of the body", ->
57
65
  @form.render()
58
- expect( @form.$el.html() ).toContain "Field Four"
59
- expect( @form.$el.html() ).toContain "Field One"
60
- expect( @form.$el.html() ).toContain "Click Me"
66
+ expect( @form.$bodyEl().html() ).toContain "Field Four"
61
67
 
62
68
  it "should allow me to set the values of the form fields with a hash", ->
63
69
  @form.render()
@@ -19,6 +19,13 @@ describe "The Card View", ->
19
19
 
20
20
  @cardView.render()
21
21
 
22
+ it "should create three card elements", ->
23
+ expect( @cardView.componentElements().length ).toEqual 3
24
+
25
+ it "should hide all but one of the card elements", ->
26
+ display = _( @cardView.$('.luca-ui-card') ).map (el)-> $(el).css('display')
27
+ expect( display ).toEqual(['block','none','none'])
28
+
22
29
  it "should be able to find the cards by name", ->
23
30
  expect( @cardView.find("one") ).toBeDefined()
24
31
  expect( @cardView.find("one").one ).toEqual true
@@ -1,5 +1,47 @@
1
1
  #### Luca.Collection
2
2
 
3
+ setupCollection = ()->
4
+ window.cachedMethodOne = 0
5
+ window.cachedMethodTwo = 0
6
+
7
+ window.CachedMethodCollection = Luca.Collection.extend
8
+ cachedMethods:["cachedMethodOne","cachedMethodTwo"]
9
+
10
+ cachedMethodOne: ()->
11
+ window.cachedMethodOne += 1
12
+
13
+ cachedMethodTwo: ()->
14
+ window.cachedMethodTwo += 1
15
+
16
+ describe "Method Caching", ->
17
+ beforeEach ->
18
+ setupCollection()
19
+ @collection = new CachedMethodCollection()
20
+
21
+ afterEach ->
22
+ @collection = undefined
23
+ window.CachedMethodCollection = undefined
24
+
25
+ it "should call the method", ->
26
+ expect( @collection.cachedMethodOne() ).toEqual 1
27
+
28
+ it "should cache the value of the method", ->
29
+ _( 5 ).times ()=> @collection.cachedMethodOne()
30
+ expect( @collection.cachedMethodOne() ).toEqual 1
31
+
32
+ it "should refresh the method cache upon reset of the models", ->
33
+ _( 3 ).times ()=> @collection.cachedMethodOne()
34
+ expect( @collection.cachedMethodOne() ).toEqual 1
35
+ @collection.reset()
36
+ _( 3 ).times ()=> @collection.cachedMethodOne()
37
+ expect( @collection.cachedMethodOne() ).toEqual 2
38
+
39
+ it "should restore the collection to the original configuration", ->
40
+ @collection.restoreMethodCache()
41
+ _( 5 ).times ()=> @collection.cachedMethodOne()
42
+ expect( @collection.cachedMethodOne() ).toEqual 6
43
+
44
+
3
45
  describe "Luca.Collection", ->
4
46
  it "should accept a name and collection manager", ->
5
47
  mgr = new Luca.CollectionManager()
@@ -36,6 +78,18 @@ describe "Luca.Collection", ->
36
78
 
37
79
  expect( registerSpy ).toHaveBeenCalled()
38
80
 
81
+ it "should query collection with filter", ->
82
+ models = []
83
+ models.push id: i, key: 'value' for i in [0..9]
84
+ models[3].key = 'specialValue'
85
+
86
+ collection = new Luca.Collection models
87
+
88
+ collection.applyFilter key: 'specialValue'
89
+
90
+ expect(collection.length).toBe 1
91
+ expect(collection.first().get('key')).toBe 'specialValue'
92
+
39
93
  describe "The ifLoaded helper", ->
40
94
  it "should fire the passed callback automatically if there are models", ->
41
95
  spy = sinon.spy()
@@ -171,7 +225,7 @@ describe "The Model Bootstrap", ->
171
225
 
172
226
  it "should fetch the cached models from the bootstrap", ->
173
227
  collection = new Luca.Collection [],
174
- cached: ()-> "sample"
228
+ cache_key: ()-> "sample"
175
229
 
176
230
  collection.fetch()
177
231
 
@@ -180,14 +234,14 @@ describe "The Model Bootstrap", ->
180
234
 
181
235
  it "should reference the cached models", ->
182
236
  collection = new Luca.Collection [],
183
- cached: ()-> "sample"
237
+ cache_key: ()-> "sample"
184
238
 
185
239
  expect( collection.cached_models().length ).toEqual(5)
186
240
 
187
241
  it "should avoid making an API call", ->
188
242
  spy = sinon.spy( Backbone.Collection.prototype.fetch )
189
243
  collection = new Luca.Collection [],
190
- cached: ()-> "sample"
244
+ cache_key: ()-> "sample"
191
245
 
192
246
  collection.fetch()
193
247
  expect( spy.called ).toBeFalsy()
@@ -196,7 +250,7 @@ describe "The Model Bootstrap", ->
196
250
  spy = sinon.spy()
197
251
 
198
252
  collection = new Luca.Collection [],
199
- cached: ()-> "sample"
253
+ cache_key: ()-> "sample"
200
254
  url: ()-> "/models"
201
255
 
202
256
  collection.bind "after:response", spy
@@ -3,15 +3,15 @@ describe 'The Luca Container', ->
3
3
  @container = new Luca.core.Container
4
4
  components:[
5
5
  name: "component_one"
6
- ctype: "template"
7
- markup: "markup for component one"
6
+ ctype: "view"
7
+ bodyTemplate: ()-> "markup for component one"
8
8
  id: "c1"
9
9
  value: 1
10
10
  spy: sinon.spy()
11
11
  ,
12
12
  name: "component_two"
13
- ctype: "template"
14
- markup: "markup for component two"
13
+ ctype: "view"
14
+ bodyTemplate: ()-> "markup for component two"
15
15
  id: "c2"
16
16
  value: 0
17
17
  spy: sinon.spy()
@@ -22,9 +22,9 @@ describe 'The Luca Container', ->
22
22
  value: 1
23
23
  spy: sinon.spy()
24
24
  components:[
25
- ctype: "template"
25
+ ctype: "view"
26
26
  name: "component_four"
27
- markup: "markup for component four"
27
+ bodyTemplate: ()-> "markup for component four"
28
28
  spy: sinon.spy()
29
29
  ]
30
30
  ]
@@ -1,8 +1,4 @@
1
1
  describe "Luca.View", ->
2
- Custom = Luca.View.extend
3
- clickHandler: sinon.spy()
4
- autoBindEventHandlers: true
5
-
6
2
  it "should be defined", ->
7
3
  expect(Luca.View).toBeDefined()
8
4
 
@@ -16,16 +12,99 @@ describe "Luca.View", ->
16
12
 
17
13
  it "should register the view in the cache", ->
18
14
  view = new Luca.View(name:"cached")
19
- expect( Luca.cache("cached") ).toBeDefined()
15
+ expect( Luca.cache("cached") ).toEqual(view)
20
16
 
21
17
  it "should trigger after initialize", ->
22
18
  view = new Luca.View()
23
19
  expect( view ).toHaveTriggered("after:initialize")
24
20
 
25
- it "should auto-bind event handlers", ->
26
- # pending
21
+ it "should be picked up by the isBackboneView helper", ->
22
+ view = new Luca.View()
23
+ expect( Luca.isBackboneView(view) ).toEqual true
24
+
25
+ it "should be picked up by the isBackboneComponent helper", ->
26
+ view = new Luca.View()
27
+ expect( Luca.isComponent(view) ).toEqual true
28
+
29
+ it "should be picked up by the supportsBackboneEvents helper", ->
30
+ view = new Luca.View()
31
+ expect( Luca.supportsBackboneEvents(view) ).toEqual true
32
+
33
+ it "should append additional class names to the view's $el", ->
34
+ view = new Luca.View(additionalClassNames:["yes-yes","yall"])
35
+ expect( view.$el.is(".yes-yes.yall") ).toEqual true
36
+
37
+ it "should accept a string for additional class names", ->
38
+ view = new Luca.View(additionalClassNames:"yes-yes yall")
39
+ expect( view.$el.is(".yes-yes.yall") ).toEqual true
40
+
41
+
42
+ describe "Introspection Helpers", ->
43
+ beforeEach ->
44
+ @view = new Luca.View
45
+ events:
46
+ "click .a" : "clickHandler"
47
+ "hover .a" : "hoverHandler"
48
+
49
+ clickHandler: ()-> "click"
50
+ hoverHandler: ()-> "hover"
51
+
52
+ collection_one: new Luca.Collection([],name:"collection_one")
53
+ collection_two: new Luca.Collection([],name:"collection_two")
54
+ view_one: new Luca.View(name:"view_one")
55
+ view_two: new Luca.View(name:"view_two")
56
+ model_one: new Luca.Model(name:"model_one")
57
+ model_two: new Luca.Model(name:"model_two")
58
+
59
+ it "should know the names of functions which are event handlers", ->
60
+ names = @view.eventHandlerProperties()
61
+ expect( names ).toEqual ["clickHandler","hoverHandler"]
27
62
 
63
+ it "should know which properties are other views", ->
64
+ viewNames = _( @view.views() ).pluck("name")
65
+ expect( viewNames ).toEqual ["view_one","view_two"]
28
66
 
67
+ it "should know which properties are other models", ->
68
+ modelNames = _( @view.models() ).map (m)-> m.get('name')
69
+ expect( modelNames ).toEqual ["model_one","model_two"]
70
+
71
+ it "should know which properties are other collections", ->
72
+ collectionNames = _( @view.collections() ).pluck("name")
73
+ expect( collectionNames ).toEqual ["collection_one","collection_two"]
74
+
75
+ describe "DOM Helper Methods", ->
76
+ it "should use the $html method to inject into the $el", ->
77
+ view = new Luca.View()
78
+ view.$html('haha')
79
+ expect( view.$html() ).toEqual 'haha'
80
+
81
+ describe "Deferrable Rendering", ->
82
+ beforeEach ->
83
+ @fetchSpy = sinon.spy()
84
+ @customSpy = sinon.spy()
85
+
86
+ @collection = new Luca.Collection
87
+ url: "/models"
88
+ fetch: @fetchSpy
89
+ custom: @customSpy
90
+ name: "haha"
91
+
92
+ @DeferrableView = Luca.View.extend
93
+ name: "deferrable_view"
94
+ deferrable: @collection
95
+
96
+ @TriggeredView = Luca.View.extend
97
+ deferrable: @collection
98
+ deferrable_method: "custom"
99
+
100
+ it "should automatically call fetch on the collection ", ->
101
+ ( new @DeferrableView ).render()
102
+ @server.respond()
103
+ expect( @fetchSpy ).toHaveBeenCalled()
104
+
105
+ it "should call a custom method if configured", ->
106
+ ( new @TriggeredView ).render()
107
+ expect( @customSpy ).toHaveBeenCalled()
29
108
 
30
109
  describe "Hooks", ->
31
110
  it "should have before and after render hooks", ->
@@ -85,3 +164,10 @@ describe "The Collection Events API", ->
85
164
  collection = @manager.get("sample")
86
165
  collection.reset([])
87
166
  expect( view.resetHandler ).toHaveBeenCalled()
167
+
168
+
169
+ describe "Code Refresh", ->
170
+ beforeEach ->
171
+
172
+ it "should reference the event handler function property names", ->
173
+ it "should reference the event handler functions", ->
@@ -18,7 +18,7 @@ describe "The Luca Framework", ->
18
18
 
19
19
  it "should allow me to add view namespaces to the registry", ->
20
20
  Luca.registry.addNamespace("Test.namespace")
21
- expect( Luca.registry.namespaces ).toContain("Test.namespace")
21
+ expect( Luca.registry.namespaces(false) ).toContain("Test.namespace")
22
22
 
23
23
  it "should resolve a value.string to the object", ->
24
24
  window.nested =
@@ -31,18 +31,17 @@ describe "The Luca Framework", ->
31
31
 
32
32
  it "should create an instance of a class by ctype", ->
33
33
  object =
34
- ctype: "template"
35
- template: "components/form_view"
34
+ ctype: "view"
36
35
 
37
36
  component = Luca.util.lazyComponent(object)
38
- expect( _.isFunction(component.render) ).toBeTruthy()
37
+
38
+ expect( Luca.isBackboneView(component) ).toEqual true
39
39
 
40
40
  it "should find a created view in the cache", ->
41
- template = new Luca.components.Template
42
- template: "components/form_view"
41
+ template = new Luca.View
43
42
  name: 'test_template'
44
43
 
45
- expect(Luca.cache("test_template")).toBeDefined()
44
+ expect( Luca.isBackboneView( Luca.cache("test_template") ) ).toEqual true
46
45
 
47
46
  it "should detect if an object is probably a backbone view", ->
48
47
  obj =
@@ -79,14 +78,14 @@ describe "Luca Component Definition", ->
79
78
  expect( Luca.random ).toBeDefined()
80
79
 
81
80
  it "should automatically register the namespace in the registry", ->
82
- expect( Luca.registry.namespaces ).toContain 'Luca.random'
81
+ expect( Luca.registry.namespaces() ).toContain Luca.random
83
82
 
84
83
  it "should automatically register the component in the registry", ->
85
84
  expect( Luca.registry.lookup("component_definition") ).toBeDefined()
86
85
 
87
86
  it "should reference the name of the extending class", ->
88
87
  instance = new Luca.random.ComponentDefinition
89
- expect( instance._className ).toEqual "Luca.random.ComponentDefinition"
88
+ expect( instance.displayName ).toEqual "Luca.random.ComponentDefinition"
90
89
 
91
90
  it "should reference the extended class", ->
92
91
  instance = new Luca.random.ComponentDefinition
@@ -94,12 +93,16 @@ describe "Luca Component Definition", ->
94
93
 
95
94
  it "should reference the name of the extended class", ->
96
95
  instance = new Luca.random.ComponentDefinition
97
- expect( instance._superClass()._className ).toEqual 'Luca.View'
96
+ expect( instance._superClass().displayName ).toEqual 'Luca.View'
98
97
 
99
98
  it "should use the backbone.extend functionality properly", ->
100
99
  instance = new Luca.random.ComponentDefinition
101
100
  expect( instance.property ).toEqual "value"
102
101
 
103
- it "should alias to _.component", ->
104
- proxy = _.component('Luca.random.ComponentDefition')
102
+ it "should alias to _.def", ->
103
+ proxy = _.def('Luca.random.ComponentDefition')
105
104
  expect( proxy.with ).toBeDefined()
105
+
106
+ it "should allow me to set the namespace before the definition", ->
107
+ Luca.util.namespace("Luca.View")
108
+ expect( Luca.util.namespace() ).toEqual Luca.View
@@ -1,25 +1,77 @@
1
- _.component('Luca.Application').extends('Luca.containers.Viewport').with
2
-
1
+ _.def('Luca.Application').extends('Luca.containers.Viewport').with
2
+
3
+ # automatically starts the @router
4
+ # if it exists, once the components
5
+ # for the application have been created
6
+ autoStartHistory: true
7
+
8
+ # we will create a collection manager singleton
9
+ # by default unless otherwise specified
10
+ useCollectionManager: true
11
+
12
+ # Luca plugin apps are apps which mount onto existing
13
+ # luca apps, and will not have the behavior of a main
14
+ # app which acts as a singleton
15
+ plugin: false
16
+
17
+ # by default, the application will use a controller
18
+ # component, which is a card view container which shows
19
+ # one view at a time. this is useful for having an application
20
+ # with several 'pages' so to speak
21
+ useController: true
22
+
23
+ #### Nested Components
24
+
25
+ # applications have one component, the controller.
26
+ # any components defined on the application class directly
27
+ # will get wrapped by the main controller unless you
28
+ # set useController = false
3
29
  components:[
4
- ctype: 'controller'
5
- name: 'main_controller'
6
- defaultCard: 'welcome'
7
- components:[
8
- ctype: 'template'
9
- name: 'welcome'
10
- template: 'sample/welcome'
11
- templateContainer: "Luca.templates"
12
- ]
30
+ ctype: 'template'
31
+ name: 'welcome'
32
+ template: 'sample/welcome'
33
+ templateContainer: "Luca.templates"
13
34
  ]
14
35
 
15
36
  initialize: (@options={})->
16
37
  Luca.containers.Viewport::initialize.apply @, arguments
17
38
 
18
- @collectionManager ||= Luca.CollectionManager.get?() || new Luca.CollectionManager()
39
+ if @useController is true
40
+ definedComponents = @components || []
41
+
42
+ @components = [
43
+ ctype: 'controller'
44
+ name: "main_controller"
45
+ components: definedComponents
46
+ ]
47
+
48
+ if @useCollectionManager is true
49
+ @collectionManager ||= Luca.CollectionManager.get?()
50
+ @collectionManager ||= new Luca.CollectionManager( @collectionManagerOptions||={} )
19
51
 
20
52
  @state = new Backbone.Model( @defaultState )
21
53
 
22
- @bind "ready", ()=> @render()
54
+ # we will render when all of the various components
55
+ # which handle our data dependencies determine that
56
+ # we are ready
57
+ @bind "ready", ()=>
58
+ @render()
59
+ # the keyRouter allows us to specify
60
+ # keyEvents on our application with an API very similar
61
+ # to the DOM events API for Backbone.View
62
+ #
63
+ # Example:
64
+ #
65
+ # keyEvents:
66
+ # meta:
67
+ # forwardslash: "altSlashHandler"
68
+ @setupKeyRouter() if @useKeyRouter is true and @keyEvents?
69
+
70
+ # if the application is a plugin designed to modify the behavior
71
+ # of another app, then don't claim ownership. otherwise the most common
72
+ # use-case is that there will be one application instance
73
+ unless @plugin is true
74
+ Luca.getApplication = ()=> @
23
75
 
24
76
  activeView: ()->
25
77
  if active = @activeSubSection()
@@ -33,6 +85,17 @@ _.component('Luca.Application').extends('Luca.containers.Viewport').with
33
85
  activeSection: ()->
34
86
  @get("active_section")
35
87
 
88
+ beforeRender: ()->
89
+ Luca.containers.Viewport::beforeRender?.apply(@, arguments)
90
+
91
+ if @router? and @autoStartHistory is true
92
+ routerStartEvent = @startRouterOn || "after:render"
93
+
94
+ if routerStartEvent is "before:render"
95
+ Backbone.history.start()
96
+ else
97
+ @bind routerStartEvent, ()-> Backbone.history.start()
98
+
36
99
  afterComponents: ()->
37
100
  Luca.containers.Viewport::afterComponents?.apply @, arguments
38
101
 
@@ -48,9 +111,6 @@ _.component('Luca.Application').extends('Luca.containers.Viewport').with
48
111
  component.bind "after:card:switch", (previous,current)=>
49
112
  @state.set(active_sub_section:current.name)
50
113
 
51
- beforeRender: ()->
52
- Luca.containers.Viewport::beforeRender?.apply @, arguments
53
- #Backbone.history.start()
54
114
 
55
115
  # boot should trigger the ready event, which will call the initial call
56
116
  # to render() your application, which will have a cascading effect on every
@@ -72,10 +132,58 @@ _.component('Luca.Application').extends('Luca.containers.Viewport').with
72
132
  @state.get(attribute)
73
133
 
74
134
  getMainController: ()->
75
- @view("main_controler")
135
+ return @components[0] if @useController is true
136
+ Luca.cache('main_controller')
76
137
 
77
138
  set: (attributes)->
78
139
  @state.set(attributes)
79
140
 
80
- view: (name)-> Luca.cache(name)
141
+ view: (name)->
142
+ Luca.cache(name)
143
+
144
+ #### Navigation Hooks
145
+ #
146
+ # delegate to the main controller so that we can switch the active section
147
+ navigate_to: (component_name, callback)->
148
+ @getMainController().navigate_to(component_name, callback)
149
+
150
+ setupKeyRouter: ()->
151
+ return unless @keyEvents
152
+
153
+ @keyEvents.control_meta ||= {}
154
+
155
+ # allow for both meta_control, control_meta for the combo
156
+ _.extend(@keyEvents.control_meta, @keyEvents.meta_control) if @keyEvents.meta_control
157
+
158
+ router = _.bind(@keyRouter, @)
159
+
160
+ $( document ).keydown( router )
161
+
162
+ #### Key Router
163
+ #
164
+ # TODO: Define a syntax for mapping combinations of meta, control, and keycodes
165
+ # to some sort of method delegation system that the application handles.
166
+ keyRouter: (e)->
167
+ return unless e and @keyEvents
168
+
169
+ isInputEvent = $(e.target).is('input') || $(e.target).is('textarea')
170
+
171
+ return if isInputEvent
172
+
173
+ keyname = Luca.keyMap[ e.keyCode ]
174
+
175
+ return unless keyname
176
+
177
+ meta = e?.metaKey is true
178
+ control = e?.ctrlKey is true
179
+
180
+ source = @keyEvents
181
+ source = if meta then @keyEvents.meta else source
182
+ source = if control then @keyEvents.control else source
183
+ source = if meta and control then @keyEvents.meta_control else source
81
184
 
185
+ if keyEvent = source?[keyname]
186
+ if @[keyEvent]?
187
+ @[keyEvent]?.call(@)
188
+ else
189
+ @trigger(keyEvent)
@@ -1,4 +1,4 @@
1
- _.component('Luca.components.Toolbar').extends('Luca.core.Container').with
1
+ _.def('Luca.components.Toolbar').extends('Luca.core.Container').with
2
2
 
3
3
  className: 'luca-ui-toolbar'
4
4
 
@@ -9,7 +9,7 @@ _.component('Luca.components.Toolbar').extends('Luca.core.Container').with
9
9
 
10
10
  prepareComponents: ()->
11
11
  _( @components ).each (component)=>
12
- component.container = @el
12
+ component.container = @$el
13
13
 
14
14
  render: ()->
15
15
  $(@container).append(@el)