joosy 0.1.0.RC2 → 0.1.0.RC3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.codoopts +5 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile.lock +4 -2
  4. data/README.md +10 -4
  5. data/app/assets/javascripts/joosy/core/application.js.coffee +3 -1
  6. data/app/assets/javascripts/joosy/core/form.js.coffee +69 -42
  7. data/app/assets/javascripts/joosy/core/helpers/view.js.coffee +30 -0
  8. data/app/assets/javascripts/joosy/core/joosy.js.coffee +125 -66
  9. data/app/assets/javascripts/joosy/core/layout.js.coffee +106 -1
  10. data/app/assets/javascripts/joosy/core/modules/container.js.coffee +41 -0
  11. data/app/assets/javascripts/joosy/core/modules/events.js.coffee +88 -1
  12. data/app/assets/javascripts/joosy/core/modules/filters.js.coffee +24 -0
  13. data/app/assets/javascripts/joosy/core/modules/log.js.coffee +18 -0
  14. data/app/assets/javascripts/joosy/core/modules/module.js.coffee +53 -1
  15. data/app/assets/javascripts/joosy/core/modules/renderer.js.coffee +22 -6
  16. data/app/assets/javascripts/joosy/core/modules/time_manager.js.coffee +21 -0
  17. data/app/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +26 -0
  18. data/app/assets/javascripts/joosy/core/page.js.coffee +225 -16
  19. data/app/assets/javascripts/joosy/core/preloader.js.coffee +8 -0
  20. data/app/assets/javascripts/joosy/core/resource/collection.js.coffee +52 -15
  21. data/app/assets/javascripts/joosy/core/resource/generic.js.coffee +78 -50
  22. data/app/assets/javascripts/joosy/core/resource/rest.js.coffee +39 -41
  23. data/app/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +36 -27
  24. data/app/assets/javascripts/joosy/core/router.js.coffee +95 -19
  25. data/app/assets/javascripts/joosy/core/widget.js.coffee +42 -1
  26. data/app/assets/javascripts/joosy/preloaders/caching.js.coffee +39 -24
  27. data/app/assets/javascripts/joosy/preloaders/inline.js.coffee +9 -7
  28. data/app/helpers/joosy/sprockets_helper.rb +4 -1
  29. data/lib/joosy/rails/version.rb +1 -1
  30. data/spec/javascripts/joosy/core/application_spec.js.coffee +3 -3
  31. data/spec/javascripts/joosy/core/resource/rest_spec.js.coffee +0 -2
  32. data/spec/javascripts/joosy/core/router_spec.js.coffee +24 -24
  33. data/vendor/assets/javascripts/jquery.form.js +978 -963
  34. data/vendor/assets/javascripts/sugar.js +1 -1
  35. metadata +20 -20
  36. data/app/assets/javascripts/joosy/core/helpers.js.coffee +0 -16
  37. data/vendor/assets/javascripts/base64.js +0 -135
data/.codoopts ADDED
@@ -0,0 +1,5 @@
1
+ --readme README.md
2
+ --title "Joosy API"
3
+ --verbose
4
+ --output-dir ./doc
5
+ ./app
data/.gitignore CHANGED
@@ -3,3 +3,5 @@
3
3
  log/*.log
4
4
  pkg/
5
5
  tmp/
6
+ .DS_Store
7
+ doc/
data/Gemfile.lock CHANGED
@@ -19,7 +19,7 @@ GIT
19
19
  PATH
20
20
  remote: .
21
21
  specs:
22
- joosy (0.1.0.RC1)
22
+ joosy (0.1.0.RC2)
23
23
  coffee-rails
24
24
  haml_coffee_assets
25
25
  jquery-rails
@@ -70,6 +70,7 @@ GEM
70
70
  diff-lcs (1.1.3)
71
71
  erubis (2.7.0)
72
72
  eventmachine (0.12.10)
73
+ eventmachine (0.12.10-java)
73
74
  execjs (1.3.0)
74
75
  multi_json (~> 1.0)
75
76
  ffi (1.0.11)
@@ -80,7 +81,7 @@ GEM
80
81
  guard-coffeescript (0.5.4)
81
82
  coffee-script (>= 2.2.0)
82
83
  guard (>= 0.8.3)
83
- haml_coffee_assets (0.7.1)
84
+ haml_coffee_assets (0.8.2)
84
85
  execjs (>= 1.2.9)
85
86
  sprockets (>= 2.0.3)
86
87
  tilt (>= 1.3.3)
@@ -92,6 +93,7 @@ GEM
92
93
  railties (>= 3.2.0.beta, < 5.0)
93
94
  thor (~> 0.14)
94
95
  json (1.6.5)
96
+ json (1.6.5-java)
95
97
  mail (2.4.1)
96
98
  i18n (>= 0.4.0)
97
99
  mime-types (~> 1.16)
data/README.md CHANGED
@@ -38,13 +38,13 @@ Unlike server-side, client-side has a state. At server-side you only have incomi
38
38
 
39
39
  They are:
40
40
 
41
- ##### Pages
41
+ ###### Pages
42
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
43
 
44
- ##### Layouts
44
+ ###### Layouts
45
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
46
 
47
- ##### Widgets
47
+ ###### Widgets
48
48
  Widgets are needed to keep your code DRY. They help you organize it in reusable modules.
49
49
 
50
50
  #### Models
@@ -73,10 +73,14 @@ Compare Joosy to Backbone like Rails to Sinatra. While Rails engine is much more
73
73
 
74
74
  ## What's next?
75
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)
76
+ We have a set of guidelines showing you, step-by-step, how to create a Joosy application. Follow them at our [Guides site](http://guides.joosy.ws/).
77
77
 
78
78
  ### Hello world app
79
79
 
80
+ Add Joosy gem to your Gemfile:
81
+
82
+ gem 'joosy'
83
+
80
84
  Using built-in generators you can quickly generate small app inside your Rails app to see Joosy application from inside a bit.
81
85
 
82
86
  rails g joosy:application dummy
@@ -84,6 +88,8 @@ Using built-in generators you can quickly generate small app inside your Rails a
84
88
 
85
89
  Now you can `rails s` and see Joosy placeholder at [localhost:3000/dummy](http://localhost:3000/dummy)
86
90
 
91
+ Generated application will consist of one `Layout` and one `Page` both including very basic practices of Joosy.
92
+
87
93
  # License
88
94
 
89
95
  Joosy is licensed under MIT: [www.opensource.org/licenses/MIT](www.opensource.org/licenses/MIT)
@@ -7,6 +7,8 @@ Joosy.Application =
7
7
  Pages: {}
8
8
  Layouts: {}
9
9
  Controls: {}
10
+
11
+ loading: true
10
12
 
11
13
  #
12
14
  # Starts Joosy application by binding to element and bootstraping routes
@@ -19,7 +21,7 @@ Joosy.Application =
19
21
  @[key] = value for key, value of options
20
22
  @templater = new Joosy.Templaters.RailsJST @name
21
23
 
22
- Joosy.Router.setupRoutes()
24
+ Joosy.Router.__setupRoutes()
23
25
 
24
26
  @sandboxSelector = Joosy.uuid()
25
27
  @content().after "<div id='#{@sandboxSelector}' style='display:none'></div>"
@@ -5,20 +5,19 @@
5
5
  #= require joosy/core/modules/container
6
6
 
7
7
  #
8
- # AJAXifies form including file uploads and stuff. Built on top of jQuery.Form
8
+ # AJAXifies form including file uploads and stuff. Built on top of jQuery.Form.
9
9
  #
10
10
  # Joosy.Form automatically cares of form validation hihglights. It can
11
11
  # read common server error responses and add .field_with_errors class to proper
12
12
  # field.
13
13
  #
14
- # If you don't have resource associated (#fill) with form it will try to find fields
14
+ # If you don't have resource associated with form (#fill), it will try to find fields
15
15
  # by exact keywords from response. Otherwise it will search for resource_name[field].
16
16
  #
17
- #
18
- # Example
17
+ # @example Joosy.Form usage
19
18
  # form = new Joosy.Form, -> (response)
20
19
  # console.log "Saved and got some: #{response}"
21
- #
20
+ #
22
21
  # form.progress = (percent) -> console.log "Uploaded by #{percent}%"
23
22
  # form.fill @resource
24
23
  #
@@ -47,10 +46,12 @@ class Joosy.Form extends Joosy.Module
47
46
  'fields': 'input,select,textarea'
48
47
 
49
48
  #
50
- # Submits your form once and unbinds leaving it simple form without AJAX
49
+ # Makes one AJAX request with form data without binding
50
+ #
51
+ # @see #constructor
51
52
  #
52
53
  # @param [Element] form Instance of HTML form element
53
- # @param [Object] opts Map of additional options (see constructor)
54
+ # @param [Hash] opts Options
54
55
  #
55
56
  @submit: (form, opts={}) ->
56
57
  form = new @(form, opts)
@@ -61,30 +62,30 @@ class Joosy.Form extends Joosy.Module
61
62
  #
62
63
  # During initialization replaces your basic form submit with AJAX request
63
64
  #
64
- # If method of form differs from POST or GET it will simulate it
65
+ # @note If method of form differs from POST or GET it will simulate it
65
66
  # by adding hidden _method input. In this cases the method itself will be
66
67
  # set to POST.
67
68
  #
68
- # For browsers having no support of HTML5 Forms it may do an iframe requests
69
+ # @note For browsers having no support of HTML5 Forms it may do an iframe requests
69
70
  # to handle file uploading.
70
71
  #
71
- # Supported options are:
72
+ # @param [jQuery] form Form element instance
73
+ # @param [Hash] opts Options
72
74
  #
73
- # * before: `(XHR) -> Boolean` triggers right before submit.
75
+ # @option opts [Function] before `(XHR) -> Bollean` to trigger right before submit.
74
76
  # By default will run form invalidation cleanup. This behavior can be canceled
75
77
  # by returning false from your own before callback. Both of callbacks will run if
76
78
  # you return true.
77
79
  #
78
- # * success: `(Object) -> null` triggers on 200 HTTP code from server. Pases
79
- # in the parsed JSON.
80
+ # @option opts [Function] success `(Object) -> null` triggers on 200 HTTP code from server.
81
+ # Pases in the parsed JSON.
80
82
  #
81
- # * progress: `(Float) -> null` runs peridically while form is uploading.
83
+ # @option opts [Function] progress `(Float) -> null` runs periodically while form is uploading
82
84
  #
83
- # * error: `(Object) -> Boolean` triggers if server responsed with anything but 200.
85
+ # @option opts [Function] error `(Object) -> Boolean` triggers if server responded with anything but 200.
84
86
  # By default will run form invalidation routine. This behavior can be canceled
85
87
  # by returning false from your own error callback. Both of callbacks will run if
86
88
  # you return true.
87
- #
88
89
  #
89
90
  constructor: (form, opts={}) ->
90
91
  if Object.isFunction opts
@@ -119,15 +120,14 @@ class Joosy.Form extends Joosy.Module
119
120
  xhr
120
121
 
121
122
  #
122
- # Resets form submit behavior
123
+ # Resets form submit behavior to default
123
124
  #
124
125
  unbind: ->
125
126
  @container.unbind('submit').find('input:submit,input:image,button:submit').unbind('click');
126
127
 
127
128
  #
128
- # Sets values of form inputs from given resource.
129
- # Form will remember given resource and will use it while doing
130
- # invalidation routine.
129
+ # Links current form with given resource and sets values of form inputs from with it.
130
+ # Form will use give resource while doing invalidation routine.
131
131
  #
132
132
  # @param [Resource] resource Resource to fill fields with
133
133
  # @param [Function] decorator Decoration callback
@@ -155,7 +155,26 @@ class Joosy.Form extends Joosy.Module
155
155
  @container.attr 'method', 'POST'
156
156
 
157
157
  #
158
- # Inner success callback
158
+ # Submit the HTML Form
159
+ #
160
+ submit: ->
161
+ @container.submit()
162
+
163
+ #
164
+ # Serializes form into query string.
165
+ #
166
+ # @param [Boolean] skipMethod Determines if we should skip magical _method field
167
+ #
168
+ # @return [String]
169
+ #
170
+ serialize: (skipMethod=true) ->
171
+ data = @container.serialize()
172
+ data = data.replace /\&?\_method\=put/i, '' if skipMethod
173
+
174
+ data
175
+
176
+ #
177
+ # Inner success callback.
159
178
  #
160
179
  __success: (response, status, xhr) ->
161
180
  if xhr
@@ -166,16 +185,16 @@ class Joosy.Form extends Joosy.Module
166
185
  @__error response.json
167
186
 
168
187
  #
169
- # Inner before callback
170
- # By default will clean invalidation
188
+ # Inner before callback.
189
+ # By default will clean invalidation.
171
190
  #
172
191
  __before: (xhr, settings) ->
173
192
  if !@before? || @before(arguments...) is true
174
193
  @fields.removeClass @invalidationClass
175
194
 
176
195
  #
177
- # Inner error callback
178
- # By default will trigger basic invalidation
196
+ # Inner error callback.
197
+ # By default will trigger basic invalidation.
179
198
  #
180
199
  __error: (data) ->
181
200
  errors = if data.responseText
@@ -190,8 +209,18 @@ class Joosy.Form extends Joosy.Module
190
209
  errors = @__stringifyErrors(errors)
191
210
 
192
211
  Object.each errors, (field, notifications) =>
193
- input = @fields.filter("[name='#{field}']").addClass @invalidationClass
212
+ input = @__findField(field).addClass @invalidationClass
194
213
  @notification? input, notifications
214
+
215
+ #
216
+ # Finds field by field name.
217
+ # This is not inlined since we want to override
218
+ # or monkeypatch it from time to time
219
+ #
220
+ # @param [String] field Name of field to find
221
+ #
222
+ __findField: (field) ->
223
+ @fields.filter("[name='#{field}']")
195
224
 
196
225
  #
197
226
  # Simulates REST methods by adding hidden _method input with real method
@@ -212,24 +241,20 @@ class Joosy.Form extends Joosy.Module
212
241
  # Turns all possible response notations into form notation (foo[bar])
213
242
  # Every direct field of incoming data will be decorated by @substitutions
214
243
  #
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']}
244
+ # @example Flat validation result
245
+ # { field1: ['error'] } # input
246
+ # { field1: ['error'] } # if form was not associated with Resource by {#fill}
247
+ # { "fluffy[field1]": ['error']} # if form was associated with Resource (named fluffy)
224
248
  #
225
- # * Complex validation result
226
- # # input
227
- # { foo: { bar: { baz: ['error'] } } }
228
- # # output
229
- # { "foo[bar][bar]": ['error'] }
249
+ # @example Complex validation result
250
+ # { foo: { bar: { baz: ['error'] } } } # input
251
+ # { "foo[bar][bar]": ['error'] } # output
230
252
  #
231
253
  # @param [Object] errors Data to prepare
232
254
  #
255
+ # @return [Hash<String, Array>] Flat hash with field names in keys and arrays
256
+ # of errors in values
257
+ #
233
258
  __stringifyErrors: (errors) ->
234
259
  result = {}
235
260
 
@@ -257,16 +282,18 @@ class Joosy.Form extends Joosy.Module
257
282
  #
258
283
  # Flattens complex inline structures into form notation
259
284
  #
260
- # Example:
285
+ # @example Basic flattening
261
286
  # data = foo: { bar: { baz: [] } }
262
287
  # inner = @__foldInlineEntities(data.foo, 'foo')
263
- #
288
+ #
264
289
  # inner # { "foo[bar][baz]": [] }
265
290
  #
266
291
  # @param [Object] hash Structure to fold
267
292
  # @param [String] scope Prefix for resulting scopes
268
293
  # @param [Object] result Context of result for recursion
269
294
  #
295
+ # @return [Hash]
296
+ #
270
297
  __foldInlineEntities: (hash, scope="", result={}) ->
271
298
  Object.each hash, (key, value) =>
272
299
  if Object.isObject(value)
@@ -0,0 +1,30 @@
1
+ #= require joosy/core/joosy
2
+
3
+ #
4
+ # Rendering and string representation helpers
5
+ #
6
+ Joosy.helpers 'Application', ->
7
+
8
+ #
9
+ # Converts \n into <br/> in your text
10
+ #
11
+ # @param [String] text Text to convert
12
+ #
13
+ @nl2br = (text) ->
14
+ text.toString().replace /\n/g, '<br/>'
15
+
16
+ #
17
+ # Wraps the inline block into given template
18
+ # Request template will receive the inline block as @yield parameter
19
+ #
20
+ # Example
21
+ # -# foo/baz template
22
+ # != @renderWrapped 'foo/bar', ->
23
+ # %b This string will be passed to 'foo/bar'
24
+ #
25
+ # -# foo/bar template
26
+ # %h1 I'm the wrapper here!
27
+ # != @yield
28
+ #
29
+ @renderWrapped = (template, lambda) ->
30
+ @render template, Joosy.Module.merge(this, yield: lambda())
@@ -1,70 +1,129 @@
1
- @Joosy = Object.extended
2
- debug: false
1
+ #
2
+ # All the tiny core stuff
3
+ #
4
+ # @module
5
+ #
6
+ @Joosy =
7
+ #
8
+ # Core modules container
9
+ #
3
10
  Modules: {}
11
+
12
+ #
13
+ # Resources container
14
+ #
4
15
  Resource: {}
16
+
17
+ #
18
+ # Templaters container
19
+ #
5
20
  Templaters: {}
6
21
 
7
- Joosy.namespace = (name, generator=false) ->
8
- name = name.split '.'
9
- space = window
10
- for part in name
11
- space = space[part] ?= {}
12
-
13
- if generator
14
- generator = generator.apply space
15
- for key, klass of space
16
- if space.hasOwnProperty(key) &&
17
- Joosy.Module.hasAncestor klass, Joosy.Module
18
- klass.__namespace__ = name
19
-
20
- Joosy.helpers = (name, generator) ->
21
- Joosy.namespace "Joosy.Helpers.#{name}", generator
22
-
23
- Joosy.test = ->
24
- text = "Hi :). I'm Joosy. And everything is just fine!"
25
-
26
- if console
27
- console.log text
28
- else
29
- alert text
30
-
31
- Joosy.uuid = ->
32
- 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) ->
33
- r = Math.random() * 16 | 0
34
- v = if c is 'x' then r else r & 3 | 8
35
- v.toString 16
36
- .toUpperCase()
37
-
38
- Joosy.preloadImages = (images, callback) ->
39
- unless Object.isArray(images)
40
- images = [images]
41
- if images.length == 0
42
- callback()
43
-
44
- ticks = images.length
45
- result = []
46
- checker = ->
47
- if (ticks -= 1) == 0
48
- callback?()
49
-
50
- for p in images
51
- result.push $('<img/>').load(checker).attr('src', p)
52
-
53
- result
54
-
55
- Joosy.buildUrl = (url, params) ->
56
- paramsString = []
57
-
58
- Object.each params, (key, value) ->
59
- paramsString.push "#{key}=#{value}"
60
-
61
- hash = url.match(/(\#.*)?$/)[0]
62
- url = url.replace /\#.*$/, ''
63
- if !paramsString.isEmpty() && !url.has(/\?/)
64
- url = url + "?"
65
-
66
- paramsString = paramsString.join '&'
67
- if !paramsString.isBlank() && url.last() != '?'
68
- paramsString = '&' + paramsString
69
-
70
- url + paramsString + hash
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
+ # Generates UUID
74
+ #
75
+ uuid: ->
76
+ 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) ->
77
+ r = Math.random() * 16 | 0
78
+ v = if c is 'x' then r else r & 3 | 8
79
+ v.toString 16
80
+ .toUpperCase()
81
+
82
+ #
83
+ # Preloads sets of images then runs callback
84
+ #
85
+ # @param [Array<String>] images Images paths
86
+ # @param [Function] callback Action to run when every picture was loaded (or triggered an error)
87
+ #
88
+ preloadImages: (images, callback) ->
89
+ unless Object.isArray(images)
90
+ images = [images]
91
+ if images.length == 0
92
+ callback()
93
+
94
+ ticks = images.length
95
+ result = []
96
+ checker = ->
97
+ if (ticks -= 1) == 0
98
+ callback?()
99
+
100
+ for p in images
101
+ result.push $('<img/>').load(checker).error(checker).attr('src', p)
102
+
103
+ result
104
+
105
+ #
106
+ # Basic URI builder. Joins base path with params hash
107
+ #
108
+ # @param [String] url Base url
109
+ # @param [Hash] params Parameters to join
110
+ #
111
+ # @example Basic usage
112
+ # Joosy.buildUrl 'http://joosy.ws/#!/test', {foo: 'bar'} # http://joosy.ws/?foo=bar#!/test
113
+ #
114
+ buildUrl: (url, params) ->
115
+ paramsString = []
116
+
117
+ Object.each params, (key, value) ->
118
+ paramsString.push "#{key}=#{value}"
119
+
120
+ hash = url.match(/(\#.*)?$/)[0]
121
+ url = url.replace /\#.*$/, ''
122
+ if !paramsString.isEmpty() && !url.has(/\?/)
123
+ url = url + "?"
124
+
125
+ paramsString = paramsString.join '&'
126
+ if !paramsString.isBlank() && url.last() != '?'
127
+ paramsString = '&' + paramsString
128
+
129
+ url + paramsString + hash