joosy 1.2.0.beta.4 → 1.2.0.rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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?
|