agt 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|