luca 0.6.6

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 (233) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +17 -0
  3. data/Gemfile.lock +77 -0
  4. data/Guardfile +22 -0
  5. data/README.md +291 -0
  6. data/Rakefile +28 -0
  7. data/app.rb +46 -0
  8. data/assets/images/glyphicons-halflings-white.png +0 -0
  9. data/assets/images/glyphicons-halflings.png +0 -0
  10. data/assets/javascripts/dependencies/backbone-min.js +37 -0
  11. data/assets/javascripts/dependencies/backbone-query.min.js +1 -0
  12. data/assets/javascripts/dependencies/bootstrap.min.js +1 -0
  13. data/assets/javascripts/dependencies/jasmine-html.js +190 -0
  14. data/assets/javascripts/dependencies/jasmine.js +2476 -0
  15. data/assets/javascripts/dependencies/jquery.js +4 -0
  16. data/assets/javascripts/dependencies/modal.js +698 -0
  17. data/assets/javascripts/dependencies/modernizr.min.js +30 -0
  18. data/assets/javascripts/dependencies/prettify.js +28 -0
  19. data/assets/javascripts/dependencies/sinon.js +3469 -0
  20. data/assets/javascripts/dependencies/spin-min.js +2 -0
  21. data/assets/javascripts/dependencies/underscore-min.js +31 -0
  22. data/assets/javascripts/dependencies/underscore-string.min.js +14 -0
  23. data/assets/javascripts/dependencies.coffee +7 -0
  24. data/assets/javascripts/luca-ui-base.coffee +12 -0
  25. data/assets/javascripts/luca-ui-spec.coffee +2 -0
  26. data/assets/javascripts/luca-ui.coffee +3 -0
  27. data/assets/javascripts/sandbox/collections/sample.coffee +0 -0
  28. data/assets/javascripts/sandbox/config.coffee +7 -0
  29. data/assets/javascripts/sandbox/sandbox.coffee +16 -0
  30. data/assets/javascripts/sandbox/templates/features/collection_helpers.luca +33 -0
  31. data/assets/javascripts/sandbox/templates/features/form_demo_code.luca +48 -0
  32. data/assets/javascripts/sandbox/templates/features/grid_demo_code.luca +24 -0
  33. data/assets/javascripts/sandbox/templates/features/introduction.luca +11 -0
  34. data/assets/javascripts/sandbox/templates/features/view_helpers.luca +43 -0
  35. data/assets/javascripts/sandbox/templates/navigation.luca +8 -0
  36. data/assets/javascripts/sandbox/views/form_demo.coffee +47 -0
  37. data/assets/javascripts/sandbox/views/grid_demo.coffee +23 -0
  38. data/assets/javascripts/sandbox/views/pages/collection_events_sample.coffee +1 -0
  39. data/assets/javascripts/sandbox/views/pages/pages_controller.coffee +28 -0
  40. data/assets/javascripts/sandbox.coffee +4 -0
  41. data/assets/javascripts/spec-dependencies.coffee +4 -0
  42. data/assets/stylesheets/bootstrap-responsive.min.css +3 -0
  43. data/assets/stylesheets/bootstrap.min.css +610 -0
  44. data/assets/stylesheets/jasmine.css +166 -0
  45. data/assets/stylesheets/luca-ui-bootstrap.css +5 -0
  46. data/assets/stylesheets/luca-ui-spec.css +3 -0
  47. data/assets/stylesheets/luca-ui.css +3 -0
  48. data/assets/stylesheets/prettify.css +40 -0
  49. data/assets/stylesheets/sandbox/sandbox.scss +4 -0
  50. data/assets/stylesheets/sandbox.css +3 -0
  51. data/config.ru +11 -0
  52. data/lib/luca/command_line.rb +69 -0
  53. data/lib/luca/rails/engine.rb +12 -0
  54. data/lib/luca/rails/version.rb +6 -0
  55. data/lib/luca/rails.rb +9 -0
  56. data/lib/luca/template.rb +51 -0
  57. data/lib/luca/test_harness.rb +106 -0
  58. data/lib/luca.rb +1 -0
  59. data/lib/sprockets/luca_template.rb +49 -0
  60. data/lib/templates/spec_manifest_javascripts.erb +7 -0
  61. data/lib/templates/spec_manifest_stylesheets.erb +11 -0
  62. data/luca.gemspec +26 -0
  63. data/public/jasmine/index.html +26 -0
  64. data/public/sandbox/api.js +1 -0
  65. data/spec/components/application_spec.coffee +0 -0
  66. data/spec/components/collection_loader_view_spec.coffee +0 -0
  67. data/spec/components/controller_spec.coffee +0 -0
  68. data/spec/components/form_view_spec.coffee +13 -0
  69. data/spec/components/grid_view_spec.coffee +0 -0
  70. data/spec/components/record_manager_spec.coffee +0 -0
  71. data/spec/components/template_spec.coffee +0 -0
  72. data/spec/containers/card_view_spec.coffee +1 -0
  73. data/spec/containers/column_view_spec.coffee +0 -0
  74. data/spec/containers/modal_view_spec.coffee +0 -0
  75. data/spec/containers/panel_view_spec.coffee +0 -0
  76. data/spec/containers/split_view_spec.coffee +0 -0
  77. data/spec/containers/tab_view_spec.coffee +0 -0
  78. data/spec/containers/viewport_spec.coffee +0 -0
  79. data/spec/core/collection_spec.coffee +215 -0
  80. data/spec/core/container_spec.coffee +0 -0
  81. data/spec/core/field_spec.coffee +0 -0
  82. data/spec/core/observer_spec.coffee +0 -0
  83. data/spec/core/view_spec.coffee +87 -0
  84. data/spec/framework_spec.coffee +48 -0
  85. data/spec/helper.coffee +120 -0
  86. data/spec/managers/collection_manager_spec.coffee +95 -0
  87. data/spec/managers/socket_manager_spec.coffee +0 -0
  88. data/src/components/application.coffee +83 -0
  89. data/src/components/base_toolbar.coffee +16 -0
  90. data/src/components/collection_loader_view.coffee +37 -0
  91. data/src/components/controller.coffee +41 -0
  92. data/src/components/fields/button_field.coffee +40 -0
  93. data/src/components/fields/checkbox_field.coffee +41 -0
  94. data/src/components/fields/file_upload_field.coffee +15 -0
  95. data/src/components/fields/hidden_field.coffee +18 -0
  96. data/src/components/fields/select_field.coffee +100 -0
  97. data/src/components/fields/text_area_field.coffee +43 -0
  98. data/src/components/fields/text_field.coffee +42 -0
  99. data/src/components/fields/type_ahead_field.coffee +10 -0
  100. data/src/components/form_button_toolbar.coffee +26 -0
  101. data/src/components/form_view.coffee +205 -0
  102. data/src/components/grid_view.coffee +208 -0
  103. data/src/components/record_manager.coffee +215 -0
  104. data/src/components/router.coffee +34 -0
  105. data/src/components/template.coffee +19 -0
  106. data/src/containers/card_view.coffee +89 -0
  107. data/src/containers/column_view.coffee +48 -0
  108. data/src/containers/modal_view.coffee +85 -0
  109. data/src/containers/panel_view.coffee +24 -0
  110. data/src/containers/split_view.coffee +12 -0
  111. data/src/containers/tab_view.coffee +77 -0
  112. data/src/containers/viewport.coffee +16 -0
  113. data/src/core/collection.coffee +319 -0
  114. data/src/core/container.coffee +256 -0
  115. data/src/core/field.coffee +68 -0
  116. data/src/core/observer.coffee +17 -0
  117. data/src/core/view.coffee +190 -0
  118. data/src/framework.coffee +110 -0
  119. data/src/index.coffee +255 -0
  120. data/src/managers/collection_manager.coffee +168 -0
  121. data/src/managers/socket_manager.coffee +54 -0
  122. data/src/modules/deferrable.coffee +18 -0
  123. data/src/modules/local_storage.coffee +50 -0
  124. data/src/stylesheets/base.scss +78 -0
  125. data/src/stylesheets/components/form_view.scss +54 -0
  126. data/src/stylesheets/components/grid_view.scss +111 -0
  127. data/src/stylesheets/components/toolbar.scss +15 -0
  128. data/src/stylesheets/containers/container.scss +7 -0
  129. data/src/stylesheets/containers/modal_view.scss +0 -0
  130. data/src/stylesheets/containers/tab_view.scss +33 -0
  131. data/src/stylesheets/normalize.scss +430 -0
  132. data/src/templates/components/bootstrap_form_controls.luca +7 -0
  133. data/src/templates/components/collection_loader_view.luca +5 -0
  134. data/src/templates/components/form_view.luca +15 -0
  135. data/src/templates/components/grid_view.luca +9 -0
  136. data/src/templates/components/grid_view_empty_text.luca +3 -0
  137. data/src/templates/containers/basic.luca +1 -0
  138. data/src/templates/containers/tab_selector_container.luca +8 -0
  139. data/src/templates/containers/tab_view.luca +1 -0
  140. data/src/templates/containers/toolbar_wrapper.luca +1 -0
  141. data/src/templates/fields/button_field.luca +2 -0
  142. data/src/templates/fields/button_field_link.luca +5 -0
  143. data/src/templates/fields/checkbox_field.luca +9 -0
  144. data/src/templates/fields/file_upload_field.luca +8 -0
  145. data/src/templates/fields/hidden_field.luca +1 -0
  146. data/src/templates/fields/select_field.luca +7 -0
  147. data/src/templates/fields/text_area_field.luca +8 -0
  148. data/src/templates/fields/text_field.luca +13 -0
  149. data/src/templates/sample/contents.luca +1 -0
  150. data/src/templates/sample/welcome.luca +1 -0
  151. data/vendor/assets/images/glyphicons-halflings-white.png +0 -0
  152. data/vendor/assets/images/glyphicons-halflings.png +0 -0
  153. data/vendor/assets/javascripts/luca-spec-dependencies.js +6135 -0
  154. data/vendor/assets/javascripts/luca-ui-base.js +1527 -0
  155. data/vendor/assets/javascripts/luca-ui-spec.js +3654 -0
  156. data/vendor/assets/javascripts/luca-ui.js +2763 -0
  157. data/vendor/assets/luca-ui/base.css +85 -0
  158. data/vendor/assets/luca-ui/components/application.js +91 -0
  159. data/vendor/assets/luca-ui/components/base_toolbar.js +23 -0
  160. data/vendor/assets/luca-ui/components/controller.js +38 -0
  161. data/vendor/assets/luca-ui/components/fields/button_field.js +45 -0
  162. data/vendor/assets/luca-ui/components/fields/checkbox_field.js +43 -0
  163. data/vendor/assets/luca-ui/components/fields/file_upload_field.js +20 -0
  164. data/vendor/assets/luca-ui/components/fields/hidden_field.js +20 -0
  165. data/vendor/assets/luca-ui/components/fields/select_field.js +97 -0
  166. data/vendor/assets/luca-ui/components/fields/text_area_field.js +48 -0
  167. data/vendor/assets/luca-ui/components/fields/text_field.js +46 -0
  168. data/vendor/assets/luca-ui/components/fields/type_ahead_field.js +13 -0
  169. data/vendor/assets/luca-ui/components/form_button_toolbar.js +32 -0
  170. data/vendor/assets/luca-ui/components/form_view.css +32 -0
  171. data/vendor/assets/luca-ui/components/form_view.js +207 -0
  172. data/vendor/assets/luca-ui/components/grid_view.css +76 -0
  173. data/vendor/assets/luca-ui/components/grid_view.js +202 -0
  174. data/vendor/assets/luca-ui/components/record_manager.js +207 -0
  175. data/vendor/assets/luca-ui/components/router.js +36 -0
  176. data/vendor/assets/luca-ui/components/template.js +26 -0
  177. data/vendor/assets/luca-ui/components/toolbar.css +11 -0
  178. data/vendor/assets/luca-ui/containers/card_view.js +98 -0
  179. data/vendor/assets/luca-ui/containers/column_view.js +52 -0
  180. data/vendor/assets/luca-ui/containers/container.css +3 -0
  181. data/vendor/assets/luca-ui/containers/modal_view.css +0 -0
  182. data/vendor/assets/luca-ui/containers/modal_view.js +87 -0
  183. data/vendor/assets/luca-ui/containers/panel_view.js +34 -0
  184. data/vendor/assets/luca-ui/containers/split_view.js +13 -0
  185. data/vendor/assets/luca-ui/containers/tab_view.css +16 -0
  186. data/vendor/assets/luca-ui/containers/tab_view.js +80 -0
  187. data/vendor/assets/luca-ui/containers/viewport.js +18 -0
  188. data/vendor/assets/luca-ui/core/collection.js +221 -0
  189. data/vendor/assets/luca-ui/core/container.js +205 -0
  190. data/vendor/assets/luca-ui/core/field.js +59 -0
  191. data/vendor/assets/luca-ui/core/observer.js +42 -0
  192. data/vendor/assets/luca-ui/core/view.js +127 -0
  193. data/vendor/assets/luca-ui/framework.js +110 -0
  194. data/vendor/assets/luca-ui/index.js +5 -0
  195. data/vendor/assets/luca-ui/managers/collection_manager.js +98 -0
  196. data/vendor/assets/luca-ui/managers/socket_manager.js +52 -0
  197. data/vendor/assets/luca-ui/modules/deferrable.js +21 -0
  198. data/vendor/assets/luca-ui/modules/local_storage.js +81 -0
  199. data/vendor/assets/luca-ui/normalize.css +359 -0
  200. data/vendor/assets/luca-ui/stylesheets/base.css +85 -0
  201. data/vendor/assets/luca-ui/stylesheets/components/form_view.css +32 -0
  202. data/vendor/assets/luca-ui/stylesheets/components/grid_view.css +76 -0
  203. data/vendor/assets/luca-ui/stylesheets/components/toolbar.css +11 -0
  204. data/vendor/assets/luca-ui/stylesheets/containers/container.css +3 -0
  205. data/vendor/assets/luca-ui/stylesheets/containers/modal_view.css +0 -0
  206. data/vendor/assets/luca-ui/stylesheets/containers/tab_view.css +16 -0
  207. data/vendor/assets/luca-ui/stylesheets/normalize.css +359 -0
  208. data/vendor/assets/luca-ui/templates/components/bootstrap_form_controls.js +4 -0
  209. data/vendor/assets/luca-ui/templates/components/form_view.js +4 -0
  210. data/vendor/assets/luca-ui/templates/components/grid_view.js +4 -0
  211. data/vendor/assets/luca-ui/templates/components/grid_view_empty_text.js +4 -0
  212. data/vendor/assets/luca-ui/templates/containers/basic.js +4 -0
  213. data/vendor/assets/luca-ui/templates/containers/tab_selector_container.js +4 -0
  214. data/vendor/assets/luca-ui/templates/containers/tab_view.js +4 -0
  215. data/vendor/assets/luca-ui/templates/containers/toolbar_wrapper.js +4 -0
  216. data/vendor/assets/luca-ui/templates/fields/button_field.js +4 -0
  217. data/vendor/assets/luca-ui/templates/fields/button_field_link.js +4 -0
  218. data/vendor/assets/luca-ui/templates/fields/checkbox_field.js +4 -0
  219. data/vendor/assets/luca-ui/templates/fields/file_upload_field.js +4 -0
  220. data/vendor/assets/luca-ui/templates/fields/hidden_field.js +4 -0
  221. data/vendor/assets/luca-ui/templates/fields/select_field.js +4 -0
  222. data/vendor/assets/luca-ui/templates/fields/text_area_field.js +4 -0
  223. data/vendor/assets/luca-ui/templates/fields/text_field.js +4 -0
  224. data/vendor/assets/luca-ui/templates/sample/contents.js +4 -0
  225. data/vendor/assets/luca-ui/templates/sample/welcome.js +4 -0
  226. data/vendor/assets/stylesheets/luca-spec-dependencies.css +166 -0
  227. data/vendor/assets/stylesheets/luca-ui-bootstrap.css +1201 -0
  228. data/vendor/assets/stylesheets/luca-ui-spec.css +586 -0
  229. data/vendor/assets/stylesheets/luca-ui.css +586 -0
  230. data/views/index.erb +20 -0
  231. data/views/jasmine.erb +22 -0
  232. data/views/spec_harness.erb +29 -0
  233. metadata +361 -0
@@ -0,0 +1,205 @@
1
+ Luca.components.FormView = Luca.core.Container.extend
2
+ tagName: 'form'
3
+
4
+ className: 'luca-ui-form-view'
5
+
6
+ hooks:[
7
+ "before:submit"
8
+ "before:reset"
9
+ "before:load"
10
+ "before:load:new"
11
+ "before:load:existing"
12
+ "after:submit"
13
+ "after:reset"
14
+ "after:load"
15
+ "after:load:new"
16
+ "after:load:existing"
17
+ "after:submit:success"
18
+ "after:submit:fatal_error"
19
+ "after:submit:error"
20
+ ]
21
+
22
+ events:
23
+ "click .submit-button" : "submitHandler"
24
+ "click .reset-button" : "resetHandler"
25
+
26
+ toolbar: true
27
+
28
+ initialize: (@options={})->
29
+ Luca.core.Container::initialize.apply @, arguments
30
+
31
+ _.bindAll @, "submitHandler", "resetHandler", "renderToolbars"
32
+
33
+ @state ||= new Backbone.Model
34
+
35
+ @setupHooks( @hooks )
36
+
37
+ @legend ||= ""
38
+
39
+ @configureToolbars()
40
+ @applyStyles()
41
+
42
+ addBootstrapFormControls: ()->
43
+ @bind "after:render", ()=>
44
+ el = @$('.toolbar-container.bottom')
45
+
46
+ el.addClass('form-controls')
47
+ el.html @formControlsTemplate || Luca.templates["components/bootstrap_form_controls"](@)
48
+
49
+ applyStyles: ()->
50
+ @applyBootstrapStyles() if Luca.enableBootstrap
51
+
52
+ @$el.addClass( "label-align-#{ @labelAlign }") if @labelAlign
53
+ @$el.addClass( @fieldLayoutClass ) if @fieldLayoutClass
54
+
55
+ applyBootstrapStyles: ()->
56
+ @inlineForm = true if @labelAlign is "left"
57
+
58
+ @$el.addClass('well') if @well
59
+ @$el.addClass('form-search') if @searchForm
60
+ @$el.addClass('form-horizontal') if @horizontalForm
61
+ @$el.addClass('form-inline') if @inlineForm
62
+
63
+ configureToolbars: ()->
64
+ return @addBootstrapFormControls() if Luca.enableBootstrap and @toolbar is true
65
+
66
+ if @toolbar is true
67
+ @toolbars = [
68
+ ctype: 'form_button_toolbar'
69
+ includeReset: true
70
+ position: 'bottom'
71
+ ]
72
+
73
+ if @toolbars and @toolbars.length
74
+ @bind "after:render", _.once @renderToolbars
75
+
76
+ resetHandler: (e)->
77
+ me = my = $( e.currentTarget )
78
+ @trigger "before:reset", @
79
+ @reset()
80
+ @trigger "after:reset", @
81
+
82
+ submitHandler: (e)->
83
+ me = my = $( e.currentTarget )
84
+ @trigger "before:submit", @
85
+ @submit()
86
+
87
+ beforeLayout: ()->
88
+ Luca.core.Container::beforeLayout?.apply @, arguments
89
+ @$el.html Luca.templates["components/form_view"]( @ )
90
+
91
+
92
+ prepareComponents: ()->
93
+ container = $('.form-view-body', @el)
94
+ _( @components ).each (component)->
95
+ component.container = container
96
+
97
+ render: ()->
98
+ $( @container ).append( @$el )
99
+
100
+ wrapper: ()->
101
+ @$el.parents('.luca-ui-form-view-wrapper')
102
+
103
+ toolbarContainers: (position="bottom")->
104
+ $(".toolbar-container.#{ position }", @wrapper() ).first()
105
+
106
+ renderToolbars: ()->
107
+ _( @toolbars ).each (toolbar)=>
108
+ toolbar.container = $("##{ @cid }-#{ toolbar.position }-toolbar-container")
109
+ toolbar = Luca.util.lazyComponent(toolbar)
110
+ toolbar.render()
111
+
112
+ getField: (name)->
113
+ _( @getFields('name', name) ).first()
114
+
115
+ getFields: (attr,value)->
116
+ # do a deep search of all of the nested components
117
+ # to find the fields
118
+ fields = @select("isField", true, true)
119
+
120
+ # if an optional attribute and value pair is passed
121
+ # then you can limit the array of fields even further
122
+ if fields.length > 0 and attr and value
123
+ fields = _(fields).select (field)->
124
+ property = field[ attr ]
125
+ return false unless property?
126
+ propvalue = if _.isFunction(property) then property() else property
127
+ value is propvalue
128
+
129
+ fields
130
+
131
+ loadModel: (@current_model)->
132
+ form = @
133
+ fields = @getFields()
134
+
135
+ @trigger "before:load", @, @current_model
136
+ if @current_model
137
+ event = "before:load:#{ (if @current_model.isNew() then "new" else "existing")}"
138
+ @trigger event, @, @current_model
139
+
140
+ _( fields ).each (field) =>
141
+ field_name = field.input_name || field.name
142
+ value = if _.isFunction(@current_model[ field_name ]) then @current_model[field_name].apply(@, form) else @current_model.get( field_name )
143
+ field?.setValue( value ) unless field.readOnly is true
144
+
145
+ @trigger "after:load", @, @current_model
146
+
147
+ if @current_model
148
+ @trigger "after:load:#{ (if @current_model.isNew() then "new" else "existing")}", @, @current_model
149
+
150
+
151
+ reset: ()->
152
+ @loadModel( @current_model )
153
+
154
+ clear: ()->
155
+ @current_model = if @defaultModel? then @defaultModel() else undefined
156
+
157
+ _( @getFields() ).each (field)=>
158
+ try
159
+ field.setValue('')
160
+ catch e
161
+ console.log "Error Clearing", @, field
162
+
163
+ getValues: (reject_blank=false,skip_buttons=true)->
164
+ _( @getFields() ).inject (memo,field)->
165
+ value = field.getValue()
166
+
167
+ skip = false
168
+ skip = true if skip_buttons and field.ctype is "button_field"
169
+ skip = true if reject_blank and _.isBlank(value)
170
+ skip = true if field.input_name is "id" and _.isBlank(value)
171
+
172
+ memo[ field.input_name || name ] = value unless skip
173
+
174
+ memo
175
+ , {}
176
+
177
+ submit_success_handler: (model, response, xhr)->
178
+ @trigger "after:submit", @, model, response
179
+
180
+ if response and response.success
181
+ @trigger "after:submit:success", @, model, response
182
+ else
183
+ @trigger "after:submit:error", @, model, response
184
+
185
+ submit_fatal_error_handler: ()->
186
+ @trigger.apply ["after:submit", @].concat(arguments)
187
+ @trigger.apply ["after:submit:fatal_error", @].concat(arguments)
188
+
189
+ submit: (save=true, saveOptions={})->
190
+ _.bindAll @, "submit_success_handler", "submit_fatal_error_handler"
191
+
192
+ saveOptions.success ||= @submit_success_handler
193
+ saveOptions.error ||= @submit_fatal_error_handler
194
+
195
+ @current_model.set( @getValues() )
196
+ return unless save
197
+ @current_model.save( @current_model.toJSON(), saveOptions )
198
+
199
+ currentModel: ()->
200
+ @current_model
201
+
202
+ setLegend: (@legend)->
203
+ $('fieldset legend', @el).first().html(@legend)
204
+
205
+ Luca.register 'form_view', 'Luca.components.FormView'
@@ -0,0 +1,208 @@
1
+ Luca.components.GridView = Luca.View.extend
2
+ events:
3
+ "dblclick .grid-view-row" : "double_click_handler"
4
+ "click .grid-view-row": "click_handler"
5
+
6
+ className: 'luca-ui-grid-view'
7
+
8
+ scrollable: true
9
+
10
+ emptyText: 'No Results To display'
11
+
12
+ # available options are striped, condensed, bordered
13
+ # or any combination of these, split up by space
14
+ tableStyle: 'striped'
15
+
16
+ hooks:[
17
+ "before:grid:render",
18
+ "before:render:header",
19
+ "before:render:row",
20
+ "after:grid:render",
21
+ "row:double:click",
22
+ "row:click",
23
+ "after:collection:load"
24
+ ]
25
+
26
+ initialize: (@options={})->
27
+ _.extend @, @options
28
+ _.extend @, Luca.modules.Deferrable
29
+
30
+ Luca.View::initialize.apply( @, arguments )
31
+
32
+ _.bindAll @, "double_click_handler", "click_handler"
33
+
34
+ @configure_collection()
35
+
36
+ @collection.bind "reset", (collection) =>
37
+ @refresh()
38
+ @trigger "after:collection:load", collection
39
+
40
+ beforeRender: ()->
41
+ @trigger "before:grid:render", @
42
+
43
+ @$el.addClass 'scrollable-grid-view' if @scrollable
44
+
45
+ @$el.html Luca.templates["components/grid_view"]()
46
+
47
+ @table = $('table.luca-ui-grid-view', @el)
48
+ @header = $("thead", @table)
49
+ @body = $("tbody", @table)
50
+ @footer = $("tfoot", @table)
51
+
52
+ if Luca.enableBootstrap
53
+ @table.addClass('table')
54
+
55
+ _( @tableStyle?.split(" ") ).each (style)=>
56
+ @table.addClass("table-#{ style }")
57
+
58
+ @setDimensions() if @scrollable
59
+
60
+ @renderHeader()
61
+
62
+ @emptyMessage()
63
+
64
+ @renderToolbars()
65
+
66
+ $(@container).append @$el
67
+
68
+ toolbarContainers:(position="bottom")->
69
+ $(".toolbar-container.#{ position }", @el)
70
+
71
+ renderToolbars: ()->
72
+ _( @toolbars ).each (toolbar)=>
73
+ toolbar = Luca.util.lazyComponent(toolbar)
74
+ toolbar.container = @toolbarContainers( toolbar.position )
75
+ toolbar.render()
76
+
77
+ setDimensions: (offset)->
78
+ @height ||= 285
79
+
80
+ $('.grid-view-body', @el).height( @height )
81
+ $('tbody.scrollable', @el).height( @height - 23 )
82
+
83
+ @container_width = do => $(@container).width()
84
+ @width = if @container_width > 0 then @container_width else 756
85
+
86
+ #@width += offset if offset
87
+
88
+ $('.grid-view-body', @el).width( @width )
89
+ $('.grid-view-body table', @el).width( @width )
90
+
91
+ @setDefaultColumnWidths()
92
+
93
+ resize: (newWidth)->
94
+ difference = newWidth - @width
95
+ @width = newWidth
96
+
97
+ $('.grid-view-body', @el).width( @width )
98
+ $('.grid-view-body table', @el).width( @width )
99
+
100
+ if @columns.length > 0
101
+ distribution = difference / @columns.length
102
+
103
+ _(@columns).each (col,index)=>
104
+ column = $(".column-#{ index }", @el )
105
+ column.width( col.width = col.width + distribution )
106
+
107
+ padLastColumn: ()->
108
+ configured_column_widths = _(@columns).inject (sum, column)->
109
+ sum = (column.width) + sum
110
+ , 0
111
+
112
+ unused_width = @width - configured_column_widths
113
+
114
+ if unused_width > 0
115
+ @lastColumn().width += unused_width
116
+
117
+ setDefaultColumnWidths: ()->
118
+ default_column_width = if @columns.length > 0 then @width / @columns.length else 200
119
+
120
+ _( @columns ).each (column)->
121
+ parseInt(column.width ||= default_column_width)
122
+
123
+ @padLastColumn()
124
+
125
+
126
+ lastColumn: ()->
127
+ @columns[ @columns.length - 1 ]
128
+
129
+ afterRender: ()->
130
+ @refresh()
131
+ @trigger "after:grid:render", @
132
+
133
+ emptyMessage: (text="")->
134
+ text ||= @emptyText
135
+ @body.html('')
136
+ @body.append Luca.templates["components/grid_view_empty_text"](colspan:@columns.length,text:text)
137
+
138
+ refresh: ()->
139
+ @body.html('')
140
+ @collection.each (model,index)=>
141
+ @render_row.apply(@, [model,index])
142
+
143
+ if @collection.models.length == 0
144
+ @emptyMessage()
145
+
146
+ ifLoaded: (fn, scope)->
147
+ scope ||= @
148
+ fn ||= ()-> true
149
+
150
+ @collection.ifLoaded(fn,scope)
151
+
152
+ applyFilter: (values, options={auto:true,refresh:true})->
153
+ @collection.applyFilter(values, options)
154
+
155
+ renderHeader: ()->
156
+ @trigger "before:render:header"
157
+
158
+ headers = _(@columns).map (column,column_index) =>
159
+ # temporary hack for scrollable grid dimensions.
160
+ style = if column.width then "width:#{ column.width }px;" else ""
161
+
162
+ "<th style='#{ style }' class='column-#{ column_index }'>#{ column.header}</th>"
163
+
164
+ @header.append("<tr>#{ headers }</tr>")
165
+
166
+ render_row: (row,row_index)->
167
+ model_id = if row?.get and row?.attributes then row.get('id') else ''
168
+
169
+ @trigger "before:render:row", row, row_index
170
+
171
+ cells = _( @columns ).map (column,col_index) =>
172
+ value = @cell_renderer(row, column, col_index)
173
+ style = if column.width then "width:#{ column.width }px;" else ""
174
+
175
+ display = if _.isUndefined(value) then "" else value
176
+
177
+ "<td style='#{ style }' class='column-#{ col_index }'>#{ display }</td>"
178
+
179
+ if @alternateRowClasses
180
+ alt_class = if row_index % 2 is 0 then "even" else "odd"
181
+
182
+ @body?.append("<tr data-record-id='#{ model_id }' data-row-index='#{ row_index }' class='grid-view-row #{ alt_class }' id='row-#{ row_index }'>#{ cells }</tr>")
183
+
184
+ cell_renderer: (row, column, columnIndex )->
185
+ if _.isFunction column.renderer
186
+ return column.renderer.apply @, [row,column,columnIndex]
187
+ else if column.data.match(/\w+\.\w+/)
188
+ source = row.attributes || row
189
+ return Luca.util.nestedValue( column.data, source )
190
+ else
191
+ return row.get?( column.data ) || row[ column.data ]
192
+
193
+ double_click_handler: (e)->
194
+ me = my = $( e.currentTarget )
195
+ rowIndex = my.data('row-index')
196
+ record = @collection.at( rowIndex )
197
+ @trigger "row:double:click", @, record, rowIndex
198
+
199
+ click_handler: (e)->
200
+ me = my = $( e.currentTarget )
201
+ rowIndex = my.data('row-index')
202
+ record = @collection.at( rowIndex )
203
+ @trigger "row:click", @, record, rowIndex
204
+
205
+ $('.grid-view-row', @body ).removeClass('selected-row')
206
+ me.addClass('selected-row')
207
+
208
+ Luca.register "grid_view","Luca.components.GridView"
@@ -0,0 +1,215 @@
1
+ # The RecordManager is a high level component which incorporates
2
+ # a filterable grid, and an editor form responsible for editing
3
+ # the records in that grid.
4
+ #
5
+ # it provides convenience methods for accesing those components.
6
+ #
7
+ # this represents a clean pattern for having multiple components
8
+ # which work together. inter-component communication should be handled
9
+ # by parent containers, and not individual components, which should
10
+ # usually not be aware of other components.
11
+
12
+ # Work in progress
13
+ Luca.components.RecordManager = Luca.containers.CardView.extend
14
+
15
+ events:
16
+ "click .record-manager-grid .edit-link" : "edit_handler"
17
+ "click .record-manager-filter .filter-button" : "filter_handler"
18
+ "click .record-manager-filter .reset-button" : "reset_filter_handler"
19
+ "click .add-button" : "add_handler"
20
+ "click .refresh-button" : "filter_handler"
21
+ "click .back-to-search-button" : "back_to_search_handler"
22
+
23
+ record_manager: true
24
+
25
+ initialize: (@options={})->
26
+ Luca.containers.CardView::initialize.apply @, arguments
27
+
28
+ throw "Record Managers must specify a name" unless @name
29
+
30
+ _.bindAll @, "add_handler", "edit_handler", "filter_handler", "reset_filter_handler"
31
+
32
+ _.extend @components[0][0], @filterConfig if @filterConfig
33
+ _.extend @components[0][1], @gridConfig if @gridConfig
34
+ _.extend @components[1][0], @editorConfig if @editorConfig
35
+
36
+ @bind "after:card:switch", () =>
37
+ @trigger("activation:search", @) if @activeCard is 0
38
+ @trigger("activation:editor", @) if @activeCard is 1
39
+
40
+ components:[
41
+ ctype: 'split_view',
42
+ relayFirstActivation: true
43
+ components:[
44
+ ctype: 'form_view'
45
+ ,
46
+ ctype: 'grid_view'
47
+ ]
48
+ ,
49
+ ctype: 'form_view'
50
+ ]
51
+
52
+ getSearch: (activate=false, reset=true)->
53
+ @activate(0) if activate is true
54
+ @getEditor().clear() if reset is true
55
+
56
+ _.first(@components)
57
+
58
+ getFilter: ()->
59
+ _.first @getSearch().components
60
+
61
+ getGrid: ()->
62
+ _.last @getSearch().components
63
+
64
+ getCollection: ()->
65
+ @getGrid().collection
66
+
67
+ getEditor: (activate=false,reset=false)->
68
+ if activate is true
69
+ @activate 1, (activator,previous,current)=>
70
+ current.reset()
71
+
72
+ _.last(@components)
73
+
74
+ beforeRender: ()->
75
+ @$el.addClass("#{ @resource }-manager")
76
+ Luca.containers.CardView::beforeRender?.apply @, arguments
77
+
78
+ @$el.addClass("#{ @resource } record-manager")
79
+ @$el.data('resource', @resource)
80
+
81
+ $(@getGrid().el).addClass("#{ @resource } record-manager-grid")
82
+ $(@getFilter().el).addClass("#{ @resource } record-manager-filter")
83
+ $(@getEditor().el).addClass("#{ @resource } record-manager-editor")
84
+
85
+ # This is an example of a best practice from ExtJS
86
+ # that we incorporate in Luca, which is that container
87
+ # components are responsible for component communication.
88
+ #
89
+ # child components should not be aware of other components
90
+ # in the layout. Their parents should be responsible for
91
+ # controlling how they interact. listening to events on one,
92
+ # changing state, inducing effects in the others
93
+ afterRender: ()->
94
+ Luca.containers.CardView::afterRender?.apply @, arguments
95
+
96
+ manager = @
97
+ grid = @getGrid()
98
+ filter = @getFilter()
99
+ editor = @getEditor()
100
+ collection = @getCollection()
101
+
102
+ # when a row is double clicked on the grid
103
+ # then we edit that row
104
+ grid.bind "row:double:click", (grid,model,index)->
105
+ manager.getEditor(true)
106
+ editor.loadModel( model )
107
+
108
+ editor.bind "before:submit", ()=>
109
+ $('.form-view-flash-container', @el).html('')
110
+ $('.form-view-body', @el).spin("large")
111
+
112
+ editor.bind "after:submit", ()=>
113
+ $('.form-view-body', @el).spin(false)
114
+
115
+ editor.bind "after:submit:fatal_error", ()=>
116
+ $('.form-view-flash-container', @el ).append "<li class='error'>There was an internal server error saving this record. Please contact developers@benchprep.com to report this error.</li>"
117
+ $('.form-view-body', @el).spin(false)
118
+
119
+ editor.bind "after:submit:error", (form, model, response)=>
120
+ _( response.errors ).each (error)=>
121
+ $('.form-view-flash-container', @el ).append "<li class='error'>#{ error }</li>"
122
+
123
+ editor.bind "after:submit:success", (form, model, response)=>
124
+ $('.form-view-flash-container', @el).append "<li class='success'>Successfully Saved Record</li>"
125
+
126
+ model.set( response.result )
127
+ form.loadModel( model )
128
+
129
+ grid.refresh()
130
+
131
+ _.delay ()=>
132
+ $('.form-view-flash-container li.success', @el).fadeOut(1000)
133
+ $('.form-view-flash-container', @el).html('')
134
+ , 4000
135
+
136
+ filter.eachComponent (component)=>
137
+ try
138
+ component.bind "on:change", @filter_handler
139
+ catch e
140
+ undefined
141
+
142
+ firstActivation: ()->
143
+ @getGrid().trigger "first:activation", @, @getGrid()
144
+ @getFilter().trigger "first:activation", @, @getGrid()
145
+
146
+ reload: ()->
147
+ manager = @
148
+
149
+ grid = @getGrid()
150
+ filter = @getFilter()
151
+ editor = @getEditor()
152
+
153
+ # refresh the select fields in the filter
154
+ filter.clear()
155
+
156
+ grid.applyFilter()
157
+
158
+ manageRecord: (record_id)->
159
+ model = @getCollection().get(record_id)
160
+ return @loadModel(model) if model
161
+
162
+ console.log "Could Not Find Model, building and fetching"
163
+
164
+ model = @buildModel()
165
+ model.set({id:record_id},{silent:true})
166
+
167
+ model.fetch
168
+ success: (model,response)=>
169
+ @loadModel(model)
170
+
171
+ loadModel: (@current_model)->
172
+ @getEditor(true).loadModel( @current_model )
173
+ @trigger "model:loaded", @current_model
174
+
175
+ currentModel: ()->
176
+ @getEditor(false).currentModel()
177
+
178
+ buildModel: ()->
179
+ editor = @getEditor(false)
180
+ collection = @getCollection()
181
+
182
+ collection.add([{}], silent:true, at: 0)
183
+
184
+ model = collection.at(0)
185
+
186
+ createModel: ()->
187
+ @loadModel(@buildModel())
188
+
189
+ ##### DOM Event Handlers
190
+ reset_filter_handler: (e)->
191
+ @getFilter().clear()
192
+ @getGrid().applyFilter( @getFilter().getValues() )
193
+
194
+ filter_handler: (e)->
195
+ @getGrid().applyFilter( @getFilter().getValues() )
196
+
197
+ edit_handler: (e)->
198
+ me = my = $( e.currentTarget )
199
+ record_id = my.parents('tr').data('record-id')
200
+
201
+ if record_id
202
+ model = @getGrid().collection.get( record_id )
203
+
204
+ model ||= @getGrid().collection.at( row_index )
205
+
206
+
207
+ add_handler: (e)->
208
+ me = my = $( e.currentTarget )
209
+ resource = my.parents('.record-manager').eq(0).data('resource')
210
+
211
+ destroy_handler: (e)->
212
+ #destroy handler
213
+
214
+ back_to_search_handler: ()->
215
+ # search handler
@@ -0,0 +1,34 @@
1
+ Luca.Router = Backbone.Router.extend
2
+ routes:
3
+ "" : "default"
4
+
5
+ initialize: (@options)->
6
+ _.extend @, @options
7
+
8
+ @routeHandlers = _( @routes ).values()
9
+
10
+ # when a route handler is fired, the route:route_name event is triggered by the router
11
+ # unfortunately this doesn't apply to calls to @navigate() so we override Backbone.Router.navigate
12
+ # and trigger an event separately.
13
+ _( @routeHandlers ).each (route_id) =>
14
+ @bind "route:#{ route_id }", ()=>
15
+ @trigger.apply @, ["change:navigation", route_id ].concat( _( arguments ).flatten() )
16
+
17
+ #### Router Functions
18
+
19
+ # Intercept calls to Backbone.Router.navigate so that we can at least
20
+ # build a path from the route, even if we don't trigger the route handler
21
+ navigate: (route, triggerRoute=false)->
22
+ Backbone.Router.prototype.navigate.apply @, arguments
23
+ @buildPathFrom( Backbone.history.getFragment() )
24
+
25
+ # given a url fragment, construct an argument chain similar to what would be
26
+ # emitted from a normal route:#{ name } event that gets triggered
27
+ # when a route is actually fired. This is used to trap route changes that happen
28
+ # through calls to @navigate()
29
+ buildPathFrom: (matchedRoute)->
30
+ _(@routes).each (route_id, route)=>
31
+ regex = @_routeToRegExp(route)
32
+ if regex.test(matchedRoute)
33
+ args = @_extractParameters(regex, matchedRoute)
34
+ @trigger.apply @, ["change:navigation", route_id].concat( args )
@@ -0,0 +1,19 @@
1
+ Luca.components.Template = Luca.View.extend
2
+ initialize: (@options={})->
3
+ Luca.View::initialize.apply @, arguments
4
+ throw "Templates must specify which template / markup to use" unless @template or @markup
5
+
6
+ if _.isString(@templateContainer)
7
+ @templateContainer = eval("(window || global).#{ @templateContainer }")
8
+
9
+ templateContainer: "Luca.templates"
10
+
11
+ beforeRender: ()->
12
+ @templateContainer = JST if _.isUndefined( @templateContainer)
13
+ @$el.html(@markup || @templateContainer[ @template ](@options) )
14
+
15
+ render: ()->
16
+ $(@container).append( @$el )
17
+
18
+
19
+ Luca.register "template", "Luca.components.Template"