alpha-simprini-engine 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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +38 -0
- data/app/assets/javascripts/alpha_simprini.coffee +15 -0
- data/app/assets/javascripts/alpha_simprini/application.coffee +22 -0
- data/app/assets/javascripts/alpha_simprini/coffee_kup/binding.coffee +119 -0
- data/app/assets/javascripts/alpha_simprini/coffee_kup/helpers.coffee +17 -0
- data/app/assets/javascripts/alpha_simprini/coffee_kup/tags.coffee +7 -0
- data/app/assets/javascripts/alpha_simprini/collection.coffee +62 -0
- data/app/assets/javascripts/alpha_simprini/delegate.coffee +21 -0
- data/app/assets/javascripts/alpha_simprini/event.coffee +75 -0
- data/app/assets/javascripts/alpha_simprini/html.coffee +56 -0
- data/app/assets/javascripts/alpha_simprini/mixin.coffee +10 -0
- data/app/assets/javascripts/alpha_simprini/model.coffee +137 -0
- data/app/assets/javascripts/alpha_simprini/models/radio_selection_model.coffee +9 -0
- data/app/assets/javascripts/alpha_simprini/packer.coffee +93 -0
- data/app/assets/javascripts/alpha_simprini/template_helpers.coffee +8 -0
- data/app/assets/javascripts/alpha_simprini/view.coffee +182 -0
- data/app/assets/javascripts/alpha_simprini/views/canvas.coffee +2 -0
- data/app/assets/javascripts/alpha_simprini/views/horizontal_split.coffee +9 -0
- data/app/assets/javascripts/alpha_simprini/views/panel.coffee +2 -0
- data/app/assets/javascripts/alpha_simprini/views/splitter.coffee +2 -0
- data/app/assets/javascripts/alpha_simprini/views/stage.coffee +7 -0
- data/app/assets/javascripts/alpha_simprini/views/vertical_split.coffee +8 -0
- data/app/assets/javascripts/alpha_simprini/views/viewport.coffee +0 -0
- data/app/assets/javascripts/bundle.js +16416 -0
- data/app/assets/javascripts/lib/backbone.coffee +11 -0
- data/app/assets/javascripts/lib/koffeecup_backbone_databind.coffee +25 -0
- data/app/assets/javascripts/lib/module.coffee +15 -0
- data/app/assets/javascripts/lib/state_machine.coffee +33 -0
- data/app/assets/javascripts/lib/string.coffee +13 -0
- data/app/assets/javascripts/qunit.js +1513 -0
- data/app/assets/javascripts/test.coffee +4 -0
- data/app/assets/javascripts/test/alpha_simprini/coffee_kup/binding.coffee +118 -0
- data/app/assets/javascripts/test/alpha_simprini/coffee_kup/helpers.coffee +10 -0
- data/app/assets/javascripts/test/alpha_simprini/coffee_kup/tags.coffee +18 -0
- data/app/assets/javascripts/test/alpha_simprini/collection.coffee +7 -0
- data/app/assets/javascripts/test/alpha_simprini/event.coffee +43 -0
- data/app/assets/javascripts/test/alpha_simprini/model.coffee +63 -0
- data/app/assets/javascripts/test/alpha_simprini/view.coffee +72 -0
- data/app/assets/javascripts/test/alpha_simprini/views/canvas.coffee +8 -0
- data/app/assets/javascripts/test/alpha_simprini/views/horizontal_split.coffee +13 -0
- data/app/assets/javascripts/test/alpha_simprini/views/vertical_split.coffee +13 -0
- data/app/assets/javascripts/test/backbone/collection.js +345 -0
- data/app/assets/javascripts/test/backbone/events.js +86 -0
- data/app/assets/javascripts/test/backbone/model.js +426 -0
- data/app/assets/javascripts/test/backbone/view.js +137 -0
- data/app/assets/stylesheets/bootstrap.sass +6 -0
- data/app/assets/stylesheets/paneling.sass +2 -0
- data/app/assets/stylesheets/qunit.scss +228 -0
- data/app/assets/stylesheets/table.sass +10 -0
- data/app/assets/stylesheets/typography.sass +19 -0
- data/app/controllers/alpha_simprini/test_controller.rb +3 -0
- data/app/controllers/resources_controller.rb +7 -0
- data/app/views/alpha_simprini/test/root.rb +13 -0
- data/app/views/resources/base.rb +2 -0
- data/app/views/resources/edit.rb +9 -0
- data/app/views/resources/form.rb +26 -0
- data/app/views/resources/index.rb +41 -0
- data/app/views/resources/new.rb +9 -0
- data/app/views/resources/show.rb +42 -0
- data/app/views/resources/show_has_many.rb +28 -0
- data/config/locales/en.yml +4 -0
- data/config/routes.rb +3 -0
- data/lib/alpha-simprini-engine.rb +33 -0
- data/lib/alpha-simprini-engine/version.rb +3 -0
- data/lib/alpha_simprini/directive_processor.rb +77 -0
- data/lib/alpha_simprini/packer.rb +161 -0
- data/lib/alpha_simprini/page.rb +37 -0
- data/lib/tasks/alpha-simprini-engine_tasks.rake +4 -0
- data/test/alpha-simprini-engine_test.rb +7 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +42 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +27 -0
- data/test/dummy/config/environments/production.rb +51 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +4 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/log/development.log +800 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C24/D70/sprockets%2F388a550030239f3784f207fa32954f64 +0 -0
- data/test/dummy/tmp/cache/assets/C34/620/sprockets%2F097ee8532856d48d70420c006f475889 +0 -0
- data/test/dummy/tmp/cache/assets/C6F/710/sprockets%2F908d18b0f21c18dad508851126164a41 +0 -0
- data/test/dummy/tmp/cache/assets/CA7/120/sprockets%2F35278a6c0150c40394fe02629c8b0eb9 +0 -0
- data/test/dummy/tmp/cache/assets/CDB/A80/sprockets%2F443b1c6e71b482fdd83b1833b7e27712 +0 -0
- data/test/dummy/tmp/cache/assets/CE3/FB0/sprockets%2F6d3f87a4232e0368bb86760780c5f1bd +0 -0
- data/test/dummy/tmp/cache/assets/D1C/160/sprockets%2F87876f7e55ebc78ae11c6c08a27f0605 +0 -0
- data/test/dummy/tmp/cache/assets/D26/6C0/sprockets%2F65bce2254bb3140a50326ea1654ea5df +0 -0
- data/test/dummy/tmp/cache/assets/D26/FA0/sprockets%2F1f50f59148df5f3e2ab889df381d9744 +0 -0
- data/test/dummy/tmp/cache/assets/D36/220/sprockets%2F841654b01d89eada195d7d1fe10b57b0 +0 -0
- data/test/dummy/tmp/cache/assets/D47/410/sprockets%2Ff1fe319eb4064bd5b861ac28f7d76555 +18122 -0
- data/test/dummy/tmp/cache/assets/D57/1E0/sprockets%2Fe267e3e1059799c429e4f5cfb95ec53c +0 -0
- data/test/dummy/tmp/cache/assets/D64/3E0/sprockets%2Fb49b7f8034b81ec601bc32fb127fbe08 +0 -0
- data/test/dummy/tmp/cache/assets/D65/850/sprockets%2F7a9b6a0e2d51adb43a1d3f859213e6b8 +15469 -0
- data/test/dummy/tmp/cache/assets/D6C/E30/sprockets%2Fcc7322b05ac2bef75e9f4f145d38117f +0 -0
- data/test/dummy/tmp/cache/assets/D7D/E00/sprockets%2Fce33b82451c73df1cfec69928b4f77b6 +0 -0
- data/test/dummy/tmp/cache/assets/D85/5D0/sprockets%2Fcf743946c378c148b19ce78eeaa9cc75 +0 -0
- data/test/dummy/tmp/cache/assets/DBB/560/sprockets%2F5a1d70cf7dd81a5ab5ce7a40a3a38d06 +0 -0
- data/test/dummy/tmp/cache/assets/DC1/670/sprockets%2Fefdfad0542298a1db90d24dc27ecc250 +0 -0
- data/test/dummy/tmp/cache/assets/DC5/1E0/sprockets%2F7517f22ad0f9dc13b8f0f6e0cc0e5de2 +0 -0
- data/test/dummy/tmp/cache/assets/DCC/220/sprockets%2F28d953c6ab9cc9a7db15600aabc6d77d +208 -0
- data/test/dummy/tmp/cache/assets/DCF/DF0/sprockets%2Ff0d3068f05825e1dfd9f5aaba946d3dc +0 -0
- data/test/dummy/tmp/cache/assets/DD3/0E0/sprockets%2F3e9e08a334bae7deadb5335f5e4f195f +0 -0
- data/test/dummy/tmp/cache/assets/DDB/980/sprockets%2Fbd2abc80a17c901e2f497f88f9be58cf +0 -0
- data/test/dummy/tmp/cache/assets/E00/ED0/sprockets%2Ffecf3bffeb200a47e96bd04cbc878076 +1535 -0
- data/test/dummy/tmp/cache/assets/EFB/560/sprockets%2Fbca0daf751c020cdeddabbefa51cb6ea +0 -0
- data/test/dummy/tmp/restart.txt +0 -0
- data/test/test_helper.rb +10 -0
- metadata +326 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
@module "AS.CK.Binding"
|
|
2
|
+
render = (locals, template) ->
|
|
3
|
+
locals.hardcode = AS.TemplateHelpers
|
|
4
|
+
jQuery CoffeeKup.render template, locals
|
|
5
|
+
|
|
6
|
+
add_remove_do = (elements..., fn) ->
|
|
7
|
+
element.appendTo(document.body) for element in elements
|
|
8
|
+
fn.call()
|
|
9
|
+
element.remove() for element in elements
|
|
10
|
+
|
|
11
|
+
test "bind", ->
|
|
12
|
+
model = new AS.Model
|
|
13
|
+
model.set property:"value"
|
|
14
|
+
|
|
15
|
+
bound = render model:model, ->
|
|
16
|
+
div -> bind @model, "property"
|
|
17
|
+
|
|
18
|
+
bound_get = render model:model, ->
|
|
19
|
+
div -> bind @model, "property", -> "#{this}-GOT"
|
|
20
|
+
|
|
21
|
+
add_remove_do bound, bound_get, =>
|
|
22
|
+
equal bound.find("span").text(), "value", "binds initial value to a span"
|
|
23
|
+
equal bound_get.find("span").text(), "value-GOT", "processes value through getter"
|
|
24
|
+
model.set property:"new"
|
|
25
|
+
equal bound.find("span").text(), "new", "updates value in place when bound property changes"
|
|
26
|
+
equal bound_get.find("span").text(), "new-GOT", "processes updated value in place through getter"
|
|
27
|
+
model.destroy()
|
|
28
|
+
ok bound.find("span").get(0) is undefined, "remove binding from dom when model is destroyed"
|
|
29
|
+
ok bound_get.find("span").get(0) is undefined, "removes binding from dom when model is destroyed"
|
|
30
|
+
|
|
31
|
+
test "bound_input", ->
|
|
32
|
+
model = new AS.Model
|
|
33
|
+
model.set property:"value"
|
|
34
|
+
|
|
35
|
+
bound = render model:model, ->
|
|
36
|
+
div -> bound_input @model, "property"
|
|
37
|
+
|
|
38
|
+
add_remove_do bound, =>
|
|
39
|
+
input = bound.find("input")
|
|
40
|
+
equal input.val(), "value", "binds initial value to input"
|
|
41
|
+
|
|
42
|
+
model.set property:"new"
|
|
43
|
+
equal input.val(), "new", "updates input when model changes"
|
|
44
|
+
|
|
45
|
+
input.val("keyup")
|
|
46
|
+
input.trigger("keyup")
|
|
47
|
+
equal model.get("property"), "keyup", "updates model on keyup of input"
|
|
48
|
+
|
|
49
|
+
input.val("change")
|
|
50
|
+
input.trigger("change")
|
|
51
|
+
equal model.get("property"), "change", "updates model on change of input"
|
|
52
|
+
|
|
53
|
+
test "bound_select", ->
|
|
54
|
+
model = new AS.Model
|
|
55
|
+
options = new AS.Collection
|
|
56
|
+
option = new AS.Model value:"option1"
|
|
57
|
+
added_option = new AS.Model value:"option2"
|
|
58
|
+
options.add option
|
|
59
|
+
|
|
60
|
+
bound = render model:model, options:options, ->
|
|
61
|
+
div -> bound_select @model, "property", @options, "value"
|
|
62
|
+
|
|
63
|
+
add_remove_do bound, =>
|
|
64
|
+
select = bound.find("select")
|
|
65
|
+
equal select.find("[value=#{option.cid}]option").text(), "option1", "renders given options from collection"
|
|
66
|
+
|
|
67
|
+
options.add added_option
|
|
68
|
+
added_option_node = select.find("[value=#{added_option.cid}]option")
|
|
69
|
+
equal added_option_node.text(), "option2", "adds option to select when item added to collection"
|
|
70
|
+
|
|
71
|
+
select.val(added_option.cid)
|
|
72
|
+
select.trigger("change")
|
|
73
|
+
equal model.get("property"), added_option, "sets value on model when property chosen"
|
|
74
|
+
|
|
75
|
+
model.set property:option
|
|
76
|
+
equal select.val(), option.cid, "changes value of select when property set on model"
|
|
77
|
+
|
|
78
|
+
option.set value:"changed option"
|
|
79
|
+
equal select.find("[value=#{option.cid}]option").text(), "changed option", "changes select text when option model value changes"
|
|
80
|
+
|
|
81
|
+
options.remove option
|
|
82
|
+
equal select.find("[value=#{option.cid}]option").get(0), undefined, "removes option from select when item added to collection"
|
|
83
|
+
equal model.get("property"), added_option, "reselects another option when option removed"
|
|
84
|
+
|
|
85
|
+
test "bound_select with blank", ->
|
|
86
|
+
model = new AS.Model
|
|
87
|
+
options = new AS.Collection
|
|
88
|
+
option = new AS.Model value:"option1"
|
|
89
|
+
options.add option
|
|
90
|
+
|
|
91
|
+
with_blank = render model:model, options:options, ->
|
|
92
|
+
div -> bound_select @model, "property", @options, "value", blank:"blank"
|
|
93
|
+
|
|
94
|
+
add_remove_do with_blank, =>
|
|
95
|
+
select = with_blank.find("select")
|
|
96
|
+
equal select.find("option:first").text(), "blank", "includes blank option"
|
|
97
|
+
equal select.val(), "blank", "starts out without selection selected"
|
|
98
|
+
equal model.get("property"), undefined, "leaves model property clear"
|
|
99
|
+
|
|
100
|
+
test "bound_select with preselect", ->
|
|
101
|
+
model = new AS.Model
|
|
102
|
+
options = new AS.Collection
|
|
103
|
+
option = new AS.Model value:"option1"
|
|
104
|
+
options.add option
|
|
105
|
+
model.set property:option
|
|
106
|
+
|
|
107
|
+
with_preselect = render model:model, options:options, ->
|
|
108
|
+
div -> bound_select @model, "property", @options, "value", blank:"blank"
|
|
109
|
+
|
|
110
|
+
add_remove_do with_preselect, =>
|
|
111
|
+
select = with_preselect.find("select")
|
|
112
|
+
equal select.find("option:first").text(), "blank", "includes blank option"
|
|
113
|
+
equal select.val(), option.cid, "starts out with selection selected"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module "AS.CK.Helpers"
|
|
2
|
+
test "autoId", ->
|
|
3
|
+
id = AS.CK.Helpers.autoId()
|
|
4
|
+
ok id.string != "", "selector is not blank"
|
|
5
|
+
ok id.string != undefined, "selector is defined"
|
|
6
|
+
equal id.query().selector, "##{id.string}", "gives id as a string and a selector"
|
|
7
|
+
|
|
8
|
+
test "shy", ->
|
|
9
|
+
shy = AS.CK.Helpers.shy("ThisShallShy")
|
|
10
|
+
equal shy, "This­Shall­Shy", "adds shy markers to titlecase text"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@module "AS.CK.Tags"
|
|
2
|
+
render = (locals, template) ->
|
|
3
|
+
locals.hardcode = AS.TemplateHelpers
|
|
4
|
+
jQuery CoffeeKup.render template, locals
|
|
5
|
+
|
|
6
|
+
test "model tag", ->
|
|
7
|
+
model = new AS.Model
|
|
8
|
+
defaults = render model:model, -> model_tag @model
|
|
9
|
+
tagnamed = render model:model, -> model_tag @model, tagname: "ul"
|
|
10
|
+
content = render model:model, ->
|
|
11
|
+
model_tag @model, -> p "content"
|
|
12
|
+
optioned = render model:model, ->
|
|
13
|
+
model_tag @model, key:"value"
|
|
14
|
+
|
|
15
|
+
ok defaults.is("div##{model.cid}"), "renders div with model cid by default"
|
|
16
|
+
ok tagnamed.is("ul##{model.cid}"), "renders tagname when given as an option"
|
|
17
|
+
equal content.find("p").text(), "content", "renders nested content when given as a function"
|
|
18
|
+
ok optioned.is("[key=value]"), "renders other options passed"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
module "AS.Collection"
|
|
2
|
+
test "adding a model to a collection with @inverse and @source property specified", ->
|
|
3
|
+
collection = new (AS.Collection.extend(inverse: "inverse", source: "source"))
|
|
4
|
+
model = new AS.Model
|
|
5
|
+
|
|
6
|
+
collection.add model
|
|
7
|
+
equal model.get('inverse'), "source", "sets collection to property on model"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
for klass in [AS.Model, AS.Collection, AS.View]
|
|
2
|
+
module "#{klass.name} Event Extensions"
|
|
3
|
+
class of_prototype
|
|
4
|
+
of_prototype.prototype = klass.prototype
|
|
5
|
+
|
|
6
|
+
test "events bound are triggered by event with and without namespace", ->
|
|
7
|
+
proof = false
|
|
8
|
+
object = new of_prototype
|
|
9
|
+
object.bind "event.namespace", -> proof = true
|
|
10
|
+
object.trigger "event"
|
|
11
|
+
ok proof, "triggers without namespace"
|
|
12
|
+
|
|
13
|
+
proof = false
|
|
14
|
+
object.trigger "event.namespace"
|
|
15
|
+
ok proof, "triggers with namespace"
|
|
16
|
+
|
|
17
|
+
test "events bound without namespace are not triggered by namespace", ->
|
|
18
|
+
proof = false
|
|
19
|
+
all_proof = false
|
|
20
|
+
object = new of_prototype
|
|
21
|
+
object.bind "all", -> all_proof = true
|
|
22
|
+
object.bind "event", -> proof = true
|
|
23
|
+
object.trigger "event.namespace"
|
|
24
|
+
ok !proof, "doesn't trigger with namespace"
|
|
25
|
+
ok all_proof, "triggers all event"
|
|
26
|
+
|
|
27
|
+
test "unbind with namespace", ->
|
|
28
|
+
proof = false
|
|
29
|
+
other_proof = false
|
|
30
|
+
none_proof = false
|
|
31
|
+
object = new of_prototype
|
|
32
|
+
object.bind "event.namespace", -> proof = true
|
|
33
|
+
object.bind "event.other_namespace", -> other_proof = true
|
|
34
|
+
object.bind "event", -> none_proof = true
|
|
35
|
+
|
|
36
|
+
object.unbind "event.namespace"
|
|
37
|
+
|
|
38
|
+
object.trigger "event"
|
|
39
|
+
|
|
40
|
+
ok !proof, "clears binding for namespace"
|
|
41
|
+
ok other_proof, "doesn't clear binding for other namespace"
|
|
42
|
+
ok none_proof, "doesn't clear binding for no namespace"
|
|
43
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module "AS.Model"
|
|
2
|
+
test "All", ->
|
|
3
|
+
model = new AS.Model
|
|
4
|
+
equal AS.All.getByCid(model.cid), model, "All models are automatically addded to the All collection"
|
|
5
|
+
|
|
6
|
+
test "has_many", ->
|
|
7
|
+
class AS.HasManyModel extends AS.Model
|
|
8
|
+
@has_many 'things', model: -> "AS.HadModel"
|
|
9
|
+
class AS.HadModel extends AS.Model
|
|
10
|
+
|
|
11
|
+
deepEqual AS.HasManyModel.associations, ["things"], "stores the association in the association list"
|
|
12
|
+
ok AS.HasManyModel.has_manys.things, "stores a configuration for the association"
|
|
13
|
+
|
|
14
|
+
equal AS.HasManyModel.has_manys.things.model, undefined, "association collection configuration is lazy to allow associated class to load"
|
|
15
|
+
model = new AS.HasManyModel
|
|
16
|
+
equal AS.HasManyModel.has_manys.things.model, AS.HadModel, "configures the collection class properly when needed"
|
|
17
|
+
|
|
18
|
+
equal model.get("things").model, AS.HadModel, "association collection is auto-created"
|
|
19
|
+
|
|
20
|
+
model.get("things").add {}
|
|
21
|
+
|
|
22
|
+
equal model.get("things").at(0).constructor, AS.HadModel, "items added to the association are properly instantiated"
|
|
23
|
+
|
|
24
|
+
model2 = new AS.HasManyModel
|
|
25
|
+
things: [{}]
|
|
26
|
+
|
|
27
|
+
equal model2.get("things").at(0).constructor, AS.HadModel, "items passed to constructor for the association are properly instantiated"
|
|
28
|
+
|
|
29
|
+
test "belongs to", ->
|
|
30
|
+
class AS.Belonging extends AS.Model
|
|
31
|
+
@belongs_to "parent"
|
|
32
|
+
class AS.Owner extends AS.Model
|
|
33
|
+
|
|
34
|
+
deepEqual AS.Belonging.associations, ["parent"], "stores the association on the class"
|
|
35
|
+
|
|
36
|
+
ok AS.Belonging.belongs_to.parent, "stores a configuration object for the association on the class"
|
|
37
|
+
|
|
38
|
+
owner = new AS.Owner id:"OWNERID"
|
|
39
|
+
model = new AS.Belonging
|
|
40
|
+
|
|
41
|
+
model.set parent:owner
|
|
42
|
+
|
|
43
|
+
equal model.get("parent"), owner, "correctly sets the owner"
|
|
44
|
+
|
|
45
|
+
model = new AS.Belonging
|
|
46
|
+
|
|
47
|
+
model.set parent:owner.get("id")
|
|
48
|
+
equal model.get("parent"), owner, "correctly sets the from a string id"
|
|
49
|
+
|
|
50
|
+
proof = false
|
|
51
|
+
model.bind "change:parent", -> proof = true
|
|
52
|
+
owner.set some:"value"
|
|
53
|
+
|
|
54
|
+
ok proof, "association changes trigger on the child"
|
|
55
|
+
|
|
56
|
+
proof = false
|
|
57
|
+
new_owner = new AS.Owner
|
|
58
|
+
model.set parent: new_owner
|
|
59
|
+
new_owner.set some:"other value"
|
|
60
|
+
|
|
61
|
+
ok proof, "changes trigger when associated model is changed"
|
|
62
|
+
|
|
63
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module "AS.View"
|
|
2
|
+
test "::klass_string()", ->
|
|
3
|
+
equal new AS.View().klass_string(), "ASView", "basic klass_string is ASView"
|
|
4
|
+
|
|
5
|
+
class SomeView extends AS.View
|
|
6
|
+
|
|
7
|
+
equal new SomeView().klass_string(), "ASView.SomeView", "subclasses include parent class string"
|
|
8
|
+
|
|
9
|
+
test "::element_string()", ->
|
|
10
|
+
equal new AS.View().element_string(), "div.ASView", "defaults to a div + klass_string"
|
|
11
|
+
equal new AS.View(tagName:"tagname").element_string(), "tagname.ASView", "allows setting of a tag"
|
|
12
|
+
|
|
13
|
+
model = {cid: "cid"}
|
|
14
|
+
|
|
15
|
+
equal new AS.View(model:model).element_string(), "div.ASView#cid.Object", "uses the cid/class of an associated model as the dom id/class"
|
|
16
|
+
|
|
17
|
+
test "::build_element()", ->
|
|
18
|
+
ok new AS.View().build_element().is(new AS.View().element_string()), "uses jQuery.satisfy to build the element"
|
|
19
|
+
model = {cid: "cid"}
|
|
20
|
+
ok new AS.View(model:model).build_element().is(new AS.View(model:model).element_string()), "with more complex element"
|
|
21
|
+
|
|
22
|
+
test "element is jquery wrapped", ->
|
|
23
|
+
equal new AS.View().el.constructor, jQuery
|
|
24
|
+
|
|
25
|
+
test "configuration", ->
|
|
26
|
+
view = new AS.View option:"OPTION"
|
|
27
|
+
equal view.option, "OPTION", "allow setting of arbitrary options from constructor"
|
|
28
|
+
|
|
29
|
+
test "event delegation", ->
|
|
30
|
+
click_proof = false
|
|
31
|
+
member_proof = false
|
|
32
|
+
button_proof = false
|
|
33
|
+
guard_pass_proof = false
|
|
34
|
+
guard_fail_proof = false
|
|
35
|
+
|
|
36
|
+
class BoundView extends AS.View
|
|
37
|
+
constructor: ->
|
|
38
|
+
@member = new AS.Model
|
|
39
|
+
super
|
|
40
|
+
@el.append @button = jQuery.satisfy("button")
|
|
41
|
+
@render()
|
|
42
|
+
|
|
43
|
+
events:
|
|
44
|
+
"click": "click_handler"
|
|
45
|
+
"click button": "button_handler"
|
|
46
|
+
"event @member": "member_handler"
|
|
47
|
+
"pass{pass:true} @member": "guard_pass_handler"
|
|
48
|
+
"fail{pass:false} @member": "guard_fail_handler"
|
|
49
|
+
|
|
50
|
+
click_handler: -> click_proof = true
|
|
51
|
+
member_handler: -> member_proof = true
|
|
52
|
+
button_handler: -> button_proof = true
|
|
53
|
+
guard_fail_handler: -> guard_fail_proof = true
|
|
54
|
+
guard_pass_handler: -> guard_pass_proof = true
|
|
55
|
+
|
|
56
|
+
render: -> @el.appendTo(document.body)
|
|
57
|
+
|
|
58
|
+
view = new BoundView
|
|
59
|
+
|
|
60
|
+
view.el.click()
|
|
61
|
+
view.member.trigger("event")
|
|
62
|
+
view.button.click()
|
|
63
|
+
view.member.trigger("pass", pass: true)
|
|
64
|
+
view.member.trigger("fail", pass: true)
|
|
65
|
+
|
|
66
|
+
ok click_proof, "binds simple dom events to view element"
|
|
67
|
+
ok member_proof, "binds to events of member objects"
|
|
68
|
+
ok button_proof, "binds to events of elements inside view"
|
|
69
|
+
ok guard_pass_proof, "correct values get past the guard"
|
|
70
|
+
ok !guard_fail_proof, "incorrect values blocked by the guard"
|
|
71
|
+
|
|
72
|
+
view.el.remove()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module "AS.Views.Stage"
|
|
2
|
+
test "is a Panel", ->
|
|
3
|
+
ok (new AS.Views.Stage).el.is(".Panel")
|
|
4
|
+
test "Comes with a canvas inside it", ->
|
|
5
|
+
view = new AS.Views.Stage
|
|
6
|
+
ok view.el.find(".Canvas").is(".ASView")
|
|
7
|
+
test "allows setting any canvas", ->
|
|
8
|
+
equal (new AS.Views.Stage canvas:"canvas").canvas, "canvas"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module "AS.Views.HorizontalSplit"
|
|
2
|
+
test "contains left, right and splitter elemens", ->
|
|
3
|
+
view = new AS.Views.HorizontalSplit()
|
|
4
|
+
|
|
5
|
+
equal view.el.find(".Panel").length, 2, "contains two panels"
|
|
6
|
+
ok view.el.find(":nth-child(2)").is(".Splitter"), "with a splitter in the middle"
|
|
7
|
+
|
|
8
|
+
test "allows configurable left, right and splitter elements", ->
|
|
9
|
+
view = new AS.Views.HorizontalSplit
|
|
10
|
+
left: 'left', right: 'right', splitter: 'splitter'
|
|
11
|
+
|
|
12
|
+
deepEqual [view.left, view.right, view.splitter], 'left right splitter'.split(" "), "lets you set them"
|
|
13
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module "AS.Views.VerticalSplit"
|
|
2
|
+
test "contains top, bottom and splitter elemens", ->
|
|
3
|
+
view = new AS.Views.VerticalSplit()
|
|
4
|
+
|
|
5
|
+
equal view.el.find(".Panel").length, 2, "contains two panels"
|
|
6
|
+
ok view.el.find(":nth-child(2)").is(".Splitter"), "with a splitter in the middle"
|
|
7
|
+
|
|
8
|
+
test "allows configurable top, bottom and splitter elements", ->
|
|
9
|
+
view = new AS.Views.VerticalSplit
|
|
10
|
+
top: 'top', bottom: 'bottom', splitter: 'splitter'
|
|
11
|
+
|
|
12
|
+
deepEqual [view.top, view.bottom, view.splitter], 'top bottom splitter'.split(" "), "lets you set them"
|
|
13
|
+
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
$(document).ready(function() {
|
|
2
|
+
|
|
3
|
+
module("Backbone.Collection");
|
|
4
|
+
|
|
5
|
+
window.lastRequest = null;
|
|
6
|
+
|
|
7
|
+
Backbone.sync = function() {
|
|
8
|
+
lastRequest = _.toArray(arguments);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
var a = new Backbone.Model({id: 3, label: 'a'});
|
|
12
|
+
var b = new Backbone.Model({id: 2, label: 'b'});
|
|
13
|
+
var c = new Backbone.Model({id: 1, label: 'c'});
|
|
14
|
+
var d = new Backbone.Model({id: 0, label: 'd'});
|
|
15
|
+
var e = null;
|
|
16
|
+
var col = new Backbone.Collection([a,b,c,d]);
|
|
17
|
+
var otherCol = new Backbone.Collection();
|
|
18
|
+
|
|
19
|
+
test("Collection: new and sort", function() {
|
|
20
|
+
equals(col.first(), a, "a should be first");
|
|
21
|
+
equals(col.last(), d, "d should be last");
|
|
22
|
+
col.comparator = function(model) { return model.id; };
|
|
23
|
+
col.sort();
|
|
24
|
+
equals(col.first(), d, "d should be first");
|
|
25
|
+
equals(col.last(), a, "a should be last");
|
|
26
|
+
equals(col.length, 4);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("Collection: get, getByCid", function() {
|
|
30
|
+
equals(col.get(0), d);
|
|
31
|
+
equals(col.get(2), b);
|
|
32
|
+
equals(col.getByCid(col.first().cid), col.first());
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("Collection: get with non-default ids", function() {
|
|
36
|
+
var col = new Backbone.Collection();
|
|
37
|
+
var MongoModel = Backbone.Model.extend({
|
|
38
|
+
idAttribute: '_id'
|
|
39
|
+
});
|
|
40
|
+
var model = new MongoModel({_id: 100});
|
|
41
|
+
col.add(model);
|
|
42
|
+
equals(col.get(100), model);
|
|
43
|
+
model.set({_id: 101});
|
|
44
|
+
equals(col.get(101), model);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("Collection: update index when id changes", function() {
|
|
48
|
+
var col = new Backbone.Collection();
|
|
49
|
+
col.add([
|
|
50
|
+
{id : 0, name : 'one'},
|
|
51
|
+
{id : 1, name : 'two'}
|
|
52
|
+
]);
|
|
53
|
+
var one = col.get(0);
|
|
54
|
+
equals(one.get('name'), 'one');
|
|
55
|
+
one.set({id : 101});
|
|
56
|
+
equals(col.get(0), null);
|
|
57
|
+
equals(col.get(101).get('name'), 'one');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("Collection: at", function() {
|
|
61
|
+
equals(col.at(2), b);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("Collection: pluck", function() {
|
|
65
|
+
equals(col.pluck('label').join(' '), 'd c b a');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("Collection: add", function() {
|
|
69
|
+
var added = opts = secondAdded = null;
|
|
70
|
+
e = new Backbone.Model({id: 10, label : 'e'});
|
|
71
|
+
otherCol.add(e);
|
|
72
|
+
otherCol.bind('add', function() {
|
|
73
|
+
secondAdded = true;
|
|
74
|
+
});
|
|
75
|
+
col.bind('add', function(model, collection, options){
|
|
76
|
+
added = model.get('label');
|
|
77
|
+
opts = options;
|
|
78
|
+
});
|
|
79
|
+
col.add(e, {amazing: true});
|
|
80
|
+
equals(added, 'e');
|
|
81
|
+
equals(col.length, 5);
|
|
82
|
+
equals(col.last(), e);
|
|
83
|
+
equals(otherCol.length, 1);
|
|
84
|
+
equals(secondAdded, null);
|
|
85
|
+
ok(opts.amazing);
|
|
86
|
+
|
|
87
|
+
var f = new Backbone.Model({id: 20, label : 'f'});
|
|
88
|
+
var g = new Backbone.Model({id: 21, label : 'g'});
|
|
89
|
+
var h = new Backbone.Model({id: 22, label : 'h'});
|
|
90
|
+
var atCol = new Backbone.Collection([f, g, h]);
|
|
91
|
+
equals(atCol.length, 3);
|
|
92
|
+
atCol.add(e, {at: 1});
|
|
93
|
+
equals(atCol.length, 4);
|
|
94
|
+
equals(atCol.at(1), e);
|
|
95
|
+
equals(atCol.last(), h);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("Collection: add model to collection twice", function() {
|
|
99
|
+
try {
|
|
100
|
+
// no id, same cid
|
|
101
|
+
var a2 = new Backbone.Model({label: a.label});
|
|
102
|
+
a2.cid = a.cid;
|
|
103
|
+
col.add(a2);
|
|
104
|
+
ok(false, "duplicate; expected add to fail");
|
|
105
|
+
} catch (e) {
|
|
106
|
+
equals(e.message, "Can't add the same model to a set twice,3");
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("Collection: add model to multiple collections", function() {
|
|
111
|
+
var counter = 0;
|
|
112
|
+
var e = new Backbone.Model({id: 10, label : 'e'});
|
|
113
|
+
e.bind('add', function(model, collection) {
|
|
114
|
+
counter++;
|
|
115
|
+
equals(e, model);
|
|
116
|
+
if (counter > 1) {
|
|
117
|
+
equals(collection, colF);
|
|
118
|
+
} else {
|
|
119
|
+
equals(collection, colE);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
var colE = new Backbone.Collection([]);
|
|
123
|
+
colE.bind('add', function(model, collection) {
|
|
124
|
+
equals(e, model);
|
|
125
|
+
equals(colE, collection);
|
|
126
|
+
});
|
|
127
|
+
var colF = new Backbone.Collection([]);
|
|
128
|
+
colF.bind('add', function(model, collection) {
|
|
129
|
+
equals(e, model);
|
|
130
|
+
equals(colF, collection);
|
|
131
|
+
});
|
|
132
|
+
colE.add(e);
|
|
133
|
+
equals(e.collection, colE);
|
|
134
|
+
colF.add(e);
|
|
135
|
+
equals(e.collection, colE);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("Collection: remove", function() {
|
|
139
|
+
var removed = otherRemoved = null;
|
|
140
|
+
col.bind('remove', function(model){ removed = model.get('label'); });
|
|
141
|
+
otherCol.bind('remove', function(){ otherRemoved = true; });
|
|
142
|
+
col.remove(e);
|
|
143
|
+
equals(removed, 'e');
|
|
144
|
+
equals(col.length, 4);
|
|
145
|
+
equals(col.first(), d);
|
|
146
|
+
equals(otherRemoved, null);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("Collection: events are unbound on remove", function() {
|
|
150
|
+
var counter = 0;
|
|
151
|
+
var dj = new Backbone.Model();
|
|
152
|
+
var emcees = new Backbone.Collection([dj]);
|
|
153
|
+
emcees.bind('change', function(){ counter++; });
|
|
154
|
+
dj.set({name : 'Kool'});
|
|
155
|
+
equals(counter, 1);
|
|
156
|
+
emcees.reset([]);
|
|
157
|
+
equals(dj.collection, undefined);
|
|
158
|
+
dj.set({name : 'Shadow'});
|
|
159
|
+
equals(counter, 1);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("Collection: remove in multiple collections", function() {
|
|
163
|
+
var modelData = {
|
|
164
|
+
id : 5,
|
|
165
|
+
title : 'Othello'
|
|
166
|
+
};
|
|
167
|
+
var passed = false;
|
|
168
|
+
var e = new Backbone.Model(modelData);
|
|
169
|
+
var f = new Backbone.Model(modelData);
|
|
170
|
+
f.bind('remove', function() {
|
|
171
|
+
passed = true;
|
|
172
|
+
});
|
|
173
|
+
var colE = new Backbone.Collection([e]);
|
|
174
|
+
var colF = new Backbone.Collection([f]);
|
|
175
|
+
ok(e != f);
|
|
176
|
+
ok(colE.length == 1);
|
|
177
|
+
ok(colF.length == 1);
|
|
178
|
+
colE.remove(e);
|
|
179
|
+
equals(passed, false);
|
|
180
|
+
ok(colE.length == 0);
|
|
181
|
+
colF.remove(e);
|
|
182
|
+
ok(colF.length == 0);
|
|
183
|
+
equals(passed, true);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("Collection: remove same model in multiple collection", function() {
|
|
187
|
+
var counter = 0;
|
|
188
|
+
var e = new Backbone.Model({id: 5, title: 'Othello'});
|
|
189
|
+
e.bind('remove', function(model, collection) {
|
|
190
|
+
counter++;
|
|
191
|
+
equals(e, model);
|
|
192
|
+
if (counter > 1) {
|
|
193
|
+
equals(collection, colE);
|
|
194
|
+
} else {
|
|
195
|
+
equals(collection, colF);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
var colE = new Backbone.Collection([e]);
|
|
199
|
+
colE.bind('remove', function(model, collection) {
|
|
200
|
+
equals(e, model);
|
|
201
|
+
equals(colE, collection);
|
|
202
|
+
});
|
|
203
|
+
var colF = new Backbone.Collection([e]);
|
|
204
|
+
colF.bind('remove', function(model, collection) {
|
|
205
|
+
equals(e, model);
|
|
206
|
+
equals(colF, collection);
|
|
207
|
+
});
|
|
208
|
+
equals(colE, e.collection);
|
|
209
|
+
colF.remove(e);
|
|
210
|
+
ok(colF.length == 0);
|
|
211
|
+
ok(colE.length == 1);
|
|
212
|
+
equals(counter, 1);
|
|
213
|
+
equals(colE, e.collection);
|
|
214
|
+
colE.remove(e);
|
|
215
|
+
equals(null, e.collection);
|
|
216
|
+
ok(colE.length == 0);
|
|
217
|
+
equals(counter, 2);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("Collection: model destroy removes from all collections", function() {
|
|
221
|
+
var e = new Backbone.Model({id: 5, title: 'Othello'});
|
|
222
|
+
e.sync = function(method, model, options) { options.success({}); };
|
|
223
|
+
var colE = new Backbone.Collection([e]);
|
|
224
|
+
var colF = new Backbone.Collection([e]);
|
|
225
|
+
e.destroy();
|
|
226
|
+
ok(colE.length == 0);
|
|
227
|
+
ok(colF.length == 0);
|
|
228
|
+
equals(null, e.collection);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("Colllection: non-persisted model destroy removes from all collections", function() {
|
|
232
|
+
var e = new Backbone.Model({title: 'Othello'});
|
|
233
|
+
e.sync = function(method, model, options) { throw "should not be called"; };
|
|
234
|
+
var colE = new Backbone.Collection([e]);
|
|
235
|
+
var colF = new Backbone.Collection([e]);
|
|
236
|
+
e.destroy();
|
|
237
|
+
ok(colE.length == 0);
|
|
238
|
+
ok(colF.length == 0);
|
|
239
|
+
equals(null, e.collection);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("Collection: fetch", function() {
|
|
243
|
+
col.fetch();
|
|
244
|
+
equals(lastRequest[0], 'read');
|
|
245
|
+
equals(lastRequest[1], col);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("Collection: create", function() {
|
|
249
|
+
var model = col.create({label: 'f'});
|
|
250
|
+
equals(lastRequest[0], 'create');
|
|
251
|
+
equals(lastRequest[1], model);
|
|
252
|
+
equals(model.get('label'), 'f');
|
|
253
|
+
equals(model.collection, col);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("Collection: create enforces validation", function() {
|
|
257
|
+
var ValidatingModel = Backbone.Model.extend({
|
|
258
|
+
validate: function(attrs) {
|
|
259
|
+
return "fail";
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
var ValidatingCollection = Backbone.Collection.extend({
|
|
263
|
+
model: ValidatingModel
|
|
264
|
+
});
|
|
265
|
+
var col = new ValidatingCollection();
|
|
266
|
+
equals(col.create({"foo":"bar"}),false);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("Collection: a failing create runs the error callback", function() {
|
|
270
|
+
var ValidatingModel = Backbone.Model.extend({
|
|
271
|
+
validate: function(attrs) {
|
|
272
|
+
return "fail";
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
var ValidatingCollection = Backbone.Collection.extend({
|
|
276
|
+
model: ValidatingModel
|
|
277
|
+
});
|
|
278
|
+
var flag = false;
|
|
279
|
+
var callback = function(model, error) { flag = true; };
|
|
280
|
+
var col = new ValidatingCollection();
|
|
281
|
+
col.create({"foo":"bar"}, { error: callback });
|
|
282
|
+
equals(flag, true);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("collection: initialize", function() {
|
|
286
|
+
var Collection = Backbone.Collection.extend({
|
|
287
|
+
initialize: function() {
|
|
288
|
+
this.one = 1;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
var coll = new Collection;
|
|
292
|
+
equals(coll.one, 1);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("Collection: toJSON", function() {
|
|
296
|
+
equals(JSON.stringify(col), '[{"id":0,"label":"d"},{"id":1,"label":"c"},{"id":2,"label":"b"},{"id":3,"label":"a"}]');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("Collection: Underscore methods", function() {
|
|
300
|
+
equals(col.map(function(model){ return model.get('label'); }).join(' '), 'd c b a');
|
|
301
|
+
equals(col.any(function(model){ return model.id === 100; }), false);
|
|
302
|
+
equals(col.any(function(model){ return model.id === 0; }), true);
|
|
303
|
+
equals(col.indexOf(b), 2);
|
|
304
|
+
equals(col.size(), 4);
|
|
305
|
+
equals(col.rest().length, 3);
|
|
306
|
+
ok(!_.include(col.rest()), a);
|
|
307
|
+
ok(!_.include(col.rest()), d);
|
|
308
|
+
ok(!col.isEmpty());
|
|
309
|
+
ok(!_.include(col.without(d)), d);
|
|
310
|
+
equals(col.max(function(model){ return model.id; }).id, 3);
|
|
311
|
+
equals(col.min(function(model){ return model.id; }).id, 0);
|
|
312
|
+
same(col.chain()
|
|
313
|
+
.filter(function(o){ return o.id % 2 === 0; })
|
|
314
|
+
.map(function(o){ return o.id * 2; })
|
|
315
|
+
.value(),
|
|
316
|
+
[0, 4]);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("Collection: reset", function() {
|
|
320
|
+
var resetCount = 0;
|
|
321
|
+
var models = col.models;
|
|
322
|
+
col.bind('reset', function() { resetCount += 1; });
|
|
323
|
+
col.reset([]);
|
|
324
|
+
equals(resetCount, 1);
|
|
325
|
+
equals(col.length, 0);
|
|
326
|
+
equals(col.last(), null);
|
|
327
|
+
col.reset(models);
|
|
328
|
+
equals(resetCount, 2);
|
|
329
|
+
equals(col.length, 4);
|
|
330
|
+
equals(col.last(), a);
|
|
331
|
+
col.reset(_.map(models, function(m){ return m.attributes; }));
|
|
332
|
+
equals(resetCount, 3);
|
|
333
|
+
equals(col.length, 4);
|
|
334
|
+
ok(col.last() !== a);
|
|
335
|
+
ok(_.isEqual(col.last().attributes, a.attributes));
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("Collection: trigger custom events on models", function() {
|
|
339
|
+
var fired = null;
|
|
340
|
+
a.bind("custom", function() { fired = true; });
|
|
341
|
+
a.trigger("custom");
|
|
342
|
+
equals(fired, true);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
});
|