admin_data 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. data/History.txt +221 -0
  2. data/README.textile +21 -0
  3. data/Rakefile +46 -0
  4. data/app/controllers/admin_data/base_controller.rb +114 -0
  5. data/app/controllers/admin_data/diagnostic_controller.rb +28 -0
  6. data/app/controllers/admin_data/feed_controller.rb +17 -0
  7. data/app/controllers/admin_data/main_controller.rb +132 -0
  8. data/app/controllers/admin_data/migration_controller.rb +19 -0
  9. data/app/controllers/admin_data/search_controller.rb +125 -0
  10. data/app/controllers/admin_data/validate_model_controller.rb +106 -0
  11. data/app/views/admin_data/diagnostic/index.html.erb +17 -0
  12. data/app/views/admin_data/diagnostic/missing_index.html.erb +26 -0
  13. data/app/views/admin_data/feed/index.rss.builder +24 -0
  14. data/app/views/admin_data/main/all_models.html.erb +22 -0
  15. data/app/views/admin_data/main/association/_association_info.html.erb +10 -0
  16. data/app/views/admin_data/main/association/_belongs_to_info.html.erb +7 -0
  17. data/app/views/admin_data/main/association/_has_many_info.html.erb +7 -0
  18. data/app/views/admin_data/main/association/_has_one_info.html.erb +6 -0
  19. data/app/views/admin_data/main/edit.html.erb +38 -0
  20. data/app/views/admin_data/main/misc/_form.html.erb +25 -0
  21. data/app/views/admin_data/main/misc/_modify_record.html.erb +22 -0
  22. data/app/views/admin_data/main/new.html.erb +23 -0
  23. data/app/views/admin_data/main/show.html.erb +41 -0
  24. data/app/views/admin_data/main/table_structure.html.erb +55 -0
  25. data/app/views/admin_data/migration/index.html.erb +18 -0
  26. data/app/views/admin_data/migration/jstest.html.erb +51 -0
  27. data/app/views/admin_data/search/_search_base.html.erb +34 -0
  28. data/app/views/admin_data/search/advance_search.html.erb +3 -0
  29. data/app/views/admin_data/search/quick_search.html.erb +6 -0
  30. data/app/views/admin_data/search/search/_advance_search_form.html.erb +48 -0
  31. data/app/views/admin_data/search/search/_errors.html.erb +5 -0
  32. data/app/views/admin_data/search/search/_listing.html.erb +40 -0
  33. data/app/views/admin_data/search/search/_search_form.html.erb +28 -0
  34. data/app/views/admin_data/search/search/_sortby.html.erb +18 -0
  35. data/app/views/admin_data/search/search/_title.html.erb +34 -0
  36. data/app/views/admin_data/shared/_breadcrum.html.erb +10 -0
  37. data/app/views/admin_data/shared/_drop_down_klasses.html.erb +4 -0
  38. data/app/views/admin_data/shared/_flash_message.html.erb +13 -0
  39. data/app/views/admin_data/shared/_header.html.erb +23 -0
  40. data/app/views/admin_data/shared/_powered_by.html.erb +23 -0
  41. data/app/views/admin_data/shared/_secondary_navigation.html.erb +28 -0
  42. data/app/views/admin_data/validate_model/_bad.html.erb +1 -0
  43. data/app/views/admin_data/validate_model/tid.html.erb +2 -0
  44. data/app/views/admin_data/validate_model/validate.html.erb +67 -0
  45. data/app/views/layouts/admin_data.html.erb +60 -0
  46. data/config/routes.rb +34 -0
  47. data/init.rb +1 -0
  48. data/lib/admin_data.rb +34 -0
  49. data/lib/admin_data/chelper.rb +37 -0
  50. data/lib/admin_data/compatibility.rb +7 -0
  51. data/lib/admin_data/helpers.rb +222 -0
  52. data/lib/admin_data/railtie.rb +21 -0
  53. data/lib/admin_data/search.rb +186 -0
  54. data/lib/admin_data/settings.rb +72 -0
  55. data/lib/admin_data/util.rb +237 -0
  56. data/lib/admin_data/version.rb +3 -0
  57. data/lib/admin_data_date_validation.rb +79 -0
  58. data/lib/css/app.css +224 -0
  59. data/lib/css/base.css +1071 -0
  60. data/lib/css/header.css +65 -0
  61. data/lib/css/rounded.css +18 -0
  62. data/lib/css/themes/drastic-dark/style.css +374 -0
  63. data/lib/css/umbrella.css +34 -0
  64. data/lib/css/vendor/images/ui-bg_diagonals-thick_75_f3d8d8_40x40.png +0 -0
  65. data/lib/css/vendor/images/ui-bg_dots-small_65_a6a6a6_2x2.png +0 -0
  66. data/lib/css/vendor/images/ui-bg_flat_0_333333_40x100.png +0 -0
  67. data/lib/css/vendor/images/ui-bg_flat_65_ffffff_40x100.png +0 -0
  68. data/lib/css/vendor/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  69. data/lib/css/vendor/images/ui-bg_glass_55_fbf8ee_1x400.png +0 -0
  70. data/lib/css/vendor/images/ui-bg_highlight-hard_100_eeeeee_1x100.png +0 -0
  71. data/lib/css/vendor/images/ui-bg_highlight-hard_100_f6f6f6_1x100.png +0 -0
  72. data/lib/css/vendor/images/ui-bg_highlight-soft_15_cc0000_1x100.png +0 -0
  73. data/lib/css/vendor/images/ui-icons_004276_256x240.png +0 -0
  74. data/lib/css/vendor/images/ui-icons_cc0000_256x240.png +0 -0
  75. data/lib/css/vendor/images/ui-icons_ffffff_256x240.png +0 -0
  76. data/lib/css/vendor/jquery-ui-1.7.2.custom.css +406 -0
  77. data/lib/css/vendor/qunit.css +119 -0
  78. data/lib/js/advance_search/act_on_result.js +47 -0
  79. data/lib/js/advance_search/adv_search.js +46 -0
  80. data/lib/js/advance_search/advance_search.js +60 -0
  81. data/lib/js/advance_search/advance_search_structure.js +79 -0
  82. data/lib/js/advance_search/ajaxify_advance_search.js +28 -0
  83. data/lib/js/advance_search/build_first_row.js +12 -0
  84. data/lib/js/advance_search/event_bindings.js +87 -0
  85. data/lib/js/advance_search/global_ajax_setting.js +10 -0
  86. data/lib/js/advance_search/trigger_submit_on_domready.js +6 -0
  87. data/lib/js/misc/drop_down_change.js +8 -0
  88. data/lib/js/misc/js_util.js +42 -0
  89. data/lib/js/misc/quick_search_input_focus.js +6 -0
  90. data/lib/js/test/act_on_result.js +120 -0
  91. data/lib/js/test/advance_search.js +80 -0
  92. data/lib/js/test/ajaxify_advance_search.js +29 -0
  93. data/lib/js/test/build_first_row.js +10 -0
  94. data/lib/js/test/event_bindings.js +100 -0
  95. data/lib/js/validate_model/ajaxify_form.js +66 -0
  96. data/lib/js/validate_model/select_all.js +15 -0
  97. data/lib/js/vendor/jack.js +903 -0
  98. data/lib/js/vendor/jquery-1.4.1.js +6078 -0
  99. data/lib/js/vendor/jquery-ui-1.7.2.custom.min.js +298 -0
  100. data/lib/js/vendor/jquery.ba-isjquery.js +21 -0
  101. data/lib/js/vendor/jquery.form.js +814 -0
  102. data/lib/js/vendor/jquery.lint.js +604 -0
  103. data/lib/js/vendor/log.js +9 -0
  104. data/lib/js/vendor/qunit.js +1043 -0
  105. data/lib/tasks/admin_data_tasks.rake +7 -0
  106. data/lib/tasks/validate_models_bg.rake +23 -0
  107. data/test/factories/article.rb +9 -0
  108. data/test/factories/car.rb +4 -0
  109. data/test/factories/city.rb +4 -0
  110. data/test/factories/comment.rb +6 -0
  111. data/test/factories/door.rb +4 -0
  112. data/test/factories/engine.rb +4 -0
  113. data/test/functional/base_controller_test.rb +5 -0
  114. data/test/functional/feed_controller_test.rb +34 -0
  115. data/test/functional/main_controller_test.rb +421 -0
  116. data/test/functional/migration_controller_test.rb +30 -0
  117. data/test/functional/routes_test.rb +61 -0
  118. data/test/functional/search_controller_test.rb +814 -0
  119. data/test/helper/view_helper_test.rb +177 -0
  120. data/test/misc_tests/date_validation_test.rb +32 -0
  121. data/test/misc_tests/settings_test.rb +29 -0
  122. data/test/misc_tests/util_test.rb +83 -0
  123. data/test/rails_root/Gemfile +22 -0
  124. data/test/rails_root/Gemfile.lock +101 -0
  125. data/test/rails_root/Rakefile +7 -0
  126. data/test/rails_root/app/controllers/application_controller.rb +3 -0
  127. data/test/rails_root/app/helpers/application_helper.rb +2 -0
  128. data/test/rails_root/app/models/article.rb +25 -0
  129. data/test/rails_root/app/models/city.rb +15 -0
  130. data/test/rails_root/app/models/comment.rb +13 -0
  131. data/test/rails_root/app/models/tech_magazine.rb +2 -0
  132. data/test/rails_root/app/models/vehicle/car.rb +4 -0
  133. data/test/rails_root/app/models/vehicle/door.rb +3 -0
  134. data/test/rails_root/app/models/vehicle/engine.rb +3 -0
  135. data/test/rails_root/app/views/layouts/application.html.erb +14 -0
  136. data/test/rails_root/config.ru +4 -0
  137. data/test/rails_root/config/application.rb +42 -0
  138. data/test/rails_root/config/boot.rb +13 -0
  139. data/test/rails_root/config/database.yml +22 -0
  140. data/test/rails_root/config/environment.rb +5 -0
  141. data/test/rails_root/config/environments/development.rb +22 -0
  142. data/test/rails_root/config/environments/production.rb +49 -0
  143. data/test/rails_root/config/environments/test.rb +35 -0
  144. data/test/rails_root/config/initializers/backtrace_silencers.rb +7 -0
  145. data/test/rails_root/config/initializers/inflections.rb +10 -0
  146. data/test/rails_root/config/initializers/mime_types.rb +5 -0
  147. data/test/rails_root/config/initializers/secret_token.rb +7 -0
  148. data/test/rails_root/config/initializers/session_store.rb +8 -0
  149. data/test/rails_root/config/locales/en.yml +5 -0
  150. data/test/rails_root/config/routes.rb +58 -0
  151. data/test/rails_root/db/migrate/20090809061114_create_tables.rb +70 -0
  152. data/test/rails_root/db/schema.rb +74 -0
  153. data/test/rails_root/db/seeds.rb +7 -0
  154. data/test/rails_root/script/rails +6 -0
  155. data/test/rails_root/test/performance/browsing_test.rb +0 -0
  156. data/test/rails_root/test/test_helper.rb +13 -0
  157. data/test/support/assertions.rb +20 -0
  158. data/test/test_helper.rb +74 -0
  159. metadata +321 -0
@@ -0,0 +1,9 @@
1
+ function log() {
2
+ try {
3
+ console.log.apply(console, arguments);
4
+ } catch(e) {
5
+ //alert( Array.prototype.join.call( arguments, " "));
6
+ }
7
+ }
8
+
9
+
@@ -0,0 +1,1043 @@
1
+ /*
2
+ * QUnit - A JavaScript Unit Testing Framework
3
+ *
4
+ * http://docs.jquery.com/QUnit
5
+ *
6
+ * Copyright (c) 2009 John Resig, Jörn Zaefferer
7
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
8
+ * and GPL (GPL-LICENSE.txt) licenses.
9
+ */
10
+
11
+ (function(window) {
12
+
13
+ var QUnit = {
14
+
15
+ // Initialize the configuration options
16
+ init: function() {
17
+ config = {
18
+ stats: { all: 0, bad: 0 },
19
+ moduleStats: { all: 0, bad: 0 },
20
+ started: +new Date,
21
+ blocking: false,
22
+ autorun: false,
23
+ assertions: [],
24
+ filters: [],
25
+ queue: []
26
+ };
27
+
28
+ var tests = id("qunit-tests"),
29
+ banner = id("qunit-banner"),
30
+ result = id("qunit-testresult");
31
+
32
+ if ( tests ) {
33
+ tests.innerHTML = "";
34
+ }
35
+
36
+ if ( banner ) {
37
+ banner.className = "";
38
+ }
39
+
40
+ if ( result ) {
41
+ result.parentNode.removeChild( result );
42
+ }
43
+ },
44
+
45
+ // call on start of module test to prepend name to all tests
46
+ module: function(name, testEnvironment) {
47
+ config.currentModule = name;
48
+
49
+ synchronize(function() {
50
+ if ( config.currentModule ) {
51
+ QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
52
+ }
53
+
54
+ config.currentModule = name;
55
+ config.moduleTestEnvironment = testEnvironment;
56
+ config.moduleStats = { all: 0, bad: 0 };
57
+
58
+ QUnit.moduleStart( name, testEnvironment );
59
+ });
60
+ },
61
+
62
+ asyncTest: function(testName, expected, callback) {
63
+ if ( arguments.length === 2 ) {
64
+ callback = expected;
65
+ expected = 0;
66
+ }
67
+
68
+ QUnit.test(testName, expected, callback, true);
69
+ },
70
+
71
+ test: function(testName, expected, callback, async) {
72
+ var name = testName, testEnvironment, testEnvironmentArg;
73
+
74
+ if ( arguments.length === 2 ) {
75
+ callback = expected;
76
+ expected = null;
77
+ }
78
+ // is 2nd argument a testEnvironment?
79
+ if ( expected && typeof expected === 'object') {
80
+ testEnvironmentArg = expected;
81
+ expected = null;
82
+ }
83
+
84
+ if ( config.currentModule ) {
85
+ name = config.currentModule + " module: " + name;
86
+ }
87
+
88
+ if ( !validTest(name) ) {
89
+ return;
90
+ }
91
+
92
+ synchronize(function() {
93
+ QUnit.testStart( testName );
94
+
95
+ testEnvironment = extend({
96
+ setup: function() {},
97
+ teardown: function() {}
98
+ }, config.moduleTestEnvironment);
99
+ if (testEnvironmentArg) {
100
+ extend(testEnvironment,testEnvironmentArg);
101
+ }
102
+
103
+ // allow utility functions to access the current test environment
104
+ QUnit.current_testEnvironment = testEnvironment;
105
+
106
+ config.assertions = [];
107
+ config.expected = expected;
108
+
109
+ try {
110
+ if ( !config.pollution ) {
111
+ saveGlobal();
112
+ }
113
+
114
+ testEnvironment.setup.call(testEnvironment);
115
+ } catch(e) {
116
+ QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
117
+ }
118
+
119
+ if ( async ) {
120
+ QUnit.stop();
121
+ }
122
+
123
+ try {
124
+ callback.call(testEnvironment);
125
+ } catch(e) {
126
+ fail("Test " + name + " died, exception and test follows", e, callback);
127
+ QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
128
+ // else next test will carry the responsibility
129
+ saveGlobal();
130
+
131
+ // Restart the tests if they're blocking
132
+ if ( config.blocking ) {
133
+ start();
134
+ }
135
+ }
136
+ });
137
+
138
+ synchronize(function() {
139
+ try {
140
+ checkPollution();
141
+ testEnvironment.teardown.call(testEnvironment);
142
+ } catch(e) {
143
+ QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
144
+ }
145
+
146
+ try {
147
+ QUnit.reset();
148
+ } catch(e) {
149
+ fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
150
+ }
151
+
152
+ if ( config.expected && config.expected != config.assertions.length ) {
153
+ QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
154
+ }
155
+
156
+ var good = 0, bad = 0,
157
+ tests = id("qunit-tests");
158
+
159
+ config.stats.all += config.assertions.length;
160
+ config.moduleStats.all += config.assertions.length;
161
+
162
+ if ( tests ) {
163
+ var ol = document.createElement("ol");
164
+ ol.style.display = "none";
165
+
166
+ for ( var i = 0; i < config.assertions.length; i++ ) {
167
+ var assertion = config.assertions[i];
168
+
169
+ var li = document.createElement("li");
170
+ li.className = assertion.result ? "pass" : "fail";
171
+ li.appendChild(document.createTextNode(assertion.message || "(no message)"));
172
+ ol.appendChild( li );
173
+
174
+ if ( assertion.result ) {
175
+ good++;
176
+ } else {
177
+ bad++;
178
+ config.stats.bad++;
179
+ config.moduleStats.bad++;
180
+ }
181
+ }
182
+
183
+ var b = document.createElement("strong");
184
+ b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";
185
+
186
+ addEvent(b, "click", function() {
187
+ var next = b.nextSibling, display = next.style.display;
188
+ next.style.display = display === "none" ? "block" : "none";
189
+ });
190
+
191
+ addEvent(b, "dblclick", function(e) {
192
+ var target = e && e.target ? e.target : window.event.srcElement;
193
+ if ( target.nodeName.toLowerCase() === "strong" ) {
194
+ var text = "", node = target.firstChild;
195
+
196
+ while ( node.nodeType === 3 ) {
197
+ text += node.nodeValue;
198
+ node = node.nextSibling;
199
+ }
200
+
201
+ text = text.replace(/(^\s*|\s*$)/g, "");
202
+
203
+ if ( window.location ) {
204
+ window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
205
+ }
206
+ }
207
+ });
208
+
209
+ var li = document.createElement("li");
210
+ li.className = bad ? "fail" : "pass";
211
+ li.appendChild( b );
212
+ li.appendChild( ol );
213
+ tests.appendChild( li );
214
+
215
+ if ( bad ) {
216
+ var toolbar = id("qunit-testrunner-toolbar");
217
+ if ( toolbar ) {
218
+ toolbar.style.display = "block";
219
+ id("qunit-filter-pass").disabled = null;
220
+ id("qunit-filter-missing").disabled = null;
221
+ }
222
+ }
223
+
224
+ } else {
225
+ for ( var i = 0; i < config.assertions.length; i++ ) {
226
+ if ( !config.assertions[i].result ) {
227
+ bad++;
228
+ config.stats.bad++;
229
+ config.moduleStats.bad++;
230
+ }
231
+ }
232
+ }
233
+
234
+ QUnit.testDone( testName, bad, config.assertions.length );
235
+
236
+ if ( !window.setTimeout && !config.queue.length ) {
237
+ done();
238
+ }
239
+ });
240
+
241
+ if ( window.setTimeout && !config.doneTimer ) {
242
+ config.doneTimer = window.setTimeout(function(){
243
+ if ( !config.queue.length ) {
244
+ done();
245
+ } else {
246
+ synchronize( done );
247
+ }
248
+ }, 13);
249
+ }
250
+ },
251
+
252
+ /**
253
+ * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
254
+ */
255
+ expect: function(asserts) {
256
+ config.expected = asserts;
257
+ },
258
+
259
+ /**
260
+ * Asserts true.
261
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
262
+ */
263
+ ok: function(a, msg) {
264
+ QUnit.log(a, msg);
265
+
266
+ config.assertions.push({
267
+ result: !!a,
268
+ message: msg
269
+ });
270
+ },
271
+
272
+ /**
273
+ * Checks that the first two arguments are equal, with an optional message.
274
+ * Prints out both actual and expected values.
275
+ *
276
+ * Prefered to ok( actual == expected, message )
277
+ *
278
+ * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
279
+ *
280
+ * @param Object actual
281
+ * @param Object expected
282
+ * @param String message (optional)
283
+ */
284
+ equal: function(actual, expected, message) {
285
+ push(expected == actual, actual, expected, message);
286
+ },
287
+
288
+ notEqual: function(actual, expected, message) {
289
+ push(expected != actual, actual, expected, message);
290
+ },
291
+
292
+ deepEqual: function(a, b, message) {
293
+ push(QUnit.equiv(a, b), a, b, message);
294
+ },
295
+
296
+ notDeepEqual: function(a, b, message) {
297
+ push(!QUnit.equiv(a, b), a, b, message);
298
+ },
299
+
300
+ strictEqual: function(actual, expected, message) {
301
+ push(expected === actual, actual, expected, message);
302
+ },
303
+
304
+ notStrictEqual: function(actual, expected, message) {
305
+ push(expected !== actual, actual, expected, message);
306
+ },
307
+
308
+ start: function() {
309
+ // A slight delay, to avoid any current callbacks
310
+ if ( window.setTimeout ) {
311
+ window.setTimeout(function() {
312
+ if ( config.timeout ) {
313
+ clearTimeout(config.timeout);
314
+ }
315
+
316
+ config.blocking = false;
317
+ process();
318
+ }, 13);
319
+ } else {
320
+ config.blocking = false;
321
+ process();
322
+ }
323
+ },
324
+
325
+ stop: function(timeout) {
326
+ config.blocking = true;
327
+
328
+ if ( timeout && window.setTimeout ) {
329
+ config.timeout = window.setTimeout(function() {
330
+ QUnit.ok( false, "Test timed out" );
331
+ QUnit.start();
332
+ }, timeout);
333
+ }
334
+ },
335
+
336
+ /**
337
+ * Resets the test setup. Useful for tests that modify the DOM.
338
+ */
339
+ reset: function() {
340
+ if ( window.jQuery ) {
341
+ jQuery("#main").html( config.fixture );
342
+ jQuery.event.global = {};
343
+ jQuery.ajaxSettings = extend({}, config.ajaxSettings);
344
+ }
345
+ },
346
+
347
+ /**
348
+ * Trigger an event on an element.
349
+ *
350
+ * @example triggerEvent( document.body, "click" );
351
+ *
352
+ * @param DOMElement elem
353
+ * @param String type
354
+ */
355
+ triggerEvent: function( elem, type, event ) {
356
+ if ( document.createEvent ) {
357
+ event = document.createEvent("MouseEvents");
358
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
359
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
360
+ elem.dispatchEvent( event );
361
+
362
+ } else if ( elem.fireEvent ) {
363
+ elem.fireEvent("on"+type);
364
+ }
365
+ },
366
+
367
+ // Safe object type checking
368
+ is: function( type, obj ) {
369
+ return Object.prototype.toString.call( obj ) === "[object "+ type +"]";
370
+ },
371
+
372
+ // Logging callbacks
373
+ done: function(failures, total) {},
374
+ log: function(result, message) {},
375
+ testStart: function(name) {},
376
+ testDone: function(name, failures, total) {},
377
+ moduleStart: function(name, testEnvironment) {},
378
+ moduleDone: function(name, failures, total) {}
379
+ };
380
+
381
+ // Backwards compatibility, deprecated
382
+ QUnit.equals = QUnit.equal;
383
+ QUnit.same = QUnit.deepEqual;
384
+
385
+ // Maintain internal state
386
+ var config = {
387
+ // The queue of tests to run
388
+ queue: [],
389
+
390
+ // block until document ready
391
+ blocking: true
392
+ };
393
+
394
+ // Load paramaters
395
+ (function() {
396
+ var location = window.location || { search: "", protocol: "file:" },
397
+ GETParams = location.search.slice(1).split('&');
398
+
399
+ for ( var i = 0; i < GETParams.length; i++ ) {
400
+ GETParams[i] = decodeURIComponent( GETParams[i] );
401
+ if ( GETParams[i] === "noglobals" ) {
402
+ GETParams.splice( i, 1 );
403
+ i--;
404
+ config.noglobals = true;
405
+ } else if ( GETParams[i].search('=') > -1 ) {
406
+ GETParams.splice( i, 1 );
407
+ i--;
408
+ }
409
+ }
410
+
411
+ // restrict modules/tests by get parameters
412
+ config.filters = GETParams;
413
+
414
+ // Figure out if we're running the tests from a server or not
415
+ QUnit.isLocal = !!(location.protocol === 'file:');
416
+ })();
417
+
418
+ // Expose the API as global variables, unless an 'exports'
419
+ // object exists, in that case we assume we're in CommonJS
420
+ if ( typeof exports === "undefined" || typeof require === "undefined" ) {
421
+ extend(window, QUnit);
422
+ window.QUnit = QUnit;
423
+ } else {
424
+ extend(exports, QUnit);
425
+ exports.QUnit = QUnit;
426
+ }
427
+
428
+ if ( typeof document === "undefined" || document.readyState === "complete" ) {
429
+ config.autorun = true;
430
+ }
431
+
432
+ addEvent(window, "load", function() {
433
+ // Initialize the config, saving the execution queue
434
+ var oldconfig = extend({}, config);
435
+ QUnit.init();
436
+ extend(config, oldconfig);
437
+
438
+ config.blocking = false;
439
+
440
+ var userAgent = id("qunit-userAgent");
441
+ if ( userAgent ) {
442
+ userAgent.innerHTML = navigator.userAgent;
443
+ }
444
+
445
+ var toolbar = id("qunit-testrunner-toolbar");
446
+ if ( toolbar ) {
447
+ toolbar.style.display = "none";
448
+
449
+ var filter = document.createElement("input");
450
+ filter.type = "checkbox";
451
+ filter.id = "qunit-filter-pass";
452
+ filter.disabled = true;
453
+ addEvent( filter, "click", function() {
454
+ var li = document.getElementsByTagName("li");
455
+ for ( var i = 0; i < li.length; i++ ) {
456
+ if ( li[i].className.indexOf("pass") > -1 ) {
457
+ li[i].style.display = filter.checked ? "none" : "";
458
+ }
459
+ }
460
+ });
461
+ toolbar.appendChild( filter );
462
+
463
+ var label = document.createElement("label");
464
+ label.setAttribute("for", "qunit-filter-pass");
465
+ label.innerHTML = "Hide passed tests";
466
+ toolbar.appendChild( label );
467
+
468
+ var missing = document.createElement("input");
469
+ missing.type = "checkbox";
470
+ missing.id = "qunit-filter-missing";
471
+ missing.disabled = true;
472
+ addEvent( missing, "click", function() {
473
+ var li = document.getElementsByTagName("li");
474
+ for ( var i = 0; i < li.length; i++ ) {
475
+ if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
476
+ li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
477
+ }
478
+ }
479
+ });
480
+ toolbar.appendChild( missing );
481
+
482
+ label = document.createElement("label");
483
+ label.setAttribute("for", "qunit-filter-missing");
484
+ label.innerHTML = "Hide missing tests (untested code is broken code)";
485
+ toolbar.appendChild( label );
486
+ }
487
+
488
+ var main = id('main');
489
+ if ( main ) {
490
+ config.fixture = main.innerHTML;
491
+ }
492
+
493
+ if ( window.jQuery ) {
494
+ config.ajaxSettings = window.jQuery.ajaxSettings;
495
+ }
496
+
497
+ QUnit.start();
498
+ });
499
+
500
+ function done() {
501
+ if ( config.doneTimer && window.clearTimeout ) {
502
+ window.clearTimeout( config.doneTimer );
503
+ config.doneTimer = null;
504
+ }
505
+
506
+ if ( config.queue.length ) {
507
+ config.doneTimer = window.setTimeout(function(){
508
+ if ( !config.queue.length ) {
509
+ done();
510
+ } else {
511
+ synchronize( done );
512
+ }
513
+ }, 13);
514
+
515
+ return;
516
+ }
517
+
518
+ config.autorun = true;
519
+
520
+ // Log the last module results
521
+ if ( config.currentModule ) {
522
+ QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
523
+ }
524
+
525
+ var banner = id("qunit-banner"),
526
+ tests = id("qunit-tests"),
527
+ html = ['Tests completed in ',
528
+ +new Date - config.started, ' milliseconds.<br/>',
529
+ '<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');
530
+
531
+ if ( banner ) {
532
+ banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
533
+ }
534
+
535
+ if ( tests ) {
536
+ var result = id("qunit-testresult");
537
+
538
+ if ( !result ) {
539
+ result = document.createElement("p");
540
+ result.id = "qunit-testresult";
541
+ result.className = "result";
542
+ tests.parentNode.insertBefore( result, tests.nextSibling );
543
+ }
544
+
545
+ result.innerHTML = html;
546
+ }
547
+
548
+ QUnit.done( config.stats.bad, config.stats.all );
549
+ }
550
+
551
+ function validTest( name ) {
552
+ var i = config.filters.length,
553
+ run = false;
554
+
555
+ if ( !i ) {
556
+ return true;
557
+ }
558
+
559
+ while ( i-- ) {
560
+ var filter = config.filters[i],
561
+ not = filter.charAt(0) == '!';
562
+
563
+ if ( not ) {
564
+ filter = filter.slice(1);
565
+ }
566
+
567
+ if ( name.indexOf(filter) !== -1 ) {
568
+ return !not;
569
+ }
570
+
571
+ if ( not ) {
572
+ run = true;
573
+ }
574
+ }
575
+
576
+ return run;
577
+ }
578
+
579
+ function push(result, actual, expected, message) {
580
+ message = message || (result ? "okay" : "failed");
581
+ QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
582
+ }
583
+
584
+ function synchronize( callback ) {
585
+ config.queue.push( callback );
586
+
587
+ if ( config.autorun && !config.blocking ) {
588
+ process();
589
+ }
590
+ }
591
+
592
+ function process() {
593
+ while ( config.queue.length && !config.blocking ) {
594
+ config.queue.shift()();
595
+ }
596
+ }
597
+
598
+ function saveGlobal() {
599
+ config.pollution = [];
600
+
601
+ if ( config.noglobals ) {
602
+ for ( var key in window ) {
603
+ config.pollution.push( key );
604
+ }
605
+ }
606
+ }
607
+
608
+ function checkPollution( name ) {
609
+ var old = config.pollution;
610
+ saveGlobal();
611
+
612
+ var newGlobals = diff( old, config.pollution );
613
+ if ( newGlobals.length > 0 ) {
614
+ ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
615
+ config.expected++;
616
+ }
617
+
618
+ var deletedGlobals = diff( config.pollution, old );
619
+ if ( deletedGlobals.length > 0 ) {
620
+ ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
621
+ config.expected++;
622
+ }
623
+ }
624
+
625
+ // returns a new Array with the elements that are in a but not in b
626
+ function diff( a, b ) {
627
+ var result = a.slice();
628
+ for ( var i = 0; i < result.length; i++ ) {
629
+ for ( var j = 0; j < b.length; j++ ) {
630
+ if ( result[i] === b[j] ) {
631
+ result.splice(i, 1);
632
+ i--;
633
+ break;
634
+ }
635
+ }
636
+ }
637
+ return result;
638
+ }
639
+
640
+ function fail(message, exception, callback) {
641
+ if ( typeof console !== "undefined" && console.error && console.warn ) {
642
+ console.error(message);
643
+ console.error(exception);
644
+ console.warn(callback.toString());
645
+
646
+ } else if ( window.opera && opera.postError ) {
647
+ opera.postError(message, exception, callback.toString);
648
+ }
649
+ }
650
+
651
+ function extend(a, b) {
652
+ for ( var prop in b ) {
653
+ a[prop] = b[prop];
654
+ }
655
+
656
+ return a;
657
+ }
658
+
659
+ function addEvent(elem, type, fn) {
660
+ if ( elem.addEventListener ) {
661
+ elem.addEventListener( type, fn, false );
662
+ } else if ( elem.attachEvent ) {
663
+ elem.attachEvent( "on" + type, fn );
664
+ } else {
665
+ fn();
666
+ }
667
+ }
668
+
669
+ function id(name) {
670
+ return !!(typeof document !== "undefined" && document && document.getElementById) &&
671
+ document.getElementById( name );
672
+ }
673
+
674
+ // Test for equality any JavaScript type.
675
+ // Discussions and reference: http://philrathe.com/articles/equiv
676
+ // Test suites: http://philrathe.com/tests/equiv
677
+ // Author: Philippe Rathé <prathe@gmail.com>
678
+ QUnit.equiv = function () {
679
+
680
+ var innerEquiv; // the real equiv function
681
+ var callers = []; // stack to decide between skip/abort functions
682
+
683
+
684
+ // Determine what is o.
685
+ function hoozit(o) {
686
+ if (QUnit.is("String", o)) {
687
+ return "string";
688
+
689
+ } else if (QUnit.is("Boolean", o)) {
690
+ return "boolean";
691
+
692
+ } else if (QUnit.is("Number", o)) {
693
+
694
+ if (isNaN(o)) {
695
+ return "nan";
696
+ } else {
697
+ return "number";
698
+ }
699
+
700
+ } else if (typeof o === "undefined") {
701
+ return "undefined";
702
+
703
+ // consider: typeof null === object
704
+ } else if (o === null) {
705
+ return "null";
706
+
707
+ // consider: typeof [] === object
708
+ } else if (QUnit.is( "Array", o)) {
709
+ return "array";
710
+
711
+ // consider: typeof new Date() === object
712
+ } else if (QUnit.is( "Date", o)) {
713
+ return "date";
714
+
715
+ // consider: /./ instanceof Object;
716
+ // /./ instanceof RegExp;
717
+ // typeof /./ === "function"; // => false in IE and Opera,
718
+ // true in FF and Safari
719
+ } else if (QUnit.is( "RegExp", o)) {
720
+ return "regexp";
721
+
722
+ } else if (typeof o === "object") {
723
+ return "object";
724
+
725
+ } else if (QUnit.is( "Function", o)) {
726
+ return "function";
727
+ } else {
728
+ return undefined;
729
+ }
730
+ }
731
+
732
+ // Call the o related callback with the given arguments.
733
+ function bindCallbacks(o, callbacks, args) {
734
+ var prop = hoozit(o);
735
+ if (prop) {
736
+ if (hoozit(callbacks[prop]) === "function") {
737
+ return callbacks[prop].apply(callbacks, args);
738
+ } else {
739
+ return callbacks[prop]; // or undefined
740
+ }
741
+ }
742
+ }
743
+
744
+ var callbacks = function () {
745
+
746
+ // for string, boolean, number and null
747
+ function useStrictEquality(b, a) {
748
+ if (b instanceof a.constructor || a instanceof b.constructor) {
749
+ // to catch short annotaion VS 'new' annotation of a declaration
750
+ // e.g. var i = 1;
751
+ // var j = new Number(1);
752
+ return a == b;
753
+ } else {
754
+ return a === b;
755
+ }
756
+ }
757
+
758
+ return {
759
+ "string": useStrictEquality,
760
+ "boolean": useStrictEquality,
761
+ "number": useStrictEquality,
762
+ "null": useStrictEquality,
763
+ "undefined": useStrictEquality,
764
+
765
+ "nan": function (b) {
766
+ return isNaN(b);
767
+ },
768
+
769
+ "date": function (b, a) {
770
+ return hoozit(b) === "date" && a.valueOf() === b.valueOf();
771
+ },
772
+
773
+ "regexp": function (b, a) {
774
+ return hoozit(b) === "regexp" &&
775
+ a.source === b.source && // the regex itself
776
+ a.global === b.global && // and its modifers (gmi) ...
777
+ a.ignoreCase === b.ignoreCase &&
778
+ a.multiline === b.multiline;
779
+ },
780
+
781
+ // - skip when the property is a method of an instance (OOP)
782
+ // - abort otherwise,
783
+ // initial === would have catch identical references anyway
784
+ "function": function () {
785
+ var caller = callers[callers.length - 1];
786
+ return caller !== Object &&
787
+ typeof caller !== "undefined";
788
+ },
789
+
790
+ "array": function (b, a) {
791
+ var i;
792
+ var len;
793
+
794
+ // b could be an object literal here
795
+ if ( ! (hoozit(b) === "array")) {
796
+ return false;
797
+ }
798
+
799
+ len = a.length;
800
+ if (len !== b.length) { // safe and faster
801
+ return false;
802
+ }
803
+ for (i = 0; i < len; i++) {
804
+ if ( ! innerEquiv(a[i], b[i])) {
805
+ return false;
806
+ }
807
+ }
808
+ return true;
809
+ },
810
+
811
+ "object": function (b, a) {
812
+ var i;
813
+ var eq = true; // unless we can proove it
814
+ var aProperties = [], bProperties = []; // collection of strings
815
+
816
+ // comparing constructors is more strict than using instanceof
817
+ if ( a.constructor !== b.constructor) {
818
+ return false;
819
+ }
820
+
821
+ // stack constructor before traversing properties
822
+ callers.push(a.constructor);
823
+
824
+ for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
825
+
826
+ aProperties.push(i); // collect a's properties
827
+
828
+ if ( ! innerEquiv(a[i], b[i])) {
829
+ eq = false;
830
+ break;
831
+ }
832
+ }
833
+
834
+ callers.pop(); // unstack, we are done
835
+
836
+ for (i in b) {
837
+ bProperties.push(i); // collect b's properties
838
+ }
839
+
840
+ // Ensures identical properties name
841
+ return eq && innerEquiv(aProperties.sort(), bProperties.sort());
842
+ }
843
+ };
844
+ }();
845
+
846
+ innerEquiv = function () { // can take multiple arguments
847
+ var args = Array.prototype.slice.apply(arguments);
848
+ if (args.length < 2) {
849
+ return true; // end transition
850
+ }
851
+
852
+ return (function (a, b) {
853
+ if (a === b) {
854
+ return true; // catch the most you can
855
+ } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
856
+ return false; // don't lose time with error prone cases
857
+ } else {
858
+ return bindCallbacks(a, callbacks, [b, a]);
859
+ }
860
+
861
+ // apply transition with (1..n) arguments
862
+ })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
863
+ };
864
+
865
+ return innerEquiv;
866
+
867
+ }();
868
+
869
+ /**
870
+ * jsDump
871
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
872
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
873
+ * Date: 5/15/2008
874
+ * @projectDescription Advanced and extensible data dumping for Javascript.
875
+ * @version 1.0.0
876
+ * @author Ariel Flesler
877
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
878
+ */
879
+ QUnit.jsDump = (function() {
880
+ function quote( str ) {
881
+ return '"' + str.toString().replace(/"/g, '\\"') + '"';
882
+ };
883
+ function literal( o ) {
884
+ return o + '';
885
+ };
886
+ function join( pre, arr, post ) {
887
+ var s = jsDump.separator(),
888
+ base = jsDump.indent(),
889
+ inner = jsDump.indent(1);
890
+ if ( arr.join )
891
+ arr = arr.join( ',' + s + inner );
892
+ if ( !arr )
893
+ return pre + post;
894
+ return [ pre, inner + arr, base + post ].join(s);
895
+ };
896
+ function array( arr ) {
897
+ var i = arr.length, ret = Array(i);
898
+ this.up();
899
+ while ( i-- )
900
+ ret[i] = this.parse( arr[i] );
901
+ this.down();
902
+ return join( '[', ret, ']' );
903
+ };
904
+
905
+ var reName = /^function (\w+)/;
906
+
907
+ var jsDump = {
908
+ parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
909
+ var parser = this.parsers[ type || this.typeOf(obj) ];
910
+ type = typeof parser;
911
+
912
+ return type == 'function' ? parser.call( this, obj ) :
913
+ type == 'string' ? parser :
914
+ this.parsers.error;
915
+ },
916
+ typeOf:function( obj ) {
917
+ var type;
918
+ if ( obj === null ) {
919
+ type = "null";
920
+ } else if (typeof obj === "undefined") {
921
+ type = "undefined";
922
+ } else if (QUnit.is("RegExp", obj)) {
923
+ type = "regexp";
924
+ } else if (QUnit.is("Date", obj)) {
925
+ type = "date";
926
+ } else if (QUnit.is("Function", obj)) {
927
+ type = "function";
928
+ } else if (QUnit.is("Array", obj)) {
929
+ type = "array";
930
+ } else if (QUnit.is("Window", obj) || QUnit.is("global", obj)) {
931
+ type = "window";
932
+ } else if (QUnit.is("HTMLDocument", obj)) {
933
+ type = "document";
934
+ } else if (QUnit.is("HTMLCollection", obj) || QUnit.is("NodeList", obj)) {
935
+ type = "nodelist";
936
+ } else if (/^\[object HTML/.test(Object.prototype.toString.call( obj ))) {
937
+ type = "node";
938
+ } else {
939
+ type = typeof obj;
940
+ }
941
+ return type;
942
+ },
943
+ separator:function() {
944
+ return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
945
+ },
946
+ indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
947
+ if ( !this.multiline )
948
+ return '';
949
+ var chr = this.indentChar;
950
+ if ( this.HTML )
951
+ chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
952
+ return Array( this._depth_ + (extra||0) ).join(chr);
953
+ },
954
+ up:function( a ) {
955
+ this._depth_ += a || 1;
956
+ },
957
+ down:function( a ) {
958
+ this._depth_ -= a || 1;
959
+ },
960
+ setParser:function( name, parser ) {
961
+ this.parsers[name] = parser;
962
+ },
963
+ // The next 3 are exposed so you can use them
964
+ quote:quote,
965
+ literal:literal,
966
+ join:join,
967
+ //
968
+ _depth_: 1,
969
+ // This is the list of parsers, to modify them, use jsDump.setParser
970
+ parsers:{
971
+ window: '[Window]',
972
+ document: '[Document]',
973
+ error:'[ERROR]', //when no parser is found, shouldn't happen
974
+ unknown: '[Unknown]',
975
+ 'null':'null',
976
+ undefined:'undefined',
977
+ 'function':function( fn ) {
978
+ var ret = 'function',
979
+ name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
980
+ if ( name )
981
+ ret += ' ' + name;
982
+ ret += '(';
983
+
984
+ ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
985
+ return join( ret, this.parse(fn,'functionCode'), '}' );
986
+ },
987
+ array: array,
988
+ nodelist: array,
989
+ arguments: array,
990
+ object:function( map ) {
991
+ var ret = [ ];
992
+ this.up();
993
+ for ( var key in map )
994
+ ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
995
+ this.down();
996
+ return join( '{', ret, '}' );
997
+ },
998
+ node:function( node ) {
999
+ var open = this.HTML ? '&lt;' : '<',
1000
+ close = this.HTML ? '&gt;' : '>';
1001
+
1002
+ var tag = node.nodeName.toLowerCase(),
1003
+ ret = open + tag;
1004
+
1005
+ for ( var a in this.DOMAttrs ) {
1006
+ var val = node[this.DOMAttrs[a]];
1007
+ if ( val )
1008
+ ret += ' ' + a + '=' + this.parse( val, 'attribute' );
1009
+ }
1010
+ return ret + close + open + '/' + tag + close;
1011
+ },
1012
+ functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
1013
+ var l = fn.length;
1014
+ if ( !l ) return '';
1015
+
1016
+ var args = Array(l);
1017
+ while ( l-- )
1018
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
1019
+ return ' ' + args.join(', ') + ' ';
1020
+ },
1021
+ key:quote, //object calls it internally, the key part of an item in a map
1022
+ functionCode:'[code]', //function calls it internally, it's the content of the function
1023
+ attribute:quote, //node calls it internally, it's an html attribute value
1024
+ string:quote,
1025
+ date:quote,
1026
+ regexp:literal, //regex
1027
+ number:literal,
1028
+ 'boolean':literal
1029
+ },
1030
+ DOMAttrs:{//attributes to dump from nodes, name=>realName
1031
+ id:'id',
1032
+ name:'name',
1033
+ 'class':'className'
1034
+ },
1035
+ HTML:true,//if true, entities are escaped ( <, >, \t, space and \n )
1036
+ indentChar:' ',//indentation unit
1037
+ multiline:true //if true, items in a collection, are separated by a \n, else just a space.
1038
+ };
1039
+
1040
+ return jsDump;
1041
+ })();
1042
+
1043
+ })(this);