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.
- checksums.yaml +4 -4
- data/.codoopts +1 -1
- data/Gruntfile.coffee +3 -3
- data/README.md +4 -0
- data/bower.json +1 -1
- data/build/joosy.js +2 -2
- data/build/joosy/form.js +1 -1
- data/build/joosy/resources.js +1 -1
- data/package.json +2 -2
- data/source/joosy/application.coffee +2 -2
- data/source/joosy/form.coffee +4 -4
- data/source/joosy/helpers/form.coffee +12 -3
- data/source/joosy/helpers/index.coffee +0 -1
- data/source/joosy/helpers/view.coffee +16 -3
- data/source/joosy/layout.coffee +0 -4
- data/source/joosy/module.coffee +16 -1
- data/source/joosy/modules/dom.coffee +106 -101
- data/source/joosy/modules/events.coffee +44 -10
- data/source/joosy/modules/filters.coffee +64 -60
- data/source/joosy/modules/page.coffee +3 -0
- data/source/joosy/modules/page/scrolling.coffee +46 -29
- data/source/joosy/modules/page/title.coffee +14 -0
- data/source/joosy/modules/renderer.coffee +219 -190
- data/source/joosy/modules/resources.coffee +3 -0
- data/source/joosy/modules/resources/cacher.coffee +81 -10
- data/source/joosy/modules/resources/function.coffee +26 -29
- data/source/joosy/modules/resources/identity_map.coffee +64 -42
- data/source/joosy/modules/resources/model.coffee +127 -73
- data/source/joosy/modules/time_manager.coffee +2 -0
- data/source/joosy/page.coffee +3 -6
- data/source/joosy/resources/array.coffee +87 -2
- data/source/joosy/resources/hash.coffee +53 -1
- data/source/joosy/resources/rest.coffee +59 -3
- data/source/joosy/resources/scalar.coffee +47 -1
- data/source/joosy/router.coffee +63 -21
- data/source/joosy/templaters/jst.coffee +3 -0
- data/source/joosy/widget.coffee +17 -11
- data/spec/joosy/core/helpers/view_spec.coffee +14 -0
- data/spec/joosy/core/modules/dom_spec.coffee +1 -1
- data/spec/joosy/core/modules/filters_spec.coffee +2 -2
- data/spec/joosy/core/modules/module_spec.coffee +1 -1
- data/spec/joosy/core/modules/renderer_spec.coffee +19 -1
- data/spec/joosy/core/router_spec.coffee +80 -45
- data/spec/joosy/core/widget_spec.coffee +9 -0
- data/spec/joosy/resources/modules/cacher_spec.coffee +3 -3
- data/spec/joosy/resources/modules/function_spec.coffee +2 -2
- data/spec/joosy/resources/modules/identity_map_spec.coffee +2 -2
- data/spec/joosy/resources/modules/model_spec.coffee +1 -1
- metadata +2 -5
- data/source/joosy/helpers/routes.coffee +0 -17
- data/source/joosy/modules/widgets_manager.coffee +0 -90
- data/spec/joosy/core/helpers/routes_spec.coffee +0 -15
@@ -1,13 +1,14 @@
|
|
1
1
|
#= require joosy/joosy
|
2
2
|
|
3
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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 [
|
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
|
-
# @
|
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
|
-
#
|
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 [
|
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
|
-
#
|
12
|
+
# Internal helper registering filters accessors
|
12
13
|
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
#
|
23
|
+
# Registers a set of plain (synchronous) filters
|
20
24
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# @
|
25
|
+
# @example
|
26
|
+
# class Test
|
27
|
+
# @extend Joosy.Modules.Filters
|
28
|
+
# @registerPlainFilters 'beforeLoad', 'afterLoad'
|
24
29
|
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
43
|
-
|
44
|
-
callback.apply(@, params)
|
35
|
+
@::["__run#{camelized}s"] = (params...) ->
|
36
|
+
return unless @["__#{filter}s"]
|
45
37
|
|
46
|
-
|
47
|
-
|
38
|
+
for callback in @["__#{filter}s"]
|
39
|
+
callback = @[callback] unless typeof(callback) == 'function'
|
40
|
+
callback.apply(@, params)
|
48
41
|
|
49
|
-
|
50
|
-
|
51
|
-
flag && callback.apply(@, params) != false
|
52
|
-
, true
|
42
|
+
@::["__confirm#{camelized}s"] = (params...) ->
|
43
|
+
return true unless @["__#{filter}s"]
|
53
44
|
|
54
|
-
|
55
|
-
|
45
|
+
@["__#{filter}s"].reduce (flag, callback) =>
|
46
|
+
callback = @[callback] unless typeof(callback) == 'function'
|
47
|
+
flag && callback.apply(@, params) != false
|
48
|
+
, true
|
56
49
|
|
57
|
-
|
58
|
-
|
59
|
-
data = callback.apply(@, [data].concat params)
|
50
|
+
@::["__apply#{camelized}s"] = (data, params...) ->
|
51
|
+
return data unless @["__#{filter}s"]
|
60
52
|
|
61
|
-
|
53
|
+
for callback in @["__#{filter}s"]
|
54
|
+
callback = @[callback] unless typeof(callback) == 'function'
|
55
|
+
data = callback.apply(@, [data].concat params)
|
62
56
|
|
63
|
-
|
64
|
-
for filter in filters
|
65
|
-
do (filter) =>
|
66
|
-
camelized = @__registerFilterCollector filter
|
57
|
+
data
|
67
58
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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,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
|
-
@
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
@
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@view
|
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
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
render
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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?
|