konacha-chai-matchers 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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.gitmodules ADDED
@@ -0,0 +1,36 @@
1
+ [submodule "chai-spies"]
2
+ path = chai-spies
3
+ url = git://github.com/chaijs/chai-spies.git
4
+ [submodule "sinon-chai"]
5
+ path = sinon-chai
6
+ url = git://github.com/domenic/sinon-chai.git
7
+ [submodule "chai-as-promised"]
8
+ path = chai-as-promised
9
+ url = git://github.com/domenic/chai-as-promised.git
10
+ [submodule "chai-jquery"]
11
+ path = chai-jquery
12
+ url = git://github.com/chaijs/chai-jquery.git
13
+ [submodule "chai-timers"]
14
+ path = chai-timers
15
+ url = git://github.com/chaijs/chai-timers.git
16
+ [submodule "chai-stats"]
17
+ path = chai-stats
18
+ url = git://github.com/chaijs/chai-stats.git
19
+ [submodule "chai-null"]
20
+ path = chai-null
21
+ url = git://github.com/chaijs/chai-null.git
22
+ [submodule "chai-factories"]
23
+ path = chai-factories
24
+ url = git://github.com/chaijs/chai-factories.git
25
+ [submodule "chai-changes"]
26
+ path = chai-changes
27
+ url = https://github.com/matthijsgroen/chai-changes.git
28
+ [submodule "chai-backbone"]
29
+ path = chai-backbone
30
+ url = https://github.com/matthijsgroen/chai-backbone.git
31
+ [submodule "Sinon.JS"]
32
+ path = Sinon.JS
33
+ url = git://github.com/cjohansen/Sinon.JS.git
34
+ [submodule "js-factories"]
35
+ path = js-factories
36
+ url = git://github.com/matthijsgroen/js-factories.git
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rake'
4
+ gem 'juicer'
5
+
6
+ # Specify your gem's dependencies in konacha-chai-matchers.gemspec
7
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Matthijs Groen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ Konacha Chai Matchers
2
+ =====================
3
+
4
+ This library contains all [Chai.js plugins](http://chaijs.com/plugins)
5
+
6
+ Not all plugins are tested!
7
+
8
+ Installation
9
+ ------------
10
+
11
+ Add in the `Gemfile`:
12
+
13
+ gem 'konacha-chai-matchers'
14
+
15
+
16
+ Usage
17
+ -----
18
+
19
+ 1. Check the vendor/assets/javascripts
20
+ 2. Require the files needed
21
+
22
+ Example:
23
+
24
+ #= require sinon
25
+ #= require chai-changes
26
+ #= require js-factories
27
+ #= require chai-backbone
28
+ #= require chai-jquery
29
+
30
+ Contribution
31
+ ------------
32
+
33
+ Please submit an Github issue for libraries you want included or where the wrong file ends up in the `vendor` folder.
34
+
35
+ Updating the vendor libraries is done through `rake update`
36
+
37
+ please check `tasks/update.rb` for the code
38
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ Dir['tasks/*.rb'].each { |ext| load ext } if defined? Rake
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "konacha-chai-matchers/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "konacha-chai-matchers"
7
+ s.version = Konacha::Chai::Matchers::VERSION
8
+ s.authors = ["Matthijs Groen"]
9
+ s.email = ["matthijs.groen@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Chai.js plugins collection for Konacha}
12
+ s.description = %q{A set of Chai.js libraries ready to use for Konacha}
13
+
14
+ s.rubyforge_project = "konacha-chai-matchers"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.license = 'MIT'
21
+ end
@@ -0,0 +1,11 @@
1
+ require "konacha-chai-matchers/version"
2
+
3
+ module Konacha
4
+ module Chai
5
+ module Matchers
6
+ class Engine < ::Rails::Engine
7
+
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Konacha
2
+ module Chai
3
+ module Matchers
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
data/tasks/update.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'pathname'
2
+
3
+ desc 'updates all submodules'
4
+ task :update do
5
+ modules = collect_modules
6
+ update_modules modules
7
+ vendor_files modules
8
+ end
9
+
10
+ def collect_modules
11
+ modules = []
12
+ File.open('.gitmodules') do |f|
13
+ contents = f.read
14
+ contents.each_line do |line|
15
+ modules << $1 if line =~ /\[submodule "(.*)"\]/
16
+ end
17
+ end
18
+ modules
19
+ end
20
+
21
+ def main_files folders
22
+ folders.map do |f|
23
+ file_search = f.downcase
24
+ file_search << '.js' unless file_search =~ /\.js$/
25
+
26
+ path_order = ["./#{f}/pkg/#{file_search}", "./#{f}/#{file_search}", "./#{f}/lib/#{file_search}"]
27
+
28
+ r = path_order.map do |p|
29
+ p if File.exist? p
30
+ end.flatten.compact.first
31
+ end.flatten.compact
32
+ end
33
+
34
+ def vendor_files modules
35
+ files = main_files modules
36
+ puts "found #{files.length} of the #{modules.length} lib files" unless files.length == modules.length
37
+ path = "./vendor/assets/javascripts/"
38
+ Pathname.new(path).mkpath()
39
+ FileUtils.cp files, path
40
+ end
41
+
42
+ def update_modules modules
43
+ modules.each do |mod|
44
+ `cd ./#{mod} && git pull`
45
+ `cd ./#{mod} && ./build` if File.exist? "./#{mod}/build"
46
+ end
47
+ end
@@ -0,0 +1,391 @@
1
+ (function (chaiAsPromised) {
2
+ "use strict";
3
+
4
+ // Module systems magic dance.
5
+
6
+ if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
7
+ // NodeJS
8
+ module.exports = chaiAsPromised;
9
+ } else if (typeof define === "function" && define.amd) {
10
+ // AMD
11
+ define(function () {
12
+ return chaiAsPromised;
13
+ });
14
+ } else {
15
+ // Other environment (usually <script> tag): plug in to global chai instance directly.
16
+ chai.use(chaiAsPromised);
17
+ }
18
+ }(function chaiAsPromised(chai, utils) {
19
+ "use strict";
20
+
21
+ var Assertion = chai.Assertion;
22
+ var assert = chai.assert;
23
+
24
+ function assertIsAboutPromise(assertion) {
25
+ if (typeof assertion._obj.then !== "function") {
26
+ throw new TypeError(utils.inspect(assertion._obj) + " is not a promise!");
27
+ }
28
+ if (typeof assertion._obj.pipe === "function") {
29
+ throw new TypeError("Chai as Promised is incompatible with jQuery's so-called “promises.” Sorry!");
30
+ }
31
+ }
32
+
33
+ function property(name, asserter) {
34
+ utils.addProperty(Assertion.prototype, name, function () {
35
+ assertIsAboutPromise(this);
36
+ return asserter.apply(this, arguments);
37
+ });
38
+ }
39
+
40
+ function method(name, asserter) {
41
+ utils.addMethod(Assertion.prototype, name, function () {
42
+ assertIsAboutPromise(this);
43
+ return asserter.apply(this, arguments);
44
+ });
45
+ }
46
+
47
+ function notify(promise, callback) {
48
+ return promise.then(function () { callback(); }, callback);
49
+ }
50
+
51
+ function addNotifyMethod(extensiblePromise) {
52
+ extensiblePromise.notify = function (callback) {
53
+ return notify(extensiblePromise, callback);
54
+ };
55
+ }
56
+
57
+ var fulfilledAsserter = function () {
58
+ var transformedPromise = this._obj.then(
59
+ function (value) {
60
+ if (utils.flag(this, "negate")) {
61
+ // If we're negated, `this.assert`'s behavior is actually flipped, so `this.assert(true, ...)` will
62
+ // throw an error, as desired.
63
+ this.assert(true, null, "expected promise to be rejected but it was fulfilled with " +
64
+ utils.inspect(value));
65
+ }
66
+
67
+ return value;
68
+ }.bind(this),
69
+ function (reason) {
70
+ // If we're in a negated state (i.e. `.not.fulfilled`) then this assertion will get flipped and thus
71
+ // pass, as desired.
72
+ this.assert(false, "expected promise to be fulfilled but it was rejected with " +
73
+ utils.inspect(reason));
74
+ }.bind(this)
75
+ );
76
+
77
+ return makeAssertionPromise(transformedPromise, this);
78
+ };
79
+
80
+ var rejectedAsserter = function () {
81
+ // THIS SHIT IS COMPLICATED. Best illustrated by exhaustive example.
82
+ ////////////////////////////////////////////////////////////////////
83
+ // `fulfilledPromise.should.be.rejected`:
84
+ // `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects
85
+ // `fulfilledPromise.should.not.be.rejected`:
86
+ // `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills
87
+ // `rejectedPromise.should.be.rejected`:
88
+ // `onOriginalRejected` does nothing relevant → fulfills
89
+ // `rejectedPromise.should.not.be.rejected`:
90
+ // `onOriginalRejected` → `this.assert(true, …)` throws → rejects
91
+ // `rejectedPromise.should.be.rejected.with(xxx)`:
92
+ // `onOriginalRejected` saves `rejectionReason` → fulfills →
93
+ // `with(xxx)` called → `onTransformedFulfilled` → assert about xxx → fulfills/rejects based on asserts
94
+ // `rejectedPromise.should.not.be.rejected.with(xxx)`:
95
+ // `onOriginalRejected` saves `rejectionReason`, `this.assert(true, …)` throws → rejects →
96
+ // `with(xxx)` called → `onTransformedRejected` → assert about xxx → fulfills/rejects based on asserts
97
+ // `fulfilledPromise.should.be.rejected.with(xxx)`:
98
+ // `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects →
99
+ // `with(xxx)` called → `onTransformedRejected` → `this.assert(false, …)` throws → rejected
100
+ // `fulfilledPromise.should.not.be.rejected.with(xxx)`:
101
+ // `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills →
102
+ // `with(xxx)` called → `onTransformedFulfilled` → fulfills
103
+
104
+ var rejectionReason = null;
105
+
106
+ var onOriginalFulfilled = function (value) {
107
+ this.assert(false, "expected promise to be rejected but it was fulfilled with " + utils.inspect(value));
108
+ }.bind(this);
109
+
110
+ var onOriginalRejected = function (reason) {
111
+ // Store the reason so that `with` can look at it later. Be sure to do this before asserting, since
112
+ // throwing an error from the assert would cancel the process.
113
+ rejectionReason = reason;
114
+
115
+ if (utils.flag(this, "negate")) {
116
+ this.assert(true, null, "expected promise to be fulfilled but it was rejected with " +
117
+ utils.inspect(reason));
118
+ }
119
+
120
+ // If we didn't throw from the assert, transform rejections into fulfillments, by not re-throwing the
121
+ // reason.
122
+ }.bind(this);
123
+
124
+ var withMethod = function (Constructor, message) {
125
+ var desiredReason = null;
126
+
127
+ if (Constructor instanceof RegExp || typeof Constructor === "string") {
128
+ message = Constructor;
129
+ Constructor = null;
130
+ } else if (Constructor && Constructor instanceof Error) {
131
+ desiredReason = Constructor;
132
+ Constructor = null;
133
+ message = null;
134
+ }
135
+
136
+ var messageVerb = null;
137
+ var messageIsGood = null;
138
+
139
+ if (message instanceof RegExp) {
140
+ messageVerb = "matching";
141
+ messageIsGood = function () {
142
+ return message.test(rejectionReason.message);
143
+ };
144
+ } else {
145
+ messageVerb = "including";
146
+ messageIsGood = function () {
147
+ return rejectionReason.message.indexOf(message) !== -1;
148
+ };
149
+ }
150
+
151
+ function constructorIsGood() {
152
+ return rejectionReason instanceof Constructor;
153
+ }
154
+
155
+ function matchesDesiredReason() {
156
+ return rejectionReason === desiredReason;
157
+ }
158
+
159
+ var onTransformedFulfilled = function () {
160
+ if (!utils.flag(this, "negate")) {
161
+ if (desiredReason) {
162
+ this.assert(matchesDesiredReason(),
163
+ null,
164
+ "expected promise to be rejected with " + utils.inspect(desiredReason) + " but " +
165
+ "it was rejected with " + utils.inspect(rejectionReason));
166
+ }
167
+
168
+ if (Constructor) {
169
+ this.assert(constructorIsGood(),
170
+ "expected promise to be rejected with " + Constructor.prototype.name + " but it " +
171
+ "was rejected with " + utils.inspect(rejectionReason));
172
+ }
173
+
174
+ if (message) {
175
+ this.assert(messageIsGood(),
176
+ "expected promise to be rejected with an error " + messageVerb + " " + message +
177
+ " but got " + utils.inspect(rejectionReason.message));
178
+ }
179
+ }
180
+ }.bind(this);
181
+
182
+ var onTransformedRejected = function () {
183
+ if (utils.flag(this, "negate")) {
184
+ if (desiredReason) {
185
+ this.assert(matchesDesiredReason(),
186
+ null,
187
+ "expected promise to not be rejected with " + utils.inspect(desiredReason));
188
+ }
189
+
190
+ if (Constructor) {
191
+ this.assert(constructorIsGood(),
192
+ null,
193
+ "expected promise to not be rejected with " + Constructor.prototype.name);
194
+ }
195
+
196
+ if (message) {
197
+ this.assert(messageIsGood(),
198
+ null,
199
+ "expected promise to be not be rejected with an error " + messageVerb + " " +
200
+ message);
201
+ }
202
+ } else {
203
+ if (desiredReason) {
204
+ this.assert(false,
205
+ "expected promise to be rejected with " + utils.inspect(desiredReason) +
206
+ " but it was fulfilled");
207
+ }
208
+
209
+ if (Constructor) {
210
+ this.assert(false, "expected promise to be rejected with " + Constructor.prototype.name +
211
+ " but it was fulfilled");
212
+ }
213
+
214
+ if (message) {
215
+ this.assert(false, "expected promise to be rejected with an error " + messageVerb + " " +
216
+ message + " but it was fulfilled");
217
+ }
218
+ }
219
+ }.bind(this);
220
+
221
+ return makeAssertionPromise(transformedPromise.then(onTransformedFulfilled, onTransformedRejected), this);
222
+ }.bind(this);
223
+
224
+ var transformedPromise = makeAssertionPromise(this._obj.then(onOriginalFulfilled, onOriginalRejected), this);
225
+ Object.defineProperty(transformedPromise, "with", { enumerable: true, configurable: true, value: withMethod });
226
+
227
+ return transformedPromise;
228
+ };
229
+
230
+ function isChaiAsPromisedAsserter(asserterName) {
231
+ return ["fulfilled", "rejected", "broken", "eventually", "become"].indexOf(asserterName) !== -1;
232
+ }
233
+
234
+ function makeAssertionPromiseToDoAsserter(currentAssertionPromise, previousAssertionPromise, doAsserter) {
235
+ var promiseToDoAsserter = currentAssertionPromise.then(function (fulfillmentValue) {
236
+ // The previous assertion promise might have picked up some flags while waiting for fulfillment.
237
+ utils.transferFlags(previousAssertionPromise, currentAssertionPromise);
238
+
239
+ // Replace the object flag with the fulfillment value, so that doAsserter can operate properly.
240
+ utils.flag(currentAssertionPromise, "object", fulfillmentValue);
241
+
242
+ // Perform the actual asserter action and return the result of it.
243
+ return doAsserter();
244
+ });
245
+ return makeAssertionPromise(promiseToDoAsserter, currentAssertionPromise);
246
+ }
247
+
248
+ function makeAssertionPromise(promise, baseAssertion) {
249
+ // An assertion-promise is an (extensible!) promise with the following additions:
250
+ var assertionPromise = Object.create(promise);
251
+
252
+ // 1. A `notify` method.
253
+ addNotifyMethod(assertionPromise);
254
+
255
+ // 2. An `assert` method that acts exactly as it would on an assertion. This is called by promisified
256
+ // asserters after the promise fulfills.
257
+ assertionPromise.assert = function () {
258
+ return Assertion.prototype.assert.apply(assertionPromise, arguments);
259
+ };
260
+
261
+ // 3. Chai asserters, which act upon the promise's fulfillment value.
262
+ var asserterNames = Object.getOwnPropertyNames(Assertion.prototype);
263
+ asserterNames.forEach(function (asserterName) {
264
+ // We already added `notify` and `assert`; don't mess with those.
265
+ if (asserterName === "notify" || asserterName === "assert") {
266
+ return;
267
+ }
268
+
269
+ // Only add asserters for other libraries; poison-pill Chai as Promised ones.
270
+ if (isChaiAsPromisedAsserter(asserterName)) {
271
+ utils.addProperty(assertionPromise, asserterName, function () {
272
+ throw new Error("Cannot use Chai as Promised asserters more than once in an assertion.");
273
+ });
274
+ return;
275
+ }
276
+
277
+ // The asserter will need to be added differently depending on its type. In all cases we use
278
+ // `makeAssertionPromiseToDoAsserter`, which, given this current `assertionPromise` we are going to
279
+ // return, plus the `baseAssertion` we are basing it off of, will return a new assertion-promise that
280
+ // builds off of `assertionPromise` and `baseAssertion` to perform the actual asserter action upon
281
+ // fulfillment.
282
+ var propertyDescriptor = Object.getOwnPropertyDescriptor(Assertion.prototype, asserterName);
283
+
284
+ if (typeof propertyDescriptor.value === "function") {
285
+ // Case 1: simple method asserters
286
+ utils.addMethod(assertionPromise, asserterName, function () {
287
+ var args = arguments;
288
+
289
+ return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
290
+ return propertyDescriptor.value.apply(assertionPromise, args);
291
+ });
292
+ });
293
+ } else if (typeof propertyDescriptor.get === "function") {
294
+ // Case 2: property asserters. These break down into two subcases: chainable methods, and pure
295
+ // properties. An example of the former is `a`/`an`: `.should.be.an.instanceOf` vs.
296
+ // `should.be.an("object")`.
297
+ var isChainableMethod = false;
298
+ try {
299
+ isChainableMethod = typeof propertyDescriptor.get.call({}) === "function";
300
+ } catch (e) { }
301
+
302
+ if (isChainableMethod) {
303
+ // Case 2A: chainable methods. Recreate the chainable method, but operating on the augmented
304
+ // promise. We need to copy both the assertion behavior and the chaining behavior, since the
305
+ // chaining behavior might for example set flags on the object.
306
+ utils.addChainableMethod(
307
+ assertionPromise,
308
+ asserterName,
309
+ function () {
310
+ var args = arguments;
311
+
312
+ return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
313
+ return propertyDescriptor.get().apply(assertionPromise, args);
314
+ });
315
+ },
316
+ function () {
317
+ return propertyDescriptor.get.call(assertionPromise);
318
+ }
319
+ );
320
+ } else {
321
+ // Case 2B: pure property case
322
+ utils.addProperty(assertionPromise, asserterName, function () {
323
+ return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
324
+ return propertyDescriptor.get.call(assertionPromise);
325
+ });
326
+ });
327
+ }
328
+ }
329
+ });
330
+
331
+ return assertionPromise;
332
+ }
333
+
334
+ property("fulfilled", fulfilledAsserter);
335
+ property("rejected", rejectedAsserter);
336
+ property("broken", rejectedAsserter);
337
+
338
+ property("eventually", function () {
339
+ return makeAssertionPromise(this._obj, this);
340
+ });
341
+
342
+ method("become", function (value) {
343
+ return this.eventually.eql(value);
344
+ });
345
+
346
+ method("notify", function (callback) {
347
+ return notify(this._obj, callback);
348
+ });
349
+
350
+ // Now use the Assertion framework to build an `assert` interface.
351
+ var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
352
+ return typeof assert[propName] === "function";
353
+ });
354
+
355
+ assert.isFulfilled = function (promise, message) {
356
+ return (new Assertion(promise, message)).to.be.fulfilled;
357
+ };
358
+
359
+ assert.isRejected = assert.isBroken = function (promise, toTestAgainst, message) {
360
+ if (typeof toTestAgainst === "string") {
361
+ message = toTestAgainst;
362
+ toTestAgainst = null;
363
+ }
364
+
365
+ var shouldBeRejectedPromise = (new Assertion(promise, message)).to.be.rejected;
366
+ return toTestAgainst ? shouldBeRejectedPromise.with(toTestAgainst) : shouldBeRejectedPromise;
367
+ };
368
+
369
+ assert.eventually = {};
370
+ originalAssertMethods.forEach(function (assertMethodName) {
371
+ assert.eventually[assertMethodName] = function (promise) {
372
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
373
+
374
+ var promiseToAssert = promise.then(function (fulfillmentValue) {
375
+ return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
376
+ });
377
+
378
+ var augmentedPromiseToAssert = Object.create(promiseToAssert);
379
+ addNotifyMethod(augmentedPromiseToAssert);
380
+ return augmentedPromiseToAssert;
381
+ };
382
+ });
383
+
384
+ assert.becomes = function (promise, value) {
385
+ return assert.eventually.deepEqual(promise, value);
386
+ };
387
+
388
+ assert.doesNotBecome = function (promise, value) {
389
+ return assert.eventually.notDeepEqual(promise, value);
390
+ };
391
+ }));