joosy 0.1.0.RC1 → 0.1.0.RC2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/Gemfile +1 -0
  2. data/Gemfile.lock +8 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +89 -0
  5. data/app/assets/javascripts/joosy/core/application.js.coffee +25 -5
  6. data/app/assets/javascripts/joosy/core/form.js.coffee +212 -22
  7. data/app/assets/javascripts/joosy/core/helpers.js.coffee +11 -1
  8. data/app/assets/javascripts/joosy/core/joosy.js.coffee +22 -17
  9. data/app/assets/javascripts/joosy/core/layout.js.coffee +17 -7
  10. data/app/assets/javascripts/joosy/core/modules/container.js.coffee +19 -15
  11. data/app/assets/javascripts/joosy/core/modules/events.js.coffee +10 -9
  12. data/app/assets/javascripts/joosy/core/modules/filters.js.coffee +16 -12
  13. data/app/assets/javascripts/joosy/core/modules/log.js.coffee +8 -5
  14. data/app/assets/javascripts/joosy/core/modules/module.js.coffee +31 -21
  15. data/app/assets/javascripts/joosy/core/modules/renderer.js.coffee +114 -51
  16. data/app/assets/javascripts/joosy/core/modules/time_manager.js.coffee +2 -2
  17. data/app/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +10 -10
  18. data/app/assets/javascripts/joosy/core/page.js.coffee +31 -21
  19. data/app/assets/javascripts/joosy/core/preloader.js.coffee +3 -3
  20. data/app/assets/javascripts/joosy/core/resource/collection.js.coffee +137 -0
  21. data/app/assets/javascripts/joosy/core/resource/generic.js.coffee +178 -13
  22. data/app/assets/javascripts/joosy/core/resource/rest.js.coffee +167 -44
  23. data/app/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +100 -32
  24. data/app/assets/javascripts/joosy/core/router.js.coffee +23 -25
  25. data/app/assets/javascripts/joosy/core/templaters/rails_jst.js.coffee +19 -3
  26. data/app/assets/javascripts/joosy/core/widget.js.coffee +7 -9
  27. data/app/assets/javascripts/joosy/preloaders/caching.js.coffee +117 -57
  28. data/app/assets/javascripts/joosy/preloaders/inline.js.coffee +23 -24
  29. data/app/helpers/joosy/sprockets_helper.rb +1 -1
  30. data/lib/joosy/forms.rb +2 -12
  31. data/lib/joosy/rails/version.rb +1 -1
  32. data/lib/rails/generators/joosy/templates/app/pages/template.js.coffee +1 -1
  33. data/lib/rails/generators/joosy/templates/app/resources/template.js.coffee +1 -1
  34. data/spec/javascripts/joosy/core/form_spec.js.coffee +55 -12
  35. data/spec/javascripts/joosy/core/layout_spec.js.coffee +1 -1
  36. data/spec/javascripts/joosy/core/modules/container_spec.js.coffee +0 -1
  37. data/spec/javascripts/joosy/core/modules/module_spec.js.coffee +1 -1
  38. data/spec/javascripts/joosy/core/modules/renderer_spec.js.coffee +39 -3
  39. data/spec/javascripts/joosy/core/modules/time_manager_spec.js.coffee +1 -1
  40. data/spec/javascripts/joosy/core/page_spec.js.coffee +4 -1
  41. data/spec/javascripts/joosy/core/resource/collection_spec.js.coffee +84 -0
  42. data/spec/javascripts/joosy/core/resource/generic_spec.js.coffee +86 -3
  43. data/spec/javascripts/joosy/core/resource/rest_collection_spec.js.coffee +15 -22
  44. data/spec/javascripts/joosy/core/resource/rest_spec.js.coffee +27 -4
  45. data/spec/javascripts/joosy/core/widget_spec.js.coffee +3 -14
  46. metadata +21 -19
  47. data/README.rdoc +0 -3
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ gemspec
5
5
 
6
6
  gem 'jasmine', :git => 'git://github.com/pivotal/jasmine-gem.git'
7
7
  gem 'guard-sprockets', :git => 'git://github.com/roundlake/guard-sprockets.git'
8
+ gem 'thin'
8
9
 
9
10
  group :test do
10
11
  if RUBY_PLATFORM =~ /linux/
data/Gemfile.lock CHANGED
@@ -19,7 +19,7 @@ GIT
19
19
  PATH
20
20
  remote: .
21
21
  specs:
22
- joosy (0.1.0.alpha)
22
+ joosy (0.1.0.RC1)
23
23
  coffee-rails
24
24
  haml_coffee_assets
25
25
  jquery-rails
@@ -66,8 +66,10 @@ GEM
66
66
  coffee-script-source
67
67
  execjs
68
68
  coffee-script-source (1.2.0)
69
+ daemons (1.1.8)
69
70
  diff-lcs (1.1.3)
70
71
  erubis (2.7.0)
72
+ eventmachine (0.12.10)
71
73
  execjs (1.3.0)
72
74
  multi_json (~> 1.0)
73
75
  ffi (1.0.11)
@@ -140,6 +142,10 @@ GEM
140
142
  hike (~> 1.2)
141
143
  rack (~> 1.0)
142
144
  tilt (~> 1.1, != 1.3.0)
145
+ thin (1.3.1)
146
+ daemons (>= 1.0.9)
147
+ eventmachine (>= 0.12.6)
148
+ rack (>= 1.0.0)
143
149
  thor (0.14.6)
144
150
  tilt (1.3.3)
145
151
  treetop (1.4.10)
@@ -157,3 +163,4 @@ DEPENDENCIES
157
163
  guard-sprockets!
158
164
  jasmine!
159
165
  joosy!
166
+ thin
data/MIT-LICENSE CHANGED
@@ -1,5 +1,5 @@
1
- Copyright (C) 2011 by Boris Staal <boris@roundlake.ru>,
2
- Peter Zotov <p.zotov@roundlake.ru>.
1
+ Copyright (C) 2011, 2012 by Boris Staal <boris@roundlake.ru>,
2
+ Peter Zotov <p.zotov@roundlake.ru>.
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining
5
5
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ ![Joosy](http://f.cl.ly/items/2N2J453J2B353F1A0t0I/joocy1.1.png)
2
+
3
+ ## What is Joosy
4
+
5
+ Joosy is a full-featured javascript MVC framework, which mostly follows Rails philosophy, particularly in preferring conventions over configuration. It helps you to avoid boilerplate and makes it easy to maintain and extend the app.
6
+
7
+ Joosy allows you to create web apps which work completely in the browser. So that, it helps you to relocate all your Rails Views to the client side. It also helps you with managing the growing quantity of JS code. On another hand, it makes your backend to have exactly one function -- to be a simple REST provider. That leads to easier development support and improves the scalability greatly.
8
+
9
+ Joosy doesn't compete with jQuery. It is built on the top of [jQuery](http://jquery.com/), and utilizes [CoffeeScript](http://coffeescript.org/). It either includes [Sugar.js](http://sugarjs.com/) in role of ActiveSupport to make your life even better.
10
+
11
+ Finally, Joosy boosts your development drastically.
12
+
13
+ ### How is Joosy different from X
14
+
15
+ We share quite different goals and totally differ in ways to achieve it. While existing MVC frameworks define basic entities for you and leave you alone with application basic patterns and solutions, Joosy tries to give convention for each of them.
16
+
17
+ #### [Backbone](http://documentcloud.github.com/backbone/)
18
+
19
+ In Backbone or Spine you only have abstract Views, Models and "Packs of …" and you should even decide on your own what's Controller for you. Joosy gives you the ready plan on how to move on to the success. If you ever used Rails you know that feeling: you concentrate on _what_ you create and not on _how_ to structure it.
20
+
21
+ #### [Ember](http://emberjs.com/)
22
+
23
+ Ember is the closest framework by spirit. But it still doesn't give you enough conventions and is even more abstract than Backbone in several parts. It either limits you to Handlebars. Joosy rendering system is built on top of same solution, Morpher. That said you can bind your variables dynamically just like in Ember. But it allows you to any templates syntax.
24
+
25
+ ### The Joosy MVC Interpretation
26
+
27
+ You probably are familiar with the concept of MVC. It's a pattern to keep code DRY, isolate business logic from the UI. However the concept of Model-View-Controller just doesn't work for browser applications. There are numerous reasons for that that can't be fit into this introduction. Instead we'll tell you what we propose.
28
+
29
+ #### Views
30
+
31
+ In Joosy views are just views. Set of files (in templates/ folder). They do not differ from Rails and give you quite the same abilities (including helpers!). Besides that Joosy has renderDynamic which allows you to bind your variables to HTML representation tightly. You change variable value, Joosy changes HTML.
32
+
33
+ By default Joosy comes with Coffee-Haml (which looks and works pretty much like Ruby Haml). Combined with Sugar.js it will feel very similar to Ruby and server-side and will provide you the easiest migration.
34
+
35
+ #### Controllers
36
+
37
+ Unlike server-side, client-side has a state. At server-side you only have incoming data and you convert it to output data. At client-side you work with events. To handle correct bootstrap and help you organize your code well, we have 3 entities which play the role of controller. Their organizing functions are similar but they behavior dramatically different at bootstrap.
38
+
39
+ They are:
40
+
41
+ ##### Pages
42
+ Pages are the heart of the entire application. They are used to fetch data, bind events, setup widgets, trig visual effects, and pass the data to the templates.
43
+
44
+ ##### Layouts
45
+ Layouts are mainly used to set a part of your page that won't be reloaded often. Pages are wrapped into Layouts. Just like pages Layouts can bind events and setup widgets. Consider layouts as a bit extended version of what you have in Rails.
46
+
47
+ ##### Widgets
48
+ Widgets are needed to keep your code DRY. They help you organize it in reusable modules.
49
+
50
+ #### Models
51
+
52
+ There are some facts that should be noted to explain it how we understand browser-side models.
53
+
54
+ * In most cases you will want to leave your logic on server-side. In a real models.
55
+ * You rarely want to make 5 HTTP requests instead of 1.
56
+ * In most cases you will have to give user a Form as a way to change anything.
57
+ * You'll get different possible set of fields depending on case you use your model at.
58
+
59
+ With all that in mind we came up to the fact: you can not reproduce real models in browser nor you should try to. What you need is a comfortable channel between browser and server-side to get structured data, work with that and send it back. That's mainly a transport and interface task while models are MUCH more than that. To solve this task Joosy offers you two things:
60
+
61
+ ###### Resources
62
+ Resource is wrapper on top of JSON dump which will help you to get data from server, parse it and structure it. It will trigger 'changed' if you change resource, will map inline hashes in other resources if possible and do all other magic stuff you expect from your model when you read data. Resources either define Collections. In most cases you can say Resources are fully-functional read part of what you get used to as model.
63
+
64
+ ###### Forms
65
+ After you got your data as resources and used it to display something, you'll need to change it. While resource defines modification methods like _save_ and _destroy_ most of complex modifications should go through Joosy.Form. It turns your form into AJAX and binds the resource to it. It handles file uploads, it gives you upload progress callback and makes it a dream to handle. Joosy.Form will either understand standard Rails invalidation response and will mark invalidated fields with ".field_with_errors" out of box.
66
+
67
+
68
+ ## What is Joosy good for
69
+
70
+ Joosy is intended to ease building of modern medium and large-sized browser-based applications, minimize code base while providing more features and what's most important, giving you ready conventions for typical tasks.
71
+
72
+ Compare Joosy to Backbone like Rails to Sinatra. While Rails engine is much more powerful, Sinatra still has a lot of cases to be used at. If all you need is to enable some RICHness on one of your pages, Joosy can handle that. But Backbone will do the trick with lesser dependencies. If you need to move complete web-resource to browser Joosy will do the task at its Best.
73
+
74
+ ## What's next?
75
+
76
+ We have a set of guidelines showing you, step-by-step, how to create a Joosy application. You should probably follow them starting from [Creating a new project](http://roundlake.github.com/joosy/guides/creating-a-new-project.html)
77
+
78
+ ### Hello world app
79
+
80
+ Using built-in generators you can quickly generate small app inside your Rails app to see Joosy application from inside a bit.
81
+
82
+ rails g joosy:application dummy
83
+ rails g joosy:preloader dummy
84
+
85
+ Now you can `rails s` and see Joosy placeholder at [localhost:3000/dummy](http://localhost:3000/dummy)
86
+
87
+ # License
88
+
89
+ Joosy is licensed under MIT: [www.opensource.org/licenses/MIT](www.opensource.org/licenses/MIT)
@@ -1,28 +1,48 @@
1
1
  #= require joosy/core/joosy
2
2
 
3
+ #
4
+ # Joosy Application container
5
+ #
3
6
  Joosy.Application =
4
7
  Pages: {}
5
8
  Layouts: {}
6
9
  Controls: {}
7
10
 
8
- selector: false
9
-
11
+ #
12
+ # Starts Joosy application by binding to element and bootstraping routes
13
+ #
14
+ # @param [String] name Name of app (the dir its located in)
15
+ # @param [String] selector jQuery-compatible selector of root application element
16
+ # @param [Object] options
17
+ #
10
18
  initialize: (@name, @selector, options={}) ->
11
19
  @[key] = value for key, value of options
12
- @templater = new Joosy.Templaters.RailsJST(@name)
20
+ @templater = new Joosy.Templaters.RailsJST @name
13
21
 
14
22
  Joosy.Router.setupRoutes()
15
23
 
16
24
  @sandboxSelector = Joosy.uuid()
17
- @content().after("<div id='#{@sandboxSelector}' style='display:none'></div>")
25
+ @content().after "<div id='#{@sandboxSelector}' style='display:none'></div>"
18
26
  @sandboxSelector = '#' + @sandboxSelector
19
27
 
28
+ #
29
+ # Gets current application root node
30
+ #
20
31
  content: ->
21
32
  $(@selector)
22
33
 
34
+ #
35
+ # Gets current application sandbox node
36
+ #
23
37
  sandbox: ->
24
38
  $(@sandboxSelector)
25
39
 
40
+ #
41
+ # Switches to given page
42
+ #
43
+ # @param [Joosy.Page] page The class (not object) of page to load
44
+ # @param [Object] params Hash of page params
45
+ #
26
46
  setCurrentPage: (page, params) ->
27
47
  #if @page not instanceof page
28
- @page = new page(params, @page)
48
+ @page = new page params, @page
@@ -4,84 +4,274 @@
4
4
  #= require joosy/core/modules/events
5
5
  #= require joosy/core/modules/container
6
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 hihglights. 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 (#fill) with form it will try to find fields
15
+ # by exact keywords from response. Otherwise it will search for resource_name[field].
16
+ #
17
+ #
18
+ # Example
19
+ # form = new Joosy.Form, -> (response)
20
+ # console.log "Saved and got some: #{response}"
21
+ #
22
+ # form.progress = (percent) -> console.log "Uploaded by #{percent}%"
23
+ # form.fill @resource
24
+ #
7
25
  class Joosy.Form extends Joosy.Module
8
26
  @include Joosy.Modules.Log
9
27
  @include Joosy.Modules.Events
10
28
  @include Joosy.Modules.Container
11
29
 
30
+ #
31
+ # Marks the CSS class to use to mark invalidated fields
32
+ #
12
33
  invalidationClass: 'field_with_errors'
34
+
35
+ #
36
+ # List of mappings for fields of invalidated data which comes from server
37
+ #
38
+ # If you have something like {foo: 'bar', bar: 'baz'} coming from server
39
+ # substitutions = {foo: 'foo_id'} will change it to {foo_id: 'bar', bar: 'baz'}
40
+ #
13
41
  substitutions: {}
14
42
 
43
+ #
44
+ # List of elements for internal usage
45
+ #
15
46
  elements:
16
47
  'fields': 'input,select,textarea'
17
48
 
49
+ #
50
+ # Submits your form once and unbinds leaving it simple form without AJAX
51
+ #
52
+ # @param [Element] form Instance of HTML form element
53
+ # @param [Object] opts Map of additional options (see constructor)
54
+ #
55
+ @submit: (form, opts={}) ->
56
+ form = new @(form, opts)
57
+ form.container.submit()
58
+ form.unbind()
59
+ null
60
+
61
+ #
62
+ # During initialization replaces your basic form submit with AJAX request
63
+ #
64
+ # If method of form differs from POST or GET it will simulate it
65
+ # by adding hidden _method input. In this cases the method itself will be
66
+ # set to POST.
67
+ #
68
+ # For browsers having no support of HTML5 Forms it may do an iframe requests
69
+ # to handle file uploading.
70
+ #
71
+ # Supported options are:
72
+ #
73
+ # * before: `(XHR) -> Boolean` triggers right before submit.
74
+ # By default will run form invalidation cleanup. This behavior can be canceled
75
+ # by returning false from your own before callback. Both of callbacks will run if
76
+ # you return true.
77
+ #
78
+ # * success: `(Object) -> null` triggers on 200 HTTP code from server. Pases
79
+ # in the parsed JSON.
80
+ #
81
+ # * progress: `(Float) -> null` runs peridically while form is uploading.
82
+ #
83
+ # * error: `(Object) -> Boolean` triggers if server responsed with anything but 200.
84
+ # By default will run form invalidation routine. This behavior can be canceled
85
+ # by returning false from your own error callback. Both of callbacks will run if
86
+ # you return true.
87
+ #
88
+ #
18
89
  constructor: (form, opts={}) ->
19
90
  if Object.isFunction opts
20
91
  @success = opts
21
92
  else
22
- Object.extended(opts).each (key, value) =>
93
+ Object.each opts, (key, value) =>
23
94
  @[key] = value
24
95
 
25
96
  @container = $(form)
26
97
  @refreshElements()
27
98
  @__delegateEvents()
28
99
 
29
- if (method = @container.get(0).getAttribute('method')?.toLowerCase()) && !['get', 'post'].has(method)
100
+ method = @container.get(0).getAttribute('method')?.toLowerCase()
101
+ if method && !['get', 'post'].has method
30
102
  @__markMethod method
31
103
  @container.attr 'method', 'POST'
32
104
 
33
105
  @container.ajaxForm
34
106
  dataType: 'json'
35
- beforeSend: => @__before(arguments...)
36
- success: => @__success(arguments...)
37
- error: => @__error(arguments...)
107
+ beforeSend: =>
108
+ @__before arguments...
109
+ success: =>
110
+ @__success arguments...
111
+ error: =>
112
+ @__error arguments...
38
113
  xhr: =>
39
114
  xhr = $.ajaxSettings.xhr()
40
115
  if xhr.upload? && @progress
41
116
  xhr.upload.onprogress = (event) =>
42
- @progress (event.position/event.total*100).round(2) if event.lengthComputable
117
+ if event.lengthComputable
118
+ @progress (event.position / event.total * 100).round 2
43
119
  xhr
44
120
 
121
+ #
122
+ # Resets form submit behavior
123
+ #
124
+ unbind: ->
125
+ @container.unbind('submit').find('input:submit,input:image,button:submit').unbind('click');
126
+
127
+ #
128
+ # Sets values of form inputs from given resource.
129
+ # Form will remember given resource and will use it while doing
130
+ # invalidation routine.
131
+ #
132
+ # @param [Resource] resource Resource to fill fields with
133
+ # @param [Function] decorator Decoration callback
134
+ #
45
135
  fill: (resource, decorator) ->
46
- e = if decorator? then decorator(resource.e) else resource.e
136
+ @__resource = resource
137
+
138
+ if decorator?
139
+ e = decorator resource.e
140
+ else
141
+ e = resource.e
47
142
  Object.each e, (key, val) =>
48
- key = resource.constructor.entityName()+"[#{key}]"
49
- @fields.filter("[name='#{key.underscore()}']:not(:file),[name='#{key.camelize(false)}']:not(:file)").val(val)
143
+ key = resource.__entityName + "[#{key}]"
144
+ input = @fields.filter("[name='#{key.underscore()}']:not(:file),[name='#{key.camelize(false)}']:not(:file)")
145
+ unless input.is ':checkbox'
146
+ input.val val
147
+ else
148
+ if val
149
+ input.attr 'checked', 'checked'
150
+ else
151
+ input.removeAttr 'checked'
50
152
 
51
153
  @container.attr 'action', resource.constructor.__buildSource(extension: resource.id)
52
154
  @__markMethod() if resource.id
53
155
  @container.attr 'method', 'POST'
54
156
 
157
+ #
158
+ # Inner success callback
159
+ #
55
160
  __success: (response, status, xhr) ->
56
161
  if xhr
57
- @success(response)
162
+ @success? response
58
163
  else if response.status == 200
59
- @success(response.json)
164
+ @success response.json
60
165
  else
61
- @__error(response.json)
166
+ @__error response.json
62
167
 
168
+ #
169
+ # Inner before callback
170
+ # By default will clean invalidation
171
+ #
63
172
  __before: (xhr, settings) ->
64
173
  if !@before? || @before(arguments...) is true
65
- @fields.removeClass(@invalidationClass)
174
+ @fields.removeClass @invalidationClass
66
175
 
176
+ #
177
+ # Inner error callback
178
+ # By default will trigger basic invalidation
179
+ #
67
180
  __error: (data) ->
68
181
  errors = if data.responseText
69
182
  try
70
- Object.extended(jQuery.parseJSON(data.responseText))
183
+ data = jQuery.parseJSON(data.responseText)
71
184
  catch error
72
- Object.extended()
185
+ {}
73
186
  else
74
- Object.extended(data)
187
+ data
75
188
 
76
189
  if !@error? || @error(errors) is true
77
- errors.each (field, notifications) =>
78
- field = @substitutions[field] if @substitutions[field]?
79
- input = @fields.filter("[name='#{field}']").addClass(@invalidationClass)
80
- @notification?(input, notifications)
190
+ errors = @__stringifyErrors(errors)
191
+
192
+ Object.each errors, (field, notifications) =>
193
+ input = @fields.filter("[name='#{field}']").addClass @invalidationClass
194
+ @notification? input, notifications
81
195
 
196
+ #
197
+ # Simulates REST methods by adding hidden _method input with real method
198
+ # while setting POST as the transport method
199
+ #
200
+ # @param [String] method Real method to simulate
201
+ #
82
202
  __markMethod: (method='PUT') ->
83
- method = $ '<input/>',
203
+ method = $('<input/>',
84
204
  type: 'hidden'
85
205
  name: '_method'
86
206
  value: method
87
- @container.append(method)
207
+ )
208
+ @container.append method
209
+
210
+ #
211
+ # Prepares server response for default error handler
212
+ # Turns all possible response notations into form notation (foo[bar])
213
+ # Every direct field of incoming data will be decorated by @substitutions
214
+ #
215
+ # Possible notations:
216
+ #
217
+ # * Flat validation result
218
+ # # input
219
+ # { field1: ['error'] }
220
+ # # if form was not associated with @__resource (see #fill)
221
+ # { "field1": ['error'] }
222
+ # # if form was associated with resource (named fluffy)
223
+ # { "fluffy[field1]": ['error']}
224
+ #
225
+ # * Complex validation result
226
+ # # input
227
+ # { foo: { bar: { baz: ['error'] } } }
228
+ # # output
229
+ # { "foo[bar][bar]": ['error'] }
230
+ #
231
+ # @param [Object] errors Data to prepare
232
+ #
233
+ __stringifyErrors: (errors) ->
234
+ result = {}
235
+
236
+ Object.each errors, (field, notifications) =>
237
+ if @substitutions[field]?
238
+ field = @substitutions[field]
239
+
240
+ if Object.isObject notifications
241
+ Object.each @__foldInlineEntities(notifications), (key, value) ->
242
+ result[field+key] = value
243
+ else
244
+ if field.indexOf(".") != -1
245
+ splited = field.split '.'
246
+ field = splited.shift()
247
+ field = @__resource.__entityName + "[#{field}]" if @__resource
248
+ field += "[#{f}]" for f in splited
249
+
250
+ else if @__resource
251
+ field = @__resource.__entityName + "[#{field}]"
252
+
253
+ result[field] = notifications
254
+
255
+ result
256
+
257
+ #
258
+ # Flattens complex inline structures into form notation
259
+ #
260
+ # Example:
261
+ # data = foo: { bar: { baz: [] } }
262
+ # inner = @__foldInlineEntities(data.foo, 'foo')
263
+ #
264
+ # inner # { "foo[bar][baz]": [] }
265
+ #
266
+ # @param [Object] hash Structure to fold
267
+ # @param [String] scope Prefix for resulting scopes
268
+ # @param [Object] result Context of result for recursion
269
+ #
270
+ __foldInlineEntities: (hash, scope="", result={}) ->
271
+ Object.each hash, (key, value) =>
272
+ if Object.isObject(value)
273
+ @__foldInlineEntities(value, "#{scope}[#{key}]", result)
274
+ else
275
+ result["#{scope}[#{key}]"] = value
276
+
277
+ result
@@ -1,6 +1,16 @@
1
1
  #= require joosy/core/joosy
2
2
 
3
+ #
4
+ # Set of system-wide helpers built-in into Joosy
5
+ #
6
+ # @class Joosy.Helpers.Application
7
+ #
3
8
  Joosy.helpers 'Application', ->
4
9
 
10
+ #
11
+ # Converts \n into <br/> in your text
12
+ #
13
+ # @param [String] text Text to convert
14
+ #
5
15
  @nl2br = (text) ->
6
- text.toString().replace(/\n/g, '<br/>')
16
+ text.toString().replace /\n/g, '<br/>'
@@ -1,24 +1,24 @@
1
- @Joosy = Object.extended(if @Joosy? then @Joosy else {})
2
-
3
- @Joosy.merge
1
+ @Joosy = Object.extended
4
2
  debug: false
5
3
  Modules: {}
6
4
  Resource: {}
7
5
  Templaters: {}
8
6
 
9
7
  Joosy.namespace = (name, generator=false) ->
10
- name = name.split('.')
8
+ name = name.split '.'
11
9
  space = window
12
- space = space[part] ?= {} for part in name
10
+ for part in name
11
+ space = space[part] ?= {}
13
12
 
14
- generator = generator.apply(space) if generator
13
+ if generator
14
+ generator = generator.apply space
15
15
  for key, klass of space
16
16
  if space.hasOwnProperty(key) &&
17
- Joosy.Module.hasAncestor(klass, Joosy.Module)
17
+ Joosy.Module.hasAncestor klass, Joosy.Module
18
18
  klass.__namespace__ = name
19
19
 
20
20
  Joosy.helpers = (name, generator) ->
21
- Joosy.namespace("Joosy.Helpers.#{name}", generator)
21
+ Joosy.namespace "Joosy.Helpers.#{name}", generator
22
22
 
23
23
  Joosy.test = ->
24
24
  text = "Hi :). I'm Joosy. And everything is just fine!"
@@ -36,13 +36,16 @@ Joosy.uuid = ->
36
36
  .toUpperCase()
37
37
 
38
38
  Joosy.preloadImages = (images, callback) ->
39
- images = [images] if !Object.isArray(images)
40
- callback() if images.length == 0
39
+ unless Object.isArray(images)
40
+ images = [images]
41
+ if images.length == 0
42
+ callback()
41
43
 
42
44
  ticks = images.length
43
45
  result = []
44
46
  checker = ->
45
- callback?() if (ticks -= 1) == 0
47
+ if (ticks -= 1) == 0
48
+ callback?()
46
49
 
47
50
  for p in images
48
51
  result.push $('<img/>').load(checker).attr('src', p)
@@ -53,13 +56,15 @@ Joosy.buildUrl = (url, params) ->
53
56
  paramsString = []
54
57
 
55
58
  Object.each params, (key, value) ->
56
- paramsString.push("#{key}=#{value}")
59
+ paramsString.push "#{key}=#{value}"
57
60
 
58
61
  hash = url.match(/(\#.*)?$/)[0]
59
- url = url.replace(/\#.*$/, '')
60
- url = url + "?" if !paramsString.isEmpty() && !url.has(/\?/)
62
+ url = url.replace /\#.*$/, ''
63
+ if !paramsString.isEmpty() && !url.has(/\?/)
64
+ url = url + "?"
61
65
 
62
- paramsString = paramsString.join('&')
63
- paramsString = '&' + paramsString if paramsString != '' && url.last() != '?'
66
+ paramsString = paramsString.join '&'
67
+ if !paramsString.isBlank() && url.last() != '?'
68
+ paramsString = '&' + paramsString
64
69
 
65
- url + paramsString + hash
70
+ url + paramsString + hash
@@ -17,11 +17,21 @@ class Joosy.Layout extends Joosy.Module
17
17
  @include Joosy.Modules.WidgetsManager
18
18
  @include Joosy.Modules.Filters
19
19
 
20
- @view 'layouts/default'
21
-
22
- @beforePaint: (callback) -> @::__beforePaint = callback
23
- @paint: (callback) -> @::__paint = callback
24
- @erase: (callback) -> @::__erase = callback
20
+ @view 'default'
21
+
22
+ @beforePaint: (callback) ->
23
+ @::__beforePaint = callback
24
+ @paint: (callback) ->
25
+ @::__paint = callback
26
+ @erase: (callback) ->
27
+ @::__erase = callback
28
+
29
+ @fetch: (callback) ->
30
+ @::__fetch = callback
31
+
32
+ data: false
33
+
34
+ constructor: (@params) ->
25
35
 
26
36
  navigate: (args...) ->
27
37
  Joosy.Router.navigate(args...)
@@ -36,7 +46,7 @@ class Joosy.Layout extends Joosy.Module
36
46
  @__runAfterLoads()
37
47
 
38
48
  __unload: ->
39
- @clearTime()
49
+ @__clearTime()
40
50
  @__unloadWidgets()
41
51
  @__runAfterUnloads()
42
52
 
@@ -44,4 +54,4 @@ class Joosy.Layout extends Joosy.Module
44
54
  @uuid = Joosy.uuid()
45
55
 
46
56
  content: ->
47
- $("##{@uuid}")
57
+ $("##{@uuid}")