autobench 0.0.1alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +16 -0
  3. data/README.md +123 -0
  4. data/bin/autobench +180 -0
  5. data/bin/autobench-config +162 -0
  6. data/lib/autobench.rb +28 -0
  7. data/lib/autobench/client.rb +78 -0
  8. data/lib/autobench/common.rb +49 -0
  9. data/lib/autobench/config.rb +62 -0
  10. data/lib/autobench/render.rb +102 -0
  11. data/lib/autobench/version.rb +3 -0
  12. data/lib/autobench/yslow.rb +75 -0
  13. data/lib/phantomas/README.md +296 -0
  14. data/lib/phantomas/core/formatter.js +65 -0
  15. data/lib/phantomas/core/helper.js +64 -0
  16. data/lib/phantomas/core/modules/requestsMonitor/requestsMonitor.js +214 -0
  17. data/lib/phantomas/core/pads.js +16 -0
  18. data/lib/phantomas/core/phantomas.js +418 -0
  19. data/lib/phantomas/lib/args.js +27 -0
  20. data/lib/phantomas/lib/modules/_coffee-script.js +2 -0
  21. data/lib/phantomas/lib/modules/assert.js +326 -0
  22. data/lib/phantomas/lib/modules/events.js +216 -0
  23. data/lib/phantomas/lib/modules/http.js +55 -0
  24. data/lib/phantomas/lib/modules/path.js +441 -0
  25. data/lib/phantomas/lib/modules/punycode.js +510 -0
  26. data/lib/phantomas/lib/modules/querystring.js +214 -0
  27. data/lib/phantomas/lib/modules/tty.js +7 -0
  28. data/lib/phantomas/lib/modules/url.js +625 -0
  29. data/lib/phantomas/lib/modules/util.js +520 -0
  30. data/lib/phantomas/modules/ajaxRequests/ajaxRequests.js +15 -0
  31. data/lib/phantomas/modules/assetsTypes/assetsTypes.js +21 -0
  32. data/lib/phantomas/modules/cacheHits/cacheHits.js +28 -0
  33. data/lib/phantomas/modules/caching/caching.js +66 -0
  34. data/lib/phantomas/modules/cookies/cookies.js +54 -0
  35. data/lib/phantomas/modules/domComplexity/domComplexity.js +130 -0
  36. data/lib/phantomas/modules/domQueries/domQueries.js +148 -0
  37. data/lib/phantomas/modules/domains/domains.js +49 -0
  38. data/lib/phantomas/modules/globalVariables/globalVariables.js +44 -0
  39. data/lib/phantomas/modules/headers/headers.js +48 -0
  40. data/lib/phantomas/modules/localStorage/localStorage.js +14 -0
  41. data/lib/phantomas/modules/requestsStats/requestsStats.js +71 -0
  42. data/lib/phantomas/modules/staticAssets/staticAssets.js +40 -0
  43. data/lib/phantomas/modules/waterfall/waterfall.js +62 -0
  44. data/lib/phantomas/modules/windowPerformance/windowPerformance.js +36 -0
  45. data/lib/phantomas/package.json +27 -0
  46. data/lib/phantomas/phantomas.js +35 -0
  47. data/lib/phantomas/run-multiple.js +177 -0
  48. data/lib/yslow.js +5 -0
  49. metadata +135 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Simple phantom.args parser
3
+ */
4
+ exports.parse = function(args) {
5
+ var res = {};
6
+ args = args || [];
7
+
8
+ args.forEach(function(item) {
9
+ var idx = item.indexOf('='),
10
+ key, val;
11
+
12
+ // --foo
13
+ if (idx < 0) {
14
+ key = item.substring(2);
15
+ val = true;
16
+ }
17
+ // --foo=bar
18
+ else {
19
+ key = item.substring(2, idx);
20
+ val = item.substring(idx+1);
21
+ }
22
+
23
+ res[key] = val;
24
+ });
25
+
26
+ return res;
27
+ };
@@ -0,0 +1,2 @@
1
+ require.stub('vm');
2
+ module.exports = require('coffee-script');
@@ -0,0 +1,326 @@
1
+ // http://wiki.commonjs.org/wiki/Unit_Testing/1.0
2
+ //
3
+ // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
4
+ //
5
+ // Originally from narwhal.js (http://narwhaljs.org)
6
+ // Copyright (c) 2009 Thomas Robinson <280north.com>
7
+ //
8
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ // of this software and associated documentation files (the 'Software'), to
10
+ // deal in the Software without restriction, including without limitation the
11
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
+ // sell copies of the Software, and to permit persons to whom the Software is
13
+ // furnished to do so, subject to the following conditions:
14
+ //
15
+ // The above copyright notice and this permission notice shall be included in
16
+ // all copies or substantial portions of the Software.
17
+ //
18
+ // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22
+ // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ // UTILITY
26
+ var util = require('util');
27
+ var pSlice = Array.prototype.slice;
28
+
29
+ // 1. The assert module provides functions that throw
30
+ // AssertionError's when particular conditions are not met. The
31
+ // assert module must conform to the following interface.
32
+
33
+ var assert = module.exports = ok;
34
+
35
+ // 2. The AssertionError is defined in assert.
36
+ // new assert.AssertionError({ message: message,
37
+ // actual: actual,
38
+ // expected: expected })
39
+
40
+ assert.AssertionError = function AssertionError(options) {
41
+ this.name = 'AssertionError';
42
+ this.message = options.message;
43
+ this.actual = options.actual;
44
+ this.expected = options.expected;
45
+ this.operator = options.operator;
46
+ var stackStartFunction = options.stackStartFunction || fail;
47
+
48
+ if (Error.captureStackTrace) {
49
+ Error.captureStackTrace(this, stackStartFunction);
50
+ }
51
+ };
52
+ util.inherits(assert.AssertionError, Error);
53
+
54
+ function replacer(key, value) {
55
+ if (value === undefined) {
56
+ return '' + value;
57
+ }
58
+ if (typeof value === 'number' && (isNaN(value) || !isFinite(value))) {
59
+ return value.toString();
60
+ }
61
+ if (typeof value === 'function' || value instanceof RegExp) {
62
+ return value.toString();
63
+ }
64
+ return value;
65
+ }
66
+
67
+ function truncate(s, n) {
68
+ if (typeof s == 'string') {
69
+ return s.length < n ? s : s.slice(0, n);
70
+ } else {
71
+ return s;
72
+ }
73
+ }
74
+
75
+ assert.AssertionError.prototype.toString = function() {
76
+ if (this.message) {
77
+ return [this.name + ':', this.message].join(' ');
78
+ } else {
79
+ return [
80
+ this.name + ':',
81
+ truncate(JSON.stringify(this.actual, replacer), 128),
82
+ this.operator,
83
+ truncate(JSON.stringify(this.expected, replacer), 128)
84
+ ].join(' ');
85
+ }
86
+ };
87
+
88
+ // assert.AssertionError instanceof Error
89
+
90
+ assert.AssertionError.__proto__ = Error.prototype;
91
+
92
+ // At present only the three keys mentioned above are used and
93
+ // understood by the spec. Implementations or sub modules can pass
94
+ // other keys to the AssertionError's constructor - they will be
95
+ // ignored.
96
+
97
+ // 3. All of the following functions must throw an AssertionError
98
+ // when a corresponding condition is not met, with a message that
99
+ // may be undefined if not provided. All assertion methods provide
100
+ // both the actual and expected values to the assertion error for
101
+ // display purposes.
102
+
103
+ function fail(actual, expected, message, operator, stackStartFunction) {
104
+ throw new assert.AssertionError({
105
+ message: message,
106
+ actual: actual,
107
+ expected: expected,
108
+ operator: operator,
109
+ stackStartFunction: stackStartFunction
110
+ });
111
+ }
112
+
113
+ // EXTENSION! allows for well behaved errors defined elsewhere.
114
+ assert.fail = fail;
115
+
116
+ // 4. Pure assertion tests whether a value is truthy, as determined
117
+ // by !!guard.
118
+ // assert.ok(guard, message_opt);
119
+ // This statement is equivalent to assert.equal(true, guard,
120
+ // message_opt);. To test strictly for the value true, use
121
+ // assert.strictEqual(true, guard, message_opt);.
122
+
123
+ function ok(value, message) {
124
+ if (!!!value) fail(value, true, message, '==', assert.ok);
125
+ }
126
+ assert.ok = ok;
127
+
128
+ // 5. The equality assertion tests shallow, coercive equality with
129
+ // ==.
130
+ // assert.equal(actual, expected, message_opt);
131
+
132
+ assert.equal = function equal(actual, expected, message) {
133
+ if (actual != expected) fail(actual, expected, message, '==', assert.equal);
134
+ };
135
+
136
+ // 6. The non-equality assertion tests for whether two objects are not equal
137
+ // with != assert.notEqual(actual, expected, message_opt);
138
+
139
+ assert.notEqual = function notEqual(actual, expected, message) {
140
+ if (actual == expected) {
141
+ fail(actual, expected, message, '!=', assert.notEqual);
142
+ }
143
+ };
144
+
145
+ // 7. The equivalence assertion tests a deep equality relation.
146
+ // assert.deepEqual(actual, expected, message_opt);
147
+
148
+ assert.deepEqual = function deepEqual(actual, expected, message) {
149
+ if (!_deepEqual(actual, expected)) {
150
+ fail(actual, expected, message, 'deepEqual', assert.deepEqual);
151
+ }
152
+ };
153
+
154
+ function _deepEqual(actual, expected) {
155
+ // 7.1. All identical values are equivalent, as determined by ===.
156
+ if (actual === expected) {
157
+ return true;
158
+
159
+ } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
160
+ if (actual.length != expected.length) return false;
161
+
162
+ for (var i = 0; i < actual.length; i++) {
163
+ if (actual[i] !== expected[i]) return false;
164
+ }
165
+
166
+ return true;
167
+
168
+ // 7.2. If the expected value is a Date object, the actual value is
169
+ // equivalent if it is also a Date object that refers to the same time.
170
+ } else if (actual instanceof Date && expected instanceof Date) {
171
+ return actual.getTime() === expected.getTime();
172
+
173
+ // 7.3. Other pairs that do not both pass typeof value == 'object',
174
+ // equivalence is determined by ==.
175
+ } else if (typeof actual != 'object' && typeof expected != 'object') {
176
+ return actual == expected;
177
+
178
+ // 7.4. For all other Object pairs, including Array objects, equivalence is
179
+ // determined by having the same number of owned properties (as verified
180
+ // with Object.prototype.hasOwnProperty.call), the same set of keys
181
+ // (although not necessarily the same order), equivalent values for every
182
+ // corresponding key, and an identical 'prototype' property. Note: this
183
+ // accounts for both named and indexed properties on Arrays.
184
+ } else {
185
+ return objEquiv(actual, expected);
186
+ }
187
+ }
188
+
189
+ function isUndefinedOrNull(value) {
190
+ return value === null || value === undefined;
191
+ }
192
+
193
+ function isArguments(object) {
194
+ return Object.prototype.toString.call(object) == '[object Arguments]';
195
+ }
196
+
197
+ function objEquiv(a, b) {
198
+ if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
199
+ return false;
200
+ // an identical 'prototype' property.
201
+ if (a.prototype !== b.prototype) return false;
202
+ //~~~I've managed to break Object.keys through screwy arguments passing.
203
+ // Converting to array solves the problem.
204
+ if (isArguments(a)) {
205
+ if (!isArguments(b)) {
206
+ return false;
207
+ }
208
+ a = pSlice.call(a);
209
+ b = pSlice.call(b);
210
+ return _deepEqual(a, b);
211
+ }
212
+ try {
213
+ var ka = Object.keys(a),
214
+ kb = Object.keys(b),
215
+ key, i;
216
+ } catch (e) {//happens when one is a string literal and the other isn't
217
+ return false;
218
+ }
219
+ // having the same number of owned properties (keys incorporates
220
+ // hasOwnProperty)
221
+ if (ka.length != kb.length)
222
+ return false;
223
+ //the same set of keys (although not necessarily the same order),
224
+ ka.sort();
225
+ kb.sort();
226
+ //~~~cheap key test
227
+ for (i = ka.length - 1; i >= 0; i--) {
228
+ if (ka[i] != kb[i])
229
+ return false;
230
+ }
231
+ //equivalent values for every corresponding key, and
232
+ //~~~possibly expensive deep test
233
+ for (i = ka.length - 1; i >= 0; i--) {
234
+ key = ka[i];
235
+ if (!_deepEqual(a[key], b[key])) return false;
236
+ }
237
+ return true;
238
+ }
239
+
240
+ // 8. The non-equivalence assertion tests for any deep inequality.
241
+ // assert.notDeepEqual(actual, expected, message_opt);
242
+
243
+ assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
244
+ if (_deepEqual(actual, expected)) {
245
+ fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
246
+ }
247
+ };
248
+
249
+ // 9. The strict equality assertion tests strict equality, as determined by ===.
250
+ // assert.strictEqual(actual, expected, message_opt);
251
+
252
+ assert.strictEqual = function strictEqual(actual, expected, message) {
253
+ if (actual !== expected) {
254
+ fail(actual, expected, message, '===', assert.strictEqual);
255
+ }
256
+ };
257
+
258
+ // 10. The strict non-equality assertion tests for strict inequality, as
259
+ // determined by !==. assert.notStrictEqual(actual, expected, message_opt);
260
+
261
+ assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
262
+ if (actual === expected) {
263
+ fail(actual, expected, message, '!==', assert.notStrictEqual);
264
+ }
265
+ };
266
+
267
+ function expectedException(actual, expected) {
268
+ if (!actual || !expected) {
269
+ return false;
270
+ }
271
+
272
+ if (expected instanceof RegExp) {
273
+ return expected.test(actual);
274
+ } else if (actual instanceof expected) {
275
+ return true;
276
+ } else if (expected.call({}, actual) === true) {
277
+ return true;
278
+ }
279
+
280
+ return false;
281
+ }
282
+
283
+ function _throws(shouldThrow, block, expected, message) {
284
+ var actual;
285
+
286
+ if (typeof expected === 'string') {
287
+ message = expected;
288
+ expected = null;
289
+ }
290
+
291
+ try {
292
+ block();
293
+ } catch (e) {
294
+ actual = e;
295
+ }
296
+
297
+ message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
298
+ (message ? ' ' + message : '.');
299
+
300
+ if (shouldThrow && !actual) {
301
+ fail('Missing expected exception' + message);
302
+ }
303
+
304
+ if (!shouldThrow && expectedException(actual, expected)) {
305
+ fail('Got unwanted exception' + message);
306
+ }
307
+
308
+ if ((shouldThrow && actual && expected &&
309
+ !expectedException(actual, expected)) || (!shouldThrow && actual)) {
310
+ throw actual;
311
+ }
312
+ }
313
+
314
+ // 11. Expected to throw an error:
315
+ // assert.throws(block, Error_opt, message_opt);
316
+
317
+ assert.throws = function(block, /*optional*/error, /*optional*/message) {
318
+ _throws.apply(this, [true].concat(pSlice.call(arguments)));
319
+ };
320
+
321
+ // EXTENSION! This is annoying to write outside this module.
322
+ assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
323
+ _throws.apply(this, [false].concat(pSlice.call(arguments)));
324
+ };
325
+
326
+ assert.ifError = function(err) { if (err) {throw err;}};
@@ -0,0 +1,216 @@
1
+ // Copyright Joyent, Inc. and other Node contributors.
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a
4
+ // 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 permit
8
+ // persons to whom the Software is furnished to do so, subject to the
9
+ // following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included
12
+ // in all copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
+ // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
+ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
+ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
+ // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ var isArray = Array.isArray;
23
+
24
+ function EventEmitter() { }
25
+ exports.EventEmitter = EventEmitter;
26
+
27
+ // By default EventEmitters will print a warning if more than
28
+ // 10 listeners are added to it. This is a useful default which
29
+ // helps finding memory leaks.
30
+ //
31
+ // Obviously not all Emitters should be limited to 10. This function allows
32
+ // that to be increased. Set to zero for unlimited.
33
+ var defaultMaxListeners = 10;
34
+ EventEmitter.prototype.setMaxListeners = function(n) {
35
+ if (!this._events) this._events = {};
36
+ this._maxListeners = n;
37
+ };
38
+
39
+
40
+ EventEmitter.prototype.emit = function() {
41
+ var type = arguments[0];
42
+ // If there is no 'error' event listener then throw.
43
+ if (type === 'error') {
44
+ if (!this._events || !this._events.error ||
45
+ (isArray(this._events.error) && !this._events.error.length))
46
+ {
47
+ if (arguments[1] instanceof Error) {
48
+ throw arguments[1]; // Unhandled 'error' event
49
+ } else {
50
+ throw new Error("Uncaught, unspecified 'error' event.");
51
+ }
52
+ return false;
53
+ }
54
+ }
55
+
56
+ if (!this._events) return false;
57
+ var handler = this._events[type];
58
+ if (!handler) return false;
59
+
60
+ if (typeof handler == 'function') {
61
+ switch (arguments.length) {
62
+ // fast cases
63
+ case 1:
64
+ handler.call(this);
65
+ break;
66
+ case 2:
67
+ handler.call(this, arguments[1]);
68
+ break;
69
+ case 3:
70
+ handler.call(this, arguments[1], arguments[2]);
71
+ break;
72
+ // slower
73
+ default:
74
+ var l = arguments.length;
75
+ var args = new Array(l - 1);
76
+ for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
77
+ handler.apply(this, args);
78
+ }
79
+ return true;
80
+
81
+ } else if (isArray(handler)) {
82
+ var l = arguments.length;
83
+ var args = new Array(l - 1);
84
+ for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
85
+
86
+ var listeners = handler.slice();
87
+ for (var i = 0, l = listeners.length; i < l; i++) {
88
+ listeners[i].apply(this, args);
89
+ }
90
+ return true;
91
+
92
+ } else {
93
+ return false;
94
+ }
95
+ };
96
+
97
+ // EventEmitter is defined in src/node_events.cc
98
+ // EventEmitter.prototype.emit() is also defined there.
99
+ EventEmitter.prototype.addListener = function(type, listener) {
100
+ if ('function' !== typeof listener) {
101
+ throw new Error('addListener only takes instances of Function');
102
+ }
103
+
104
+ if (!this._events) this._events = {};
105
+
106
+ // To avoid recursion in the case that type == "newListeners"! Before
107
+ // adding it to the listeners, first emit "newListeners".
108
+ this.emit('newListener', type, listener);
109
+
110
+ if (!this._events[type]) {
111
+ // Optimize the case of one listener. Don't need the extra array object.
112
+ this._events[type] = listener;
113
+ } else if (isArray(this._events[type])) {
114
+
115
+ // If we've already got an array, just append.
116
+ this._events[type].push(listener);
117
+
118
+ // Check for listener leak
119
+ if (!this._events[type].warned) {
120
+ var m;
121
+ if (this._maxListeners !== undefined) {
122
+ m = this._maxListeners;
123
+ } else {
124
+ m = defaultMaxListeners;
125
+ }
126
+
127
+ if (m && m > 0 && this._events[type].length > m) {
128
+ this._events[type].warned = true;
129
+ console.error('(node) warning: possible EventEmitter memory ' +
130
+ 'leak detected. %d listeners added. ' +
131
+ 'Use emitter.setMaxListeners() to increase limit.',
132
+ this._events[type].length);
133
+ console.trace();
134
+ }
135
+ }
136
+ } else {
137
+ // Adding the second element, need to change to array.
138
+ this._events[type] = [this._events[type], listener];
139
+ }
140
+
141
+ return this;
142
+ };
143
+
144
+ EventEmitter.prototype.on = EventEmitter.prototype.addListener;
145
+
146
+ EventEmitter.prototype.once = function(type, listener) {
147
+ if ('function' !== typeof listener) {
148
+ throw new Error('.once only takes instances of Function');
149
+ }
150
+
151
+ var self = this;
152
+ function g() {
153
+ self.removeListener(type, g);
154
+ listener.apply(this, arguments);
155
+ };
156
+
157
+ g.listener = listener;
158
+ self.on(type, g);
159
+
160
+ return this;
161
+ };
162
+
163
+ EventEmitter.prototype.removeListener = function(type, listener) {
164
+ if ('function' !== typeof listener) {
165
+ throw new Error('removeListener only takes instances of Function');
166
+ }
167
+
168
+ // does not use listeners(), so no side effect of creating _events[type]
169
+ if (!this._events || !this._events[type]) return this;
170
+
171
+ var list = this._events[type];
172
+
173
+ if (isArray(list)) {
174
+ var position = -1;
175
+ for (var i = 0, length = list.length; i < length; i++) {
176
+ if (list[i] === listener ||
177
+ (list[i].listener && list[i].listener === listener))
178
+ {
179
+ position = i;
180
+ break;
181
+ }
182
+ }
183
+
184
+ if (position < 0) return this;
185
+ list.splice(position, 1);
186
+ if (list.length == 0)
187
+ delete this._events[type];
188
+ } else if (list === listener ||
189
+ (list.listener && list.listener === listener))
190
+ {
191
+ delete this._events[type];
192
+ }
193
+
194
+ return this;
195
+ };
196
+
197
+ EventEmitter.prototype.removeAllListeners = function(type) {
198
+ if (arguments.length === 0) {
199
+ this._events = {};
200
+ return this;
201
+ }
202
+
203
+ // does not use listeners(), so no side effect of creating _events[type]
204
+ if (type && this._events && this._events[type]) this._events[type] = null;
205
+ return this;
206
+ };
207
+
208
+ EventEmitter.prototype.listeners = function(type) {
209
+ if (!this._events) this._events = {};
210
+ if (!this._events[type]) this._events[type] = [];
211
+ if (!isArray(this._events[type])) {
212
+ this._events[type] = [this._events[type]];
213
+ }
214
+ return this._events[type];
215
+ };
216
+