jasmine-ajax 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +2 -0
  2. data/.pairs +6 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +35 -0
  5. data/MIT.LICENSE +20 -0
  6. data/README.markdown +168 -0
  7. data/Rakefile +3 -0
  8. data/examples/jquery/Rakefile +2 -0
  9. data/examples/jquery/public/css/master.css +59 -0
  10. data/examples/jquery/public/css/reset.css +47 -0
  11. data/examples/jquery/public/images/fail-whale.png +0 -0
  12. data/examples/jquery/public/index.html +60 -0
  13. data/examples/jquery/public/javascripts/Tweet.js +6 -0
  14. data/examples/jquery/public/javascripts/TwitSearch.js +32 -0
  15. data/examples/jquery/public/javascripts/TwitterApi.js +31 -0
  16. data/examples/jquery/spec/SpecRunner.html +32 -0
  17. data/examples/jquery/spec/javascripts/TweetSpec.js +24 -0
  18. data/examples/jquery/spec/javascripts/TwitterApiSpec.js +88 -0
  19. data/examples/jquery/spec/javascripts/helpers/test_responses/search.js +16 -0
  20. data/examples/jquery/spec/javascripts/helpers/tweets.js +3 -0
  21. data/examples/jquery/spec/javascripts/jasmine-0.11.1/jasmine-html.js +182 -0
  22. data/examples/jquery/spec/javascripts/jasmine-0.11.1/jasmine.css +166 -0
  23. data/examples/jquery/spec/javascripts/jasmine-0.11.1/jasmine.js +2343 -0
  24. data/examples/jquery/spec/javascripts/support/jasmine.yml +78 -0
  25. data/examples/jquery/spec/javascripts/support/jasmine_runner.rb +21 -0
  26. data/examples/prototype/Rakefile +2 -0
  27. data/examples/prototype/public/css/master.css +59 -0
  28. data/examples/prototype/public/css/reset.css +47 -0
  29. data/examples/prototype/public/images/fail-whale.png +0 -0
  30. data/examples/prototype/public/index.html +45 -0
  31. data/examples/prototype/public/javascripts/Tweet.js +6 -0
  32. data/examples/prototype/public/javascripts/TwitSearch.js +32 -0
  33. data/examples/prototype/public/javascripts/TwitterApi.js +24 -0
  34. data/examples/prototype/spec/SpecRunner.html +32 -0
  35. data/examples/prototype/spec/javascripts/TweetSpec.js +24 -0
  36. data/examples/prototype/spec/javascripts/TwitterApiSpec.js +89 -0
  37. data/examples/prototype/spec/javascripts/helpers/test_responses/search.js +16 -0
  38. data/examples/prototype/spec/javascripts/helpers/tweets.js +3 -0
  39. data/examples/prototype/spec/javascripts/jasmine-0.11.1/jasmine-html.js +182 -0
  40. data/examples/prototype/spec/javascripts/jasmine-0.11.1/jasmine.css +166 -0
  41. data/examples/prototype/spec/javascripts/jasmine-0.11.1/jasmine.js +2343 -0
  42. data/examples/prototype/spec/javascripts/support/jasmine.yml +78 -0
  43. data/examples/prototype/spec/javascripts/support/jasmine_runner.rb +21 -0
  44. data/frameworks/jquery.js +8176 -0
  45. data/frameworks/prototype.js +4874 -0
  46. data/lib/assets/javascripts/mock-ajax.js +207 -0
  47. data/lib/spec-helper.js +0 -0
  48. data/lib/version.rb +5 -0
  49. data/spec/javascripts/fake-xml-http-request-spec.js +45 -0
  50. data/spec/javascripts/helpers/spec-helper.js +5 -0
  51. data/spec/javascripts/mock-ajax-jquery-spec.js +374 -0
  52. data/spec/javascripts/mock-ajax-prototypejs-spec.js +287 -0
  53. data/spec/javascripts/mock-ajax-spec.js +193 -0
  54. data/spec/javascripts/support/jasmine.yml +81 -0
  55. data/spec/javascripts/support/jasmine_runner.rb +21 -0
  56. metadata +108 -0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .idea/
2
+ .rvmrc
data/.pairs ADDED
@@ -0,0 +1,6 @@
1
+ pairs:
2
+ hg: Hunter Gillane
3
+
4
+ email:
5
+ prefix: pair
6
+ domain: pivotallabs.com
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ..gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ childprocess (0.2.2)
5
+ ffi (~> 1.0.6)
6
+ diff-lcs (1.1.3)
7
+ ffi (1.0.9)
8
+ jasmine (1.1.0)
9
+ jasmine-core (>= 1.1.0)
10
+ rack (>= 1.1)
11
+ rspec (>= 1.3.1)
12
+ selenium-webdriver (>= 0.1.3)
13
+ jasmine-core (1.1.0)
14
+ json_pure (1.6.1)
15
+ rack (1.3.3)
16
+ rspec (2.6.0)
17
+ rspec-core (~> 2.6.0)
18
+ rspec-expectations (~> 2.6.0)
19
+ rspec-mocks (~> 2.6.0)
20
+ rspec-core (2.6.4)
21
+ rspec-expectations (2.6.0)
22
+ diff-lcs (~> 1.1.2)
23
+ rspec-mocks (2.6.0)
24
+ rubyzip (0.9.4)
25
+ selenium-webdriver (2.6.0)
26
+ childprocess (>= 0.2.1)
27
+ ffi (>= 1.0.7)
28
+ json_pure
29
+ rubyzip
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ jasmine (~> 1.1.0)
data/MIT.LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Pivotal Labs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,168 @@
1
+ jasmine-ajax - Faking Ajax responses in your Jasmine suite.
2
+ ===
3
+ jasmine-ajax is a library that lets you define a set of fake responses for Ajax requests made by your application, specify per spec which response should be used, and keep track of the Ajax requests you make so you can make assertions about the results.
4
+
5
+ Libraries Supported
6
+ ---
7
+ jasmine-ajax is currently compatible with jQuery and Prototype. Support for other libraries planned.
8
+
9
+ Installing
10
+ ---
11
+ Download [mock-ajax.js](http://cloud.github.com/downloads/pivotal/jasmine-ajax/mock-ajax.js) and add it to your project. If you are using the jasmine gem, be sure the location you put mock-ajax.js is included in your src_files path in jasmine.yml. If you are using Jasmine standalone, make sure you add it to your spec runner.
12
+
13
+ Setup
14
+ ---
15
+ Using the library in your Jasmine specs consists of four parts:
16
+
17
+ 1. Defining test responses
18
+ 2. Installing the mock
19
+ 3. Defining the response for each request
20
+ 4. Inspecting Ajax requests and setting expectations on them
21
+
22
+ Example
23
+ ---
24
+ Let's use a simple Foursquare venue search app to show each of these steps.
25
+
26
+ ### 1. Defining Test Responses ###
27
+ After signing up for an API key and playing around with curl a bit you should have an idea of what API resources you are interested in and what sample responses look like. Once you do, you can define simple JavaScripts objects that will be used to build XMLHttpRequest objects later.
28
+
29
+ For example, if you have a response that looks like this:
30
+
31
+ {
32
+ "meta":{
33
+ "code":200,
34
+ "errorType":"deprecated",
35
+ "errorDetail":"This endpoint will stop returning groups in the future. Please use a current version, see http://bit.ly/lZx3NU."
36
+ },
37
+ "response":{
38
+ "groups":[{
39
+ "type":"nearby",
40
+ "name":"Nearby",
41
+ "items":[{
42
+ "id":"4bb9fd9f3db7b7138dbd229a",
43
+ "name":"Pivotal Labs",
44
+ "contact":{
45
+ "twitter":"pivotalboulder"
46
+ },
47
+ "location":{
48
+ "address":"1701 Pearl St.",
49
+ "crossStreet":"at 17th St.",
50
+ "city":"Boulder",
51
+ "state":"CO",
52
+ "lat":40.019461,
53
+ "lng":-105.273296,
54
+ "distance":0
55
+ },
56
+ "categories":[{
57
+ "id":"4bf58dd8d48988d124941735",
58
+ "name":"Office",
59
+ "pluralName":"Offices",
60
+ "icon":"https://foursquare.com/img/categories/building/default.png",
61
+ "parents":["Homes, Work, Others"
62
+ ],
63
+ "primary":true
64
+ }
65
+ ],
66
+ "verified":false,
67
+ "stats":{
68
+ "checkinsCount":223,
69
+ "usersCount":62
70
+ },
71
+ "hereNow":{
72
+ "count":0
73
+ }
74
+ }
75
+ ]
76
+ }
77
+ ]
78
+ }
79
+ }
80
+
81
+ Then you'd define a mock response that looks something like this:
82
+
83
+ var TestResponses = {
84
+ search: {
85
+ success: {
86
+ status: 200,
87
+ responseText: '{"response":{"groups":[{"type":"nearby","name":"Nearby","items":[{"id":"4bb9fd9f3db7b7138dbd229a","name":"Pivotal Labs","contact":{"twitter":"pivotalboulder"},"location":{"address":"1701 Pearl St.","crossStreet":"at 17th St.","city":"Boulder","state":"CO","lat":40.019461,"lng":-105.273296,"distance":0},"categories":[{"id":"4bf58dd8d48988d124941735","name":"Office","pluralName":"Offices","icon":"https://foursquare.com/img/categories/building/default.png","parents":["Homes, Work, Others"],"primary":true}],"verified":false,"stats":{"checkinsCount":223,"usersCount":62},"hereNow":{"count":0}},{"id":"4af2eccbf964a5203ae921e3","name":"Laughing Goat Café","contact":{},"location":{"address":"1709 Pearl St.","crossStreet":"btw 16th & 17th","city":"Boulder","state":"CO","postalCode":"80302","country":"USA","lat":40.019321,"lng":-105.27311982,"distance":21},"categories":[{"id":"4bf58dd8d48988d1e0931735","name":"Coffee Shop","pluralName":"Coffee Shops","icon":"https://foursquare.com/img/categories/food/coffeeshop.png","parents":["Food"],"primary":true},{"id":"4bf58dd8d48988d1a7941735","name":"College Library","pluralName":"College Libraries","icon":"https://foursquare.com/img/categories/education/default.png","parents":["Colleges & Universities"]}],"verified":false,"stats":{"checkinsCount":1314,"usersCount":517},"hereNow":{"count":0}},{"id":"4ca777a597c8a1cdf7bc7aa5","name":"Ted\'s Montana Grill","contact":{"phone":"3034495546","formattedPhone":"(303) 449-5546","twitter":"TedMontanaGrill"},"location":{"address":"1701 Pearl St.","crossStreet":"17th and Pearl","city":"Boulder","state":"CO","postalCode":"80302","country":"USA","lat":40.019376,"lng":-105.273311,"distance":9},"categories":[{"id":"4bf58dd8d48988d1cc941735","name":"Steakhouse","pluralName":"Steakhouses","icon":"https://foursquare.com/img/categories/food/steakhouse.png","parents":["Food"],"primary":true}],"verified":true,"stats":{"checkinsCount":197,"usersCount":150},"url":"http://www.tedsmontanagrill.com/","hereNow":{"count":0}},{"id":"4d3cac5a8edf3704e894b2a5","name":"Pizzeria Locale","contact":{},"location":{"address":"1730 Pearl St","city":"Boulder","state":"CO","postalCode":"80302","country":"USA","lat":40.0193746,"lng":-105.2726744,"distance":53},"categories":[{"id":"4bf58dd8d48988d1ca941735","name":"Pizza Place","pluralName":"Pizza Places","icon":"https://foursquare.com/img/categories/food/pizza.png","parents":["Food"],"primary":true}],"verified":false,"stats":{"checkinsCount":511,"usersCount":338},"hereNow":{"count":2}},{"id":"4d012cd17c56370462a6b4f0","name":"The Pinyon","contact":{},"location":{"address":"1710 Pearl St.","city":"Boulder","state":"CO","country":"USA","lat":40.019219,"lng":-105.2730563,"distance":33},"categories":[{"id":"4bf58dd8d48988d14e941735","name":"American Restaurant","pluralName":"American Restaurants","icon":"https://foursquare.com/img/categories/food/default.png","parents":["Food"],"primary":true}],"verified":true,"stats":{"checkinsCount":163,"usersCount":98},"hereNow":{"count":1}}]}]}'
88
+ }
89
+ }
90
+ };
91
+
92
+ A good place to define this is in `spec/javascripts/helpers/test_responses`. You can also define failure responses, for whatever status codes the API you are working with supports.
93
+
94
+ ### 2. Installing the mock ###
95
+ Install the mock using `jasmine.Ajax.useMock()`:
96
+
97
+ beforeEach(function() {
98
+ jasmine.Ajax.useMock();
99
+ ...
100
+ After this, all Ajax requests will be captured by jasmine-ajax. If you want to do things like load fixtures, do it before you install the mock (see below).
101
+
102
+ ### 3. Set responses ###
103
+ Now that you've defined some test responses and installed the mock, you need to tell jasmine-ajax which response to use for a given spec. If you want to use your success response for a set of related success specs, you might use:
104
+
105
+ describe("on success", function() {
106
+ beforeEach(function() {
107
+ request.response(TestResponses.search.success);
108
+ });
109
+
110
+ Now for all the specs in this example group, whenever an Ajax response is sent, it will use the `TestResponses.search.success` object defined in your test responses to build the XMLHttpRequest object.
111
+
112
+ ### 4. Inspect Ajax requests ###
113
+ Putting it all together, you can install the mock, pass some spies as callbacks to your search object, and make expectations about the expected behavior.
114
+
115
+ describe("FoursquareVenueSearch", function() {
116
+ var foursquare, request;
117
+ var onSuccess, onFailure;
118
+
119
+ beforeEach(function() {
120
+ jasmine.Ajax.useMock();
121
+
122
+ onSuccess = jasmine.createSpy('onSuccess');
123
+ onFailure = jasmine.createSpy('onFailure');
124
+
125
+ foursquare = new FoursquareVenueSearch();
126
+
127
+ foursquare.search('40.019461,-105.273296', {
128
+ onSuccess: onSuccess,
129
+ onFailure: onFailure
130
+ });
131
+
132
+ request = mostRecentAjaxRequest();
133
+ });
134
+
135
+ describe("on success", function() {
136
+ beforeEach(function() {
137
+ request.response(TestResponses.search.success);
138
+ });
139
+
140
+ it("calls onSuccess with an array of Locations", function() {
141
+ expect(onSuccess).toHaveBeenCalled();
142
+
143
+ var successArgs = onSuccess.mostRecentCall.args[0];
144
+
145
+ expect(successArgs.length).toEqual(1);
146
+ expect(successArgs[0]).toEqual(jasmine.any(Venue));
147
+ });
148
+ });
149
+ });
150
+
151
+
152
+ Loading Fixtures
153
+ ---
154
+ Most third-party Jasmine extensions use Ajax to load HTML fixtures into the DOM. Since jasmine-ajax intercepts all Ajax calls after it is installed, you need to load your fixtures before installing the mock. If you are using jasmine-jquery, that looks like this:
155
+
156
+ beforeEach(function){
157
+ // first load your fixtures
158
+ loadFixtures('fixture.html');
159
+
160
+ // then install the mock
161
+ jasmine.Ajax.useMock();
162
+ });
163
+
164
+ Jasmine
165
+ ------------
166
+ http://github.com/pivotal/jasmine
167
+
168
+ Copyright (c) 2011 Pivotal Labs. This software is licensed under the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+
2
+ require 'jasmine'
3
+ load 'jasmine/tasks/jasmine.rake'
@@ -0,0 +1,2 @@
1
+ require 'jasmine'
2
+ load 'jasmine/tasks/jasmine.rake'
@@ -0,0 +1,59 @@
1
+ body {
2
+ margin-top: 100px;
3
+ text-align: center;
4
+ }
5
+
6
+ #wrap {
7
+ width: 800px;
8
+ margin: 0 auto;
9
+ text-align: left;
10
+ }
11
+
12
+ #twit_search {
13
+ margin-bottom: 75px;
14
+ }
15
+
16
+ #twit_search form {
17
+ color: #555555;
18
+ text-align: center;
19
+ font-size: 150%;
20
+ }
21
+
22
+ #twit_search form input[type=text] {
23
+ font-size: 100%;
24
+ color: #555555;
25
+ border: 2px solid black;
26
+ -moz-border-radius: 2px;
27
+ -webkit-border-radius: 2px;
28
+ padding: 4px 6px;
29
+ }
30
+
31
+ #twit_search form input[type=submit] {
32
+ font-size: 130%;
33
+ }
34
+
35
+ #results {
36
+ margin: 0 auto;
37
+ width: 60%;
38
+ }
39
+
40
+ #results li {
41
+ margin: 38px 0;
42
+ }
43
+
44
+ #results img {
45
+ clear: both;
46
+ margin-right: 10px;
47
+ float: left;
48
+ }
49
+
50
+ #results img + p {
51
+ font-size: 130%;
52
+ color: #333333;
53
+ }
54
+
55
+ #results p.user, #results p.timestamp {
56
+ margin-top: 5px;
57
+ text-align: right;
58
+ font-style: italic;
59
+ }
@@ -0,0 +1,47 @@
1
+ /* From http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ */
2
+ html, body, div, span, applet, object, iframe,
3
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
4
+ a, abbr, acronym, address, big, cite, code,
5
+ del, dfn, em, font, img, ins, kbd, q, s, samp,
6
+ small, strike, strong, sub, sup, tt, var,
7
+ dl, dt, dd, ol, ul, li,
8
+ fieldset, form, label, legend,
9
+ table, caption, tbody, tfoot, thead, tr, th, td {
10
+ margin: 0;
11
+ padding: 0;
12
+ border: 0;
13
+ outline: 0;
14
+ font-weight: inherit;
15
+ font-style: inherit;
16
+ font-size: 100%;
17
+ font-family: inherit;
18
+ vertical-align: baseline;
19
+ }
20
+ /* remember to define focus styles! */
21
+ :focus {
22
+ outline: 0;
23
+ }
24
+ body {
25
+ line-height: 1;
26
+ color: black;
27
+ background: white;
28
+ }
29
+ ol, ul {
30
+ list-style: none;
31
+ }
32
+ /* tables still need 'cellspacing="0"' in the markup */
33
+ table {
34
+ border-collapse: separate;
35
+ border-spacing: 0;
36
+ }
37
+ caption, th, td {
38
+ text-align: left;
39
+ font-weight: normal;
40
+ }
41
+ blockquote:before, blockquote:after,
42
+ q:before, q:after {
43
+ content: "";
44
+ }
45
+ blockquote, q {
46
+ quotes: "" "";
47
+ }
@@ -0,0 +1,60 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+
8
+ <title>Jasmine BDD</title>
9
+ <link rel="stylesheet" type="text/css" href="css/reset.css" />
10
+ <link rel="stylesheet" type="text/css" href="css/master.css" />
11
+ <script type="text/javascript" src="../../../frameworks/jquery.js"></script>
12
+ <script type="text/javascript" src="javascripts/TwitSearch.js"></script>
13
+ <script type="text/javascript" src="javascripts/TwitterApi.js"></script>
14
+ <script type="text/javascript" src="javascripts/Tweet.js"></script>
15
+ </head>
16
+
17
+ <body>
18
+ <div id="wrap">
19
+ <div id="twit_search">
20
+ <form action="index.html#" method="get">
21
+ <input type="text" name="query" id="query" />
22
+ <input type="submit" value="Search Twitter" />
23
+ </form>
24
+ </div>
25
+ <ul id="results"></ul>
26
+ <script type="text/javascript">
27
+
28
+ $(function(){
29
+ $("#twit_search form").submit(function(e){
30
+ e.preventDefault();
31
+ var search_query = $("#query").val();
32
+ new TwitterApi().search(
33
+ search_query, {
34
+ onSuccess: TwitSearch.displayResults,
35
+ onFailure: TwitSearch.searchFailure
36
+ // onComplete: TwitSearch.cleanup,
37
+ // onFailWhale: TwitSearch.failWhale
38
+ }
39
+ );
40
+ });
41
+ });
42
+
43
+ // document.observe('dom:loaded', function(){
44
+ // $$("#twit_search form").first().observe("submit", function(event) {
45
+ // event.preventDefault();
46
+ // var search_query = $("query").value
47
+ // new TwitterApi().search(
48
+ // search_query, {
49
+ // onSuccess: TwitSearch.displayResults,
50
+ // onFailure: TwitSearch.searchFailure,
51
+ // onComplete: TwitSearch.cleanup,
52
+ // onFailWhale: TwitSearch.failWhale
53
+ // }
54
+ // );
55
+ // });
56
+ // });
57
+ </script>
58
+ </div>
59
+ </body>
60
+ </html>
@@ -0,0 +1,6 @@
1
+ function Tweet(tweet){
2
+ this.postedAt = tweet.created_at;
3
+ this.text = tweet.text;
4
+ this.imageUrl = tweet.profile_image_url;
5
+ this.user = tweet.from_user;
6
+ }
@@ -0,0 +1,32 @@
1
+ var TwitSearch = function(){
2
+
3
+ return {
4
+ displayResults: function(tweets){
5
+ var updateStr = "";
6
+
7
+ $(tweets).each(function(index, tweet) {
8
+ updateStr += "<li><img src='" + tweet.imageUrl + "' alt='" + tweet.user + " profile image' />" +
9
+ "<p>" + tweet.text + "</p>" +
10
+ "<p class='user'>" + tweet.user + "</p>" +
11
+ "<p class='timestamp'>" + tweet.postedAt + "</p>";
12
+
13
+ });
14
+
15
+ $("#results").html(updateStr);
16
+ },
17
+
18
+ searchFailure: function(response){
19
+ $("#results").html("<h2>Oops. Something went wrong.</h2>");
20
+ },
21
+
22
+ cleanup: function(){},
23
+
24
+ rateLimitReached: function(){
25
+ console.log("rate limited");
26
+ },
27
+
28
+ failWhale: function(){
29
+ $("#results").html("<img src='images/fail-whale.png' />");
30
+ }
31
+ }
32
+ }();