joosy 0.1.0.alpha → 1.0.0.RC1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/.codoopts +5 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +15 -2
  4. data/Gemfile.lock +102 -81
  5. data/Guardfile +16 -16
  6. data/LICENSE +22 -0
  7. data/MIT-LICENSE +2 -2
  8. data/README.md +118 -0
  9. data/app/assets/javascripts/joosy/core/application.js.coffee +53 -0
  10. data/app/assets/javascripts/joosy/core/form.js.coffee +338 -0
  11. data/app/assets/javascripts/joosy/core/helpers/form.js.coffee +72 -0
  12. data/app/assets/javascripts/joosy/core/helpers/view.js.coffee +42 -0
  13. data/app/assets/javascripts/joosy/core/helpers/widgets.js.coffee +14 -0
  14. data/app/assets/javascripts/joosy/core/joosy.js.coffee +184 -0
  15. data/app/assets/javascripts/joosy/core/layout.js.coffee +168 -0
  16. data/app/assets/javascripts/joosy/core/modules/container.js.coffee +124 -0
  17. data/app/assets/javascripts/joosy/core/modules/events.js.coffee +122 -0
  18. data/app/assets/javascripts/joosy/core/modules/filters.js.coffee +39 -0
  19. data/app/assets/javascripts/joosy/core/modules/log.js.coffee +36 -0
  20. data/app/assets/javascripts/joosy/core/modules/module.js.coffee +117 -0
  21. data/app/assets/javascripts/joosy/core/modules/renderer.js.coffee +200 -0
  22. data/app/assets/javascripts/joosy/core/modules/time_manager.js.coffee +46 -0
  23. data/app/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +87 -0
  24. data/app/assets/javascripts/joosy/core/page.js.coffee +387 -0
  25. data/app/assets/javascripts/joosy/core/preloader.js.coffee +13 -0
  26. data/app/assets/javascripts/joosy/core/resource/collection.js.coffee +175 -0
  27. data/app/assets/javascripts/joosy/core/resource/generic.js.coffee +303 -0
  28. data/app/assets/javascripts/joosy/core/resource/rest.js.coffee +244 -0
  29. data/app/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +24 -0
  30. data/app/assets/javascripts/joosy/core/router.js.coffee +201 -0
  31. data/app/assets/javascripts/joosy/core/templaters/rails_jst.js.coffee +37 -0
  32. data/app/assets/javascripts/joosy/core/widget.js.coffee +85 -0
  33. data/app/assets/javascripts/joosy/preloaders/caching.js.coffee +169 -0
  34. data/app/assets/javascripts/joosy/preloaders/inline.js.coffee +56 -0
  35. data/{vendor → app}/assets/javascripts/joosy.js.coffee +0 -1
  36. data/app/helpers/joosy/sprockets_helper.rb +39 -12
  37. data/joosy.gemspec +4 -3
  38. data/lib/joosy/rails/engine.rb +12 -1
  39. data/lib/joosy/rails/version.rb +2 -2
  40. data/lib/joosy.rb +9 -0
  41. data/lib/rails/generators/joosy/application_generator.rb +15 -3
  42. data/lib/rails/generators/joosy/joosy_base.rb +2 -2
  43. data/lib/rails/generators/joosy/layout_generator.rb +8 -1
  44. data/lib/rails/generators/joosy/page_generator.rb +21 -6
  45. data/lib/rails/generators/joosy/preloader_generator.rb +14 -7
  46. data/lib/rails/generators/joosy/resource_generator.rb +29 -0
  47. data/lib/rails/generators/joosy/templates/app/helpers/application.js.coffee +4 -0
  48. data/lib/rails/generators/joosy/templates/app/layouts/application.js.coffee +1 -0
  49. data/lib/rails/generators/joosy/templates/app/layouts/template.js.coffee +1 -1
  50. data/lib/rails/generators/joosy/templates/app/pages/application.js.coffee +1 -1
  51. data/lib/rails/generators/joosy/templates/app/pages/template.js.coffee +3 -3
  52. data/lib/rails/generators/joosy/templates/app/pages/welcome/index.js.coffee +22 -0
  53. data/lib/rails/generators/joosy/templates/app/resources/template.js.coffee +2 -0
  54. data/lib/rails/generators/joosy/templates/app/routes.js.coffee +7 -1
  55. data/lib/rails/generators/joosy/templates/app/templates/layouts/application.jst.hamlc +2 -0
  56. data/lib/rails/generators/joosy/templates/app/templates/pages/welcome/index.jst.hamlc +7 -0
  57. data/lib/rails/generators/joosy/templates/app/widgets/template.js.coffee +2 -2
  58. data/lib/rails/generators/joosy/templates/app.js.coffee +4 -0
  59. data/lib/rails/generators/joosy/templates/app_preloader.js.coffee.erb +9 -12
  60. data/lib/rails/generators/joosy/templates/app_resources_predefiner.js.coffee.erb +11 -0
  61. data/lib/rails/generators/joosy/templates/preload.html.erb +5 -6
  62. data/lib/rails/generators/joosy/templates/preload.html.haml +3 -5
  63. data/lib/rails/generators/joosy/widget_generator.rb +8 -1
  64. data/lib/rails/resources_with_joosy.rb +11 -0
  65. data/spec/javascripts/helpers/spec_helper.js.coffee +25 -0
  66. data/spec/javascripts/joosy/core/application_spec.js.coffee +40 -0
  67. data/spec/javascripts/joosy/core/form_spec.js.coffee +200 -0
  68. data/spec/javascripts/joosy/core/helpers/forms_spec.js.coffee +103 -0
  69. data/spec/javascripts/joosy/core/helpers/view_spec.js.coffee +10 -0
  70. data/spec/javascripts/joosy/core/joosy_spec.js.coffee +97 -0
  71. data/spec/javascripts/joosy/core/layout_spec.js.coffee +50 -0
  72. data/spec/javascripts/joosy/core/modules/container_spec.js.coffee +32 -27
  73. data/spec/javascripts/joosy/core/modules/events_spec.js.coffee +55 -18
  74. data/spec/javascripts/joosy/core/modules/filters_spec.js.coffee +28 -27
  75. data/spec/javascripts/joosy/core/modules/log_spec.js.coffee +3 -3
  76. data/spec/javascripts/joosy/core/modules/module_spec.js.coffee +6 -15
  77. data/spec/javascripts/joosy/core/modules/renderer_spec.js.coffee +203 -0
  78. data/spec/javascripts/joosy/core/modules/time_manager_spec.js.coffee +12 -7
  79. data/spec/javascripts/joosy/core/modules/widget_manager_spec.js.coffee +31 -17
  80. data/spec/javascripts/joosy/core/page_spec.js.coffee +178 -0
  81. data/spec/javascripts/joosy/core/resource/collection_spec.js.coffee +84 -0
  82. data/spec/javascripts/joosy/core/resource/generic_spec.js.coffee +149 -0
  83. data/spec/javascripts/joosy/core/resource/rest_collection_spec.js.coffee +31 -0
  84. data/spec/javascripts/joosy/core/resource/rest_spec.js.coffee +171 -0
  85. data/spec/javascripts/joosy/core/router_spec.js.coffee +143 -0
  86. data/spec/javascripts/joosy/core/templaters/rails_jst_spec.js.coffee +25 -0
  87. data/spec/javascripts/joosy/core/widget_spec.js.coffee +40 -0
  88. data/spec/javascripts/joosy/preloaders/caching_spec.js.coffee +36 -0
  89. data/spec/javascripts/joosy/preloaders/inline_spec.js.coffee +16 -0
  90. data/spec/javascripts/support/assets/coolface.jpg +0 -0
  91. data/spec/javascripts/support/assets/okay.jpg +0 -0
  92. data/spec/javascripts/support/assets/test.js +1 -0
  93. data/spec/javascripts/support/sinon-ie-1.3.1.js +82 -0
  94. data/vendor/assets/javascripts/jquery.form.js +978 -963
  95. data/vendor/assets/javascripts/metamorph.js +409 -0
  96. data/vendor/assets/javascripts/sugar.js +1057 -366
  97. metadata +95 -50
  98. data/README.rdoc +0 -3
  99. data/lib/joosy/forms.rb +0 -47
  100. data/lib/joosy-rails.rb +0 -5
  101. data/lib/rails/generators/joosy/templates/preload.html.slim +0 -21
  102. data/tmp/javascripts/.gitignore +0 -1
  103. data/tmp/spec/javascripts/helpers/.gitignore +0 -1
  104. data/vendor/assets/javascripts/base64.js +0 -135
  105. data/vendor/assets/javascripts/inflection.js +0 -656
  106. data/vendor/assets/javascripts/joosy/core/application.js.coffee +0 -26
  107. data/vendor/assets/javascripts/joosy/core/form.js.coffee +0 -87
  108. data/vendor/assets/javascripts/joosy/core/joosy.js.coffee +0 -62
  109. data/vendor/assets/javascripts/joosy/core/layout.js.coffee +0 -38
  110. data/vendor/assets/javascripts/joosy/core/modules/container.js.coffee +0 -51
  111. data/vendor/assets/javascripts/joosy/core/modules/events.js.coffee +0 -17
  112. data/vendor/assets/javascripts/joosy/core/modules/filters.js.coffee +0 -39
  113. data/vendor/assets/javascripts/joosy/core/modules/log.js.coffee +0 -12
  114. data/vendor/assets/javascripts/joosy/core/modules/module.js.coffee +0 -28
  115. data/vendor/assets/javascripts/joosy/core/modules/time_manager.js.coffee +0 -23
  116. data/vendor/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +0 -45
  117. data/vendor/assets/javascripts/joosy/core/page.js.coffee +0 -136
  118. data/vendor/assets/javascripts/joosy/core/resource/rest.js.coffee +0 -69
  119. data/vendor/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +0 -42
  120. data/vendor/assets/javascripts/joosy/core/router.js.coffee +0 -75
  121. data/vendor/assets/javascripts/joosy/core/widget.js.coffee +0 -35
  122. data/vendor/assets/javascripts/joosy/preloader/development.js.coffee +0 -27
  123. data/vendor/assets/javascripts/joosy/preloader/production.js.coffee +0 -84
@@ -0,0 +1,338 @@
1
+ #= require joosy/core/joosy
2
+ #= require joosy/core/modules/module
3
+ #= require joosy/core/modules/log
4
+ #= require joosy/core/modules/events
5
+ #= require joosy/core/modules/container
6
+
7
+ #
8
+ # AJAXifies form including file uploads and stuff. Built on top of jQuery.Form.
9
+ #
10
+ # Joosy.Form automatically cares of form validation highlights. It can
11
+ # read common server error responses and add .field_with_errors class to proper
12
+ # field.
13
+ #
14
+ # If you don't have resource associated with form (#fill), it will try to find fields
15
+ # by exact keywords from response. Otherwise it will search for resource_name[field].
16
+ #
17
+ # @example Joosy.Form usage
18
+ # form = new Joosy.Form, -> (response)
19
+ # console.log "Saved and got some: #{response}"
20
+ #
21
+ # form.progress = (percent) -> console.log "Uploaded by #{percent}%"
22
+ # form.fill @resource
23
+ #
24
+ # @include Joosy.Modules.Log
25
+ # @include Joosy.Modules.Events
26
+ # @include Joosy.Modules.Container
27
+ #
28
+ class Joosy.Form extends Joosy.Module
29
+ @include Joosy.Modules.Log
30
+ @include Joosy.Modules.Events
31
+ @include Joosy.Modules.Container
32
+
33
+ #
34
+ # Marks the CSS class to use to mark invalidated fields
35
+ #
36
+ invalidationClass: 'field_with_errors'
37
+
38
+ #
39
+ # List of mappings for fields of invalidated data which comes from server
40
+ #
41
+ # If you have something like {foo: 'bar', bar: 'baz'} coming from server
42
+ # substitutions = {foo: 'foo_id'} will change it to {foo_id: 'bar', bar: 'baz'}
43
+ #
44
+ substitutions: {}
45
+
46
+ #
47
+ # List of elements for internal usage
48
+ #
49
+ elements:
50
+ 'fields': 'input,select,textarea'
51
+
52
+ #
53
+ # Makes one AJAX request with form data without binding
54
+ #
55
+ # @see #constructor
56
+ #
57
+ # @param [Element] form Instance of HTML form element
58
+ # @param [Hash] options Options
59
+ #
60
+ @submit: (form, options={}) ->
61
+ form = new @(form, options)
62
+ form.container.submit()
63
+ form.unbind()
64
+ null
65
+
66
+ @attach: ->
67
+ new Joosy.Form arguments...
68
+
69
+ #
70
+ # During initialization replaces your basic form submit with AJAX request
71
+ #
72
+ # @note If method of form differs from POST or GET it will simulate it
73
+ # by adding hidden _method input. In this cases the method itself will be
74
+ # set to POST.
75
+ #
76
+ # @note For browsers having no support of HTML5 Forms it may do an iframe requests
77
+ # to handle file uploading.
78
+ #
79
+ # @param [jQuery] form Form element instance
80
+ # @param [Hash] options Options
81
+ #
82
+ # @option options [Function] before `(XHR) -> Bollean` to trigger right before submit.
83
+ # By default will run form invalidation cleanup. This behavior can be canceled
84
+ # by returning false from your own before callback. Both of callbacks will run if
85
+ # you return true.
86
+ #
87
+ # @option options [Function] success `(Object) -> null` triggers on 200 HTTP code from server.
88
+ # Pases in the parsed JSON.
89
+ #
90
+ # @option options [Function] progress `(Float) -> null` runs periodically while form is uploading
91
+ #
92
+ # @option options [Function] error `(Object) -> Boolean` triggers if server responded with anything but 200.
93
+ # By default will run form invalidation routine. This behavior can be canceled
94
+ # by returning false from your own error callback. Both of callbacks will run if
95
+ # you return true.
96
+ #
97
+ # @option options [Joosy.Resource.Generic] resource The resource to fill the form with
98
+ # @option options [String] resourceName The string to use as a resource name prefix for fields to match invalidation
99
+ # @option options [String] action Action URL for the form
100
+ #
101
+ constructor: (form, options={}) ->
102
+ if Object.isFunction options
103
+ @success = options
104
+ else
105
+ Object.each options, (key, value) =>
106
+ @[key] = value
107
+
108
+ @container = $(form)
109
+ return if @container.length == 0
110
+
111
+ @refreshElements()
112
+ @__delegateEvents()
113
+
114
+ method = @container.get(0).getAttribute('method')?.toLowerCase()
115
+ if method && !['get', 'post'].has method
116
+ @__markMethod method
117
+ @container.attr 'method', 'POST'
118
+
119
+ @container.ajaxForm
120
+ dataType: 'json'
121
+ beforeSend: =>
122
+ @__before arguments...
123
+ success: =>
124
+ @__success arguments...
125
+ error: =>
126
+ @__error arguments...
127
+ xhr: =>
128
+ xhr = $.ajaxSettings.xhr()
129
+ if xhr.upload? && @progress
130
+ xhr.upload.onprogress = (event) =>
131
+ if event.lengthComputable
132
+ @progress (event.position / event.total * 100).round 2
133
+ xhr
134
+
135
+ if options.resource?
136
+ @fill(options.resource, options)
137
+ delete @resource
138
+
139
+ #
140
+ # Resets form submit behavior to default
141
+ #
142
+ unbind: ->
143
+ @container.unbind('submit').find('input:submit,input:image,button:submit').unbind('click');
144
+
145
+ #
146
+ # Links current form with given resource and sets values of form inputs from with it.
147
+ # Form will use give resource while doing invalidation routine.
148
+ #
149
+ # @param [Resource] resource Resource to fill fields with
150
+ # @param [Hash] options Options
151
+ #
152
+ # @option options [Function] decorator `(Object) -> Object` decoration callback
153
+ # Pases in the parsed JSON.
154
+ #
155
+ # @option options [String] action Action URL for the form
156
+ #
157
+ fill: (resource, options) ->
158
+ resource = resource.build() if Object.isFunction(resource.build)
159
+ @__resource = resource
160
+
161
+ if options?.decorator?
162
+ data = options.decorator resource.data
163
+ else
164
+ data = resource.data
165
+
166
+ Object.each data, (key, val) =>
167
+ key = resource.__entityName + "[#{key}]"
168
+ input = @fields.filter("[name='#{key.underscore()}']:not(:file),[name='#{key.camelize(false)}']:not(:file)")
169
+ unless input.is ':checkbox'
170
+ input.val val
171
+ else
172
+ if val
173
+ input.attr 'checked', 'checked'
174
+ else
175
+ input.removeAttr 'checked'
176
+
177
+ @__markMethod(options?.method || 'PUT') if resource.id()
178
+
179
+ url = options?.action || (if resource.id()? then resource.memberPath() else resource.collectionPath())
180
+
181
+ @container.attr 'action', url
182
+ @container.attr 'method', 'POST'
183
+
184
+ #
185
+ # Submit the HTML Form
186
+ #
187
+ submit: ->
188
+ @container.submit()
189
+
190
+ #
191
+ # Serializes form into query string.
192
+ #
193
+ # @param [Boolean] skipMethod Determines if we should skip magical _method field
194
+ #
195
+ # @return [String]
196
+ #
197
+ serialize: (skipMethod=true) ->
198
+ data = @container.serialize()
199
+ data = data.replace /\&?\_method\=put/i, '' if skipMethod
200
+
201
+ data
202
+
203
+ #
204
+ # Inner success callback.
205
+ #
206
+ __success: (response, status, xhr) ->
207
+ if xhr
208
+ @success? response
209
+ else if response.status == 200
210
+ @success response.json
211
+ else
212
+ @__error response.json
213
+
214
+ #
215
+ # Inner before callback.
216
+ # By default will clean invalidation.
217
+ #
218
+ __before: (xhr, settings) ->
219
+ if !@before? || @before(arguments...) is true
220
+ @fields.removeClass @invalidationClass
221
+
222
+ #
223
+ # Inner error callback.
224
+ # By default will trigger basic invalidation.
225
+ #
226
+ __error: (data) ->
227
+ errors = if data.responseText
228
+ try
229
+ data = jQuery.parseJSON(data.responseText)
230
+ catch error
231
+ {}
232
+ else
233
+ data
234
+
235
+ if !@error? || @error(errors) is true
236
+ errors = @__stringifyErrors(errors)
237
+
238
+ Object.each errors, (field, notifications) =>
239
+ input = @findField(field).addClass @invalidationClass
240
+ @notification? input, notifications
241
+
242
+ return errors
243
+
244
+ return false
245
+
246
+ #
247
+ # Finds field by field name.
248
+ # This is not inlined since we want to override
249
+ # or monkeypatch it from time to time
250
+ #
251
+ # @param [String] field Name of field to find
252
+ #
253
+ findField: (field) ->
254
+ @fields.filter("[name='#{field}']")
255
+
256
+ #
257
+ # Simulates REST methods by adding hidden _method input with real method
258
+ # while setting POST as the transport method
259
+ #
260
+ # @param [String] method Real method to simulate
261
+ #
262
+ __markMethod: (method='PUT') ->
263
+ method = $('<input/>',
264
+ type: 'hidden'
265
+ name: '_method'
266
+ value: method
267
+ )
268
+ @container.append method
269
+
270
+ #
271
+ # Prepares server response for default error handler
272
+ # Turns all possible response notations into form notation (foo[bar])
273
+ # Every direct field of incoming data will be decorated by @substitutions
274
+ #
275
+ # @example Flat validation result
276
+ # { field1: ['error'] } # input
277
+ # { field1: ['error'] } # if form was not associated with Resource by {#fill}
278
+ # { "fluffy[field1]": ['error']} # if form was associated with Resource (named fluffy)
279
+ #
280
+ # @example Complex validation result
281
+ # { foo: { bar: { baz: ['error'] } } } # input
282
+ # { "foo[bar][bar]": ['error'] } # output
283
+ #
284
+ # @param [Object] errors Data to prepare
285
+ #
286
+ # @return [Hash<String, Array>] Flat hash with field names in keys and arrays
287
+ # of errors in values
288
+ #
289
+ __stringifyErrors: (errors) ->
290
+ result = {}
291
+
292
+ Object.each errors, (field, notifications) =>
293
+ if @substitutions[field]?
294
+ field = @substitutions[field]
295
+
296
+ if Object.isObject notifications
297
+ Object.each @__foldInlineEntities(notifications), (key, value) ->
298
+ result[field+key] = value
299
+ else
300
+ if field.indexOf(".") != -1
301
+ splited = field.split '.'
302
+ field = splited.shift()
303
+ if @__resource
304
+ name = @resourceName || @__resource.__entityName
305
+ field = name + "[#{field}]"
306
+ field += "[#{f}]" for f in splited
307
+
308
+ else if @__resource
309
+ name = @resourceName || @__resource.__entityName
310
+ field = name + "[#{field}]"
311
+
312
+ result[field] = notifications
313
+
314
+ result
315
+
316
+ #
317
+ # Flattens complex inline structures into form notation
318
+ #
319
+ # @example Basic flattening
320
+ # data = foo: { bar: { baz: [] } }
321
+ # inner = @__foldInlineEntities(data.foo, 'foo')
322
+ #
323
+ # inner # { "foo[bar][baz]": [] }
324
+ #
325
+ # @param [Object] hash Structure to fold
326
+ # @param [String] scope Prefix for resulting scopes
327
+ # @param [Object] result Context of result for recursion
328
+ #
329
+ # @return [Hash]
330
+ #
331
+ __foldInlineEntities: (hash, scope="", result={}) ->
332
+ Object.each hash, (key, value) =>
333
+ if Object.isObject(value)
334
+ @__foldInlineEntities(value, "#{scope}[#{key}]", result)
335
+ else
336
+ result["#{scope}[#{key}]"] = value
337
+
338
+ result
@@ -0,0 +1,72 @@
1
+ #= require joosy/core/joosy
2
+
3
+ #
4
+ # Form helper
5
+ #
6
+ Joosy.helpers 'Application', ->
7
+
8
+ description = (resource, method, extendIds) ->
9
+ if resource instanceof Joosy.Resource.Generic
10
+ id = resource.id()
11
+ resource = resource.__entityName
12
+
13
+ name: resource + "#{if method.match(/^\[.*\]$/) then method else "[#{method}]"}"
14
+ id: resource + (if id && extendIds then '_'+id else '') + "_#{method.parameterize().underscore()}"
15
+
16
+ input = (type, resource, method, options={}) =>
17
+ d = description(resource, method, options.extendIds)
18
+ delete options.extendIds
19
+ @tag 'input', Joosy.Module.merge {type: type, name: d.name, id: d.id}, options
20
+
21
+ class Form
22
+ constructor: (@context, @resource, @options) ->
23
+ label: (method, options={}, content='') -> @context.label(@resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options), content)
24
+ radioButton: (method, tagValue, options={}) -> @context.radioButton(@resource, method, tagValue, Joosy.Module.merge(extendIds: @options.extendIds, options))
25
+ textArea: (method, options={}) -> @context.textArea(@resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options))
26
+ checkBox: (method, options={}, checkedValue=1, uncheckedValue=0) ->
27
+ @context.checkBox(@resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options), checkedValue, uncheckedValue)
28
+
29
+ ['text', 'file', 'hidden', 'password'].each (type) =>
30
+ Form.prototype[type+'Field'] = (method, options={}) ->
31
+ @context[type+'Field'] @resource, method, Joosy.Module.merge(extendIds: @options.extendIds, options)
32
+
33
+ @formFor = (resource, options={}, block) ->
34
+ if Object.isFunction(options)
35
+ block = options
36
+ options = {}
37
+
38
+ uuid = Joosy.uuid()
39
+ form = @tag 'form', Joosy.Module.merge(options.html || {}, id: uuid), block?.call(this, new Form(this, resource, options))
40
+
41
+ @onRefresh? -> Joosy.Form.attach '#'+uuid, Joosy.Module.merge(options, resource: resource)
42
+
43
+ form
44
+
45
+ @label = (resource, method, options={}, content='') ->
46
+ if !Object.isObject(options)
47
+ content = options
48
+ options = {}
49
+
50
+ d = description(resource, method, options.extendIds)
51
+ delete options.extendIds
52
+
53
+ @tag 'label', Joosy.Module.merge(options, for: d.id), content
54
+
55
+ ['text', 'file', 'hidden', 'password'].each (type) =>
56
+ @[type+'Field'] = (resource, method, options={}) -> input type, resource, method, options
57
+
58
+ @radioButton = (resource, method, tagValue, options={}) -> input 'radio', resource, method, Joosy.Module.merge(value: tagValue, options)
59
+
60
+ @checkBox = (resource, method, options={}, checkedValue=1, uncheckedValue=0) ->
61
+ spy = input 'hidden', resource, method, value: uncheckedValue, extendIds: options.extendIds
62
+ box = input 'checkbox', resource, method, Joosy.Module.merge(value: checkedValue, options)
63
+
64
+ spy+box
65
+
66
+ @textArea = (resource, method, options={}) ->
67
+ value = options.value
68
+ extendIds = options.extendIds
69
+ delete options.value
70
+ delete options.extendIds
71
+
72
+ @tag 'textarea', Joosy.Module.merge(description(resource, method, extendIds), options), value
@@ -0,0 +1,42 @@
1
+ #= require joosy/core/joosy
2
+
3
+ #
4
+ # Rendering and string representation helpers
5
+ #
6
+ Joosy.helpers 'Application', ->
7
+
8
+ @tag = (name, options={}, content='') ->
9
+ content = content() if Object.isFunction(content)
10
+
11
+ element = document.createElement name
12
+ temp = document.createElement 'div'
13
+
14
+ Object.each options, (name, value) -> element.setAttribute name, value
15
+ element.innerHTML = content
16
+
17
+ temp.appendChild element
18
+ temp.innerHTML
19
+
20
+ #
21
+ # Converts \n into <br/> in your text
22
+ #
23
+ # @param [String] text Text to convert
24
+ #
25
+ @nl2br = (text) ->
26
+ text.toString().replace /\n/g, '<br/>'
27
+
28
+ #
29
+ # Wraps the inline block into given template
30
+ # Request template will receive the inline block as @yield parameter
31
+ #
32
+ # Example
33
+ # -# foo/baz template
34
+ # != @renderWrapped 'foo/bar', ->
35
+ # %b This string will be passed to 'foo/bar'
36
+ #
37
+ # -# foo/bar template
38
+ # %h1 I'm the wrapper here!
39
+ # != @yield
40
+ #
41
+ @renderWrapped = (template, lambda) ->
42
+ @render template, Joosy.Module.merge(this, yield: lambda())
@@ -0,0 +1,14 @@
1
+ #= require joosy/core/joosy
2
+
3
+ #
4
+ # Widgets manipulation
5
+ #
6
+ Joosy.helpers 'Application', ->
7
+
8
+ @widget = (element, widget) ->
9
+ uuid = Joosy.uuid()
10
+ element = @tag element, id: uuid
11
+
12
+ @onRefresh -> @registerWidget '#'+uuid, widget
13
+
14
+ element
@@ -0,0 +1,184 @@
1
+ #
2
+ # All the tiny core stuff
3
+ #
4
+ # @mixin
5
+ #
6
+ @Joosy =
7
+ #
8
+ # Core modules container
9
+ #
10
+ Modules: {}
11
+
12
+ #
13
+ # Resources container
14
+ #
15
+ Resource: {}
16
+
17
+ #
18
+ # Templaters container
19
+ #
20
+ Templaters: {}
21
+
22
+ #
23
+ # If enabled Joosy will log to console a lot of 'in progress' stuff
24
+ #
25
+ debug: false
26
+
27
+ #
28
+ # Registeres anything inside specified namespace (objects chain starting from `window`)
29
+ #
30
+ # @example Basic usage
31
+ # Joosy.namespace 'foobar', ->
32
+ # class @RealThing
33
+ #
34
+ # foo = new foobar.RealThing
35
+ #
36
+ # @param [String] name Namespace keyword
37
+ # @param [Boolean] generator Namespace content
38
+ #
39
+ namespace: (name, generator=false) ->
40
+ name = name.split '.'
41
+ space = window
42
+ for part in name
43
+ space = space[part] ?= {}
44
+
45
+ if generator
46
+ generator = generator.apply space
47
+ for key, klass of space
48
+ if space.hasOwnProperty(key) &&
49
+ Joosy.Module.hasAncestor klass, Joosy.Module
50
+ klass.__namespace__ = name
51
+
52
+ #
53
+ # Registeres given methods as a helpers inside a given set
54
+ #
55
+ # @param [String] name Helpers set keyword
56
+ # @param [Boolean] generator Helpers content
57
+ #
58
+ helpers: (name, generator) ->
59
+ Joosy.namespace "Joosy.Helpers.#{name}", generator
60
+
61
+ #
62
+ # Scary `hello world`
63
+ #
64
+ test: ->
65
+ text = "Hi :). I'm Joosy. And everything is just fine!"
66
+
67
+ if console
68
+ console.log text
69
+ else
70
+ alert text
71
+
72
+ #
73
+ # Runs set of callbacks finializing with result callback
74
+ #
75
+ # @example Basic usage
76
+ # Joosy.synchronize (context) ->
77
+ # contet.do (done) -> done()
78
+ # contet.do (done) -> done()
79
+ # content.after ->
80
+ # console.log 'Success!'
81
+ #
82
+ # @param [Function] block Configuration block (see example)
83
+ #
84
+ synchronize: ->
85
+ Joosy.Modules.Events.synchronize arguments...
86
+
87
+ #
88
+ # Generates UUID
89
+ #
90
+ uuid: ->
91
+ 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) ->
92
+ r = Math.random() * 16 | 0
93
+ v = if c is 'x' then r else r & 3 | 8
94
+ v.toString 16
95
+ .toUpperCase()
96
+
97
+ #
98
+ # Preloads sets of images then runs callback
99
+ #
100
+ # @param [Array<String>] images Images paths
101
+ # @param [Function] callback Action to run when every picture was loaded (or triggered an error)
102
+ #
103
+ preloadImages: (images, callback) ->
104
+ unless Object.isArray(images)
105
+ images = [images]
106
+ if images.length == 0
107
+ callback()
108
+
109
+ ticks = images.length
110
+ result = []
111
+ checker = ->
112
+ if (ticks -= 1) == 0
113
+ callback?()
114
+
115
+ for p in images
116
+ result.push $('<img/>').load(checker).error(checker).attr('src', p)
117
+
118
+ result
119
+
120
+ #
121
+ # Basic URI builder. Joins base path with params hash
122
+ #
123
+ # @param [String] url Base url
124
+ # @param [Hash] params Parameters to join
125
+ #
126
+ # @example Basic usage
127
+ # Joosy.buildUrl 'http://joosy.ws/#!/test', {foo: 'bar'} # http://joosy.ws/?foo=bar#!/test
128
+ #
129
+ buildUrl: (url, params) ->
130
+ paramsString = []
131
+
132
+ Object.each params, (key, value) ->
133
+ paramsString.push "#{key}=#{value}"
134
+
135
+ hash = url.match(/(\#.*)?$/)[0]
136
+ url = url.replace /\#.*$/, ''
137
+ if !paramsString.isEmpty() && !url.has(/\?/)
138
+ url = url + "?"
139
+
140
+ paramsString = paramsString.join '&'
141
+ if !paramsString.isBlank() && url.last() != '?'
142
+ paramsString = '&' + paramsString
143
+
144
+ url + paramsString + hash
145
+
146
+ #
147
+ # Creates classes and collection classes for the given resources that might have been extracted from the routes
148
+ #
149
+ # @param [Hash] resources Resources declaration
150
+ #
151
+ # @example Basic usage
152
+ # Joosy.defineResources {'': {foo: '/foos'}, 'namespace': {bar: '/namespace/bars'}}
153
+ #
154
+ defineResources: (resources) ->
155
+ Object.extended(resources).each (namespace, resources) ->
156
+ if namespace.isBlank()
157
+ Object.extended(resources).each (resource, path) ->
158
+ Joosy.defineResource resource, path
159
+ else
160
+ Joosy.namespace namespace, ->
161
+ Object.extended(resources).each (resource, path) =>
162
+ Joosy.defineResource resource, path, @
163
+
164
+ #
165
+ #
166
+ # @param [String] resource Entity name in singular form
167
+ # @param [String] path Entity REST end-point
168
+ # @param [Object] space Namespace for new classes
169
+ #
170
+ # @example Basic usage
171
+ # Joosy.defineResource 'foo', '/foos'
172
+ #
173
+ defineResource: (resource, path, space=window) ->
174
+ className = resource.camelize()
175
+ collectionName = "#{resource.pluralize().camelize()}Collection"
176
+ unless space[className]
177
+ Joosy.Modules.Log.debugAs space, "Define #{className}"
178
+ space[className] = class extends Joosy.Resource.REST
179
+ @entity resource
180
+ @source path
181
+ unless space[collectionName]
182
+ Joosy.Modules.Log.debugAs space, "Define #{collectionName}"
183
+ space[collectionName] = class extends Joosy.Resource.RESTCollection
184
+ @model space[className]