joosy 1.2.0.alpha.38 → 1.2.0.alpha.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gruntfile.coffee +15 -7
  3. data/bower.json +4 -3
  4. data/lib/extensions/resources.js +11 -3
  5. data/lib/joosy.js +235 -300
  6. data/package.json +3 -3
  7. data/spec/helpers/ground.coffee +33 -0
  8. data/spec/helpers/matchers.coffee +65 -0
  9. data/spec/joosy/core/application_spec.coffee +8 -8
  10. data/spec/joosy/core/helpers/view_spec.coffee +9 -5
  11. data/spec/joosy/core/helpers/widgets_spec.coffee +43 -10
  12. data/spec/joosy/core/joosy_spec.coffee +42 -50
  13. data/spec/joosy/core/layout_spec.coffee +30 -34
  14. data/spec/joosy/core/modules/container_spec.coffee +79 -76
  15. data/spec/joosy/core/modules/events_spec.coffee +148 -81
  16. data/spec/joosy/core/modules/filters_spec.coffee +68 -49
  17. data/spec/joosy/core/modules/log_spec.coffee +13 -5
  18. data/spec/joosy/core/modules/module_spec.coffee +24 -14
  19. data/spec/joosy/core/modules/renderer_spec.coffee +95 -89
  20. data/spec/joosy/core/modules/time_manager_spec.coffee +11 -16
  21. data/spec/joosy/core/modules/widget_manager_spec.coffee +89 -71
  22. data/spec/joosy/core/page_spec.coffee +201 -137
  23. data/spec/joosy/core/templaters/jst_spec.coffee +62 -0
  24. data/spec/joosy/core/widget_spec.coffee +25 -29
  25. data/spec/joosy/extensions/form/form_spec.coffee +3 -1
  26. data/spec/joosy/extensions/resources/base_spec.coffee +3 -0
  27. data/spec/joosy/extensions/resources/collection_spec.coffee +3 -0
  28. data/spec/joosy/extensions/resources/rest_collection_spec.coffee +3 -0
  29. data/spec/joosy/extensions/resources/rest_spec.coffee +3 -0
  30. data/src/joosy/core/application.coffee +1 -7
  31. data/src/joosy/core/helpers/view.coffee +12 -0
  32. data/src/joosy/core/helpers/widgets.coffee +19 -9
  33. data/src/joosy/core/joosy.coffee +0 -23
  34. data/src/joosy/core/layout.coffee +7 -5
  35. data/src/joosy/core/module.coffee +24 -4
  36. data/src/joosy/core/modules/container.coffee +29 -28
  37. data/src/joosy/core/modules/events.coffee +85 -72
  38. data/src/joosy/core/modules/filters.coffee +3 -1
  39. data/src/joosy/core/modules/renderer.coffee +91 -74
  40. data/src/joosy/core/modules/widgets_manager.coffee +12 -9
  41. data/src/joosy/core/page.coffee +7 -14
  42. data/src/joosy/core/templaters/{rails_jst.coffee → jst.coffee} +21 -19
  43. data/src/joosy/core/widget.coffee +3 -3
  44. data/src/joosy/extensions/resources/base.coffee +8 -3
  45. data/src/joosy/extensions/resources/rest.coffee +8 -0
  46. data/src/joosy/extensions/resources/rest_collection.coffee +1 -0
  47. data/tasks/joosy.coffee +46 -17
  48. data/templates/application/base/pages/welcome/index.coffee +5 -5
  49. data/templates/application/base/templates/layouts/application.jst.hamlc +2 -2
  50. data/templates/application/base/templates/pages/welcome/index.jst.hamlc +2 -2
  51. data/templates/application/standalone/Gruntfile.coffee +3 -1
  52. metadata +6 -5
  53. data/spec/helpers/helper.coffee +0 -68
  54. data/spec/joosy/core/templaters/rails_jst_spec.coffee +0 -25
@@ -91,29 +91,6 @@
91
91
  v.toString 16
92
92
  .toUpperCase()
93
93
 
94
- #
95
- # Preloads sets of images then runs callback
96
- #
97
- # @param [Array<String>] images Images paths
98
- # @param [Function] callback Action to run when every picture was loaded (or triggered an error)
99
- #
100
- preloadImages: (images, callback) ->
101
- unless Object.isArray(images)
102
- images = [images]
103
- if images.length == 0
104
- callback()
105
-
106
- ticks = images.length
107
- result = []
108
- checker = ->
109
- if (ticks -= 1) == 0
110
- callback?()
111
-
112
- for p in images
113
- result.push $('<img/>').load(checker).error(checker).attr('src', p)
114
-
115
- result
116
-
117
94
  #
118
95
  # Basic URI builder. Joins base path with params hash
119
96
  #
@@ -33,6 +33,7 @@ class Joosy.Layout extends Joosy.Module
33
33
  @include Joosy.Modules.Filters
34
34
 
35
35
  @view 'default'
36
+ @helper 'page'
36
37
 
37
38
  #
38
39
  # Sets the method which will controll the painting preparation proccess.
@@ -116,6 +117,7 @@ class Joosy.Layout extends Joosy.Module
116
117
  # @param [Hash] params List of route params
117
118
  #
118
119
  constructor: (@params) ->
120
+ @uid = Joosy.uid()
119
121
 
120
122
  #
121
123
  # @see Joosy.Router.navigate
@@ -157,11 +159,11 @@ class Joosy.Layout extends Joosy.Module
157
159
  @__runAfterUnloads()
158
160
 
159
161
  #
160
- # @todo Rename this shit already. We are not going to release having function that marks
161
- # element with UUID called `yield`.
162
+ # Helpers that outputs container for the page
162
163
  #
163
- yield: ->
164
- @uuid = Joosy.uuid()
164
+ page: (tag, options={}) ->
165
+ options.id = @uid
166
+ Joosy.Helpers.Application.tag tag, options
165
167
 
166
168
  #
167
169
  # Gets layout element.
@@ -169,4 +171,4 @@ class Joosy.Layout extends Joosy.Module
169
171
  # @return [jQuery]
170
172
  #
171
173
  content: ->
172
- $("##{@uuid}")
174
+ $("##{@uid}")
@@ -52,14 +52,34 @@ class @Joosy.Module
52
52
 
53
53
  false
54
54
 
55
- @alias: (method, feature, action) ->
56
- chained = "#{method}Without#{feature.camelize()}"
55
+ #
56
+ # Allows to override method keeping the previous implementation accessible
57
+ #
58
+ # @param [String] method Name of the method to override
59
+ # @param [String] feature Shortcut to use as previous implementation suffix
60
+ # @param [String] action Name of new method to use
61
+ # @param [Function] action New implementation
62
+ #
63
+ @aliasMethodChain: (method, feature, action) ->
64
+ camelized = feature.charAt(0).toUpperCase() + feature.slice(1)
65
+ chained = "#{method}Without#{camelized}"
66
+
67
+ action = @::[action] unless Object.isFunction(action)
57
68
 
58
69
  @::[chained] = @::[method]
59
70
  @::[method] = action
60
71
 
61
- @aliasStatic: (method, feature, action) ->
62
- chained = "#{method}Without#{feature.camelize()}"
72
+ #
73
+ # Allows to override class-level method keeping the previous implementation accessible
74
+ #
75
+ # @param [String] method Name of the method to override
76
+ # @param [String] feature Shortcut to use as previous implementation suffix
77
+ # @param [String] action Name of new method to use
78
+ # @param [Function] action New implementation
79
+ #
80
+ @aliasStaticMethodChain: (method, feature, action) ->
81
+ camelized = feature.charAt(0).toUpperCase() + feature.slice(1)
82
+ chained = "#{method}Without#{camelized}"
63
83
 
64
84
  @[chained] = @[method]
65
85
  @[method] = action
@@ -11,41 +11,38 @@ Joosy.Modules.Container =
11
11
  eventSplitter: /^(\S+)\s*(.*)$/
12
12
 
13
13
  included: ->
14
+ #
15
+ # Extends elements mapping scheme
16
+ #
17
+ # @example
18
+ # @mapElements
19
+ # 'name': '.selector'
20
+ # 'name2': '$name .selector'
21
+ # 'category':
22
+ # 'name3': '.selector'
23
+ #
14
24
  @mapElements = (map) ->
15
25
  unless @::hasOwnProperty "__elements"
16
26
  @::__elements = Object.clone(@.__super__.__elements) || {}
17
27
  Object.merge @::__elements, map
18
28
 
29
+ #
30
+ # Extends events mapping scheme
31
+ #
32
+ # @example
33
+ # @mapEvents
34
+ # 'click': ($container, event) -> #fires on container
35
+ # 'click .selector': ($element, event) -> #fires on .selector
36
+ # 'click $name': ($element, event) -> #fires on selector assigned to 'name' element
37
+ #
19
38
  @mapEvents = (map) ->
20
39
  unless @::hasOwnProperty "__events"
21
40
  @::__events = Object.clone(@.__super__.__events) || {}
22
41
  Object.merge @::__events, map
23
42
 
24
- onRefresh: (callback) ->
25
- @__onRefreshes = [] unless @hasOwnProperty "__onRefreshes"
26
- @__onRefreshes.push callback
27
-
28
43
  $: (selector) ->
29
44
  $(selector, @container)
30
45
 
31
- #
32
- # Rebinds selectors defined in 'elements' hash to object properties
33
- #
34
- refreshElements: ->
35
- if @hasOwnProperty "__onRefreshes"
36
- @__onRefreshes.each (callback) => callback.apply @
37
- @__onRefreshes = []
38
-
39
- #
40
- # Clears old HTML links, set the new HTML from the callback and refreshes elements
41
- #
42
- # @param [Function] htmlCallback `() -> String` callback that will generate new HTML
43
- #
44
- reloadContainer: (htmlCallback) ->
45
- @__removeMetamorphs?()
46
- @container.html htmlCallback()
47
- @refreshElements()
48
-
49
46
  #
50
47
  # Fills container with given data removing all events
51
48
  #
@@ -88,18 +85,22 @@ Joosy.Modules.Container =
88
85
 
89
86
  return unless entries
90
87
 
91
- Object.each entries, (key, value) =>
88
+ for key,value of entries
92
89
  if Object.isObject(value)
93
90
  @__assignElements root['$'+key]={}, value
94
91
  else
95
92
  value = @__extractSelector value
96
-
97
- root['$'+key] = (filter) =>
98
- return @$(value) unless filter
99
- return @$(value).filter(filter)
100
-
93
+ root['$'+key] = @__wrapElement(value)
101
94
  root['$'+key].selector = value
102
95
 
96
+ #
97
+ # Wraps actual element closures. Required to clear context to avoid circular reference
98
+ #
99
+ __wrapElement: (value) ->
100
+ (filter) =>
101
+ return @$(value) unless filter
102
+ return @$(value).filter(filter)
103
+
103
104
  #
104
105
  # Binds events defined in 'events' to container
105
106
  #
@@ -23,12 +23,17 @@ Joosy.Modules.Events =
23
23
  events = name
24
24
  name = Object.keys(@__oneShotEvents).length.toString()
25
25
 
26
- events = @__splitEvents events
27
- @__validateEvents events
28
-
29
- @__oneShotEvents[name] = [events, callback]
26
+ @__oneShotEvents[name] = [@__splitEvents(events), callback]
30
27
  name
31
28
 
29
+ #
30
+ # Removes waiter action
31
+ #
32
+ # @param [Function] target Name of waiter to unbind
33
+ #
34
+ unwait: (target) ->
35
+ delete @__oneShotEvents[target]
36
+
32
37
  #
33
38
  # Binds action to run each time any of given event was triggered
34
39
  #
@@ -45,26 +50,16 @@ Joosy.Modules.Events =
45
50
  events = name
46
51
  name = Object.keys(@__boundEvents).length.toString()
47
52
 
48
- events = @__splitEvents events
49
- @__validateEvents events
50
-
51
- @__boundEvents[name] = [events, callback]
53
+ @__boundEvents[name] = [@__splitEvents(events), callback]
52
54
  name
53
55
 
54
56
  #
55
57
  # Unbinds action from runing on trigger
56
58
  #
57
- # @param [Function] target Action to unbind
59
+ # @param [Function] target Name of bind to unbind
58
60
  #
59
61
  unbind: (target) ->
60
- needle = undefined
61
-
62
- for name, [events, callback] of @__boundEvents
63
- if (Object.isFunction(target) && callback == target) || name == target
64
- needle = name
65
- break
66
-
67
- delete @__boundEvents[needle] if needle?
62
+ delete @__boundEvents[target]
68
63
 
69
64
  #
70
65
  # Triggers event for {bind} and {wait}
@@ -73,6 +68,7 @@ Joosy.Modules.Events =
73
68
  #
74
69
  trigger: (event, data...) ->
75
70
  Joosy.Modules.Log.debugAs @, "Event #{event} triggered"
71
+
76
72
  if @__oneShotEvents
77
73
  fire = []
78
74
  for name, [events, callback] of @__oneShotEvents
@@ -83,6 +79,7 @@ Joosy.Modules.Events =
83
79
  callback = @__oneShotEvents[name][1]
84
80
  delete @__oneShotEvents[name]
85
81
  callback data...
82
+
86
83
  if @__boundEvents
87
84
  for name, [events, callback] of @__boundEvents
88
85
  if events.any event
@@ -101,75 +98,91 @@ Joosy.Modules.Events =
101
98
  # @param [Function] block Configuration block (see example)
102
99
  #
103
100
  synchronize: (block) ->
104
- context = new Joosy.Events.SynchronizationContext(this)
105
- block.call(this, context)
101
+ context = new Joosy.Events.SynchronizationContext(@)
102
+ block.call(@, context)
106
103
 
107
104
  if context.expectations.length == 0
108
- context.after.call(this)
105
+ context.after.call(@)
109
106
  else
110
- @wait context.expectations, => context.after.call(this)
107
+ @wait context.expectations, => context.after.call(@)
111
108
  context.actions.each (data) =>
112
- data[0].call this, =>
109
+ data[0].call @, =>
113
110
  @trigger data[1]
114
111
 
115
112
  __splitEvents: (events) ->
116
113
  if Object.isString events
117
114
  if events.isBlank()
118
- []
115
+ events = []
119
116
  else
120
- events.trim().split /\s+/
121
- else
122
- events
117
+ events = events.trim().split /\s+/
123
118
 
124
- __validateEvents: (events) ->
125
119
  unless Object.isArray(events) && events.length > 0
126
120
  throw new Error "#{Joosy.Module.__className @}> bind invalid events: #{events}"
127
121
 
128
-
129
- Joosy.Events = {}
130
-
131
- class Joosy.Events.Namespace
132
- constructor: (@parent) ->
133
- @bindings = []
134
-
135
- bind: (args...) -> @bindings.push @parent.bind(args...)
136
- unbind: ->
137
- @parent.unbind b for b in @bindings
138
- @bindings = []
122
+ events
139
123
 
140
124
  #
141
- # Internal representation of {Joosy.Modules.Events.synchronize} context
125
+ # Additional events helpers and tools
142
126
  #
143
- # @see Joosy.Modules.Events.synchronize
144
- #
145
- class Joosy.Events.SynchronizationContext
146
- @uid = 0
147
-
148
- constructor: (@parent) ->
149
- @expectations = []
150
- @actions = []
151
-
152
- #
153
- # Internal simple counter to separate given synchronization actions
154
- #
155
- uid: ->
156
- @constructor.uid += 1
157
-
158
- #
159
- # Registeres another async function that should be synchronized
160
- #
161
- # @param [Function] action `(Function) -> null` to call.
162
- # Should call given function to mark itself complete.
163
- #
164
- do: (action) ->
165
- event = "synchro-#{@uid()}"
166
- @expectations.push event
167
- @actions.push [action, event]
168
-
169
- #
170
- # Registers finalizer: the action that will be called when all do-functions
171
- # marked themselves as complete.
172
- #
173
- # @param [Function] after Function to call.
174
- #
175
- after: (@after) ->
127
+ Joosy.namespace 'Joosy.Events', ->
128
+ #
129
+ # Events namespace
130
+ #
131
+ # Creates unified collection of bindings to a particular instance
132
+ # that can be unbinded alltogether
133
+ #
134
+ # @example
135
+ # namespace = Joosy.Events.Namespace(something)
136
+ #
137
+ # namespace.bind 'event1', ->
138
+ # namespace.bind 'event2', ->
139
+ # namespace.unbind() # unbinds both bindings
140
+ #
141
+ class @Namespace
142
+ #
143
+ # @param [Object] @parent Any instance that can trigger events
144
+ #
145
+ constructor: (@parent) ->
146
+ @bindings = []
147
+
148
+ bind: (args...) -> @bindings.push @parent.bind(args...)
149
+ unbind: ->
150
+ @parent.unbind b for b in @bindings
151
+ @bindings = []
152
+
153
+ #
154
+ # Internal representation of {Joosy.Modules.Events.synchronize} context
155
+ #
156
+ # @see Joosy.Modules.Events.synchronize
157
+ #
158
+ class @SynchronizationContext
159
+ @uid = 0
160
+
161
+ constructor: (@parent) ->
162
+ @expectations = []
163
+ @actions = []
164
+
165
+ #
166
+ # Internal simple counter to separate given synchronization actions
167
+ #
168
+ uid: ->
169
+ @constructor.uid += 1
170
+
171
+ #
172
+ # Registeres another async function that should be synchronized
173
+ #
174
+ # @param [Function] action `(Function) -> null` to call.
175
+ # Should call given function to mark itself complete.
176
+ #
177
+ do: (action) ->
178
+ event = "synchro-#{@uid()}"
179
+ @expectations.push event
180
+ @actions.push [action, event]
181
+
182
+ #
183
+ # Registers finalizer: the action that will be called when all do-functions
184
+ # marked themselves as complete.
185
+ #
186
+ # @param [Function] after Function to call.
187
+ #
188
+ after: (@after) ->
@@ -31,7 +31,9 @@ Joosy.Modules.Filters =
31
31
 
32
32
 
33
33
  ['beforeLoad', 'afterLoad', 'afterUnload'].each (filter) =>
34
- Joosy.Modules.Filters["__run#{filter.camelize(true)}s"] = (opts...) ->
34
+ camelized = filter.charAt(0).toUpperCase() + filter.slice(1);
35
+
36
+ Joosy.Modules.Filters["__run#{camelized}s"] = (opts...) ->
35
37
  return true unless @["__#{filter}s"]
36
38
 
37
39
  @["__#{filter}s"].reduce (flag, func) =>
@@ -6,18 +6,15 @@
6
6
  # Core DOM rendering mechanics
7
7
  #
8
8
  # @mixin
9
- # @todo Describe this scary thing o_O
10
9
  #
11
10
  Joosy.Modules.Renderer =
12
11
 
13
12
  #
14
- # Default behavior for non-set renderer (empty template?)
13
+ # Default behavior for non-set view (empty template?)
15
14
  #
16
- __renderer: ->
15
+ __renderDefault: ->
17
16
  throw new Error "#{Joosy.Module.__className @constructor} does not have an attached template"
18
17
 
19
- __helpers: null
20
-
21
18
  #
22
19
  # Defines class-level helpers: @view and @helpers
23
20
  #
@@ -26,33 +23,69 @@ Joosy.Modules.Renderer =
26
23
  #
27
24
  included: ->
28
25
  @view = (template, options={}) ->
29
- if Object.isFunction template
30
- @::__renderer = template
31
- else
32
- @::__renderer = (locals={}) ->
33
- if options.dynamic
34
- @renderDynamic template, locals
35
- else
36
- @render template, locals
37
-
38
- @helpers = (helpers...) ->
39
- @::__helpers ||= []
40
- helpers.map (helper) =>
41
- module = Joosy.Helpers[helper]
42
- unless module
43
- throw new Error "Cannot find helper module #{helper}"
44
-
45
- @::__helpers.push module
26
+ @::__renderDefault = (locals={}) ->
27
+ if options.dynamic
28
+ @renderDynamic template, locals
29
+ else
30
+ @render template, locals
46
31
 
32
+ @helper = (helpers...) ->
33
+ unless @::hasOwnProperty "__helpers"
34
+ @::__helpers = @.__super__.__helpers?.clone() || []
35
+
36
+ @::__helpers = @::__helpers.add(helpers).unique()
47
37
  @::__helpers = @::__helpers.unique()
48
38
 
39
+ #
40
+ # Renders given template with given locals
41
+ #
42
+ # @param [String] template Name of the template to render using templater
43
+ # @param [Function] template `(locals) ->` lambda to use as template
44
+ # @param [Object] locals Locals to assign
45
+ # @param [Object] parentStackPointer Internal rendering stack pointer
46
+ #
47
+ render: (template, locals={}, parentStackPointer=false) ->
48
+ @__render false, template, locals, parentStackPointer
49
+
50
+ #
51
+ # Dynamically renders given template with given locals
52
+ #
53
+ # Whenever any of assigned locals triggers `changed` event, DOM will automatically be refreshed
54
+ #
55
+ # @param [String] template Name of the template to render using templater
56
+ # @param [Function] template `(locals) ->` lambda to use as template
57
+ # @param [Object] locals Locals to assign
58
+ # @param [Object] parentStackPointer Internal rendering stack pointer
59
+ #
60
+ renderDynamic: (template, locals={}, parentStackPointer=false) ->
61
+ @__render true, template, locals, parentStackPointer
62
+
63
+ #
64
+ # Converts all possible `@helper` arguments to the objects available for merge
65
+ #
66
+ __assignHelpers: ->
67
+ return unless @__helpers?
68
+
69
+ unless @hasOwnProperty "__helpers"
70
+ @__helpers = @__helpers.clone()
71
+
72
+ @__helpers.each (helper, i) =>
73
+ unless Object.isObject(helper)
74
+ unless @[helper]?
75
+ throw new Error "Cannot find method '#{helper}' to use as helper"
76
+
77
+ @__helpers[i] = {}
78
+ @__helpers[i][helper] = => @[helper] arguments...
79
+
80
+ #
81
+ # Collects and merges all requested helpers including global scope to one cached object
82
+ #
49
83
  __instantiateHelpers: ->
50
84
  unless @__helpersInstance
51
- @__helpersInstance = Object.extended Joosy.Helpers.Application
85
+ @__assignHelpers()
52
86
 
53
- if @onRefresh
54
- @__helpersInstance.onRefresh = (callback) =>
55
- @onRefresh callback
87
+ @__helpersInstance = Object.extended Joosy.Helpers.Application
88
+ @__helpersInstance.__renderer = @
56
89
 
57
90
  if @__helpers
58
91
  for helper in @__helpers
@@ -60,102 +93,80 @@ Joosy.Modules.Renderer =
60
93
 
61
94
  @__helpersInstance
62
95
 
63
- # If we do not have __proto__ available...
64
- __proxifyHelpers: (locals) ->
65
- if locals.hasOwnProperty '__proto__'
66
- locals.__proto__ = @__instantiateHelpers()
67
- locals
68
- else
69
- unless @__helpersProxyInstance
70
- @__helpersProxyInstance = (locals) ->
71
- Joosy.Module.merge this, locals
72
-
73
- @__helpersProxyInstance.prototype = @__instantiateHelpers()
74
-
75
- new @__helpersProxyInstance locals
76
-
77
- render: (template, locals={}, parentStackPointer=false) ->
78
- @__render false, template, locals, parentStackPointer
79
-
80
- renderDynamic: (template, locals={}, parentStackPointer=false) ->
81
- @__render true, template, locals, parentStackPointer
96
+ #
97
+ # Defines local `@render*` methods with proper stack pointer set
98
+ #
99
+ # @param [Object] parentStackPointer Internal rendering stack pointer
100
+ #
101
+ __instantiateRenderers: (parentStackPointer) ->
102
+ render: (template, locals={}) =>
103
+ @render template, locals, parentStackPointer
104
+ renderDynamic: (template, locals={}) =>
105
+ @renderDynamic template, locals, parentStackPointer
106
+ renderInline: (locals={}, template) =>
107
+ @renderDynamic template, locals, parentStackPointer
82
108
 
109
+ #
110
+ # Actual rendering implementation
111
+ #
83
112
  __render: (dynamic, template, locals={}, parentStackPointer=false) ->
84
113
  stack = @__renderingStackChildFor parentStackPointer
85
114
 
86
115
  stack.template = template
87
116
  stack.locals = locals
88
117
 
89
- # If template was given as a lambda, parameters should
90
- # be passed as a context, not as a argument
91
- assignContext = false
92
-
93
118
  if Object.isString template
94
119
  if @__renderSection?
95
120
  template = Joosy.Application.templater.resolveTemplate @__renderSection(), template, this
96
-
97
121
  template = Joosy.Application.templater.buildView template
98
- else if Object.isFunction template
99
- assignContext = true
100
122
  else if !Object.isFunction template
101
123
  throw new Error "#{Joosy.Module.__className @}> template (maybe @view) does not look like a string or lambda"
102
124
 
103
125
  if !Object.isObject(locals) && Object.extended().constructor != locals.constructor
104
126
  throw new Error "#{Joosy.Module.__className @}> locals (maybe @data?) is not a hash"
105
127
 
106
- renderers =
107
- render: (template, locals={}) =>
108
- @render template, locals, stack
109
- renderDynamic: (template, locals={}) =>
110
- @renderDynamic template, locals, stack
111
- renderInline: (locals={}, template) =>
112
- @renderDynamic template, locals, stack
113
-
114
128
  context = =>
115
129
  data = {}
116
130
 
117
131
  Joosy.Module.merge data, stack.locals
118
132
  Joosy.Module.merge data, @__instantiateHelpers(), false
119
- Joosy.Module.merge data, renderers
133
+ Joosy.Module.merge data, @__instantiateRenderers(stack)
120
134
  data
121
135
 
122
136
  result = ->
123
- if assignContext
124
- template.call(context())
125
- else
126
- template(context())
137
+ template(context())
127
138
 
128
139
  if dynamic
129
140
  morph = Metamorph result()
130
141
  update = =>
131
142
  if morph.isRemoved()
132
- for [object, callback] in morph.__bindings
133
- object.unbind callback
143
+ for [object, binding] in morph.__bindings
144
+ object.unbind binding
134
145
  else
135
146
  for child in stack.children
136
147
  @__removeMetamorphs child
137
148
  stack.children = []
138
149
  morph.html result()
139
- @refreshElements?()
140
150
 
141
151
  # This is here to break stack tree and save from
142
- # repeating DOM handling
152
+ # repeating DOM modification
143
153
  update = update.debounce 0
144
154
 
145
- morph.__bindings = []
146
-
147
155
  for key, object of locals
148
156
  if locals.hasOwnProperty key
149
157
  if object?.bind? && object?.unbind?
150
- binding = [object, update]
151
- object.bind 'changed', update
158
+ binding = [object, object.bind('changed', update)]
152
159
  stack.metamorphBindings.push binding
153
- morph.__bindings.push binding
160
+
161
+ morph.__bindings = stack.metamorphBindings
154
162
 
155
163
  morph.outerHTML()
156
164
  else
157
165
  result()
158
166
 
167
+ #
168
+ # Template for the rendering stack node
169
+ #
159
170
  __renderingStackElement: (parent=null) ->
160
171
  metamorphBindings: []
161
172
  locals: null
@@ -163,6 +174,9 @@ Joosy.Modules.Renderer =
163
174
  children: []
164
175
  parent: parent
165
176
 
177
+ #
178
+ # Creates new rendering stack node using given pointer as the parent
179
+ #
166
180
  __renderingStackChildFor: (parentPointer) ->
167
181
  if !@__renderingStack
168
182
  @__renderingStack = []
@@ -176,6 +190,9 @@ Joosy.Modules.Renderer =
176
190
  parentPointer.children.push element
177
191
  element
178
192
 
193
+ #
194
+ # Disables and unbinds all dynamic bindings for the whole rendering stack
195
+ #
179
196
  __removeMetamorphs: (stackPointer=false) ->
180
197
  remove = (stackPointer) =>
181
198
  if stackPointer?.children