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,200 @@
1
+ #= require_tree ../templaters
2
+ #= require metamorph
3
+
4
+ #
5
+ # Core DOM rendering mechanics
6
+ #
7
+ # @mixin
8
+ # @todo Describe this scary thing o_O
9
+ #
10
+ Joosy.Modules.Renderer =
11
+
12
+ #
13
+ # Default behavior for non-set renderer (empty template?)
14
+ #
15
+ __renderer: ->
16
+ throw new Error "#{Joosy.Module.__className @constructor} does not have an attached template"
17
+
18
+ __helpers: null
19
+
20
+ #
21
+ # Defines class-level helpers: @view and @helpers
22
+ #
23
+ # View (@view): Sets the curent template by specifying its name or lambda
24
+ # Helpers (@helpers): Lists set of helpers' namespaces to include
25
+ #
26
+ included: ->
27
+ @view = (template, options={}) ->
28
+ if Object.isFunction template
29
+ @::__renderer = template
30
+ else
31
+ @::__renderer = (locals={}) ->
32
+ if options.dynamic
33
+ @renderDynamic template, locals
34
+ else
35
+ @render template, locals
36
+
37
+ @helpers = (helpers...) ->
38
+ @::__helpers ||= []
39
+ helpers.map (helper) =>
40
+ module = Joosy.Helpers[helper]
41
+ unless module
42
+ throw new Error "Cannot find helper module #{helper}"
43
+
44
+ @::__helpers.push module
45
+
46
+ @::__helpers = @::__helpers.unique()
47
+
48
+ __instantiateHelpers: ->
49
+ unless @__helpersInstance
50
+ @__helpersInstance = Object.extended Joosy.Helpers.Application
51
+
52
+ if @onRefresh
53
+ @__helpersInstance.onRefresh = (callback) =>
54
+ @onRefresh callback
55
+
56
+ if @__helpers
57
+ for helper in @__helpers
58
+ Joosy.Module.merge @__helpersInstance, helper
59
+
60
+ @__helpersInstance
61
+
62
+ # If we do not have __proto__ available...
63
+ __proxifyHelpers: (locals) ->
64
+ if locals.hasOwnProperty '__proto__'
65
+ locals.__proto__ = @__instantiateHelpers()
66
+ locals
67
+ else
68
+ unless @__helpersProxyInstance
69
+ @__helpersProxyInstance = (locals) ->
70
+ Joosy.Module.merge this, locals
71
+
72
+ @__helpersProxyInstance.prototype = @__instantiateHelpers()
73
+
74
+ new @__helpersProxyInstance locals
75
+
76
+ render: (template, locals={}, parentStackPointer=false) ->
77
+ @__render false, template, locals, parentStackPointer
78
+
79
+ renderDynamic: (template, locals={}, parentStackPointer=false) ->
80
+ @__render true, template, locals, parentStackPointer
81
+
82
+ __render: (dynamic, template, locals={}, parentStackPointer=false) ->
83
+ stack = @__renderingStackChildFor parentStackPointer
84
+
85
+ stack.template = template
86
+ stack.locals = locals
87
+
88
+ isResource = Joosy.Module.hasAncestor locals.constructor, Joosy.Resource.Generic
89
+ isCollection = Joosy.Module.hasAncestor locals.constructor, Joosy.Resource.Collection
90
+
91
+ if Object.isString template
92
+ if @__renderSection?
93
+ template = Joosy.Application.templater.resolveTemplate @__renderSection(), template, this
94
+
95
+ template = Joosy.Application.templater.buildView template
96
+ else if !Object.isFunction template
97
+ throw new Error "#{Joosy.Module.__className @}> template (maybe @view) does not look like a string or lambda"
98
+
99
+ if !Object.isObject(locals) && Object.extended().constructor != locals.constructor && !isResource && !isCollection
100
+ throw new Error "#{Joosy.Module.__className @}> locals (maybe @data?) not in: dumb hash, Resource, Collection"
101
+
102
+ renderers =
103
+ render: (template, locals={}) =>
104
+ @render template, locals, stack
105
+ renderDynamic: (template, locals={}) =>
106
+ @renderDynamic template, locals, stack
107
+
108
+ context = =>
109
+ data = {}
110
+
111
+ if isResource
112
+ Joosy.Module.merge data, stack.locals.data
113
+ else
114
+ Joosy.Module.merge data, stack.locals
115
+
116
+ Joosy.Module.merge data, @__instantiateHelpers(), false
117
+ Joosy.Module.merge data, renderers
118
+ data
119
+
120
+ result = -> template(context())
121
+
122
+ if dynamic
123
+ morph = Metamorph result()
124
+ update = =>
125
+ if morph.isRemoved()
126
+ for [object, callback] in morph.__bindings
127
+ object.unbind callback
128
+ else
129
+ for child in stack.children
130
+ @__removeMetamorphs child
131
+ stack.children = []
132
+ morph.html result()
133
+ @refreshElements?()
134
+
135
+ # This is here to break stack tree and save from
136
+ # repeating DOM handling
137
+ update = update.debounce 0
138
+
139
+ morph.__bindings = []
140
+
141
+ if isCollection
142
+ for resource in locals.data
143
+ binding = [resource, update]
144
+ resource.bind 'changed', update
145
+ stack.metamorphBindings.push binding
146
+ morph.__bindings.push binding
147
+ if isResource || isCollection
148
+ binding = [locals, update]
149
+ locals.bind 'changed', update
150
+ stack.metamorphBindings.push binding
151
+ morph.__bindings.push binding
152
+ else
153
+ for key, object of locals
154
+ if locals.hasOwnProperty key
155
+ if object?.bind? && object?.unbind?
156
+ binding = [object, update]
157
+ object.bind 'changed', update
158
+ stack.metamorphBindings.push binding
159
+ morph.__bindings.push binding
160
+
161
+ morph.outerHTML()
162
+ else
163
+ result()
164
+
165
+ __renderingStackElement: (parent=null) ->
166
+ metamorphBindings: []
167
+ locals: null
168
+ template: null
169
+ children: []
170
+ parent: parent
171
+
172
+ __renderingStackChildFor: (parentPointer) ->
173
+ if !@__renderingStack
174
+ @__renderingStack = []
175
+
176
+ if !parentPointer
177
+ element = @__renderingStackElement()
178
+ @__renderingStack.push element
179
+ element
180
+ else
181
+ element = @__renderingStackElement parentPointer
182
+ parentPointer.children.push element
183
+ element
184
+
185
+ __removeMetamorphs: (stackPointer=false) ->
186
+ remove = (stackPointer) =>
187
+ if stackPointer?.children
188
+ for child in stackPointer.children
189
+ @__removeMetamorphs child
190
+
191
+ if stackPointer?.metamorphBindings
192
+ for [object, callback] in stackPointer.metamorphBindings
193
+ object.unbind callback
194
+ stackPointer.metamorphBindings = []
195
+
196
+ unless stackPointer
197
+ @__renderingStack?.each (stackPointer) ->
198
+ remove stackPointer
199
+ else
200
+ remove stackPointer
@@ -0,0 +1,46 @@
1
+ #
2
+ # Comfortable and clever wrappers for timeouts management
3
+ #
4
+ # @mixin
5
+ #
6
+ Joosy.Modules.TimeManager =
7
+
8
+ #
9
+ # Registeres timeout for current object
10
+ #
11
+ # @param [Integer] timeout Miliseconds to wait
12
+ # @param [Function] action Action to run on timeout
13
+ #
14
+ setTimeout: (timeout, action) ->
15
+ @__timeouts ||= []
16
+
17
+ timer = window.setTimeout (=> action()), timeout
18
+ @__timeouts.push timer
19
+
20
+ timer
21
+
22
+ #
23
+ # Registeres interval for current object
24
+ #
25
+ # @param [Integer] delay Miliseconds between runs
26
+ # @param [Function] action Action to run
27
+ #
28
+ setInterval: (delay, action) ->
29
+ @__intervals ||= []
30
+
31
+ timer = window.setInterval (=> action()), delay
32
+ @__intervals.push timer
33
+
34
+ timer
35
+
36
+ #
37
+ # Drops all registered timeouts and intervals for this object
38
+ #
39
+ __clearTime: ->
40
+ if @__intervals
41
+ for entry in @__intervals
42
+ window.clearInterval entry
43
+
44
+ if @__timeouts
45
+ for entry in @__timeouts
46
+ window.clearTimeout entry
@@ -0,0 +1,87 @@
1
+ #
2
+ # Widgets management routines
3
+ #
4
+ # @mixin
5
+ #
6
+ Joosy.Modules.WidgetsManager =
7
+
8
+ #
9
+ # Registeres and runs widget inside specified container
10
+ #
11
+ # @param [DOM] container jQuery or direct dom node object
12
+ # @param [Joosy.Widget] widget Class or object of Joosy.Widget to register
13
+ #
14
+ registerWidget: (container, widget) ->
15
+ if Joosy.Module.hasAncestor widget, Joosy.Widget
16
+ widget = new widget()
17
+
18
+ if Object.isFunction(widget)
19
+ widget = widget()
20
+
21
+ @__activeWidgets ||= []
22
+ @__activeWidgets.push widget.__load(this, $(container))
23
+
24
+ widget
25
+
26
+ #
27
+ # Unregisteres and destroys widget
28
+ #
29
+ # @param [Joosy.Widget] widget Object of Joosy.Widget to unregister
30
+ #
31
+ unregisterWidget: (widget) ->
32
+ widget.__unload()
33
+
34
+ @__activeWidgets.splice @__activeWidgets.indexOf(widget), 1
35
+
36
+ #
37
+ # Gathers widgets definitions from current and super classes
38
+ #
39
+ __collectWidgets: ->
40
+ widgets = Object.extended @widgets || {}
41
+
42
+ klass = this
43
+ while klass = klass.constructor.__super__
44
+ Joosy.Module.merge widgets, klass.widgets, false
45
+
46
+ widgets
47
+
48
+ #
49
+ # Intialize all widgets for current object
50
+ #
51
+ __setupWidgets: ->
52
+ widgets = @__collectWidgets()
53
+ registered = Object.extended()
54
+
55
+ widgets.each (selector, widget) =>
56
+ if selector == '$container'
57
+ activeSelector = @container
58
+ else
59
+ if r = selector.match /\$([A-z_]+)/
60
+ selector = @elements[r[1]]
61
+
62
+ activeSelector = $(selector, @container)
63
+
64
+ registered[selector] = Object.extended()
65
+
66
+ activeSelector.each (index, elem) =>
67
+ if Joosy.Module.hasAncestor widget, Joosy.Widget
68
+ instance = new widget
69
+ else
70
+ instance = widget.call this, index
71
+
72
+ registered[selector][Joosy.Module.__className instance] ||= 0
73
+ registered[selector][Joosy.Module.__className instance] += 1
74
+
75
+ @registerWidget $(elem), instance
76
+
77
+ registered.each (selector, value) =>
78
+ value.each (widget, count) =>
79
+ Joosy.Modules.Log.debugAs @, "Widget #{widget} registered at '#{selector}'. Elements: #{count}"
80
+
81
+ #
82
+ # Unregister all widgets for current object
83
+ #
84
+ __unloadWidgets: ->
85
+ if @__activeWidgets
86
+ for widget in @__activeWidgets
87
+ widget.__unload()
@@ -0,0 +1,387 @@
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
+ #
12
+ # Base class for all of your Joosy Pages.
13
+ # @see http://guides.joosy.ws/guides/layouts-pages-and-routing.html
14
+ #
15
+ # @example Sample application page
16
+ # class @RumbaPage extends Joosy.Layout
17
+ # @view 'rumba'
18
+ #
19
+ # @include Joosy.Modules.Log
20
+ # @include Joosy.Modules.Events
21
+ # @include Joosy.Modules.Container
22
+ # @include Joosy.Modules.Renderer
23
+ # @include Joosy.Modules.TimeManager
24
+ # @include Joosy.Modules.WidgetsManager
25
+ # @include Joosy.Modules.Filters
26
+ #
27
+ class Joosy.Page extends Joosy.Module
28
+ @include Joosy.Modules.Log
29
+ @include Joosy.Modules.Events
30
+ @include Joosy.Modules.Container
31
+ @include Joosy.Modules.Renderer
32
+ @include Joosy.Modules.TimeManager
33
+ @include Joosy.Modules.WidgetsManager
34
+ @include Joosy.Modules.Filters
35
+
36
+ #
37
+ # Default layout is no layout.
38
+ #
39
+ layout: false
40
+
41
+ #
42
+ # Previous page.
43
+ #
44
+ previous: false
45
+
46
+ #
47
+ # Route params.
48
+ #
49
+ params: false
50
+
51
+ #
52
+ # Prefetched page data.
53
+ #
54
+ data: false
55
+
56
+ #
57
+ # Sets layout for current page
58
+ #
59
+ # @param [Class] layoutClass Layout to use
60
+ #
61
+ @layout: (layoutClass) ->
62
+ @::__layoutClass = layoutClass
63
+
64
+ #
65
+ # Sets the method which will controll the painting preparation proccess.
66
+ #
67
+ # This method will be called right ater previous page {Joosy.Page.erase} and in parallel with
68
+ # page data fetching so you can use it to initiate preloader.
69
+ #
70
+ # @note Given method will be called with `complete` function as parameter. As soon as your
71
+ # preparations are done you should call that function.
72
+ #
73
+ # @example Sample before painter
74
+ # @beforePaint (complete) ->
75
+ # if !@data # checks if parallel fetching finished
76
+ # $('preloader').slideDown -> complete()
77
+ #
78
+ #
79
+ @beforePaint: (callback) ->
80
+ @::__beforePaint = callback
81
+
82
+ #
83
+ # Sets the method which will controll the painting proccess.
84
+ #
85
+ # This method will be called after fetching, erasing and beforePaint is complete.
86
+ # It should be used to setup appearance effects of page.
87
+ #
88
+ # @note Given method will be called with `complete` function as parameter. As soon as your
89
+ # preparations are done you should call that function.
90
+ #
91
+ # @example Sample painter
92
+ # @paint (complete) ->
93
+ # @container.fadeIn -> complete()
94
+ #
95
+ @paint: (callback) ->
96
+ @::__paint = callback
97
+
98
+ #
99
+ # @todo Does anybody have idea why we could need this method?
100
+ # Looks like something should be removed from here and bootstrap proccess.
101
+ #
102
+ @afterPaint: (callback) ->
103
+ @::__afterPaint = callback
104
+
105
+ #
106
+ # Sets the method which will controll the erasing proccess.
107
+ #
108
+ # Use this method to setup hiding effect.
109
+ #
110
+ # @note Given method will be called with `complete` function as parameter. As soon as your
111
+ # preparations are done you should call that function.
112
+ #
113
+ # @note This method will be caled _before_ unload routines so in theory you can
114
+ # access page data from that. Think twice if you are doing it right though.
115
+ #
116
+ # @example Sample eraser
117
+ # @erase (complete) ->
118
+ # @container.fadeOut -> complete()
119
+ #
120
+ @erase: (callback) ->
121
+ @::__erase = callback
122
+
123
+ #
124
+ # Sets the method which will controll the data fetching proccess.
125
+ #
126
+ # @note Given method will be called with `complete` function as parameter. As soon as your
127
+ # preparations are done you should call that function.
128
+ #
129
+ # @example Basic usage
130
+ # @fetch (complete) ->
131
+ # $.get '/rumbas', (@data) => complete()
132
+ #
133
+ @fetch: (callback) ->
134
+ @::__fetch = callback
135
+
136
+ #
137
+ # Sets the several separate methods that will fetch data in parallel.
138
+ #
139
+ # @note This will work through {Joosy.Modules.Events.synchronize}
140
+ #
141
+ # @example Basic usage
142
+ # @fetchSynchronized (context) ->
143
+ # context.do (done) ->
144
+ # $.get '/rumbas', (data) =>
145
+ # @data.rumbas = data
146
+ # done()
147
+ #
148
+ # context.do (done) ->
149
+ # $.get '/kutuzkas', (data) =>
150
+ # @data.kutuzkas = data
151
+ # done()
152
+ #
153
+ @fetchSynchronized: (callback) ->
154
+ @::__fetch = (complete) ->
155
+ @synchronize (context) ->
156
+ context.after -> complete()
157
+ callback.call(this, context)
158
+
159
+ #
160
+ # Sets the position where page will be scrolled to after load.
161
+ #
162
+ # @note If you use animated scroll joosy will atempt to temporarily fix the
163
+ # height of your document while scrolling to prevent jump effect.
164
+ #
165
+ # @param [jQuery] element Element to scroll to
166
+ # @param [Hash] options
167
+ #
168
+ # @option options [Integer] speed Sets the animation duration (500 is default)
169
+ # @option options [Integer] margin Defines the margin from element position.
170
+ # Can be negative.
171
+ #
172
+ @scroll: (element, options={}) ->
173
+ @::__scrollElement = element
174
+ @::__scrollSpeed = options.speed || 500
175
+ @::__scrollMargin = options.margin || 0
176
+
177
+ #
178
+ # Scrolls page to stored positions
179
+ #
180
+ __performScrolling: ->
181
+ scroll = $(@__extractSelector @__scrollElement).offset()?.top + @__scrollMargin
182
+ Joosy.Modules.Log.debugAs @, "Scrolling to #{@__extractSelector @__scrollElement}"
183
+ $('html, body').animate {scrollTop: scroll}, @__scrollSpeed, =>
184
+ if @__scrollSpeed != 0
185
+ @__releaseHeight()
186
+
187
+ #
188
+ # Sets the page HTML title.
189
+ #
190
+ # @note Title will be reverted on unload.
191
+ #
192
+ # @param [String] title Title to set.
193
+ #
194
+ @title: (title, separator=' / ') ->
195
+ @afterLoad ->
196
+ titleStr = if Object.isFunction(title) then title.apply(this) else title
197
+ titleStr = titleStr.join(separator) if Object.isArray(titleStr)
198
+ @__previousTitle = document.title
199
+ document.title = titleStr
200
+
201
+ @afterUnload ->
202
+ document.title = @__previousTitle
203
+
204
+ #
205
+ # Constructor is very destructive (c), it calls bootstrap directly so use with caution.
206
+ #
207
+ # @params [Hash] params Route params
208
+ # @params [Joosy.Page] previous Previous page to unload
209
+ #
210
+ constructor: (@params, @previous) ->
211
+ Joosy.Application.loading = true
212
+
213
+ @__layoutClass ||= ApplicationLayout
214
+
215
+ if @__runBeforeLoads @params, @previous
216
+ if !@previous?.layout?.uuid? || @previous?.__layoutClass != @__layoutClass
217
+ @__bootstrapLayout()
218
+ else
219
+ @__bootstrap()
220
+
221
+ #
222
+ # @see Joosy.Router.navigate
223
+ #
224
+ navigate: (args...) ->
225
+ Joosy.Router.navigate(args...)
226
+
227
+ #
228
+ # This is required by {Joosy.Modules.Renderer}
229
+ # Sets the base template dir to app_name/templates/pages
230
+ #
231
+ __renderSection: ->
232
+ 'pages'
233
+
234
+ #
235
+ # Freezes the page height through $(html).
236
+ #
237
+ # Required to implement better {Joosy.Page.scroll} behavior.
238
+ #
239
+ __fixHeight: ->
240
+ $('html').css 'min-height', $(document).height()
241
+
242
+ #
243
+ # Undo {#__fixHeight}
244
+ #
245
+ __releaseHeight: ->
246
+ $('html').css 'min-height', ''
247
+
248
+ #
249
+ # Page bootstrap proccess
250
+ #
251
+ # * {Joosy.Modules.Container.refreshElements}
252
+ # * {Joosy.Modules.Container.__delegateEvents}
253
+ # * {Joosy.Modules.WidgetsManager.__setupWidgets}
254
+ # * Scrolling
255
+ #
256
+ __load: ->
257
+ @refreshElements()
258
+ @__delegateEvents()
259
+ @__setupWidgets()
260
+ @__runAfterLoads @params, @previous
261
+ @__performScrolling() if @__scrollElement
262
+ Joosy.Application.loading = false
263
+ Joosy.Router.trigger 'loaded', this
264
+ @trigger 'loaded'
265
+
266
+ Joosy.Modules.Log.debugAs @, "Page loaded"
267
+
268
+ #
269
+ # Page destruction proccess.
270
+ #
271
+ # * {Joosy.Modules.TimeManager.__clearTime}
272
+ # * {Joosy.Modules.WidgetsManager.__unloadWidgets}
273
+ # * {Joosy.Modules.Renderer.__removeMetamorphs}
274
+ #
275
+ __unload: ->
276
+ @__clearTime()
277
+ @__unloadWidgets()
278
+ @__removeMetamorphs()
279
+ @__runAfterUnloads @params, @previous
280
+ delete @previous
281
+
282
+ #
283
+ # Proxies callback through possible async wrapper.
284
+ #
285
+ # If wrapper is defined, it will be called with given callback as one of parameters.
286
+ # If wrapper is not defined callback will be called directly.
287
+ #
288
+ # @note Magic People Voodoo People
289
+ #
290
+ # @param [Object] entity Object possibly containing wrapper method
291
+ # @param [String] receiver String name of wrapper method inside entity
292
+ # @param [Hash] params Params to send to wrapper, callback will be
293
+ # attached as the last of them.
294
+ # @param [Function] callback Callback to run
295
+ #
296
+ __callSyncedThrough: (entity, receiver, params, callback) ->
297
+ if entity?[receiver]?
298
+ entity[receiver].apply entity, params.clone().add(callback)
299
+ else
300
+ callback()
301
+
302
+ #
303
+ # The single page (without layout reloading) bootstrap logic
304
+ #
305
+ # @example Hacky boot sequence description
306
+ # previous::erase \
307
+ # previous::unload \
308
+ # beforePaint \
309
+ # > paint
310
+ # fetch /
311
+ #
312
+ __bootstrap: ->
313
+ Joosy.Modules.Log.debugAs @, "Boostraping page"
314
+ @layout = @previous.layout
315
+
316
+ callbacksParams = [@layout.content()]
317
+
318
+ if @__scrollElement && @__scrollSpeed != 0
319
+ @__fixHeight()
320
+
321
+ @wait "stageClear dataReceived", =>
322
+ @__callSyncedThrough this, '__paint', callbacksParams, =>
323
+ # Page HTML
324
+ @swapContainer @layout.content(), @__renderer(@data || {})
325
+ @container = @layout.content()
326
+
327
+ # Loading
328
+ @__load()
329
+
330
+ @layout.content()
331
+
332
+ @__callSyncedThrough @previous, '__erase', callbacksParams, =>
333
+ @previous?.__unload()
334
+ @__callSyncedThrough this, '__beforePaint', callbacksParams, =>
335
+ @trigger 'stageClear'
336
+
337
+ @__callSyncedThrough this, '__fetch', [], =>
338
+ Joosy.Modules.Log.debugAs @, "Fetch complete"
339
+ @trigger 'dataReceived'
340
+
341
+ #
342
+ # The page+layout bootstrap logic
343
+ #
344
+ # @example Hacky boot sequence description
345
+ # previous::erase \
346
+ # previous::unload \
347
+ # beforePaint \
348
+ # > paint
349
+ # fetch /
350
+ #
351
+ __bootstrapLayout: ->
352
+ Joosy.Modules.Log.debugAs @, "Boostraping page with layout"
353
+ @layout = new @__layoutClass(@params)
354
+
355
+ callbacksParams = [Joosy.Application.content(), this]
356
+
357
+ if @__scrollElement && @__scrollSpeed != 0
358
+ @__fixHeight()
359
+
360
+ @wait "stageClear dataReceived", =>
361
+ @__callSyncedThrough @layout, '__paint', callbacksParams, =>
362
+ # Layout HTML
363
+ data = Joosy.Module.merge {}, @layout.data || {}
364
+ data = Joosy.Module.merge data, yield: => @layout.yield()
365
+
366
+ @swapContainer Joosy.Application.content(), @layout.__renderer data
367
+
368
+ # Page HTML
369
+ @swapContainer @layout.content(), @__renderer(@data || {})
370
+ @container = @layout.content()
371
+
372
+ # Loading
373
+ @layout.__load Joosy.Application.content()
374
+ @__load()
375
+
376
+ Joosy.Application.content()
377
+
378
+ @__callSyncedThrough @previous?.layout, '__erase', callbacksParams, =>
379
+ @previous?.layout?.__unload?()
380
+ @previous?.__unload()
381
+ @__callSyncedThrough @layout, '__beforePaint', callbacksParams, =>
382
+ @trigger 'stageClear'
383
+
384
+ @__callSyncedThrough @layout, '__fetch', [], =>
385
+ @__callSyncedThrough this, '__fetch', [], =>
386
+ Joosy.Modules.Log.debugAs @, "Fetch complete"
387
+ @trigger 'dataReceived'