rack-cors 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack-cors might be problematic. Click here for more details.

checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTQyY2Q0OGNmOGZjMWNhOTc0OTdmOTY3ZTk0NDY0NzhmODE5YTI5Yg==
5
+ data.tar.gz: !binary |-
6
+ ZDQyMTJlMTg3MDczMmE0ZjFkMmZjOGExMGU3NDE0NmQ2MGY1YzBlZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NGViNTEzOTIzZjc2ZjVhNjZkZDBmODQ5NzAzNzk3ZThiYzZkMGU2YTU3MmUx
10
+ NDc3ODlkMTIwNjFmNGZhOWUwZmNjMmEyN2YxZWQ1NTA3NDU3NjEzMGYyNDdi
11
+ NTM4ZTI3NmQ4ZTE1MmNjOGY5ZTBlNDk2YTQyZDQ0NzA5ZTc2NTE=
12
+ data.tar.gz: !binary |-
13
+ NGIwN2QwMjQ1OTQwNzY3M2MzYWFiNWI0Y2RjMjVmMTEzMTk3YjE1ZGJhNjQw
14
+ YzRjNzhkZTRmZGExYjU2YzU5Y2Q4OTkyNzAyYTFhODRmNGViOGI0MTQyNmNh
15
+ MjAwYjUxZjRkMGZjMjRmMDJhYjRiOWU2ZmNjNTFjNmEzZTY2YWQ=
data/Gemfile CHANGED
@@ -1,12 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
- gem "rack"
4
-
5
- group :development do
6
- gem "rake"
7
- gem "shoulda"
8
- gem "mocha"
9
- gem "rack-test"
10
- gem "bundler", ">= 1.1.0"
11
- gem "jeweler", "~> 1.8.3"
12
- end
3
+ # Specify your gem's dependencies in rack-cors.gemspec
4
+ gemspec
data/LICENSE.txt CHANGED
@@ -1,4 +1,6 @@
1
- Copyright (c) 2012 Calvin Yu
1
+ Copyright (c) 2013 Calvin Yu
2
+
3
+ MIT License
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -33,7 +33,8 @@ You configure Rack::Cors by passing a block to the <tt>use</tt> command:
33
33
  resource '/file/at/*',
34
34
  :methods => [:get, :post, :put, :delete, :options],
35
35
  :headers => 'x-domain-token',
36
- :expose => ['Some-Custom-Response-Header']
36
+ :expose => ['Some-Custom-Response-Header'],
37
+ :max_age => 600
37
38
  # headers to expose
38
39
  end
39
40
 
@@ -49,13 +50,15 @@ from any origins on any resource of your application, methods :get, :post and :o
49
50
  module YourApp
50
51
  class Application < Rails::Application
51
52
 
52
- # ...
53
+ # ...
53
54
 
54
- config.middleware.use Rack::Cors do
55
- allow do
56
- origins '*'
57
- resource '*', :headers => :any, :methods => [:get, :post, :options]
55
+ config.middleware.use Rack::Cors do
56
+ allow do
57
+ origins '*'
58
+ resource '*', :headers => :any, :methods => [:get, :post, :options]
59
+ end
58
60
  end
61
+
59
62
  end
60
63
  end
61
64
 
data/Rakefile CHANGED
@@ -1,29 +1,4 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler'
5
- begin
6
- Bundler.setup(:default, :development)
7
- rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
10
- exit e.status_code
11
- end
12
- require 'rake'
13
-
14
- require 'jeweler'
15
- Jeweler::Tasks.new do |gem|
16
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
- gem.name = "rack-cors"
18
- gem.homepage = "http://github.com/cyu/rack-cors"
19
- gem.license = "MIT"
20
- gem.summary = "Middleware for enabling Cross-Origin Resource Sharing in Rack apps"
21
- gem.description = "Middleware that will make Rack-based apps CORS compatible. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the project here: http://github.com/cyu/rack-cors"
22
- gem.email = "me@sourcebender.com"
23
- gem.authors = ["Calvin Yu"]
24
- # dependencies defined in Gemfile
25
- end
26
- Jeweler::RubygemsDotOrgTasks.new
1
+ require "bundler/gem_tasks"
27
2
 
28
3
  require 'rake/testtask'
29
4
  Rake::TestTask.new(:test) do |test|
@@ -39,7 +14,7 @@ Rake::RDocTask.new do |rdoc|
39
14
  version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
15
 
41
16
  rdoc.rdoc_dir = 'rdoc'
42
- rdoc.title = "rack-cors2 #{version}"
17
+ rdoc.title = "rack-cors #{version}"
43
18
  rdoc.rdoc_files.include('README*')
44
19
  rdoc.rdoc_files.include('lib/**/*.rb')
45
20
  end
data/lib/rack/cors.rb CHANGED
@@ -39,7 +39,7 @@ module Rack
39
39
  " Access-Control-Request-Headers: #{env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"
40
40
  ].join("\n")
41
41
  end
42
- if env['REQUEST_METHOD'] == 'OPTIONS'
42
+ if env['REQUEST_METHOD'] == 'OPTIONS' and env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']
43
43
  if headers = process_preflight(env)
44
44
  debug(env) do
45
45
  "Preflight Headers:\n" +
@@ -54,6 +54,8 @@ module Rack
54
54
  status, headers, body = @app.call env
55
55
  if cors_headers
56
56
  headers = headers.merge(cors_headers)
57
+
58
+ # http://www.w3.org/TR/cors/#resource-implementation
57
59
  unless headers['Access-Control-Allow-Origin'] == '*'
58
60
  vary = headers['Vary']
59
61
  headers['Vary'] = ((vary ? vary.split(/,\s*/) : []) + ['Origin']).uniq.join(', ')
@@ -87,8 +89,12 @@ module Rack
87
89
  end
88
90
 
89
91
  def find_resource(origin, path, env)
90
- allowed = all_resources.detect {|r| r.allow_origin?(origin,env)}
91
- allowed ? allowed.find_resource(path) : nil
92
+ all_resources.each do |r|
93
+ if r.allow_origin?(origin, env) and found = r.find_resource(path)
94
+ return found
95
+ end
96
+ end
97
+ nil
92
98
  end
93
99
 
94
100
  class Resources
@@ -104,9 +110,9 @@ module Rack
104
110
  when Regexp, /^https?:\/\// then n
105
111
  when 'file://' then n
106
112
  when '*' then @public_resources = true; n
107
- else "http://#{n}"
113
+ else ["http://#{n}", "https://#{n}"]
108
114
  end
109
- end
115
+ end.flatten
110
116
  @origins.push(blk) if blk
111
117
  end
112
118
 
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class Cors
3
+ VERSION = "0.2.9"
4
+ end
5
+ end
data/rack-cors.gemspec CHANGED
@@ -1,67 +1,26 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/cors/version'
5
5
 
6
- Gem::Specification.new do |s|
7
- s.name = "rack-cors"
8
- s.version = "0.2.8"
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-cors"
8
+ spec.version = Rack::Cors::VERSION
9
+ spec.authors = ["Calvin Yu"]
10
+ spec.email = ["me@sourcebender.com"]
11
+ spec.description = %q{Middleware that will make Rack-based apps CORS compatible. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the project here: http://github.com/cyu/rack-cors}
12
+ spec.summary = %q{Middleware for enabling Cross-Origin Resource Sharing in Rack apps}
13
+ spec.homepage = "http://github.com/cyu/rack-cors"
14
+ spec.license = "MIT"
9
15
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Calvin Yu"]
12
- s.date = "2013-05-31"
13
- s.description = "Middleware that will make Rack-based apps CORS compatible. Read more here: http://blog.sourcebender.com/2010/06/09/introducin-rack-cors.html. Fork the project here: http://github.com/cyu/rack-cors"
14
- s.email = "me@sourcebender.com"
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- "Gemfile",
21
- "LICENSE.txt",
22
- "README.rdoc",
23
- "Rakefile",
24
- "VERSION",
25
- "lib/rack/cors.rb",
26
- "rack-cors.gemspec",
27
- "test/unit/cors_test.rb",
28
- "test/unit/dsl_test.rb",
29
- "test/unit/test.ru"
30
- ]
31
- s.homepage = "http://github.com/cyu/rack-cors"
32
- s.licenses = ["MIT"]
33
- s.require_paths = ["lib"]
34
- s.rubygems_version = "1.8.23"
35
- s.summary = "Middleware for enabling Cross-Origin Resource Sharing in Rack apps"
16
+ spec.files = `git ls-files`.split($/).reject { |f| f == '.gitignore' or f =~ /^examples/ }
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
36
20
 
37
- if s.respond_to? :specification_version then
38
- s.specification_version = 3
39
-
40
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
41
- s.add_runtime_dependency(%q<rack>, [">= 0"])
42
- s.add_development_dependency(%q<rake>, [">= 0"])
43
- s.add_development_dependency(%q<shoulda>, [">= 0"])
44
- s.add_development_dependency(%q<mocha>, [">= 0"])
45
- s.add_development_dependency(%q<rack-test>, [">= 0"])
46
- s.add_development_dependency(%q<bundler>, [">= 1.1.0"])
47
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
48
- else
49
- s.add_dependency(%q<rack>, [">= 0"])
50
- s.add_dependency(%q<rake>, [">= 0"])
51
- s.add_dependency(%q<shoulda>, [">= 0"])
52
- s.add_dependency(%q<mocha>, [">= 0"])
53
- s.add_dependency(%q<rack-test>, [">= 0"])
54
- s.add_dependency(%q<bundler>, [">= 1.1.0"])
55
- s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
56
- end
57
- else
58
- s.add_dependency(%q<rack>, [">= 0"])
59
- s.add_dependency(%q<rake>, [">= 0"])
60
- s.add_dependency(%q<shoulda>, [">= 0"])
61
- s.add_dependency(%q<mocha>, [">= 0"])
62
- s.add_dependency(%q<rack-test>, [">= 0"])
63
- s.add_dependency(%q<bundler>, [">= 1.1.0"])
64
- s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
65
- end
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "shoulda", ">= 0"
24
+ spec.add_development_dependency "mocha", ">= 0.14.0"
25
+ spec.add_development_dependency "rack-test", ">= 0"
66
26
  end
67
-
@@ -0,0 +1,1286 @@
1
+ (function (global, module) {
2
+
3
+ var exports = module.exports;
4
+
5
+ /**
6
+ * Exports.
7
+ */
8
+
9
+ module.exports = expect;
10
+ expect.Assertion = Assertion;
11
+
12
+ /**
13
+ * Exports version.
14
+ */
15
+
16
+ expect.version = '0.1.2';
17
+
18
+ /**
19
+ * Possible assertion flags.
20
+ */
21
+
22
+ var flags = {
23
+ not: ['to', 'be', 'have', 'include', 'only']
24
+ , to: ['be', 'have', 'include', 'only', 'not']
25
+ , only: ['have']
26
+ , have: ['own']
27
+ , be: ['an']
28
+ };
29
+
30
+ function expect (obj) {
31
+ return new Assertion(obj);
32
+ }
33
+
34
+ /**
35
+ * Constructor
36
+ *
37
+ * @api private
38
+ */
39
+
40
+ function Assertion (obj, flag, parent) {
41
+ this.obj = obj;
42
+ this.flags = {};
43
+
44
+ if (undefined != parent) {
45
+ this.flags[flag] = true;
46
+
47
+ for (var i in parent.flags) {
48
+ if (parent.flags.hasOwnProperty(i)) {
49
+ this.flags[i] = true;
50
+ }
51
+ }
52
+ }
53
+
54
+ var $flags = flag ? flags[flag] : keys(flags)
55
+ , self = this
56
+
57
+ if ($flags) {
58
+ for (var i = 0, l = $flags.length; i < l; i++) {
59
+ // avoid recursion
60
+ if (this.flags[$flags[i]]) continue;
61
+
62
+ var name = $flags[i]
63
+ , assertion = new Assertion(this.obj, name, this)
64
+
65
+ if ('function' == typeof Assertion.prototype[name]) {
66
+ // clone the function, make sure we dont touch the prot reference
67
+ var old = this[name];
68
+ this[name] = function () {
69
+ return old.apply(self, arguments);
70
+ }
71
+
72
+ for (var fn in Assertion.prototype) {
73
+ if (Assertion.prototype.hasOwnProperty(fn) && fn != name) {
74
+ this[name][fn] = bind(assertion[fn], assertion);
75
+ }
76
+ }
77
+ } else {
78
+ this[name] = assertion;
79
+ }
80
+ }
81
+ }
82
+ };
83
+
84
+ /**
85
+ * Performs an assertion
86
+ *
87
+ * @api private
88
+ */
89
+
90
+ Assertion.prototype.assert = function (truth, msg, error, expected) {
91
+ var msg = this.flags.not ? error : msg
92
+ , ok = this.flags.not ? !truth : truth
93
+ , err;
94
+
95
+ if (!ok) {
96
+ err = new Error(msg.call(this));
97
+ if (arguments.length > 3) {
98
+ err.actual = this.obj;
99
+ err.expected = expected;
100
+ err.showDiff = true;
101
+ }
102
+ throw err;
103
+ }
104
+
105
+ this.and = new Assertion(this.obj);
106
+ };
107
+
108
+ /**
109
+ * Check if the value is truthy
110
+ *
111
+ * @api public
112
+ */
113
+
114
+ Assertion.prototype.ok = function () {
115
+ this.assert(
116
+ !!this.obj
117
+ , function(){ return 'expected ' + i(this.obj) + ' to be truthy' }
118
+ , function(){ return 'expected ' + i(this.obj) + ' to be falsy' });
119
+ };
120
+
121
+ /**
122
+ * Creates an anonymous function which calls fn with arguments.
123
+ *
124
+ * @api public
125
+ */
126
+
127
+ Assertion.prototype.withArgs = function() {
128
+ expect(this.obj).to.be.a('function');
129
+ var fn = this.obj;
130
+ var args = Array.prototype.slice.call(arguments);
131
+ return expect(function() { fn.apply(null, args); });
132
+ }
133
+
134
+ /**
135
+ * Assert that the function throws.
136
+ *
137
+ * @param {Function|RegExp} callback, or regexp to match error string against
138
+ * @api public
139
+ */
140
+
141
+ Assertion.prototype.throwError =
142
+ Assertion.prototype.throwException = function (fn) {
143
+ expect(this.obj).to.be.a('function');
144
+
145
+ var thrown = false
146
+ , not = this.flags.not
147
+
148
+ try {
149
+ this.obj();
150
+ } catch (e) {
151
+ if (isRegExp(fn)) {
152
+ var subject = 'string' == typeof e ? e : e.message;
153
+ if (not) {
154
+ expect(subject).to.not.match(fn);
155
+ } else {
156
+ expect(subject).to.match(fn);
157
+ }
158
+ } else if ('function' == typeof fn) {
159
+ fn(e);
160
+ }
161
+ thrown = true;
162
+ }
163
+
164
+ if (isRegExp(fn) && not) {
165
+ // in the presence of a matcher, ensure the `not` only applies to
166
+ // the matching.
167
+ this.flags.not = false;
168
+ }
169
+
170
+ var name = this.obj.name || 'fn';
171
+ this.assert(
172
+ thrown
173
+ , function(){ return 'expected ' + name + ' to throw an exception' }
174
+ , function(){ return 'expected ' + name + ' not to throw an exception' });
175
+ };
176
+
177
+ /**
178
+ * Checks if the array is empty.
179
+ *
180
+ * @api public
181
+ */
182
+
183
+ Assertion.prototype.empty = function () {
184
+ var expectation;
185
+
186
+ if ('object' == typeof this.obj && null !== this.obj && !isArray(this.obj)) {
187
+ if ('number' == typeof this.obj.length) {
188
+ expectation = !this.obj.length;
189
+ } else {
190
+ expectation = !keys(this.obj).length;
191
+ }
192
+ } else {
193
+ if ('string' != typeof this.obj) {
194
+ expect(this.obj).to.be.an('object');
195
+ }
196
+
197
+ expect(this.obj).to.have.property('length');
198
+ expectation = !this.obj.length;
199
+ }
200
+
201
+ this.assert(
202
+ expectation
203
+ , function(){ return 'expected ' + i(this.obj) + ' to be empty' }
204
+ , function(){ return 'expected ' + i(this.obj) + ' to not be empty' });
205
+ return this;
206
+ };
207
+
208
+ /**
209
+ * Checks if the obj exactly equals another.
210
+ *
211
+ * @api public
212
+ */
213
+
214
+ Assertion.prototype.be =
215
+ Assertion.prototype.equal = function (obj) {
216
+ this.assert(
217
+ obj === this.obj
218
+ , function(){ return 'expected ' + i(this.obj) + ' to equal ' + i(obj) }
219
+ , function(){ return 'expected ' + i(this.obj) + ' to not equal ' + i(obj) });
220
+ return this;
221
+ };
222
+
223
+ /**
224
+ * Checks if the obj sortof equals another.
225
+ *
226
+ * @api public
227
+ */
228
+
229
+ Assertion.prototype.eql = function (obj) {
230
+ this.assert(
231
+ expect.eql(this.obj, obj)
232
+ , function(){ return 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj) }
233
+ , function(){ return 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj) }
234
+ , obj);
235
+ return this;
236
+ };
237
+
238
+ /**
239
+ * Assert within start to finish (inclusive).
240
+ *
241
+ * @param {Number} start
242
+ * @param {Number} finish
243
+ * @api public
244
+ */
245
+
246
+ Assertion.prototype.within = function (start, finish) {
247
+ var range = start + '..' + finish;
248
+ this.assert(
249
+ this.obj >= start && this.obj <= finish
250
+ , function(){ return 'expected ' + i(this.obj) + ' to be within ' + range }
251
+ , function(){ return 'expected ' + i(this.obj) + ' to not be within ' + range });
252
+ return this;
253
+ };
254
+
255
+ /**
256
+ * Assert typeof / instance of
257
+ *
258
+ * @api public
259
+ */
260
+
261
+ Assertion.prototype.a =
262
+ Assertion.prototype.an = function (type) {
263
+ if ('string' == typeof type) {
264
+ // proper english in error msg
265
+ var n = /^[aeiou]/.test(type) ? 'n' : '';
266
+
267
+ // typeof with support for 'array'
268
+ this.assert(
269
+ 'array' == type ? isArray(this.obj) :
270
+ 'regexp' == type ? isRegExp(this.obj) :
271
+ 'object' == type
272
+ ? 'object' == typeof this.obj && null !== this.obj
273
+ : type == typeof this.obj
274
+ , function(){ return 'expected ' + i(this.obj) + ' to be a' + n + ' ' + type }
275
+ , function(){ return 'expected ' + i(this.obj) + ' not to be a' + n + ' ' + type });
276
+ } else {
277
+ // instanceof
278
+ var name = type.name || 'supplied constructor';
279
+ this.assert(
280
+ this.obj instanceof type
281
+ , function(){ return 'expected ' + i(this.obj) + ' to be an instance of ' + name }
282
+ , function(){ return 'expected ' + i(this.obj) + ' not to be an instance of ' + name });
283
+ }
284
+
285
+ return this;
286
+ };
287
+
288
+ /**
289
+ * Assert numeric value above _n_.
290
+ *
291
+ * @param {Number} n
292
+ * @api public
293
+ */
294
+
295
+ Assertion.prototype.greaterThan =
296
+ Assertion.prototype.above = function (n) {
297
+ this.assert(
298
+ this.obj > n
299
+ , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n }
300
+ , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n });
301
+ return this;
302
+ };
303
+
304
+ /**
305
+ * Assert numeric value below _n_.
306
+ *
307
+ * @param {Number} n
308
+ * @api public
309
+ */
310
+
311
+ Assertion.prototype.lessThan =
312
+ Assertion.prototype.below = function (n) {
313
+ this.assert(
314
+ this.obj < n
315
+ , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n }
316
+ , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n });
317
+ return this;
318
+ };
319
+
320
+ /**
321
+ * Assert string value matches _regexp_.
322
+ *
323
+ * @param {RegExp} regexp
324
+ * @api public
325
+ */
326
+
327
+ Assertion.prototype.match = function (regexp) {
328
+ this.assert(
329
+ regexp.exec(this.obj)
330
+ , function(){ return 'expected ' + i(this.obj) + ' to match ' + regexp }
331
+ , function(){ return 'expected ' + i(this.obj) + ' not to match ' + regexp });
332
+ return this;
333
+ };
334
+
335
+ /**
336
+ * Assert property "length" exists and has value of _n_.
337
+ *
338
+ * @param {Number} n
339
+ * @api public
340
+ */
341
+
342
+ Assertion.prototype.length = function (n) {
343
+ expect(this.obj).to.have.property('length');
344
+ var len = this.obj.length;
345
+ this.assert(
346
+ n == len
347
+ , function(){ return 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len }
348
+ , function(){ return 'expected ' + i(this.obj) + ' to not have a length of ' + len });
349
+ return this;
350
+ };
351
+
352
+ /**
353
+ * Assert property _name_ exists, with optional _val_.
354
+ *
355
+ * @param {String} name
356
+ * @param {Mixed} val
357
+ * @api public
358
+ */
359
+
360
+ Assertion.prototype.property = function (name, val) {
361
+ if (this.flags.own) {
362
+ this.assert(
363
+ Object.prototype.hasOwnProperty.call(this.obj, name)
364
+ , function(){ return 'expected ' + i(this.obj) + ' to have own property ' + i(name) }
365
+ , function(){ return 'expected ' + i(this.obj) + ' to not have own property ' + i(name) });
366
+ return this;
367
+ }
368
+
369
+ if (this.flags.not && undefined !== val) {
370
+ if (undefined === this.obj[name]) {
371
+ throw new Error(i(this.obj) + ' has no property ' + i(name));
372
+ }
373
+ } else {
374
+ var hasProp;
375
+ try {
376
+ hasProp = name in this.obj
377
+ } catch (e) {
378
+ hasProp = undefined !== this.obj[name]
379
+ }
380
+
381
+ this.assert(
382
+ hasProp
383
+ , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) }
384
+ , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) });
385
+ }
386
+
387
+ if (undefined !== val) {
388
+ this.assert(
389
+ val === this.obj[name]
390
+ , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name)
391
+ + ' of ' + i(val) + ', but got ' + i(this.obj[name]) }
392
+ , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name)
393
+ + ' of ' + i(val) });
394
+ }
395
+
396
+ this.obj = this.obj[name];
397
+ return this;
398
+ };
399
+
400
+ /**
401
+ * Assert that the array contains _obj_ or string contains _obj_.
402
+ *
403
+ * @param {Mixed} obj|string
404
+ * @api public
405
+ */
406
+
407
+ Assertion.prototype.string =
408
+ Assertion.prototype.contain = function (obj) {
409
+ if ('string' == typeof this.obj) {
410
+ this.assert(
411
+ ~this.obj.indexOf(obj)
412
+ , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) }
413
+ , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) });
414
+ } else {
415
+ this.assert(
416
+ ~indexOf(this.obj, obj)
417
+ , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) }
418
+ , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) });
419
+ }
420
+ return this;
421
+ };
422
+
423
+ /**
424
+ * Assert exact keys or inclusion of keys by using
425
+ * the `.own` modifier.
426
+ *
427
+ * @param {Array|String ...} keys
428
+ * @api public
429
+ */
430
+
431
+ Assertion.prototype.key =
432
+ Assertion.prototype.keys = function ($keys) {
433
+ var str
434
+ , ok = true;
435
+
436
+ $keys = isArray($keys)
437
+ ? $keys
438
+ : Array.prototype.slice.call(arguments);
439
+
440
+ if (!$keys.length) throw new Error('keys required');
441
+
442
+ var actual = keys(this.obj)
443
+ , len = $keys.length;
444
+
445
+ // Inclusion
446
+ ok = every($keys, function (key) {
447
+ return ~indexOf(actual, key);
448
+ });
449
+
450
+ // Strict
451
+ if (!this.flags.not && this.flags.only) {
452
+ ok = ok && $keys.length == actual.length;
453
+ }
454
+
455
+ // Key string
456
+ if (len > 1) {
457
+ $keys = map($keys, function (key) {
458
+ return i(key);
459
+ });
460
+ var last = $keys.pop();
461
+ str = $keys.join(', ') + ', and ' + last;
462
+ } else {
463
+ str = i($keys[0]);
464
+ }
465
+
466
+ // Form
467
+ str = (len > 1 ? 'keys ' : 'key ') + str;
468
+
469
+ // Have / include
470
+ str = (!this.flags.only ? 'include ' : 'only have ') + str;
471
+
472
+ // Assertion
473
+ this.assert(
474
+ ok
475
+ , function(){ return 'expected ' + i(this.obj) + ' to ' + str }
476
+ , function(){ return 'expected ' + i(this.obj) + ' to not ' + str });
477
+
478
+ return this;
479
+ };
480
+ /**
481
+ * Assert a failure.
482
+ *
483
+ * @param {String ...} custom message
484
+ * @api public
485
+ */
486
+ Assertion.prototype.fail = function (msg) {
487
+ var error = function() { return msg || "explicit failure"; }
488
+ this.assert(false, error, error);
489
+ return this;
490
+ };
491
+
492
+ /**
493
+ * Function bind implementation.
494
+ */
495
+
496
+ function bind (fn, scope) {
497
+ return function () {
498
+ return fn.apply(scope, arguments);
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Array every compatibility
504
+ *
505
+ * @see bit.ly/5Fq1N2
506
+ * @api public
507
+ */
508
+
509
+ function every (arr, fn, thisObj) {
510
+ var scope = thisObj || global;
511
+ for (var i = 0, j = arr.length; i < j; ++i) {
512
+ if (!fn.call(scope, arr[i], i, arr)) {
513
+ return false;
514
+ }
515
+ }
516
+ return true;
517
+ };
518
+
519
+ /**
520
+ * Array indexOf compatibility.
521
+ *
522
+ * @see bit.ly/a5Dxa2
523
+ * @api public
524
+ */
525
+
526
+ function indexOf (arr, o, i) {
527
+ if (Array.prototype.indexOf) {
528
+ return Array.prototype.indexOf.call(arr, o, i);
529
+ }
530
+
531
+ if (arr.length === undefined) {
532
+ return -1;
533
+ }
534
+
535
+ for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0
536
+ ; i < j && arr[i] !== o; i++);
537
+
538
+ return j <= i ? -1 : i;
539
+ };
540
+
541
+ // https://gist.github.com/1044128/
542
+ var getOuterHTML = function(element) {
543
+ if ('outerHTML' in element) return element.outerHTML;
544
+ var ns = "http://www.w3.org/1999/xhtml";
545
+ var container = document.createElementNS(ns, '_');
546
+ var elemProto = (window.HTMLElement || window.Element).prototype;
547
+ var xmlSerializer = new XMLSerializer();
548
+ var html;
549
+ if (document.xmlVersion) {
550
+ return xmlSerializer.serializeToString(element);
551
+ } else {
552
+ container.appendChild(element.cloneNode(false));
553
+ html = container.innerHTML.replace('><', '>' + element.innerHTML + '<');
554
+ container.innerHTML = '';
555
+ return html;
556
+ }
557
+ };
558
+
559
+ // Returns true if object is a DOM element.
560
+ var isDOMElement = function (object) {
561
+ if (typeof HTMLElement === 'object') {
562
+ return object instanceof HTMLElement;
563
+ } else {
564
+ return object &&
565
+ typeof object === 'object' &&
566
+ object.nodeType === 1 &&
567
+ typeof object.nodeName === 'string';
568
+ }
569
+ };
570
+
571
+ /**
572
+ * Inspects an object.
573
+ *
574
+ * @see taken from node.js `util` module (copyright Joyent, MIT license)
575
+ * @api private
576
+ */
577
+
578
+ function i (obj, showHidden, depth) {
579
+ var seen = [];
580
+
581
+ function stylize (str) {
582
+ return str;
583
+ };
584
+
585
+ function format (value, recurseTimes) {
586
+ // Provide a hook for user-specified inspect functions.
587
+ // Check that value is an object with an inspect function on it
588
+ if (value && typeof value.inspect === 'function' &&
589
+ // Filter out the util module, it's inspect function is special
590
+ value !== exports &&
591
+ // Also filter out any prototype objects using the circular check.
592
+ !(value.constructor && value.constructor.prototype === value)) {
593
+ return value.inspect(recurseTimes);
594
+ }
595
+
596
+ // Primitive types cannot have properties
597
+ switch (typeof value) {
598
+ case 'undefined':
599
+ return stylize('undefined', 'undefined');
600
+
601
+ case 'string':
602
+ var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '')
603
+ .replace(/'/g, "\\'")
604
+ .replace(/\\"/g, '"') + '\'';
605
+ return stylize(simple, 'string');
606
+
607
+ case 'number':
608
+ return stylize('' + value, 'number');
609
+
610
+ case 'boolean':
611
+ return stylize('' + value, 'boolean');
612
+ }
613
+ // For some reason typeof null is "object", so special case here.
614
+ if (value === null) {
615
+ return stylize('null', 'null');
616
+ }
617
+
618
+ if (isDOMElement(value)) {
619
+ return getOuterHTML(value);
620
+ }
621
+
622
+ // Look up the keys of the object.
623
+ var visible_keys = keys(value);
624
+ var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys;
625
+
626
+ // Functions without properties can be shortcutted.
627
+ if (typeof value === 'function' && $keys.length === 0) {
628
+ if (isRegExp(value)) {
629
+ return stylize('' + value, 'regexp');
630
+ } else {
631
+ var name = value.name ? ': ' + value.name : '';
632
+ return stylize('[Function' + name + ']', 'special');
633
+ }
634
+ }
635
+
636
+ // Dates without properties can be shortcutted
637
+ if (isDate(value) && $keys.length === 0) {
638
+ return stylize(value.toUTCString(), 'date');
639
+ }
640
+
641
+ // Error objects can be shortcutted
642
+ if (value instanceof Error) {
643
+ return stylize("["+value.toString()+"]", 'Error');
644
+ }
645
+
646
+ var base, type, braces;
647
+ // Determine the object type
648
+ if (isArray(value)) {
649
+ type = 'Array';
650
+ braces = ['[', ']'];
651
+ } else {
652
+ type = 'Object';
653
+ braces = ['{', '}'];
654
+ }
655
+
656
+ // Make functions say that they are functions
657
+ if (typeof value === 'function') {
658
+ var n = value.name ? ': ' + value.name : '';
659
+ base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']';
660
+ } else {
661
+ base = '';
662
+ }
663
+
664
+ // Make dates with properties first say the date
665
+ if (isDate(value)) {
666
+ base = ' ' + value.toUTCString();
667
+ }
668
+
669
+ if ($keys.length === 0) {
670
+ return braces[0] + base + braces[1];
671
+ }
672
+
673
+ if (recurseTimes < 0) {
674
+ if (isRegExp(value)) {
675
+ return stylize('' + value, 'regexp');
676
+ } else {
677
+ return stylize('[Object]', 'special');
678
+ }
679
+ }
680
+
681
+ seen.push(value);
682
+
683
+ var output = map($keys, function (key) {
684
+ var name, str;
685
+ if (value.__lookupGetter__) {
686
+ if (value.__lookupGetter__(key)) {
687
+ if (value.__lookupSetter__(key)) {
688
+ str = stylize('[Getter/Setter]', 'special');
689
+ } else {
690
+ str = stylize('[Getter]', 'special');
691
+ }
692
+ } else {
693
+ if (value.__lookupSetter__(key)) {
694
+ str = stylize('[Setter]', 'special');
695
+ }
696
+ }
697
+ }
698
+ if (indexOf(visible_keys, key) < 0) {
699
+ name = '[' + key + ']';
700
+ }
701
+ if (!str) {
702
+ if (indexOf(seen, value[key]) < 0) {
703
+ if (recurseTimes === null) {
704
+ str = format(value[key]);
705
+ } else {
706
+ str = format(value[key], recurseTimes - 1);
707
+ }
708
+ if (str.indexOf('\n') > -1) {
709
+ if (isArray(value)) {
710
+ str = map(str.split('\n'), function (line) {
711
+ return ' ' + line;
712
+ }).join('\n').substr(2);
713
+ } else {
714
+ str = '\n' + map(str.split('\n'), function (line) {
715
+ return ' ' + line;
716
+ }).join('\n');
717
+ }
718
+ }
719
+ } else {
720
+ str = stylize('[Circular]', 'special');
721
+ }
722
+ }
723
+ if (typeof name === 'undefined') {
724
+ if (type === 'Array' && key.match(/^\d+$/)) {
725
+ return str;
726
+ }
727
+ name = json.stringify('' + key);
728
+ if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
729
+ name = name.substr(1, name.length - 2);
730
+ name = stylize(name, 'name');
731
+ } else {
732
+ name = name.replace(/'/g, "\\'")
733
+ .replace(/\\"/g, '"')
734
+ .replace(/(^"|"$)/g, "'");
735
+ name = stylize(name, 'string');
736
+ }
737
+ }
738
+
739
+ return name + ': ' + str;
740
+ });
741
+
742
+ seen.pop();
743
+
744
+ var numLinesEst = 0;
745
+ var length = reduce(output, function (prev, cur) {
746
+ numLinesEst++;
747
+ if (indexOf(cur, '\n') >= 0) numLinesEst++;
748
+ return prev + cur.length + 1;
749
+ }, 0);
750
+
751
+ if (length > 50) {
752
+ output = braces[0] +
753
+ (base === '' ? '' : base + '\n ') +
754
+ ' ' +
755
+ output.join(',\n ') +
756
+ ' ' +
757
+ braces[1];
758
+
759
+ } else {
760
+ output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
761
+ }
762
+
763
+ return output;
764
+ }
765
+ return format(obj, (typeof depth === 'undefined' ? 2 : depth));
766
+ };
767
+
768
+ expect.stringify = i;
769
+
770
+ function isArray (ar) {
771
+ return Object.prototype.toString.call(ar) == '[object Array]';
772
+ };
773
+
774
+ function isRegExp(re) {
775
+ var s;
776
+ try {
777
+ s = '' + re;
778
+ } catch (e) {
779
+ return false;
780
+ }
781
+
782
+ return re instanceof RegExp || // easy case
783
+ // duck-type for context-switching evalcx case
784
+ typeof(re) === 'function' &&
785
+ re.constructor.name === 'RegExp' &&
786
+ re.compile &&
787
+ re.test &&
788
+ re.exec &&
789
+ s.match(/^\/.*\/[gim]{0,3}$/);
790
+ };
791
+
792
+ function isDate(d) {
793
+ if (d instanceof Date) return true;
794
+ return false;
795
+ };
796
+
797
+ function keys (obj) {
798
+ if (Object.keys) {
799
+ return Object.keys(obj);
800
+ }
801
+
802
+ var keys = [];
803
+
804
+ for (var i in obj) {
805
+ if (Object.prototype.hasOwnProperty.call(obj, i)) {
806
+ keys.push(i);
807
+ }
808
+ }
809
+
810
+ return keys;
811
+ }
812
+
813
+ function map (arr, mapper, that) {
814
+ if (Array.prototype.map) {
815
+ return Array.prototype.map.call(arr, mapper, that);
816
+ }
817
+
818
+ var other= new Array(arr.length);
819
+
820
+ for (var i= 0, n = arr.length; i<n; i++)
821
+ if (i in arr)
822
+ other[i] = mapper.call(that, arr[i], i, arr);
823
+
824
+ return other;
825
+ };
826
+
827
+ function reduce (arr, fun) {
828
+ if (Array.prototype.reduce) {
829
+ return Array.prototype.reduce.apply(
830
+ arr
831
+ , Array.prototype.slice.call(arguments, 1)
832
+ );
833
+ }
834
+
835
+ var len = +this.length;
836
+
837
+ if (typeof fun !== "function")
838
+ throw new TypeError();
839
+
840
+ // no value to return if no initial value and an empty array
841
+ if (len === 0 && arguments.length === 1)
842
+ throw new TypeError();
843
+
844
+ var i = 0;
845
+ if (arguments.length >= 2) {
846
+ var rv = arguments[1];
847
+ } else {
848
+ do {
849
+ if (i in this) {
850
+ rv = this[i++];
851
+ break;
852
+ }
853
+
854
+ // if array contains no values, no initial value to return
855
+ if (++i >= len)
856
+ throw new TypeError();
857
+ } while (true);
858
+ }
859
+
860
+ for (; i < len; i++) {
861
+ if (i in this)
862
+ rv = fun.call(null, rv, this[i], i, this);
863
+ }
864
+
865
+ return rv;
866
+ };
867
+
868
+ /**
869
+ * Asserts deep equality
870
+ *
871
+ * @see taken from node.js `assert` module (copyright Joyent, MIT license)
872
+ * @api private
873
+ */
874
+
875
+ expect.eql = function eql (actual, expected) {
876
+ // 7.1. All identical values are equivalent, as determined by ===.
877
+ if (actual === expected) {
878
+ return true;
879
+ } else if ('undefined' != typeof Buffer
880
+ && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
881
+ if (actual.length != expected.length) return false;
882
+
883
+ for (var i = 0; i < actual.length; i++) {
884
+ if (actual[i] !== expected[i]) return false;
885
+ }
886
+
887
+ return true;
888
+
889
+ // 7.2. If the expected value is a Date object, the actual value is
890
+ // equivalent if it is also a Date object that refers to the same time.
891
+ } else if (actual instanceof Date && expected instanceof Date) {
892
+ return actual.getTime() === expected.getTime();
893
+
894
+ // 7.3. Other pairs that do not both pass typeof value == "object",
895
+ // equivalence is determined by ==.
896
+ } else if (typeof actual != 'object' && typeof expected != 'object') {
897
+ return actual == expected;
898
+
899
+ // If both are regular expression use the special `regExpEquiv` method
900
+ // to determine equivalence.
901
+ } else if (isRegExp(actual) && isRegExp(expected)) {
902
+ return regExpEquiv(actual, expected);
903
+ // 7.4. For all other Object pairs, including Array objects, equivalence is
904
+ // determined by having the same number of owned properties (as verified
905
+ // with Object.prototype.hasOwnProperty.call), the same set of keys
906
+ // (although not necessarily the same order), equivalent values for every
907
+ // corresponding key, and an identical "prototype" property. Note: this
908
+ // accounts for both named and indexed properties on Arrays.
909
+ } else {
910
+ return objEquiv(actual, expected);
911
+ }
912
+ }
913
+
914
+ function isUndefinedOrNull (value) {
915
+ return value === null || value === undefined;
916
+ }
917
+
918
+ function isArguments (object) {
919
+ return Object.prototype.toString.call(object) == '[object Arguments]';
920
+ }
921
+
922
+ function regExpEquiv (a, b) {
923
+ return a.source === b.source && a.global === b.global &&
924
+ a.ignoreCase === b.ignoreCase && a.multiline === b.multiline;
925
+ }
926
+
927
+ function objEquiv (a, b) {
928
+ if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
929
+ return false;
930
+ // an identical "prototype" property.
931
+ if (a.prototype !== b.prototype) return false;
932
+ //~~~I've managed to break Object.keys through screwy arguments passing.
933
+ // Converting to array solves the problem.
934
+ if (isArguments(a)) {
935
+ if (!isArguments(b)) {
936
+ return false;
937
+ }
938
+ a = pSlice.call(a);
939
+ b = pSlice.call(b);
940
+ return expect.eql(a, b);
941
+ }
942
+ try{
943
+ var ka = keys(a),
944
+ kb = keys(b),
945
+ key, i;
946
+ } catch (e) {//happens when one is a string literal and the other isn't
947
+ return false;
948
+ }
949
+ // having the same number of owned properties (keys incorporates hasOwnProperty)
950
+ if (ka.length != kb.length)
951
+ return false;
952
+ //the same set of keys (although not necessarily the same order),
953
+ ka.sort();
954
+ kb.sort();
955
+ //~~~cheap key test
956
+ for (i = ka.length - 1; i >= 0; i--) {
957
+ if (ka[i] != kb[i])
958
+ return false;
959
+ }
960
+ //equivalent values for every corresponding key, and
961
+ //~~~possibly expensive deep test
962
+ for (i = ka.length - 1; i >= 0; i--) {
963
+ key = ka[i];
964
+ if (!expect.eql(a[key], b[key]))
965
+ return false;
966
+ }
967
+ return true;
968
+ }
969
+
970
+ var json = (function () {
971
+ "use strict";
972
+
973
+ if ('object' == typeof JSON && JSON.parse && JSON.stringify) {
974
+ return {
975
+ parse: nativeJSON.parse
976
+ , stringify: nativeJSON.stringify
977
+ }
978
+ }
979
+
980
+ var JSON = {};
981
+
982
+ function f(n) {
983
+ // Format integers to have at least two digits.
984
+ return n < 10 ? '0' + n : n;
985
+ }
986
+
987
+ function date(d, key) {
988
+ return isFinite(d.valueOf()) ?
989
+ d.getUTCFullYear() + '-' +
990
+ f(d.getUTCMonth() + 1) + '-' +
991
+ f(d.getUTCDate()) + 'T' +
992
+ f(d.getUTCHours()) + ':' +
993
+ f(d.getUTCMinutes()) + ':' +
994
+ f(d.getUTCSeconds()) + 'Z' : null;
995
+ };
996
+
997
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
998
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
999
+ gap,
1000
+ indent,
1001
+ meta = { // table of character substitutions
1002
+ '\b': '\\b',
1003
+ '\t': '\\t',
1004
+ '\n': '\\n',
1005
+ '\f': '\\f',
1006
+ '\r': '\\r',
1007
+ '"' : '\\"',
1008
+ '\\': '\\\\'
1009
+ },
1010
+ rep;
1011
+
1012
+
1013
+ function quote(string) {
1014
+
1015
+ // If the string contains no control characters, no quote characters, and no
1016
+ // backslash characters, then we can safely slap some quotes around it.
1017
+ // Otherwise we must also replace the offending characters with safe escape
1018
+ // sequences.
1019
+
1020
+ escapable.lastIndex = 0;
1021
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
1022
+ var c = meta[a];
1023
+ return typeof c === 'string' ? c :
1024
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
1025
+ }) + '"' : '"' + string + '"';
1026
+ }
1027
+
1028
+
1029
+ function str(key, holder) {
1030
+
1031
+ // Produce a string from holder[key].
1032
+
1033
+ var i, // The loop counter.
1034
+ k, // The member key.
1035
+ v, // The member value.
1036
+ length,
1037
+ mind = gap,
1038
+ partial,
1039
+ value = holder[key];
1040
+
1041
+ // If the value has a toJSON method, call it to obtain a replacement value.
1042
+
1043
+ if (value instanceof Date) {
1044
+ value = date(key);
1045
+ }
1046
+
1047
+ // If we were called with a replacer function, then call the replacer to
1048
+ // obtain a replacement value.
1049
+
1050
+ if (typeof rep === 'function') {
1051
+ value = rep.call(holder, key, value);
1052
+ }
1053
+
1054
+ // What happens next depends on the value's type.
1055
+
1056
+ switch (typeof value) {
1057
+ case 'string':
1058
+ return quote(value);
1059
+
1060
+ case 'number':
1061
+
1062
+ // JSON numbers must be finite. Encode non-finite numbers as null.
1063
+
1064
+ return isFinite(value) ? String(value) : 'null';
1065
+
1066
+ case 'boolean':
1067
+ case 'null':
1068
+
1069
+ // If the value is a boolean or null, convert it to a string. Note:
1070
+ // typeof null does not produce 'null'. The case is included here in
1071
+ // the remote chance that this gets fixed someday.
1072
+
1073
+ return String(value);
1074
+
1075
+ // If the type is 'object', we might be dealing with an object or an array or
1076
+ // null.
1077
+
1078
+ case 'object':
1079
+
1080
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
1081
+ // so watch out for that case.
1082
+
1083
+ if (!value) {
1084
+ return 'null';
1085
+ }
1086
+
1087
+ // Make an array to hold the partial results of stringifying this object value.
1088
+
1089
+ gap += indent;
1090
+ partial = [];
1091
+
1092
+ // Is the value an array?
1093
+
1094
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
1095
+
1096
+ // The value is an array. Stringify every element. Use null as a placeholder
1097
+ // for non-JSON values.
1098
+
1099
+ length = value.length;
1100
+ for (i = 0; i < length; i += 1) {
1101
+ partial[i] = str(i, value) || 'null';
1102
+ }
1103
+
1104
+ // Join all of the elements together, separated with commas, and wrap them in
1105
+ // brackets.
1106
+
1107
+ v = partial.length === 0 ? '[]' : gap ?
1108
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
1109
+ '[' + partial.join(',') + ']';
1110
+ gap = mind;
1111
+ return v;
1112
+ }
1113
+
1114
+ // If the replacer is an array, use it to select the members to be stringified.
1115
+
1116
+ if (rep && typeof rep === 'object') {
1117
+ length = rep.length;
1118
+ for (i = 0; i < length; i += 1) {
1119
+ if (typeof rep[i] === 'string') {
1120
+ k = rep[i];
1121
+ v = str(k, value);
1122
+ if (v) {
1123
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
1124
+ }
1125
+ }
1126
+ }
1127
+ } else {
1128
+
1129
+ // Otherwise, iterate through all of the keys in the object.
1130
+
1131
+ for (k in value) {
1132
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
1133
+ v = str(k, value);
1134
+ if (v) {
1135
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+
1141
+ // Join all of the member texts together, separated with commas,
1142
+ // and wrap them in braces.
1143
+
1144
+ v = partial.length === 0 ? '{}' : gap ?
1145
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
1146
+ '{' + partial.join(',') + '}';
1147
+ gap = mind;
1148
+ return v;
1149
+ }
1150
+ }
1151
+
1152
+ // If the JSON object does not yet have a stringify method, give it one.
1153
+
1154
+ JSON.stringify = function (value, replacer, space) {
1155
+
1156
+ // The stringify method takes a value and an optional replacer, and an optional
1157
+ // space parameter, and returns a JSON text. The replacer can be a function
1158
+ // that can replace values, or an array of strings that will select the keys.
1159
+ // A default replacer method can be provided. Use of the space parameter can
1160
+ // produce text that is more easily readable.
1161
+
1162
+ var i;
1163
+ gap = '';
1164
+ indent = '';
1165
+
1166
+ // If the space parameter is a number, make an indent string containing that
1167
+ // many spaces.
1168
+
1169
+ if (typeof space === 'number') {
1170
+ for (i = 0; i < space; i += 1) {
1171
+ indent += ' ';
1172
+ }
1173
+
1174
+ // If the space parameter is a string, it will be used as the indent string.
1175
+
1176
+ } else if (typeof space === 'string') {
1177
+ indent = space;
1178
+ }
1179
+
1180
+ // If there is a replacer, it must be a function or an array.
1181
+ // Otherwise, throw an error.
1182
+
1183
+ rep = replacer;
1184
+ if (replacer && typeof replacer !== 'function' &&
1185
+ (typeof replacer !== 'object' ||
1186
+ typeof replacer.length !== 'number')) {
1187
+ throw new Error('JSON.stringify');
1188
+ }
1189
+
1190
+ // Make a fake root object containing our value under the key of ''.
1191
+ // Return the result of stringifying the value.
1192
+
1193
+ return str('', {'': value});
1194
+ };
1195
+
1196
+ // If the JSON object does not yet have a parse method, give it one.
1197
+
1198
+ JSON.parse = function (text, reviver) {
1199
+ // The parse method takes a text and an optional reviver function, and returns
1200
+ // a JavaScript value if the text is a valid JSON text.
1201
+
1202
+ var j;
1203
+
1204
+ function walk(holder, key) {
1205
+
1206
+ // The walk method is used to recursively walk the resulting structure so
1207
+ // that modifications can be made.
1208
+
1209
+ var k, v, value = holder[key];
1210
+ if (value && typeof value === 'object') {
1211
+ for (k in value) {
1212
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
1213
+ v = walk(value, k);
1214
+ if (v !== undefined) {
1215
+ value[k] = v;
1216
+ } else {
1217
+ delete value[k];
1218
+ }
1219
+ }
1220
+ }
1221
+ }
1222
+ return reviver.call(holder, key, value);
1223
+ }
1224
+
1225
+
1226
+ // Parsing happens in four stages. In the first stage, we replace certain
1227
+ // Unicode characters with escape sequences. JavaScript handles many characters
1228
+ // incorrectly, either silently deleting them, or treating them as line endings.
1229
+
1230
+ text = String(text);
1231
+ cx.lastIndex = 0;
1232
+ if (cx.test(text)) {
1233
+ text = text.replace(cx, function (a) {
1234
+ return '\\u' +
1235
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
1236
+ });
1237
+ }
1238
+
1239
+ // In the second stage, we run the text against regular expressions that look
1240
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
1241
+ // because they can cause invocation, and '=' because it can cause mutation.
1242
+ // But just to be safe, we want to reject all unexpected forms.
1243
+
1244
+ // We split the second stage into 4 regexp operations in order to work around
1245
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
1246
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
1247
+ // replace all simple value tokens with ']' characters. Third, we delete all
1248
+ // open brackets that follow a colon or comma or that begin the text. Finally,
1249
+ // we look to see that the remaining characters are only whitespace or ']' or
1250
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
1251
+
1252
+ if (/^[\],:{}\s]*$/
1253
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
1254
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
1255
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
1256
+
1257
+ // In the third stage we use the eval function to compile the text into a
1258
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
1259
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
1260
+ // in parens to eliminate the ambiguity.
1261
+
1262
+ j = eval('(' + text + ')');
1263
+
1264
+ // In the optional fourth stage, we recursively walk the new structure, passing
1265
+ // each name/value pair to a reviver function for possible transformation.
1266
+
1267
+ return typeof reviver === 'function' ?
1268
+ walk({'': j}, '') : j;
1269
+ }
1270
+
1271
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
1272
+
1273
+ throw new SyntaxError('JSON.parse');
1274
+ };
1275
+
1276
+ return JSON;
1277
+ })();
1278
+
1279
+ if ('undefined' != typeof window) {
1280
+ window.expect = module.exports;
1281
+ }
1282
+
1283
+ })(
1284
+ this
1285
+ , 'undefined' != typeof module ? module : {exports: {}}
1286
+ );