extjs-mvc 0.4.0.j → 0.4.0.k

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. data/Rakefile +1 -1
  2. data/VERSION +1 -1
  3. data/lib/extjs-mvc.rb +0 -14
  4. data/lib/extjs-mvc/api.rb +14 -27
  5. data/lib/extjs-mvc/src/App.js +219 -0
  6. data/lib/extjs-mvc/src/MVC.js +260 -0
  7. data/lib/extjs-mvc/src/Presenter.js +52 -0
  8. data/lib/extjs-mvc/src/README.rdoc +69 -0
  9. data/lib/extjs-mvc/src/controller/Controller.js +278 -0
  10. data/lib/extjs-mvc/src/controller/CrudController.js +460 -0
  11. data/lib/extjs-mvc/src/lib/Array.js +26 -0
  12. data/lib/extjs-mvc/src/lib/Booter.js +417 -0
  13. data/lib/extjs-mvc/src/lib/ClassManager.js +191 -0
  14. data/lib/extjs-mvc/src/lib/ControllerClassManager.js +95 -0
  15. data/lib/extjs-mvc/src/lib/Dependencies.js +44 -0
  16. data/lib/extjs-mvc/src/lib/DispatchMatcher.js +98 -0
  17. data/lib/extjs-mvc/src/lib/Dispatcher.js +129 -0
  18. data/lib/extjs-mvc/src/lib/Environment.js +43 -0
  19. data/lib/extjs-mvc/src/lib/Inflector.js +155 -0
  20. data/lib/extjs-mvc/src/lib/ModelClassManager.js +19 -0
  21. data/lib/extjs-mvc/src/lib/Route.js +139 -0
  22. data/lib/extjs-mvc/src/lib/Router.js +282 -0
  23. data/lib/extjs-mvc/src/lib/String.js +94 -0
  24. data/lib/extjs-mvc/src/lib/ViewClassManager.js +229 -0
  25. data/lib/extjs-mvc/src/lib/notes.txt +32 -0
  26. data/lib/extjs-mvc/src/model/AdapterManager.js +30 -0
  27. data/lib/extjs-mvc/src/model/Association.js +26 -0
  28. data/lib/extjs-mvc/src/model/Base.js +63 -0
  29. data/lib/extjs-mvc/src/model/BelongsToAssociation.js +116 -0
  30. data/lib/extjs-mvc/src/model/Cache.js +131 -0
  31. data/lib/extjs-mvc/src/model/HasManyAssociation.js +160 -0
  32. data/lib/extjs-mvc/src/model/Model.js +331 -0
  33. data/lib/extjs-mvc/src/model/UrlBuilder.js +106 -0
  34. data/lib/extjs-mvc/src/model/adapters/AbstractAdapter.js +296 -0
  35. data/lib/extjs-mvc/src/model/adapters/MemoryAdapter.js +103 -0
  36. data/lib/extjs-mvc/src/model/adapters/RESTAdapter.js +345 -0
  37. data/lib/extjs-mvc/src/model/adapters/RESTJSONAdapter.js +68 -0
  38. data/lib/extjs-mvc/src/model/adapters/notes.txt +42 -0
  39. data/lib/extjs-mvc/src/model/associations/Association.js +192 -0
  40. data/lib/extjs-mvc/src/model/associations/notes.txt +87 -0
  41. data/lib/extjs-mvc/src/model/validations/Errors.js +136 -0
  42. data/lib/extjs-mvc/src/model/validations/Plugin.js +139 -0
  43. data/lib/extjs-mvc/src/model/validations/Validations.js +276 -0
  44. data/lib/extjs-mvc/src/notes/Charts.graffle +0 -0
  45. data/lib/extjs-mvc/src/overrides/Ext.Component.js +21 -0
  46. data/lib/extjs-mvc/src/overrides/Ext.extend.js +142 -0
  47. data/lib/extjs-mvc/src/spec/Array.spec.js +15 -0
  48. data/lib/extjs-mvc/src/spec/ExtMVC.spec.js +65 -0
  49. data/lib/extjs-mvc/src/spec/Model.spec.js +370 -0
  50. data/lib/extjs-mvc/src/spec/OS.spec.js +83 -0
  51. data/lib/extjs-mvc/src/spec/Router.spec.js +99 -0
  52. data/lib/extjs-mvc/src/spec/SpecHelper.js +106 -0
  53. data/lib/extjs-mvc/src/spec/String.spec.js +83 -0
  54. data/lib/extjs-mvc/src/spec/model/AbstractAdapter.spec.js +49 -0
  55. data/lib/extjs-mvc/src/spec/model/Associations.spec.js +99 -0
  56. data/lib/extjs-mvc/src/spec/model/Cache.spec.js +5 -0
  57. data/lib/extjs-mvc/src/spec/model/RESTAdapter.spec.js +19 -0
  58. data/lib/extjs-mvc/src/spec/model/ValidationErrors.spec.js +64 -0
  59. data/lib/extjs-mvc/src/spec/model/Validations.spec.js +166 -0
  60. data/lib/extjs-mvc/src/spec/model/ValidationsPlugin.spec.js +108 -0
  61. data/lib/extjs-mvc/src/spec/suite.html +60 -0
  62. data/lib/extjs-mvc/src/specs-old/JSSpec.css +216 -0
  63. data/lib/extjs-mvc/src/specs-old/JSSpec.js +1512 -0
  64. data/lib/extjs-mvc/src/specs-old/all.html +66 -0
  65. data/lib/extjs-mvc/src/specs-old/base.js +14 -0
  66. data/lib/extjs-mvc/src/specs-old/controller.js +17 -0
  67. data/lib/extjs-mvc/src/specs-old/diff_match_patch.js +1 -0
  68. data/lib/extjs-mvc/src/specs-old/model.js +70 -0
  69. data/lib/extjs-mvc/src/specs-old/route.js +38 -0
  70. data/lib/extjs-mvc/src/specs-old/router.js +59 -0
  71. data/lib/extjs-mvc/src/specs-old/string.js +22 -0
  72. data/lib/extjs-mvc/src/testrunner/JSpecFormatter.js +111 -0
  73. data/lib/extjs-mvc/src/testrunner/TestClient.js +181 -0
  74. data/lib/extjs-mvc/src/testrunner/TestGrid.js +351 -0
  75. data/lib/extjs-mvc/src/testrunner/TestRunner.js +110 -0
  76. data/lib/extjs-mvc/src/testrunner/TestViewport.js +94 -0
  77. data/lib/extjs-mvc/src/vendor.yml +30 -0
  78. data/lib/extjs-mvc/src/vendor/ext-3.1.1/vendor.yml +16 -0
  79. data/lib/extjs-mvc/src/view/FormWindow.js +184 -0
  80. data/lib/extjs-mvc/src/view/HasManyEditorGridPanel.js +211 -0
  81. data/lib/extjs-mvc/src/view/scaffold/Edit.js +46 -0
  82. data/lib/extjs-mvc/src/view/scaffold/Index.js +561 -0
  83. data/lib/extjs-mvc/src/view/scaffold/New.js +20 -0
  84. data/lib/extjs-mvc/src/view/scaffold/ScaffoldFormPanel.js +255 -0
  85. data/lib/{vendor.yml → extjs-mvc/vendor.yml} +0 -0
  86. data/test/app/vendor/extjs-mvc/App.js +219 -0
  87. data/test/app/vendor/extjs-mvc/MVC.js +260 -0
  88. data/test/app/vendor/extjs-mvc/Presenter.js +52 -0
  89. data/test/app/vendor/extjs-mvc/README.rdoc +69 -0
  90. data/test/app/vendor/extjs-mvc/controller/Controller.js +278 -0
  91. data/test/app/vendor/extjs-mvc/controller/CrudController.js +460 -0
  92. data/test/app/vendor/extjs-mvc/lib/Array.js +26 -0
  93. data/test/app/vendor/extjs-mvc/lib/Booter.js +417 -0
  94. data/test/app/vendor/extjs-mvc/lib/ClassManager.js +191 -0
  95. data/test/app/vendor/extjs-mvc/lib/ControllerClassManager.js +95 -0
  96. data/test/app/vendor/extjs-mvc/lib/Dependencies.js +44 -0
  97. data/test/app/vendor/extjs-mvc/lib/DispatchMatcher.js +98 -0
  98. data/test/app/vendor/extjs-mvc/lib/Dispatcher.js +129 -0
  99. data/test/app/vendor/extjs-mvc/lib/Environment.js +43 -0
  100. data/test/app/vendor/extjs-mvc/lib/Inflector.js +155 -0
  101. data/test/app/vendor/extjs-mvc/lib/ModelClassManager.js +19 -0
  102. data/test/app/vendor/extjs-mvc/lib/Route.js +139 -0
  103. data/test/app/vendor/extjs-mvc/lib/Router.js +282 -0
  104. data/test/app/vendor/extjs-mvc/lib/String.js +94 -0
  105. data/test/app/vendor/extjs-mvc/lib/ViewClassManager.js +229 -0
  106. data/test/app/vendor/extjs-mvc/lib/notes.txt +32 -0
  107. data/test/app/vendor/extjs-mvc/model/AdapterManager.js +30 -0
  108. data/test/app/vendor/extjs-mvc/model/Association.js +26 -0
  109. data/test/app/vendor/extjs-mvc/model/Base.js +63 -0
  110. data/test/app/vendor/extjs-mvc/model/BelongsToAssociation.js +116 -0
  111. data/test/app/vendor/extjs-mvc/model/Cache.js +131 -0
  112. data/test/app/vendor/extjs-mvc/model/HasManyAssociation.js +160 -0
  113. data/test/app/vendor/extjs-mvc/model/Model.js +331 -0
  114. data/test/app/vendor/extjs-mvc/model/UrlBuilder.js +106 -0
  115. data/test/app/vendor/extjs-mvc/model/adapters/AbstractAdapter.js +296 -0
  116. data/test/app/vendor/extjs-mvc/model/adapters/MemoryAdapter.js +103 -0
  117. data/test/app/vendor/extjs-mvc/model/adapters/RESTAdapter.js +345 -0
  118. data/test/app/vendor/extjs-mvc/model/adapters/RESTJSONAdapter.js +68 -0
  119. data/test/app/vendor/extjs-mvc/model/adapters/notes.txt +42 -0
  120. data/test/app/vendor/extjs-mvc/model/associations/Association.js +192 -0
  121. data/test/app/vendor/extjs-mvc/model/associations/notes.txt +87 -0
  122. data/test/app/vendor/extjs-mvc/model/validations/Errors.js +136 -0
  123. data/test/app/vendor/extjs-mvc/model/validations/Plugin.js +139 -0
  124. data/test/app/vendor/extjs-mvc/model/validations/Validations.js +276 -0
  125. data/test/app/vendor/extjs-mvc/notes/Charts.graffle +0 -0
  126. data/test/app/vendor/extjs-mvc/overrides/Ext.Component.js +21 -0
  127. data/test/app/vendor/extjs-mvc/overrides/Ext.extend.js +142 -0
  128. data/test/app/vendor/extjs-mvc/spec/Array.spec.js +15 -0
  129. data/test/app/vendor/extjs-mvc/spec/ExtMVC.spec.js +65 -0
  130. data/test/app/vendor/extjs-mvc/spec/Model.spec.js +370 -0
  131. data/test/app/vendor/extjs-mvc/spec/OS.spec.js +83 -0
  132. data/test/app/vendor/extjs-mvc/spec/Router.spec.js +99 -0
  133. data/test/app/vendor/extjs-mvc/spec/SpecHelper.js +106 -0
  134. data/test/app/vendor/extjs-mvc/spec/String.spec.js +83 -0
  135. data/test/app/vendor/extjs-mvc/spec/model/AbstractAdapter.spec.js +49 -0
  136. data/test/app/vendor/extjs-mvc/spec/model/Associations.spec.js +99 -0
  137. data/test/app/vendor/extjs-mvc/spec/model/Cache.spec.js +5 -0
  138. data/test/app/vendor/extjs-mvc/spec/model/RESTAdapter.spec.js +19 -0
  139. data/test/app/vendor/extjs-mvc/spec/model/ValidationErrors.spec.js +64 -0
  140. data/test/app/vendor/extjs-mvc/spec/model/Validations.spec.js +166 -0
  141. data/test/app/vendor/extjs-mvc/spec/model/ValidationsPlugin.spec.js +108 -0
  142. data/test/app/vendor/extjs-mvc/spec/suite.html +60 -0
  143. data/test/app/vendor/extjs-mvc/specs-old/JSSpec.css +216 -0
  144. data/test/app/vendor/extjs-mvc/specs-old/JSSpec.js +1512 -0
  145. data/test/app/vendor/extjs-mvc/specs-old/all.html +66 -0
  146. data/test/app/vendor/extjs-mvc/specs-old/base.js +14 -0
  147. data/test/app/vendor/extjs-mvc/specs-old/controller.js +17 -0
  148. data/test/app/vendor/extjs-mvc/specs-old/diff_match_patch.js +1 -0
  149. data/test/app/vendor/extjs-mvc/specs-old/model.js +70 -0
  150. data/test/app/vendor/extjs-mvc/specs-old/route.js +38 -0
  151. data/test/app/vendor/extjs-mvc/specs-old/router.js +59 -0
  152. data/test/app/vendor/extjs-mvc/specs-old/string.js +22 -0
  153. data/test/app/vendor/extjs-mvc/testrunner/JSpecFormatter.js +111 -0
  154. data/test/app/vendor/extjs-mvc/testrunner/TestClient.js +181 -0
  155. data/test/app/vendor/extjs-mvc/testrunner/TestGrid.js +351 -0
  156. data/test/app/vendor/extjs-mvc/testrunner/TestRunner.js +110 -0
  157. data/test/app/vendor/extjs-mvc/testrunner/TestViewport.js +94 -0
  158. data/test/app/vendor/extjs-mvc/vendor.yml +30 -0
  159. data/test/app/vendor/extjs-mvc/vendor/ext-3.1.1/vendor.yml +16 -0
  160. data/test/app/vendor/extjs-mvc/view/FormWindow.js +184 -0
  161. data/test/app/vendor/extjs-mvc/view/HasManyEditorGridPanel.js +211 -0
  162. data/test/app/vendor/extjs-mvc/view/scaffold/Edit.js +46 -0
  163. data/test/app/vendor/extjs-mvc/view/scaffold/Index.js +561 -0
  164. data/test/app/vendor/extjs-mvc/view/scaffold/New.js +20 -0
  165. data/test/app/vendor/extjs-mvc/view/scaffold/ScaffoldFormPanel.js +255 -0
  166. data/test/helper.rb +7 -1
  167. data/test/test_extjs-mvc.rb +46 -0
  168. metadata +167 -7
  169. data/test/test_extjs-mvc-gem.rb +0 -7
@@ -0,0 +1,1512 @@
1
+ /**
2
+ * JSSpec
3
+ *
4
+ * Copyright 2007 Alan Kang
5
+ * - mailto:jania902@gmail.com
6
+ * - http://jania.pe.kr
7
+ *
8
+ * http://jania.pe.kr/aw/moin.cgi/JSSpec
9
+ *
10
+ * Dependencies:
11
+ * - diff_match_patch.js ( http://code.google.com/p/google-diff-match-patch )
12
+ *
13
+ * This library is free software; you can redistribute it and/or
14
+ * modify it under the terms of the GNU Lesser General Public
15
+ * License as published by the Free Software Foundation; either
16
+ * version 2.1 of the License, or (at your option) any later version.
17
+ *
18
+ * This library is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
+ * Lesser General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU Lesser General Public
24
+ * License along with this library; if not, write to the Free Software
25
+ * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26
+ */
27
+
28
+ /**
29
+ * Namespace
30
+ */
31
+
32
+ var JSSpec = {
33
+ specs: [],
34
+
35
+ EMPTY_FUNCTION: function() {},
36
+
37
+ Browser: {
38
+ Trident: navigator.appName == "Microsoft Internet Explorer",
39
+ Webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
40
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
41
+ Presto: navigator.appName == "Opera"
42
+ }
43
+ };
44
+
45
+
46
+
47
+ /**
48
+ * Executor
49
+ */
50
+ JSSpec.Executor = function(target, onSuccess, onException) {
51
+ this.target = target;
52
+ this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION;
53
+ this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION;
54
+
55
+ if(JSSpec.Browser.Trident) {
56
+ // Exception handler for Trident. It helps to collect exact line number where exception occured.
57
+ window.onerror = function(message, fileName, lineNumber) {
58
+ var self = window._curExecutor;
59
+ var ex = {message:message, fileName:fileName, lineNumber:lineNumber};
60
+
61
+ if(JSSpec._secondPass) {
62
+ ex = self.mergeExceptions(JSSpec._assertionFailure, ex);
63
+ delete JSSpec._secondPass;
64
+ delete JSSpec._assertionFailure;
65
+
66
+ ex.type = "failure";
67
+ self.onException(self, ex);
68
+ } else if(JSSpec._assertionFailure) {
69
+ JSSpec._secondPass = true;
70
+ self.run();
71
+ } else {
72
+ self.onException(self, ex);
73
+ }
74
+
75
+ return true;
76
+ };
77
+ }
78
+ };
79
+ JSSpec.Executor.prototype.mergeExceptions = function(assertionFailure, normalException) {
80
+ var merged = {
81
+ message:assertionFailure.message,
82
+ fileName:normalException.fileName,
83
+ lineNumber:normalException.lineNumber
84
+ };
85
+
86
+ return merged;
87
+ };
88
+
89
+ JSSpec.Executor.prototype.run = function() {
90
+ var self = this;
91
+ var target = this.target;
92
+ var onSuccess = this.onSuccess;
93
+ var onException = this.onException;
94
+
95
+ window.setTimeout(
96
+ function() {
97
+ var result;
98
+ if(JSSpec.Browser.Trident) {
99
+ window._curExecutor = self;
100
+
101
+ result = self.target();
102
+ self.onSuccess(self, result);
103
+ } else {
104
+ try {
105
+ result = self.target();
106
+ self.onSuccess(self, result);
107
+ } catch(ex) {
108
+ if(JSSpec.Browser.Webkit) ex = {message:ex.message, fileName:ex.sourceURL, lineNumber:ex.line};
109
+
110
+ if(JSSpec._secondPass) {
111
+ ex = self.mergeExceptions(JSSpec._assertionFailure, ex);
112
+ delete JSSpec._secondPass;
113
+ delete JSSpec._assertionFailure;
114
+
115
+ ex.type = "failure";
116
+ self.onException(self, ex);
117
+ } else if(JSSpec._assertionFailure) {
118
+ JSSpec._secondPass = true;
119
+ self.run();
120
+ } else {
121
+ self.onException(self, ex);
122
+ }
123
+ }
124
+ }
125
+ },
126
+ 0
127
+ );
128
+ };
129
+
130
+
131
+
132
+ /**
133
+ * CompositeExecutor composites one or more executors and execute them sequencially.
134
+ */
135
+ JSSpec.CompositeExecutor = function(onSuccess, onException, continueOnException) {
136
+ this.queue = [];
137
+ this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION;
138
+ this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION;
139
+ this.continueOnException = !!continueOnException;
140
+ };
141
+
142
+ JSSpec.CompositeExecutor.prototype.addFunction = function(func) {
143
+ this.addExecutor(new JSSpec.Executor(func));
144
+ };
145
+
146
+ JSSpec.CompositeExecutor.prototype.addExecutor = function(executor) {
147
+ var last = this.queue.length == 0 ? null : this.queue[this.queue.length - 1];
148
+ if(last) {
149
+ last.next = executor;
150
+ }
151
+
152
+ executor.parent = this;
153
+ executor.onSuccessBackup = executor.onSuccess;
154
+ executor.onSuccess = function(result) {
155
+ this.onSuccessBackup(result);
156
+ if(this.next) {
157
+ this.next.run();
158
+ } else {
159
+ this.parent.onSuccess();
160
+ }
161
+ };
162
+ executor.onExceptionBackup = executor.onException;
163
+ executor.onException = function(executor, ex) {
164
+ this.onExceptionBackup(executor, ex);
165
+
166
+ if(this.parent.continueOnException) {
167
+ if(this.next) {
168
+ this.next.run();
169
+ } else {
170
+ this.parent.onSuccess();
171
+ }
172
+ } else {
173
+ this.parent.onException(executor, ex);
174
+ }
175
+ };
176
+
177
+ this.queue.push(executor);
178
+ };
179
+
180
+ JSSpec.CompositeExecutor.prototype.run = function() {
181
+ if(this.queue.length > 0) {
182
+ this.queue[0].run();
183
+ }
184
+ };
185
+
186
+ /**
187
+ * Spec is a set of Examples in a specific context
188
+ */
189
+ JSSpec.Spec = function(context, entries) {
190
+ this.id = JSSpec.Spec.id++;
191
+ this.context = context;
192
+ this.url = location.href;
193
+
194
+ this.filterEntriesByEmbeddedExpressions(entries);
195
+ this.extractOutSpecialEntries(entries);
196
+ this.examples = this.makeExamplesFromEntries(entries);
197
+ this.examplesMap = this.makeMapFromExamples(this.examples);
198
+ };
199
+
200
+ JSSpec.Spec.id = 0;
201
+ JSSpec.Spec.prototype.getExamples = function() {
202
+ return this.examples;
203
+ };
204
+
205
+ JSSpec.Spec.prototype.hasException = function() {
206
+ return this.getTotalFailures() > 0 || this.getTotalErrors() > 0;
207
+ };
208
+
209
+ JSSpec.Spec.prototype.getTotalFailures = function() {
210
+ var examples = this.examples;
211
+ var failures = 0;
212
+ for(var i = 0; i < examples.length; i++) {
213
+ if(examples[i].isFailure()) failures++;
214
+ }
215
+ return failures;
216
+ };
217
+
218
+ JSSpec.Spec.prototype.getTotalErrors = function() {
219
+ var examples = this.examples;
220
+ var errors = 0;
221
+ for(var i = 0; i < examples.length; i++) {
222
+ if(examples[i].isError()) errors++;
223
+ }
224
+ return errors;
225
+ };
226
+
227
+ JSSpec.Spec.prototype.filterEntriesByEmbeddedExpressions = function(entries) {
228
+ var isTrue;
229
+ for(name in entries) {
230
+ var m = name.match(/\[\[(.+)\]\]/);
231
+ if(m && m[1]) {
232
+ eval("isTrue = (" + m[1] + ")");
233
+ if(!isTrue) delete entries[name];
234
+ }
235
+ }
236
+ };
237
+
238
+ JSSpec.Spec.prototype.extractOutSpecialEntries = function(entries) {
239
+ this.beforeEach = JSSpec.EMPTY_FUNCTION;
240
+ this.beforeAll = JSSpec.EMPTY_FUNCTION;
241
+ this.afterEach = JSSpec.EMPTY_FUNCTION;
242
+ this.afterAll = JSSpec.EMPTY_FUNCTION;
243
+
244
+ for(name in entries) {
245
+ if(name == 'before' || name == 'before each' || name == 'before_each') {
246
+ this.beforeEach = entries[name];
247
+ } else if(name == 'before all' || name == 'before_all') {
248
+ this.beforeAll = entries[name];
249
+ } else if(name == 'after' || name == 'after each' || name == 'after_each') {
250
+ this.afterEach = entries[name];
251
+ } else if(name == 'after all' || name == 'after_all') {
252
+ this.afterAll = entries[name];
253
+ }
254
+ }
255
+
256
+ delete entries['before'];
257
+ delete entries['before each'];
258
+ delete entries['before_each'];
259
+ delete entries['before all'];
260
+ delete entries['before_all'];
261
+ delete entries['after'];
262
+ delete entries['after each'];
263
+ delete entries['after_each'];
264
+ delete entries['after all'];
265
+ delete entries['after_all'];
266
+ };
267
+
268
+ JSSpec.Spec.prototype.makeExamplesFromEntries = function(entries) {
269
+ var examples = [];
270
+ for(name in entries) {
271
+ examples.push(new JSSpec.Example(name, entries[name], this.beforeEach, this.afterEach));
272
+ }
273
+ return examples;
274
+ };
275
+
276
+ JSSpec.Spec.prototype.makeMapFromExamples = function(examples) {
277
+ var map = {};
278
+ for(var i = 0; i < examples.length; i++) {
279
+ var example = examples[i];
280
+ map[example.id] = examples[i];
281
+ }
282
+ return map;
283
+ };
284
+
285
+ JSSpec.Spec.prototype.getExampleById = function(id) {
286
+ return this.examplesMap[id];
287
+ };
288
+
289
+ JSSpec.Spec.prototype.getExecutor = function() {
290
+ var self = this;
291
+ var onException = function(executor, ex) {
292
+ self.exception = ex;
293
+ };
294
+
295
+ var composite = new JSSpec.CompositeExecutor();
296
+ composite.addFunction(function() {JSSpec.log.onSpecStart(self);});
297
+ composite.addExecutor(new JSSpec.Executor(this.beforeAll, null, function(exec, ex) {
298
+ self.exception = ex;
299
+ JSSpec.log.onSpecEnd(self);
300
+ }));
301
+
302
+ var exampleAndAfter = new JSSpec.CompositeExecutor(null,null,true);
303
+ for(var i = 0; i < this.examples.length; i++) {
304
+ exampleAndAfter.addExecutor(this.examples[i].getExecutor());
305
+ }
306
+ exampleAndAfter.addExecutor(new JSSpec.Executor(this.afterAll, null, onException));
307
+ exampleAndAfter.addFunction(function() {JSSpec.log.onSpecEnd(self);});
308
+ composite.addExecutor(exampleAndAfter);
309
+
310
+ return composite;
311
+ };
312
+
313
+ /**
314
+ * Example
315
+ */
316
+ JSSpec.Example = function(name, target, before, after) {
317
+ this.id = JSSpec.Example.id++;
318
+ this.name = name;
319
+ this.target = target;
320
+ this.before = before;
321
+ this.after = after;
322
+ };
323
+
324
+ JSSpec.Example.id = 0;
325
+ JSSpec.Example.prototype.isFailure = function() {
326
+ return this.exception && this.exception.type == "failure";
327
+ };
328
+
329
+ JSSpec.Example.prototype.isError = function() {
330
+ return this.exception && !this.exception.type;
331
+ };
332
+
333
+ JSSpec.Example.prototype.getExecutor = function() {
334
+ var self = this;
335
+ var onException = function(executor, ex) {
336
+ self.exception = ex;
337
+ };
338
+
339
+ var composite = new JSSpec.CompositeExecutor();
340
+ composite.addFunction(function() {JSSpec.log.onExampleStart(self);});
341
+ composite.addExecutor(new JSSpec.Executor(this.before, null, function(exec, ex) {
342
+ self.exception = ex;
343
+ JSSpec.log.onExampleEnd(self);
344
+ }));
345
+
346
+ var targetAndAfter = new JSSpec.CompositeExecutor(null,null,true);
347
+
348
+ targetAndAfter.addExecutor(new JSSpec.Executor(this.target, null, onException));
349
+ targetAndAfter.addExecutor(new JSSpec.Executor(this.after, null, onException));
350
+ targetAndAfter.addFunction(function() {JSSpec.log.onExampleEnd(self);});
351
+
352
+ composite.addExecutor(targetAndAfter);
353
+
354
+ return composite;
355
+ };
356
+
357
+ /**
358
+ * Runner
359
+ */
360
+ JSSpec.Runner = function(specs, logger) {
361
+ JSSpec.log = logger;
362
+
363
+ this.totalExamples = 0;
364
+ this.specs = [];
365
+ this.specsMap = {};
366
+ this.addAllSpecs(specs);
367
+ };
368
+
369
+ JSSpec.Runner.prototype.addAllSpecs = function(specs) {
370
+ for(var i = 0; i < specs.length; i++) {
371
+ this.addSpec(specs[i]);
372
+ }
373
+ };
374
+
375
+ JSSpec.Runner.prototype.addSpec = function(spec) {
376
+ this.specs.push(spec);
377
+ this.specsMap[spec.id] = spec;
378
+ this.totalExamples += spec.getExamples().length;
379
+ };
380
+
381
+ JSSpec.Runner.prototype.getSpecById = function(id) {
382
+ return this.specsMap[id];
383
+ };
384
+
385
+ JSSpec.Runner.prototype.getSpecByContext = function(context) {
386
+ for(var i = 0; i < this.specs.length; i++) {
387
+ if(this.specs[i].context == context) return this.specs[i];
388
+ }
389
+ return null;
390
+ };
391
+
392
+ JSSpec.Runner.prototype.getSpecs = function() {
393
+ return this.specs;
394
+ };
395
+
396
+ JSSpec.Runner.prototype.hasException = function() {
397
+ return this.getTotalFailures() > 0 || this.getTotalErrors() > 0;
398
+ };
399
+
400
+ JSSpec.Runner.prototype.getTotalFailures = function() {
401
+ var specs = this.specs;
402
+ var failures = 0;
403
+ for(var i = 0; i < specs.length; i++) {
404
+ failures += specs[i].getTotalFailures();
405
+ }
406
+ return failures;
407
+ };
408
+
409
+ JSSpec.Runner.prototype.getTotalErrors = function() {
410
+ var specs = this.specs;
411
+ var errors = 0;
412
+ for(var i = 0; i < specs.length; i++) {
413
+ errors += specs[i].getTotalErrors();
414
+ }
415
+ return errors;
416
+ };
417
+
418
+
419
+ JSSpec.Runner.prototype.run = function() {
420
+ JSSpec.log.onRunnerStart();
421
+ var executor = new JSSpec.CompositeExecutor(function() {JSSpec.log.onRunnerEnd()},null,true);
422
+ for(var i = 0; i < this.specs.length; i++) {
423
+ executor.addExecutor(this.specs[i].getExecutor());
424
+ }
425
+ executor.run();
426
+ };
427
+
428
+
429
+ JSSpec.Runner.prototype.rerun = function(context) {
430
+ JSSpec.runner = new JSSpec.Runner([this.getSpecByContext(context)], JSSpec.log);
431
+ JSSpec.runner.run();
432
+ };
433
+
434
+ /**
435
+ * Logger
436
+ */
437
+ JSSpec.Logger = function() {
438
+ this.finishedExamples = 0;
439
+ this.startedAt = null;
440
+ };
441
+
442
+ JSSpec.Logger.prototype.onRunnerStart = function() {
443
+ this._title = document.title;
444
+
445
+ this.startedAt = new Date();
446
+ var container = document.getElementById('jsspec_container');
447
+ if(container) {
448
+ container.innerHTML = "";
449
+ } else {
450
+ container = document.createElement("DIV");
451
+ container.id = "jsspec_container";
452
+ document.body.appendChild(container);
453
+ }
454
+
455
+ var title = document.createElement("DIV");
456
+ title.id = "title";
457
+ title.innerHTML = [
458
+ '<h1>JSSpec</h1>',
459
+ '<ul>',
460
+ JSSpec.options.rerun ? '<li>[<a href="?" title="rerun all specs">X</a>] ' + JSSpec.util.escapeTags(decodeURIComponent(JSSpec.options.rerun)) + '</li>' : '',
461
+ ' <li><span id="total_examples">' + JSSpec.runner.totalExamples + '</span> examples</li>',
462
+ ' <li><span id="total_failures">0</span> failures</li>',
463
+ ' <li><span id="total_errors">0</span> errors</li>',
464
+ ' <li><span id="progress">0</span>% done</li>',
465
+ ' <li><span id="total_elapsed">0</span> secs</li>',
466
+ '</ul>',
467
+ '<p><a href="http://jania.pe.kr/aw/moin.cgi/JSSpec">JSSpec homepage</a></p>',
468
+ ].join("");
469
+ container.appendChild(title);
470
+
471
+ var list = document.createElement("DIV");
472
+ list.id = "list";
473
+ list.innerHTML = [
474
+ '<h2>List</h2>',
475
+ '<ul class="specs">',
476
+ function() {
477
+ var specs = JSSpec.runner.getSpecs();
478
+ var sb = [];
479
+ for(var i = 0; i < specs.length; i++) {
480
+ var spec = specs[i];
481
+ sb.push('<li id="spec_' + specs[i].id + '_list"><h3><a href="#spec_' + specs[i].id + '">' + JSSpec.util.escapeTags(specs[i].context) + '</a> [<a href="?rerun=' + encodeURIComponent(specs[i].context) + '">rerun</a>]</h3></li>');
482
+ }
483
+ return sb.join("");
484
+ }(),
485
+ '</ul>'
486
+ ].join("");
487
+ container.appendChild(list);
488
+
489
+ var log = document.createElement("DIV");
490
+ log.id = "log";
491
+ log.innerHTML = [
492
+ '<h2>Log</h2>',
493
+ '<ul class="specs">',
494
+ function() {
495
+ var specs = JSSpec.runner.getSpecs();
496
+ var sb = [];
497
+ for(var i = 0; i < specs.length; i++) {
498
+ var spec = specs[i];
499
+ sb.push(' <li id="spec_' + specs[i].id + '">');
500
+ sb.push(' <h3>' + JSSpec.util.escapeTags(specs[i].context) + ' [<a href="?rerun=' + encodeURIComponent(specs[i].context) + '">rerun</a>]</h3>');
501
+ sb.push(' <ul id="spec_' + specs[i].id + '_examples" class="examples">');
502
+ for(var j = 0; j < spec.examples.length; j++) {
503
+ var example = spec.examples[j];
504
+ sb.push(' <li id="example_' + example.id + '">')
505
+ sb.push(' <h4>' + JSSpec.util.escapeTags(example.name) + '</h4>')
506
+ sb.push(' </li>')
507
+ }
508
+ sb.push(' </ul>');
509
+ sb.push(' </li>');
510
+ }
511
+ return sb.join("");
512
+ }(),
513
+ '</ul>'
514
+ ].join("");
515
+
516
+ container.appendChild(log);
517
+
518
+ // add event handler for toggling
519
+ var specs = JSSpec.runner.getSpecs();
520
+ var sb = [];
521
+ for(var i = 0; i < specs.length; i++) {
522
+ var spec = document.getElementById("spec_" + specs[i].id);
523
+ var title = spec.getElementsByTagName("H3")[0];
524
+ title.onclick = function(e) {
525
+ var target = document.getElementById(this.parentNode.id + "_examples");
526
+ target.style.display = target.style.display == "none" ? "block" : "none";
527
+ return true;
528
+ }
529
+ }
530
+ };
531
+
532
+ JSSpec.Logger.prototype.onRunnerEnd = function() {
533
+ if(JSSpec.runner.hasException()) {
534
+ var times = 4;
535
+ var title1 = "*" + this._title;
536
+ var title2 = "*F" + JSSpec.runner.getTotalFailures() + " E" + JSSpec.runner.getTotalErrors() + "* " + this._title;
537
+ } else {
538
+ var times = 2;
539
+ var title1 = this._title;
540
+ var title2 = "Success";
541
+ }
542
+ this.blinkTitle(times,title1,title2);
543
+ };
544
+
545
+ JSSpec.Logger.prototype.blinkTitle = function(times, title1, title2) {
546
+ var times = times * 2;
547
+ var mode = true;
548
+
549
+ var f = function() {
550
+ if(times > 0) {
551
+ document.title = mode ? title1 : title2;
552
+ mode = !mode;
553
+ times--;
554
+ window.setTimeout(f, 500);
555
+ } else {
556
+ document.title = title1;
557
+ }
558
+ };
559
+
560
+ f();
561
+ };
562
+
563
+ JSSpec.Logger.prototype.onSpecStart = function(spec) {
564
+ var spec_list = document.getElementById("spec_" + spec.id + "_list");
565
+ var spec_log = document.getElementById("spec_" + spec.id);
566
+
567
+ spec_list.className = "ongoing";
568
+ spec_log.className = "ongoing";
569
+ };
570
+
571
+ JSSpec.Logger.prototype.onSpecEnd = function(spec) {
572
+ var spec_list = document.getElementById("spec_" + spec.id + "_list");
573
+ var spec_log = document.getElementById("spec_" + spec.id);
574
+ var examples = document.getElementById("spec_" + spec.id + "_examples");
575
+ var className = spec.hasException() ? "exception" : "success";
576
+
577
+ spec_list.className = className;
578
+ spec_log.className = className;
579
+
580
+ if(JSSpec.options.autocollapse && !spec.hasException()) examples.style.display = "none";
581
+
582
+ if(spec.exception) {
583
+ heading.appendChild(document.createTextNode(" - " + spec.exception.message));
584
+ }
585
+ };
586
+
587
+ JSSpec.Logger.prototype.onExampleStart = function(example) {
588
+ var li = document.getElementById("example_" + example.id);
589
+ li.className = "ongoing";
590
+ };
591
+
592
+ JSSpec.Logger.prototype.onExampleEnd = function(example) {
593
+ var li = document.getElementById("example_" + example.id);
594
+ li.className = example.exception ? "exception" : "success";
595
+
596
+ if(example.exception) {
597
+ var div = document.createElement("DIV");
598
+ div.innerHTML = example.exception.message + "<p><br />" + " at " + example.exception.fileName + ", line " + example.exception.lineNumber + "</p>";
599
+ li.appendChild(div);
600
+ }
601
+
602
+ var title = document.getElementById("title");
603
+ var runner = JSSpec.runner;
604
+
605
+ title.className = runner.hasException() ? "exception" : "success";
606
+
607
+ this.finishedExamples++;
608
+ document.getElementById("total_failures").innerHTML = runner.getTotalFailures();
609
+ document.getElementById("total_errors").innerHTML = runner.getTotalErrors();
610
+ var progress = parseInt(this.finishedExamples / runner.totalExamples * 100);
611
+ document.getElementById("progress").innerHTML = progress;
612
+ document.getElementById("total_elapsed").innerHTML = (new Date().getTime() - this.startedAt.getTime()) / 1000;
613
+
614
+ document.title = progress + "%: " + this._title;
615
+ };
616
+
617
+ /**
618
+ * IncludeMatcher
619
+ */
620
+ JSSpec.IncludeMatcher = function(actual, expected, condition) {
621
+ this.actual = actual;
622
+ this.expected = expected;
623
+ this.condition = condition;
624
+ this.match = false;
625
+ this.explaination = this.makeExplain();
626
+ };
627
+
628
+ JSSpec.IncludeMatcher.createInstance = function(actual, expected, condition) {
629
+ return new JSSpec.IncludeMatcher(actual, expected, condition);
630
+ };
631
+
632
+ JSSpec.IncludeMatcher.prototype.matches = function() {
633
+ return this.match;
634
+ };
635
+
636
+ JSSpec.IncludeMatcher.prototype.explain = function() {
637
+ return this.explaination;
638
+ };
639
+
640
+ JSSpec.IncludeMatcher.prototype.makeExplain = function() {
641
+ if(typeof this.actual.length == 'undefined') {
642
+ return this.makeExplainForNotArray();
643
+ } else {
644
+ return this.makeExplainForArray();
645
+ }
646
+ };
647
+
648
+ JSSpec.IncludeMatcher.prototype.makeExplainForNotArray = function() {
649
+ if(this.condition) {
650
+ this.match = !!this.actual[this.expected];
651
+ } else {
652
+ this.match = !this.actual[this.expected];
653
+ }
654
+
655
+ var sb = [];
656
+ sb.push('<p>actual value:</p>');
657
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual, false, this.expected) + '</p>');
658
+ sb.push('<p>should ' + (this.condition ? '' : 'not') + ' include:</p>');
659
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected) + '</p>');
660
+ return sb.join("");
661
+ }
662
+ ;
663
+ JSSpec.IncludeMatcher.prototype.makeExplainForArray = function() {
664
+ var matches;
665
+ if(this.condition) {
666
+ for(var i = 0; i < this.actual.length; i++) {
667
+ matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches();
668
+ if(matches) {
669
+ this.match = true;
670
+ break;
671
+ }
672
+ }
673
+ } else {
674
+ for(var i = 0; i < this.actual.length; i++) {
675
+ matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches();
676
+ if(matches) {
677
+ this.match = false;
678
+ break;
679
+ }
680
+ }
681
+ }
682
+
683
+ if(this.match) return "";
684
+
685
+ var sb = [];
686
+ sb.push('<p>actual value:</p>');
687
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual, false, this.condition ? null : i) + '</p>');
688
+ sb.push('<p>should ' + (this.condition ? '' : 'not') + ' include:</p>');
689
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected) + '</p>');
690
+ return sb.join("");
691
+ };
692
+
693
+ /**
694
+ * PropertyLengthMatcher
695
+ */
696
+ JSSpec.PropertyLengthMatcher = function(num, property, o, condition) {
697
+ this.num = num;
698
+ this.o = o;
699
+ this.property = property;
700
+ if((property == 'characters' || property == 'items') && typeof o.length != 'undefined') {
701
+ this.property = 'length';
702
+ }
703
+
704
+ this.condition = condition;
705
+ this.conditionMet = function(x) {
706
+ if(condition == 'exactly') return x.length == num;
707
+ if(condition == 'at least') return x.length >= num;
708
+ if(condition == 'at most') return x.length <= num;
709
+
710
+ throw "Unknown condition '" + condition + "'";
711
+ };
712
+ this.match = false;
713
+ this.explaination = this.makeExplain();
714
+ };
715
+
716
+ JSSpec.PropertyLengthMatcher.prototype.makeExplain = function() {
717
+ if(this.o._type == 'String' && this.property == 'length') {
718
+ this.match = this.conditionMet(this.o);
719
+ return this.match ? '' : this.makeExplainForString();
720
+ } else if(typeof this.o.length != 'undefined' && this.property == "length") {
721
+ this.match = this.conditionMet(this.o);
722
+ return this.match ? '' : this.makeExplainForArray();
723
+ } else if(typeof this.o[this.property] != 'undefined' && this.o[this.property] != null) {
724
+ this.match = this.conditionMet(this.o[this.property]);
725
+ return this.match ? '' : this.makeExplainForObject();
726
+ } else if(typeof this.o[this.property] == 'undefined' || this.o[this.property] == null) {
727
+ this.match = false;
728
+ return this.makeExplainForNoProperty();
729
+ }
730
+
731
+ this.match = true;
732
+ };
733
+
734
+ JSSpec.PropertyLengthMatcher.prototype.makeExplainForString = function() {
735
+ var sb = [];
736
+
737
+ var exp = this.num == 0 ?
738
+ 'be an <strong>empty string</strong>' :
739
+ 'have <strong>' + this.condition + ' ' + this.num + ' characters</strong>';
740
+
741
+ sb.push('<p>actual value has <strong>' + this.o.length + ' characters</strong>:</p>');
742
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.o) + '</p>');
743
+ sb.push('<p>but it should ' + exp + '.</p>');
744
+
745
+ return sb.join("");
746
+ };
747
+
748
+ JSSpec.PropertyLengthMatcher.prototype.makeExplainForArray = function() {
749
+ var sb = [];
750
+
751
+ var exp = this.num == 0 ?
752
+ 'be an <strong>empty array</strong>' :
753
+ 'have <strong>' + this.condition + ' ' + this.num + ' items</strong>';
754
+
755
+ sb.push('<p>actual value has <strong>' + this.o.length + ' items</strong>:</p>');
756
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.o) + '</p>');
757
+ sb.push('<p>but it should ' + exp + '.</p>');
758
+
759
+ return sb.join("");
760
+ };
761
+
762
+ JSSpec.PropertyLengthMatcher.prototype.makeExplainForObject = function() {
763
+ var sb = [];
764
+
765
+ var exp = this.num == 0 ?
766
+ 'be <strong>empty</strong>' :
767
+ 'have <strong>' + this.condition + ' ' + this.num + ' ' + this.property + '.</strong>';
768
+
769
+ sb.push('<p>actual value has <strong>' + this.o[this.property].length + ' ' + this.property + '</strong>:</p>');
770
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.o, false, this.property) + '</p>');
771
+ sb.push('<p>but it should ' + exp + '.</p>');
772
+
773
+ return sb.join("");
774
+ };
775
+
776
+ JSSpec.PropertyLengthMatcher.prototype.makeExplainForNoProperty = function() {
777
+ var sb = [];
778
+
779
+ sb.push('<p>actual value:</p>');
780
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.o) + '</p>');
781
+ sb.push('<p>should have <strong>' + this.condition + ' ' + this.num + ' ' + this.property + '</strong> but there\'s no such property.</p>');
782
+
783
+ return sb.join("");
784
+ };
785
+
786
+ JSSpec.PropertyLengthMatcher.prototype.matches = function() {
787
+ return this.match;
788
+ };
789
+
790
+ JSSpec.PropertyLengthMatcher.prototype.explain = function() {
791
+ return this.explaination;
792
+ };
793
+
794
+ JSSpec.PropertyLengthMatcher.createInstance = function(num, property, o, condition) {
795
+ return new JSSpec.PropertyLengthMatcher(num, property, o, condition);
796
+ };
797
+
798
+ /**
799
+ * EqualityMatcher
800
+ */
801
+ JSSpec.EqualityMatcher = {};
802
+
803
+ JSSpec.EqualityMatcher.createInstance = function(expected, actual) {
804
+ if(expected == null || actual == null) {
805
+ return new JSSpec.NullEqualityMatcher(expected, actual);
806
+ } else if(expected._type && expected._type == actual._type) {
807
+ if(expected._type == "String") {
808
+ return new JSSpec.StringEqualityMatcher(expected, actual);
809
+ } else if(expected._type == "Date") {
810
+ return new JSSpec.DateEqualityMatcher(expected, actual);
811
+ } else if(expected._type == "Number") {
812
+ return new JSSpec.NumberEqualityMatcher(expected, actual);
813
+ } else if(expected._type == "Array") {
814
+ return new JSSpec.ArrayEqualityMatcher(expected, actual);
815
+ } else if(expected._type == "Boolean") {
816
+ return new JSSpec.BooleanEqualityMatcher(expected, actual);
817
+ }
818
+ }
819
+
820
+ return new JSSpec.ObjectEqualityMatcher(expected, actual);
821
+ };
822
+
823
+ JSSpec.EqualityMatcher.basicExplain = function(expected, actual, expectedDesc, actualDesc) {
824
+ var sb = [];
825
+
826
+ sb.push(actualDesc || '<p>actual value:</p>');
827
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(actual) + '</p>');
828
+ sb.push(expectedDesc || '<p>should be:</p>');
829
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(expected) + '</p>');
830
+
831
+ return sb.join("");
832
+ };
833
+
834
+ JSSpec.EqualityMatcher.diffExplain = function(expected, actual) {
835
+ var sb = [];
836
+
837
+ sb.push('<p>diff:</p>');
838
+ sb.push('<p style="margin-left:2em;">');
839
+
840
+ var dmp = new diff_match_patch();
841
+ var diff = dmp.diff_main(expected, actual);
842
+ dmp.diff_cleanupEfficiency(diff);
843
+
844
+ sb.push(JSSpec.util.inspect(dmp.diff_prettyHtml(diff), true));
845
+
846
+ sb.push('</p>');
847
+
848
+ return sb.join("");
849
+ };
850
+
851
+ /**
852
+ * BooleanEqualityMatcher
853
+ */
854
+ JSSpec.BooleanEqualityMatcher = function(expected, actual) {
855
+ this.expected = expected;
856
+ this.actual = actual;
857
+ };
858
+
859
+ JSSpec.BooleanEqualityMatcher.prototype.explain = function() {
860
+ var sb = [];
861
+
862
+ sb.push('<p>actual value:</p>');
863
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual) + '</p>');
864
+ sb.push('<p>should be:</p>');
865
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected) + '</p>');
866
+
867
+ return sb.join("");
868
+ };
869
+
870
+ JSSpec.BooleanEqualityMatcher.prototype.matches = function() {
871
+ return this.expected == this.actual;
872
+ };
873
+
874
+ /**
875
+ * NullEqualityMatcher
876
+ */
877
+ JSSpec.NullEqualityMatcher = function(expected, actual) {
878
+ this.expected = expected;
879
+ this.actual = actual;
880
+ };
881
+
882
+ JSSpec.NullEqualityMatcher.prototype.matches = function() {
883
+ return this.expected == this.actual && typeof this.expected == typeof this.actual;
884
+ };
885
+
886
+ JSSpec.NullEqualityMatcher.prototype.explain = function() {
887
+ return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual);
888
+ };
889
+
890
+ JSSpec.DateEqualityMatcher = function(expected, actual) {
891
+ this.expected = expected;
892
+ this.actual = actual;
893
+ };
894
+
895
+ JSSpec.DateEqualityMatcher.prototype.matches = function() {
896
+ return this.expected.getTime() == this.actual.getTime();
897
+ };
898
+
899
+ JSSpec.DateEqualityMatcher.prototype.explain = function() {
900
+ var sb = [];
901
+
902
+ sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual));
903
+ sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected.toString(), this.actual.toString()));
904
+
905
+ return sb.join("");
906
+ };
907
+
908
+ /**
909
+ * ObjectEqualityMatcher
910
+ */
911
+ JSSpec.ObjectEqualityMatcher = function(expected, actual) {
912
+ this.expected = expected;
913
+ this.actual = actual;
914
+ this.match = this.expected == this.actual;
915
+ this.explaination = this.makeExplain();
916
+ };
917
+
918
+ JSSpec.ObjectEqualityMatcher.prototype.matches = function() {return this.match};
919
+
920
+ JSSpec.ObjectEqualityMatcher.prototype.explain = function() {return this.explaination};
921
+
922
+ JSSpec.ObjectEqualityMatcher.prototype.makeExplain = function() {
923
+ if(this.expected == this.actual) {
924
+ this.match = true;
925
+ return "";
926
+ }
927
+
928
+ if(JSSpec.util.isDomNode(this.expected)) {
929
+ return this.makeExplainForDomNode();
930
+ }
931
+
932
+ var key, expectedHasItem, actualHasItem;
933
+
934
+ for(key in this.expected) {
935
+ expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined';
936
+ actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined';
937
+ if(expectedHasItem && !actualHasItem) return this.makeExplainForMissingItem(key);
938
+ }
939
+ for(key in this.actual) {
940
+ expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined';
941
+ actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined';
942
+ if(actualHasItem && !expectedHasItem) return this.makeExplainForUnknownItem(key);
943
+ }
944
+
945
+ for(key in this.expected) {
946
+ var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[key], this.actual[key]);
947
+ if(!matcher.matches()) return this.makeExplainForItemMismatch(key);
948
+ }
949
+
950
+ this.match = true;
951
+ };
952
+
953
+ JSSpec.ObjectEqualityMatcher.prototype.makeExplainForDomNode = function(key) {
954
+ var sb = [];
955
+
956
+ sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual));
957
+
958
+ return sb.join("");
959
+ };
960
+
961
+ JSSpec.ObjectEqualityMatcher.prototype.makeExplainForMissingItem = function(key) {
962
+ var sb = [];
963
+
964
+ sb.push('<p>actual value has no item named <strong>' + JSSpec.util.inspect(key) + '</strong></p>');
965
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual, false, key) + '</p>');
966
+ sb.push('<p>but it should have the item whose value is <strong>' + JSSpec.util.inspect(this.expected[key]) + '</strong></p>');
967
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected, false, key) + '</p>');
968
+
969
+ return sb.join("");
970
+ };
971
+
972
+ JSSpec.ObjectEqualityMatcher.prototype.makeExplainForUnknownItem = function(key) {
973
+ var sb = [];
974
+
975
+ sb.push('<p>actual value has item named <strong>' + JSSpec.util.inspect(key) + '</strong></p>');
976
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual, false, key) + '</p>');
977
+ sb.push('<p>but there should be no such item</p>');
978
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected, false, key) + '</p>');
979
+
980
+ return sb.join("");
981
+ };
982
+
983
+ JSSpec.ObjectEqualityMatcher.prototype.makeExplainForItemMismatch = function(key) {
984
+ var sb = [];
985
+
986
+ sb.push('<p>actual value has an item named <strong>' + JSSpec.util.inspect(key) + '</strong> whose value is <strong>' + JSSpec.util.inspect(this.actual[key]) + '</strong></p>');
987
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual, false, key) + '</p>');
988
+ sb.push('<p>but it\'s value should be <strong>' + JSSpec.util.inspect(this.expected[key]) + '</strong></p>');
989
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected, false, key) + '</p>');
990
+
991
+ return sb.join("");
992
+ };
993
+
994
+
995
+
996
+
997
+ /**
998
+ * ArrayEqualityMatcher
999
+ */
1000
+ JSSpec.ArrayEqualityMatcher = function(expected, actual) {
1001
+ this.expected = expected;
1002
+ this.actual = actual;
1003
+ this.match = this.expected == this.actual;
1004
+ this.explaination = this.makeExplain();
1005
+ };
1006
+
1007
+ JSSpec.ArrayEqualityMatcher.prototype.matches = function() {return this.match};
1008
+
1009
+ JSSpec.ArrayEqualityMatcher.prototype.explain = function() {return this.explaination};
1010
+
1011
+ JSSpec.ArrayEqualityMatcher.prototype.makeExplain = function() {
1012
+ if(this.expected.length != this.actual.length) return this.makeExplainForLengthMismatch();
1013
+
1014
+ for(var i = 0; i < this.expected.length; i++) {
1015
+ var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[i], this.actual[i]);
1016
+ if(!matcher.matches()) return this.makeExplainForItemMismatch(i);
1017
+ }
1018
+
1019
+ this.match = true;
1020
+ };
1021
+
1022
+ JSSpec.ArrayEqualityMatcher.prototype.makeExplainForLengthMismatch = function() {
1023
+ return JSSpec.EqualityMatcher.basicExplain(
1024
+ this.expected,
1025
+ this.actual,
1026
+ '<p>but it should be <strong>' + this.expected.length + '</strong></p>',
1027
+ '<p>actual value has <strong>' + this.actual.length + '</strong> items</p>'
1028
+ );
1029
+ };
1030
+
1031
+ JSSpec.ArrayEqualityMatcher.prototype.makeExplainForItemMismatch = function(index) {
1032
+ var postfix = ["th", "st", "nd", "rd", "th"][Math.min((index + 1) % 10,4)];
1033
+
1034
+ var sb = [];
1035
+
1036
+ sb.push('<p>' + (index + 1) + postfix + ' item (index ' + index + ') of actual value is <strong>' + JSSpec.util.inspect(this.actual[index]) + '</strong>:</p>');
1037
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual, false, index) + '</p>');
1038
+ sb.push('<p>but it should be <strong>' + JSSpec.util.inspect(this.expected[index]) + '</strong>:</p>');
1039
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.expected, false, index) + '</p>');
1040
+
1041
+ return sb.join("");
1042
+ };
1043
+
1044
+ /**
1045
+ * NumberEqualityMatcher
1046
+ */
1047
+ JSSpec.NumberEqualityMatcher = function(expected, actual) {
1048
+ this.expected = expected;
1049
+ this.actual = actual;
1050
+ };
1051
+
1052
+ JSSpec.NumberEqualityMatcher.prototype.matches = function() {
1053
+ if(this.expected == this.actual) return true;
1054
+ };
1055
+
1056
+ JSSpec.NumberEqualityMatcher.prototype.explain = function() {
1057
+ return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual);
1058
+ };
1059
+
1060
+ /**
1061
+ * StringEqualityMatcher
1062
+ */
1063
+ JSSpec.StringEqualityMatcher = function(expected, actual) {
1064
+ this.expected = expected;
1065
+ this.actual = actual;
1066
+ };
1067
+
1068
+ JSSpec.StringEqualityMatcher.prototype.matches = function() {
1069
+ if(this.expected == this.actual) return true;
1070
+ };
1071
+
1072
+ JSSpec.StringEqualityMatcher.prototype.explain = function() {
1073
+ var sb = [];
1074
+
1075
+ sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual));
1076
+ sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected, this.actual));
1077
+ return sb.join("");
1078
+ };
1079
+
1080
+ /**
1081
+ * PatternMatcher
1082
+ */
1083
+ JSSpec.PatternMatcher = function(actual, pattern, condition) {
1084
+ this.actual = actual;
1085
+ this.pattern = pattern;
1086
+ this.condition = condition;
1087
+ this.match = false;
1088
+ this.explaination = this.makeExplain();
1089
+ };
1090
+
1091
+ JSSpec.PatternMatcher.createInstance = function(actual, pattern, condition) {
1092
+ return new JSSpec.PatternMatcher(actual, pattern, condition);
1093
+ };
1094
+
1095
+ JSSpec.PatternMatcher.prototype.makeExplain = function() {
1096
+ var sb;
1097
+ if(this.actual == null || this.actual._type != 'String') {
1098
+ sb = [];
1099
+ sb.push('<p>actual value:</p>');
1100
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual) + '</p>');
1101
+ sb.push('<p>should ' + (this.condition ? '' : 'not') + ' match with pattern:</p>');
1102
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.pattern) + '</p>');
1103
+ sb.push('<p>but pattern matching cannot be performed.</p>');
1104
+ return sb.join("");
1105
+ } else {
1106
+ this.match = this.condition == !!this.actual.match(this.pattern);
1107
+ if(this.match) return "";
1108
+
1109
+ sb = [];
1110
+ sb.push('<p>actual value:</p>');
1111
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.actual) + '</p>');
1112
+ sb.push('<p>should ' + (this.condition ? '' : 'not') + ' match with pattern:</p>');
1113
+ sb.push('<p style="margin-left:2em;">' + JSSpec.util.inspect(this.pattern) + '</p>');
1114
+ return sb.join("");
1115
+ }
1116
+ };
1117
+
1118
+ JSSpec.PatternMatcher.prototype.matches = function() {
1119
+ return this.match;
1120
+ };
1121
+
1122
+ JSSpec.PatternMatcher.prototype.explain = function() {
1123
+ return this.explaination;
1124
+ };
1125
+
1126
+ /**
1127
+ * Domain Specific Languages
1128
+ */
1129
+ JSSpec.DSL = {};
1130
+
1131
+ JSSpec.DSL.forString = {
1132
+ normalizeHtml: function() {
1133
+ var html = this;
1134
+
1135
+ // Uniformize quotation, turn tag names and attribute names into lower case
1136
+ html = html.replace(/<(\/?)(\w+)([^>]*?)>/img, function(str, closingMark, tagName, attrs) {
1137
+ var sortedAttrs = JSSpec.util.sortHtmlAttrs(JSSpec.util.correctHtmlAttrQuotation(attrs).toLowerCase())
1138
+ return "<" + closingMark + tagName.toLowerCase() + sortedAttrs + ">"
1139
+ });
1140
+
1141
+ // validation self-closing tags
1142
+ html = html.replace(/<(br|hr|img)([^>]*?)>/mg, function(str, tag, attrs) {
1143
+ return "<" + tag + attrs + " />";
1144
+ });
1145
+
1146
+ // append semi-colon at the end of style value
1147
+ html = html.replace(/style="(.*?)"/mg, function(str, styleStr) {
1148
+ styleStr = JSSpec.util.sortStyleEntries(styleStr.strip()); // for Safari
1149
+ if(styleStr.charAt(styleStr.length - 1) != ';') styleStr += ";"
1150
+
1151
+ return 'style="' + styleStr + '"'
1152
+ });
1153
+
1154
+ // sort style entries
1155
+
1156
+ // remove empty style attributes
1157
+ html = html.replace(/ style=";"/mg, "");
1158
+
1159
+ // remove new-lines
1160
+ html = html.replace(/\r/mg, '');
1161
+ html = html.replace(/\n/mg, '');
1162
+
1163
+ return html;
1164
+ }
1165
+ };
1166
+
1167
+
1168
+
1169
+ JSSpec.DSL.describe = function(context, entries) {
1170
+ JSSpec.specs.push(new JSSpec.Spec(context, entries));
1171
+ };
1172
+
1173
+ JSSpec.DSL.value_of = function(target) {
1174
+ if(JSSpec._secondPass) return {};
1175
+
1176
+ var subject = new JSSpec.DSL.Subject(target);
1177
+ return subject;
1178
+ };
1179
+
1180
+ JSSpec.DSL.Subject = function(target) {
1181
+ this.target = target;
1182
+ };
1183
+
1184
+ JSSpec.DSL.Subject.prototype._type = 'Subject';
1185
+
1186
+ JSSpec.DSL.Subject.prototype.should_fail = function(message) {
1187
+ JSSpec._assertionFailure = {message:message};
1188
+ throw JSSpec._assertionFailure;
1189
+ };
1190
+
1191
+ JSSpec.DSL.Subject.prototype.should_be = function(expected) {
1192
+ var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target);
1193
+ if(!matcher.matches()) {
1194
+ JSSpec._assertionFailure = {message:matcher.explain()};
1195
+ throw JSSpec._assertionFailure;
1196
+ }
1197
+ };
1198
+
1199
+ JSSpec.DSL.Subject.prototype.should_not_be = function(expected) {
1200
+ // TODO JSSpec.EqualityMatcher should support 'condition'
1201
+ var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target);
1202
+ if(matcher.matches()) {
1203
+ JSSpec._assertionFailure = {message:"'" + this.target + "' should not be '" + expected + "'"};
1204
+ throw JSSpec._assertionFailure;
1205
+ }
1206
+ };
1207
+
1208
+ JSSpec.DSL.Subject.prototype.should_be_empty = function() {
1209
+ this.should_have(0, this.getType() == 'String' ? 'characters' : 'items');
1210
+ };
1211
+
1212
+ JSSpec.DSL.Subject.prototype.should_not_be_empty = function() {
1213
+ this.should_have_at_least(1, this.getType() == 'String' ? 'characters' : 'items');
1214
+ };
1215
+
1216
+ JSSpec.DSL.Subject.prototype.should_be_true = function() {
1217
+ this.should_be(true);
1218
+ };
1219
+
1220
+ JSSpec.DSL.Subject.prototype.should_be_false = function() {
1221
+ this.should_be(false);
1222
+ };
1223
+
1224
+ JSSpec.DSL.Subject.prototype.should_be_null = function() {
1225
+ this.should_be(null);
1226
+ };
1227
+
1228
+ JSSpec.DSL.Subject.prototype.should_be_undefined = function() {
1229
+ this.should_be(undefined);
1230
+ };
1231
+
1232
+ JSSpec.DSL.Subject.prototype.should_not_be_null = function() {
1233
+ this.should_not_be(null);
1234
+ };
1235
+
1236
+ JSSpec.DSL.Subject.prototype.should_not_be_undefined = function() {
1237
+ this.should_not_be(undefined);
1238
+ };
1239
+
1240
+ JSSpec.DSL.Subject.prototype._should_have = function(num, property, condition) {
1241
+ var matcher = JSSpec.PropertyLengthMatcher.createInstance(num, property, this.target, condition);
1242
+ if(!matcher.matches()) {
1243
+ JSSpec._assertionFailure = {message:matcher.explain()};
1244
+ throw JSSpec._assertionFailure;
1245
+ }
1246
+ };
1247
+
1248
+ JSSpec.DSL.Subject.prototype.should_have = function(num, property) {
1249
+ this._should_have(num, property, "exactly");
1250
+ };
1251
+
1252
+ JSSpec.DSL.Subject.prototype.should_have_exactly = function(num, property) {
1253
+ this._should_have(num, property, "exactly");
1254
+ };
1255
+
1256
+ JSSpec.DSL.Subject.prototype.should_have_at_least = function(num, property) {
1257
+ this._should_have(num, property, "at least");
1258
+ };
1259
+
1260
+ JSSpec.DSL.Subject.prototype.should_have_at_most = function(num, property) {
1261
+ this._should_have(num, property, "at most");
1262
+ };
1263
+
1264
+ JSSpec.DSL.Subject.prototype.should_include = function(expected) {
1265
+ var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, true);
1266
+ if(!matcher.matches()) {
1267
+ JSSpec._assertionFailure = {message:matcher.explain()};
1268
+ throw JSSpec._assertionFailure;
1269
+ }
1270
+ };
1271
+
1272
+ JSSpec.DSL.Subject.prototype.should_not_include = function(expected) {
1273
+ var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, false);
1274
+ if(!matcher.matches()) {
1275
+ JSSpec._assertionFailure = {message:matcher.explain()};
1276
+ throw JSSpec._assertionFailure;
1277
+ }
1278
+ };
1279
+
1280
+ JSSpec.DSL.Subject.prototype.should_match = function(pattern) {
1281
+ var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, true);
1282
+ if(!matcher.matches()) {
1283
+ JSSpec._assertionFailure = {message:matcher.explain()};
1284
+ throw JSSpec._assertionFailure;
1285
+ }
1286
+ }
1287
+ JSSpec.DSL.Subject.prototype.should_not_match = function(pattern) {
1288
+ var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, false);
1289
+ if(!matcher.matches()) {
1290
+ JSSpec._assertionFailure = {message:matcher.explain()};
1291
+ throw JSSpec._assertionFailure;
1292
+ }
1293
+ };
1294
+
1295
+ JSSpec.DSL.Subject.prototype.getType = function() {
1296
+ if(typeof this.target == 'undefined') {
1297
+ return 'undefined';
1298
+ } else if(this.target == null) {
1299
+ return 'null';
1300
+ } else if(this.target._type) {
1301
+ return this.target._type;
1302
+ } else if(JSSpec.util.isDomNode(this.target)) {
1303
+ return 'DomNode';
1304
+ } else {
1305
+ return 'object';
1306
+ }
1307
+ };
1308
+
1309
+ /**
1310
+ * Utilities
1311
+ */
1312
+ JSSpec.util = {
1313
+ escapeTags: function(string) {
1314
+ return string.replace(/</img, '&lt;').replace(/>/img, '&gt;');
1315
+ },
1316
+ parseOptions: function(defaults) {
1317
+ var options = defaults;
1318
+
1319
+ var url = location.href;
1320
+ var queryIndex = url.indexOf('?');
1321
+ if(queryIndex == -1) return options;
1322
+
1323
+ var query = url.substring(queryIndex + 1);
1324
+ var pairs = query.split('&');
1325
+ for(var i = 0; i < pairs.length; i++) {
1326
+ var tokens = pairs[i].split('=');
1327
+ options[tokens[0]] = tokens[1];
1328
+ }
1329
+
1330
+ return options;
1331
+ },
1332
+ correctHtmlAttrQuotation: function(html) {
1333
+ html = html.replace(/(\w+)=['"]([^'"]+)['"]/mg,function (str, name, value) {return name + '=' + '"' + value + '"';});
1334
+ html = html.replace(/(\w+)=([^ '"]+)/mg,function (str, name, value) {return name + '=' + '"' + value + '"';});
1335
+ html = html.replace(/'/mg, '"');
1336
+
1337
+ return html;
1338
+ },
1339
+ sortHtmlAttrs: function(html) {
1340
+ var attrs = [];
1341
+ html.replace(/((\w+)="[^"]+")/mg, function(str, matched) {
1342
+ attrs.push(matched);
1343
+ });
1344
+ return attrs.length == 0 ? "" : " " + attrs.sort().join(" ");
1345
+ },
1346
+ sortStyleEntries: function(styleText) {
1347
+ var entries = styleText.split(/; /);
1348
+ return entries.sort().join("; ");
1349
+ },
1350
+ escapeHtml: function(str) {
1351
+ if(!this._div) {
1352
+ this._div = document.createElement("DIV");
1353
+ this._text = document.createTextNode('');
1354
+ this._div.appendChild(this._text);
1355
+ }
1356
+ this._text.data = str;
1357
+ return this._div.innerHTML;
1358
+ },
1359
+ isDomNode: function(o) {
1360
+ // TODO: make it more stricter
1361
+ return (typeof o.nodeName == 'string') && (typeof o.nodeType == 'number');
1362
+ },
1363
+ inspectDomPath: function(o) {
1364
+ var sb = [];
1365
+ while(o && o.nodeName != '#document' && o.parent) {
1366
+ var siblings = o.parentNode.childNodes;
1367
+ for(var i = 0; i < siblings.length; i++) {
1368
+ if(siblings[i] == o) {
1369
+ sb.push(o.nodeName + (i == 0 ? '' : '[' + i + ']'));
1370
+ break;
1371
+ }
1372
+ }
1373
+ o = o.parentNode;
1374
+ }
1375
+ return sb.join(" &gt; ");
1376
+ },
1377
+ inspectDomNode: function(o) {
1378
+ if(o.nodeType == 1) {
1379
+ var nodeName = o.nodeName.toLowerCase();
1380
+ var sb = [];
1381
+ sb.push('<span class="dom_value">');
1382
+ sb.push("&lt;");
1383
+ sb.push(nodeName);
1384
+
1385
+ var attrs = o.attributes;
1386
+ for(var i = 0; i < attrs.length; i++) {
1387
+ if(
1388
+ attrs[i].nodeValue &&
1389
+ attrs[i].nodeName != 'contentEditable' &&
1390
+ attrs[i].nodeName != 'style' &&
1391
+ typeof attrs[i].nodeValue != 'function'
1392
+ ) sb.push(' <span class="dom_attr_name">' + attrs[i].nodeName.toLowerCase() + '</span>=<span class="dom_attr_value">"' + attrs[i].nodeValue + '"</span>');
1393
+ }
1394
+ if(o.style && o.style.cssText) {
1395
+ sb.push(' <span class="dom_attr_name">style</span>=<span class="dom_attr_value">"' + o.style.cssText + '"</span>');
1396
+ }
1397
+ sb.push('&gt;');
1398
+ sb.push(JSSpec.util.escapeHtml(o.innerHTML));
1399
+ sb.push('&lt;/' + nodeName + '&gt;');
1400
+ sb.push(' <span class="dom_path">(' + JSSpec.util.inspectDomPath(o) + ')</span>' );
1401
+ sb.push('</span>');
1402
+ return sb.join("");
1403
+ } else if(o.nodeType == 3) {
1404
+ return '<span class="dom_value">#text ' + o.nodeValue + '</span>';
1405
+ } else {
1406
+ return '<span class="dom_value">UnknownDomNode</span>';
1407
+ }
1408
+ },
1409
+ inspect: function(o, dontEscape, emphasisKey) {
1410
+ var sb, inspected;
1411
+
1412
+ if(typeof o == 'undefined') return '<span class="undefined_value">undefined</span>';
1413
+ if(o == null) return '<span class="null_value">null</span>';
1414
+ if(o._type == 'String') return '<span class="string_value">"' + (dontEscape ? o : JSSpec.util.escapeHtml(o)) + '"</span>';
1415
+
1416
+ if(o._type == 'Date') {
1417
+ return '<span class="date_value">"' + o.toString() + '"</span>';
1418
+ }
1419
+
1420
+ if(o._type == 'Number') return '<span class="number_value">' + (dontEscape ? o : JSSpec.util.escapeHtml(o)) + '</span>';
1421
+
1422
+ if(o._type == 'Boolean') return '<span class="boolean_value">' + o + '</span>';
1423
+
1424
+ if(o._type == 'RegExp') return '<span class="regexp_value">' + JSSpec.util.escapeHtml(o.toString()) + '</span>';
1425
+
1426
+ if(JSSpec.util.isDomNode(o)) return JSSpec.util.inspectDomNode(o);
1427
+
1428
+ if(o._type == 'Array' || typeof o.length != 'undefined') {
1429
+ sb = [];
1430
+ for(var i = 0; i < o.length; i++) {
1431
+ inspected = JSSpec.util.inspect(o[i]);
1432
+ sb.push(i == emphasisKey ? ('<strong>' + inspected + '</strong>') : inspected);
1433
+ }
1434
+ return '<span class="array_value">[' + sb.join(', ') + ']</span>';
1435
+ }
1436
+
1437
+ // object
1438
+ sb = [];
1439
+ for(var key in o) {
1440
+ if(key == 'should') continue;
1441
+
1442
+ inspected = JSSpec.util.inspect(key) + ":" + JSSpec.util.inspect(o[key]);
1443
+ sb.push(key == emphasisKey ? ('<strong>' + inspected + '</strong>') : inspected);
1444
+ }
1445
+ return '<span class="object_value">{' + sb.join(', ') + '}</span>';
1446
+ }
1447
+ };
1448
+
1449
+ describe = JSSpec.DSL.describe;
1450
+ behavior_of = JSSpec.DSL.describe;
1451
+ value_of = JSSpec.DSL.value_of;
1452
+ expect = JSSpec.DSL.value_of; // @deprecated
1453
+
1454
+ String.prototype._type = "String";
1455
+ Number.prototype._type = "Number";
1456
+ Date.prototype._type = "Date";
1457
+ Array.prototype._type = "Array";
1458
+ Boolean.prototype._type = "Boolean";
1459
+ RegExp.prototype._type = "RegExp";
1460
+
1461
+ var targets = [Array.prototype, Date.prototype, Number.prototype, String.prototype, Boolean.prototype, RegExp.prototype];
1462
+
1463
+ String.prototype.normalizeHtml = JSSpec.DSL.forString.normalizeHtml;
1464
+ String.prototype.asHtml = String.prototype.normalizeHtml; //@deprecated
1465
+
1466
+
1467
+
1468
+ /**
1469
+ * Main
1470
+ */
1471
+ JSSpec.defaultOptions = {
1472
+ autorun: 1,
1473
+ specIdBeginsWith: 0,
1474
+ exampleIdBeginsWith: 0,
1475
+ autocollapse: 1
1476
+ };
1477
+ JSSpec.options = JSSpec.util.parseOptions(JSSpec.defaultOptions);
1478
+
1479
+ JSSpec.Spec.id = JSSpec.options.specIdBeginsWith;
1480
+ JSSpec.Example.id = JSSpec.options.exampleIdBeginsWith;
1481
+
1482
+
1483
+
1484
+ window.onload = function() {
1485
+ if(JSSpec.specs.length > 0) {
1486
+ if(!JSSpec.options.inSuite) {
1487
+ JSSpec.runner = new JSSpec.Runner(JSSpec.specs, new JSSpec.Logger());
1488
+ if(JSSpec.options.rerun) {
1489
+ JSSpec.runner.rerun(decodeURIComponent(JSSpec.options.rerun));
1490
+ } else {
1491
+ JSSpec.runner.run();
1492
+ }
1493
+ } else {
1494
+ // in suite, send all specs to parent
1495
+ var parentWindow = window.frames.parent.window;
1496
+ for(var i = 0; i < JSSpec.specs.length; i++) {
1497
+ parentWindow.JSSpec.specs.push(JSSpec.specs[i]);
1498
+ }
1499
+ }
1500
+ } else {
1501
+ var links = document.getElementById('list').getElementsByTagName('A');
1502
+ var frameContainer = document.createElement('DIV');
1503
+ frameContainer.style.display = 'none';
1504
+ document.body.appendChild(frameContainer);
1505
+
1506
+ for(var i = 0; i < links.length; i++) {
1507
+ var frame = document.createElement('IFRAME');
1508
+ frame.src = links[i].href + '?inSuite=0&specIdBeginsWith=' + (i * 10000) + '&exampleIdBeginsWith=' + (i * 10000);
1509
+ frameContainer.appendChild(frame);
1510
+ }
1511
+ }
1512
+ }