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,132 @@
|
|
|
1
|
+
|
|
2
|
+
# Internal: For a given function on an object it will find the property
|
|
3
|
+
# name and its kind (value/getter/setter).
|
|
4
|
+
findCaller = (caller, proto) ->
|
|
5
|
+
keys = Object.keys proto
|
|
6
|
+
|
|
7
|
+
for k in keys
|
|
8
|
+
descriptor = Object.getPropertyDescriptor proto, k
|
|
9
|
+
|
|
10
|
+
if descriptor?
|
|
11
|
+
return {key: k, descriptor, kind: 'value'} if descriptor.value is caller
|
|
12
|
+
return {key: k, descriptor, kind: 'get'} if descriptor.get is caller
|
|
13
|
+
return {key: k, descriptor, kind: 'set'} if descriptor.set is caller
|
|
14
|
+
else
|
|
15
|
+
return {key: k} if proto[k] is caller
|
|
16
|
+
|
|
17
|
+
{}
|
|
18
|
+
|
|
19
|
+
unless Object::super?
|
|
20
|
+
# Public: Gives access to the super method of any
|
|
21
|
+
Object.defineProperty Object.prototype, 'super', {
|
|
22
|
+
enumerable: false
|
|
23
|
+
configurable: true
|
|
24
|
+
value: (args...) ->
|
|
25
|
+
# To define which function to use as super when
|
|
26
|
+
# calling the `this.super` method we need to know which
|
|
27
|
+
# function is the caller.
|
|
28
|
+
caller = arguments.caller ? @super.caller
|
|
29
|
+
if caller?
|
|
30
|
+
# When the caller has a `__super__` property, we face
|
|
31
|
+
# a mixin method, we can access the `__super__` property
|
|
32
|
+
# to retrieve its super property.
|
|
33
|
+
if caller.__super__?
|
|
34
|
+
value = caller.__super__[caller.__included__.indexOf @constructor]
|
|
35
|
+
|
|
36
|
+
# The `this.super` method can be called only if the super
|
|
37
|
+
# is a function.
|
|
38
|
+
if value?
|
|
39
|
+
if typeof value is 'function'
|
|
40
|
+
value.apply(this, args)
|
|
41
|
+
else
|
|
42
|
+
throw new Error "The super for #{caller._name} isn't a function"
|
|
43
|
+
else
|
|
44
|
+
throw new Error "No super method for #{caller._name}"
|
|
45
|
+
|
|
46
|
+
# Without the `__super__` property we face a method declared
|
|
47
|
+
# in the including class and that may redefine a method from
|
|
48
|
+
# a mixin or a parent.
|
|
49
|
+
else
|
|
50
|
+
# The name of the property that stores the caller is retrieved.
|
|
51
|
+
# The `kind` variable is either `'value'`, `'get'`, `'set'`
|
|
52
|
+
# or `'null'`. It will be needed to find the correspondant
|
|
53
|
+
# super method in the property descriptor.
|
|
54
|
+
{key, kind} = findCaller caller, @constructor.prototype
|
|
55
|
+
|
|
56
|
+
# If the key is present we'll try to get a descriptor on the
|
|
57
|
+
# `__super__` class property.
|
|
58
|
+
if key?
|
|
59
|
+
desc = Object.getPropertyDescriptor @constructor.__super__, key
|
|
60
|
+
|
|
61
|
+
# And if a descriptor is available we get the function
|
|
62
|
+
# corresponding to the `kind` and call it with the arguments.
|
|
63
|
+
if desc?
|
|
64
|
+
value = desc[kind].apply(this, args)
|
|
65
|
+
|
|
66
|
+
# Otherwise, the value of the property is simply called.
|
|
67
|
+
else
|
|
68
|
+
value = @constructor.__super__[key].apply(this, args)
|
|
69
|
+
|
|
70
|
+
return value
|
|
71
|
+
|
|
72
|
+
# And in other cases an error is raised.
|
|
73
|
+
else
|
|
74
|
+
throw new Error "No super method for #{caller.name || caller._name}"
|
|
75
|
+
else
|
|
76
|
+
throw new Error "Super called with a caller"
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Public:
|
|
81
|
+
Object.defineProperty Function.prototype, 'super', {
|
|
82
|
+
enumerable: false
|
|
83
|
+
configurable: true
|
|
84
|
+
value: (args...) ->
|
|
85
|
+
caller = arguments.caller or @super.caller
|
|
86
|
+
if caller?
|
|
87
|
+
if caller.__super__?
|
|
88
|
+
value = caller.__super__[caller.__included__.indexOf this]
|
|
89
|
+
|
|
90
|
+
if value?
|
|
91
|
+
if typeof value is 'function'
|
|
92
|
+
value.apply(this, args)
|
|
93
|
+
else
|
|
94
|
+
throw new Error "The super for #{caller._name} isn't a function"
|
|
95
|
+
else
|
|
96
|
+
throw new Error "No super method for #{caller._name}"
|
|
97
|
+
|
|
98
|
+
else
|
|
99
|
+
# super method in the property descriptor.
|
|
100
|
+
{key, kind} = findCaller caller, this
|
|
101
|
+
|
|
102
|
+
reverseMixins = []
|
|
103
|
+
reverseMixins.unshift m for m in @__mixins__
|
|
104
|
+
|
|
105
|
+
# If the key is present we'll try to get a descriptor on the
|
|
106
|
+
# `__super__` class property.
|
|
107
|
+
if key?
|
|
108
|
+
for m in reverseMixins
|
|
109
|
+
if m[key]?
|
|
110
|
+
mixin = m
|
|
111
|
+
break
|
|
112
|
+
|
|
113
|
+
desc = Object.getPropertyDescriptor mixin, key
|
|
114
|
+
|
|
115
|
+
# And if a descriptor is available we get the function
|
|
116
|
+
# corresponding to the `kind` and call it with the arguments.
|
|
117
|
+
if desc?
|
|
118
|
+
value = desc[kind].apply(this, args)
|
|
119
|
+
|
|
120
|
+
# Otherwise, the value of the property is simply called.
|
|
121
|
+
else
|
|
122
|
+
value = mixin[key].apply(this, args)
|
|
123
|
+
|
|
124
|
+
return value
|
|
125
|
+
|
|
126
|
+
# And in other cases an error is raised.
|
|
127
|
+
else
|
|
128
|
+
throw new Error "No super class method for #{caller.name || caller._name}"
|
|
129
|
+
else
|
|
130
|
+
throw new Error "super called without a caller"
|
|
131
|
+
|
|
132
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
Math.PI2 = Math.PI * 2
|
|
3
|
+
Math.PI_2 = Math.PI / 2
|
|
4
|
+
Math.PI_4 = Math.PI / 4
|
|
5
|
+
Math.PI_8 = Math.PI / 8
|
|
6
|
+
|
|
7
|
+
### Public ###
|
|
8
|
+
|
|
9
|
+
Math.degToRad = (n) -> n * Math.PI / 180
|
|
10
|
+
|
|
11
|
+
Math.radToDeg = (n) -> n * 180 / Math.PI
|
|
12
|
+
|
|
13
|
+
Math.normalize = (value, minimum, maximum) ->
|
|
14
|
+
(value - minimum) / (maximum - minimum)
|
|
15
|
+
|
|
16
|
+
Math.interpolate = (normValue, minimum, maximum) ->
|
|
17
|
+
minimum + (maximum - minimum) * normValue
|
|
18
|
+
|
|
19
|
+
Math.deltaBelowRatio = (a, b, ratio=10000000000) -> Math.abs(a - b) < 1 / ratio
|
|
20
|
+
|
|
21
|
+
Math.map = (value, min1, max1, min2, max2) ->
|
|
22
|
+
Math.interpolate Math.normalize(value, min1, max1), min2, max2
|
|
23
|
+
|
|
24
|
+
# Returns true if the passed-in argument can be casted to a number.
|
|
25
|
+
#
|
|
26
|
+
# Math.isFloat 0.245 # true
|
|
27
|
+
# Math.isFloat '12' # true
|
|
28
|
+
# Math.isFloat 'foo' # false
|
|
29
|
+
Math.isFloat = (floats...) ->
|
|
30
|
+
return false for float in floats when isNaN parseFloat float
|
|
31
|
+
true
|
|
32
|
+
|
|
33
|
+
Math.isInt = (ints...) ->
|
|
34
|
+
return false for int in ints when isNaN parseInt int
|
|
35
|
+
true
|
|
36
|
+
|
|
37
|
+
# Returns the passed-in arguments casted into floats.
|
|
38
|
+
Math.asFloat = (floats...) ->
|
|
39
|
+
floats[i] = parseFloat n for n,i in floats
|
|
40
|
+
floats
|
|
41
|
+
|
|
42
|
+
# Returns the passed-in arguments casted into floats.
|
|
43
|
+
Math.asInt = (ints...) ->
|
|
44
|
+
ints[i] = parseInt n for n,i in ints
|
|
45
|
+
ints
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Public: The `Activable` mixin provides the basic interface for an activable
|
|
3
|
+
# widget. You can hook your own activation/deactivation routines by overriding
|
|
4
|
+
# the `activated` and `deactivated` methods.
|
|
5
|
+
#
|
|
6
|
+
# ```coffeescript
|
|
7
|
+
# class Dummy
|
|
8
|
+
# @include agt.mixins.Activable
|
|
9
|
+
#
|
|
10
|
+
# activated: ->
|
|
11
|
+
# # ...
|
|
12
|
+
#
|
|
13
|
+
# deactivated: ->
|
|
14
|
+
# # ...
|
|
15
|
+
# ```
|
|
16
|
+
#
|
|
17
|
+
# `Activable` instances are deactivated at creation.
|
|
18
|
+
class agt.mixins.Activable
|
|
19
|
+
active: false
|
|
20
|
+
|
|
21
|
+
# Public: Activates the instance.
|
|
22
|
+
activate: ->
|
|
23
|
+
return if @active
|
|
24
|
+
@active = true
|
|
25
|
+
@activated?()
|
|
26
|
+
|
|
27
|
+
# Public: Deactivates the instance.
|
|
28
|
+
deactivate: ->
|
|
29
|
+
return unless @active
|
|
30
|
+
@active = false
|
|
31
|
+
@deactivated?()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Public: Provides class methods to deal with aliased methods and properties.
|
|
3
|
+
#
|
|
4
|
+
# ```coffeescript
|
|
5
|
+
# class Dummy
|
|
6
|
+
# @extend agt.mixins.Aliasable
|
|
7
|
+
#
|
|
8
|
+
# someMethod: ->
|
|
9
|
+
# @alias 'someMethod', 'someMethodAlias'
|
|
10
|
+
# ```
|
|
11
|
+
class agt.mixins.Aliasable
|
|
12
|
+
|
|
13
|
+
# Public: Creates aliases for the given `source` property of tthe current
|
|
14
|
+
# class prototype. Any number of alias can be passed at once.
|
|
15
|
+
#
|
|
16
|
+
# source - The {String} name of the aliased property
|
|
17
|
+
# aliases - A list of {String}s to use as aliases.
|
|
18
|
+
@alias: (source, aliases...) ->
|
|
19
|
+
desc = Object.getPropertyDescriptor @prototype, source
|
|
20
|
+
|
|
21
|
+
if desc?
|
|
22
|
+
Object.defineProperty @prototype, alias, desc for alias in aliases
|
|
23
|
+
else
|
|
24
|
+
if @prototype[ source ]?
|
|
25
|
+
@prototype[ alias ] = @prototype[ source ] for alias in aliases
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Public: The `AlternateCase` mixin add methods to convert the properties
|
|
3
|
+
# of a class instance to camelCase or snake_case.
|
|
4
|
+
#
|
|
5
|
+
# The methods are available on the class itself and should be called
|
|
6
|
+
# after having declared all the class members.
|
|
7
|
+
#
|
|
8
|
+
# For instance, given the class below:
|
|
9
|
+
#
|
|
10
|
+
# ```coffeescript
|
|
11
|
+
# class Dummy
|
|
12
|
+
# @extend agt.mixins.AlternateCase
|
|
13
|
+
#
|
|
14
|
+
# someProperty: 'foo'
|
|
15
|
+
# someMethod: ->
|
|
16
|
+
#
|
|
17
|
+
# @snakify()
|
|
18
|
+
# ```
|
|
19
|
+
#
|
|
20
|
+
# An instance will have both `someProperty` and `someMethod` as defined
|
|
21
|
+
# by the class, but also `some_property` and `some_method`.
|
|
22
|
+
#
|
|
23
|
+
# The alternative is also possible. Given a class that uses snake_case
|
|
24
|
+
# to declare its member, the `camelize` method will provides the camelCase
|
|
25
|
+
# alternative to the class.
|
|
26
|
+
class agt.mixins.AlternateCase
|
|
27
|
+
|
|
28
|
+
# Public: Converts all the prototype properties to snake_case.
|
|
29
|
+
@snakify: -> @convert 'toSnakeCase'
|
|
30
|
+
|
|
31
|
+
# Public: Converts all the prototype properties to camelCase.
|
|
32
|
+
@camelize: -> @convert 'toCamelCase'
|
|
33
|
+
|
|
34
|
+
# Public: Adds the specified alternatives of each properties on the
|
|
35
|
+
# current prototype. The passed-in argument is the name of the class
|
|
36
|
+
# method to call to convert the key string.
|
|
37
|
+
#
|
|
38
|
+
# alternateCase - The {String} name of the class method to use
|
|
39
|
+
# to convert.
|
|
40
|
+
@convert: (alternateCase) ->
|
|
41
|
+
for key,value of @prototype
|
|
42
|
+
alternate = @[alternateCase] key
|
|
43
|
+
|
|
44
|
+
descriptor = Object.getPropertyDescriptor @prototype, key
|
|
45
|
+
|
|
46
|
+
if descriptor?
|
|
47
|
+
Object.defineProperty @prototype, alternate, descriptor
|
|
48
|
+
else
|
|
49
|
+
@prototype[alternate] = value
|
|
50
|
+
|
|
51
|
+
# Public: Converts a string to `snake_case`.
|
|
52
|
+
#
|
|
53
|
+
# str - The {String} to convert.
|
|
54
|
+
#
|
|
55
|
+
# Returns a {String} in `snake_case` .
|
|
56
|
+
@toSnakeCase: (str) ->
|
|
57
|
+
str.
|
|
58
|
+
replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
59
|
+
.split(/_+/g)
|
|
60
|
+
.join('_')
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
|
|
63
|
+
# Public: Converts a string to `camelCase`.
|
|
64
|
+
#
|
|
65
|
+
# str - The {String} to convert.
|
|
66
|
+
#
|
|
67
|
+
# Returns a {String} in `camelCase`.
|
|
68
|
+
@toCamelCase: (str) ->
|
|
69
|
+
a = str.toLowerCase().split(/[_\s-]/)
|
|
70
|
+
s = a.shift()
|
|
71
|
+
s = "#{ s }#{w.replace /^./, (s) -> s.toUpperCase()}" for w in a
|
|
72
|
+
s
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Internal: Contains all the function that will instanciate a class
|
|
3
|
+
# with a specific number of arguments. These functions are all generated
|
|
4
|
+
# at runtime with the `Function` constructor.
|
|
5
|
+
build = (klass, args) ->
|
|
6
|
+
BUILDS = (
|
|
7
|
+
new Function( "return new arguments[0](#{
|
|
8
|
+
("arguments[1][#{ j-1 }]" for j in [ 0..i ] when j isnt 0).join ","
|
|
9
|
+
});") for i in [ 0..24 ]
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
f = BUILDS[ if args? then args.length else 0 ]
|
|
13
|
+
f klass, args
|
|
14
|
+
|
|
15
|
+
# Public: A `Cloneable` object can return a copy of itself through its `clone`
|
|
16
|
+
# method.
|
|
17
|
+
#
|
|
18
|
+
# The `Cloneable` generator function produces a different mixin when called
|
|
19
|
+
# with or without arguments.
|
|
20
|
+
#
|
|
21
|
+
# When called without argument, the returned mixin creates a clone using
|
|
22
|
+
# a copy constructor (a constructor that initialize the current object
|
|
23
|
+
# with an object).
|
|
24
|
+
#
|
|
25
|
+
# ```coffeescript
|
|
26
|
+
# class Dummy
|
|
27
|
+
# @include agt.mixins.Cloneable()
|
|
28
|
+
#
|
|
29
|
+
# constructor: (options={}) ->
|
|
30
|
+
# @property = options.property or 'foo'
|
|
31
|
+
# @otherProperty = options.otherProperty or 'bar'
|
|
32
|
+
#
|
|
33
|
+
# instance = new Dummy
|
|
34
|
+
# otherInstance = instance.clone()
|
|
35
|
+
# # otherInstance = {property: 'foo', otherProperty: 'bar'}
|
|
36
|
+
# ```
|
|
37
|
+
#
|
|
38
|
+
# When called with arguments, the `clone` method will call the class
|
|
39
|
+
# constructor with the values extracted from the given properties.
|
|
40
|
+
#
|
|
41
|
+
# ```coffeescript
|
|
42
|
+
# class Dummy
|
|
43
|
+
# @include agt.mixins.Cloneable('property', 'otherProperty')
|
|
44
|
+
#
|
|
45
|
+
# constructor: (@property='foo', @otherProperty='bar') ->
|
|
46
|
+
#
|
|
47
|
+
# instance = new Dummy
|
|
48
|
+
# otherInstance = instance.clone()
|
|
49
|
+
# # otherInstance = {property: 'foo', otherProperty: 'bar'}
|
|
50
|
+
# ```
|
|
51
|
+
#
|
|
52
|
+
# properties - A list of {String} of the properties to pass in the constructor.
|
|
53
|
+
#
|
|
54
|
+
# Returns a {ConcreteCloneable} mixin configured with the passed-in arguments.
|
|
55
|
+
agt.mixins.Cloneable = (properties...) ->
|
|
56
|
+
|
|
57
|
+
# Public: The concrete cloneable mixin as created by the
|
|
58
|
+
# [Cloneable](../files/mixins/cloneable.coffee.html) generator.
|
|
59
|
+
class ConcreteCloneable
|
|
60
|
+
|
|
61
|
+
# Public: Returns a copy of this instance.
|
|
62
|
+
#
|
|
63
|
+
# When the mixin is configurated without any `properties` the cloned
|
|
64
|
+
# instance is passed directly as argument at the creation of the new
|
|
65
|
+
# instance.
|
|
66
|
+
#
|
|
67
|
+
# Returns an {Object}.
|
|
68
|
+
clone: -> new @constructor this
|
|
69
|
+
|
|
70
|
+
if properties.length > 0
|
|
71
|
+
ConcreteCloneable::clone = -> build @constructor, properties.map (p) => @[ p ]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
namespace('agt.mixins')
|
|
2
|
+
# Public: The `Delegation` mixin allow to define properties on an object that
|
|
3
|
+
# proxy another property of an object stored in one of its property.
|
|
4
|
+
#
|
|
5
|
+
# ```coffeescript
|
|
6
|
+
# class Dummy
|
|
7
|
+
# @extend agt.mixins.Delegation
|
|
8
|
+
#
|
|
9
|
+
# @delegate 'someProperty', to: 'someObject'
|
|
10
|
+
#
|
|
11
|
+
# constructor: ->
|
|
12
|
+
# @someObject = someProperty: 'some value'
|
|
13
|
+
#
|
|
14
|
+
# instance = new Dummy
|
|
15
|
+
# instance.someProperty
|
|
16
|
+
# # 'some value'
|
|
17
|
+
# ```
|
|
18
|
+
class agt.mixins.Delegation
|
|
19
|
+
|
|
20
|
+
# Public: The `delegate` class method generates a property on the current
|
|
21
|
+
# prototype that proxy the property of the given object.
|
|
22
|
+
#
|
|
23
|
+
# The `to` option specify the property of the object accessed by
|
|
24
|
+
# the delegated property.
|
|
25
|
+
#
|
|
26
|
+
# The delegated property name can be prefixed with the name of the
|
|
27
|
+
# accessed property
|
|
28
|
+
#
|
|
29
|
+
# ```coffeescript
|
|
30
|
+
# class Dummy
|
|
31
|
+
# @extend agt.mixins.Delegation
|
|
32
|
+
#
|
|
33
|
+
# @delegate 'someProperty', to: 'someObject', prefix: true
|
|
34
|
+
# # delegated property is named `someObjectSomeProperty`
|
|
35
|
+
# ```
|
|
36
|
+
#
|
|
37
|
+
# By default, using a prefix generates a camelCase property name.
|
|
38
|
+
# You can use the `case` option to change that to a snake_case property
|
|
39
|
+
# name.
|
|
40
|
+
#
|
|
41
|
+
# ```coffeescript
|
|
42
|
+
# class Dummy
|
|
43
|
+
# @extend agt.mixins.Delegation
|
|
44
|
+
#
|
|
45
|
+
# @delegate 'some_property', to: 'some_object', prefix: true, case: 'snake'
|
|
46
|
+
# # delegated property is named `some_object_some_property`
|
|
47
|
+
# ```
|
|
48
|
+
#
|
|
49
|
+
# The `delegate` method accept any number of properties to delegate
|
|
50
|
+
# with the same options.
|
|
51
|
+
#
|
|
52
|
+
# ```coffeescript
|
|
53
|
+
# class Dummy
|
|
54
|
+
# @extend agt.mixins.Delegation
|
|
55
|
+
#
|
|
56
|
+
# @delegate 'someProperty', 'someOtherProperty', to: 'someObject'
|
|
57
|
+
# ```
|
|
58
|
+
#
|
|
59
|
+
# properties - A list of {String} of the properties to delegate.
|
|
60
|
+
# options - The delegation options {Object}:
|
|
61
|
+
# :to - The {String} name of the target property.
|
|
62
|
+
# :prefix - A {Boolean} indicating whether to prefix the created
|
|
63
|
+
# delegated property name with the target property name.
|
|
64
|
+
# :case - An optional {String} to define the case to use to generate
|
|
65
|
+
# a prefixed delegated property.
|
|
66
|
+
@delegate: (properties..., options={}) ->
|
|
67
|
+
delegated = options.to
|
|
68
|
+
prefixed = options.prefix
|
|
69
|
+
_case = options.case or agt.CAMEL_CASE
|
|
70
|
+
|
|
71
|
+
properties.forEach (property) =>
|
|
72
|
+
localAlias = property
|
|
73
|
+
|
|
74
|
+
# Currently, only `camel`, and `snake` cases are supported.
|
|
75
|
+
if prefixed
|
|
76
|
+
switch _case
|
|
77
|
+
when agt.SNAKE_CASE
|
|
78
|
+
localAlias = delegated + '_' + property
|
|
79
|
+
when agt.CAMEL_CASE
|
|
80
|
+
localAlias = delegated + property.replace /^./, (m) ->
|
|
81
|
+
m.toUpperCase()
|
|
82
|
+
|
|
83
|
+
# The `Delegation` mixin rely on `Object.property` and thus can't
|
|
84
|
+
# be used on IE8.
|
|
85
|
+
Object.defineProperty @prototype, localAlias, {
|
|
86
|
+
enumerable: true
|
|
87
|
+
configurable: true
|
|
88
|
+
get: -> @[ delegated ][ property ]
|
|
89
|
+
set: (value) -> @[ delegated ][ property ] = value
|
|
90
|
+
}
|