agt 0.0.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 +7 -0
- data/LICENSE.md +7 -0
- data/README.md +5 -0
- data/app/assets/javascripts/agt/config.coffee +9 -0
- data/app/assets/javascripts/agt/dom.coffee +12 -0
- data/app/assets/javascripts/agt/function.coffee +317 -0
- data/app/assets/javascripts/agt/geom/circle.coffee +338 -0
- data/app/assets/javascripts/agt/geom/cubic_bezier.coffee +37 -0
- data/app/assets/javascripts/agt/geom/diamond.coffee +241 -0
- data/app/assets/javascripts/agt/geom/ellipsis.coffee +141 -0
- data/app/assets/javascripts/agt/geom/linear_spline.coffee +56 -0
- data/app/assets/javascripts/agt/geom/matrix.coffee +240 -0
- data/app/assets/javascripts/agt/geom/mixins/geometry.coffee +171 -0
- data/app/assets/javascripts/agt/geom/mixins/intersections.coffee +150 -0
- data/app/assets/javascripts/agt/geom/mixins/path.coffee +145 -0
- data/app/assets/javascripts/agt/geom/mixins/proxyable.coffee +9 -0
- data/app/assets/javascripts/agt/geom/mixins/spline.coffee +329 -0
- data/app/assets/javascripts/agt/geom/mixins/surface.coffee +55 -0
- data/app/assets/javascripts/agt/geom/mixins/triangulable.coffee +112 -0
- data/app/assets/javascripts/agt/geom/point.coffee +454 -0
- data/app/assets/javascripts/agt/geom/polygon.coffee +119 -0
- data/app/assets/javascripts/agt/geom/quad_bezier.coffee +43 -0
- data/app/assets/javascripts/agt/geom/quint_bezier.coffee +42 -0
- data/app/assets/javascripts/agt/geom/rectangle.coffee +599 -0
- data/app/assets/javascripts/agt/geom/spiral.coffee +90 -0
- data/app/assets/javascripts/agt/geom/transformation_proxy.coffee +43 -0
- data/app/assets/javascripts/agt/geom/triangle.coffee +442 -0
- data/app/assets/javascripts/agt/impulse.coffee +105 -0
- data/app/assets/javascripts/agt/index.coffee +56 -0
- data/app/assets/javascripts/agt/inflector/inflection.coffee +21 -0
- data/app/assets/javascripts/agt/inflector/inflections.coffee +75 -0
- data/app/assets/javascripts/agt/inflector/inflector.coffee +235 -0
- data/app/assets/javascripts/agt/inheritance.coffee +132 -0
- data/app/assets/javascripts/agt/math.coffee +45 -0
- data/app/assets/javascripts/agt/mixins/activable.coffee +31 -0
- data/app/assets/javascripts/agt/mixins/aliasable.coffee +25 -0
- data/app/assets/javascripts/agt/mixins/alternate_case.coffee +72 -0
- data/app/assets/javascripts/agt/mixins/cloneable.coffee +71 -0
- data/app/assets/javascripts/agt/mixins/delegation.coffee +90 -0
- data/app/assets/javascripts/agt/mixins/disposable.coffee +7 -0
- data/app/assets/javascripts/agt/mixins/equatable.coffee +37 -0
- data/app/assets/javascripts/agt/mixins/formattable.coffee +52 -0
- data/app/assets/javascripts/agt/mixins/globalizable.coffee +175 -0
- data/app/assets/javascripts/agt/mixins/has_ancestors.coffee +47 -0
- data/app/assets/javascripts/agt/mixins/has_collection.coffee +107 -0
- data/app/assets/javascripts/agt/mixins/has_nested_collection.coffee +51 -0
- data/app/assets/javascripts/agt/mixins/memoizable.coffee +64 -0
- data/app/assets/javascripts/agt/mixins/parameterizable.coffee +101 -0
- data/app/assets/javascripts/agt/mixins/poolable.coffee +62 -0
- data/app/assets/javascripts/agt/mixins/sourcable.coffee +45 -0
- data/app/assets/javascripts/agt/mixins/state_machine.coffee +47 -0
- data/app/assets/javascripts/agt/net/router.coffee +165 -0
- data/app/assets/javascripts/agt/object.coffee +9 -0
- data/app/assets/javascripts/agt/particles/actions/base_action.coffee +7 -0
- data/app/assets/javascripts/agt/particles/actions/die_on_surface.coffee +14 -0
- data/app/assets/javascripts/agt/particles/actions/force.coffee +11 -0
- data/app/assets/javascripts/agt/particles/actions/friction.coffee +14 -0
- data/app/assets/javascripts/agt/particles/actions/live.coffee +9 -0
- data/app/assets/javascripts/agt/particles/actions/macro_action.coffee +13 -0
- data/app/assets/javascripts/agt/particles/actions/move.coffee +11 -0
- data/app/assets/javascripts/agt/particles/actions/null_action.coffee +9 -0
- data/app/assets/javascripts/agt/particles/counters/by_rate.coffee +17 -0
- data/app/assets/javascripts/agt/particles/counters/fixed.coffee +9 -0
- data/app/assets/javascripts/agt/particles/counters/null_counter.coffee +9 -0
- data/app/assets/javascripts/agt/particles/emission.coffee +35 -0
- data/app/assets/javascripts/agt/particles/emitters/null_emitter.coffee +7 -0
- data/app/assets/javascripts/agt/particles/emitters/path.coffee +10 -0
- data/app/assets/javascripts/agt/particles/emitters/ponctual.coffee +9 -0
- data/app/assets/javascripts/agt/particles/emitters/surface.coffee +10 -0
- data/app/assets/javascripts/agt/particles/initializers/explosion.coffee +19 -0
- data/app/assets/javascripts/agt/particles/initializers/life.coffee +16 -0
- data/app/assets/javascripts/agt/particles/initializers/macro_initializer.coffee +10 -0
- data/app/assets/javascripts/agt/particles/initializers/null_initializer.coffee +7 -0
- data/app/assets/javascripts/agt/particles/initializers/particle_sub_system.coffee +12 -0
- data/app/assets/javascripts/agt/particles/initializers/stream.coffee +20 -0
- data/app/assets/javascripts/agt/particles/mixins/randomizable.coffee +10 -0
- data/app/assets/javascripts/agt/particles/particle.coffee +32 -0
- data/app/assets/javascripts/agt/particles/sub_system.coffee +11 -0
- data/app/assets/javascripts/agt/particles/system.coffee +103 -0
- data/app/assets/javascripts/agt/particles/timers/instant.coffee +11 -0
- data/app/assets/javascripts/agt/particles/timers/limited.coffee +19 -0
- data/app/assets/javascripts/agt/particles/timers/null_timer.coffee +11 -0
- data/app/assets/javascripts/agt/particles/timers/unlimited.coffee +9 -0
- data/app/assets/javascripts/agt/particles/timers/until_death.coffee +11 -0
- data/app/assets/javascripts/agt/promise.coffee +214 -0
- data/app/assets/javascripts/agt/random/random.coffee +100 -0
- data/app/assets/javascripts/agt/random/seeds/lagged_fibonnacci.coffee +53 -0
- data/app/assets/javascripts/agt/random/seeds/linear.coffee +16 -0
- data/app/assets/javascripts/agt/random/seeds/linear_congruential.coffee +23 -0
- data/app/assets/javascripts/agt/random/seeds/math_random.coffee +9 -0
- data/app/assets/javascripts/agt/random/seeds/mersenne_twister.coffee +42 -0
- data/app/assets/javascripts/agt/random/seeds/no_random.coffee +12 -0
- data/app/assets/javascripts/agt/random/seeds/paul_houle.coffee +19 -0
- data/app/assets/javascripts/agt/signal.coffee +272 -0
- data/app/assets/javascripts/agt/sprites/animation.coffee +13 -0
- data/app/assets/javascripts/agt/sprites/sprite.coffee +30 -0
- data/app/assets/javascripts/agt/widgets/hash.coffee +36 -0
- data/app/assets/javascripts/agt/widgets/widgets.coffee +194 -0
- data/app/assets/javascripts/agt/widgets/widgets/checked_input.coffee +7 -0
- data/app/assets/javascripts/agt/widgets/widgets/focus_bubbling.coffee +15 -0
- data/lib/agt.rb +8 -0
- data/lib/agt/version.rb +5 -0
- metadata +173 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Public: An `Equatable` object can be compared in equality with another object.
|
|
3
|
+
# Objects are considered as equal if all the listed properties are equal.
|
|
4
|
+
#
|
|
5
|
+
# ```coffeescript
|
|
6
|
+
# class Dummy
|
|
7
|
+
# @include agt.mixins.Equatable('p1', 'p2')
|
|
8
|
+
#
|
|
9
|
+
# constructor: (@p1, @p2) ->
|
|
10
|
+
# # ...
|
|
11
|
+
#
|
|
12
|
+
# dummy = new Dummy(10, 'foo')
|
|
13
|
+
# dummy.equals p1: 10, p2: 'foo' # true
|
|
14
|
+
# dummy.equals new Dummy(5, 'bar') # false
|
|
15
|
+
# ```
|
|
16
|
+
#
|
|
17
|
+
# The `Equatable` mixin is called a parameterized mixin as
|
|
18
|
+
# it's in fact a function that will generate a mixin based
|
|
19
|
+
# on its arguments.
|
|
20
|
+
#
|
|
21
|
+
# properties - A list of {String} of the properties to compare to set equality.
|
|
22
|
+
#
|
|
23
|
+
# Returns a {ConcreteEquatable} mixin.
|
|
24
|
+
agt.mixins.Equatable = (properties...) ->
|
|
25
|
+
|
|
26
|
+
# Public: A concrete mixin is generated and returned by the
|
|
27
|
+
# [Equatable](../files/mixins/equatable.coffee.html) generator.
|
|
28
|
+
class ConcreteEquatable
|
|
29
|
+
|
|
30
|
+
# Public: Compares the `properties` of the passed-in object with the current
|
|
31
|
+
# object and return `true` if all the values are equal.
|
|
32
|
+
#
|
|
33
|
+
# o - The {Object} to compare to this instance.
|
|
34
|
+
#
|
|
35
|
+
# Returns a {Boolean} of whether the objects are equal or not.
|
|
36
|
+
equals: (o) -> o? and properties.every (p) =>
|
|
37
|
+
if @[ p ].equals? then @[ p ].equals o[ p ] else o[p] is @[ p ]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Public: A `Formattable` object provides a `toString` that return
|
|
3
|
+
# a string representation of the current instance.
|
|
4
|
+
#
|
|
5
|
+
# ```coffeescript
|
|
6
|
+
# class Dummy
|
|
7
|
+
# @include agt.mixins.Formattable('Dummy', 'p1', 'p2')
|
|
8
|
+
#
|
|
9
|
+
# constructor: (@p1, @p2) ->
|
|
10
|
+
# # ...
|
|
11
|
+
#
|
|
12
|
+
# dummy = new Dummy(10, 'foo')
|
|
13
|
+
# dummy.toString()
|
|
14
|
+
# # [Dummy(p1=10, p2=foo)]
|
|
15
|
+
# ```
|
|
16
|
+
#
|
|
17
|
+
# You may wonder why the class name is passed in the `Formattable`
|
|
18
|
+
# call, the reason is that javascript minification can alter the
|
|
19
|
+
# naming of the functions and in that case, the constructor function
|
|
20
|
+
# name can't be relied on anymore.
|
|
21
|
+
# Passing the class name will ensure that the initial class name
|
|
22
|
+
# is always accessible through an instance.
|
|
23
|
+
#
|
|
24
|
+
# classname - The {String} name of the class for which generate a mixin.
|
|
25
|
+
# properties - A list of {String} of the properties to include
|
|
26
|
+
# in the formatted output.
|
|
27
|
+
#
|
|
28
|
+
# Returns a {ConcreteFormattable} mixin.
|
|
29
|
+
agt.mixins.Formattable = (classname, properties...) ->
|
|
30
|
+
# Public: The concrete class as returned by the
|
|
31
|
+
# [Formattable](../files/mixins/formattable.coffee.html) generator.
|
|
32
|
+
class ConcreteFormattable
|
|
33
|
+
### Public ###
|
|
34
|
+
|
|
35
|
+
# Returns the object representation as a {String}.
|
|
36
|
+
# By default the object representation looks like `[ClassName]`.
|
|
37
|
+
# If the mixin was configurated using at least on property the
|
|
38
|
+
# object representation will now looks like
|
|
39
|
+
# `[ClassName(property=value)]`
|
|
40
|
+
#
|
|
41
|
+
# Returns a {String}.
|
|
42
|
+
toString: -> "[#{ classname }]"
|
|
43
|
+
|
|
44
|
+
if properties.length > 0
|
|
45
|
+
@::toString = ->
|
|
46
|
+
formattedProperties = ("#{ p }=#{ @[ p ] }" for p in properties)
|
|
47
|
+
"[#{ classname }(#{ formattedProperties.join ', ' })]"
|
|
48
|
+
|
|
49
|
+
# Returns the class name {String} of this instance.
|
|
50
|
+
#
|
|
51
|
+
# Returns a {String}
|
|
52
|
+
classname: -> classname
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
|
|
3
|
+
# Internal: The list of properties that are unglobalizable by default.
|
|
4
|
+
DEFAULT_UNGLOBALIZABLE = [
|
|
5
|
+
'globalizable'
|
|
6
|
+
'unglobalizable'
|
|
7
|
+
'globalized'
|
|
8
|
+
'globalize'
|
|
9
|
+
'unglobalize'
|
|
10
|
+
'globalizeMember'
|
|
11
|
+
'unglobalizeMember'
|
|
12
|
+
'keepContext'
|
|
13
|
+
'previousValues'
|
|
14
|
+
'previousDescriptors'
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# Public: A `Globalizable` object can expose some methods on the
|
|
18
|
+
# specified global object (`window` in a browser or `global` in nodejs
|
|
19
|
+
# when using methods from the `vm` module).
|
|
20
|
+
#
|
|
21
|
+
# The *globalization* process is **reversible** and take care to preserve
|
|
22
|
+
# the initial properties of the global that may be overriden.
|
|
23
|
+
#
|
|
24
|
+
# The properties exposed on the global object are defined
|
|
25
|
+
# in the `globalizable` property.
|
|
26
|
+
#
|
|
27
|
+
# ```coffeescript
|
|
28
|
+
# class Dummy
|
|
29
|
+
# @include agt.mixins.Globalizable window
|
|
30
|
+
#
|
|
31
|
+
# globalizable: ['someMethod']
|
|
32
|
+
#
|
|
33
|
+
# someMethod: -> console.log 'in some method'
|
|
34
|
+
#
|
|
35
|
+
# instance = new Dummy
|
|
36
|
+
# instance.globalize()
|
|
37
|
+
#
|
|
38
|
+
# someMethod()
|
|
39
|
+
# # output: 'in some method'
|
|
40
|
+
# ```
|
|
41
|
+
#
|
|
42
|
+
# The process can be reversed with the `unglobalize` method.
|
|
43
|
+
#
|
|
44
|
+
# ```coffeescript
|
|
45
|
+
# instance.unglobalize()
|
|
46
|
+
# ```
|
|
47
|
+
#
|
|
48
|
+
# The `Globalizable` function takes the target global object as the first
|
|
49
|
+
# argument. The second argument define whether the functions on
|
|
50
|
+
# a globalized object are bound to this object or to the global object.
|
|
51
|
+
#
|
|
52
|
+
# global - The global {Object} onto which adds globalized methods
|
|
53
|
+
# and properties.
|
|
54
|
+
# keepContext - A {Boolean} defining whether the initial context
|
|
55
|
+
# of the methods are preserved or not.
|
|
56
|
+
#
|
|
57
|
+
# Returns a {ConcreteGlobalizable} mixin to decorate a class with.
|
|
58
|
+
agt.mixins.Globalizable = (global, keepContext=true) ->
|
|
59
|
+
|
|
60
|
+
# Public: The concrete globalizable mixin as returned by the
|
|
61
|
+
# [Globalizable](../files/mixins/globalizable.coffee.html) generator.
|
|
62
|
+
class ConcreteGlobalizable
|
|
63
|
+
|
|
64
|
+
# Public: An {Array} storing the {String} name of the properties that
|
|
65
|
+
# can't be globalized. This takes precedence over the `globalizable`
|
|
66
|
+
# property of the decorated class.
|
|
67
|
+
@unglobalizable: DEFAULT_UNGLOBALIZABLE.concat()
|
|
68
|
+
|
|
69
|
+
# Public: {Boolean} that defines whether the methods context
|
|
70
|
+
# are preserved or not.
|
|
71
|
+
keepContext: keepContext
|
|
72
|
+
|
|
73
|
+
# Public: The method that actually exposes the object methods on global.
|
|
74
|
+
globalize: ->
|
|
75
|
+
# But only if the object isn't already `globalized`.
|
|
76
|
+
return if @globalized
|
|
77
|
+
|
|
78
|
+
# Creates the objects that will stores the previous values
|
|
79
|
+
# and property descriptors present on `global` before the
|
|
80
|
+
# object globalization.
|
|
81
|
+
@previousValues = {}
|
|
82
|
+
@previousDescriptors = {}
|
|
83
|
+
|
|
84
|
+
# Then for each properties set for globalization the
|
|
85
|
+
# `globalizeMember` method is called.
|
|
86
|
+
@globalizable.forEach (k) =>
|
|
87
|
+
unless k in (@constructor.unglobalizable or ConcreteGlobalizable.unglobalizable)
|
|
88
|
+
@globalizeMember k
|
|
89
|
+
|
|
90
|
+
# And the object is marked as `globalized`.
|
|
91
|
+
@globalized = true
|
|
92
|
+
|
|
93
|
+
# Public: The reverse process of `globalize`.
|
|
94
|
+
unglobalize: ->
|
|
95
|
+
return unless @globalized
|
|
96
|
+
|
|
97
|
+
# For each properties set for globalization the
|
|
98
|
+
# `unglobalizeMember` method is called.
|
|
99
|
+
@globalizable.forEach (k) =>
|
|
100
|
+
unless k in (@constructor.unglobalizable or ConcreteGlobalizable.unglobalizable)
|
|
101
|
+
@unglobalizeMember k
|
|
102
|
+
|
|
103
|
+
# And then the object is cleaned of the globalization artifacts
|
|
104
|
+
# and the `globalized` mark is removed.
|
|
105
|
+
@previousValues = null
|
|
106
|
+
@previousDescriptors = null
|
|
107
|
+
@globalized = false
|
|
108
|
+
|
|
109
|
+
# Internal: Exposes a member of the current object on global.
|
|
110
|
+
#
|
|
111
|
+
# key - The {String} name of the property to globalize.
|
|
112
|
+
globalizeMember: (key) ->
|
|
113
|
+
# If possible we prefer using property descriptors rather than
|
|
114
|
+
# accessing directly the properties. It will allow to correctly
|
|
115
|
+
# expose virtual properties (get/set) created through
|
|
116
|
+
# `Object.defineProperty`.
|
|
117
|
+
oldDescriptor = Object.getPropertyDescriptor global, key
|
|
118
|
+
selfDescriptor = Object.getPropertyDescriptor this, key
|
|
119
|
+
|
|
120
|
+
# If we have a property descriptor for the previous global property
|
|
121
|
+
# we store it to restore it in the `unglobalize` process.
|
|
122
|
+
if oldDescriptor?
|
|
123
|
+
@previousDescriptors[ key ] = oldDescriptor
|
|
124
|
+
# Otherwise the property value is stored.
|
|
125
|
+
else if @[ key ]?
|
|
126
|
+
@previousValues[ key ] = global if global[ key ]?
|
|
127
|
+
|
|
128
|
+
# If we have a property descriptor for the object property, we'll
|
|
129
|
+
# use it to create the property on global with the same settings.
|
|
130
|
+
if selfDescriptor?
|
|
131
|
+
# But if we have to bind functions to the object there'll be
|
|
132
|
+
# a need for additional setup.
|
|
133
|
+
if keepContext
|
|
134
|
+
# For instance, if the descriptor contains a `get` and `set`
|
|
135
|
+
# property then we have to bind both.
|
|
136
|
+
if selfDescriptor.get? or selfDescriptor.set?
|
|
137
|
+
selfDescriptor.get = selfDescriptor.get?.bind(@)
|
|
138
|
+
selfDescriptor.set = selfDescriptor.set?.bind(@)
|
|
139
|
+
# Otherwise, if the value is a function we bind it.
|
|
140
|
+
else if typeof selfDescriptor.value is 'function'
|
|
141
|
+
selfDescriptor.value = selfDescriptor.value.bind(@)
|
|
142
|
+
|
|
143
|
+
# Finally the descriptor is used to create the new property
|
|
144
|
+
# on the global object.
|
|
145
|
+
Object.defineProperty global, key, selfDescriptor
|
|
146
|
+
|
|
147
|
+
# Without a property descriptor for the object's property
|
|
148
|
+
# the value is retreived and used to create a new property
|
|
149
|
+
# descriptor.
|
|
150
|
+
else
|
|
151
|
+
value = @[ key ]
|
|
152
|
+
value = value.bind(@) if typeof value is 'function' and keepContext
|
|
153
|
+
Object.defineProperty global, key, {
|
|
154
|
+
value
|
|
155
|
+
enumerable: true
|
|
156
|
+
writable: true
|
|
157
|
+
configurable: true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Internal: The inverse process of `globalizeMember`.
|
|
161
|
+
#
|
|
162
|
+
# key - The {String} name of the property to unglobalize.
|
|
163
|
+
unglobalizeMember: (key) ->
|
|
164
|
+
# If we have a previous descriptor we restore ot on global.
|
|
165
|
+
if @previousDescriptors[ key ]?
|
|
166
|
+
Object.defineProperty global, key, @previousDescriptors[ key ]
|
|
167
|
+
|
|
168
|
+
# If there's no previous descriptor but a previous value,
|
|
169
|
+
# the value is affected to the global property.
|
|
170
|
+
else if @previousValues[ key ]?
|
|
171
|
+
global[ key ] = @previousValues[ key ]
|
|
172
|
+
|
|
173
|
+
# And if there's nothing the property is unset.
|
|
174
|
+
else
|
|
175
|
+
global[ key ] = undefined
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
|
|
3
|
+
# Public: The `HasAncestors` mixin adds several methods to instance to deal
|
|
4
|
+
# with parents and ancestors.
|
|
5
|
+
#
|
|
6
|
+
# ```coffee
|
|
7
|
+
# class Dummy
|
|
8
|
+
# @concern agt.mixins.HasAncestors through: 'parentNode'
|
|
9
|
+
#
|
|
10
|
+
# @ancestorScope 'activeAncestors', (ancestor) -> ancestor.active
|
|
11
|
+
# ```
|
|
12
|
+
#
|
|
13
|
+
# options - The option {Object}:
|
|
14
|
+
# :through - The {String} name of the property giving access
|
|
15
|
+
# to the instance parent.
|
|
16
|
+
#
|
|
17
|
+
# Returns a {ConcreteHasAncestors} mixin.
|
|
18
|
+
agt.mixins.HasAncestors = (options={}) ->
|
|
19
|
+
through = options.through or 'parent'
|
|
20
|
+
|
|
21
|
+
# Public: The concrete mixin as returned by the
|
|
22
|
+
# [HasAncestors](../files/mixins/has_ancestors.coffee.html) generator.
|
|
23
|
+
class ConcreteHasAncestors
|
|
24
|
+
|
|
25
|
+
# Public: Returns an array of all the ancestors of the current object.
|
|
26
|
+
# The ancestors are ordered such as the first element is the direct
|
|
27
|
+
# parent of the current object.
|
|
28
|
+
#
|
|
29
|
+
# Returns an {Array}
|
|
30
|
+
@getter 'ancestors', ->
|
|
31
|
+
ancestors = []
|
|
32
|
+
parent = @[ through ]
|
|
33
|
+
|
|
34
|
+
while parent?
|
|
35
|
+
ancestors.push parent
|
|
36
|
+
parent = parent[ through ]
|
|
37
|
+
|
|
38
|
+
ancestors
|
|
39
|
+
|
|
40
|
+
# Public: Returns an object containing the current object followed by its
|
|
41
|
+
# parent and ancestors.
|
|
42
|
+
@getter 'selfAndAncestors', -> [ this ].concat @ancestors
|
|
43
|
+
|
|
44
|
+
# Public: Defines a getter property on instances named with `name` and that
|
|
45
|
+
# filter the `ancestors` array with the given `block`.
|
|
46
|
+
@ancestorsScope: (name, block) ->
|
|
47
|
+
@getter name, -> @ancestors.filter(block, this)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
|
|
3
|
+
# Public: The `HasCollection` mixin provides methods to expose a collection
|
|
4
|
+
# in a class. The mixin is created using two strings.
|
|
5
|
+
#
|
|
6
|
+
# ```coffeescript
|
|
7
|
+
# class Dummy
|
|
8
|
+
# @concern agt.mixins.HasCollection 'children', 'child'
|
|
9
|
+
#
|
|
10
|
+
# @childrenScope 'activeChildren', (child) -> child.active
|
|
11
|
+
#
|
|
12
|
+
# constructor: ->
|
|
13
|
+
# @children = []
|
|
14
|
+
# ```
|
|
15
|
+
#
|
|
16
|
+
# The `plural` string is used to access the collection in all methods
|
|
17
|
+
# provided by the mixin. The `singular` string will be used to create
|
|
18
|
+
# the collection managing methods.
|
|
19
|
+
#
|
|
20
|
+
# For instance, given that `'children'` and `'child'` was passed as arguments
|
|
21
|
+
# to `HasCollection` the following methods and properties will be created:
|
|
22
|
+
#
|
|
23
|
+
# - `childrenSize` [getter]
|
|
24
|
+
# - `childrenCount` [getter]
|
|
25
|
+
# - `childrenLength` [getter]
|
|
26
|
+
# - `hasChildren` [getter]
|
|
27
|
+
# - `addChild`
|
|
28
|
+
# - `removeChild`
|
|
29
|
+
# - `hasChild`
|
|
30
|
+
# - `containsChild`
|
|
31
|
+
#
|
|
32
|
+
# plural - The {String} name of the property where the collection can be found.
|
|
33
|
+
# singular - The singularized {String} name.
|
|
34
|
+
#
|
|
35
|
+
# Returns a {ConcreteHasCollection} mixin.
|
|
36
|
+
agt.mixins.HasCollection = (plural, singular) ->
|
|
37
|
+
|
|
38
|
+
pluralPostfix = plural.replace /^./, (s) -> s.toUpperCase()
|
|
39
|
+
singularPostfix = singular.replace /^./, (s) -> s.toUpperCase()
|
|
40
|
+
|
|
41
|
+
# Public: The concrete mixin as returned by the
|
|
42
|
+
# [HasCollection](../files/mixins/has_collection.coffee.html) generator.
|
|
43
|
+
class ConcreteHasCollection
|
|
44
|
+
# The mixin integrates `Aliasable` to create various alias to the
|
|
45
|
+
# collection methods.
|
|
46
|
+
@extend agt.mixins.Aliasable
|
|
47
|
+
|
|
48
|
+
# Public: Creates a `name` scope on instances that filter
|
|
49
|
+
# the collection using the passed-in `block`.
|
|
50
|
+
#
|
|
51
|
+
# name - The {String} name of the collection scope.
|
|
52
|
+
# block - The {Function} filter for the scope.
|
|
53
|
+
@[ "#{ plural }Scope" ] = (name, block) ->
|
|
54
|
+
@getter name, -> @[ plural ].filter block, this
|
|
55
|
+
|
|
56
|
+
# Public: A property returning the number of elements in the collection.
|
|
57
|
+
@getter "#{ plural }Size", -> @[ plural ].length
|
|
58
|
+
|
|
59
|
+
# Creates aliases for the collection size property.
|
|
60
|
+
@alias "#{ plural }Size", "#{ plural }Length", "#{ plural }Count"
|
|
61
|
+
|
|
62
|
+
# Public: Returns `true` if the passed-in `item` is present
|
|
63
|
+
# in the collection.
|
|
64
|
+
#
|
|
65
|
+
# item - The item {Object} to search in the collection.
|
|
66
|
+
#
|
|
67
|
+
# Returns a {Boolean} of whether the item is present
|
|
68
|
+
# in the collection or not.
|
|
69
|
+
@::[ "has#{ singularPostfix }" ] = (item) -> item in @[ plural ]
|
|
70
|
+
|
|
71
|
+
# Creates an alias for `has<Item>` named `contains<Item>`.
|
|
72
|
+
@alias "has#{ singularPostfix }", "contains#{ singularPostfix }"
|
|
73
|
+
|
|
74
|
+
# Public: Returns `true` if the collection has at least one item.
|
|
75
|
+
#
|
|
76
|
+
# Returns a {Boolean} of whether the collection has items or not.
|
|
77
|
+
@getter "has#{ pluralPostfix }", -> @[ plural ].length > 0
|
|
78
|
+
|
|
79
|
+
# Public: Adds `item` in the collection unless it's already present.
|
|
80
|
+
#
|
|
81
|
+
# item - The item {Object} to append to the collection.
|
|
82
|
+
#
|
|
83
|
+
# Returns the {Number} of items in the collection.
|
|
84
|
+
@::[ "add#{ singularPostfix }" ] = (item) ->
|
|
85
|
+
@[ plural ].push item unless @[ "has#{ singularPostfix }" ] item
|
|
86
|
+
@[ "#{ plural }Size" ]
|
|
87
|
+
|
|
88
|
+
# Public: Removes `item` from the collection.
|
|
89
|
+
#
|
|
90
|
+
# item - The item {Object} to remove from the collection.
|
|
91
|
+
#
|
|
92
|
+
# Returns the {Number} of items in the collection.
|
|
93
|
+
@::[ "remove#{ singularPostfix }" ] = (item) ->
|
|
94
|
+
if @[ "has#{ singularPostfix }" ] item
|
|
95
|
+
@[ plural ].splice @[ "find#{ singularPostfix }" ](item), 1
|
|
96
|
+
@[ "#{ plural }Size" ]
|
|
97
|
+
|
|
98
|
+
# Public: Returns the index at which `item` is stored
|
|
99
|
+
# in the collection. It returns `-1` if `item` can't be found.
|
|
100
|
+
#
|
|
101
|
+
# item - The item {Object} to search in the collection.
|
|
102
|
+
#
|
|
103
|
+
# Returns the index {Number} of the passed-in item or `-1`.
|
|
104
|
+
@::[ "find#{ singularPostfix }" ] = (item) -> @[ plural ].indexOf item
|
|
105
|
+
|
|
106
|
+
# Creates an alias for `find<Item>` named `indexOf<Item>`
|
|
107
|
+
@alias "find#{ singularPostfix }", "indexOf#{ singularPostfix }"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Public: The `HasNestedCollection` adds a property with named `name`
|
|
3
|
+
# that collects and concatenates all the descendants collections
|
|
4
|
+
# into a single array.
|
|
5
|
+
# It operates on classes that already includes the `HasCollection` mixin.
|
|
6
|
+
#
|
|
7
|
+
# ```coffeescript
|
|
8
|
+
# class Dummy
|
|
9
|
+
# @concern agt.mixins.HasCollection 'children', 'child'
|
|
10
|
+
# @concern agt.mixins.HasNestedCollection 'descendants', through: 'children'
|
|
11
|
+
#
|
|
12
|
+
# @descendantsScope 'activeDescendants', (descendant) -> descendant.active
|
|
13
|
+
#
|
|
14
|
+
# constructor: ->
|
|
15
|
+
# @children = []
|
|
16
|
+
# ```
|
|
17
|
+
#
|
|
18
|
+
# name - The {String} name of the nested collection accessor.
|
|
19
|
+
# options - The options {Object}:
|
|
20
|
+
# :through - The {String} name of the collection accessor
|
|
21
|
+
# to collect on the collection items the nested
|
|
22
|
+
# collections.
|
|
23
|
+
#
|
|
24
|
+
# Returns a {ConcreteHasNestedCollection} mixin.
|
|
25
|
+
agt.mixins.HasNestedCollection = (name, options={}) ->
|
|
26
|
+
|
|
27
|
+
# The collection is accessed with the named passed in the `through`option.
|
|
28
|
+
through = options.through
|
|
29
|
+
throw new Error('missing through option') unless through?
|
|
30
|
+
|
|
31
|
+
# Public: The concrete mixin as returned by the
|
|
32
|
+
# [HasNestedCollection](../files/mixins/has_nested_collection.coffee.html)
|
|
33
|
+
# generator.
|
|
34
|
+
class ConcreteHasNestedCollection
|
|
35
|
+
|
|
36
|
+
# Public: Creates a property on instances that filters the nested
|
|
37
|
+
# collections items using the passed-in `block`.
|
|
38
|
+
#
|
|
39
|
+
# scopeName - The {String} name for the scope.
|
|
40
|
+
# block - The {Function} filter of the scope.
|
|
41
|
+
@[ "#{ name }Scope" ] = (scopeName, block) ->
|
|
42
|
+
@getter scopeName, -> @[ name ].filter block, this
|
|
43
|
+
|
|
44
|
+
# Public: Returns a flat array containing all the items contained
|
|
45
|
+
# in all the nested collections.
|
|
46
|
+
@getter name, ->
|
|
47
|
+
items = []
|
|
48
|
+
@[ through ].forEach (item) ->
|
|
49
|
+
items.push(item)
|
|
50
|
+
items = items.concat(item[ name ]) if item[ name ]?
|
|
51
|
+
items
|