netzke-core 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (216) hide show
  1. data/CHANGELOG.md +16 -1
  2. data/Gemfile +12 -0
  3. data/README.md +22 -15
  4. data/Rakefile +19 -38
  5. data/app/controllers/netzke_controller.rb +1 -79
  6. data/javascripts/ext.js +17 -13
  7. data/lib/netzke/base.rb +6 -2
  8. data/lib/netzke/core/action_config.rb +7 -0
  9. data/lib/netzke/core/actions.rb +6 -32
  10. data/lib/netzke/core/client_class.rb +1 -1
  11. data/lib/netzke/core/component_config.rb +1 -1
  12. data/lib/netzke/core/composition.rb +17 -111
  13. data/lib/netzke/core/config_to_dsl_delegator.rb +0 -7
  14. data/lib/netzke/core/configuration.rb +48 -0
  15. data/lib/netzke/core/dsl_support.rb +71 -0
  16. data/lib/netzke/core/dynamic_assets.rb +10 -1
  17. data/lib/netzke/core/embedding.rb +2 -2
  18. data/lib/netzke/core/html.rb +29 -0
  19. data/lib/netzke/core/javascript.rb +32 -26
  20. data/lib/netzke/core/railz/action_view_ext/ext.rb +2 -2
  21. data/lib/netzke/core/railz/controller_extensions.rb +96 -3
  22. data/lib/netzke/core/railz/routes.rb +14 -3
  23. data/lib/netzke/core/ruby_ext/array.rb +4 -18
  24. data/lib/netzke/core/ruby_ext/hash.rb +19 -25
  25. data/lib/netzke/core/ruby_ext/time_with_zone.rb +1 -1
  26. data/lib/netzke/core/ruby_ext.rb +0 -2
  27. data/lib/netzke/core/session.rb +8 -1
  28. data/lib/netzke/core/state.rb +3 -3
  29. data/lib/netzke/core/version.rb +1 -1
  30. data/lib/netzke/core.rb +15 -7
  31. data/lib/netzke-core.rb +2 -9
  32. data/tasks/app_test_tasks.rake +94 -0
  33. data/tasks/rake_helper.rb +51 -0
  34. data/test/core_test_app/README +2 -2
  35. data/test/core_test_app/app/assets/javascripts/expect/expect.js +1253 -0
  36. data/test/core_test_app/app/assets/javascripts/mocha/mocha.js +5340 -0
  37. data/test/core_test_app/app/assets/stylesheets/mocha/mocha.css +231 -0
  38. data/test/core_test_app/app/components/{component_with_actions.rb → actions.rb} +7 -9
  39. data/test/core_test_app/app/components/{some_composite.rb → composition.rb} +16 -5
  40. data/test/core_test_app/app/components/{component_with_custom_css → css_inclusion}/stylesheets/custom.css +2 -2
  41. data/test/core_test_app/app/components/css_inclusion.rb +12 -0
  42. data/test/core_test_app/app/components/{component_loader/javascripts/component_loader.js → dynamic_loading/javascripts/dynamic_loading.js} +9 -1
  43. data/test/core_test_app/app/components/{component_loader.rb → dynamic_loading.rb} +12 -5
  44. data/test/core_test_app/app/components/{server_caller/javascripts/server_caller.js → endpoints/javascripts/endpoints.js} +10 -6
  45. data/test/core_test_app/app/components/{server_caller.rb → endpoints.rb} +12 -8
  46. data/test/core_test_app/app/components/{extended_server_caller.rb → endpoints_extended.rb} +6 -4
  47. data/test/core_test_app/app/components/ext_direct/composite.rb +5 -1
  48. data/test/core_test_app/app/components/feedback.rb +26 -0
  49. data/test/core_test_app/app/components/haml_panel/html/body.html.haml +10 -0
  50. data/test/core_test_app/app/components/haml_panel/html/server_response.html.haml +3 -0
  51. data/test/core_test_app/app/components/haml_panel.rb +32 -0
  52. data/test/core_test_app/app/components/hello_world.rb +1 -0
  53. data/test/core_test_app/app/components/js_inclusion/javascripts/extra_one.js +2 -0
  54. data/test/core_test_app/app/components/js_inclusion/javascripts/extra_two.js +2 -0
  55. data/test/core_test_app/app/components/js_inclusion/javascripts/js_inclusion.js +5 -0
  56. data/test/core_test_app/app/components/{component_with_js_mixin → js_inclusion}/javascripts/method_set_one.js +0 -0
  57. data/test/core_test_app/app/components/{component_with_js_mixin → js_inclusion}/javascripts/method_set_two.js +0 -0
  58. data/test/core_test_app/app/components/{component_with_js_mixin.rb → js_inclusion.rb} +3 -3
  59. data/test/core_test_app/app/components/js_inclusion_extended/javascripts/some_method_set.js +5 -0
  60. data/test/core_test_app/app/components/js_inclusion_extended.rb +6 -0
  61. data/test/core_test_app/app/components/loaded_css_inclusion.rb +22 -0
  62. data/test/core_test_app/app/components/{localized_panel.rb → localization.rb} +5 -5
  63. data/test/core_test_app/app/components/localization_extended.rb +2 -0
  64. data/test/core_test_app/app/components/persistence.rb +60 -0
  65. data/test/core_test_app/app/components/persistence_with_shared_state.rb +7 -0
  66. data/test/core_test_app/app/components/plugin_with_actions.rb +24 -0
  67. data/test/core_test_app/app/components/plugin_with_components.rb +3 -3
  68. data/test/core_test_app/app/components/plugin_with_endpoints.rb +29 -0
  69. data/test/core_test_app/app/components/plugins.rb +9 -0
  70. data/test/core_test_app/app/components/{kinda_complex_component → ruby_modules}/basic_stuff.rb +1 -1
  71. data/test/core_test_app/app/components/{kinda_complex_component → ruby_modules}/extra_stuff.rb +3 -3
  72. data/test/core_test_app/app/components/{kinda_complex_component.rb → ruby_modules.rb} +1 -1
  73. data/test/core_test_app/app/components/scoped/deeply_scoped/scoping.rb +10 -0
  74. data/test/core_test_app/app/components/scoped/scoping.rb +8 -0
  75. data/test/core_test_app/app/components/scoped/scoping_extended.rb +8 -0
  76. data/test/core_test_app/app/components/self_reloading.rb +25 -0
  77. data/test/core_test_app/app/components/server_counter/javascripts/server_counter.js +4 -4
  78. data/test/core_test_app/app/components/server_counter.rb +10 -10
  79. data/test/core_test_app/app/components/simple_authentication_component.rb +1 -0
  80. data/test/core_test_app/app/components/simple_component.rb +0 -4
  81. data/test/core_test_app/app/components/simple_composite.rb +1 -1
  82. data/test/core_test_app/app/components/simple_form_with_file_upload.rb +1 -2
  83. data/test/core_test_app/app/components/simple_tab_panel.rb +5 -3
  84. data/test/core_test_app/app/components/{panel_with_tools.rb → tools.rb} +3 -3
  85. data/test/core_test_app/app/components/window_with_simple_component.rb +1 -0
  86. data/test/core_test_app/app/controllers/alternative_controller.rb +10 -0
  87. data/test/core_test_app/app/controllers/specs_controller.rb +11 -0
  88. data/test/core_test_app/app/views/layouts/application.html.erb +19 -1
  89. data/test/core_test_app/app/views/simple_rails/multiple_nested.html.erb +2 -2
  90. data/test/core_test_app/config/boot.rb +3 -10
  91. data/test/core_test_app/config/environments/development.rb +1 -0
  92. data/test/core_test_app/config/initializers/netzke.rb +4 -1
  93. data/test/core_test_app/config/locales/en.yml +11 -4
  94. data/test/core_test_app/config/locales/es.yml +1 -4
  95. data/test/core_test_app/config/routes.rb +10 -1
  96. data/test/core_test_app/core_test_app.tmproj +254 -0
  97. data/test/core_test_app/db/development.sqlite3 +0 -0
  98. data/test/core_test_app/{lib/tasks/.gitkeep → db/test.sqlite3} +0 -0
  99. data/test/core_test_app/log/development.log +78046 -0
  100. data/test/core_test_app/log/production.log +18 -0
  101. data/test/core_test_app/log/test.log +120048 -0
  102. data/test/core_test_app/rails_app.tmproj +324 -0
  103. data/test/core_test_app/tmp/cache/assets/C8B/BF0/sprockets%2F54de2792b036d1dab855f88599503551 +0 -0
  104. data/test/core_test_app/tmp/cache/assets/C92/5A0/sprockets%2F39e75754782ee12179bf35c9a0971d80 +0 -0
  105. data/test/core_test_app/tmp/cache/assets/C99/720/sprockets%2F981d5a1b957a012e380b22011a6d176d +0 -0
  106. data/test/core_test_app/tmp/cache/assets/C9F/750/sprockets%2F20ce3d64040a5d3a0a8883bd60754356 +0 -0
  107. data/test/core_test_app/tmp/cache/assets/CC4/C00/sprockets%2Fc615df52887d8c2e67e8413576a419c5 +0 -0
  108. data/test/core_test_app/tmp/cache/assets/CF1/3F0/sprockets%2Fc69ee42924fab565a3533d56473ce878 +0 -0
  109. data/test/core_test_app/tmp/cache/assets/D0E/870/sprockets%2Fa593bf4fac106add88c9434141a49663 +0 -0
  110. data/test/core_test_app/tmp/cache/assets/D14/8E0/sprockets%2F20748e8d1d7d090d122904a9fe6f18fc +0 -0
  111. data/test/core_test_app/tmp/cache/assets/D3E/DA0/sprockets%2Fa175f1ac5996544b908ba3ba3f64c4f3 +0 -0
  112. data/test/core_test_app/tmp/cache/assets/D43/C00/sprockets%2F7bc60c758776356d615ab5edff201ee2 +0 -0
  113. data/test/core_test_app/tmp/cache/assets/D4B/C50/sprockets%2F8483b7e322da338e8f9eb3b30a957e9a +0 -0
  114. data/test/core_test_app/tmp/cache/assets/D64/090/sprockets%2F5a01ff309c3f2503eb5e4f5667cca4b3 +0 -0
  115. data/test/core_test_app/tmp/cache/assets/D8B/FB0/sprockets%2F7a86225caaa389f1be600b4f3a2d1ef0 +0 -0
  116. data/test/core_test_app/tmp/cache/assets/D98/9C0/sprockets%2F18b80e8fe200aebc522e561a867ea6fb +0 -0
  117. data/test/core_test_app/tmp/cache/assets/DB0/6E0/sprockets%2F03e33f5a4779eeb48bcfc86ee717fb55 +0 -0
  118. data/test/core_test_app/tmp/cache/assets/DED/7E0/sprockets%2Feaedd52ba538f19e4ab543a3e20ce2c4 +0 -0
  119. data/test/core_test_app/tmp/cache/assets/E07/FF0/sprockets%2Fb3c071e0a6de36f041adbbdaa8ab060b +0 -0
  120. data/test/core_test_app/tmp/capybara/capybara-20101214105940.html +40 -0
  121. data/test/core_test_app/tmp/capybara/capybara-20101214110642.html +40 -0
  122. data/test/core_test_app/tmp/capybara/capybara-20101214110749.html +40 -0
  123. data/test/core_test_app/tmp/capybara/capybara-20101214110750.html +40 -0
  124. data/test/core_test_app/tmp/capybara/capybara-20101214111025.html +40 -0
  125. data/test/core_test_app/tmp/capybara/capybara-20101214111027.html +40 -0
  126. data/test/core_test_app/tmp/capybara/capybara-20101214111213.html +40 -0
  127. data/test/core_test_app/tmp/capybara/capybara-20101214111214.html +40 -0
  128. data/test/core_test_app/tmp/capybara/capybara-20101214111332.html +40 -0
  129. data/test/core_test_app/tmp/capybara/capybara-20101214111333.html +40 -0
  130. data/test/core_test_app/{spec/component → tmp/pids/passenger.3000.pid.lock} +0 -0
  131. metadata +370 -133
  132. data/.autotest +0 -1
  133. data/.travis.yml +0 -18
  134. data/Manifest +0 -50
  135. data/config/ci/before-travis.sh +0 -10
  136. data/install.rb +0 -1
  137. data/lib/netzke/core/options_hash.rb +0 -27
  138. data/lib/netzke/core/ruby_ext/string.rb +0 -26
  139. data/lib/netzke/core/ruby_ext/symbol.rb +0 -13
  140. data/netzke-core.gemspec +0 -253
  141. data/test/core_test_app/.gitignore +0 -4
  142. data/test/core_test_app/.powrc +0 -4
  143. data/test/core_test_app/.rvmrc +0 -1
  144. data/test/core_test_app/Gemfile +0 -19
  145. data/test/core_test_app/Gemfile.lock +0 -147
  146. data/test/core_test_app/app/components/border_layout_panel.rb +0 -4
  147. data/test/core_test_app/app/components/card_component_loader.rb +0 -25
  148. data/test/core_test_app/app/components/component_with_custom_css.rb +0 -11
  149. data/test/core_test_app/app/components/component_with_js_mixin/javascripts/component_with_js_mixin.js +0 -5
  150. data/test/core_test_app/app/components/component_with_js_mixin/javascripts/extra_one.js +0 -2
  151. data/test/core_test_app/app/components/component_with_js_mixin/javascripts/extra_two.js +0 -2
  152. data/test/core_test_app/app/components/component_with_nested_through.rb +0 -28
  153. data/test/core_test_app/app/components/component_with_required_js.rb +0 -24
  154. data/test/core_test_app/app/components/dsl_delegated_properties.rb +0 -4
  155. data/test/core_test_app/app/components/dsl_delegated_properties_base.rb +0 -5
  156. data/test/core_test_app/app/components/extended_component_with_actions.rb +0 -9
  157. data/test/core_test_app/app/components/extended_component_with_js_mixin/javascripts/some_method_set.js +0 -5
  158. data/test/core_test_app/app/components/extended_component_with_js_mixin.rb +0 -10
  159. data/test/core_test_app/app/components/extended_localized_panel.rb +0 -2
  160. data/test/core_test_app/app/components/included.js +0 -5
  161. data/test/core_test_app/app/components/inline_composite.rb +0 -13
  162. data/test/core_test_app/app/components/loader_of_component_with_custom_css.rb +0 -23
  163. data/test/core_test_app/app/components/multipane_component_loader.rb +0 -41
  164. data/test/core_test_app/app/components/nested_component.rb +0 -17
  165. data/test/core_test_app/app/components/panel_with_plugin.rb +0 -9
  166. data/test/core_test_app/app/components/scoped_components/deep_scoped_components/some_deep_scoped_component.rb +0 -10
  167. data/test/core_test_app/app/components/scoped_components/extended_scoped_component.rb +0 -8
  168. data/test/core_test_app/app/components/scoped_components/some_scoped_component.rb +0 -8
  169. data/test/core_test_app/app/components/some_ext_component.rb +0 -8
  170. data/test/core_test_app/app/components/some_plugin.rb +0 -40
  171. data/test/core_test_app/app/components/stateful_component.rb +0 -46
  172. data/test/core_test_app/app/components/stateful_component_with_shared_state.rb +0 -11
  173. data/test/core_test_app/config/database.yml.travis +0 -11
  174. data/test/core_test_app/db/schema.rb +0 -29
  175. data/test/core_test_app/features/actions_and_tools.feature +0 -22
  176. data/test/core_test_app/features/basic.feature +0 -7
  177. data/test/core_test_app/features/client-server.feature +0 -19
  178. data/test/core_test_app/features/complex_component.feature +0 -18
  179. data/test/core_test_app/features/component.feature +0 -10
  180. data/test/core_test_app/features/component_loader.feature +0 -52
  181. data/test/core_test_app/features/composition.feature +0 -45
  182. data/test/core_test_app/features/config_to_dsl_delegation.feature +0 -10
  183. data/test/core_test_app/features/custom_css.feature +0 -17
  184. data/test/core_test_app/features/ext.direct.feature +0 -32
  185. data/test/core_test_app/features/file_inclusion.feature +0 -10
  186. data/test/core_test_app/features/i18n.feature +0 -35
  187. data/test/core_test_app/features/inheritance.feature +0 -18
  188. data/test/core_test_app/features/js_include.feature +0 -20
  189. data/test/core_test_app/features/nested_views.feature +0 -10
  190. data/test/core_test_app/features/persistence.feature +0 -34
  191. data/test/core_test_app/features/plugin.feature +0 -16
  192. data/test/core_test_app/features/rails_views.feature +0 -16
  193. data/test/core_test_app/features/scopes.feature +0 -14
  194. data/test/core_test_app/features/step_definitions/custom_css_steps.rb +0 -7
  195. data/test/core_test_app/features/step_definitions/generic_steps.rb +0 -64
  196. data/test/core_test_app/features/step_definitions/web_steps.rb +0 -219
  197. data/test/core_test_app/features/support/env.rb +0 -58
  198. data/test/core_test_app/features/support/paths.rb +0 -42
  199. data/test/core_test_app/spec/action_config_spec.rb +0 -15
  200. data/test/core_test_app/spec/actions_spec.rb +0 -96
  201. data/test/core_test_app/spec/base_spec.rb +0 -35
  202. data/test/core_test_app/spec/client_class_spec.rb +0 -17
  203. data/test/core_test_app/spec/composition_spec.rb +0 -118
  204. data/test/core_test_app/spec/core_ext_spec.rb +0 -27
  205. data/test/core_test_app/spec/endpoint_response_spec.rb +0 -17
  206. data/test/core_test_app/spec/javascript_spec.rb +0 -33
  207. data/test/core_test_app/spec/js_class_config_scope.rb +0 -37
  208. data/test/core_test_app/spec/panel_spec.rb +0 -11
  209. data/test/core_test_app/spec/services_spec.rb +0 -16
  210. data/test/core_test_app/spec/spec.opt +0 -2
  211. data/test/core_test_app/spec/spec_helper.rb +0 -27
  212. data/test/core_test_app/spec/state_spec.rb +0 -20
  213. data/test/core_test_app/vendor/plugins/.gitkeep +0 -0
  214. data/test/unit/core_ext_test.rb +0 -8
  215. data/test/unit/netzke_core_test.rb +0 -178
  216. data/uninstall.rb +0 -1
@@ -0,0 +1,1253 @@
1
+
2
+ (function (global, module) {
3
+
4
+ if ('undefined' == typeof module) {
5
+ var module = { exports: {} }
6
+ , exports = module.exports
7
+ }
8
+
9
+ /**
10
+ * Exports.
11
+ */
12
+
13
+ module.exports = expect;
14
+ expect.Assertion = Assertion;
15
+
16
+ /**
17
+ * Exports version.
18
+ */
19
+
20
+ expect.version = '0.1.2';
21
+
22
+ /**
23
+ * Possible assertion flags.
24
+ */
25
+
26
+ var flags = {
27
+ not: ['to', 'be', 'have', 'include', 'only']
28
+ , to: ['be', 'have', 'include', 'only', 'not']
29
+ , only: ['have']
30
+ , have: ['own']
31
+ , be: ['an']
32
+ };
33
+
34
+ function expect (obj) {
35
+ return new Assertion(obj);
36
+ }
37
+
38
+ /**
39
+ * Constructor
40
+ *
41
+ * @api private
42
+ */
43
+
44
+ function Assertion (obj, flag, parent) {
45
+ this.obj = obj;
46
+ this.flags = {};
47
+
48
+ if (undefined != parent) {
49
+ this.flags[flag] = true;
50
+
51
+ for (var i in parent.flags) {
52
+ if (parent.flags.hasOwnProperty(i)) {
53
+ this.flags[i] = true;
54
+ }
55
+ }
56
+ }
57
+
58
+ var $flags = flag ? flags[flag] : keys(flags)
59
+ , self = this
60
+
61
+ if ($flags) {
62
+ for (var i = 0, l = $flags.length; i < l; i++) {
63
+ // avoid recursion
64
+ if (this.flags[$flags[i]]) continue;
65
+
66
+ var name = $flags[i]
67
+ , assertion = new Assertion(this.obj, name, this)
68
+
69
+ if ('function' == typeof Assertion.prototype[name]) {
70
+ // clone the function, make sure we dont touch the prot reference
71
+ var old = this[name];
72
+ this[name] = function () {
73
+ return old.apply(self, arguments);
74
+ }
75
+
76
+ for (var fn in Assertion.prototype) {
77
+ if (Assertion.prototype.hasOwnProperty(fn) && fn != name) {
78
+ this[name][fn] = bind(assertion[fn], assertion);
79
+ }
80
+ }
81
+ } else {
82
+ this[name] = assertion;
83
+ }
84
+ }
85
+ }
86
+ };
87
+
88
+ /**
89
+ * Performs an assertion
90
+ *
91
+ * @api private
92
+ */
93
+
94
+ Assertion.prototype.assert = function (truth, msg, error) {
95
+ var msg = this.flags.not ? error : msg
96
+ , ok = this.flags.not ? !truth : truth;
97
+
98
+ if (!ok) {
99
+ throw new Error(msg.call(this));
100
+ }
101
+
102
+ this.and = new Assertion(this.obj);
103
+ };
104
+
105
+ /**
106
+ * Check if the value is truthy
107
+ *
108
+ * @api public
109
+ */
110
+
111
+ Assertion.prototype.ok = function () {
112
+ this.assert(
113
+ !!this.obj
114
+ , function(){ return 'expected ' + i(this.obj) + ' to be truthy' }
115
+ , function(){ return 'expected ' + i(this.obj) + ' to be falsy' });
116
+ };
117
+
118
+ /**
119
+ * Assert that the function throws.
120
+ *
121
+ * @param {Function|RegExp} callback, or regexp to match error string against
122
+ * @api public
123
+ */
124
+
125
+ Assertion.prototype.throwError =
126
+ Assertion.prototype.throwException = function (fn) {
127
+ expect(this.obj).to.be.a('function');
128
+
129
+ var thrown = false
130
+ , not = this.flags.not
131
+
132
+ try {
133
+ this.obj();
134
+ } catch (e) {
135
+ if ('function' == typeof fn) {
136
+ fn(e);
137
+ } else if ('object' == typeof fn) {
138
+ var subject = 'string' == typeof e ? e : e.message;
139
+ if (not) {
140
+ expect(subject).to.not.match(fn);
141
+ } else {
142
+ expect(subject).to.match(fn);
143
+ }
144
+ }
145
+ thrown = true;
146
+ }
147
+
148
+ if ('object' == typeof fn && not) {
149
+ // in the presence of a matcher, ensure the `not` only applies to
150
+ // the matching.
151
+ this.flags.not = false;
152
+ }
153
+
154
+ var name = this.obj.name || 'fn';
155
+ this.assert(
156
+ thrown
157
+ , function(){ return 'expected ' + name + ' to throw an exception' }
158
+ , function(){ return 'expected ' + name + ' not to throw an exception' });
159
+ };
160
+
161
+ /**
162
+ * Checks if the array is empty.
163
+ *
164
+ * @api public
165
+ */
166
+
167
+ Assertion.prototype.empty = function () {
168
+ var expectation;
169
+
170
+ if ('object' == typeof this.obj && null !== this.obj && !isArray(this.obj)) {
171
+ if ('number' == typeof this.obj.length) {
172
+ expectation = !this.obj.length;
173
+ } else {
174
+ expectation = !keys(this.obj).length;
175
+ }
176
+ } else {
177
+ if ('string' != typeof this.obj) {
178
+ expect(this.obj).to.be.an('object');
179
+ }
180
+
181
+ expect(this.obj).to.have.property('length');
182
+ expectation = !this.obj.length;
183
+ }
184
+
185
+ this.assert(
186
+ expectation
187
+ , function(){ return 'expected ' + i(this.obj) + ' to be empty' }
188
+ , function(){ return 'expected ' + i(this.obj) + ' to not be empty' });
189
+ return this;
190
+ };
191
+
192
+ /**
193
+ * Checks if the obj exactly equals another.
194
+ *
195
+ * @api public
196
+ */
197
+
198
+ Assertion.prototype.be =
199
+ Assertion.prototype.equal = function (obj) {
200
+ this.assert(
201
+ obj === this.obj
202
+ , function(){ return 'expected ' + i(this.obj) + ' to equal ' + i(obj) }
203
+ , function(){ return 'expected ' + i(this.obj) + ' to not equal ' + i(obj) });
204
+ return this;
205
+ };
206
+
207
+ /**
208
+ * Checks if the obj sortof equals another.
209
+ *
210
+ * @api public
211
+ */
212
+
213
+ Assertion.prototype.eql = function (obj) {
214
+ this.assert(
215
+ expect.eql(obj, this.obj)
216
+ , function(){ return 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj) }
217
+ , function(){ return 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj) });
218
+ return this;
219
+ };
220
+
221
+ /**
222
+ * Assert within start to finish (inclusive).
223
+ *
224
+ * @param {Number} start
225
+ * @param {Number} finish
226
+ * @api public
227
+ */
228
+
229
+ Assertion.prototype.within = function (start, finish) {
230
+ var range = start + '..' + finish;
231
+ this.assert(
232
+ this.obj >= start && this.obj <= finish
233
+ , function(){ return 'expected ' + i(this.obj) + ' to be within ' + range }
234
+ , function(){ return 'expected ' + i(this.obj) + ' to not be within ' + range });
235
+ return this;
236
+ };
237
+
238
+ /**
239
+ * Assert typeof / instance of
240
+ *
241
+ * @api public
242
+ */
243
+
244
+ Assertion.prototype.a =
245
+ Assertion.prototype.an = function (type) {
246
+ if ('string' == typeof type) {
247
+ // proper english in error msg
248
+ var n = /^[aeiou]/.test(type) ? 'n' : '';
249
+
250
+ // typeof with support for 'array'
251
+ this.assert(
252
+ 'array' == type ? isArray(this.obj) :
253
+ 'object' == type
254
+ ? 'object' == typeof this.obj && null !== this.obj
255
+ : type == typeof this.obj
256
+ , function(){ return 'expected ' + i(this.obj) + ' to be a' + n + ' ' + type }
257
+ , function(){ return 'expected ' + i(this.obj) + ' not to be a' + n + ' ' + type });
258
+ } else {
259
+ // instanceof
260
+ var name = type.name || 'supplied constructor';
261
+ this.assert(
262
+ this.obj instanceof type
263
+ , function(){ return 'expected ' + i(this.obj) + ' to be an instance of ' + name }
264
+ , function(){ return 'expected ' + i(this.obj) + ' not to be an instance of ' + name });
265
+ }
266
+
267
+ return this;
268
+ };
269
+
270
+ /**
271
+ * Assert numeric value above _n_.
272
+ *
273
+ * @param {Number} n
274
+ * @api public
275
+ */
276
+
277
+ Assertion.prototype.greaterThan =
278
+ Assertion.prototype.above = function (n) {
279
+ this.assert(
280
+ this.obj > n
281
+ , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n }
282
+ , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n });
283
+ return this;
284
+ };
285
+
286
+ /**
287
+ * Assert numeric value below _n_.
288
+ *
289
+ * @param {Number} n
290
+ * @api public
291
+ */
292
+
293
+ Assertion.prototype.lessThan =
294
+ Assertion.prototype.below = function (n) {
295
+ this.assert(
296
+ this.obj < n
297
+ , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n }
298
+ , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n });
299
+ return this;
300
+ };
301
+
302
+ /**
303
+ * Assert string value matches _regexp_.
304
+ *
305
+ * @param {RegExp} regexp
306
+ * @api public
307
+ */
308
+
309
+ Assertion.prototype.match = function (regexp) {
310
+ this.assert(
311
+ regexp.exec(this.obj)
312
+ , function(){ return 'expected ' + i(this.obj) + ' to match ' + regexp }
313
+ , function(){ return 'expected ' + i(this.obj) + ' not to match ' + regexp });
314
+ return this;
315
+ };
316
+
317
+ /**
318
+ * Assert property "length" exists and has value of _n_.
319
+ *
320
+ * @param {Number} n
321
+ * @api public
322
+ */
323
+
324
+ Assertion.prototype.length = function (n) {
325
+ expect(this.obj).to.have.property('length');
326
+ var len = this.obj.length;
327
+ this.assert(
328
+ n == len
329
+ , function(){ return 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len }
330
+ , function(){ return 'expected ' + i(this.obj) + ' to not have a length of ' + len });
331
+ return this;
332
+ };
333
+
334
+ /**
335
+ * Assert property _name_ exists, with optional _val_.
336
+ *
337
+ * @param {String} name
338
+ * @param {Mixed} val
339
+ * @api public
340
+ */
341
+
342
+ Assertion.prototype.property = function (name, val) {
343
+ if (this.flags.own) {
344
+ this.assert(
345
+ Object.prototype.hasOwnProperty.call(this.obj, name)
346
+ , function(){ return 'expected ' + i(this.obj) + ' to have own property ' + i(name) }
347
+ , function(){ return 'expected ' + i(this.obj) + ' to not have own property ' + i(name) });
348
+ return this;
349
+ }
350
+
351
+ if (this.flags.not && undefined !== val) {
352
+ if (undefined === this.obj[name]) {
353
+ throw new Error(i(this.obj) + ' has no property ' + i(name));
354
+ }
355
+ } else {
356
+ var hasProp;
357
+ try {
358
+ hasProp = name in this.obj
359
+ } catch (e) {
360
+ hasProp = undefined !== this.obj[name]
361
+ }
362
+
363
+ this.assert(
364
+ hasProp
365
+ , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) }
366
+ , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) });
367
+ }
368
+
369
+ if (undefined !== val) {
370
+ this.assert(
371
+ val === this.obj[name]
372
+ , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name)
373
+ + ' of ' + i(val) + ', but got ' + i(this.obj[name]) }
374
+ , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name)
375
+ + ' of ' + i(val) });
376
+ }
377
+
378
+ this.obj = this.obj[name];
379
+ return this;
380
+ };
381
+
382
+ /**
383
+ * Assert that the array contains _obj_ or string contains _obj_.
384
+ *
385
+ * @param {Mixed} obj|string
386
+ * @api public
387
+ */
388
+
389
+ Assertion.prototype.string =
390
+ Assertion.prototype.contain = function (obj) {
391
+ if ('string' == typeof this.obj) {
392
+ this.assert(
393
+ ~this.obj.indexOf(obj)
394
+ , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) }
395
+ , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) });
396
+ } else {
397
+ this.assert(
398
+ ~indexOf(this.obj, obj)
399
+ , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) }
400
+ , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) });
401
+ }
402
+ return this;
403
+ };
404
+
405
+ /**
406
+ * Assert exact keys or inclusion of keys by using
407
+ * the `.own` modifier.
408
+ *
409
+ * @param {Array|String ...} keys
410
+ * @api public
411
+ */
412
+
413
+ Assertion.prototype.key =
414
+ Assertion.prototype.keys = function ($keys) {
415
+ var str
416
+ , ok = true;
417
+
418
+ $keys = isArray($keys)
419
+ ? $keys
420
+ : Array.prototype.slice.call(arguments);
421
+
422
+ if (!$keys.length) throw new Error('keys required');
423
+
424
+ var actual = keys(this.obj)
425
+ , len = $keys.length;
426
+
427
+ // Inclusion
428
+ ok = every($keys, function (key) {
429
+ return ~indexOf(actual, key);
430
+ });
431
+
432
+ // Strict
433
+ if (!this.flags.not && this.flags.only) {
434
+ ok = ok && $keys.length == actual.length;
435
+ }
436
+
437
+ // Key string
438
+ if (len > 1) {
439
+ $keys = map($keys, function (key) {
440
+ return i(key);
441
+ });
442
+ var last = $keys.pop();
443
+ str = $keys.join(', ') + ', and ' + last;
444
+ } else {
445
+ str = i($keys[0]);
446
+ }
447
+
448
+ // Form
449
+ str = (len > 1 ? 'keys ' : 'key ') + str;
450
+
451
+ // Have / include
452
+ str = (!this.flags.only ? 'include ' : 'only have ') + str;
453
+
454
+ // Assertion
455
+ this.assert(
456
+ ok
457
+ , function(){ return 'expected ' + i(this.obj) + ' to ' + str }
458
+ , function(){ return 'expected ' + i(this.obj) + ' to not ' + str });
459
+
460
+ return this;
461
+ };
462
+ /**
463
+ * Assert a failure.
464
+ *
465
+ * @param {String ...} custom message
466
+ * @api public
467
+ */
468
+ Assertion.prototype.fail = function (msg) {
469
+ msg = msg || "explicit failure";
470
+ this.assert(false, msg, msg);
471
+ return this;
472
+ };
473
+
474
+ /**
475
+ * Function bind implementation.
476
+ */
477
+
478
+ function bind (fn, scope) {
479
+ return function () {
480
+ return fn.apply(scope, arguments);
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Array every compatibility
486
+ *
487
+ * @see bit.ly/5Fq1N2
488
+ * @api public
489
+ */
490
+
491
+ function every (arr, fn, thisObj) {
492
+ var scope = thisObj || global;
493
+ for (var i = 0, j = arr.length; i < j; ++i) {
494
+ if (!fn.call(scope, arr[i], i, arr)) {
495
+ return false;
496
+ }
497
+ }
498
+ return true;
499
+ };
500
+
501
+ /**
502
+ * Array indexOf compatibility.
503
+ *
504
+ * @see bit.ly/a5Dxa2
505
+ * @api public
506
+ */
507
+
508
+ function indexOf (arr, o, i) {
509
+ if (Array.prototype.indexOf) {
510
+ return Array.prototype.indexOf.call(arr, o, i);
511
+ }
512
+
513
+ if (arr.length === undefined) {
514
+ return -1;
515
+ }
516
+
517
+ for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0
518
+ ; i < j && arr[i] !== o; i++);
519
+
520
+ return j <= i ? -1 : i;
521
+ };
522
+
523
+ // https://gist.github.com/1044128/
524
+ var getOuterHTML = function(element) {
525
+ if ('outerHTML' in element) return element.outerHTML;
526
+ var ns = "http://www.w3.org/1999/xhtml";
527
+ var container = document.createElementNS(ns, '_');
528
+ var elemProto = (window.HTMLElement || window.Element).prototype;
529
+ var xmlSerializer = new XMLSerializer();
530
+ var html;
531
+ if (document.xmlVersion) {
532
+ return xmlSerializer.serializeToString(element);
533
+ } else {
534
+ container.appendChild(element.cloneNode(false));
535
+ html = container.innerHTML.replace('><', '>' + element.innerHTML + '<');
536
+ container.innerHTML = '';
537
+ return html;
538
+ }
539
+ };
540
+
541
+ // Returns true if object is a DOM element.
542
+ var isDOMElement = function (object) {
543
+ if (typeof HTMLElement === 'object') {
544
+ return object instanceof HTMLElement;
545
+ } else {
546
+ return object &&
547
+ typeof object === 'object' &&
548
+ object.nodeType === 1 &&
549
+ typeof object.nodeName === 'string';
550
+ }
551
+ };
552
+
553
+ /**
554
+ * Inspects an object.
555
+ *
556
+ * @see taken from node.js `util` module (copyright Joyent, MIT license)
557
+ * @api private
558
+ */
559
+
560
+ function i (obj, showHidden, depth) {
561
+ var seen = [];
562
+
563
+ function stylize (str) {
564
+ return str;
565
+ };
566
+
567
+ function format (value, recurseTimes) {
568
+ // Provide a hook for user-specified inspect functions.
569
+ // Check that value is an object with an inspect function on it
570
+ if (value && typeof value.inspect === 'function' &&
571
+ // Filter out the util module, it's inspect function is special
572
+ value !== exports &&
573
+ // Also filter out any prototype objects using the circular check.
574
+ !(value.constructor && value.constructor.prototype === value)) {
575
+ return value.inspect(recurseTimes);
576
+ }
577
+
578
+ // Primitive types cannot have properties
579
+ switch (typeof value) {
580
+ case 'undefined':
581
+ return stylize('undefined', 'undefined');
582
+
583
+ case 'string':
584
+ var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '')
585
+ .replace(/'/g, "\\'")
586
+ .replace(/\\"/g, '"') + '\'';
587
+ return stylize(simple, 'string');
588
+
589
+ case 'number':
590
+ return stylize('' + value, 'number');
591
+
592
+ case 'boolean':
593
+ return stylize('' + value, 'boolean');
594
+ }
595
+ // For some reason typeof null is "object", so special case here.
596
+ if (value === null) {
597
+ return stylize('null', 'null');
598
+ }
599
+
600
+ if (isDOMElement(value)) {
601
+ return getOuterHTML(value);
602
+ }
603
+
604
+ // Look up the keys of the object.
605
+ var visible_keys = keys(value);
606
+ var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys;
607
+
608
+ // Functions without properties can be shortcutted.
609
+ if (typeof value === 'function' && $keys.length === 0) {
610
+ if (isRegExp(value)) {
611
+ return stylize('' + value, 'regexp');
612
+ } else {
613
+ var name = value.name ? ': ' + value.name : '';
614
+ return stylize('[Function' + name + ']', 'special');
615
+ }
616
+ }
617
+
618
+ // Dates without properties can be shortcutted
619
+ if (isDate(value) && $keys.length === 0) {
620
+ return stylize(value.toUTCString(), 'date');
621
+ }
622
+
623
+ var base, type, braces;
624
+ // Determine the object type
625
+ if (isArray(value)) {
626
+ type = 'Array';
627
+ braces = ['[', ']'];
628
+ } else {
629
+ type = 'Object';
630
+ braces = ['{', '}'];
631
+ }
632
+
633
+ // Make functions say that they are functions
634
+ if (typeof value === 'function') {
635
+ var n = value.name ? ': ' + value.name : '';
636
+ base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']';
637
+ } else {
638
+ base = '';
639
+ }
640
+
641
+ // Make dates with properties first say the date
642
+ if (isDate(value)) {
643
+ base = ' ' + value.toUTCString();
644
+ }
645
+
646
+ if ($keys.length === 0) {
647
+ return braces[0] + base + braces[1];
648
+ }
649
+
650
+ if (recurseTimes < 0) {
651
+ if (isRegExp(value)) {
652
+ return stylize('' + value, 'regexp');
653
+ } else {
654
+ return stylize('[Object]', 'special');
655
+ }
656
+ }
657
+
658
+ seen.push(value);
659
+
660
+ var output = map($keys, function (key) {
661
+ var name, str;
662
+ if (value.__lookupGetter__) {
663
+ if (value.__lookupGetter__(key)) {
664
+ if (value.__lookupSetter__(key)) {
665
+ str = stylize('[Getter/Setter]', 'special');
666
+ } else {
667
+ str = stylize('[Getter]', 'special');
668
+ }
669
+ } else {
670
+ if (value.__lookupSetter__(key)) {
671
+ str = stylize('[Setter]', 'special');
672
+ }
673
+ }
674
+ }
675
+ if (indexOf(visible_keys, key) < 0) {
676
+ name = '[' + key + ']';
677
+ }
678
+ if (!str) {
679
+ if (indexOf(seen, value[key]) < 0) {
680
+ if (recurseTimes === null) {
681
+ str = format(value[key]);
682
+ } else {
683
+ str = format(value[key], recurseTimes - 1);
684
+ }
685
+ if (str.indexOf('\n') > -1) {
686
+ if (isArray(value)) {
687
+ str = map(str.split('\n'), function (line) {
688
+ return ' ' + line;
689
+ }).join('\n').substr(2);
690
+ } else {
691
+ str = '\n' + map(str.split('\n'), function (line) {
692
+ return ' ' + line;
693
+ }).join('\n');
694
+ }
695
+ }
696
+ } else {
697
+ str = stylize('[Circular]', 'special');
698
+ }
699
+ }
700
+ if (typeof name === 'undefined') {
701
+ if (type === 'Array' && key.match(/^\d+$/)) {
702
+ return str;
703
+ }
704
+ name = json.stringify('' + key);
705
+ if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
706
+ name = name.substr(1, name.length - 2);
707
+ name = stylize(name, 'name');
708
+ } else {
709
+ name = name.replace(/'/g, "\\'")
710
+ .replace(/\\"/g, '"')
711
+ .replace(/(^"|"$)/g, "'");
712
+ name = stylize(name, 'string');
713
+ }
714
+ }
715
+
716
+ return name + ': ' + str;
717
+ });
718
+
719
+ seen.pop();
720
+
721
+ var numLinesEst = 0;
722
+ var length = reduce(output, function (prev, cur) {
723
+ numLinesEst++;
724
+ if (indexOf(cur, '\n') >= 0) numLinesEst++;
725
+ return prev + cur.length + 1;
726
+ }, 0);
727
+
728
+ if (length > 50) {
729
+ output = braces[0] +
730
+ (base === '' ? '' : base + '\n ') +
731
+ ' ' +
732
+ output.join(',\n ') +
733
+ ' ' +
734
+ braces[1];
735
+
736
+ } else {
737
+ output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
738
+ }
739
+
740
+ return output;
741
+ }
742
+ return format(obj, (typeof depth === 'undefined' ? 2 : depth));
743
+ };
744
+
745
+ function isArray (ar) {
746
+ return Object.prototype.toString.call(ar) == '[object Array]';
747
+ };
748
+
749
+ function isRegExp(re) {
750
+ var s;
751
+ try {
752
+ s = '' + re;
753
+ } catch (e) {
754
+ return false;
755
+ }
756
+
757
+ return re instanceof RegExp || // easy case
758
+ // duck-type for context-switching evalcx case
759
+ typeof(re) === 'function' &&
760
+ re.constructor.name === 'RegExp' &&
761
+ re.compile &&
762
+ re.test &&
763
+ re.exec &&
764
+ s.match(/^\/.*\/[gim]{0,3}$/);
765
+ };
766
+
767
+ function isDate(d) {
768
+ if (d instanceof Date) return true;
769
+ return false;
770
+ };
771
+
772
+ function keys (obj) {
773
+ if (Object.keys) {
774
+ return Object.keys(obj);
775
+ }
776
+
777
+ var keys = [];
778
+
779
+ for (var i in obj) {
780
+ if (Object.prototype.hasOwnProperty.call(obj, i)) {
781
+ keys.push(i);
782
+ }
783
+ }
784
+
785
+ return keys;
786
+ }
787
+
788
+ function map (arr, mapper, that) {
789
+ if (Array.prototype.map) {
790
+ return Array.prototype.map.call(arr, mapper, that);
791
+ }
792
+
793
+ var other= new Array(arr.length);
794
+
795
+ for (var i= 0, n = arr.length; i<n; i++)
796
+ if (i in arr)
797
+ other[i] = mapper.call(that, arr[i], i, arr);
798
+
799
+ return other;
800
+ };
801
+
802
+ function reduce (arr, fun) {
803
+ if (Array.prototype.reduce) {
804
+ return Array.prototype.reduce.apply(
805
+ arr
806
+ , Array.prototype.slice.call(arguments, 1)
807
+ );
808
+ }
809
+
810
+ var len = +this.length;
811
+
812
+ if (typeof fun !== "function")
813
+ throw new TypeError();
814
+
815
+ // no value to return if no initial value and an empty array
816
+ if (len === 0 && arguments.length === 1)
817
+ throw new TypeError();
818
+
819
+ var i = 0;
820
+ if (arguments.length >= 2) {
821
+ var rv = arguments[1];
822
+ } else {
823
+ do {
824
+ if (i in this) {
825
+ rv = this[i++];
826
+ break;
827
+ }
828
+
829
+ // if array contains no values, no initial value to return
830
+ if (++i >= len)
831
+ throw new TypeError();
832
+ } while (true);
833
+ }
834
+
835
+ for (; i < len; i++) {
836
+ if (i in this)
837
+ rv = fun.call(null, rv, this[i], i, this);
838
+ }
839
+
840
+ return rv;
841
+ };
842
+
843
+ /**
844
+ * Asserts deep equality
845
+ *
846
+ * @see taken from node.js `assert` module (copyright Joyent, MIT license)
847
+ * @api private
848
+ */
849
+
850
+ expect.eql = function eql (actual, expected) {
851
+ // 7.1. All identical values are equivalent, as determined by ===.
852
+ if (actual === expected) {
853
+ return true;
854
+ } else if ('undefined' != typeof Buffer
855
+ && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
856
+ if (actual.length != expected.length) return false;
857
+
858
+ for (var i = 0; i < actual.length; i++) {
859
+ if (actual[i] !== expected[i]) return false;
860
+ }
861
+
862
+ return true;
863
+
864
+ // 7.2. If the expected value is a Date object, the actual value is
865
+ // equivalent if it is also a Date object that refers to the same time.
866
+ } else if (actual instanceof Date && expected instanceof Date) {
867
+ return actual.getTime() === expected.getTime();
868
+
869
+ // 7.3. Other pairs that do not both pass typeof value == "object",
870
+ // equivalence is determined by ==.
871
+ } else if (typeof actual != 'object' && typeof expected != 'object') {
872
+ return actual == expected;
873
+
874
+ // 7.4. For all other Object pairs, including Array objects, equivalence is
875
+ // determined by having the same number of owned properties (as verified
876
+ // with Object.prototype.hasOwnProperty.call), the same set of keys
877
+ // (although not necessarily the same order), equivalent values for every
878
+ // corresponding key, and an identical "prototype" property. Note: this
879
+ // accounts for both named and indexed properties on Arrays.
880
+ } else {
881
+ return objEquiv(actual, expected);
882
+ }
883
+ }
884
+
885
+ function isUndefinedOrNull (value) {
886
+ return value === null || value === undefined;
887
+ }
888
+
889
+ function isArguments (object) {
890
+ return Object.prototype.toString.call(object) == '[object Arguments]';
891
+ }
892
+
893
+ function objEquiv (a, b) {
894
+ if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
895
+ return false;
896
+ // an identical "prototype" property.
897
+ if (a.prototype !== b.prototype) return false;
898
+ //~~~I've managed to break Object.keys through screwy arguments passing.
899
+ // Converting to array solves the problem.
900
+ if (isArguments(a)) {
901
+ if (!isArguments(b)) {
902
+ return false;
903
+ }
904
+ a = pSlice.call(a);
905
+ b = pSlice.call(b);
906
+ return expect.eql(a, b);
907
+ }
908
+ try{
909
+ var ka = keys(a),
910
+ kb = keys(b),
911
+ key, i;
912
+ } catch (e) {//happens when one is a string literal and the other isn't
913
+ return false;
914
+ }
915
+ // having the same number of owned properties (keys incorporates hasOwnProperty)
916
+ if (ka.length != kb.length)
917
+ return false;
918
+ //the same set of keys (although not necessarily the same order),
919
+ ka.sort();
920
+ kb.sort();
921
+ //~~~cheap key test
922
+ for (i = ka.length - 1; i >= 0; i--) {
923
+ if (ka[i] != kb[i])
924
+ return false;
925
+ }
926
+ //equivalent values for every corresponding key, and
927
+ //~~~possibly expensive deep test
928
+ for (i = ka.length - 1; i >= 0; i--) {
929
+ key = ka[i];
930
+ if (!expect.eql(a[key], b[key]))
931
+ return false;
932
+ }
933
+ return true;
934
+ }
935
+
936
+ var json = (function () {
937
+ "use strict";
938
+
939
+ if ('object' == typeof JSON && JSON.parse && JSON.stringify) {
940
+ return {
941
+ parse: nativeJSON.parse
942
+ , stringify: nativeJSON.stringify
943
+ }
944
+ }
945
+
946
+ var JSON = {};
947
+
948
+ function f(n) {
949
+ // Format integers to have at least two digits.
950
+ return n < 10 ? '0' + n : n;
951
+ }
952
+
953
+ function date(d, key) {
954
+ return isFinite(d.valueOf()) ?
955
+ d.getUTCFullYear() + '-' +
956
+ f(d.getUTCMonth() + 1) + '-' +
957
+ f(d.getUTCDate()) + 'T' +
958
+ f(d.getUTCHours()) + ':' +
959
+ f(d.getUTCMinutes()) + ':' +
960
+ f(d.getUTCSeconds()) + 'Z' : null;
961
+ };
962
+
963
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
964
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
965
+ gap,
966
+ indent,
967
+ meta = { // table of character substitutions
968
+ '\b': '\\b',
969
+ '\t': '\\t',
970
+ '\n': '\\n',
971
+ '\f': '\\f',
972
+ '\r': '\\r',
973
+ '"' : '\\"',
974
+ '\\': '\\\\'
975
+ },
976
+ rep;
977
+
978
+
979
+ function quote(string) {
980
+
981
+ // If the string contains no control characters, no quote characters, and no
982
+ // backslash characters, then we can safely slap some quotes around it.
983
+ // Otherwise we must also replace the offending characters with safe escape
984
+ // sequences.
985
+
986
+ escapable.lastIndex = 0;
987
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
988
+ var c = meta[a];
989
+ return typeof c === 'string' ? c :
990
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
991
+ }) + '"' : '"' + string + '"';
992
+ }
993
+
994
+
995
+ function str(key, holder) {
996
+
997
+ // Produce a string from holder[key].
998
+
999
+ var i, // The loop counter.
1000
+ k, // The member key.
1001
+ v, // The member value.
1002
+ length,
1003
+ mind = gap,
1004
+ partial,
1005
+ value = holder[key];
1006
+
1007
+ // If the value has a toJSON method, call it to obtain a replacement value.
1008
+
1009
+ if (value instanceof Date) {
1010
+ value = date(key);
1011
+ }
1012
+
1013
+ // If we were called with a replacer function, then call the replacer to
1014
+ // obtain a replacement value.
1015
+
1016
+ if (typeof rep === 'function') {
1017
+ value = rep.call(holder, key, value);
1018
+ }
1019
+
1020
+ // What happens next depends on the value's type.
1021
+
1022
+ switch (typeof value) {
1023
+ case 'string':
1024
+ return quote(value);
1025
+
1026
+ case 'number':
1027
+
1028
+ // JSON numbers must be finite. Encode non-finite numbers as null.
1029
+
1030
+ return isFinite(value) ? String(value) : 'null';
1031
+
1032
+ case 'boolean':
1033
+ case 'null':
1034
+
1035
+ // If the value is a boolean or null, convert it to a string. Note:
1036
+ // typeof null does not produce 'null'. The case is included here in
1037
+ // the remote chance that this gets fixed someday.
1038
+
1039
+ return String(value);
1040
+
1041
+ // If the type is 'object', we might be dealing with an object or an array or
1042
+ // null.
1043
+
1044
+ case 'object':
1045
+
1046
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
1047
+ // so watch out for that case.
1048
+
1049
+ if (!value) {
1050
+ return 'null';
1051
+ }
1052
+
1053
+ // Make an array to hold the partial results of stringifying this object value.
1054
+
1055
+ gap += indent;
1056
+ partial = [];
1057
+
1058
+ // Is the value an array?
1059
+
1060
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
1061
+
1062
+ // The value is an array. Stringify every element. Use null as a placeholder
1063
+ // for non-JSON values.
1064
+
1065
+ length = value.length;
1066
+ for (i = 0; i < length; i += 1) {
1067
+ partial[i] = str(i, value) || 'null';
1068
+ }
1069
+
1070
+ // Join all of the elements together, separated with commas, and wrap them in
1071
+ // brackets.
1072
+
1073
+ v = partial.length === 0 ? '[]' : gap ?
1074
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
1075
+ '[' + partial.join(',') + ']';
1076
+ gap = mind;
1077
+ return v;
1078
+ }
1079
+
1080
+ // If the replacer is an array, use it to select the members to be stringified.
1081
+
1082
+ if (rep && typeof rep === 'object') {
1083
+ length = rep.length;
1084
+ for (i = 0; i < length; i += 1) {
1085
+ if (typeof rep[i] === 'string') {
1086
+ k = rep[i];
1087
+ v = str(k, value);
1088
+ if (v) {
1089
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
1090
+ }
1091
+ }
1092
+ }
1093
+ } else {
1094
+
1095
+ // Otherwise, iterate through all of the keys in the object.
1096
+
1097
+ for (k in value) {
1098
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
1099
+ v = str(k, value);
1100
+ if (v) {
1101
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
1102
+ }
1103
+ }
1104
+ }
1105
+ }
1106
+
1107
+ // Join all of the member texts together, separated with commas,
1108
+ // and wrap them in braces.
1109
+
1110
+ v = partial.length === 0 ? '{}' : gap ?
1111
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
1112
+ '{' + partial.join(',') + '}';
1113
+ gap = mind;
1114
+ return v;
1115
+ }
1116
+ }
1117
+
1118
+ // If the JSON object does not yet have a stringify method, give it one.
1119
+
1120
+ JSON.stringify = function (value, replacer, space) {
1121
+
1122
+ // The stringify method takes a value and an optional replacer, and an optional
1123
+ // space parameter, and returns a JSON text. The replacer can be a function
1124
+ // that can replace values, or an array of strings that will select the keys.
1125
+ // A default replacer method can be provided. Use of the space parameter can
1126
+ // produce text that is more easily readable.
1127
+
1128
+ var i;
1129
+ gap = '';
1130
+ indent = '';
1131
+
1132
+ // If the space parameter is a number, make an indent string containing that
1133
+ // many spaces.
1134
+
1135
+ if (typeof space === 'number') {
1136
+ for (i = 0; i < space; i += 1) {
1137
+ indent += ' ';
1138
+ }
1139
+
1140
+ // If the space parameter is a string, it will be used as the indent string.
1141
+
1142
+ } else if (typeof space === 'string') {
1143
+ indent = space;
1144
+ }
1145
+
1146
+ // If there is a replacer, it must be a function or an array.
1147
+ // Otherwise, throw an error.
1148
+
1149
+ rep = replacer;
1150
+ if (replacer && typeof replacer !== 'function' &&
1151
+ (typeof replacer !== 'object' ||
1152
+ typeof replacer.length !== 'number')) {
1153
+ throw new Error('JSON.stringify');
1154
+ }
1155
+
1156
+ // Make a fake root object containing our value under the key of ''.
1157
+ // Return the result of stringifying the value.
1158
+
1159
+ return str('', {'': value});
1160
+ };
1161
+
1162
+ // If the JSON object does not yet have a parse method, give it one.
1163
+
1164
+ JSON.parse = function (text, reviver) {
1165
+ // The parse method takes a text and an optional reviver function, and returns
1166
+ // a JavaScript value if the text is a valid JSON text.
1167
+
1168
+ var j;
1169
+
1170
+ function walk(holder, key) {
1171
+
1172
+ // The walk method is used to recursively walk the resulting structure so
1173
+ // that modifications can be made.
1174
+
1175
+ var k, v, value = holder[key];
1176
+ if (value && typeof value === 'object') {
1177
+ for (k in value) {
1178
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
1179
+ v = walk(value, k);
1180
+ if (v !== undefined) {
1181
+ value[k] = v;
1182
+ } else {
1183
+ delete value[k];
1184
+ }
1185
+ }
1186
+ }
1187
+ }
1188
+ return reviver.call(holder, key, value);
1189
+ }
1190
+
1191
+
1192
+ // Parsing happens in four stages. In the first stage, we replace certain
1193
+ // Unicode characters with escape sequences. JavaScript handles many characters
1194
+ // incorrectly, either silently deleting them, or treating them as line endings.
1195
+
1196
+ text = String(text);
1197
+ cx.lastIndex = 0;
1198
+ if (cx.test(text)) {
1199
+ text = text.replace(cx, function (a) {
1200
+ return '\\u' +
1201
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
1202
+ });
1203
+ }
1204
+
1205
+ // In the second stage, we run the text against regular expressions that look
1206
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
1207
+ // because they can cause invocation, and '=' because it can cause mutation.
1208
+ // But just to be safe, we want to reject all unexpected forms.
1209
+
1210
+ // We split the second stage into 4 regexp operations in order to work around
1211
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
1212
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
1213
+ // replace all simple value tokens with ']' characters. Third, we delete all
1214
+ // open brackets that follow a colon or comma or that begin the text. Finally,
1215
+ // we look to see that the remaining characters are only whitespace or ']' or
1216
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
1217
+
1218
+ if (/^[\],:{}\s]*$/
1219
+ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
1220
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
1221
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
1222
+
1223
+ // In the third stage we use the eval function to compile the text into a
1224
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
1225
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
1226
+ // in parens to eliminate the ambiguity.
1227
+
1228
+ j = eval('(' + text + ')');
1229
+
1230
+ // In the optional fourth stage, we recursively walk the new structure, passing
1231
+ // each name/value pair to a reviver function for possible transformation.
1232
+
1233
+ return typeof reviver === 'function' ?
1234
+ walk({'': j}, '') : j;
1235
+ }
1236
+
1237
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
1238
+
1239
+ throw new SyntaxError('JSON.parse');
1240
+ };
1241
+
1242
+ return JSON;
1243
+ })();
1244
+
1245
+ if ('undefined' != typeof window) {
1246
+ window.expect = module.exports;
1247
+ }
1248
+
1249
+ })(
1250
+ this
1251
+ , 'undefined' != typeof module ? module : {}
1252
+ , 'undefined' != typeof exports ? exports : {}
1253
+ );