agt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +7 -0
  3. data/README.md +5 -0
  4. data/app/assets/javascripts/agt/config.coffee +9 -0
  5. data/app/assets/javascripts/agt/dom.coffee +12 -0
  6. data/app/assets/javascripts/agt/function.coffee +317 -0
  7. data/app/assets/javascripts/agt/geom/circle.coffee +338 -0
  8. data/app/assets/javascripts/agt/geom/cubic_bezier.coffee +37 -0
  9. data/app/assets/javascripts/agt/geom/diamond.coffee +241 -0
  10. data/app/assets/javascripts/agt/geom/ellipsis.coffee +141 -0
  11. data/app/assets/javascripts/agt/geom/linear_spline.coffee +56 -0
  12. data/app/assets/javascripts/agt/geom/matrix.coffee +240 -0
  13. data/app/assets/javascripts/agt/geom/mixins/geometry.coffee +171 -0
  14. data/app/assets/javascripts/agt/geom/mixins/intersections.coffee +150 -0
  15. data/app/assets/javascripts/agt/geom/mixins/path.coffee +145 -0
  16. data/app/assets/javascripts/agt/geom/mixins/proxyable.coffee +9 -0
  17. data/app/assets/javascripts/agt/geom/mixins/spline.coffee +329 -0
  18. data/app/assets/javascripts/agt/geom/mixins/surface.coffee +55 -0
  19. data/app/assets/javascripts/agt/geom/mixins/triangulable.coffee +112 -0
  20. data/app/assets/javascripts/agt/geom/point.coffee +454 -0
  21. data/app/assets/javascripts/agt/geom/polygon.coffee +119 -0
  22. data/app/assets/javascripts/agt/geom/quad_bezier.coffee +43 -0
  23. data/app/assets/javascripts/agt/geom/quint_bezier.coffee +42 -0
  24. data/app/assets/javascripts/agt/geom/rectangle.coffee +599 -0
  25. data/app/assets/javascripts/agt/geom/spiral.coffee +90 -0
  26. data/app/assets/javascripts/agt/geom/transformation_proxy.coffee +43 -0
  27. data/app/assets/javascripts/agt/geom/triangle.coffee +442 -0
  28. data/app/assets/javascripts/agt/impulse.coffee +105 -0
  29. data/app/assets/javascripts/agt/index.coffee +56 -0
  30. data/app/assets/javascripts/agt/inflector/inflection.coffee +21 -0
  31. data/app/assets/javascripts/agt/inflector/inflections.coffee +75 -0
  32. data/app/assets/javascripts/agt/inflector/inflector.coffee +235 -0
  33. data/app/assets/javascripts/agt/inheritance.coffee +132 -0
  34. data/app/assets/javascripts/agt/math.coffee +45 -0
  35. data/app/assets/javascripts/agt/mixins/activable.coffee +31 -0
  36. data/app/assets/javascripts/agt/mixins/aliasable.coffee +25 -0
  37. data/app/assets/javascripts/agt/mixins/alternate_case.coffee +72 -0
  38. data/app/assets/javascripts/agt/mixins/cloneable.coffee +71 -0
  39. data/app/assets/javascripts/agt/mixins/delegation.coffee +90 -0
  40. data/app/assets/javascripts/agt/mixins/disposable.coffee +7 -0
  41. data/app/assets/javascripts/agt/mixins/equatable.coffee +37 -0
  42. data/app/assets/javascripts/agt/mixins/formattable.coffee +52 -0
  43. data/app/assets/javascripts/agt/mixins/globalizable.coffee +175 -0
  44. data/app/assets/javascripts/agt/mixins/has_ancestors.coffee +47 -0
  45. data/app/assets/javascripts/agt/mixins/has_collection.coffee +107 -0
  46. data/app/assets/javascripts/agt/mixins/has_nested_collection.coffee +51 -0
  47. data/app/assets/javascripts/agt/mixins/memoizable.coffee +64 -0
  48. data/app/assets/javascripts/agt/mixins/parameterizable.coffee +101 -0
  49. data/app/assets/javascripts/agt/mixins/poolable.coffee +62 -0
  50. data/app/assets/javascripts/agt/mixins/sourcable.coffee +45 -0
  51. data/app/assets/javascripts/agt/mixins/state_machine.coffee +47 -0
  52. data/app/assets/javascripts/agt/net/router.coffee +165 -0
  53. data/app/assets/javascripts/agt/object.coffee +9 -0
  54. data/app/assets/javascripts/agt/particles/actions/base_action.coffee +7 -0
  55. data/app/assets/javascripts/agt/particles/actions/die_on_surface.coffee +14 -0
  56. data/app/assets/javascripts/agt/particles/actions/force.coffee +11 -0
  57. data/app/assets/javascripts/agt/particles/actions/friction.coffee +14 -0
  58. data/app/assets/javascripts/agt/particles/actions/live.coffee +9 -0
  59. data/app/assets/javascripts/agt/particles/actions/macro_action.coffee +13 -0
  60. data/app/assets/javascripts/agt/particles/actions/move.coffee +11 -0
  61. data/app/assets/javascripts/agt/particles/actions/null_action.coffee +9 -0
  62. data/app/assets/javascripts/agt/particles/counters/by_rate.coffee +17 -0
  63. data/app/assets/javascripts/agt/particles/counters/fixed.coffee +9 -0
  64. data/app/assets/javascripts/agt/particles/counters/null_counter.coffee +9 -0
  65. data/app/assets/javascripts/agt/particles/emission.coffee +35 -0
  66. data/app/assets/javascripts/agt/particles/emitters/null_emitter.coffee +7 -0
  67. data/app/assets/javascripts/agt/particles/emitters/path.coffee +10 -0
  68. data/app/assets/javascripts/agt/particles/emitters/ponctual.coffee +9 -0
  69. data/app/assets/javascripts/agt/particles/emitters/surface.coffee +10 -0
  70. data/app/assets/javascripts/agt/particles/initializers/explosion.coffee +19 -0
  71. data/app/assets/javascripts/agt/particles/initializers/life.coffee +16 -0
  72. data/app/assets/javascripts/agt/particles/initializers/macro_initializer.coffee +10 -0
  73. data/app/assets/javascripts/agt/particles/initializers/null_initializer.coffee +7 -0
  74. data/app/assets/javascripts/agt/particles/initializers/particle_sub_system.coffee +12 -0
  75. data/app/assets/javascripts/agt/particles/initializers/stream.coffee +20 -0
  76. data/app/assets/javascripts/agt/particles/mixins/randomizable.coffee +10 -0
  77. data/app/assets/javascripts/agt/particles/particle.coffee +32 -0
  78. data/app/assets/javascripts/agt/particles/sub_system.coffee +11 -0
  79. data/app/assets/javascripts/agt/particles/system.coffee +103 -0
  80. data/app/assets/javascripts/agt/particles/timers/instant.coffee +11 -0
  81. data/app/assets/javascripts/agt/particles/timers/limited.coffee +19 -0
  82. data/app/assets/javascripts/agt/particles/timers/null_timer.coffee +11 -0
  83. data/app/assets/javascripts/agt/particles/timers/unlimited.coffee +9 -0
  84. data/app/assets/javascripts/agt/particles/timers/until_death.coffee +11 -0
  85. data/app/assets/javascripts/agt/promise.coffee +214 -0
  86. data/app/assets/javascripts/agt/random/random.coffee +100 -0
  87. data/app/assets/javascripts/agt/random/seeds/lagged_fibonnacci.coffee +53 -0
  88. data/app/assets/javascripts/agt/random/seeds/linear.coffee +16 -0
  89. data/app/assets/javascripts/agt/random/seeds/linear_congruential.coffee +23 -0
  90. data/app/assets/javascripts/agt/random/seeds/math_random.coffee +9 -0
  91. data/app/assets/javascripts/agt/random/seeds/mersenne_twister.coffee +42 -0
  92. data/app/assets/javascripts/agt/random/seeds/no_random.coffee +12 -0
  93. data/app/assets/javascripts/agt/random/seeds/paul_houle.coffee +19 -0
  94. data/app/assets/javascripts/agt/signal.coffee +272 -0
  95. data/app/assets/javascripts/agt/sprites/animation.coffee +13 -0
  96. data/app/assets/javascripts/agt/sprites/sprite.coffee +30 -0
  97. data/app/assets/javascripts/agt/widgets/hash.coffee +36 -0
  98. data/app/assets/javascripts/agt/widgets/widgets.coffee +194 -0
  99. data/app/assets/javascripts/agt/widgets/widgets/checked_input.coffee +7 -0
  100. data/app/assets/javascripts/agt/widgets/widgets/focus_bubbling.coffee +15 -0
  101. data/lib/agt.rb +8 -0
  102. data/lib/agt/version.rb +5 -0
  103. metadata +173 -0
@@ -0,0 +1,64 @@
1
+ namespace('agt.mixins')
2
+ # Public: A `Memoizable` object can store data resulting of heavy methods
3
+ # in order to speed up further call to that method.
4
+ #
5
+ # The invalidation of the memoized data is defined using a `memoizationKey`.
6
+ # That key should be generated based on the data that may induce changes
7
+ # in the functions's results.
8
+ #
9
+ # ```coffeescript
10
+ # class Dummy
11
+ # @include agt.mixins.Memoizable
12
+ #
13
+ # constructor: (@p1, @p2) ->
14
+ # # ...
15
+ #
16
+ # heavyMethod: (arg) ->
17
+ # key = "heavyMethod-#{arg}"
18
+ # return @memoFor key if @memoized key
19
+ #
20
+ # # do costly computation
21
+ # @memoize key, result
22
+ #
23
+ # memoizationKey: -> "#{p1};#{p2}"
24
+ # ```
25
+ class agt.mixins.Memoizable
26
+ # Public: Returns `true` if data are available for the given `prop`.
27
+ #
28
+ # When the current state of the object don't match the stored
29
+ # memoization key, the whole data stored in the memo are cleared.
30
+ #
31
+ # prop - The {String} name of a property.
32
+ #
33
+ # Retuns a {Boolean} of whether the value of the propery is memoized or not.
34
+ memoized: (prop) ->
35
+ if @memoizationKey() is @__memoizationKey__
36
+ @__memo__?[ prop ]?
37
+ else
38
+ @__memo__ = {}
39
+ false
40
+
41
+ # Public: Returns the memoized data for the given `prop`.
42
+ #
43
+ # prop - The {String} name of a property.
44
+ #
45
+ # Returns the memoized data for the given prop
46
+ memoFor: (prop) -> @__memo__[ prop ]
47
+
48
+ # Public: Register a memo in the current object for the given `prop`.
49
+ # The memoization key is updated with the current state of the
50
+ # object.
51
+ memoize: (prop, value) ->
52
+ @__memo__ ||= {}
53
+ @__memoizationKey__ = @memoizationKey()
54
+ @__memo__[ prop ] = value
55
+
56
+ # Public Abstract: Generates the memoization key for this
57
+ # instance's state.
58
+ #
59
+ # By default the memoization key of an object is the return of its `toString`
60
+ # method. **You SHOULD redefine the memoization key generation in the class
61
+ # including the `Memoizable` mixin.**
62
+ #
63
+ # Returns a {String} that identify the state of the current instance.
64
+ memoizationKey: -> @toString()
@@ -0,0 +1,101 @@
1
+ namespace('agt.mixins')
2
+ # Public: A `Parameterizable` object provides a class and an instance
3
+ # method to convert an arbitrary {Object} or a series of values into
4
+ # an instance of the class receiving the mixin.
5
+ #
6
+ # ```coffeescript
7
+ # class Dummy
8
+ # @include agt.mixins.Parameterizable('dummyFrom', {
9
+ # x: 0
10
+ # y: 0
11
+ # name: 'Untitled'
12
+ # })
13
+ #
14
+ # constructor: (@x, @y, @name) ->
15
+ #
16
+ # instance = new Dummy
17
+ #
18
+ # Dummy.instanceFrom(1,2,'foo')
19
+ # instance.instanceFrom(1,2,'foo')
20
+ # ```
21
+ #
22
+ # The methods can be used either with an object or with a list
23
+ # of values whose order is the order of the `parameters` object keys:
24
+ #
25
+ # ``` coffeescript
26
+ # Dummy.instanceFrom(1,2,'Foo') # [Dummy(x=1,y=2,name='Foo')]
27
+ # Dummy.instanceFrom(x: 1, y: 2, name: 'Foo') # [Dummy(x=1,y=2,name='Foo')]
28
+ # ```
29
+ #
30
+ # The last `instanceFrom` argument controls can be a {Boolean} of whether
31
+ # the method raises an exception when the creation arguments types mismatch:
32
+ #
33
+ # ``` coffeescript
34
+ # Dummy.instanceFrom(1,'foo','bar' true) # value for y doesn't match type number
35
+ # Dummy.instanceFrom(x: 1, y: 'foo', name: 'bar', true) # value for y doesn't match type number
36
+ # ```
37
+ #
38
+ # The only edge case is that if you have yourself defined a {Boolean} as last
39
+ # argument you'll always have to pass the `strict` argument when calling the
40
+ # methods.
41
+ #
42
+ # The `allowPartial` argument, when set to `true`, will prevent the method
43
+ # to fallback on the default values provided in the `parameters` object
44
+ # leaving the class constructor to deal with it.
45
+ #
46
+ # method - The {String} name of the method to create.
47
+ # parameters - An {Object} describing the arguments name, type and order.
48
+ # allowPartial - A {Boolean} of
49
+ agt.mixins.Parameterizable = (method, parameters, allowPartial=false) ->
50
+
51
+ # Public: The concrete mixin as returned by the
52
+ # [Parameterizable](../files/mixins/parameterizable.coffee.html) generator.
53
+ class ConcreteParameterizable
54
+
55
+ # Internal:
56
+ @included: (klass) ->
57
+ f = (args..., strict)->
58
+ (args.push(strict); strict = false) if typeof strict isnt 'boolean'
59
+ output = {}
60
+
61
+ o = arguments[ 0 ]
62
+ n = 0
63
+ firstArgumentIsObject = o? and typeof o is 'object'
64
+
65
+ for k,v of parameters
66
+ value = if firstArgumentIsObject then o[ k ] else arguments[ n++ ]
67
+ parameterType = typeof v
68
+
69
+ if strict
70
+ if typeof value is parameterType
71
+ output[ k ] = value
72
+ else
73
+ if parameterType is 'number'
74
+ value = parseFloat value
75
+
76
+ if isNaN value
77
+ throw new Error "value for #{ k } doesn't match type #{ parameterType}"
78
+ else
79
+ output[k] = value
80
+ else
81
+ throw new Error "value for #{ k } doesn't match type #{ parameterType}"
82
+
83
+ else
84
+ if value?
85
+ if parameterType is 'number'
86
+ value = parseFloat value
87
+
88
+ if isNaN value
89
+ output[ k ] = v unless allowPartial
90
+ else
91
+ output[ k ] = value
92
+ else
93
+ output[ k ] = value
94
+ else
95
+ output[ k ] = v unless allowPartial
96
+
97
+
98
+ output
99
+
100
+ klass[method] = f
101
+ klass::[method] = f
@@ -0,0 +1,62 @@
1
+ namespace('agt.mixins')
2
+ # Public: A `Poolable` class has the ability to manage a pool of instances
3
+ # and prevent the further creation of instances as long as unused ones
4
+ # are still present.
5
+ class agt.mixins.Poolable
6
+
7
+ # Internal: The two objects stores are created in the extended hook to avoid
8
+ # that all the class extending `Poolable` shares the same instances.
9
+ @extended: (klass) ->
10
+ klass.usedInstances = []
11
+ klass.unusedInstances = []
12
+
13
+ # Public: Resets all the instances pools.
14
+ @resetPools: ->
15
+ @usedInstances = []
16
+ @unusedInstances = []
17
+
18
+ # Public: The `get` method returns an instance of the class.
19
+ # If the class defines an `init` method, it will be called with the
20
+ # passed-in `options` {Object}.
21
+ #
22
+ # options - The option {Object} to use to setup the created instance.
23
+ #
24
+ # Returns an instance of the current class.
25
+ @get: (options={}) ->
26
+ # Either retrieve or create the instance.
27
+ if @unusedInstances.length > 0
28
+ instance = @unusedInstances.shift()
29
+ else
30
+ instance = new this
31
+
32
+ # Stores the instance in the used pool.
33
+ @usedInstances.push instance
34
+
35
+ # Init the instance and return it.
36
+ instance.init(options)
37
+ instance
38
+
39
+ # Public: The `release` method takes an instance and move
40
+ # it from the the used pool to the unused pool.
41
+ #
42
+ # instance - The instance of the current class.
43
+ @release: (instance) ->
44
+ return unless instance in @usedInstances
45
+
46
+ # The instance is removed from the used instances pool.
47
+ index = @usedInstances.indexOf(instance)
48
+ @usedInstances.splice(index, 1)
49
+
50
+ # And then moved to the unused instances one.
51
+ @unusedInstances.push instance
52
+
53
+ # Public: Default `init` implementation, just copy all the options
54
+ # in the instance.
55
+ #
56
+ # options - The setup {Object} for this instance.
57
+ init: (options={}) -> @[ k ] = v for k,v of options
58
+
59
+ # Public: Default `dispose` implementation, call the `release` method
60
+ # on the instance constructor. A proper implementation should
61
+ # take care of removing/cleaning all the instance properties.
62
+ dispose: -> @constructor.release(this)
@@ -0,0 +1,45 @@
1
+ namespace('agt.mixins')
2
+ # Public: A `Sourcable` object is an object that can return the source code
3
+ # to re-create it by code.
4
+ #
5
+ # ```coffeescript
6
+ # class Dummy
7
+ # @include agt.mixins.Sourcable('agt.geom.Dummy', 'p1', 'p2')
8
+ #
9
+ # constructor: (@p1, @p2) ->
10
+ #
11
+ # dummy = new Dummy(10,'foo')
12
+ # dummy.toSource() # "new agt.geom.Dummy(10,'foo')"
13
+ # ```
14
+ #
15
+ # name - The {String} path to the current class.
16
+ # signature - A list of {String} name of properties
17
+ agt.mixins.Sourcable = (name, signature...) ->
18
+
19
+ # Public: A concrete class is generated and returned by
20
+ # [Sourcable](../files/mixins/sourcable.coffee.html).
21
+ class ConcreteSourcable
22
+
23
+ # Internal: Generates the source for a property's value.
24
+ sourceFor = (value) ->
25
+ switch typeof value
26
+ when 'object'
27
+ isArray = Object::toString.call(value).indexOf('Array') isnt -1
28
+ if isArray
29
+ "[#{ value.map (el) -> sourceFor el }]"
30
+ else
31
+ if value.toSource?
32
+ value.toSource()
33
+ else
34
+ value
35
+ when 'string'
36
+ "'#{ value.replace "'", "\\'" }'"
37
+ else value
38
+
39
+ # Public: Returns the source code corresponding to the current instance.
40
+ #
41
+ # Returns a {String} with the source of the instance.
42
+ toSource: ->
43
+ args = (@[ arg ] for arg in signature).map (o) -> sourceFor o
44
+
45
+ "new #{ name }(#{ args.join ',' })"
@@ -0,0 +1,47 @@
1
+ namespace('agt.mixins')
2
+ class agt.mixins.StateMachine
3
+ @initial: (state) -> @initialState = state
4
+
5
+ @event: (name, block) ->
6
+ @::[name] = block
7
+
8
+ @state: (name, defines) ->
9
+ @states ?= {}
10
+ @stateProperties ?= []
11
+
12
+ @states[name] = defines
13
+ @::[name] = -> @state is name
14
+ @stateProperties.push(k) for k,v of defines when k not in @stateProperties
15
+
16
+ this
17
+
18
+ initializeStateMachine: -> @setState(@constructor.initialState)
19
+
20
+ transition: (options) ->
21
+ throw new Error("From option is mandatory") unless options.from?
22
+ throw new Error("To option is mandatory") unless options.to?
23
+
24
+ {from, to} = options
25
+ if from is 'all'
26
+ from = Object.keys(@constructor.states)
27
+ else
28
+ from = from.split(' ')
29
+
30
+ to = options.to
31
+
32
+ for f in from
33
+ unless @constructor.states[f]
34
+ throw new Error("From state '#{f}' does not match any defined state")
35
+
36
+ unless @constructor.states[to]
37
+ throw new Error("To state '#{to}' does not match any defined state")
38
+
39
+ if @state in from
40
+ @setState(to)
41
+ else
42
+ throw new Error "Can't transition from #{@state} to #{to}"
43
+
44
+ setState: (state) ->
45
+ @state = state
46
+ state = @constructor.states[@state]
47
+ @[key] = state[key] for key in @constructor.stateProperties
@@ -0,0 +1,165 @@
1
+ namespace('agt.net')
2
+ # Public: The router class allow to trigger view changes on url changes.
3
+ # Typically the url changes may be handled by the history API or a url hash
4
+ # handler.
5
+ # The routes the router will support are defined in the callback passed to
6
+ # the router constructor as in the example below:
7
+ #
8
+ # ```coffee
9
+ # router = new Router ->
10
+ # @match '/posts', ->
11
+ # # render the post index
12
+ #
13
+ # @match '/posts/:id', ({id}) ->
14
+ # # render a single post identified with an :id
15
+ # ```
16
+ class agt.net.Router
17
+ @extend agt.mixins.Aliasable
18
+
19
+ ### Public ###
20
+
21
+ # Creates a new router instance. The constructor takes a {Function}
22
+ # and call it in the constructor this the router as the context object
23
+ # of the call. This is generally inside the block {Function} that you'll
24
+ # define the route to support.
25
+ #
26
+ # block - The initialization {Function}.
27
+ constructor: (block) ->
28
+ @routes = {}
29
+ @beforeFilters = []
30
+ @afterFilters = []
31
+
32
+ block.call(this)
33
+ @buildRoutesHandlers()
34
+
35
+ # Registers a route on this router.
36
+ #
37
+ # A route is defined with a `path` {String}, starting with a `/` and that
38
+ # can contains variables patterns for dynamic routes.
39
+ #
40
+ # For instance, the following path `'/posts/:post_id/comments/:id'` sets
41
+ # that the route defines two variables `post_id` and `id` that can contains
42
+ # any value. It means that this route will match both `'/posts/1/comments/2'`
43
+ # or `'/posts/foo/comments/bar'`.
44
+ #
45
+ # To limit the legal values for a variable, you can pass a string
46
+ # in the option object with the name of the variable as key, such as in:
47
+ #
48
+ # ```coffee
49
+ # @match '/posts/:id', id: '(\d+)', ({id}) ->
50
+ # ```
51
+ #
52
+ # In the example above the route can only be matched if the content for the
53
+ # `id` variable in the path contains only numbers. It means that
54
+ # `'/posts/1/comments/2'` will be matched by the route but
55
+ # `'/posts/foo/comments/bar'` won't.
56
+ #
57
+ # path - The path {String} to support.
58
+ # options - An option {Object} that can contain properties matching the
59
+ # path variables and containing strings that will be used to
60
+ # construct the regexp String to test paths.
61
+ match: (path, options={}, handle) ->
62
+ [options, handle] = [{}, options] if typeof options is 'function'
63
+ path = path.replace /^\/|\/$/g, ''
64
+ @routes[path] = {handle, options}
65
+
66
+ @alias 'match', 'get'
67
+
68
+ # Registers a filter to be called before the route handler.
69
+ #
70
+ # ```coffee
71
+ # @beforeFilter (path, router) ->
72
+ # # ...
73
+ # ```
74
+ #
75
+ # filter - The {Function} to invoke before the route handler.
76
+ beforeFilter: (filter) -> @beforeFilters.push filter
77
+
78
+ # Registers a filter to be called after the route handler.
79
+ #
80
+ # ```coffee
81
+ # @afterFilter (path, router) ->
82
+ # # ...
83
+ # ```
84
+ #
85
+ # filter - The {Function} to invoke after the route handler.
86
+ afterFilter: (filter) -> @afterFilters.push filter
87
+
88
+ # Defines a handler for paths that doesn't match any routes.
89
+ #
90
+ # notFoundHandle - The {Function} to call for route not found.
91
+ notFound: (@notFoundHandle) ->
92
+
93
+ # Triggers a change of route with the passed-in path.
94
+ #
95
+ # A `route:changed` event is dispatched after all the filters and the
96
+ # handler was called.
97
+ #
98
+ # path - The path {String} to match.
99
+ goto: (path) ->
100
+ path = '/' if path is '.'
101
+ path = path.replace(/^\./, '')
102
+ path = path.replace(/\/$/, '') unless path is '/'
103
+ path = "/#{path}" if path.indexOf('/') isnt 0
104
+
105
+ handler = @findRoute(path)
106
+
107
+ @beforeFilters.forEach (filter) => filter(path, this)
108
+
109
+ if handler?
110
+ handler(path)
111
+ else
112
+ @notFoundHandle?({path})
113
+
114
+ @afterFilters.forEach (filter) => filter(path, this)
115
+
116
+ document.dispatchEvent agt.domEvent('route:changed', {path}) if document?
117
+
118
+ # Internal: Returns the {Function} to handle the passed-in `path`.
119
+ #
120
+ # path - The path {String} to match.
121
+ #
122
+ # Returns a {Function}.
123
+ findRoute: (path) ->
124
+ for k, {test, handle} of @routes
125
+ return handle if test(path)
126
+
127
+ # Internal: Builds the route handlers.
128
+ buildRoutesHandlers: ->
129
+ for path, data of @routes
130
+ @routes[path] = @buildRouteHandler(path, data)
131
+
132
+ # Internal: Build a single route handler using the information provided.
133
+ #
134
+ # path - A {String} of the route path pattern.
135
+ # options - An object with the following properties:
136
+ # :handle - The {Function} that will handle the route.
137
+ # :options - An {Object} that contains the path variables patterns.
138
+ buildRouteHandler: (path, {handle, options}) ->
139
+ pathArray = path.split '/'
140
+ pathRe = []
141
+ pathParams = []
142
+
143
+ for part in pathArray
144
+ params_re = /^:(.+)$/
145
+ if res = params_re.exec(part)
146
+ param_name = res[1]
147
+ pathRe.push options[param_name] ? '([^/]+)'
148
+ pathParams.push param_name
149
+ else
150
+ pathRe.push part
151
+
152
+ re = new RegExp('^/' + pathRe.join('/') + '$')
153
+
154
+ {
155
+ options
156
+ test: (path) -> re.test(path)
157
+ handle: (path) ->
158
+ params = {path: path}
159
+ res = re.exec(path)
160
+ if res? and res.length > 1
161
+ for pname,i in pathParams
162
+ params[pname] = decodeURI(res[i+1])
163
+
164
+ handle(params)
165
+ }