joosy 0.1.0.RC1

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 (98) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +14 -0
  3. data/Gemfile.lock +159 -0
  4. data/Guardfile +30 -0
  5. data/MIT-LICENSE +21 -0
  6. data/README.rdoc +3 -0
  7. data/Rakefile +14 -0
  8. data/app/assets/javascripts/joosy.js.coffee +7 -0
  9. data/app/assets/javascripts/joosy/core/application.js.coffee +28 -0
  10. data/app/assets/javascripts/joosy/core/form.js.coffee +87 -0
  11. data/app/assets/javascripts/joosy/core/helpers.js.coffee +6 -0
  12. data/app/assets/javascripts/joosy/core/joosy.js.coffee +65 -0
  13. data/app/assets/javascripts/joosy/core/layout.js.coffee +47 -0
  14. data/app/assets/javascripts/joosy/core/modules/container.js.coffee +59 -0
  15. data/app/assets/javascripts/joosy/core/modules/events.js.coffee +35 -0
  16. data/app/assets/javascripts/joosy/core/modules/filters.js.coffee +39 -0
  17. data/app/assets/javascripts/joosy/core/modules/log.js.coffee +15 -0
  18. data/app/assets/javascripts/joosy/core/modules/module.js.coffee +43 -0
  19. data/app/assets/javascripts/joosy/core/modules/renderer.js.coffee +116 -0
  20. data/app/assets/javascripts/joosy/core/modules/time_manager.js.coffee +25 -0
  21. data/app/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +58 -0
  22. data/app/assets/javascripts/joosy/core/page.js.coffee +156 -0
  23. data/app/assets/javascripts/joosy/core/preloader.js.coffee +5 -0
  24. data/app/assets/javascripts/joosy/core/resource/generic.js.coffee +61 -0
  25. data/app/assets/javascripts/joosy/core/resource/rest.js.coffee +76 -0
  26. data/app/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +48 -0
  27. data/app/assets/javascripts/joosy/core/router.js.coffee +89 -0
  28. data/app/assets/javascripts/joosy/core/templaters/rails_jst.js.coffee +20 -0
  29. data/app/assets/javascripts/joosy/core/widget.js.coffee +41 -0
  30. data/app/assets/javascripts/joosy/preloaders/caching.js.coffee +94 -0
  31. data/app/assets/javascripts/joosy/preloaders/inline.js.coffee +55 -0
  32. data/app/helpers/joosy/sprockets_helper.rb +23 -0
  33. data/app/views/layouts/json_wrapper.json.erb +1 -0
  34. data/joosy.gemspec +25 -0
  35. data/lib/joosy.rb +9 -0
  36. data/lib/joosy/forms.rb +47 -0
  37. data/lib/joosy/rails/engine.rb +17 -0
  38. data/lib/joosy/rails/version.rb +5 -0
  39. data/lib/rails/generators/joosy/application_generator.rb +41 -0
  40. data/lib/rails/generators/joosy/joosy_base.rb +30 -0
  41. data/lib/rails/generators/joosy/layout_generator.rb +32 -0
  42. data/lib/rails/generators/joosy/page_generator.rb +44 -0
  43. data/lib/rails/generators/joosy/preloader_generator.rb +32 -0
  44. data/lib/rails/generators/joosy/resource_generator.rb +29 -0
  45. data/lib/rails/generators/joosy/templates/app.js.coffee +10 -0
  46. data/lib/rails/generators/joosy/templates/app/helpers/application.js.coffee +4 -0
  47. data/lib/rails/generators/joosy/templates/app/layouts/application.js.coffee +2 -0
  48. data/lib/rails/generators/joosy/templates/app/layouts/template.js.coffee +2 -0
  49. data/lib/rails/generators/joosy/templates/app/pages/application.js.coffee +1 -0
  50. data/lib/rails/generators/joosy/templates/app/pages/template.js.coffee +5 -0
  51. data/lib/rails/generators/joosy/templates/app/pages/welcome/index.js.coffee +22 -0
  52. data/lib/rails/generators/joosy/templates/app/resources/template.js.coffee +2 -0
  53. data/lib/rails/generators/joosy/templates/app/routes.js.coffee +8 -0
  54. data/lib/rails/generators/joosy/templates/app/templates/layouts/application.jst.hamlc +2 -0
  55. data/lib/rails/generators/joosy/templates/app/templates/pages/welcome/index.jst.hamlc +7 -0
  56. data/lib/rails/generators/joosy/templates/app/widgets/template.js.coffee +2 -0
  57. data/lib/rails/generators/joosy/templates/app_controller.rb +9 -0
  58. data/lib/rails/generators/joosy/templates/app_preloader.js.coffee.erb +13 -0
  59. data/lib/rails/generators/joosy/templates/preload.html.erb +26 -0
  60. data/lib/rails/generators/joosy/templates/preload.html.haml +19 -0
  61. data/lib/rails/generators/joosy/widget_generator.rb +32 -0
  62. data/spec/javascripts/helpers/spec_helper.js.coffee +44 -0
  63. data/spec/javascripts/joosy/core/application_spec.js.coffee +40 -0
  64. data/spec/javascripts/joosy/core/form_spec.js.coffee +141 -0
  65. data/spec/javascripts/joosy/core/joosy_spec.js.coffee +72 -0
  66. data/spec/javascripts/joosy/core/layout_spec.js.coffee +50 -0
  67. data/spec/javascripts/joosy/core/modules/container_spec.js.coffee +92 -0
  68. data/spec/javascripts/joosy/core/modules/events_spec.js.coffee +53 -0
  69. data/spec/javascripts/joosy/core/modules/filters_spec.js.coffee +71 -0
  70. data/spec/javascripts/joosy/core/modules/log_spec.js.coffee +15 -0
  71. data/spec/javascripts/joosy/core/modules/module_spec.js.coffee +47 -0
  72. data/spec/javascripts/joosy/core/modules/renderer_spec.js.coffee +149 -0
  73. data/spec/javascripts/joosy/core/modules/time_manager_spec.js.coffee +25 -0
  74. data/spec/javascripts/joosy/core/modules/widget_manager_spec.js.coffee +75 -0
  75. data/spec/javascripts/joosy/core/page_spec.js.coffee +175 -0
  76. data/spec/javascripts/joosy/core/resource/generic_spec.js.coffee +35 -0
  77. data/spec/javascripts/joosy/core/resource/rest_collection_spec.js.coffee +65 -0
  78. data/spec/javascripts/joosy/core/resource/rest_spec.js.coffee +108 -0
  79. data/spec/javascripts/joosy/core/router_spec.js.coffee +123 -0
  80. data/spec/javascripts/joosy/core/templaters/rails_jst_spec.js.coffee +25 -0
  81. data/spec/javascripts/joosy/core/widget_spec.js.coffee +51 -0
  82. data/spec/javascripts/joosy/preloaders/caching_spec.js.coffee +36 -0
  83. data/spec/javascripts/joosy/preloaders/inline_spec.js.coffee +16 -0
  84. data/spec/javascripts/support/assets/coolface.jpg +0 -0
  85. data/spec/javascripts/support/assets/okay.jpg +0 -0
  86. data/spec/javascripts/support/assets/test.js +1 -0
  87. data/spec/javascripts/support/jasmine.yml +74 -0
  88. data/spec/javascripts/support/jasmine_config.rb +23 -0
  89. data/spec/javascripts/support/jasmine_runner.rb +32 -0
  90. data/spec/javascripts/support/sinon-1.3.1.js +3469 -0
  91. data/spec/javascripts/support/sinon-ie-1.3.1.js +82 -0
  92. data/vendor/assets/javascripts/base64.js +135 -0
  93. data/vendor/assets/javascripts/inflection.js +656 -0
  94. data/vendor/assets/javascripts/jquery.form.js +980 -0
  95. data/vendor/assets/javascripts/jquery.hashchange.js +390 -0
  96. data/vendor/assets/javascripts/metamorph.js +409 -0
  97. data/vendor/assets/javascripts/sugar.js +6040 -0
  98. metadata +232 -0
@@ -0,0 +1,156 @@
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
+ #= require joosy/core/modules/renderer
7
+ #= require joosy/core/modules/time_manager
8
+ #= require joosy/core/modules/widgets_manager
9
+ #= require joosy/core/modules/filters
10
+
11
+ class Joosy.Page extends Joosy.Module
12
+ @include Joosy.Modules.Log
13
+ @include Joosy.Modules.Events
14
+ @include Joosy.Modules.Container
15
+ @include Joosy.Modules.Renderer
16
+ @include Joosy.Modules.TimeManager
17
+ @include Joosy.Modules.WidgetsManager
18
+ @include Joosy.Modules.Filters
19
+
20
+ layout: false
21
+ previous: false
22
+ params: false
23
+ data: false
24
+
25
+ @fetch: (callback) ->
26
+ @::__fetch = callback
27
+
28
+ @scroll: (element, options={}) ->
29
+ @::__scrollElement = element
30
+ @::__scrollSpeed = options.speed || 500
31
+ @::__scrollMargin = options.margin || 0
32
+
33
+ @layout: (layoutClass) ->
34
+ @::__layoutClass = layoutClass
35
+
36
+ @beforePaint: (callback) -> @::__beforePaint = callback
37
+ @paint: (callback) -> @::__paint = callback
38
+ @afterPaint: (callback) -> @::__afterPaint = callback
39
+ @erase: (callback) -> @::__erase = callback
40
+
41
+ constructor: (@params, @previous) ->
42
+ @__layoutClass ||= ApplicationLayout
43
+
44
+ if @__runBeforeLoads(@params, @previous)
45
+ if !@previous?.layout?.uuid? || @previous?.__layoutClass != @__layoutClass
46
+ @__bootstrapLayout()
47
+ else
48
+ @__bootstrap()
49
+
50
+ navigate: (args...) -> Joosy.Router.navigate(args...)
51
+
52
+ __renderSection: ->
53
+ 'pages'
54
+
55
+ __fixHeight: ->
56
+ $('html').css 'min-height', $(document).height()
57
+
58
+ __releaseHeight: ->
59
+ $('html').css 'min-height', ''
60
+
61
+ __load: ->
62
+ @refreshElements()
63
+ @__delegateEvents()
64
+ @__setupWidgets()
65
+ @__runAfterLoads(@params, @previous)
66
+ if @__scrollElement
67
+ scroll = $(@__extractSelector(@__scrollElement)).offset()?.top + @__scrollMargin
68
+ Joosy.Modules.Log.debugAs @, "Scrolling to #{@__extractSelector(@__scrollElement)}"
69
+ $('html, body').animate {scrollTop: scroll}, @__scrollSpeed, =>
70
+ @__releaseHeight() if @__scrollSpeed != 0
71
+
72
+ Joosy.Modules.Log.debugAs @, "Page loaded"
73
+
74
+ __unload: ->
75
+ @clearTime()
76
+ @__unloadWidgets()
77
+ @__removeMetamorphs()
78
+ @__runAfterUnloads(@params, @previous)
79
+ delete @previous
80
+
81
+
82
+ __callSyncedThrough: (entity, receiver, params, callback) ->
83
+ if entity?[receiver]?
84
+ entity[receiver].apply entity, params.clone().add(callback)
85
+ else
86
+ callback()
87
+
88
+ # Boot Sequence:
89
+ #
90
+ # previous::erase \
91
+ # previous::unload \
92
+ # beforePaint \
93
+ # > paint
94
+ # fetch /
95
+ #
96
+ __bootstrap: ->
97
+ Joosy.Modules.Log.debugAs @, "Boostraping page"
98
+ @layout = @previous.layout
99
+
100
+ callbacksParams = [@layout.content()]
101
+
102
+ @__fixHeight() if @__scrollElement && @__scrollSpeed != 0
103
+
104
+ @wait "stageClear dataReceived", =>
105
+ @__callSyncedThrough this, '__paint', callbacksParams, =>
106
+ # Page HTML
107
+ @swapContainer @layout.content(), @__renderer(@data || {})
108
+ @container = @layout.content()
109
+
110
+ # Loading
111
+ @__load()
112
+
113
+ @layout.content()
114
+
115
+ @__callSyncedThrough @previous, '__erase', callbacksParams, =>
116
+ @previous?.__unload()
117
+ @__callSyncedThrough this, '__beforePaint', callbacksParams, =>
118
+ @trigger 'stageClear'
119
+
120
+ @__callSyncedThrough this, '__fetch', [], =>
121
+ Joosy.Modules.Log.debugAs @, "Fetch complete"
122
+ @trigger 'dataReceived'
123
+
124
+ __bootstrapLayout: ->
125
+ Joosy.Modules.Log.debugAs @, "Boostraping page with layout"
126
+ @layout = new @__layoutClass
127
+
128
+ callbacksParams = [Joosy.Application.content(), this]
129
+
130
+ @__fixHeight() if @__scrollElement && @__scrollSpeed != 0
131
+
132
+ @wait "stageClear dataReceived", =>
133
+ @__callSyncedThrough @layout, '__paint', callbacksParams, =>
134
+ # Layout HTML
135
+ @swapContainer Joosy.Application.content(), @layout.__renderer
136
+ yield: => @layout.yield()
137
+
138
+ # Page HTML
139
+ @swapContainer @layout.content(), @__renderer(@data || {})
140
+ @container = @layout.content()
141
+
142
+ # Loading
143
+ @layout.__load Joosy.Application.content()
144
+ @__load()
145
+
146
+ Joosy.Application.content()
147
+
148
+ @__callSyncedThrough @previous?.layout, '__erase', callbacksParams, =>
149
+ @previous?.layout?.__unload?()
150
+ @previous?.__unload()
151
+ @__callSyncedThrough @layout, '__beforePaint', callbacksParams, =>
152
+ @trigger 'stageClear'
153
+
154
+ @__callSyncedThrough this, '__fetch', [], =>
155
+ Joosy.Modules.Log.debugAs @, "Fetch complete"
156
+ @trigger 'dataReceived'
@@ -0,0 +1,5 @@
1
+ # Preloader stub
2
+ @Preloader = Object.extended
3
+ load: (libraries, options) ->
4
+ @.merge options
5
+ @complete?.call window
@@ -0,0 +1,61 @@
1
+ class Joosy.Resource.Generic extends Joosy.Module
2
+ @include Joosy.Modules.Log
3
+ @include Joosy.Modules.Events
4
+
5
+ @beforeLoad: (action) -> @::__beforeLoad = action
6
+
7
+ @create: ->
8
+ shim = ->
9
+ shim.__call.apply(shim, arguments)
10
+
11
+ if shim.__proto__
12
+ shim.__proto__ = @prototype
13
+ else
14
+ for key, value of @prototype
15
+ shim[key] = value
16
+
17
+ shim.constructor = @
18
+
19
+ @apply(shim, arguments)
20
+
21
+ shim
22
+
23
+ constructor: (data) ->
24
+ @__fillData(data)
25
+
26
+ get: (path) ->
27
+ target = @__callTarget(path)
28
+ target[0][target[1]]
29
+
30
+ set: (path, value) ->
31
+ target = @__callTarget(path)
32
+ target[0][target[1]] = value
33
+ @trigger 'changed'
34
+
35
+ __callTarget: (path) ->
36
+ if path.has(/\./) && !@e[path]?
37
+ path = path.split '.'
38
+ keyword = path.pop()
39
+ target = @e
40
+
41
+ for part in path
42
+ target[part] ||= {}
43
+ target = target[part]
44
+
45
+ [target, keyword]
46
+ else
47
+ [@e, path]
48
+
49
+ __call: (path, value) ->
50
+ if value
51
+ @set path, value
52
+ else
53
+ @get path
54
+
55
+ __fillData: (data) ->
56
+ @e = @__prepareData(data)
57
+
58
+ __prepareData: (data) ->
59
+ data = Object.extended(data)
60
+ data = @__beforeLoad(data) if @__beforeLoad?
61
+ data
@@ -0,0 +1,76 @@
1
+ #= require ./rest_collection
2
+
3
+ class Joosy.Resource.REST extends Joosy.Resource.Generic
4
+
5
+ __primaryKey: 'id'
6
+
7
+ @entity: (name) -> @::__entityName = name
8
+ @source: (source) -> @::__source = source
9
+ @primary: (primary) -> @::__primaryKey = primary
10
+
11
+ constructor: (description) ->
12
+ if @constructor.__isId(description)
13
+ @id = description
14
+ else
15
+ super description
16
+ @id = @e[@__primaryKey]
17
+
18
+ @entityName: ->
19
+ unless @::hasOwnProperty '__entityName'
20
+ throw new Error "Joosy.Resource.REST does not have entity name"
21
+
22
+ @::__entityName
23
+
24
+ # Returns single entity if int/string given
25
+ # Returns collection if no value or Object (with parameters) given
26
+ @find: (description, callback, options) ->
27
+ if @__isId(description)
28
+ resource = new @(description)
29
+ resource.fetch callback, options
30
+ resource
31
+ else
32
+ if Object.isFunction(description) && !callback
33
+ callback = description
34
+ description = undefined
35
+ resources = new Joosy.Resource.RESTCollection(@, description)
36
+ resources.fetch callback, options
37
+ resources
38
+
39
+ fetch: (callback, options) ->
40
+ @constructor.__ajax 'get', @constructor.__buildSource(extension: @id), options, (e) =>
41
+ @__fillData(e)
42
+ callback?(this)
43
+
44
+ this
45
+
46
+ save: ->
47
+
48
+ destroy: (callback, options) ->
49
+ @constructor.__ajax 'delete', @constructor.__buildSource(extension: @id), options, (e) =>
50
+ callback?(this)
51
+
52
+ this
53
+
54
+ @__isId: (something) -> Object.isNumber(something) || Object.isString(something)
55
+
56
+ @__ajax: (method, url, options={}, callback) ->
57
+ $.ajax url, Object.extended(
58
+ type: method
59
+ success: callback
60
+ cache: false
61
+ dataType: 'json'
62
+ ).merge options
63
+
64
+ @__buildSource: (options={}) ->
65
+ unless @::hasOwnProperty '__source'
66
+ @::__source = "/" + @entityName().pluralize()
67
+
68
+ source = Joosy.buildUrl("#{@::__source}/#{options.extension || ''}", options.params)
69
+
70
+ __fillData: (data) ->
71
+ data = @__prepareData data
72
+
73
+ if Object.isObject(data) && data[@constructor.entityName()] && data.keys().length == 1
74
+ @e = Object.extended data[@constructor.entityName()]
75
+ else
76
+ @e = data
@@ -0,0 +1,48 @@
1
+ class Joosy.Resource.RESTCollection extends Joosy.Module
2
+ @include Joosy.Modules.Log
3
+ @include Joosy.Modules.Events
4
+
5
+ data: []
6
+ pages: Object.extended()
7
+
8
+ constructor: (@model, params={}) ->
9
+ @params = Object.extended(params)
10
+
11
+ # Clears the storage and attempts to import given JSON
12
+ reset: (entities) ->
13
+ @data = @modelize entities
14
+ @pages = Object.extended().merge { 1: @data }
15
+
16
+ this
17
+
18
+ # Clears the storage and gets new data from server
19
+ fetch: (callback, options) ->
20
+ @model.__ajax 'get', @model.__buildSource(params: @params), options, (data) =>
21
+ @reset(data)
22
+ callback?(this)
23
+
24
+ this
25
+
26
+ # Returns the subset for requested page. Requests with &page=x if not found localy.
27
+ page: (number, callback=false) ->
28
+ if @pages[number]?
29
+ callback?(@pages[number])
30
+ return this
31
+
32
+ @model.__ajax 'get', @model.__buildSource(params: @params.merge(page: number)), {}, (data) =>
33
+ @pages[number] = @modelize data
34
+
35
+ @data = []
36
+ @pages.keys().sort().each (x) =>
37
+ @data.add @pages[x]
38
+
39
+ callback?(@pages[number])
40
+
41
+ this
42
+
43
+ modelize: (collection) ->
44
+ if collection instanceof Array
45
+ collection.map (x) =>
46
+ @model.create x
47
+ else
48
+ []
@@ -0,0 +1,89 @@
1
+ #= require joosy/core/joosy
2
+
3
+ Joosy.Router =
4
+ rawRoutes: Object.extended()
5
+ routes: Object.extended()
6
+
7
+ reset: ->
8
+ @rawRoutes = Object.extended()
9
+ @routes = Object.extended()
10
+
11
+ map: (routes) ->
12
+ @rawRoutes.merge routes
13
+
14
+ setupRoutes: ->
15
+ @prepareRoutes @rawRoutes
16
+
17
+ @respondRoute(location.hash)
18
+ $(window).hashchange => @respondRoute(location.hash)
19
+
20
+ prepareRoutes: (routes, namespace='') ->
21
+ if !namespace && routes[404]
22
+ @wildcardAction = routes[404]
23
+ delete routes[404]
24
+
25
+ routes.each (path, response) =>
26
+ path = (namespace + path).replace(/\/{2,}/, '/')
27
+ if response && (typeof(response) == 'function' || response.prototype?)
28
+ @routes.merge @prepareRoute path,response
29
+ else
30
+ @prepareRoutes Object.extended(response), path
31
+
32
+ prepareRoute: (path, response) ->
33
+ matchPath = path.replace(/\/:([^\/]+)/g, '/([^/]+)').replace(/^\/?/, '^/?').replace(/\/?$/, '/?$')
34
+ result = Object.extended()
35
+
36
+ result[matchPath] =
37
+ capture: (path.match(/\/:[^\/]+/g) || []).map((str) -> str.substr(2))
38
+ action: response
39
+ result
40
+
41
+ respondRoute: (hash) ->
42
+ Joosy.Modules.Log.debug "Router> Answering '#{hash}'"
43
+
44
+ fullPath = hash.replace(/^#!?/, '')
45
+
46
+ @currentPath = fullPath
47
+ found = false
48
+
49
+ queryArray = fullPath.split('&')
50
+ path = queryArray.shift()
51
+ urlParams = @paramsFromQueryArray(queryArray)
52
+
53
+ for regex, route of @routes when @routes.hasOwnProperty(regex)
54
+ if vals = path.match(new RegExp(regex))
55
+ params = @paramsFromRouteMatch(vals, route).merge urlParams
56
+
57
+ if !Joosy.Module.hasAncestor(route.action, Joosy.Page)
58
+ route.action.call(this, params)
59
+ else
60
+ Joosy.Application.setCurrentPage(route.action, params)
61
+
62
+ found = true
63
+ break
64
+
65
+ if !found && @wildcardAction
66
+ @wildcardAction(path, urlParams)
67
+
68
+ paramsFromRouteMatch: (vals, route) ->
69
+ params = Object.extended()
70
+
71
+ vals.shift()
72
+ for param in route.capture
73
+ params[param] = vals.shift()
74
+
75
+ params
76
+
77
+ paramsFromQueryArray: (queryArray) ->
78
+ params = Object.extended()
79
+
80
+ if queryArray
81
+ $.each queryArray, () ->
82
+ if this != ''
83
+ pair = @split '='
84
+ params[pair[0]] = pair[1]
85
+
86
+ params
87
+
88
+ navigate: (to) ->
89
+ location.hash = '!' + to
@@ -0,0 +1,20 @@
1
+ #= require joosy/core/joosy
2
+
3
+ class Joosy.Templaters.RailsJST
4
+ constructor: (@applicationName) ->
5
+
6
+ buildView: (name) ->
7
+ template = JST[location = "#{@applicationName}/templates/#{name}"]
8
+
9
+ unless template
10
+ throw new Error "Template '#{name}' not found. Checked at: #{location}"
11
+
12
+ template
13
+
14
+ resolveTemplate: (section, template, entity) ->
15
+ return template.substr 1 if template.startsWith '/'
16
+
17
+ path = entity.constructor?.__namespace__?.map('underscore') || []
18
+ path.unshift(section)
19
+
20
+ "#{path.join('/')}/#{template}"