joosy 0.1.0.RC1

Sign up to get free protection for your applications and to get access to all the features.
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}"