joosy 1.2.0.beta.4 → 1.2.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.codoopts +1 -1
  3. data/Gruntfile.coffee +3 -3
  4. data/README.md +4 -0
  5. data/bower.json +1 -1
  6. data/build/joosy.js +2 -2
  7. data/build/joosy/form.js +1 -1
  8. data/build/joosy/resources.js +1 -1
  9. data/package.json +2 -2
  10. data/source/joosy/application.coffee +2 -2
  11. data/source/joosy/form.coffee +4 -4
  12. data/source/joosy/helpers/form.coffee +12 -3
  13. data/source/joosy/helpers/index.coffee +0 -1
  14. data/source/joosy/helpers/view.coffee +16 -3
  15. data/source/joosy/layout.coffee +0 -4
  16. data/source/joosy/module.coffee +16 -1
  17. data/source/joosy/modules/dom.coffee +106 -101
  18. data/source/joosy/modules/events.coffee +44 -10
  19. data/source/joosy/modules/filters.coffee +64 -60
  20. data/source/joosy/modules/page.coffee +3 -0
  21. data/source/joosy/modules/page/scrolling.coffee +46 -29
  22. data/source/joosy/modules/page/title.coffee +14 -0
  23. data/source/joosy/modules/renderer.coffee +219 -190
  24. data/source/joosy/modules/resources.coffee +3 -0
  25. data/source/joosy/modules/resources/cacher.coffee +81 -10
  26. data/source/joosy/modules/resources/function.coffee +26 -29
  27. data/source/joosy/modules/resources/identity_map.coffee +64 -42
  28. data/source/joosy/modules/resources/model.coffee +127 -73
  29. data/source/joosy/modules/time_manager.coffee +2 -0
  30. data/source/joosy/page.coffee +3 -6
  31. data/source/joosy/resources/array.coffee +87 -2
  32. data/source/joosy/resources/hash.coffee +53 -1
  33. data/source/joosy/resources/rest.coffee +59 -3
  34. data/source/joosy/resources/scalar.coffee +47 -1
  35. data/source/joosy/router.coffee +63 -21
  36. data/source/joosy/templaters/jst.coffee +3 -0
  37. data/source/joosy/widget.coffee +17 -11
  38. data/spec/joosy/core/helpers/view_spec.coffee +14 -0
  39. data/spec/joosy/core/modules/dom_spec.coffee +1 -1
  40. data/spec/joosy/core/modules/filters_spec.coffee +2 -2
  41. data/spec/joosy/core/modules/module_spec.coffee +1 -1
  42. data/spec/joosy/core/modules/renderer_spec.coffee +19 -1
  43. data/spec/joosy/core/router_spec.coffee +80 -45
  44. data/spec/joosy/core/widget_spec.coffee +9 -0
  45. data/spec/joosy/resources/modules/cacher_spec.coffee +3 -3
  46. data/spec/joosy/resources/modules/function_spec.coffee +2 -2
  47. data/spec/joosy/resources/modules/identity_map_spec.coffee +2 -2
  48. data/spec/joosy/resources/modules/model_spec.coffee +1 -1
  49. metadata +2 -5
  50. data/source/joosy/helpers/routes.coffee +0 -17
  51. data/source/joosy/modules/widgets_manager.coffee +0 -90
  52. data/spec/joosy/core/helpers/routes_spec.coffee +0 -15
@@ -1,13 +1,14 @@
1
1
  #= require joosy/joosy
2
2
 
3
- # @private
3
+ # @nodoc
4
4
  class SynchronizationContext
5
5
  constructor: -> @actions = []
6
6
  do: (action) -> @actions.push action
7
7
  after: (@after) ->
8
8
 
9
9
  #
10
- # @private
10
+ # @nodoc
11
+ #
11
12
  # Events namespace
12
13
  #
13
14
  # Creates unified collection of bindings to a particular instance
@@ -46,12 +47,18 @@ Joosy.Modules.Events =
46
47
  # Creates events namespace
47
48
  #
48
49
  # @example
49
- # namespace = @entity.eventsNamespace, ->
50
+ # namespace = @entity.eventsNamespace ->
50
51
  # @bind 'action1', ->
51
52
  # @bind 'action2', ->
52
53
  #
53
54
  # namespace.unbind()
54
55
  #
56
+ # @example
57
+ # namespace = @entity.eventsNamespace()
58
+ # namespace.bind 'action1', ->
59
+ # namespace.bind 'action2', ->
60
+ # namespace.unbind()
61
+ #
55
62
  eventsNamespace: (actions) ->
56
63
  namespace = new Namespace @
57
64
  actions?.call?(namespace)
@@ -60,10 +67,20 @@ Joosy.Modules.Events =
60
67
  #
61
68
  # Waits for the list of given events to happen at least once. Then runs callback.
62
69
  #
63
- # @param [String|Array] events List of events to wait for separated by space
70
+ # @overload ~wait(events, callback)
71
+ # Uses internal unique ID as the name of the binding
72
+ #
73
+ # @overload ~wait(name, events, callback)
74
+ # Allows to pass custom name for the binding
75
+ #
76
+ # @param [String] name Custom name for the binding
77
+ # @param [String] events List of events to wait for separated by space
78
+ # @param [Array] events List of events to wait in the form of Array
64
79
  # @param [Function] callback Action to run when all events were triggered at least once
65
80
  # @param [Hash] options Options
66
81
  #
82
+ # @return [String] An ID (or custom name) of binding
83
+ #
67
84
  wait: (name, events, callback) ->
68
85
  @__oneShotEvents = {} unless @hasOwnProperty('__oneShotEvents')
69
86
 
@@ -85,7 +102,7 @@ Joosy.Modules.Events =
85
102
  #
86
103
  # Removes waiter action
87
104
  #
88
- # @param [Function] target Name of waiter to unbind
105
+ # @param [String] target Name of {Joosy.Modules.Events~wait} binding
89
106
  #
90
107
  unwait: (target) ->
91
108
  delete @__oneShotEvents[target] if @hasOwnProperty '__oneShotEvents'
@@ -93,9 +110,18 @@ Joosy.Modules.Events =
93
110
  #
94
111
  # Binds action to run each time any of given event was triggered
95
112
  #
96
- # @param [String|Array] events List of events separated by space
113
+ # @overload ~bind(events, callback)
114
+ # Uses internal unique ID as the name of the binding
115
+ #
116
+ # @overload ~bind(name, events, callback)
117
+ # Allows to pass custom name for the binding
118
+ #
119
+ # @param [String] name Custom name for the binding
120
+ # @param [String] events List of events to wait for separated by space
121
+ # @param [Array] events List of events to wait in the form of Array
97
122
  # @param [Function] callback Action to run on trigger
98
- # @param [Hash] options Options
123
+ #
124
+ # @return [String] An ID (or custom name) of binding
99
125
  #
100
126
  bind: (name, events, callback) ->
101
127
  @__boundEvents = {} unless @hasOwnProperty '__boundEvents'
@@ -118,15 +144,16 @@ Joosy.Modules.Events =
118
144
  #
119
145
  # Unbinds action from runing on trigger
120
146
  #
121
- # @param [Function] target Name of bind to unbind
147
+ # @param [String] target Name of {Joosy.Modules.Events~bind} binding
122
148
  #
123
149
  unbind: (target) ->
124
150
  delete @__boundEvents[target] if @hasOwnProperty '__boundEvents'
125
151
 
126
152
  #
127
- # Triggers event for {bind} and {wait}
153
+ # Triggers event for {Joosy.Modules.Events~bind} and {Joosy.Modules.Events~wait}
128
154
  #
129
- # @param [String] Name of event to trigger
155
+ # @param [String] event Name of event to trigger
156
+ # @param [Mixed] data Data to pass to event
130
157
  #
131
158
  trigger: (event, data...) ->
132
159
  Joosy.Modules.Log.debugAs @, "Event #{event} triggered"
@@ -187,6 +214,13 @@ Joosy.Modules.Events =
187
214
  if ++counter >= context.actions.length
188
215
  context.after.call(@)
189
216
 
217
+ #
218
+ # Turns the list of events given in form of stiring into the array
219
+ #
220
+ # @param [String] events
221
+ # @return [Array]
222
+ # @private
223
+ #
190
224
  __splitEvents: (events) ->
191
225
  if typeof(events) == 'string'
192
226
  if events.length == 0
@@ -4,82 +4,86 @@
4
4
  # Filters registration routines
5
5
  #
6
6
  # @mixin
7
+ # @private
7
8
  #
8
9
  Joosy.Modules.Filters =
9
10
 
10
11
  #
11
- # Defines static registration routines
12
+ # Internal helper registering filters accessors
12
13
  #
13
- # @example Set of methods
14
- # class Test
15
- # @beforeLoad -> # supposed to run before load and control loading queue
16
- # @afterLoad -> # supposed to run after load to finalize loading
17
- # @afterUnload -> # supposed to run after unload to collect garbage
14
+ __registerFilterCollector: (filter) ->
15
+ @[filter] = (callback) ->
16
+ unless @::hasOwnProperty "__#{filter}s"
17
+ @::["__#{filter}s"] = [].concat @.__super__["__#{filter}s"] || []
18
+ @::["__#{filter}s"].push callback
19
+
20
+ filter.charAt(0).toUpperCase() + filter.slice(1)
21
+
18
22
  #
19
- # # private
23
+ # Registers a set of plain (synchronous) filters
20
24
  #
21
- # @__confirmBeforeLoads() # Runs filters registered as beforeLoad
22
- # @__runAfterLoads() # Runs filters registered as afterLoad
23
- # @__runAfterUnloads() # Runs filters registered as afterUnload
25
+ # @example
26
+ # class Test
27
+ # @extend Joosy.Modules.Filters
28
+ # @registerPlainFilters 'beforeLoad', 'afterLoad'
24
29
  #
25
- included: ->
26
- @__registerFilterCollector = (filter) ->
27
- @[filter] = (callback) ->
28
- unless @::hasOwnProperty "__#{filter}s"
29
- @::["__#{filter}s"] = [].concat @.__super__["__#{filter}s"] || []
30
- @::["__#{filter}s"].push callback
31
-
32
- filter.charAt(0).toUpperCase() + filter.slice(1)
33
-
34
- @registerPlainFilters = (filters...) ->
35
- for filter in filters
36
- do (filter) =>
37
- camelized = @__registerFilterCollector filter
38
-
39
- @::["__run#{camelized}s"] = (params...) ->
40
- return unless @["__#{filter}s"]
30
+ registerPlainFilters: (filters...) ->
31
+ for filter in filters
32
+ do (filter) =>
33
+ camelized = @__registerFilterCollector filter
41
34
 
42
- for callback in @["__#{filter}s"]
43
- callback = @[callback] unless typeof(callback) == 'function'
44
- callback.apply(@, params)
35
+ @::["__run#{camelized}s"] = (params...) ->
36
+ return unless @["__#{filter}s"]
45
37
 
46
- @::["__confirm#{camelized}s"] = (params...) ->
47
- return true unless @["__#{filter}s"]
38
+ for callback in @["__#{filter}s"]
39
+ callback = @[callback] unless typeof(callback) == 'function'
40
+ callback.apply(@, params)
48
41
 
49
- @["__#{filter}s"].reduce (flag, callback) =>
50
- callback = @[callback] unless typeof(callback) == 'function'
51
- flag && callback.apply(@, params) != false
52
- , true
42
+ @::["__confirm#{camelized}s"] = (params...) ->
43
+ return true unless @["__#{filter}s"]
53
44
 
54
- @::["__apply#{camelized}s"] = (data, params...) ->
55
- return data unless @["__#{filter}s"]
45
+ @["__#{filter}s"].reduce (flag, callback) =>
46
+ callback = @[callback] unless typeof(callback) == 'function'
47
+ flag && callback.apply(@, params) != false
48
+ , true
56
49
 
57
- for callback in @["__#{filter}s"]
58
- callback = @[callback] unless typeof(callback) == 'function'
59
- data = callback.apply(@, [data].concat params)
50
+ @::["__apply#{camelized}s"] = (data, params...) ->
51
+ return data unless @["__#{filter}s"]
60
52
 
61
- data
53
+ for callback in @["__#{filter}s"]
54
+ callback = @[callback] unless typeof(callback) == 'function'
55
+ data = callback.apply(@, [data].concat params)
62
56
 
63
- @registerSequencedFilters = (filters...) ->
64
- for filter in filters
65
- do (filter) =>
66
- camelized = @__registerFilterCollector filter
57
+ data
67
58
 
68
- @::["__run#{camelized}s"] = (params, callback) ->
69
- return callback() unless @["__#{filter}s"]
70
-
71
- runners = @["__#{filter}s"]
72
- filterer = @
73
-
74
- if runners.length == 1
75
- return runners[0].apply @, params.concat(callback)
76
-
77
- Joosy.synchronize (context) ->
78
- for runner in runners
79
- do (runner) ->
80
- context.do (done) ->
81
- runner.apply filterer, params.concat(done)
82
- context.after callback
59
+ #
60
+ # Registers a set of sequenced (asynchronous) filters
61
+ #
62
+ # @example
63
+ # class Test
64
+ # @extend Joosy.Modules.Filters
65
+ # @registerSequencedFilters 'fetch', 'paint'
66
+ #
67
+ registerSequencedFilters: (filters...) ->
68
+ for filter in filters
69
+ do (filter) =>
70
+ camelized = @__registerFilterCollector filter
71
+
72
+ @::["__run#{camelized}s"] = (params, callback) ->
73
+ return callback() unless @["__#{filter}s"]
74
+
75
+ runners = @["__#{filter}s"]
76
+ filterer = @
77
+
78
+ if runners.length == 1
79
+ return runners[0].apply @, params.concat(callback)
80
+
81
+ Joosy.synchronize (context) ->
82
+ for runner in runners
83
+ do (runner) ->
84
+ context.do (done) ->
85
+ runner.apply filterer, params.concat(done)
86
+ context.after callback
83
87
 
84
88
  # AMD wrapper
85
89
  if define?.amd?
@@ -1 +1,4 @@
1
+ #
2
+ # The namespace for page modules
3
+ #
1
4
  Joosy.Modules.Page = {}
@@ -1,9 +1,22 @@
1
1
  #= require ../page
2
2
 
3
+ #
4
+ # The auto-scrolling filters for Page (or possibly widgets)
5
+ #
6
+ # @see Joosy.Page
3
7
  # @mixin
8
+ #
4
9
  Joosy.Modules.Page.Scrolling =
5
10
 
6
11
  included: ->
12
+ @afterLoad ->
13
+ @__performScrolling() if @__scrollElement
14
+
15
+ @paint (complete) ->
16
+ @__fixHeight() if @__scrollElement && @__scrollSpeed != 0
17
+ complete()
18
+
19
+ ClassMethods:
7
20
  #
8
21
  # Sets the position where page will be scrolled to after load.
9
22
  #
@@ -17,38 +30,42 @@ Joosy.Modules.Page.Scrolling =
17
30
  # @option options [Integer] margin Defines the margin from element position.
18
31
  # Can be negative.
19
32
  #
20
- @scroll = (element, options={}) ->
33
+ # @example
34
+ # class TestPage extends Joosy.Page
35
+ # @scroll '#header', speed: 300, margin: -100
36
+ #
37
+ scroll: (element, options={}) ->
21
38
  @::__scrollElement = element
22
39
  @::__scrollSpeed = options.speed || 500
23
40
  @::__scrollMargin = options.margin || 0
24
41
 
25
- @paint (complete) ->
26
- @__fixHeight() if @__scrollElement && @__scrollSpeed != 0
27
- complete()
28
-
29
- @afterLoad ->
30
- @__performScrolling() if @__scrollElement
31
-
32
- #
33
- # Scrolls page to stored positions
34
- #
35
- __performScrolling: ->
36
- scroll = $(@__extractSelector @__scrollElement).offset()?.top + @__scrollMargin
37
- Joosy.Modules.Log.debugAs @, "Scrolling to #{@__extractSelector @__scrollElement}"
38
- $('html, body').animate {scrollTop: scroll}, @__scrollSpeed, =>
39
- if @__scrollSpeed != 0
40
- @__releaseHeight()
42
+ InstanceMethods:
43
+ #
44
+ # Scrolls page to stored positions
45
+ #
46
+ # @private
47
+ #
48
+ __performScrolling: ->
49
+ scroll = $(@__extractSelector @__scrollElement).offset()?.top + @__scrollMargin
50
+ Joosy.Modules.Log.debugAs @, "Scrolling to #{@__extractSelector @__scrollElement}"
51
+ $('html, body').animate {scrollTop: scroll}, @__scrollSpeed, =>
52
+ if @__scrollSpeed != 0
53
+ @__releaseHeight()
41
54
 
42
- #
43
- # Freezes the page height through $(html).
44
- #
45
- # Required to implement better {Joosy.Page.scroll} behavior.
46
- #
47
- __fixHeight: ->
48
- $('html').css 'min-height', $(document).height()
55
+ #
56
+ # Freezes the page height through $(html).
57
+ #
58
+ # Required to implement better {Joosy.Modules.Page.Scrolling.scroll} behavior.
59
+ #
60
+ # @private
61
+ #
62
+ __fixHeight: ->
63
+ $('html').css 'min-height', $(document).height()
49
64
 
50
- #
51
- # Undo {#__fixHeight}
52
- #
53
- __releaseHeight: ->
54
- $('html').css 'min-height', ''
65
+ #
66
+ # Undoes {Joosy.Modules.Page.Scrolling#__fixHeight}
67
+ #
68
+ # @private
69
+ #
70
+ __releaseHeight: ->
71
+ $('html').css 'min-height', ''
@@ -1,6 +1,11 @@
1
1
  #= require ../page
2
2
 
3
+ #
4
+ # Title management for Page (or possibly other widgets)
5
+ #
6
+ # @see Joosy.Page
3
7
  # @mixin
8
+ #
4
9
  Joosy.Modules.Page.Title =
5
10
 
6
11
  #
@@ -9,6 +14,15 @@ Joosy.Modules.Page.Title =
9
14
  # @note Title will be reverted on unload.
10
15
  #
11
16
  # @param [String] title Title to set.
17
+ # @param [String] separator The string to use to `.join` when title is an array
18
+ #
19
+ # @example
20
+ # class TestPage extends Joosy.Page
21
+ # @title 'Test title'
22
+ #
23
+ # @example
24
+ # class TestPage extends Joosy.Page
25
+ # @title -> I18n.t('titles.test')
12
26
  #
13
27
  title: (title, separator=' / ') ->
14
28
  @afterLoad ->
@@ -7,14 +7,16 @@
7
7
  # @mixin
8
8
  #
9
9
  Joosy.Modules.Renderer =
10
- #
11
- # Defines class-level helpers: @view and @helpers
12
- #
13
- # View (@view): Sets the curent template by specifying its name or lambda
14
- # Helpers (@helpers): Lists set of helpers' namespaces to include
15
- #
16
- included: ->
17
- @view = (template, options={}) ->
10
+
11
+ ClassMethods:
12
+ #
13
+ # Sets the curent template by specifying its name or lambda
14
+ #
15
+ # @param [String] template
16
+ # @param [Hash] options
17
+ # @option options [Boolean] dynamic Marks if the whole view should be rendered as a Dynamic one
18
+ #
19
+ view: (template, options={}) ->
18
20
  @::__view = template
19
21
  @::__renderDefault = (locals={}) ->
20
22
  if options.dynamic
@@ -22,195 +24,222 @@ Joosy.Modules.Renderer =
22
24
  else
23
25
  @render template, locals
24
26
 
25
- @helper = (helpers...) ->
27
+ #
28
+ # Lists set of helpers' namespaces to include
29
+ #
30
+ helper: (helpers...) ->
26
31
  unless @::hasOwnProperty "__helpers"
27
32
  @::__helpers = @.__super__.__helpers?.slice() || []
28
33
 
29
34
  @::__helpers = @::__helpers.concat(helpers).filter (value, i, array) ->
30
35
  array.indexOf(value) == i
31
36
 
32
- #
33
- # Renders given template with given locals
34
- #
35
- # @param [String] template Name of the template to render using templater
36
- # @param [Function] template `(locals) ->` lambda to use as template
37
- # @param [Object] locals Locals to assign
38
- # @param [Object] parentStackPointer Internal rendering stack pointer
39
- #
40
- render: (template, locals={}, parentStackPointer=false) ->
41
- @__render false, template, locals, parentStackPointer
42
-
43
- #
44
- # Dynamically renders given template with given locals
45
- #
46
- # Whenever any of assigned locals triggers `changed` event, DOM will automatically be refreshed
47
- #
48
- # @param [String] template Name of the template to render using templater
49
- # @param [Function] template `(locals) ->` lambda to use as template
50
- # @param [Object] locals Locals to assign
51
- # @param [Object] parentStackPointer Internal rendering stack pointer
52
- #
53
- renderDynamic: (template, locals={}, parentStackPointer=false) ->
54
- @__render true, template, locals, parentStackPointer
55
-
56
- #
57
- # Converts all possible `@helper` arguments to the objects available for merge
58
- #
59
- __assignHelpers: ->
60
- return unless @__helpers?
61
-
62
- unless @hasOwnProperty "__helpers"
63
- @__helpers = @__helpers.slice()
64
-
65
- for helper, i in @__helpers
66
- do (helper, i) =>
67
- unless helper.constructor == Object
68
- unless @[helper]?
69
- throw new Error "Cannot find method '#{helper}' to use as helper"
70
-
71
- @__helpers[i] = {}
72
- @__helpers[i][helper] = => @[helper] arguments...
73
-
74
- #
75
- # Collects and merges all requested helpers including global scope to one cached object
76
- #
77
- __instantiateHelpers: ->
78
- unless @__helpersInstance
79
- @__assignHelpers()
80
-
81
- @__helpersInstance = {}
82
- @__helpersInstance.__renderer = @
83
-
84
- Joosy.Module.merge @__helpersInstance, Joosy.Helpers.Application
85
- Joosy.Module.merge @__helpersInstance, Joosy.Helpers.Routes if Joosy.Helpers.Routes?
86
-
87
- if @__helpers
88
- for helper in @__helpers
89
- Joosy.Module.merge @__helpersInstance, helper
90
-
91
- @__helpersInstance
92
-
93
- #
94
- # Defines local `@render*` methods with proper stack pointer set
95
- #
96
- # @param [Object] parentStackPointer Internal rendering stack pointer
97
- #
98
- __instantiateRenderers: (parentStackPointer) ->
99
- render: (template, locals={}) =>
100
- @render template, locals, parentStackPointer
101
- renderDynamic: (template, locals={}) =>
102
- @renderDynamic template, locals, parentStackPointer
103
- renderInline: (locals={}, partial) =>
104
- template = (params) -> partial.apply(params)
105
- @renderDynamic template, locals, parentStackPointer
106
-
107
- #
108
- # Actual rendering implementation
109
- #
110
- __render: (dynamic, template, locals={}, parentStackPointer=false) ->
111
- stack = @__renderingStackChildFor parentStackPointer
112
-
113
- stack.template = template
114
- stack.locals = locals
115
-
116
- if typeof(template) == 'string'
117
- if @__renderSection?
118
- template = Joosy.templater().resolveTemplate @__renderSection(), template, this
119
- template = Joosy.templater().buildView template
120
- else if typeof(template) != 'function'
121
- throw new Error "#{Joosy.Module.__className @}> template (maybe @view) does not look like a string or lambda"
122
-
123
- if locals.constructor != Object
124
- throw new Error "#{Joosy.Module.__className @}> locals (maybe @data?) is not a hash"
125
-
126
- context = =>
127
- data = {}
128
-
129
- Joosy.Module.merge data, stack.locals
130
- Joosy.Module.merge data, @__instantiateHelpers(), false
131
- Joosy.Module.merge data, @__instantiateRenderers(stack)
132
- data
133
-
134
- result = ->
135
- template(context())
136
-
137
- if dynamic
138
- morph = Metamorph result()
139
- update = =>
140
- if morph.isRemoved()
141
- for [object, binding] in morph.__bindings
142
- object.unbind binding
143
- else
144
- for child in stack.children
37
+ InstanceMethods:
38
+ #
39
+ # Renders given template with given locals
40
+ #
41
+ # @param [String] template Name of the template to render using templater
42
+ # @param [Function] template `(locals) ->` lambda to use as template
43
+ # @param [Object] locals Locals to assign
44
+ # @param [Object] parentStackPointer Internal rendering stack pointer
45
+ #
46
+ render: (template, locals={}, parentStackPointer=false) ->
47
+ @__render false, template, locals, parentStackPointer
48
+
49
+ #
50
+ # Dynamically renders given template with given locals
51
+ #
52
+ # Whenever any of assigned locals triggers `changed` event, DOM will automatically be refreshed
53
+ #
54
+ # @param [String] template Name of the template to render using templater
55
+ # @param [Function] template `(locals) ->` lambda to use as template
56
+ # @param [Object] locals Locals to assign
57
+ # @param [Object] parentStackPointer Internal rendering stack pointer
58
+ #
59
+ renderDynamic: (template, locals={}, callback, parentStackPointer=false) ->
60
+ @__render (callback || true), template, locals, parentStackPointer
61
+
62
+ #
63
+ # Converts all possible `@helper` arguments to the objects available for merge
64
+ #
65
+ # @private
66
+ #
67
+ __assignHelpers: ->
68
+ return unless @__helpers?
69
+
70
+ unless @hasOwnProperty "__helpers"
71
+ @__helpers = @__helpers.slice()
72
+
73
+ for helper, i in @__helpers
74
+ do (helper, i) =>
75
+ unless helper.constructor == Object
76
+ unless @[helper]?
77
+ throw new Error "Cannot find method '#{helper}' to use as helper"
78
+
79
+ @__helpers[i] = {}
80
+ @__helpers[i][helper] = => @[helper] arguments...
81
+
82
+ #
83
+ # Collects and merges all requested helpers including global scope to one cached object
84
+ #
85
+ # @private
86
+ #
87
+ __instantiateHelpers: ->
88
+ unless @__helpersInstance
89
+ @__assignHelpers()
90
+
91
+ @__helpersInstance = {}
92
+ @__helpersInstance.__renderer = @
93
+
94
+ Joosy.Module.merge @__helpersInstance, Joosy.Helpers.Application
95
+ Joosy.Module.merge @__helpersInstance, Joosy.Helpers.Routes if Joosy.Helpers.Routes?
96
+
97
+ if @__helpers
98
+ for helper in @__helpers
99
+ Joosy.Module.merge @__helpersInstance, helper
100
+
101
+ @__helpersInstance
102
+
103
+ #
104
+ # Defines local `@render*` methods with proper stack pointer set
105
+ #
106
+ # @param [Object] parentStackPointer Internal rendering stack pointer
107
+ # @private
108
+ #
109
+ __instantiateRenderers: (parentStackPointer) ->
110
+
111
+ render: (template, locals={}) =>
112
+ @render template, locals, parentStackPointer
113
+
114
+ renderDynamic: (template, locals={}, callback) =>
115
+ @renderDynamic template, locals, callback, parentStackPointer
116
+
117
+ renderInline: (locals={}, callback, partial) =>
118
+ if arguments.length < 3
119
+ partial = callback
120
+ callback = undefined
121
+
122
+ template = (params) ->
123
+ partial.apply(params)
124
+
125
+ @renderDynamic template, locals, callback, parentStackPointer
126
+
127
+ #
128
+ # Actual rendering implementation
129
+ #
130
+ # @private
131
+ #
132
+ __render: (dynamic, template, locals={}, parentStackPointer=false) ->
133
+ stack = @__renderingStackChildFor parentStackPointer
134
+
135
+ stack.template = template
136
+ stack.locals = locals
137
+
138
+ if typeof(template) == 'string'
139
+ if @__renderSection?
140
+ template = Joosy.templater().resolveTemplate @__renderSection(), template, this
141
+ template = Joosy.templater().buildView template
142
+ else if typeof(template) != 'function'
143
+ throw new Error "#{Joosy.Module.__className @}> template (maybe @view) does not look like a string or lambda"
144
+
145
+ if locals.constructor != Object
146
+ throw new Error "#{Joosy.Module.__className @}> locals (maybe @data?) is not a hash"
147
+
148
+ context = =>
149
+ data = {}
150
+
151
+ Joosy.Module.merge data, stack.locals
152
+ Joosy.Module.merge data, @__instantiateHelpers(), false
153
+ Joosy.Module.merge data, @__instantiateRenderers(stack)
154
+ data
155
+
156
+ result = ->
157
+ template(context())
158
+
159
+ if dynamic
160
+ morph = Metamorph result()
161
+ update = =>
162
+ if morph.isRemoved()
163
+ for [object, binding] in morph.__bindings
164
+ object.unbind binding
165
+ else
166
+ for child in stack.children
167
+ @__removeMetamorphs child
168
+ stack.children = []
169
+ morph.html result()
170
+ dynamic() if dynamic instanceof Function
171
+
172
+ # This is here to break stack tree and save from
173
+ # repeating DOM modification
174
+ timeout = null
175
+ debouncedUpdate = ->
176
+ clearTimeout timeout
177
+ timeout = setTimeout update, 0
178
+
179
+ for key, object of locals
180
+ if locals.hasOwnProperty key
181
+ if object?.bind? && object?.unbind?
182
+ binding = [object, object.bind('changed', debouncedUpdate)]
183
+ stack.metamorphBindings.push binding
184
+
185
+ morph.__bindings = stack.metamorphBindings
186
+
187
+ morph.outerHTML()
188
+ else
189
+ result()
190
+
191
+ #
192
+ # Template for the rendering stack node
193
+ #
194
+ # @private
195
+ #
196
+ __renderingStackElement: (parent=null) ->
197
+ metamorphBindings: []
198
+ locals: null
199
+ template: null
200
+ children: []
201
+ parent: parent
202
+
203
+ #
204
+ # Creates new rendering stack node using given pointer as the parent
205
+ #
206
+ # @private
207
+ #
208
+ __renderingStackChildFor: (parentPointer) ->
209
+ if !@__renderingStack
210
+ @__renderingStack = []
211
+
212
+ if !parentPointer
213
+ element = @__renderingStackElement()
214
+ @__renderingStack.push element
215
+ element
216
+ else
217
+ element = @__renderingStackElement parentPointer
218
+ parentPointer.children.push element
219
+ element
220
+
221
+ #
222
+ # Disables and unbinds all dynamic bindings for the whole rendering stack
223
+ #
224
+ # @private
225
+ #
226
+ __removeMetamorphs: (stackPointer=false) ->
227
+ remove = (stackPointer) =>
228
+ if stackPointer?.children
229
+ for child in stackPointer.children
145
230
  @__removeMetamorphs child
146
- stack.children = []
147
- morph.html result()
148
-
149
- # This is here to break stack tree and save from
150
- # repeating DOM modification
151
- timeout = null
152
- debouncedUpdate = ->
153
- clearTimeout timeout
154
- timeout = setTimeout update, 0
155
-
156
- for key, object of locals
157
- if locals.hasOwnProperty key
158
- if object?.bind? && object?.unbind?
159
- binding = [object, object.bind('changed', debouncedUpdate)]
160
- stack.metamorphBindings.push binding
161
-
162
- morph.__bindings = stack.metamorphBindings
163
-
164
- morph.outerHTML()
165
- else
166
- result()
167
-
168
- #
169
- # Template for the rendering stack node
170
- #
171
- __renderingStackElement: (parent=null) ->
172
- metamorphBindings: []
173
- locals: null
174
- template: null
175
- children: []
176
- parent: parent
177
-
178
- #
179
- # Creates new rendering stack node using given pointer as the parent
180
- #
181
- __renderingStackChildFor: (parentPointer) ->
182
- if !@__renderingStack
183
- @__renderingStack = []
184
-
185
- if !parentPointer
186
- element = @__renderingStackElement()
187
- @__renderingStack.push element
188
- element
189
- else
190
- element = @__renderingStackElement parentPointer
191
- parentPointer.children.push element
192
- element
193
-
194
- #
195
- # Disables and unbinds all dynamic bindings for the whole rendering stack
196
- #
197
- __removeMetamorphs: (stackPointer=false) ->
198
- remove = (stackPointer) =>
199
- if stackPointer?.children
200
- for child in stackPointer.children
201
- @__removeMetamorphs child
202
-
203
- if stackPointer?.metamorphBindings
204
- for [object, callback] in stackPointer.metamorphBindings
205
- object.unbind callback
206
- stackPointer.metamorphBindings = []
207
-
208
- unless stackPointer
209
- if @__renderingStack?
210
- remove stackPointer for stackPointer in @__renderingStack
211
-
212
- else
213
- remove stackPointer
231
+
232
+ if stackPointer?.metamorphBindings
233
+ for [object, callback] in stackPointer.metamorphBindings
234
+ object.unbind callback
235
+ stackPointer.metamorphBindings = []
236
+
237
+ unless stackPointer
238
+ if @__renderingStack?
239
+ remove stackPointer for stackPointer in @__renderingStack
240
+
241
+ else
242
+ remove stackPointer
214
243
 
215
244
  # AMD wrapper
216
245
  if define?.amd?